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