Skip to content

Commit

Permalink
[mlir][EmitC] Add func, call and return operations and conversions (#…
Browse files Browse the repository at this point in the history
…79612)

This adds a `func`, `call` and `return` operation to the EmitC dialect,
closely related to the corresponding operations of the Func dialect. In
contrast to the operations of the Func dialect, the EmitC operations do
not support multiple results. The `emitc.func` op features a
`specifiers` argument that for example allows, with corresponding
support in the emitter, to emit `inline static` functions.

Furthermore, this adds patterns and a pass to convert the Func dialect
to EmitC. A `func.func` op that is `private` is converted to
`emitc.func` with a `"static"` specifier.
  • Loading branch information
marbre committed Feb 1, 2024
1 parent ae931b4 commit e7d40a8
Show file tree
Hide file tree
Showing 18 changed files with 815 additions and 31 deletions.
18 changes: 18 additions & 0 deletions mlir/include/mlir/Conversion/FuncToEmitC/FuncToEmitC.h
@@ -0,0 +1,18 @@
//===- FuncToEmitC.h - Func to EmitC Patterns -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITC_H
#define MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITC_H

namespace mlir {
class RewritePatternSet;

void populateFuncToEmitCPatterns(RewritePatternSet &patterns);
} // namespace mlir

#endif // MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITC_H
21 changes: 21 additions & 0 deletions mlir/include/mlir/Conversion/FuncToEmitC/FuncToEmitCPass.h
@@ -0,0 +1,21 @@
//===- FuncToEmitCPass.h - Func to EmitC Pass -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITCPASS_H
#define MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITCPASS_H

#include <memory>

namespace mlir {
class Pass;

#define GEN_PASS_DECL_CONVERTFUNCTOEMITC
#include "mlir/Conversion/Passes.h.inc"
} // namespace mlir

#endif // MLIR_CONVERSION_FUNCTOEMITC_FUNCTOEMITCPASS_H
1 change: 1 addition & 0 deletions mlir/include/mlir/Conversion/Passes.h
Expand Up @@ -29,6 +29,7 @@
#include "mlir/Conversion/ControlFlowToSPIRV/ControlFlowToSPIRV.h"
#include "mlir/Conversion/ControlFlowToSPIRV/ControlFlowToSPIRVPass.h"
#include "mlir/Conversion/ConvertToLLVM/ToLLVMPass.h"
#include "mlir/Conversion/FuncToEmitC/FuncToEmitCPass.h"
#include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVMPass.h"
#include "mlir/Conversion/FuncToSPIRV/FuncToSPIRVPass.h"
#include "mlir/Conversion/GPUCommon/GPUCommonPass.h"
Expand Down
9 changes: 9 additions & 0 deletions mlir/include/mlir/Conversion/Passes.td
Expand Up @@ -359,6 +359,15 @@ def ConvertControlFlowToSPIRV : Pass<"convert-cf-to-spirv"> {
];
}

//===----------------------------------------------------------------------===//
// FuncToEmitC
//===----------------------------------------------------------------------===//

def ConvertFuncToEmitC : Pass<"convert-func-to-emitc", "ModuleOp"> {
let summary = "Convert Func dialect to EmitC dialect";
let dependentDialects = ["emitc::EmitCDialect"];
}

//===----------------------------------------------------------------------===//
// FuncToLLVM
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
Expand Up @@ -20,6 +20,7 @@
#include "mlir/IR/Dialect.h"
#include "mlir/Interfaces/CastInterfaces.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Interfaces/FunctionInterfaces.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"

#include "mlir/Dialect/EmitC/IR/EmitCDialect.h.inc"
Expand Down
170 changes: 170 additions & 0 deletions mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
Expand Up @@ -16,8 +16,10 @@
include "mlir/Dialect/EmitC/IR/EmitCAttributes.td"
include "mlir/Dialect/EmitC/IR/EmitCTypes.td"

include "mlir/Interfaces/CallInterfaces.td"
include "mlir/Interfaces/CastInterfaces.td"
include "mlir/Interfaces/ControlFlowInterfaces.td"
include "mlir/Interfaces/FunctionInterfaces.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
include "mlir/IR/RegionKindInterface.td"

Expand Down Expand Up @@ -386,6 +388,174 @@ def EmitC_ForOp : EmitC_Op<"for",
let hasRegionVerifier = 1;
}

def EmitC_CallOp : EmitC_Op<"call",
[CallOpInterface,
DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
let summary = "call operation";
let description = [{
The `emitc.call` operation represents a direct call to an `emitc.func`
that is within the same symbol scope as the call. The operands and result type
of the call must match the specified function type. The callee is encoded as a
symbol reference attribute named "callee".

Example:

```mlir
%2 = emitc.call @my_add(%0, %1) : (f32, f32) -> f32
```
}];
let arguments = (ins FlatSymbolRefAttr:$callee, Variadic<AnyType>:$operands);
let results = (outs Variadic<AnyType>);

let builders = [
OpBuilder<(ins "FuncOp":$callee, CArg<"ValueRange", "{}">:$operands), [{
$_state.addOperands(operands);
$_state.addAttribute("callee", SymbolRefAttr::get(callee));
$_state.addTypes(callee.getFunctionType().getResults());
}]>,
OpBuilder<(ins "SymbolRefAttr":$callee, "TypeRange":$results,
CArg<"ValueRange", "{}">:$operands), [{
$_state.addOperands(operands);
$_state.addAttribute("callee", callee);
$_state.addTypes(results);
}]>,
OpBuilder<(ins "StringAttr":$callee, "TypeRange":$results,
CArg<"ValueRange", "{}">:$operands), [{
build($_builder, $_state, SymbolRefAttr::get(callee), results, operands);
}]>,
OpBuilder<(ins "StringRef":$callee, "TypeRange":$results,
CArg<"ValueRange", "{}">:$operands), [{
build($_builder, $_state, StringAttr::get($_builder.getContext(), callee),
results, operands);
}]>];

let extraClassDeclaration = [{
FunctionType getCalleeType();

/// Get the argument operands to the called function.
operand_range getArgOperands() {
return {arg_operand_begin(), arg_operand_end()};
}

MutableOperandRange getArgOperandsMutable() {
return getOperandsMutable();
}

operand_iterator arg_operand_begin() { return operand_begin(); }
operand_iterator arg_operand_end() { return operand_end(); }

/// Return the callee of this operation.
CallInterfaceCallable getCallableForCallee() {
return (*this)->getAttrOfType<SymbolRefAttr>("callee");
}

/// Set the callee for this operation.
void setCalleeFromCallable(CallInterfaceCallable callee) {
(*this)->setAttr("callee", callee.get<SymbolRefAttr>());
}
}];

let assemblyFormat = [{
$callee `(` $operands `)` attr-dict `:` functional-type($operands, results)
}];
}

