From b31c62e34827939591b87af09de0e79056281415 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai Date: Mon, 9 Dec 2024 16:50:30 -0800 Subject: [PATCH] [CIR][CIRGen] Add uwtable attribute The module-level uwtable attribute controls the unwind tables for any synthesized functions, and the function-level attribute controls them for those functions. I'll add support for this attribute to the LLVM dialect as well, but translate it from CIR directly for now to avoid waiting on the MLIR addition and a subsequent rebase. --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 29 ++++++++++ .../clang/CIR/Dialect/IR/CIRDialect.td | 1 + clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenModule.cpp | 11 +++- .../Lowering/DirectToLLVM/LowerToLLVMIR.cpp | 22 ++++++++ clang/test/CIR/CodeGen/uwtable.cpp | 56 +++++++++++++++++++ 6 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 clang/test/CIR/CodeGen/uwtable.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 0cfbf84fa58a..3293ec7566db 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -1119,6 +1119,35 @@ 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 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 4a1130de6ca5..26ad4fda556d 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -369,7 +369,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..e38a0a2bd1f3 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::UWTableKindAttr::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::UWTableKindAttr::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..e8017c9b0562 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() {}