diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 0cfbf84fa58a..aebcb5f146f4 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1119,6 +1119,36 @@ def ConvergentAttr : CIRUnitAttr<"Convergent", "convergent"> { let storageType = [{ ConvergentAttr }]; } +def UWTableKindNone + : I32EnumAttrCase<"None", 0, "none">; +def UWTableKindSync + : I32EnumAttrCase<"Sync", 1, "sync">; +def UWTableKindAsync + : I32EnumAttrCase<"Async", 2, "async">; + +def UWTableKind : I32EnumAttr<"UWTableKind", "Unwind table kind", [ + UWTableKindNone, UWTableKindSync, UWTableKindAsync +]> { + let cppNamespace = "::cir"; + let genSpecializedAttr = 0; +} + +def UWTableAttr : EnumAttr { + let summary = "Unwind table kind attribute"; + let description = [{ + The kind of unwind tables to generate for a function. `none` means no unwind + tables are generated; `sync` means synchronous unwind tables (that are only + valid at call boundaries), and `async` means asynchronous unwind tables + (that are valid at all instructions). When applied to a module, this + controls the unwind table generation for any synthesized functions. + }]; + + let cppClassName = "UWTableAttr"; + let assemblyFormat = [{ + `<` $value `>` + }]; +} + class CIR_GlobalCtorDtor : CIR_Attr<"Global" # name, "global_" # attrMnemonic> { diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index 5b3b4eedc682..46d2f1a13273 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -38,6 +38,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getLangAttrName() { return "cir.lang"; } static llvm::StringRef getTripleAttrName() { return "cir.triple"; } static llvm::StringRef getOptInfoAttrName() { return "cir.opt_info"; } + static llvm::StringRef getUWTableAttrName() { return "cir.uwtable"; } static llvm::StringRef getGlobalCtorsAttrName() { return "cir.global_ctors"; } static llvm::StringRef getGlobalDtorsAttrName() { return "cir.global_dtors"; } diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 5ab4473bb7ec..19aa040adc19 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -361,7 +361,6 @@ struct MissingFeatures { static bool codeModel() { return false; } static bool largeDataThreshold() { return false; } static bool directAccessExternalData() { return false; } - static bool setUwtable() { return false; } static bool setFramePointer() { return false; } static bool simplifyPersonality() { return false; } static bool emitVersionIdentMetadata() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 69e611972a6c..4bfd0fdd7a13 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2500,6 +2500,12 @@ void CIRGenModule::setCIRFunctionAttributesForDefinition(const Decl *decl, FuncOp f) { mlir::NamedAttrList attrs{f.getExtraAttrs().getElements().getValue()}; + if ((!decl || !decl->hasAttr()) && codeGenOpts.UnwindTables) { + auto attr = cir::UWTableAttr::get( + &getMLIRContext(), cir::UWTableKind(codeGenOpts.UnwindTables)); + attrs.set(attr.getMnemonic(), attr); + } + if (!hasUnwindExceptions(getLangOpts())) { auto attr = cir::NoThrowAttr::get(&getMLIRContext()); attrs.set(attr.getMnemonic(), attr); @@ -3258,7 +3264,10 @@ void CIRGenModule::Release() { llvm_unreachable("NYI"); assert(!MissingFeatures::directAccessExternalData()); if (codeGenOpts.UnwindTables) - assert(!MissingFeatures::setUwtable()); + theModule->setAttr( + cir::CIRDialect::getUWTableAttrName(), + cir::UWTableAttr::get(&getMLIRContext(), + cir::UWTableKind(codeGenOpts.UnwindTables))); switch (codeGenOpts.getFramePointer()) { case CodeGenOptions::FramePointerKind::None: diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp index ca0b498f9f2f..2418b3bbfa11 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVMIR.cpp @@ -81,6 +81,10 @@ class CIRDialectLLVMIRTranslationInterface oclVerMD->addOperand(llvm::MDNode::get(llvmContext, oclVerElts)); } + if (auto uwTableAttr = + mlir::dyn_cast(attribute.getValue())) + llvmModule->setUwtable(convertUWTableKind(uwTableAttr.getValue())); + // Drop ammended CIR attribute from LLVM op. module->removeAttr(attribute.getName()); } @@ -129,6 +133,11 @@ class CIRDialectLLVMIRTranslationInterface attr.getValue())) { emitOpenCLKernelArgMetadata(clArgMetadata, func.getNumArguments(), llvmFunc, moduleTranslation); + } else if (auto uwTableAttr = + mlir::dyn_cast(attr.getValue())) { + llvm::AttrBuilder builder(llvmFunc->getContext()); + builder.addUWTableAttr(convertUWTableKind(uwTableAttr.getValue())); + llvmFunc->addFnAttrs(builder); } } } @@ -261,6 +270,19 @@ class CIRDialectLLVMIRTranslationInterface llvmFunc->setMetadata("kernel_arg_name", llvm::MDNode::get(vmCtx, argNames)); } + +private: + static llvm::UWTableKind convertUWTableKind(cir::UWTableKind kind) { + // TODO(cir): Use UWTableKindAttr from the LLVM dialect when available. + switch (kind) { + case cir::UWTableKind::None: + return llvm::UWTableKind::None; + case cir::UWTableKind::Sync: + return llvm::UWTableKind::Sync; + case cir::UWTableKind::Async: + return llvm::UWTableKind::Async; + } + } }; void registerCIRDialectTranslation(mlir::DialectRegistry ®istry) { diff --git a/clang/test/CIR/CodeGen/uwtable.cpp b/clang/test/CIR/CodeGen/uwtable.cpp new file mode 100644 index 000000000000..ff9d9873f9b6 --- /dev/null +++ b/clang/test/CIR/CodeGen/uwtable.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t-none.cir +// RUN: FileCheck %s --input-file=%t-none.cir --check-prefix=CIR-NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -funwind-tables=0 %s -o %t-none-explicit.cir +// RUN: FileCheck %s --input-file=%t-none-explicit.cir --check-prefix=CIR-NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -funwind-tables=1 %s -o %t-sync.cir +// RUN: FileCheck %s --input-file=%t-sync.cir --check-prefix=CIR-SYNC +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -funwind-tables=2 %s -o %t-async.cir +// RUN: FileCheck %s --input-file=%t-async.cir --check-prefix=CIR-ASYNC + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-none.ll +// RUN: FileCheck %s --input-file=%t-none.ll --check-prefix=LLVM-NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -funwind-tables=0 %s -o %t-none-explicit.ll +// RUN: FileCheck %s --input-file=%t-none-explicit.ll --check-prefix=LLVM-NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -funwind-tables=1 %s -o %t-sync.ll +// RUN: FileCheck %s --input-file=%t-sync.ll --check-prefix=LLVM-SYNC +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -funwind-tables=2 %s -o %t-async.ll +// RUN: FileCheck %s --input-file=%t-async.ll --check-prefix=LLVM-ASYNC + +// CIR-NONE-NOT: #cir.uwtable + +// CIR-SYNC-DAG: module {{.*}} attributes {{{.*}}cir.uwtable = #cir.uwtable +// CIR-SYNC-DAG: cir.func @_Z1fv() extra(#[[f_attr:.*]]) +// CIR-SYNC-DAG: cir.func @_Z1gv() extra(#[[g_attr:.*]]) +// CIR-SYNC-DAG: #[[f_attr]] = #cir +// CIR-SYNC-DAG: #[[g_attr]] = +// CIR-SYNC-NOT: #cir.uwtable + +// CIR-ASYNC-DAG: module {{.*}} attributes {{{.*}}cir.uwtable = #cir.uwtable +// CIR-ASYNC-DAG: cir.func @_Z1fv() extra(#[[f_attr:.*]]) +// CIR-ASYNC-DAG: cir.func @_Z1gv() extra(#[[g_attr:.*]]) +// CIR-ASYNC-DAG: #[[f_attr]] = #cir +// CIR-ASYNC-DAG: #[[g_attr]] = +// CIR-ASYNC-NOT: #cir.uwtable + +// Avoid matching "uwtable" in the ModuleID and source_filename comments. +// LLVM-NONE: define {{.*}} @_Z1fv() +// LLVM-NONE-NOT: uwtable + +// LLVM-SYNC: define {{.*}} @_Z1fv() #[[#F_ATTRS:]] +// LLVM-SYNC: define {{.*}} @_Z1gv() #[[#G_ATTRS:]] +// LLVM-SYNC: attributes #[[#F_ATTRS]] = {{{.*}}uwtable(sync) +// LLVM-SYNC: attributes #[[#G_ATTRS]] = +// LLVM-SYNC-NOT: uwtable +// LLVM-SYNC-DAG: ![[#METADATA:]] = !{i32 7, !"uwtable", i32 1} +// LLVM-SYNC-DAG: !llvm.module.flags = !{{{.*}}[[#METADATA]] + +// LLVM-ASYNC: define {{.*}} @_Z1fv() #[[#ATTRS:]] +// LLVM-ASYNC: define {{.*}} @_Z1gv() #[[#G_ATTRS:]] +// LLVM-ASYNC: attributes #[[#ATTRS]] = {{{.*}}uwtable{{ }} +// LLVM-ASYNC: attributes #[[#G_ATTRS]] = +// LLVM-ASYNC-NOT: uwtable +// LLVM-ASYNC-DAG: ![[#METADATA:]] = !{i32 7, !"uwtable", i32 2} +// LLVM-ASYNC-DAG: !llvm.module.flags = !{{{.*}}[[#METADATA]] +void f() {} + +[[clang::nouwtable]] void g() {}