From 5283f46615665b0bf8a14f3d4f3acaa321fd8832 Mon Sep 17 00:00:00 2001 From: Erich Keane Date: Fri, 6 Feb 2026 06:37:48 -0800 Subject: [PATCH] [CIR] Add tons of function infra, plus a handful of attributes (#179811) This patch puts together a lot more of the CIR infrastructure for function attributes, plus adds a bunch of 'TODO' messages for areas that have been skipped. Along the way, we also implement 8 attributes in some way: -Convergent gets a little more work, to make the `noconvergent` C attribute have an effect -optsize/minsize are implemented, sourced from the command line -nobuiltin is a call-only attribute that tells not to replace the individual call with a builtin. This is a touch confusing, since no-builtins is an attribute that means "don't replace anything in the body of this function with builtins (from this list)". The spelling confusion is existing, and it seems that changing the names away from LLVM would be confusing. -save_reg_params & zero_call_used_regs are boht pretty simple registers -temp-func-name just passes a string to LLVM, consistent with existing implementation. -default-func-attrs is a difficult one. It takes command line arguments and passes them as LLVM-IR attributes directly on functions/calls. In the dialect, we are capturing these in their own attribute to pass them on correctly. However, this is one we cannot recover from LLVM-IR for obvious reasons, so we instead choose to let the 'passthrough' mechanism work for those. --- .../clang/CIR/Dialect/IR/CIRDialect.td | 10 + clang/lib/CIR/CodeGen/CIRGenCall.cpp | 207 +++++++++++++++++- clang/lib/CIR/CodeGen/CIRGenModule.h | 6 + .../CodeGen/default-func-attrs-cmd-line.cpp | 29 +++ .../CIR/CodeGen/no-builtin-attr-automatic.cpp | 83 +++++++ .../CIR/CodeGen/offload-convergent-attr.cu | 45 ++++ clang/test/CIR/CodeGen/optsize-func-attr.cpp | 74 +++++++ .../CIR/CodeGen/save-reg-params-func-attr.cpp | 29 +++ .../test/CIR/CodeGen/trap-func-name-attr.cpp | 33 +++ .../CodeGen/zero-call-used-regs-func-attr.cpp | 102 +++++++++ .../CodeGenBuiltins/X86/avx512-reduceIntrin.c | 8 +- .../X86/avx512-reduceMinMaxIntrin.c | 8 +- .../CodeGenBuiltins/X86/avx512fp16-builtins.c | 8 +- .../X86/avx512vlbf16-builtins.c | 12 +- .../X86/avx512vlfp16-builtins.c | 16 +- mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td | 10 + .../mlir/Target/LLVMIR/ModuleTranslation.h | 31 +-- mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp | 20 +- .../LLVMIR/LLVMToLLVMIRTranslation.cpp | 23 +- mlir/lib/Target/LLVMIR/ModuleImport.cpp | 28 +++ mlir/lib/Target/LLVMIR/ModuleTranslation.cpp | 35 ++- mlir/test/Dialect/LLVMIR/func.mlir | 24 ++ mlir/test/Dialect/LLVMIR/roundtrip.mlir | 21 ++ .../LLVMIR/Import/function-attributes.ll | 32 +++ .../test/Target/LLVMIR/Import/instructions.ll | 86 ++++++++ mlir/test/Target/LLVMIR/llvmir.mlir | 190 ++++++++++++++++ 26 files changed, 1116 insertions(+), 54 deletions(-) create mode 100644 clang/test/CIR/CodeGen/default-func-attrs-cmd-line.cpp create mode 100644 clang/test/CIR/CodeGen/no-builtin-attr-automatic.cpp create mode 100644 clang/test/CIR/CodeGen/offload-convergent-attr.cu create mode 100644 clang/test/CIR/CodeGen/optsize-func-attr.cpp create mode 100644 clang/test/CIR/CodeGen/save-reg-params-func-attr.cpp create mode 100644 clang/test/CIR/CodeGen/trap-func-name-attr.cpp create mode 100644 clang/test/CIR/CodeGen/zero-call-used-regs-func-attr.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index c06807efbb83..3e134d952b8b 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -54,6 +54,8 @@ def CIR_Dialect : Dialect { static llvm::StringRef getNoCallerSavedRegsAttrName() { return "no_caller_saved_registers"; } static llvm::StringRef getNoCallbackAttrName() { return "nocallback"; } static llvm::StringRef getAllocSizeAttrName() { return "allocsize"; } + static llvm::StringRef getOptimizeForSizeAttrName() { return "optsize"; } + static llvm::StringRef getMinSizeAttrName() { return "minsize"; } // Note: we have to name this with the underscore instead of the dash like // traditional LLVM-IR does, because the LLVM-IR-Dialect doesn't have a way // of forming names with a dash instead of underscore in its auto-generated @@ -61,7 +63,15 @@ def CIR_Dialect : Dialect { // of a [a-zA-Z0-9_] character regex(numbers only if not first), so there is // no way to get an underscore into this, even with escaping. static llvm::StringRef getModularFormatAttrName() { return "modular_format"; } + // NoBuiltins means "don't put builtins into my body", whereas "nobuiltin" + // means "I'm not a builtin, so don't replace me". This is a subtle + // difference, but one that reflects Classic Codegen. static llvm::StringRef getNoBuiltinsAttrName() { return "nobuiltins"; } + static llvm::StringRef getNoBuiltinAttrName() { return "nobuiltin"; } + static llvm::StringRef getTrapFuncNameAttrName() { return "trap_func_name"; } + static llvm::StringRef getZeroCallUsedRegsAttrName() { return "zero_call_used_regs"; } + static llvm::StringRef getSaveRegParamsAttrName() { return "save_reg_params"; } + static llvm::StringRef getDefaultFuncAttrsAttrName() { return "default_func_attrs"; } void registerAttributes(); void registerTypes(); diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index abc4dd9b3c16..cfbba27e12b9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -145,6 +145,169 @@ static void addNoBuiltinAttributes(mlir::MLIRContext &ctx, mlir::ArrayAttr::get(&ctx, nbFuncs.getArrayRef())); } +/// Add denormal-fp-math and denormal-fp-math-f32 as appropriate for the +/// requested denormal behavior, accounting for the overriding behavior of the +/// -f32 case. +static void addDenormalModeAttrs(llvm::DenormalMode fpDenormalMode, + llvm::DenormalMode fp32DenormalMode, + mlir::NamedAttrList &attrs) { + // TODO(cir): Classic-codegen sets the denormal modes here. There are two + // values, both with a string, but it seems that perhaps we could combine + // these into a single attribute? It seems a little silly to have two so + // similar named attributes that do the same thing. +} + +/// Add default attributes to a function, which have merge semantics under +/// -mlink-builtin-bitcode and should not simply overwrite any existing +/// attributes in the linked library. +static void +addMergeableDefaultFunctionAttributes(const CodeGenOptions &codeGenOpts, + mlir::NamedAttrList &attrs) { + addDenormalModeAttrs(codeGenOpts.FPDenormalMode, codeGenOpts.FP32DenormalMode, + attrs); +} + +static llvm::StringLiteral +getZeroCallUsedRegsKindStr(llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind k) { + switch (k) { + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::Skip: + llvm_unreachable("No string value, shouldn't be able to get here"); + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::UsedGPRArg: + return "used-gpr-arg"; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::UsedGPR: + return "used-gpr"; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::UsedArg: + return "used-arg"; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::Used: + return "used"; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::AllGPRArg: + return "all-gpr-arg"; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::AllGPR: + return "all-gpr"; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::AllArg: + return "all-arg"; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::All: + return "all"; + } + + llvm_unreachable("Unknown kind?"); +} + +/// Add default attributes to a function, which have merge semantics under +/// -mlink-builtin-bitcode and should not simply overwrite any existing +/// attributes in the linked library. +static void addTrivialDefaultFunctionAttributes( + mlir::MLIRContext *mlirCtx, StringRef name, bool hasOptNoneAttr, + const CodeGenOptions &codeGenOpts, const LangOptions &langOpts, + bool attrOnCallSite, mlir::NamedAttrList &attrs) { + // TODO(cir): Handle optimize attribute flag here. + // OptimizeNoneAttr takes precedence over -Os or -Oz. No warning needed. + if (!hasOptNoneAttr) { + if (codeGenOpts.OptimizeSize) + attrs.set(cir::CIRDialect::getOptimizeForSizeAttrName(), + mlir::UnitAttr::get(mlirCtx)); + if (codeGenOpts.OptimizeSize == 2) + attrs.set(cir::CIRDialect::getMinSizeAttrName(), + mlir::UnitAttr::get(mlirCtx)); + } + + // TODO(cir): Classic codegen adds 'DisableRedZone', 'indirect-tls-seg-refs' + // and 'NoImplicitFloat' here. + + if (attrOnCallSite) { + // Add the 'nobuiltin' tag, which is different from 'no-builtins'. + if (!codeGenOpts.SimplifyLibCalls || langOpts.isNoBuiltinFunc(name)) + attrs.set(cir::CIRDialect::getNoBuiltinAttrName(), + mlir::UnitAttr::get(mlirCtx)); + + if (!codeGenOpts.TrapFuncName.empty()) + attrs.set(cir::CIRDialect::getTrapFuncNameAttrName(), + mlir::StringAttr::get(mlirCtx, codeGenOpts.TrapFuncName)); + } else { + // TODO(cir): Set frame pointer attribute here. + // TODO(cir): a number of other attribute 1-offs based on codegen/lang opts + // should be done here: less-recise-fpmad null-pointer-is-valid + // no-trapping-math + // various inf/nan/nsz/etc work here. + // + // TODO(cir): set stack-protector buffer size attribute (sorted oddly in + // classic compiler inside of the above region, but should be done on its + // own). + // TODO(cir): other attributes here: + // reciprocal estimates, prefer-vector-width, stackrealign, backchain, + // split-stack, speculative-load-hardening. + + if (codeGenOpts.getZeroCallUsedRegs() == + llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::Skip) + attrs.erase(cir::CIRDialect::getZeroCallUsedRegsAttrName()); + else + attrs.set(cir::CIRDialect::getZeroCallUsedRegsAttrName(), + mlir::StringAttr::get(mlirCtx, + getZeroCallUsedRegsKindStr( + codeGenOpts.getZeroCallUsedRegs()))); + } + + if (langOpts.assumeFunctionsAreConvergent()) { + // Conservatively, mark all functions and calls in CUDA and OpenCL as + // convergent (meaning, they may call an intrinsically convergent op, such + // as __syncthreads() / barrier(), and so can't have certain optimizations + // applied around them). LLVM will remove this attribute where it safely + // can. + attrs.set(cir::CIRDialect::getConvergentAttrName(), + mlir::UnitAttr::get(mlirCtx)); + } + + // TODO(cir): Classic codegen adds 'nounwind' here in a bunch of offload + // targets. + + if (codeGenOpts.SaveRegParams && !attrOnCallSite) + attrs.set(cir::CIRDialect::getSaveRegParamsAttrName(), + mlir::UnitAttr::get(mlirCtx)); + + // These come in the form of an optional equality sign, so make sure we pass + // these on correctly. These will eventually just be passed through to + // LLVM-IR, but we want to put them all in 1 array to simplify the + // LLVM-MLIR dialect. + SmallVector defaultFuncAttrs; + llvm::transform( + codeGenOpts.DefaultFunctionAttrs, std::back_inserter(defaultFuncAttrs), + [mlirCtx](llvm::StringRef arg) { + auto [var, value] = arg.split('='); + auto valueAttr = + value.empty() + ? cast(mlir::UnitAttr::get(mlirCtx)) + : cast(mlir::StringAttr::get(mlirCtx, value)); + return mlir::NamedAttribute(var, valueAttr); + }); + + if (!defaultFuncAttrs.empty()) + attrs.set(cir::CIRDialect::getDefaultFuncAttrsAttrName(), + mlir::DictionaryAttr::get(mlirCtx, defaultFuncAttrs)); + + // TODO(cir): Do branch protection attributes here. +} + +/// This function matches the behavior of 'getDefaultFunctionAttributes' from +/// classic codegen, despite the similarity of its name to +/// 'addDefaultFunctionDefinitionAttributes', which is a caller of this +/// function. +void CIRGenModule::addDefaultFunctionAttributes(StringRef name, + bool hasOptNoneAttr, + bool attrOnCallSite, + mlir::NamedAttrList &attrs) { + + addTrivialDefaultFunctionAttributes(&getMLIRContext(), name, hasOptNoneAttr, + codeGenOpts, langOpts, attrOnCallSite, + attrs); + + if (!attrOnCallSite) { + // TODO(cir): Classic codegen adds pointer-auth attributes here, by calling + // into TargetCodeGenInfo. At the moment, we've not looked into this as it + // is somewhat less used. + addMergeableDefaultFunctionAttributes(codeGenOpts, attrs); + } +} + /// Construct the CIR attribute list of a function or call. void CIRGenModule::constructAttributeList(llvm::StringRef name, const CIRGenFunctionInfo &info, @@ -244,9 +407,6 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name, // TODO(cir): Implement 'BPFFastCall' attribute here. This requires C, and // the BPF target. - // TODO(cir): Detecting 'OptimizeNone' is done here in classic codegen, when - // we figure out when to do that, we should do it here. - if (auto *allocSizeAttr = targetDecl->getAttr()) { unsigned size = allocSizeAttr->getElemSizeParam().getLLVMIndex(); @@ -283,11 +443,44 @@ void CIRGenModule::constructAttributeList(llvm::StringRef name, addNoBuiltinAttributes(getMLIRContext(), attrs, getLangOpts(), nba); - // TODO(cir): We should set default function attrs here. + bool hasOptNoneAttr = targetDecl && targetDecl->hasAttr(); + addDefaultFunctionAttributes(name, hasOptNoneAttr, attrOnCallSite, attrs); + if (targetDecl) { + // TODO(cir): There is another region of `if (targetDecl)` that handles + // removing some attributes that are necessary modifications of the + // default-function attrs. Including: + // NoSpeculativeLoadHardening + // SpeculativeLoadHardening + // NoSplitStack + // Non-lazy-bind + // 'sample-profile-suffix-elision-policy'. + + if (targetDecl->hasAttr()) { + // A function "__attribute__((...))" overrides the command-line flag. + auto kind = + targetDecl->getAttr()->getZeroCallUsedRegs(); + attrs.set( + cir::CIRDialect::getZeroCallUsedRegsAttrName(), + mlir::StringAttr::get( + &getMLIRContext(), + ZeroCallUsedRegsAttr::ConvertZeroCallUsedRegsKindToStr(kind))); + } + + if (targetDecl->hasAttr()) + attrs.erase(cir::CIRDialect::getConvergentAttrName()); + } + + // TODO(cir): A bunch of non-call-site function IR attributes from + // declaration-specific information, including tail calls, + // cmse_nonsecure_entry, additional/automatic 'returns-twice' functions, + // CPU-features/overrides, and hotpatch support. + + // TODO(cir): Add loader-replaceable attribute here. + + // TODO(cir): Ret attrs. + // + // TODO(cir): Arg attrs. - // TODO(cir): There is another region of `if (targetDecl)` that handles - // removing some attributes that are necessary modifications of the - // default-function attrs. We should do that here. assert(!cir::MissingFeatures::opCallAttrs()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 88b66129a834..4444092b5846 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -283,6 +283,12 @@ public: cir::CallingConv &callingConv, cir::SideEffect &sideEffect, bool attrOnCallSite, bool isThunk); + /// Helper function for constructAttributeList/others. Builds a set of + /// function attributes to add to a function based on language opts, codegen + /// opts, and some small properties. + void addDefaultFunctionAttributes(StringRef name, bool hasOptNoneAttr, + bool attrOnCallSite, + mlir::NamedAttrList &attrs); /// Will return a global variable of the given type. If a variable with a /// different type already exists then a new variable with the right type diff --git a/clang/test/CIR/CodeGen/default-func-attrs-cmd-line.cpp b/clang/test/CIR/CodeGen/default-func-attrs-cmd-line.cpp new file mode 100644 index 000000000000..df1e9fa7d9b1 --- /dev/null +++ b/clang/test/CIR/CodeGen/default-func-attrs-cmd-line.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -default-function-attr "key=value" -default-function-attr "just_key" -default-function-attr "key-2=1" -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -default-function-attr "key=value" -default-function-attr "just_key" -default-function-attr "key-2=1" -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -default-function-attr "key=value" -default-function-attr "just_key" -default-function-attr "key-2=1" -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + +extern "C" { +// CIR: cir.func{{.*}}@func() attributes { +// CIR-SAME: default_func_attrs = {just_key, key = "value", "key-2" = "1"} +// LLVM: define{{.*}}@func() #[[FUNC_ATTRS:.*]] { +void func() {} + +void caller() { + func(); + // CIR: cir.call @func() + // CIR-SAME: default_func_attrs = {just_key, key = "value", "key-2" = "1"} + // LLVM: call void{{.*}}@func() #[[FUNC_CALL_ATTRS:.*]] +} +} + +// LLVM: attributes #[[FUNC_ATTRS]] = +// LLVM-SAME: "just_key" +// LLVM-SAME: "key"="value" +// LLVM-SAME: "key-2"="1" +// LLVM: attributes #[[FUNC_CALL_ATTRS]] = +// LLVM-SAME: "just_key" +// LLVM-SAME: "key"="value" +// LLVM-SAME: "key-2"="1" diff --git a/clang/test/CIR/CodeGen/no-builtin-attr-automatic.cpp b/clang/test/CIR/CodeGen/no-builtin-attr-automatic.cpp new file mode 100644 index 000000000000..7cdda57b515c --- /dev/null +++ b/clang/test/CIR/CodeGen/no-builtin-attr-automatic.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-builtin-memset -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefixes=CIR,CIR-STD +// +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-builtin-memset -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-STD +// +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-builtin-memset -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-STD +// +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-builtin-memset -fno-builtin -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefixes=CIR,CIR-NB +// +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-builtin-memset -fno-builtin -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-NB +// +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fno-builtin-memset -fno-builtin -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefixes=LLVM,LLVM-NB + +extern "C" { +__attribute__((hot)) +__attribute__((no_builtin)) +void no_builtin() {} +// CIR: cir.func{{.*}}@no_builtin() +// CIR-SAME: nobuiltins = [] +// LLVM: define{{.*}}@no_builtin() #[[NO_BUILTIN_ATTRS:.*]] { + +__attribute__((cold)) +__attribute__((no_builtin("memcpy"))) +void no_memcpy() {} +// CIR: cir.func{{.*}}@no_memcpy() +// CIR-STD-SAME: nobuiltins = ["memset", "memcpy"] +// CIR-NB-SAME: nobuiltins = [] +// LLVM: define{{.*}}@no_memcpy() #[[NO_MEMCPY_ATTRS:.*]] { + +__attribute__((noduplicate)) +void memset() {} +// CIR: cir.func{{.*}}@memset() +// CIR-STD-SAME: nobuiltins = ["memset"] +// CIR-NB-SAME: nobuiltins = [] +// LLVM: define{{.*}}@memset() #[[MEMSET_ATTRS:.*]] { + +void caller() { + no_builtin(); + // CIR: cir.call @no_builtin() + // CIR-NB-SAME: nobuiltin + // CIR-SAME: nobuiltins = [] + // LLVM: call void @no_builtin() #[[NO_BUILTIN_CALL_ATTRS:.*]] + no_memcpy(); + // CIR: cir.call @no_memcpy() + // CIR-STD-SAME: nobuiltins = ["memset", "memcpy"] + // CIR-NB-SAME: nobuiltin + // CIR-NB-SAME: nobuiltins = [] + // LLVM: call void @no_memcpy() #[[NO_MEMCPY_CALL_ATTRS:.*]] + memset(); + // CIR: cir.call @memset() + // CIR-STD-SAME: nobuiltins = ["memset"] + // CIR-NB-SAME: nobuiltin + // CIR-NB-SAME: nobuiltins = [] + // LLVM: call void @memset() #[[MEMSET_CALL_ATTRS:.*]] +} +} + +// LLVM: attributes #[[NO_BUILTIN_ATTRS]] +// LLVM-SAME: no-builtins +// LLVM: attributes #[[NO_MEMCPY_ATTRS]] +// LLVM-STD-SAME: no-builtin-memcpy +// LLVM-STD-SAME: no-builtin-memset +// LLVM-NB-SAME: no-builtins +// LLVM: attributes #[[MEMSET_ATTRS]] +// LLVM-STD-SAME: no-builtin-memset +// LLVM-NB-SAME: no-builtins +// LLVM: attributes #[[NO_BUILTIN_CALL_ATTRS]] +// LLVM-NB-SAME: nobuiltin +// LLVM-SAME: no-builtins +// LLVM: attributes #[[NO_MEMCPY_CALL_ATTRS]] +// LLVM-STD-SAME: no-builtin-memcpy +// LLVM-STD-SAME: no-builtin-memset +// LLVM-NB-SAME: nobuiltin +// LLVM-NB-SAME: no-builtins +// LLVM: attributes #[[MEMSET_CALL_ATTRS]] +// LLVM-STD-SAME: no-builtin-memset +// LLVM-NB-SAME: nobuiltin +// LLVM-NB-SAME: no-builtins diff --git a/clang/test/CIR/CodeGen/offload-convergent-attr.cu b/clang/test/CIR/CodeGen/offload-convergent-attr.cu new file mode 100644 index 000000000000..1112ca361432 --- /dev/null +++ b/clang/test/CIR/CodeGen/offload-convergent-attr.cu @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcuda-is-device -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcuda-is-device -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcuda-is-device -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + +extern "C" { +__attribute__((device)) +void normal() {} +// CIR: cir.func{{.*}}@normal() +// CIR-SAME: convergent +// LLVM: define {{.*}}@normal(){{.*}} #[[NORMAL_ATTR:.*]] { + +__attribute__((hot)) +__attribute__((device)) +__attribute__((noconvergent)) +void no_conv() {} +// CIR: cir.func{{.*}}@no_conv() +// CIR-NOT: convergent +// LLVM: define {{.*}}@no_conv(){{.*}} #[[NO_CONV_ATTR:.*]] { + +// CIR: cir.func{{.*}}@caller +__attribute__((device)) +void caller() { + normal(); + // CIR: cir.call{{.*}}@normal() + // CIR-SAME: convergent + // LLVM: call void{{.*}}@normal() #[[NORMAL_CALL_ATTR:.*]] + no_conv(); + // CIR: cir.call{{.*}}@no_conv() + // CIR-NOT: convergent + // CIR: cir.return + // LLVM: call void{{.*}}@no_conv() #[[NO_CONV_CALL_ATTR:.*]] +} +} + +// LLVM: attributes #[[NORMAL_ATTR]] +// LLVM-SAME: convergent +// LLVM: attributes #[[NO_CONV_ATTR]] +// LLVM-NOT: convergent +// LLVM: attributes #[[NORMAL_CALL_ATTR]] +// LLVM-SAME: convergent +// LLVM: attributes #[[NO_CONV_CALL_ATTR]] +// LLVM-NOT: convergent diff --git a/clang/test/CIR/CodeGen/optsize-func-attr.cpp b/clang/test/CIR/CodeGen/optsize-func-attr.cpp new file mode 100644 index 000000000000..28441b855858 --- /dev/null +++ b/clang/test/CIR/CodeGen/optsize-func-attr.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-llvm-passes -Os -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-llvm-passes -Os -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,BOTH +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-llvm-passes -Os -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG,BOTH +// +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-llvm-passes -Oz -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR,CIROZ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-llvm-passes -Oz -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,BOTH,BOTHOZ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -disable-llvm-passes -Oz -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG,OGCGOZ,BOTH,BOTHOZ + +extern "C" { + __attribute__((hot)) + void normal(){} + // CIR: cir.func{{.*}}@normal() + // CIROZ-SAME: minsize + // CIR-SAME: optsize + // BOTH: define{{.*}}@normal(){{.*}} #[[NORMAL_ATTR:.*]] { + + __attribute__((cold)) + __attribute__((optnone)) + void optnone(){} + // CIR: cir.func{{.*}}@optnone() + // CIR-NOT: optsize + // CIR-NOT: minsize + // BOTH: define{{.*}}@optnone(){{.*}} #[[OPTNONE_ATTR:.*]] { + + // CIR: cir.func{{.*}}@caller() + void caller() { + normal(); + // CIR: cir.call{{.*}}@normal() + // CIROZ-SAME: minsize + // CIR-SAME: optsize + // LLVM: call void @normal() #[[NORMAL_ATTR]] + // OGCG: call void @normal() #[[NORMAL_CALL_ATTR:.*]] + optnone(); + // CIR: cir.call{{.*}}@optnone() + // CIR-NOT: optsize + // CIR-NOT: minsize + // LLVM: call void @optnone() #[[OPTNONE_ATTR]] + // OGCG: call void @optnone() #[[OPTNONE_CALL_ATTR:.*]] + + // CIR: cir.return + } +} + +// BOTH: attributes #[[NORMAL_ATTR]] +// BOTHOZ-SAME: minsize +// BOTH-SAME: optsize +// +// BOTH: attributes #[[OPTNONE_ATTR]] +// BOTH-NOT: optsize +// BOTH-NOT: minsize +// +// attributes for caller, to block the 'NOT'. +// BOTH: attributes +// +// CIR doesn't have sufficiently different 'attributes' implemented for the +// caller and the callee to be different when doing -O settings (as 'optnone' +// is the only difference). So the below call attributes are only necessary +// for classic codegen. +// OGCG: attributes #[[NORMAL_CALL_ATTR]] +// OGCGOZ-SAME: minsize +// OGCG-SAME: optsize +// +// OGCG: attributes #[[OPTNONE_CALL_ATTR]] +// OGCG-NOT: optsize +// OGCG-NOT: minsize +// +// to block the 'NOT'. +// BOTH: llvm.module.flags diff --git a/clang/test/CIR/CodeGen/save-reg-params-func-attr.cpp b/clang/test/CIR/CodeGen/save-reg-params-func-attr.cpp new file mode 100644 index 000000000000..61bf1bdf3128 --- /dev/null +++ b/clang/test/CIR/CodeGen/save-reg-params-func-attr.cpp @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -msave-reg-params -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -msave-reg-params -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -msave-reg-params -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + +extern "C" { + + __attribute__((hot)) + void func(){} + // CIR: cir.func{{.*}}@func() + // CIR-SAME: save_reg_params + // LLVM: define{{.*}}@func() #[[FUNC_ATTRS:.*]] { + + void caller() { + func(); + // CIR: cir.call{{.*}}@func() + // CIR-NOT: save_reg_params + // CIR: cir.return + // LLVM: call void{{.*}}@func() #[[CALL_ATTRS:.*]] + + } +} + +// LLVM: attributes #[[FUNC_ATTRS]] +// LLVM-SAME: "save-reg-params" +// LLVM: attributes #[[CALL_ATTRS]] +// LLVM-NOT: "save-reg-params" diff --git a/clang/test/CIR/CodeGen/trap-func-name-attr.cpp b/clang/test/CIR/CodeGen/trap-func-name-attr.cpp new file mode 100644 index 000000000000..38afc6b3b11d --- /dev/null +++ b/clang/test/CIR/CodeGen/trap-func-name-attr.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ftrap-function=trap_func -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ftrap-function=trap_func -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ftrap-function=trap_func -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM + +extern "C" { + void normal() {} + // CIR: cir.func{{.*}}@normal() + // CIR-NOT: trap_func_name + // LLVM: define{{.*}}@normal() #[[FUNC_ATTR:.*]] { + void trap_func(){} + // CIR: cir.func{{.*}}@trap_func() + // CIR-NOT: trap_func_name + // LLVM: define{{.*}}@trap_func() #[[FUNC_ATTR]] { + + void caller() { + normal(); + // CIR: cir.call{{.*}}normal() + // CIR-SAME: trap_func_name = "trap_func" + // LLVM: call void{{.*}} @normal() #[[CALL_ATTR:.*]] + trap_func(); + // CIR: cir.call{{.*}}trap_func() + // CIR-SAME: trap_func_name = "trap_func" + // LLVM: call void{{.*}} @trap_func() #[[CALL_ATTR]] + } +} + +// LLVM: attributes #[[FUNC_ATTR]] +// LLVM-NOT: trap-func-name +// LLVM: attributes #[[CALL_ATTR]] +// LLVM-SAME: "trap-func-name"="trap_func" diff --git a/clang/test/CIR/CodeGen/zero-call-used-regs-func-attr.cpp b/clang/test/CIR/CodeGen/zero-call-used-regs-func-attr.cpp new file mode 100644 index 000000000000..b5953fd63d58 --- /dev/null +++ b/clang/test/CIR/CodeGen/zero-call-used-regs-func-attr.cpp @@ -0,0 +1,102 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR,CIR_NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,LLVM_NONE +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,LLVM_NONE + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fzero-call-used-regs=skip -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR,CIR_SKIP +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fzero-call-used-regs=skip -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,LLVM_SKIP +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fzero-call-used-regs=skip -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,LLVM_SKIP + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fzero-call-used-regs=all-gpr -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR,CIR_ALLGPR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fzero-call-used-regs=all-gpr -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,LLVM_ALLGPR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fzero-call-used-regs=all-gpr -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,LLVM_ALLGPR + +extern "C" { + __attribute__((hot)) + void normal(){} + // CIR: cir.func{{.*}}@normal() + // CIR_NONE-NOT: zero-call-used-regs + // CIR_SKIP-NOT: zero-call-used-regs + // CIR_ALLGPR-SAME: zero_call_used_regs = "all-gpr" + // LLVM: define{{.*}}@normal() #[[NORM_ATTR:.*]] { + + __attribute__((cold)) + __attribute__((zero_call_used_regs("skip"))) + void skip() { } + // CIR: cir.func{{.*}}@skip() + // CIR-SAME: zero_call_used_regs = "skip" + // LLVM: define{{.*}}@skip() #[[SKIP_ATTR:.*]] { + + __attribute__((zero_call_used_regs("all"))) + void all() { } + // CIR: cir.func{{.*}}@all() + // CIR-SAME: zero_call_used_regs = "all" + // LLVM: define{{.*}}@all() #[[ALL_ATTR:.*]] { + + __attribute__((zero_call_used_regs("used"))) + void used() { } + // CIR: cir.func{{.*}}@used() + // CIR-SAME: zero_call_used_regs = "used" + // LLVM: define{{.*}}@used() #[[USED_ATTR:.*]] { + + __attribute__((zero_call_used_regs("used-gpr-arg"))) + void used_gpr_arg() { } + // CIR: cir.func{{.*}}@used_gpr_arg() + // CIR-SAME: zero_call_used_regs = "used-gpr-arg" + // LLVM: define{{.*}}@used_gpr_arg() #[[USED_GPR_ATTR:.*]] { + + void caller() { + normal(); + // CIR: cir.call{{.*}}@normal() + // CIR-NOT: zero-call-used-regs + // LLVM: call void{{.*}}@normal() #[[NORM_CALL_ATTR:.*]] + skip(); + // CIR: cir.call{{.*}}@skip() + // CIR-SAME: zero_call_used_regs = "skip" + // LLVM: call void{{.*}}@skip() #[[SKIP_CALL_ATTR:.*]] + all(); + // CIR: cir.call{{.*}}@all() + // CIR-SAME: zero_call_used_regs = "all" + // LLVM: call void{{.*}}@all() #[[ALL_CALL_ATTR:.*]] + used(); + // CIR: cir.call{{.*}}@used() + // CIR-SAME: zero_call_used_regs = "used" + // LLVM: call void{{.*}}@used() #[[USED_CALL_ATTR:.*]] + used_gpr_arg(); + // CIR: cir.call{{.*}}@used_gpr_arg() + // CIR-SAME: zero_call_used_regs = "used-gpr-arg" + // LLVM: call void{{.*}}@used_gpr_arg() #[[USED_GPR_CALL_ATTR:.*]] + } +} + +// LLVM: attributes #[[NORM_ATTR]] +// LLVM_NONE-NOT: zero-call-used-regs +// LLVM_SKIP-NOT: zero-call-used-regs +// LLVM_ALLGPR-SAME: "zero-call-used-regs"="all-gpr" +// LLVM: attributes #[[SKIP_ATTR]] +// LLVM-SAME: "zero-call-used-regs"="skip" +// LLVM: attributes #[[ALL_ATTR]] +// LLVM-SAME: "zero-call-used-regs"="all" +// LLVM: attributes #[[USED_ATTR]] +// LLVM-SAME: "zero-call-used-regs"="used" +// LLVM: attributes #[[USED_GPR_ATTR]] +// LLVM-SAME: "zero-call-used-regs"="used-gpr-arg" +// +// LLVM: attributes #[[NORM_CALL_ATTR]] +// LLVM-NOT: zero-call-used-regs +// LLVM: attributes #[[SKIP_CALL_ATTR]] +// LLVM-SAME: "zero-call-used-regs"="skip" +// LLVM: attributes #[[ALL_CALL_ATTR]] +// LLVM-SAME: "zero-call-used-regs"="all" +// LLVM: attributes #[[USED_CALL_ATTR]] +// LLVM-SAME: "zero-call-used-regs"="used" +// LLVM: attributes #[[USED_GPR_CALL_ATTR]] +// LLVM-SAME: "zero-call-used-regs"="used-gpr-arg" diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceIntrin.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceIntrin.c index d2e15e3889a2..c720dc031c3d 100644 --- a/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceIntrin.c +++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceIntrin.c @@ -10,7 +10,7 @@ double test_mm512_reduce_add_pd(__m512d __W, double ExtraAddOp){ // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.double, !cir.vector<8 x !cir.double>) -> !cir.double // CIR-LABEL: test_mm512_reduce_add_pd - // CIR: cir.call @_mm512_reduce_add_pd(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.double>) -> !cir.double + // CIR: cir.call @_mm512_reduce_add_pd(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.double>) -> !cir.double // LLVM-LABEL: test_mm512_reduce_add_pd // LLVM: call double @llvm.vector.reduce.fadd.v8f64(double -0.000000e+00, <8 x double> %{{.*}}) @@ -27,7 +27,7 @@ double test_mm512_reduce_mul_pd(__m512d __W, double ExtraMulOp){ // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.double, !cir.vector<8 x !cir.double>) -> !cir.double // CIR-LABEL: test_mm512_reduce_mul_pd - // CIR: cir.call @_mm512_reduce_mul_pd(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.double>) -> !cir.double + // CIR: cir.call @_mm512_reduce_mul_pd(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.double>) -> !cir.double // LLVM-LABEL: test_mm512_reduce_mul_pd // LLVM: call double @llvm.vector.reduce.fmul.v8f64(double 1.000000e+00, <8 x double> %{{.*}}) @@ -45,7 +45,7 @@ float test_mm512_reduce_add_ps(__m512 __W){ // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.float, !cir.vector<16 x !cir.float>) -> !cir.float // CIR-LABEL: test_mm512_reduce_add_ps - // CIR: cir.call @_mm512_reduce_add_ps(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.float>) -> !cir.float + // CIR: cir.call @_mm512_reduce_add_ps(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.float>) -> !cir.float // LLVM-LABEL: test_mm512_reduce_add_ps // LLVM: call float @llvm.vector.reduce.fadd.v16f32(float -0.000000e+00, <16 x float> %{{.*}}) @@ -60,7 +60,7 @@ float test_mm512_reduce_mul_ps(__m512 __W){ // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.float, !cir.vector<16 x !cir.float>) -> !cir.float // CIR-LABEL: test_mm512_reduce_mul_ps - // CIR: cir.call @_mm512_reduce_mul_ps(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.float>) -> !cir.float + // CIR: cir.call @_mm512_reduce_mul_ps(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.float>) -> !cir.float // LLVM-LABEL: test_mm512_reduce_mul_ps // LLVM: call float @llvm.vector.reduce.fmul.v16f32(float 1.000000e+00, <16 x float> %{{.*}}) diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceMinMaxIntrin.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceMinMaxIntrin.c index c724942a7a7f..f61b55b6b27f 100644 --- a/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceMinMaxIntrin.c +++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512-reduceMinMaxIntrin.c @@ -9,7 +9,7 @@ double test_mm512_reduce_max_pd(__m512d __W, double ExtraAddOp){ // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] : (!cir.vector<8 x !cir.double>) -> !cir.double // CIR-LABEL: test_mm512_reduce_max_pd - // CIR: cir.call @_mm512_reduce_max_pd(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.double>) -> !cir.double + // CIR: cir.call @_mm512_reduce_max_pd(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.double>) -> !cir.double // LLVM-LABEL: test_mm512_reduce_max_pd // LLVM: call double @llvm.vector.reduce.fmax.v8f64(<8 x double> %{{.*}}) @@ -26,7 +26,7 @@ double test_mm512_reduce_min_pd(__m512d __W, double ExtraMulOp){ // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] : (!cir.vector<8 x !cir.double>) -> !cir.double // CIR-LABEL: test_mm512_reduce_min_pd - // CIR: cir.call @_mm512_reduce_min_pd(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.double>) -> !cir.double + // CIR: cir.call @_mm512_reduce_min_pd(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.double>) -> !cir.double // LLVM-LABEL: test_mm512_reduce_min_pd // LLVM: call double @llvm.vector.reduce.fmin.v8f64(<8 x double> %{{.*}}) @@ -43,7 +43,7 @@ float test_mm512_reduce_max_ps(__m512 __W){ // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] : (!cir.vector<16 x !cir.float>) -> !cir.float // CIR-LABEL: test_mm512_reduce_max_ps - // CIR: cir.call @_mm512_reduce_max_ps(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.float>) -> !cir.float + // CIR: cir.call @_mm512_reduce_max_ps(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.float>) -> !cir.float // LLVM-LABEL: test_mm512_reduce_max_ps // LLVM: call float @llvm.vector.reduce.fmax.v16f32(<16 x float> %{{.*}}) @@ -58,7 +58,7 @@ float test_mm512_reduce_min_ps(__m512 __W){ // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] : (!cir.vector<16 x !cir.float>) -> !cir.float // CIR-LABEL: test_mm512_reduce_min_ps - // CIR: cir.call @_mm512_reduce_min_ps(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.float>) -> !cir.float + // CIR: cir.call @_mm512_reduce_min_ps(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.float>) -> !cir.float // LLVM-LABEL: test_mm512_reduce_min_ps // LLVM: call float @llvm.vector.reduce.fmin.v16f32(<16 x float> %{{.*}}) diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512fp16-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512fp16-builtins.c index 399dfee75d81..cd6b87d65c90 100644 --- a/clang/test/CIR/CodeGenBuiltins/X86/avx512fp16-builtins.c +++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512fp16-builtins.c @@ -70,7 +70,7 @@ _Float16 test_mm512_reduce_add_ph(__m512h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<32 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm512_reduce_add_ph - // CIR: cir.call @_mm512_reduce_add_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<32 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm512_reduce_add_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<32 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm512_reduce_add_ph // LLVM: call half @llvm.vector.reduce.fadd.v32f16(half 0xH8000, <32 x half> %{{.*}}) @@ -85,7 +85,7 @@ _Float16 test_mm512_reduce_mul_ph(__m512h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<32 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm512_reduce_mul_ph - // CIR: cir.call @_mm512_reduce_mul_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<32 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm512_reduce_mul_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<32 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm512_reduce_mul_ph // LLVM: call half @llvm.vector.reduce.fmul.v32f16(half 0xH3C00, <32 x half> %{{.*}}) @@ -100,7 +100,7 @@ _Float16 test_mm512_reduce_max_ph(__m512h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] (!cir.vector<32 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm512_reduce_max_ph - // CIR: cir.call @_mm512_reduce_max_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<32 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm512_reduce_max_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<32 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm512_reduce_max_ph // LLVM: call half @llvm.vector.reduce.fmax.v32f16(<32 x half> %{{.*}}) @@ -115,7 +115,7 @@ _Float16 test_mm512_reduce_min_ph(__m512h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] (!cir.vector<32 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm512_reduce_min_ph - // CIR: cir.call @_mm512_reduce_min_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<32 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm512_reduce_min_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<32 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm512_reduce_min_ph // LLVM: call half @llvm.vector.reduce.fmin.v32f16(<32 x half> %{{.*}}) diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512vlbf16-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512vlbf16-builtins.c index 1c8d68c1ab69..f85488aead8f 100644 --- a/clang/test/CIR/CodeGenBuiltins/X86/avx512vlbf16-builtins.c +++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512vlbf16-builtins.c @@ -9,7 +9,7 @@ __m256bh test_mm512_mask_cvtneps_pbh(__m256bh src, __mmask16 k, __m512 a) { // CIR-LABEL: test_mm512_mask_cvtneps_pbh - // CIR: cir.call @_mm512_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.bf16>, !u16i, !cir.vector<16 x !cir.float>) -> !cir.vector<16 x !cir.bf16> + // CIR: cir.call @_mm512_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.bf16>, !u16i, !cir.vector<16 x !cir.float>) -> !cir.vector<16 x !cir.bf16> // LLVM-LABEL: @test_mm512_mask_cvtneps_pbh // LLVM: call <16 x bfloat> @llvm.x86.avx512bf16.cvtneps2bf16.512 @@ -21,7 +21,7 @@ __m256bh test_mm512_mask_cvtneps_pbh(__m256bh src, __mmask16 k, __m512 a) { __m256bh test_mm512_maskz_cvtneps_pbh(__mmask16 k, __m512 a) { // CIR-LABEL: test_mm512_maskz_cvtneps_pbh - // CIR: cir.call @_mm512_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltins = [{{.*}}]} : (!u16i, !cir.vector<16 x !cir.float>) -> !cir.vector<16 x !cir.bf16> + // CIR: cir.call @_mm512_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltin, nobuiltins = [{{.*}}]} : (!u16i, !cir.vector<16 x !cir.float>) -> !cir.vector<16 x !cir.bf16> // LLVM-LABEL: @test_mm512_maskz_cvtneps_pbh // LLVM: call <16 x bfloat> @llvm.x86.avx512bf16.cvtneps2bf16.512(<16 x float> {{.+}}) @@ -34,7 +34,7 @@ __m256bh test_mm512_maskz_cvtneps_pbh(__mmask16 k, __m512 a) { __m128bh test_mm256_mask_cvtneps_pbh(__m128bh src, __mmask8 k, __m256 a) { // CIR-LABEL: test_mm256_mask_cvtneps_pbh - // CIR: cir.call @_mm256_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.bf16>, !u8i, !cir.vector<8 x !cir.float>) -> !cir.vector<8 x !cir.bf16> + // CIR: cir.call @_mm256_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.bf16>, !u8i, !cir.vector<8 x !cir.float>) -> !cir.vector<8 x !cir.bf16> // LLVM-LABEL: @test_mm256_mask_cvtneps_pbh // LLVM: call <8 x bfloat> @llvm.x86.avx512bf16.cvtneps2bf16.256(<8 x float> {{.+}}) @@ -46,7 +46,7 @@ __m128bh test_mm256_mask_cvtneps_pbh(__m128bh src, __mmask8 k, __m256 a) { __m128bh test_mm256_maskz_cvtneps_pbh(__mmask8 k, __m256 a) { // CIR-LABEL: test_mm256_maskz_cvtneps_pbh - // CIR: cir.call @_mm256_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltins = [{{.*}}]} : (!u8i, !cir.vector<8 x !cir.float>) -> !cir.vector<8 x !cir.bf16> + // CIR: cir.call @_mm256_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltin, nobuiltins = [{{.*}}]} : (!u8i, !cir.vector<8 x !cir.float>) -> !cir.vector<8 x !cir.bf16> // LLVM-LABEL: @test_mm256_maskz_cvtneps_pbh // LLVM: call <8 x bfloat> @llvm.x86.avx512bf16.cvtneps2bf16.256(<8 x float> {{.+}}) @@ -58,7 +58,7 @@ __m128bh test_mm256_maskz_cvtneps_pbh(__mmask8 k, __m256 a) { __m128bh test_mm_mask_cvtneps_pbh(__m128bh src, __mmask8 k, __m128 a) { // CIR-LABEL: test_mm_mask_cvtneps_pbh - // CIR: cir.call @_mm_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.bf16>, !u8i, !cir.vector<4 x !cir.float>) -> !cir.vector<8 x !cir.bf16>{{.+}} + // CIR: cir.call @_mm_mask_cvtneps_pbh({{.+}}, {{.+}}, {{.+}}) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.bf16>, !u8i, !cir.vector<4 x !cir.float>) -> !cir.vector<8 x !cir.bf16>{{.+}} // LLVM-LABEL: @test_mm_mask_cvtneps_pbh // LLVM: call <8 x bfloat> @llvm.x86.avx512bf16.mask.cvtneps2bf16.128(<4 x float> {{.+}}, <8 x bfloat> {{.+}}, <4 x i1> {{.+}}) @@ -70,7 +70,7 @@ __m128bh test_mm_mask_cvtneps_pbh(__m128bh src, __mmask8 k, __m128 a) { __m128bh test_mm_maskz_cvtneps_pbh(__mmask8 k, __m128 a) { // CIR-LABEL: test_mm_maskz_cvtneps_pbh - // CIR: cir.call @_mm_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltins = [{{.*}}]} : (!u8i, !cir.vector<4 x !cir.float>) -> !cir.vector<8 x !cir.bf16> + // CIR: cir.call @_mm_maskz_cvtneps_pbh({{.+}}, {{.+}}) {nobuiltin, nobuiltins = [{{.*}}]} : (!u8i, !cir.vector<4 x !cir.float>) -> !cir.vector<8 x !cir.bf16> // LLVM-LABEL: @test_mm_maskz_cvtneps_pbh // LLVM: call <8 x bfloat> @llvm.x86.avx512bf16.mask.cvtneps2bf16.128(<4 x float> {{.+}}, <8 x bfloat> {{.+}}, <4 x i1> {{.+}}) diff --git a/clang/test/CIR/CodeGenBuiltins/X86/avx512vlfp16-builtins.c b/clang/test/CIR/CodeGenBuiltins/X86/avx512vlfp16-builtins.c index d3eaa8c66d27..a9b5c74ba9af 100644 --- a/clang/test/CIR/CodeGenBuiltins/X86/avx512vlfp16-builtins.c +++ b/clang/test/CIR/CodeGenBuiltins/X86/avx512vlfp16-builtins.c @@ -12,7 +12,7 @@ _Float16 test_mm256_reduce_add_ph(__m256h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<16 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm256_reduce_add_ph - // CIR: cir.call @_mm256_reduce_add_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm256_reduce_add_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm256_reduce_add_ph // LLVM: call half @llvm.vector.reduce.fadd.v16f16(half 0xH8000, <16 x half> %{{.*}}) @@ -27,7 +27,7 @@ _Float16 test_mm256_reduce_mul_ph(__m256h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<16 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm256_reduce_mul_ph - // CIR: cir.call @_mm256_reduce_mul_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm256_reduce_mul_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm256_reduce_mul_ph // LLVM: call half @llvm.vector.reduce.fmul.v16f16(half 0xH3C00, <16 x half> %{{.*}}) @@ -42,7 +42,7 @@ _Float16 test_mm256_reduce_max_ph(__m256h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] (!cir.vector<16 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm256_reduce_max_ph - // CIR: cir.call @_mm256_reduce_max_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm256_reduce_max_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm256_reduce_max_ph // LLVM: call half @llvm.vector.reduce.fmax.v16f16(<16 x half> %{{.*}}) @@ -57,7 +57,7 @@ _Float16 test_mm256_reduce_min_ph(__m256h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] : (!cir.vector<16 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm256_reduce_min_ph - // CIR: cir.call @_mm256_reduce_min_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm256_reduce_min_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<16 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm256_reduce_min_ph // LLVM: call half @llvm.vector.reduce.fmin.v16f16(<16 x half> %{{.*}}) @@ -72,7 +72,7 @@ _Float16 test_mm_reduce_add_ph(__m128h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fadd" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<8 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm_reduce_add_ph - // CIR: cir.call @_mm_reduce_add_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm_reduce_add_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm_reduce_add_ph // LLVM: call half @llvm.vector.reduce.fadd.v8f16(half 0xH8000, <8 x half> %{{.*}}) @@ -87,7 +87,7 @@ _Float16 test_mm_reduce_mul_ph(__m128h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmul" %[[R:.*]], %[[V:.*]] : (!cir.f16, !cir.vector<8 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm_reduce_mul_ph - // CIR: cir.call @_mm_reduce_mul_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm_reduce_mul_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm_reduce_mul_ph // LLVM: call half @llvm.vector.reduce.fmul.v8f16(half 0xH3C00, <8 x half> %{{.*}}) @@ -102,7 +102,7 @@ _Float16 test_mm_reduce_max_ph(__m128h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmax" %[[V:.*]] (!cir.vector<8 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm_reduce_max_ph - // CIR: cir.call @_mm_reduce_max_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm_reduce_max_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm_reduce_max_ph // LLVM: call half @llvm.vector.reduce.fmax.v8f16(<8 x half> %{{.*}}) @@ -117,7 +117,7 @@ _Float16 test_mm_reduce_min_ph(__m128h __W) { // CIR: cir.call_llvm_intrinsic "vector.reduce.fmin" %[[V:.*]] : (!cir.vector<8 x !cir.f16>) -> !cir.f16 // CIR-LABEL: test_mm_reduce_min_ph - // CIR: cir.call @_mm_reduce_min_ph(%[[VEC:.*]]) {nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.f16>) -> !cir.f16 + // CIR: cir.call @_mm_reduce_min_ph(%[[VEC:.*]]) {nobuiltin, nobuiltins = [{{.*}}]} : (!cir.vector<8 x !cir.f16>) -> !cir.f16 // LLVM-LABEL: test_mm_reduce_min_ph // LLVM: call half @llvm.vector.reduce.fmin.v8f16(<8 x half> %{{.*}}) diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index c57d0a15c404..0cdad9ab6045 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -801,6 +801,11 @@ def LLVM_CallOp OptionalAttr:$modular_format, OptionalAttr:$nobuiltins, OptionalAttr:$allocsize, + UnitAttr:$optsize, UnitAttr:$minsize, + UnitAttr:$nobuiltin, UnitAttr:$save_reg_params, + OptionalAttr:$zero_call_used_regs, + OptionalAttr:$trap_func_name, + OptionalAttr:$default_func_attrs, VariadicOfVariadic:$op_bundle_operands, DenseI32ArrayAttr:$op_bundle_sizes, OptionalAttr:$op_bundle_tags, @@ -2008,6 +2013,11 @@ def LLVM_LLVMFuncOp : LLVM_Op<"func", [ OptionalAttr:$modular_format, OptionalAttr:$nobuiltins, OptionalAttr:$allocsize, + OptionalAttr:$optsize, + OptionalAttr:$minsize, + OptionalAttr:$save_reg_params, + OptionalAttr:$zero_call_used_regs, + OptionalAttr:$default_func_attrs, OptionalAttr:$vec_type_hint, OptionalAttr:$work_group_size_hint, OptionalAttr:$reqd_work_group_size, diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h index 349c0f8810a4..c67bb57985bd 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -351,22 +351,25 @@ public: // A helper callback that takes an attribute, and if it is a StringAttr, // properly converts it to the 'no-builtin-VALUE' form. - static std::optional convertNoBuiltin(mlir::Attribute a) { - if (auto str = dyn_cast(a)) - return ("no-builtin-" + str.getValue()).str(); - return std::nullopt; - } + static std::optional convertNoBuiltin(llvm::LLVMContext &ctx, + mlir::Attribute a); - /// A template that takes an ArrayAttr, converts it via a user provided - /// callback, then adds each element to as function attributes to the provided - /// operation. - template - void convertFunctionArrayAttr(ArrayAttr array, Operation *op, - const Converter &conv) { - for (Attribute a : array) { - auto result = conv(a); + static std::optional + convertDefaultFuncAttr(llvm::LLVMContext &ctx, + mlir::NamedAttribute namedAttr); + + /// A template that takes a collection-like attribute, and converts it via a + /// user provided callback, then adds each element as function attributes to + /// the provided operation. + template + void convertFunctionAttrCollection(AttrsTy attrs, Operation *op, + const Converter &conv) { + if (!attrs) + return; + for (auto elt : attrs) { + std::optional result = conv(getLLVMContext(), elt); if (result) - op->addFnAttr(llvm::Attribute::get(getLLVMContext(), *result)); + op->addFnAttr(*result); } } diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index d4573060eca2..4c67720654f8 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -999,7 +999,10 @@ void CallOp::build(OpBuilder &builder, OperationState &state, TypeRange results, /*cold=*/nullptr, /*noduplicate=*/nullptr, /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr, /*modular_format=*/nullptr, /*nobuiltins=*/nullptr, - /*alloc_size=*/nullptr, + /*allocsize=*/nullptr, /*optsize=*/nullptr, /*minsize=*/nullptr, + /*nobuiltin=*/nullptr, /*save_reg_params=*/nullptr, + /*zero_call_used_regs=*/nullptr, /*trap_func_name=*/nullptr, + /*default_func_attrs=*/nullptr, /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{}, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, /*access_groups=*/nullptr, /*alias_scopes=*/nullptr, @@ -1035,7 +1038,10 @@ void CallOp::build(OpBuilder &builder, OperationState &state, /*cold=*/nullptr, /*noduplicate=*/nullptr, /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr, /*modular_format=*/nullptr, /*nobuiltins=*/nullptr, - /*alloc_size=*/nullptr, + /*allocsize=*/nullptr, /*optsize=*/nullptr, /*minsize=*/nullptr, + /*nobuiltin=*/nullptr, /*save_reg_params=*/nullptr, + /*zero_call_used_regs=*/nullptr, /*trap_func_name=*/nullptr, + /*default_func_attrs=*/nullptr, /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{}, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, /*access_groups=*/nullptr, @@ -1057,7 +1063,10 @@ void CallOp::build(OpBuilder &builder, OperationState &state, /*cold=*/nullptr, /*noduplicate=*/nullptr, /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr, /*modular_format=*/nullptr, /*nobuiltins=*/nullptr, - /*alloc_size=*/nullptr, + /*allocsize=*/nullptr, /*optsize=*/nullptr, /*minsize=*/nullptr, + /*nobuiltin=*/nullptr, /*save_reg_params=*/nullptr, + /*zero_call_used_regs=*/nullptr, /*trap_func_name=*/nullptr, + /*default_func_attrs=*/nullptr, /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{}, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, /*access_groups=*/nullptr, /*alias_scopes=*/nullptr, @@ -1079,7 +1088,10 @@ void CallOp::build(OpBuilder &builder, OperationState &state, LLVMFuncOp func, /*cold=*/nullptr, /*noduplicate=*/nullptr, /*no_caller_saved_registers=*/nullptr, /*nocallback=*/nullptr, /*modular_format=*/nullptr, /*nobuiltins=*/nullptr, - /*alloc_size=*/nullptr, + /*allocsize=*/nullptr, /*optsize=*/nullptr, /*minsize=*/nullptr, + /*nobuiltin=*/nullptr, /*save_reg_params=*/nullptr, + /*zero_call_used_regs=*/nullptr, /*trap_func_name=*/nullptr, + /*default_func_attrs=*/nullptr, /*op_bundle_operands=*/{}, /*op_bundle_tags=*/{}, /*access_groups=*/nullptr, /*alias_scopes=*/nullptr, /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp index 93a8e00d40e3..36cbcd370364 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp @@ -423,6 +423,15 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder, call->addFnAttr(llvm::Attribute::WillReturn); if (callOp.getNoreturnAttr()) call->addFnAttr(llvm::Attribute::NoReturn); + if (callOp.getOptsizeAttr()) + call->addFnAttr(llvm::Attribute::OptimizeForSize); + if (callOp.getMinsizeAttr()) + call->addFnAttr(llvm::Attribute::MinSize); + if (callOp.getSaveRegParamsAttr()) + call->addFnAttr(llvm::Attribute::get(moduleTranslation.getLLVMContext(), + "save-reg-params")); + if (callOp.getNobuiltinAttr()) + call->addFnAttr(llvm::Attribute::NoBuiltin); if (callOp.getReturnsTwiceAttr()) call->addFnAttr(llvm::Attribute::ReturnsTwice); if (callOp.getColdAttr()) @@ -446,16 +455,28 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder, call->addFnAttr(llvm::Attribute::get(moduleTranslation.getLLVMContext(), "modular-format", modFormat.getValue())); + if (StringAttr zcsr = callOp.getZeroCallUsedRegsAttr()) + call->addFnAttr(llvm::Attribute::get(moduleTranslation.getLLVMContext(), + "zero-call-used-regs", + zcsr.getValue())); + if (StringAttr trapFunc = callOp.getTrapFuncNameAttr()) + call->addFnAttr(llvm::Attribute::get(moduleTranslation.getLLVMContext(), + "trap-func-name", + trapFunc.getValue())); if (ArrayAttr noBuiltins = callOp.getNobuiltinsAttr()) { if (noBuiltins.empty()) call->addFnAttr(llvm::Attribute::get(moduleTranslation.getLLVMContext(), "no-builtins")); - moduleTranslation.convertFunctionArrayAttr( + moduleTranslation.convertFunctionAttrCollection( noBuiltins, call, ModuleTranslation::convertNoBuiltin); } + moduleTranslation.convertFunctionAttrCollection( + callOp.getDefaultFuncAttrsAttr(), call, + ModuleTranslation::convertDefaultFuncAttr); + if (llvm::Attribute attr = moduleTranslation.convertAllocsizeAttr(callOp.getAllocsizeAttr()); attr.isValid()) diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index 1653c2e358d6..60cb0aab123b 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -2688,6 +2688,7 @@ static constexpr std::array kExplicitLLVMFuncOpAttributes{ StringLiteral("instrument-function-exit"), StringLiteral("modular-format"), StringLiteral("memory"), + StringLiteral("minsize"), StringLiteral("no_caller_saved_registers"), StringLiteral("no-infs-fp-math"), StringLiteral("no-nans-fp-math"), @@ -2699,12 +2700,16 @@ static constexpr std::array kExplicitLLVMFuncOpAttributes{ StringLiteral("noreturn"), StringLiteral("nounwind"), StringLiteral("optnone"), + StringLiteral("optsize"), StringLiteral("returns_twice"), + StringLiteral("save-reg-params"), StringLiteral("target-features"), + StringLiteral("trap-func-name"), StringLiteral("tune-cpu"), StringLiteral("uwtable"), StringLiteral("vscale_range"), StringLiteral("willreturn"), + StringLiteral("zero-call-used-regs"), StringLiteral("denormal_fpenv"), }; @@ -2795,6 +2800,12 @@ void ModuleImport::processFunctionAttributes(llvm::Function *func, funcOp.setWillReturn(true); if (func->hasFnAttribute(llvm::Attribute::NoReturn)) funcOp.setNoreturn(true); + if (func->hasFnAttribute(llvm::Attribute::OptimizeForSize)) + funcOp.setOptsize(true); + if (func->hasFnAttribute("save-reg-params")) + funcOp.setSaveRegParams(true); + if (func->hasFnAttribute(llvm::Attribute::MinSize)) + funcOp.setMinsize(true); if (func->hasFnAttribute(llvm::Attribute::ReturnsTwice)) funcOp.setReturnsTwice(true); if (func->hasFnAttribute(llvm::Attribute::Cold)) @@ -2810,6 +2821,10 @@ void ModuleImport::processFunctionAttributes(llvm::Function *func, if (llvm::Attribute attr = func->getFnAttribute("modular-format"); attr.isStringAttribute()) funcOp.setModularFormat(StringAttr::get(context, attr.getValueAsString())); + if (llvm::Attribute attr = func->getFnAttribute("zero-call-used-regs"); + attr.isStringAttribute()) + funcOp.setZeroCallUsedRegsAttr( + StringAttr::get(context, attr.getValueAsString())); if (func->hasFnAttribute("aarch64_pstate_sm_enabled")) funcOp.setArmStreaming(true); @@ -3024,6 +3039,12 @@ LogicalResult ModuleImport::convertCallAttributes(llvm::CallInst *inst, op.setNoUnwind(callAttrs.getFnAttr(llvm::Attribute::NoUnwind).isValid()); op.setWillReturn(callAttrs.getFnAttr(llvm::Attribute::WillReturn).isValid()); op.setNoreturn(callAttrs.getFnAttr(llvm::Attribute::NoReturn).isValid()); + op.setOptsize( + callAttrs.getFnAttr(llvm::Attribute::OptimizeForSize).isValid()); + op.setSaveRegParams(callAttrs.getFnAttr("save-reg-params").isValid()); + op.setNobuiltin(callAttrs.getFnAttr(llvm::Attribute::NoBuiltin).isValid()); + op.setMinsize(callAttrs.getFnAttr(llvm::Attribute::MinSize).isValid()); + op.setReturnsTwice( callAttrs.getFnAttr(llvm::Attribute::ReturnsTwice).isValid()); op.setHot(callAttrs.getFnAttr(llvm::Attribute::Hot).isValid()); @@ -3037,6 +3058,13 @@ LogicalResult ModuleImport::convertCallAttributes(llvm::CallInst *inst, if (llvm::Attribute attr = callAttrs.getFnAttr("modular-format"); attr.isStringAttribute()) op.setModularFormat(StringAttr::get(context, attr.getValueAsString())); + if (llvm::Attribute attr = callAttrs.getFnAttr("zero-call-used-regs"); + attr.isStringAttribute()) + op.setZeroCallUsedRegsAttr( + StringAttr::get(context, attr.getValueAsString())); + if (llvm::Attribute attr = callAttrs.getFnAttr("trap-func-name"); + attr.isStringAttribute()) + op.setTrapFuncNameAttr(StringAttr::get(context, attr.getValueAsString())); op.setNoInline(callAttrs.getFnAttr(llvm::Attribute::NoInline).isValid()); op.setAlwaysInline( callAttrs.getFnAttr(llvm::Attribute::AlwaysInline).isValid()); diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp index 7115386c18c1..f622ab118a2b 100644 --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -1713,6 +1713,12 @@ static void convertFunctionAttributes(ModuleTranslation &mod, LLVMFuncOp func, llvmFunc->addFnAttr(llvm::Attribute::WillReturn); if (func.getNoreturnAttr()) llvmFunc->addFnAttr(llvm::Attribute::NoReturn); + if (func.getOptsizeAttr()) + llvmFunc->addFnAttr(llvm::Attribute::OptimizeForSize); + if (func.getMinsizeAttr()) + llvmFunc->addFnAttr(llvm::Attribute::MinSize); + if (func.getSaveRegParamsAttr()) + llvmFunc->addFnAttr("save-reg-params"); if (func.getNoCallerSavedRegistersAttr()) llvmFunc->addFnAttr("no_caller_saved_registers"); if (func.getNocallbackAttr()) @@ -1727,15 +1733,20 @@ static void convertFunctionAttributes(ModuleTranslation &mod, LLVMFuncOp func, if (UWTableKindAttr uwTableKindAttr = func.getUwtableKindAttr()) llvmFunc->setUWTableKind( convertUWTableKindToLLVM(uwTableKindAttr.getUwtableKind())); + if (StringAttr zcsr = func.getZeroCallUsedRegsAttr()) + llvmFunc->addFnAttr("zero-call-used-regs", zcsr.getValue()); if (ArrayAttr noBuiltins = func.getNobuiltinsAttr()) { if (noBuiltins.empty()) llvmFunc->addFnAttr("no-builtins"); - mod.convertFunctionArrayAttr(noBuiltins, llvmFunc, - ModuleTranslation::convertNoBuiltin); + mod.convertFunctionAttrCollection(noBuiltins, llvmFunc, + ModuleTranslation::convertNoBuiltin); } + mod.convertFunctionAttrCollection(func.getDefaultFuncAttrsAttr(), llvmFunc, + ModuleTranslation::convertDefaultFuncAttr); + if (llvm::Attribute attr = mod.convertAllocsizeAttr(func.getAllocsizeAttr()); attr.isValid()) llvmFunc->addFnAttr(attr); @@ -1879,6 +1890,26 @@ LogicalResult ModuleTranslation::convertArgAndResultAttrs( return success(); } +std::optional +ModuleTranslation::convertNoBuiltin(llvm::LLVMContext &ctx, mlir::Attribute a) { + if (auto str = dyn_cast(a)) + return llvm::Attribute::get(ctx, ("no-builtin-" + str.getValue()).str()); + return std::nullopt; +} + +std::optional +ModuleTranslation::convertDefaultFuncAttr(llvm::LLVMContext &ctx, + mlir::NamedAttribute namedAttr) { + StringAttr name = namedAttr.getName(); + Attribute value = namedAttr.getValue(); + + if (auto strVal = dyn_cast(value)) + return llvm::Attribute::get(ctx, name.getValue(), strVal.getValue()); + if (mlir::isa(value)) + return llvm::Attribute::get(ctx, name.getValue()); + return std::nullopt; +} + FailureOr ModuleTranslation::convertParameterAttrs(Location loc, DictionaryAttr paramAttrs) { diff --git a/mlir/test/Dialect/LLVMIR/func.mlir b/mlir/test/Dialect/LLVMIR/func.mlir index 8dc7f1ddab11..8af1e5248542 100644 --- a/mlir/test/Dialect/LLVMIR/func.mlir +++ b/mlir/test/Dialect/LLVMIR/func.mlir @@ -396,6 +396,30 @@ module { llvm.return } + llvm.func @minsize_optsize() attributes { minsize, optsize } { + // CHECK: @minsize_optsize + // CHECK-SAME: attributes {minsize, optsize} + llvm.return + } + + llvm.func @save_reg_params() attributes { save_reg_params } { + // CHECK: @save_reg_params + // CHECK-SAME: attributes {save_reg_params} + llvm.return + } + + llvm.func @zero_call_used_regs() attributes { zero_call_used_regs="used-gpr-arg"} { + // CHECK: @zero_call_used_regs + // CHECK-SAME: attributes {zero_call_used_regs = "used-gpr-arg"} + llvm.return + } + + llvm.func @default_func_attrs() attributes {default_func_attrs={key="value",justKey}} { + // CHECK: @default_func_attrs + // CHECK-SAME: attributes {default_func_attrs = {justKey, key = "value"}} + llvm.return + } + } // ----- diff --git a/mlir/test/Dialect/LLVMIR/roundtrip.mlir b/mlir/test/Dialect/LLVMIR/roundtrip.mlir index a39a4e9e18a5..c680d0d98ac5 100644 --- a/mlir/test/Dialect/LLVMIR/roundtrip.mlir +++ b/mlir/test/Dialect/LLVMIR/roundtrip.mlir @@ -161,6 +161,27 @@ func.func @ops(%arg0: i32, %arg1: f32, // CHECK: llvm.call @baz() {allocsize = array} : () -> () llvm.call @baz() {allocsize = array} : () -> () +// CHECK: llvm.call @baz() {minsize} : () -> () + llvm.call @baz() {minsize} : () -> () + +// CHECK: llvm.call @baz() {optsize} : () -> () + llvm.call @baz() {optsize} : () -> () + +// CHECK: llvm.call @baz() {nobuiltin} : () -> () + llvm.call @baz() {nobuiltin} : () -> () + +// CHECK: llvm.call @baz() {save_reg_params} : () -> () + llvm.call @baz() {save_reg_params} : () -> () + +// CHECK: llvm.call @baz() {zero_call_used_regs = "all"} : () -> () + llvm.call @baz() {zero_call_used_regs="all"} : () -> () + +// CHECK: llvm.call @baz() {zero_call_used_regs = "thing"} : () -> () + llvm.call @baz() {zero_call_used_regs="thing"} : () -> () + +// CHECK: llvm.call @baz() {default_func_attrs = {justKey, key = "value"}} : () -> () + llvm.call @baz() {default_func_attrs={justKey, key = "value"}} : () -> () + // Terminator operations and their successors. // // CHECK: llvm.br ^[[BB1:.*]] diff --git a/mlir/test/Target/LLVMIR/Import/function-attributes.ll b/mlir/test/Target/LLVMIR/Import/function-attributes.ll index 6b04f6e5c562..8f3a1f916110 100644 --- a/mlir/test/Target/LLVMIR/Import/function-attributes.ll +++ b/mlir/test/Target/LLVMIR/Import/function-attributes.ll @@ -488,5 +488,37 @@ declare void @alloc_size_2(i32, i32) allocsize(0, 1) // ----- +; CHECK-LABEL: @minsize +; CHECK-SAME: attributes {minsize} +declare void @minsize() minsize + +// ----- + +; CHECK-LABEL: @optsize +; CHECK-SAME: attributes {optsize} +declare void @optsize() optsize + +// ----- + +; CHECK-LABEL: @save_reg_params +; CHECK-SAME: attributes {save_reg_params} +declare void @save_reg_params() "save-reg-params" + +// ----- + +; CHECK-LABEL: @zero_call_used_regs +; CHECK-SAME: attributes {zero_call_used_regs = "skip"} +declare void @zero_call_used_regs() "zero-call-used-regs"="skip" + +// ----- + +; Note: the 'default-func-attrs' aren't recoverable due to the way they lower +; to LLVM-IR, so they are handled on import as passthrough attributes. +; CHECK-LABEL: @default_func_attrs +; CHECK-SAME: attributes {passthrough = {{\[}}["key", "value"], "keyOnly"]} +declare void @default_func_attrs() "key"="value" "keyOnly" + +// ----- + ; expected-warning @unknown {{'preallocated' attribute is invalid on current operation, skipping it}} declare void @test() preallocated(i32) diff --git a/mlir/test/Target/LLVMIR/Import/instructions.ll b/mlir/test/Target/LLVMIR/Import/instructions.ll index a72227f01716..22a274049ecf 100644 --- a/mlir/test/Target/LLVMIR/Import/instructions.ll +++ b/mlir/test/Target/LLVMIR/Import/instructions.ll @@ -840,6 +840,92 @@ define void @call_alloc_size_2() { call void @f(i32 0, i32 0) allocsize(1, 0) ret void } +; // ----- + +; CHECK: llvm.func @f() +declare void @f() + +; CHECK-LABEL: @call_minsize +define void @call_minsize() { +; CHECK: llvm.call @f() {minsize} + call void @f() minsize + ret void +} + +; // ----- + +; CHECK: llvm.func @f() +declare void @f() + +; CHECK-LABEL: @call_optsize +define void @call_optsize() { +; CHECK: llvm.call @f() {optsize} + call void @f() optsize + ret void +} + +; // ----- + +; CHECK: llvm.func @f() +declare void @f() + +; CHECK-LABEL: @call_save_reg_params +define void @call_save_reg_params() { +; CHECK: llvm.call @f() {save_reg_params} + call void @f() "save-reg-params" + ret void +} + +; // ----- + +; CHECK: llvm.func @f() +declare void @f() + +; CHECK-LABEL: @call_zero_call_used_regs +define void @call_zero_call_used_regs() { +; CHECK: llvm.call @f() {zero_call_used_regs = "used"} + call void @f() "zero-call-used-regs"="used" + ret void +} + +; // ----- + +; CHECK: llvm.func @f() +declare void @f() + +; CHECK-LABEL: @call_trap_func_name +define void @call_trap_func_name() { +; CHECK: llvm.call @f() {trap_func_name = "something"} + call void @f() "trap-func-name"="something" + ret void +} + +; // ----- + +; CHECK: llvm.func @f() +declare void @f() + +; Note: the 'default-func-attrs' aren't recoverable due to the way they lower +; to LLVM-IR, and 'call' operations don't have passthrough, so these would be +; lost in translation. +; CHECK-LABEL: @call_default_func_attrs +define void @call_default_func_attrs() { +; CHECK: llvm.call @f() : () -> () + call void @f() "key"="value" "key" + ret void +} + +; // ----- + +; CHECK: llvm.func @f() +declare void @f() + +; CHECK-LABEL: @call_nobuiltin +define void @call_nobuiltin() { +; CHECK: llvm.call @f() {nobuiltin} + call void @f() nobuiltin + ret void +} ; // ----- diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir index f88cbda459e8..4fb5285584a8 100644 --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -2823,6 +2823,196 @@ llvm.func @allocsize_call_2() { // ----- +// CHECK-LABEL: @minsize +// CHECK-SAME: #[[ATTRS:[0-9]+]] +llvm.func @minsize() attributes { minsize } { + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: minsize + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @minsize_call +// CHECK: call void @f() #[[ATTRS:[0-9]+]] +llvm.func @minsize_call() { + llvm.call @f() {minsize} : () -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: minsize + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @optsize +// CHECK-SAME: #[[ATTRS:[0-9]+]] +llvm.func @optsize() attributes { optsize } { + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: optsize + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @optsize_call +// CHECK: call void @f() #[[ATTRS:[0-9]+]] +llvm.func @optsize_call() { + llvm.call @f() {optsize} : () -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: optsize + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @save_reg_params +// CHECK-SAME: #[[ATTRS:[0-9]+]] +llvm.func @save_reg_params() attributes { save_reg_params } { + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "save-reg-params" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @save_reg_params_call +// CHECK: call void @f() #[[ATTRS:[0-9]+]] +llvm.func @save_reg_params_call() { + llvm.call @f() {save_reg_params} : () -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "save-reg-params" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @zero_call_used_regs_1 +// CHECK-SAME: #[[ATTRS:[0-9]+]] +llvm.func @zero_call_used_regs_1() attributes { zero_call_used_regs = "skip"} { + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "zero-call-used-regs"="skip" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @zero_call_used_regs_2 +// CHECK-SAME: #[[ATTRS:[0-9]+]] +llvm.func @zero_call_used_regs_2() attributes { zero_call_used_regs = "all"} { + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "zero-call-used-regs"="all" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @zero_call_used_regs_call_1 +// CHECK: call void @f() #[[ATTRS:[0-9]+]] +llvm.func @zero_call_used_regs_call_1() { + llvm.call @f() {zero_call_used_regs="used_gpr_all"} : () -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "zero-call-used-regs"="used_gpr_all" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @zero_call_used_regs_call_2 +// CHECK: call void @f() #[[ATTRS:[0-9]+]] +llvm.func @zero_call_used_regs_call_2() { + llvm.call @f() {zero_call_used_regs="used"} : () -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "zero-call-used-regs"="used" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @trap_func_name_call +// CHECK: call void @f() #[[ATTRS:[0-9]+]] +llvm.func @trap_func_name_call() { + llvm.call @f() {trap_func_name="whatever"} : () -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "trap-func-name"="whatever" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @default_func_attrs +// CHECK-SAME: #[[ATTRS:[0-9]+]] +llvm.func @default_func_attrs() attributes {default_func_attrs={key="value", justKey}} { + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "justKey" +// CHECK-SAME: "key"="value" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @default_func_attrs +// CHECK: call void @f() #[[ATTRS:[0-9]+]] +llvm.func @default_func_attrs_call() { + llvm.call @f() {default_func_attrs={key="value", justKey}} : () -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: "justKey" +// CHECK-SAME: "key"="value" + +// ----- + +llvm.func @f() + +// CHECK-LABEL: @nobuiltin_call +// CHECK: call void @f() #[[ATTRS:[0-9]+]] +llvm.func @nobuiltin_call() { + llvm.call @f() {nobuiltin} : () -> () + llvm.return +} + +// CHECK: #[[ATTRS]] +// CHECK-SAME: nobuiltin + +// ----- + llvm.func @f() // CHECK-LABEL: @convergent_call