def EmitC_FuncOp : EmitC_Op<"func", [
AutomaticAllocationScope,
FunctionOpInterface, IsolatedFromAbove
]> {
let summary = "An operation with a name containing a single `SSACFG` region";
let description = [{
Operations within the function cannot implicitly capture values defined
outside of the function, i.e. Functions are `IsolatedFromAbove`. All
external references must use function arguments or attributes that establish
a symbolic connection (e.g. symbols referenced by name via a string
attribute like SymbolRefAttr). While the MLIR textual form provides a nice
inline syntax for function arguments, they are internally represented as
“block arguments” to the first block in the region.

Only dialect attribute names may be specified in the attribute dictionaries
for function arguments, results, or the function itself.

Example:

```mlir
// A function with no results:
emitc.func @foo(%arg0 : i32) {
emitc.call_opaque "bar" (%arg0) : (i32) -> ()
emitc.return
}

// A function with its argument as single result:
emitc.func @foo(%arg0 : i32) -> i32 {
emitc.return %arg0 : i32
}

// A function with specifiers attribute:
emitc.func @example_specifiers_fn_attr() -> i32
attributes {specifiers = ["static","inline"]} {
%0 = emitc.call_opaque "foo" (): () -> i32
emitc.return %0 : i32
}

```
}];
let arguments = (ins SymbolNameAttr:$sym_name,
TypeAttrOf<FunctionType>:$function_type,
OptionalAttr<StrArrayAttr>:$specifiers,
OptionalAttr<DictArrayAttr>:$arg_attrs,
OptionalAttr<DictArrayAttr>:$res_attrs);
let regions = (region AnyRegion:$body);

let builders = [OpBuilder<(ins
"StringRef":$name, "FunctionType":$type,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs,
CArg<"ArrayRef<DictionaryAttr>", "{}">:$argAttrs)
>];
let extraClassDeclaration = [{
//===------------------------------------------------------------------===//
// FunctionOpInterface Methods
//===------------------------------------------------------------------===//

/// Returns the region on the current operation that is callable. This may
/// return null in the case of an external callable object, e.g. an external
/// function.
::mlir::Region *getCallableRegion() { return isExternal() ? nullptr : &getBody(); }

/// Returns the argument types of this function.
ArrayRef<Type> getArgumentTypes() { return getFunctionType().getInputs(); }

/// Returns the result types of this function.
ArrayRef<Type> getResultTypes() { return getFunctionType().getResults(); }
}];
let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
}

def EmitC_ReturnOp : EmitC_Op<"return", [Pure, HasParent<"FuncOp">,
ReturnLike, Terminator]> {
let summary = "Function return operation";
let description = [{
The `emitc.return` operation represents a return operation within a function.
The operation takes zero or exactly one operand and produces no results.
The operand number and type must match the signature of the function
that contains the operation.

Example:

```mlir
emitc.func @foo() : (i32) {
...
emitc.return %0 : i32
}
```
}];
let arguments = (ins Optional<AnyType>:$operand);

let assemblyFormat = "attr-dict ($operand^ `:` type($operand))?";
let hasVerifier = 1;
}

def EmitC_IncludeOp
: EmitC_Op<"include", [HasParent<"ModuleOp">]> {
let summary = "Include operation";
Expand Down
1 change: 1 addition & 0 deletions mlir/lib/Conversion/CMakeLists.txt
Expand Up @@ -18,6 +18,7 @@ add_subdirectory(ControlFlowToLLVM)
add_subdirectory(ControlFlowToSCF)
add_subdirectory(ControlFlowToSPIRV)
add_subdirectory(ConvertToLLVM)
add_subdirectory(FuncToEmitC)
add_subdirectory(FuncToLLVM)
add_subdirectory(FuncToSPIRV)
add_subdirectory(GPUCommon)
Expand Down
16 changes: 16 additions & 0 deletions mlir/lib/Conversion/FuncToEmitC/CMakeLists.txt
@@ -0,0 +1,16 @@
add_mlir_conversion_library(MLIRFuncToEmitC
FuncToEmitC.cpp
FuncToEmitCPass.cpp

ADDITIONAL_HEADER_DIRS
${MLIR_MAIN_INCLUDE_DIR}/mlir/Conversion/FuncToEmitC

DEPENDS
MLIRConversionPassIncGen

LINK_LIBS PUBLIC
MLIREmitCDialect
MLIRFuncDialect
MLIRPass
MLIRTransformUtils
)

0 comments on commit e7d40a8

Please sign in to comment.