diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitCBase.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitCBase.td --- a/mlir/include/mlir/Dialect/EmitC/IR/EmitCBase.td +++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitCBase.td @@ -32,6 +32,11 @@ let useDefaultTypePrinterParser = 1; let useDefaultAttributePrinterParser = 1; let useFoldAPI = kEmitFoldAdaptorFolder; + let hasOperationAttrVerify = 1; + let extraClassDeclaration = [{ + /// Get the name of the attribute used for emitc function attributes. + static StringRef getFuncAttrName() { return "emitc.func_attr"; } + }]; } #endif // MLIR_DIALECT_EMITC_IR_EMITCBASE diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp --- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp +++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp @@ -44,6 +44,20 @@ return builder.create(loc, type, value); } +LogicalResult EmitCDialect::verifyOperationAttribute(Operation *op, + NamedAttribute attr) { + if (attr.getName() == getFuncAttrName()) { + auto arrayAttr = dyn_cast(attr.getValue()); + if (!arrayAttr || !llvm::all_of(arrayAttr, [](Attribute attr) { + return attr.isa(); + })) + return op->emitError() << "attribute '" << attr.getName() + << "' must be an array of strings"; + } + // Pass through other attributes + return success(); +} + //===----------------------------------------------------------------------===// // ApplyOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp --- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp +++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp @@ -603,6 +603,16 @@ CppEmitter::Scope scope(emitter); raw_indented_ostream &os = emitter.ostream(); + // Emit any function attributes if the (discardable) emitc attribute is + // present. + if (auto func_attr = functionOp->getAttrOfType( + EmitCDialect::getFuncAttrName())) { + for (auto &attr : func_attr) { + if (auto stringAttr = attr.dyn_cast()) + os << stringAttr.getValue() << "\n"; + } + } + if (failed(emitter.emitTypes(functionOp.getLoc(), functionOp.getFunctionType().getResults()))) return failure(); diff --git a/mlir/test/Target/Cpp/common-cpp.mlir b/mlir/test/Target/Cpp/common-cpp.mlir --- a/mlir/test/Target/Cpp/common-cpp.mlir +++ b/mlir/test/Target/Cpp/common-cpp.mlir @@ -82,8 +82,13 @@ return %2 : !emitc.opaque<"status_t"> } -func.func @apply(%arg0: i32) -> !emitc.ptr { - // CHECK: int32_t* [[V2]] = &[[V1]]; +// Note: FileCheck will interpret [[nodiscard]] as a variable, so we use +// __attribute__ below. +// CHECK: static +// CHECK: __attribute__((warn_unused_result)) +// CHECK: int32_t* apply(int32_t [[V1:[^ ]*]]) { +func.func @apply(%arg0: i32) -> !emitc.ptr attributes {emitc.func_attr = ["static", "__attribute__((warn_unused_result))"]} { + // CHECK: int32_t* [[V2:[^ ]*]] = &[[V1]]; %0 = emitc.apply "&"(%arg0) : (i32) -> !emitc.ptr // CHECK: int32_t [[V3]] = *[[V2]]; %1 = emitc.apply "*"(%0) : (!emitc.ptr) -> (i32)