serene/libserene/include/serene/slir/ops.td

266 lines
8.0 KiB
TableGen

/*
* Serene Programming Language
*
* Copyright (c) 2019-2022 Sameer Rahmani <lxsameer@gnu.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SERENE_DIALECT_OPS
#define SERENE_DIALECT_OPS
include "mlir/IR/OpBase.td"
include "mlir/Interfaces/CallInterfaces.td"
include "mlir/IR/RegionKindInterface.td"
include "mlir/IR/SymbolInterfaces.td"
include "mlir/IR/OpAsmInterface.td"
// Base class for Serene dialect operations. This operation inherits from the base
// `Op` class in OpBase.td, and provides:
// * The parent dialect of the operation.
// * The mnemonic for the operation, or the name without the dialect prefix.
// * A list of traits for the operation.
class Serene_Op<string mnemonic, list<Trait> traits = []> :
Op<Serene_Dialect, mnemonic, traits>;
def ValueOp : Serene_Op<"value", [
ConstantLike, NoSideEffect,
TypesMatchWith<
"result and attribute have the same type",
"value", "result", "$_self">]> {
let summary = "This operation represent a compile time value";
let description = [{
The `value` operation produces an SSA value equal to value
specified by an attribute. This is the way Serene marks constant
compile time values.
Example:
```
// Integer constant
%1 = serene.value 42 : i32
// Equivalent generic form
%1 = "serene.value"() {value = 42 : i32} : () -> i32
```
}];
let arguments = (ins AnyAttr:$value);
let results = (outs AnyType:$result);
let builders = [
OpBuilder<(ins "mlir::Attribute":$value),
[{ build($_builder, $_state, value.getType(), value); }]>,
OpBuilder<(ins "mlir::Attribute":$value, "mlir::Type":$type),
[{ build($_builder, $_state, type, value); }]>,
];
let extraClassDeclaration = [{
/// Whether the constant op can be constructed with a particular value and
/// type.
static bool isBuildableWith(mlir::Attribute value, mlir::Type type);
}];
// Need to define the ::fold() method to make value op foldable
// let hasFolder = 1;
let assemblyFormat = "attr-dict $value";
}
// Symbol Op
def SymbolOp : Serene_Op<"symbol", [
ConstantLike, NoSideEffect, IsolatedFromAbove
]> {
let summary = "This operation is the compile time contructor for symbol type";
let description = [{
The `symbol` operation produces an SSA value that holds a value of type
symbol at compile time.
Example:
```
%1 = serene.symbol "some.ns" "symbol_name"
// Equivalent generic form
%1 = "serene.symbol"() {ns = "some.ns", name = "symbol_name"} : () -> i32
```
}];
let arguments = (ins StrAttr:$ns, StrAttr:$name);
let results = (outs SereneSymbol:$result);
let assemblyFormat = "attr-dict $ns $name";
}
// Define
def DefnieOp: Serene_Op<"define", [Symbol]> {
let summary = "This operation defines a global binding in the current namespace";
let description = [{
`define` defines a global binding in the current namespace. It always return a
symbol type.
Examples:
```mlir
%foo = "serene.define"(%0){name = "foo"}: (i64) -> !serene.symbol
// compact form
%bar = serene.define "bar", %0 : i64
```
}];
let arguments = (ins StrAttr:$sym_name, AnyType:$value, //StrAttr:$name,SymbolNameAttr:$sym_name
OptionalAttr<StrAttr>:$sym_visibility);
let results = (outs SereneSymbol);
let assemblyFormat = "attr-dict $sym_name `,` $value `:` type($value)";
}
def FnOp: Serene_Op<"fn", [
AffineScope, AutomaticAllocationScope,
IsolatedFromAbove,
]> {
let summary = "This operation is just a place holder for a function";
let description = [{
A place holder for an anonymous function. For example consider an expression
like `(def a (fn (x) x))`, in this case we don't immediately create an anonymous
function since we need to set the name and create the function later.
}];
let arguments = (ins StrAttr:$name,
TypeAttr:$return_type,
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region VariadicRegion<AnyRegion>:$bodies);
let results = (outs SereneFn);
}
def ReturnOp: Serene_Op<"ret", [
NoSideEffect, HasParent<"FnOp">,
ReturnLike, Terminator
// MemRefsNormalizable
]> {
let summary = "This operation marks the return value of a function";
let description = [{
`ret` marks the return value of a function body.
Example:
```mlir
serene.ret %foo : i32
```
}];
let arguments = (ins Variadic<AnyType>:$operands);
let builders = [OpBuilder<(ins), [{
build($_builder, $_state, llvm::None);
}]>];
let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?";
// TODO: Turn on the verifier for `ret`
// let hasVerifier = 1;
}
def CallOp : Serene_Op<"call",
[MemRefsNormalizable]> {
let summary = "call operation";
let description = [{
The `serene.call` operation represents a direct call to a function that is
within the same symbol scope as the call. The operands and result types of
the call must match the specified function type. The callee is encoded as a
symbol reference attribute named "callee".
Example:
```mlir
%2 = serene.call @my_add(%0, %1) : (f32, f32) -> f32
```
}];
let arguments = (ins SereneFn:$fn,
Variadic<AnyType>:$args);
let results = (outs Variadic<AnyType>);
let assemblyFormat = [{
$fn `(` $args `)` attr-dict `:` functional-type($args, results)
}];
}
def NsOp : Serene_Op<"ns", [
AffineScope, IsolatedFromAbove, NoRegionArguments, SymbolTable, Symbol,
OpAsmOpInterface
] # GraphRegionNoTerminator.traits> {
let summary = "This operation represents a Serene namespace.";
let description = [{
The `serene.ns` operation represents a namespace that will eventually
lowers to a `mlir::ModuleOp`.
Example:
```mlir
serene.ns @some.ns {
....
}
```
}];
// TODO: Create a new Attr type that is an array to represent
// required namespaces
let arguments = (ins SymbolNameAttr:$sym_name,
OptionalAttr<StrAttr>:$sym_visibility);
let regions = (region SizedRegion<1>:$body);
let assemblyFormat = "$sym_name attr-dict-with-keyword $body";
let builders = [
OpBuilder<(ins CArg<"llvm::Optional<llvm::StringRef>", "{}">:$name)>
];
let extraClassDeclaration = [{
/// Construct a namespace from the given location with an optional name.
// static NsOp create(slir::reader::LocationRange loc, Optional<StringRef> name = llvm::None);
/// Return the name of this module if present.
llvm::StringRef getName() { return sym_name(); }
//===------------------------------------------------------------------===//
// SymbolOpInterface Methods
//===------------------------------------------------------------------===//
/// A ModuleOp may optionally define a symbol.
bool isOptionalSymbol() { return false; }
//===------------------------------------------------------------------===//
// DataLayoutOpInterface Methods
//===------------------------------------------------------------------===//
mlir::DataLayoutSpecInterface getDataLayoutSpec();
//===------------------------------------------------------------------===//
// OpAsmOpInterface Methods
//===------------------------------------------------------------------===//
static ::llvm::StringRef getDefaultDialect() {
return "builtin";
}
}];
}
#endif // SERENE_DIALECT_OPS