Skip to content

Commit 4cc7d60

Browse files
authored
[MLIR] emitc: Add emitc.file op (llvm#123298)
A `emitc.file` represents a file that can be emitted into a single C++ file. This allows to manage multiple source files within the same MLIR module, but emit them into separate files. This feature is opt-in. By default, `mlir-translate` emits all ops outside of `emitc.file` and ignores all `emitc.file` ops and their bodies. When specifying the `-file-id=id` flag, `mlir-translate` emits all ops outside of `emitc.file` and the ops within the `emitc.file` with matching `id`. Example: ```mlir emitc.file "main" { func @func_one() { return } } emitc.file "test" { func @func_two() { return } } ``` `mlir-translate -file-id=main` will emit `func_one` and `mlir-translate -file-id=test` will emit `func_two`.
1 parent 27fe2c9 commit 4cc7d60

File tree

6 files changed

+136
-15
lines changed

6 files changed

+136
-15
lines changed

mlir/include/mlir/Dialect/EmitC/IR/EmitC.td

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ include "mlir/Interfaces/FunctionInterfaces.td"
2323
include "mlir/Interfaces/SideEffectInterfaces.td"
2424
include "mlir/IR/OpAsmInterface.td"
2525
include "mlir/IR/RegionKindInterface.td"
26+
include "mlir/IR/BuiltinAttributes.td"
2627

2728
//===----------------------------------------------------------------------===//
2829
// EmitC op definitions
@@ -56,6 +57,52 @@ def IntegerIndexOrOpaqueType : Type<CPred<"emitc::isIntegerIndexOrOpaqueType($_s
5657
"integer, index or opaque type supported by EmitC">;
5758
def FloatIntegerIndexOrOpaqueType : AnyTypeOf<[EmitCFloatType, IntegerIndexOrOpaqueType]>;
5859

60+
def EmitC_FileOp
61+
: EmitC_Op<"file", [IsolatedFromAbove, NoRegionArguments, SymbolTable,
62+
OpAsmOpInterface]#GraphRegionNoTerminator.traits> {
63+
let summary = "A file container operation";
64+
let description = [{
65+
A `file` represents a single C/C++ file.
66+
67+
`mlir-translate` ignores the body of all `emitc.file` ops
68+
unless the `-file-id=id` flag is used. With that flag, all `emitc.file` ops
69+
with matching id are emitted.
70+
71+
Example:
72+
73+
```mlir
74+
emitc.file "main" {
75+
emitc.func @func_one() {
76+
emitc.return
77+
}
78+
}
79+
```
80+
}];
81+
82+
let arguments = (ins Builtin_StringAttr:$id);
83+
let regions = (region SizedRegion<1>:$bodyRegion);
84+
85+
let assemblyFormat = "$id attr-dict-with-keyword $bodyRegion";
86+
let builders = [OpBuilder<(ins CArg<"StringRef">:$id)>];
87+
let extraClassDeclaration = [{
88+
/// Construct a file op from the given location with a name.
89+
static FileOp create(Location loc, StringRef name);
90+
91+
//===------------------------------------------------------------------===//
92+
// OpAsmOpInterface Methods
93+
//===------------------------------------------------------------------===//
94+
95+
/// EmitC ops in the body can omit their 'emitc.' prefix in the assembly.
96+
static ::llvm::StringRef getDefaultDialect() {
97+
return "emitc";
98+
}
99+
}];
100+
101+
// We need to ensure that the body region has a block;
102+
// the auto-generated builders do not guarantee that.
103+
let skipDefaultBuilders = 1;
104+
}
105+
59106
def EmitC_AddOp : EmitC_BinaryOp<"add", [CExpression]> {
60107
let summary = "Addition operation";
61108
let description = [{

mlir/include/mlir/Target/Cpp/CppEmitter.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define MLIR_TARGET_CPP_CPPEMITTER_H
1515

1616
#include "mlir/Support/LLVM.h"
17+
#include "llvm/ADT/StringRef.h"
1718

1819
namespace mlir {
1920
class Operation;
@@ -23,8 +24,11 @@ namespace emitc {
2324
/// the region of 'op' need almost all be in EmitC dialect. The parameter
2425
/// 'declareVariablesAtTop' enforces that all variables for op results and block
2526
/// arguments are declared at the beginning of the function.
27+
/// If parameter 'fileId' is non-empty, then body of `emitc.file` ops
28+
/// with matching id are emitted.
2629
LogicalResult translateToCpp(Operation *op, raw_ostream &os,
27-
bool declareVariablesAtTop = false);
30+
bool declareVariablesAtTop = false,
31+
StringRef fileId = {});
2832
} // namespace emitc
2933
} // namespace mlir
3034

mlir/lib/Dialect/EmitC/IR/EmitC.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,15 @@ void SwitchOp::getRegionInvocationBounds(
12891289
bounds.emplace_back(/*lb=*/0, /*ub=*/regIndex == liveIndex);
12901290
}
12911291

1292+
//===----------------------------------------------------------------------===//
1293+
// FileOp
1294+
//===----------------------------------------------------------------------===//
1295+
void FileOp::build(OpBuilder &builder, OperationState &state, StringRef id) {
1296+
state.addRegion()->emplaceBlock();
1297+
state.attributes.push_back(
1298+
builder.getNamedAttr("id", builder.getStringAttr(id)));
1299+
}
1300+
12921301
//===----------------------------------------------------------------------===//
12931302
// TableGen'd op method definitions
12941303
//===----------------------------------------------------------------------===//

mlir/lib/Target/Cpp/TranslateRegistration.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,17 @@ void registerToCppTranslation() {
2929
llvm::cl::desc("Declare variables at top when emitting C/C++"),
3030
llvm::cl::init(false));
3131

32+
static llvm::cl::opt<std::string> fileId(
33+
"file-id", llvm::cl::desc("Emit emitc.file ops with matching id"),
34+
llvm::cl::init(""));
35+
3236
TranslateFromMLIRRegistration reg(
3337
"mlir-to-cpp", "translate from mlir to cpp",
3438
[](Operation *op, raw_ostream &output) {
3539
return emitc::translateToCpp(
3640
op, output,
37-
/*declareVariablesAtTop=*/declareVariablesAtTop);
41+
/*declareVariablesAtTop=*/declareVariablesAtTop,
42+
/*fileId=*/fileId);
3843
},
3944
[](DialectRegistry &registry) {
4045
// clang-format off

mlir/lib/Target/Cpp/TranslateToCpp.cpp

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ static FailureOr<int> getOperatorPrecedence(Operation *operation) {
114114
namespace {
115115
/// Emitter that uses dialect specific emitters to emit C++ code.
116116
struct CppEmitter {
117-
explicit CppEmitter(raw_ostream &os, bool declareVariablesAtTop);
117+
explicit CppEmitter(raw_ostream &os, bool declareVariablesAtTop,
118+
StringRef fileId);
118119

119120
/// Emits attribute or returns failure.
120121
LogicalResult emitAttribute(Location loc, Attribute attr);
@@ -231,6 +232,11 @@ struct CppEmitter {
231232
/// be declared at the beginning of a function.
232233
bool shouldDeclareVariablesAtTop() { return declareVariablesAtTop; };
233234

235+
/// Returns whether this file op should be emitted
236+
bool shouldEmitFile(FileOp file) {
237+
return !fileId.empty() && file.getId() == fileId;
238+
}
239+
234240
/// Get expression currently being emitted.
235241
ExpressionOp getEmittedExpression() { return emittedExpression; }
236242

@@ -258,6 +264,9 @@ struct CppEmitter {
258264
/// includes results from ops located in nested regions.
259265
bool declareVariablesAtTop;
260266

267+
/// Only emit file ops whos id matches this value.
268+
std::string fileId;
269+
261270
/// Map from value to name of C++ variable that contain the name.
262271
ValueMapper valueMapper;
263272

@@ -963,6 +972,19 @@ static LogicalResult printOperation(CppEmitter &emitter, ModuleOp moduleOp) {
963972
return success();
964973
}
965974

975+
static LogicalResult printOperation(CppEmitter &emitter, FileOp file) {
976+
if (!emitter.shouldEmitFile(file))
977+
return success();
978+
979+
CppEmitter::Scope scope(emitter);
980+
981+
for (Operation &op : file) {
982+
if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/false)))
983+
return failure();
984+
}
985+
return success();
986+
}
987+
966988
static LogicalResult printFunctionArgs(CppEmitter &emitter,
967989
Operation *functionOp,
968990
ArrayRef<Type> arguments) {
@@ -1162,8 +1184,10 @@ static LogicalResult printOperation(CppEmitter &emitter,
11621184
return success();
11631185
}
11641186

1165-
CppEmitter::CppEmitter(raw_ostream &os, bool declareVariablesAtTop)
1166-
: os(os), declareVariablesAtTop(declareVariablesAtTop) {
1187+
CppEmitter::CppEmitter(raw_ostream &os, bool declareVariablesAtTop,
1188+
StringRef fileId)
1189+
: os(os), declareVariablesAtTop(declareVariablesAtTop),
1190+
fileId(fileId.str()) {
11671191
valueInScopeCount.push(0);
11681192
labelInScopeCount.push(0);
11691193
}
@@ -1558,12 +1582,13 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
15581582
emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
15591583
emitc::CallOpaqueOp, emitc::CastOp, emitc::CmpOp,
15601584
emitc::ConditionalOp, emitc::ConstantOp, emitc::DeclareFuncOp,
1561-
emitc::DivOp, emitc::ExpressionOp, emitc::ForOp, emitc::FuncOp,
1562-
emitc::GlobalOp, emitc::IfOp, emitc::IncludeOp, emitc::LoadOp,
1563-
emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
1564-
emitc::MulOp, emitc::RemOp, emitc::ReturnOp, emitc::SubOp,
1565-
emitc::SwitchOp, emitc::UnaryMinusOp, emitc::UnaryPlusOp,
1566-
emitc::VariableOp, emitc::VerbatimOp>(
1585+
emitc::DivOp, emitc::ExpressionOp, emitc::FileOp, emitc::ForOp,
1586+
emitc::FuncOp, emitc::GlobalOp, emitc::IfOp, emitc::IncludeOp,
1587+
emitc::LoadOp, emitc::LogicalAndOp, emitc::LogicalNotOp,
1588+
emitc::LogicalOrOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp,
1589+
emitc::SubOp, emitc::SwitchOp, emitc::UnaryMinusOp,
1590+
emitc::UnaryPlusOp, emitc::VariableOp, emitc::VerbatimOp>(
1591+
15671592
[&](auto op) { return printOperation(*this, op); })
15681593
// Func ops.
15691594
.Case<func::CallOp, func::FuncOp, func::ReturnOp>(
@@ -1606,8 +1631,9 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
16061631
// Never emit a semicolon for some operations, especially if endening with
16071632
// `}`.
16081633
trailingSemicolon &=
1609-
!isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::ForOp, emitc::IfOp,
1610-
emitc::IncludeOp, emitc::SwitchOp, emitc::VerbatimOp>(op);
1634+
!isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::FileOp, emitc::ForOp,
1635+
emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp, emitc::VerbatimOp>(
1636+
op);
16111637

16121638
os << (trailingSemicolon ? ";\n" : "\n");
16131639

@@ -1743,7 +1769,8 @@ LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef<Type> types) {
17431769
}
17441770

17451771
LogicalResult emitc::translateToCpp(Operation *op, raw_ostream &os,
1746-
bool declareVariablesAtTop) {
1747-
CppEmitter emitter(os, declareVariablesAtTop);
1772+
bool declareVariablesAtTop,
1773+
StringRef fileId) {
1774+
CppEmitter emitter(os, declareVariablesAtTop, fileId);
17481775
return emitter.emitOperation(*op, /*trailingSemicolon=*/false);
17491776
}

mlir/test/Target/Cpp/file.mlir

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s --check-prefix NO-FILTER
2+
// RUN: mlir-translate -mlir-to-cpp -file-id=non-existing %s | FileCheck %s --check-prefix NON-EXISTING
3+
// RUN: mlir-translate -mlir-to-cpp -file-id=file_one %s | FileCheck %s --check-prefix FILE-ONE
4+
// RUN: mlir-translate -mlir-to-cpp -file-id=file_two %s | FileCheck %s --check-prefix FILE-TWO
5+
6+
7+
// NO-FILTER-NOT: func_one
8+
// NO-FILTER-NOT: func_two
9+
10+
// NON-EXISTING-NOT: func_one
11+
// NON-EXISTING-NOT: func_two
12+
13+
// FILE-ONE: func_one
14+
// FILE-ONE-NOT: func_two
15+
16+
// FILE-TWO-NOT: func_one
17+
// FILE-TWO: func_two
18+
19+
emitc.file "file_one" {
20+
emitc.func @func_one(%arg: f32) {
21+
emitc.return
22+
}
23+
}
24+
25+
emitc.file "file_two" {
26+
emitc.func @func_two(%arg: f32) {
27+
emitc.return
28+
}
29+
}

0 commit comments

Comments
 (0)