diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2544,6 +2544,19 @@ let Documentation = [VectorCallDocs]; } +def ZeroCallUsedRegs : InheritableAttr { + let Spellings = [GCC<"zero_call_used_regs">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Args = [ + EnumArgument<"ZeroCallUsedRegs", "ZeroCallUsedRegsKind", + ["skip", "used-gpr-arg", "used-gpr", "used-arg", "used", + "all-gpr-arg", "all-gpr", "all-arg", "all"], + ["Skip", "UsedGPRArg", "UsedGPR", "UsedArg", "Used", + "AllGPRArg", "AllGPR", "AllArg", "All"]> + ]; + let Documentation = [ZeroCallUsedRegsDocs]; +} + def Pascal : DeclOrTypeAttr { let Spellings = [Clang<"pascal">, Keyword<"__pascal">, Keyword<"_pascal">]; // let Subjects = [Function, ObjCMethod]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -6101,3 +6101,36 @@ } }]; } + +def ZeroCallUsedRegsDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute, when attached to a funcditon, causes the compiler to zero a +subset of all call-used registers before the function returns. It's used to +increase program security by either mitigating `Return-Oriented Programming`_ +(ROP) attacks or preventing information leakage through registers. + +The `choice` parameters gives the programmer flexibility to choose the subset +of the call-used registers to be zeroed: + +- ``skip`` doesn't zero any call-used registers. +- ``used`` only zeros call-used registers used in the function. By ``used``, we + mean a register whose contents have been set or referenced in the function. +- ``used-gpr`` only zeros call-used GPR registers used in the funciton. +- ``used-arg`` only zeros call-used registers used to pass arguments to the + function. +- ``used-gpr-arg`` only zeros call-used GPR registers used to pass arguments to + the function. +- ``all`` zeros all call-used registers. +- ``all-gpr`` zeros all call-used GPR registers. +- ``all-arg`` zeros all call-used registers used to pass arguments to the + function. +- ``all-gpr-arg`` zeros all call-used GPR registers used to pass arguments to + the function. + +The default for the attribute is contolled by the ``-fzero-call-used-regs`` +flag. + +.. _Return-Oriented Programming: https://en.wikipedia.org/wiki/Return-oriented_programming + }]; +} diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -451,6 +451,10 @@ ENUM_CODEGENOPT(SwiftAsyncFramePointer, SwiftAsyncFramePointerKind, 2, SwiftAsyncFramePointerKind::Always) +/// Whether to zero out caller-used registers before returning. +ENUM_CODEGENOPT(ZeroCallUsedRegs, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind, + 5, llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::Skip) + #undef CODEGENOPT #undef ENUM_CODEGENOPT #undef VALUE_CODEGENOPT diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2856,6 +2856,14 @@ HelpText<"Enable matrix data type and related builtin functions">, MarshallingInfoFlag>; +def fzero_call_used_regs_EQ + : Joined<["-"], "fzero-call-used-regs=">, Group, Flags<[CC1Option]>, + HelpText<"Clear call-used registers upon function return.">, + Values<"skip,used-gpr-arg,used-gpr,used-arg,used,all-gpr-arg,all-gpr,all-arg,all">, + NormalizedValues<["Skip", "UsedGPRArg", "UsedGPR", "UsedArg", "Used", + "AllGPRArg", "AllGPR", "AllArg", "All"]>, + NormalizedValuesScope<"llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind">, + MarshallingInfoEnum, "Skip">; def fdebug_types_section: Flag <["-"], "fdebug-types-section">, Group, HelpText<"Place debug types in their own section (ELF Only)">; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1877,6 +1877,37 @@ if (CodeGenOpts.SpeculativeLoadHardening) FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening); + + // Add zero-call-used-regs attribute. + switch (CodeGenOpts.getZeroCallUsedRegs()) { + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::Skip: + FuncAttrs.removeAttribute("zero-call-used-regs"); + break; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::UsedGPRArg: + FuncAttrs.addAttribute("zero-call-used-regs", "used-gpr-arg"); + break; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::UsedGPR: + FuncAttrs.addAttribute("zero-call-used-regs", "used-gpr"); + break; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::UsedArg: + FuncAttrs.addAttribute("zero-call-used-regs", "used-arg"); + break; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::Used: + FuncAttrs.addAttribute("zero-call-used-regs", "used"); + break; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::AllGPRArg: + FuncAttrs.addAttribute("zero-call-used-regs", "all-gpr-arg"); + break; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::AllGPR: + FuncAttrs.addAttribute("zero-call-used-regs", "all-gpr"); + break; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::AllArg: + FuncAttrs.addAttribute("zero-call-used-regs", "all-arg"); + break; + case llvm::ZeroCallUsedRegs::ZeroCallUsedRegsKind::All: + FuncAttrs.addAttribute("zero-call-used-regs", "all"); + break; + } } if (getLangOpts().assumeFunctionsAreConvergent()) { @@ -2166,6 +2197,15 @@ FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening); if (TargetDecl->hasAttr()) FuncAttrs.removeAttribute("split-stack"); + if (TargetDecl->hasAttr()) { + // A function "__attribute__((...))" overrides the command-line flag. + auto Kind = + TargetDecl->getAttr()->getZeroCallUsedRegs(); + FuncAttrs.removeAttribute("zero-call-used-regs"); + FuncAttrs.addAttribute( + "zero-call-used-regs", + ZeroCallUsedRegsAttr::ConvertZeroCallUsedRegsKindToStr(Kind)); + } // Add NonLazyBind attribute to function declarations when -fno-plt // is used. diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5728,6 +5728,7 @@ Args.AddLastArg(CmdArgs, options::OPT_fdigraphs, options::OPT_fno_digraphs); Args.AddLastArg(CmdArgs, options::OPT_femulated_tls, options::OPT_fno_emulated_tls); + Args.AddLastArg(CmdArgs, options::OPT_fzero_call_used_regs_EQ); // AltiVec-like language extensions aren't relevant for assembling. if (!isa(JA) || Output.getType() != types::TY_PP_Asm) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7574,6 +7574,24 @@ D->addAttr(::new (S.Context) OpenCLAccessAttr(S.Context, AL)); } +static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // Check that the argument is a string literal. + StringRef KindStr; + SourceLocation LiteralLoc; + if (!S.checkStringLiteralArgumentAttr(AL, 0, KindStr, &LiteralLoc)) + return; + + ZeroCallUsedRegsAttr::ZeroCallUsedRegsKind kind; + if (!ZeroCallUsedRegsAttr::ConvertStrToZeroCallUsedRegsKind(KindStr, kind)) { + S.Diag(LiteralLoc, diag::warn_attribute_type_not_supported) + << AL << KindStr; + return; + } + + D->dropAttr(); + D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, kind, AL)); +} + static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // The 'sycl_kernel' attribute applies only to function templates. const auto *FD = cast(D); @@ -8309,6 +8327,9 @@ case ParsedAttr::AT_InternalLinkage: handleInternalLinkageAttr(S, D, AL); break; + case ParsedAttr::AT_ZeroCallUsedRegs: + handleZeroCallUsedRegsAttr(S, D, AL); + break; // Microsoft attributes: case ParsedAttr::AT_LayoutVersion: diff --git a/clang/test/CodeGen/zero-call-used-regs.c b/clang/test/CodeGen/zero-call-used-regs.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/zero-call-used-regs.c @@ -0,0 +1,249 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=skip -o - | FileCheck %s --check-prefix CHECK-SKIP +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=used-gpr-arg -o - | FileCheck %s --check-prefix CHECK-USED-GPR-ARG +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=used-gpr -o - | FileCheck %s --check-prefix CHECK-USED-GPR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=used-arg -o - | FileCheck %s --check-prefix CHECK-USED-ARG +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=used -o - | FileCheck %s --check-prefix CHECK-USED +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=all-gpr-arg -o - | FileCheck %s --check-prefix CHECK-ALL-GPR-ARG +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=all-gpr -o - | FileCheck %s --check-prefix CHECK-ALL-GPR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=all-arg -o - | FileCheck %s --check-prefix CHECK-ALL-ARG +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -fzero-call-used-regs=all -o - | FileCheck %s --check-prefix CHECK-ALL + +// -fzero-call-used-regs=skip: +// +// CHECK-SKIP: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM:[0-9]*]] +// CHECK-SKIP: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-SKIP: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-SKIP: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-SKIP: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-SKIP: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-SKIP: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-SKIP: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-SKIP: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-SKIP: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// +// CHECK-SKIP-NOT: attributes #[[ATTR_NUM]] = {{.*}} "zero-call-used-regs"= +// CHECK-SKIP: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-SKIP: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-SKIP: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-SKIP: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-SKIP: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-SKIP: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-SKIP: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-SKIP: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" +// CHECK-SKIP: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" + +// -fzero-call-used-regs=used-gpr-arg: +// +// CHECK-USED-GPR-ARG: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-USED-GPR-ARG: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-USED-GPR-ARG: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG]] +// CHECK-USED-GPR-ARG: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-USED-GPR-ARG: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-USED-GPR-ARG: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-USED-GPR-ARG: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-USED-GPR-ARG: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-USED-GPR-ARG: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-USED-GPR-ARG: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" +// CHECK-USED-GPR-ARG: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" + +// -fzero-call-used-regs=used-gpr: +// +// CHECK-USED-GPR: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-USED-GPR: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-USED-GPR: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-USED-GPR: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR]] +// CHECK-USED-GPR: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-USED-GPR: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-USED-GPR: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-USED-GPR: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-USED-GPR: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-USED-GPR: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// +// CHECK-USED-GPR: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-USED-GPR: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-USED-GPR: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-USED-GPR: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-USED-GPR: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-USED-GPR: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-USED-GPR: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-USED-GPR: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" +// CHECK-USED-GPR: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" + +// -fzero-call-used-regs=used-arg: +// +// CHECK-USED-ARG: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-USED-ARG: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-USED-ARG: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-USED-ARG: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-USED-ARG: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG]] +// CHECK-USED-ARG: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-USED-ARG: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-USED-ARG: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-USED-ARG: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-USED-ARG: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// +// CHECK-USED-ARG: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-USED-ARG: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-USED-ARG: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-USED-ARG: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-USED-ARG: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-USED-ARG: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-USED-ARG: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-USED-ARG: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" +// CHECK-USED-ARG: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" + +// -fzero-call-used-regs=used: +// +// CHECK-USED: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-USED: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-USED: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-USED: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-USED: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-USED: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED]] +// CHECK-USED: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-USED: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-USED: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-USED: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// +// CHECK-USED: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-USED: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-USED: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-USED: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-USED: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-USED: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-USED: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-USED: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" +// CHECK-USED: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" + +// -fzero-call-used-regs=all-gpr-arg: +// +// CHECK-ALL-GPR-ARG: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-ALL-GPR-ARG: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-ALL-GPR-ARG: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-ALL-GPR-ARG: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-ALL-GPR-ARG: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-ALL-GPR-ARG: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-ALL-GPR-ARG: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG]] +// CHECK-ALL-GPR-ARG: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-ALL-GPR-ARG: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-ALL-GPR-ARG: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" +// CHECK-ALL-GPR-ARG: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" + +// -fzero-call-used-regs=all-gpr: +// +// CHECK-ALL-GPR: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-ALL-GPR: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-ALL-GPR: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-ALL-GPR: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-ALL-GPR: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-ALL-GPR: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-ALL-GPR: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-ALL-GPR: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR]] +// CHECK-ALL-GPR: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-ALL-GPR: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" +// CHECK-ALL-GPR: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" + +// -fzero-call-used-regs=all-arg: +// +// CHECK-ALL-ARG: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-ALL-ARG: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-ALL-ARG: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-ALL-ARG: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-ALL-ARG: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-ALL-ARG: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-ALL-ARG: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-ALL-ARG: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-ALL-ARG: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG]] +// CHECK-ALL-ARG: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-ALL-ARG: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" + +// -fzero-call-used-regs=all: +// +// CHECK-ALL: define {{.*}} @no_attribute({{.*}} #[[ATTR_NUM_ALL:[0-9]*]] +// CHECK-ALL: define {{.*}} @skip_test({{.*}} #[[ATTR_NUM_SKIP:[0-9]*]] +// CHECK-ALL: define {{.*}} @used_gpr_arg_test({{.*}} #[[ATTR_NUM_USED_GPR_ARG:[0-9]*]] +// CHECK-ALL: define {{.*}} @used_gpr_test({{.*}} #[[ATTR_NUM_USED_GPR:[0-9]*]] +// CHECK-ALL: define {{.*}} @used_arg_test({{.*}} #[[ATTR_NUM_USED_ARG:[0-9]*]] +// CHECK-ALL: define {{.*}} @used_test({{.*}} #[[ATTR_NUM_USED:[0-9]*]] +// CHECK-ALL: define {{.*}} @all_gpr_arg_test({{.*}} #[[ATTR_NUM_ALL_GPR_ARG:[0-9]*]] +// CHECK-ALL: define {{.*}} @all_gpr_test({{.*}} #[[ATTR_NUM_ALL_GPR:[0-9]*]] +// CHECK-ALL: define {{.*}} @all_arg_test({{.*}} #[[ATTR_NUM_ALL_ARG:[0-9]*]] +// CHECK-ALL: define {{.*}} @all_test({{.*}} #[[ATTR_NUM_ALL]] +// +// CHECK-ALL: attributes #[[ATTR_NUM_ALL]] = {{.*}} "zero-call-used-regs"="all" +// CHECK-ALL: attributes #[[ATTR_NUM_SKIP]] = {{.*}} "zero-call-used-regs"="skip" +// CHECK-ALL: attributes #[[ATTR_NUM_USED_GPR_ARG]] = {{.*}} "zero-call-used-regs"="used-gpr-arg" +// CHECK-ALL: attributes #[[ATTR_NUM_USED_GPR]] = {{.*}} "zero-call-used-regs"="used-gpr" +// CHECK-ALL: attributes #[[ATTR_NUM_USED_ARG]] = {{.*}} "zero-call-used-regs"="used-arg" +// CHECK-ALL: attributes #[[ATTR_NUM_USED]] = {{.*}} "zero-call-used-regs"="used" +// CHECK-ALL: attributes #[[ATTR_NUM_ALL_GPR_ARG]] = {{.*}} "zero-call-used-regs"="all-gpr-arg" +// CHECK-ALL: attributes #[[ATTR_NUM_ALL_GPR]] = {{.*}} "zero-call-used-regs"="all-gpr" +// CHECK-ALL: attributes #[[ATTR_NUM_ALL_ARG]] = {{.*}} "zero-call-used-regs"="all-arg" + +#define __zero_call_used_regs(...) __attribute__((zero_call_used_regs(__VA_ARGS__))) + +void no_attribute() { +} + +void __zero_call_used_regs("skip") skip_test() { +} + +void __zero_call_used_regs("used-gpr-arg") used_gpr_arg_test() { +} + +void __zero_call_used_regs("used-gpr") used_gpr_test() { +} + +void __zero_call_used_regs("used-arg") used_arg_test() { +} + +void __zero_call_used_regs("used") used_test() { +} + +void __zero_call_used_regs("all-gpr-arg") all_gpr_arg_test() { +} + +void __zero_call_used_regs("all-gpr") all_gpr_test() { +} + +void __zero_call_used_regs("all-arg") all_arg_test() { +} + +void __zero_call_used_regs("all") all_test() { +} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -188,4 +188,5 @@ // CHECK-NEXT: WorkGroupSizeHint (SubjectMatchRule_function) // CHECK-NEXT: XRayInstrument (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: XRayLogArgs (SubjectMatchRule_function, SubjectMatchRule_objc_method) +// CHECK-NEXT: ZeroCallUsedRegs (SubjectMatchRule_function) // CHECK-NEXT: End of supported attributes. diff --git a/clang/test/Sema/zero_call_used_regs.c b/clang/test/Sema/zero_call_used_regs.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/zero_call_used_regs.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 %s -verify -fno-builtin + +#define _zero_call_used_regs(...) __attribute__((zero_call_used_regs(__VA_ARGS__))) + +void failure() _zero_call_used_regs(); // expected-error {{takes one argument}} +void failure() _zero_call_used_regs("used", "used-gpr"); // expected-error {{takes one argument}} +void failure() _zero_call_used_regs(0); // expected-error {{requires a string}} +void failure() _zero_call_used_regs("hello"); // expected-warning {{argument not supported: hello}} + +void success() _zero_call_used_regs("skip"); +void success() _zero_call_used_regs("used-gpr-arg"); +void success() _zero_call_used_regs("used-gpr"); +void success() _zero_call_used_regs("used-arg"); +void success() _zero_call_used_regs("used"); +void success() _zero_call_used_regs("all-gpr-arg"); +void success() _zero_call_used_regs("all-gpr"); +void success() _zero_call_used_regs("all-arg"); +void success() _zero_call_used_regs("all"); diff --git a/llvm/include/llvm/Support/CodeGen.h b/llvm/include/llvm/Support/CodeGen.h --- a/llvm/include/llvm/Support/CodeGen.h +++ b/llvm/include/llvm/Support/CodeGen.h @@ -69,6 +69,35 @@ // Specify what functions should keep the frame pointer. enum class FramePointerKind { None, NonLeaf, All }; + // Specify what type of zeroing callee-used registers. + namespace ZeroCallUsedRegs { + const unsigned ONLY_USED = 1UL << 1; + const unsigned ONLY_GPR = 1UL << 2; + const unsigned ONLY_ARG = 1UL << 3; + const unsigned ENABLED = 1UL << 4; + + enum class ZeroCallUsedRegsKind : unsigned int { + // Don't zero any call-used regs. + Skip = 1UL << 0, + // Only zeros call-used GPRs used in the fn and pass args. + UsedGPRArg = ENABLED | ONLY_USED | ONLY_GPR | ONLY_ARG, + // Only zeros call-used GPRs used in the fn. + UsedGPR = ENABLED | ONLY_USED | ONLY_GPR, + // Only zeros call-used regs used in the fn and pass args. + UsedArg = ENABLED | ONLY_USED | ONLY_ARG, + // Only zeros call-used regs used in the fn. + Used = ENABLED | ONLY_USED, + // Zeros all call-used GPRs that pass args. + AllGPRArg = ENABLED | ONLY_GPR | ONLY_ARG, + // Zeros all call-used GPRs. + AllGPR = ENABLED | ONLY_GPR, + // Zeros all call-used regs that pass args. + AllArg = ENABLED | ONLY_ARG, + // Zeros all call-used regs. + All = ENABLED, + }; + } // namespace ZeroCallUsedRegs + } // end llvm namespace #endif diff --git a/llvm/lib/Target/X86/X86FrameLowering.h b/llvm/lib/Target/X86/X86FrameLowering.h --- a/llvm/lib/Target/X86/X86FrameLowering.h +++ b/llvm/lib/Target/X86/X86FrameLowering.h @@ -227,6 +227,11 @@ const DebugLoc &DL, uint64_t Offset, uint64_t Align) const; + /// Emit target zero call-used regs. + void emitZeroCallUsedRegs(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + const DebugLoc &DL) const; + void adjustFrameForMsvcCxxEh(MachineFunction &MF) const; /// Aligns the stack pointer by ANDing it with -MaxAlign. diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -484,6 +484,178 @@ } } +void X86FrameLowering::emitZeroCallUsedRegs(MachineBasicBlock &MBB, + MachineBasicBlock::iterator &MBBI, + const DebugLoc &DL) const { + MachineFunction &MF = *MBB.getParent(); + const Function &F = MF.getFunction(); + + if (!F.hasFnAttribute("zero-call-used-regs") || + // No need to zero call regs in main(). + (F.hasExternalLinkage() && F.getName() == "main")) + return; + + using namespace ZeroCallUsedRegs; + + ZeroCallUsedRegsKind ZeroRegsKind = + StringSwitch( + F.getFnAttribute("zero-call-used-regs").getValueAsString()) + .Case("skip", ZeroCallUsedRegsKind::Skip) + .Case("used-gpr-arg", ZeroCallUsedRegsKind::UsedGPRArg) + .Case("used-gpr", ZeroCallUsedRegsKind::UsedGPR) + .Case("used-arg", ZeroCallUsedRegsKind::UsedArg) + .Case("used", ZeroCallUsedRegsKind::Used) + .Case("all-gpr-arg", ZeroCallUsedRegsKind::AllGPRArg) + .Case("all-gpr", ZeroCallUsedRegsKind::AllGPR) + .Case("all-arg", ZeroCallUsedRegsKind::AllArg) + .Case("all", ZeroCallUsedRegsKind::All); + + if (ZeroRegsKind == ZeroCallUsedRegsKind::Skip) + return; + + const bool Is64Bit = STI.is64Bit(); + const bool OnlyGPR = static_cast(ZeroRegsKind) & ONLY_GPR; + const bool OnlyUsed = static_cast(ZeroRegsKind) & ONLY_USED; + const bool OnlyArg = static_cast(ZeroRegsKind) & ONLY_ARG; + + const X86RegisterInfo &MRI = *STI.getRegisterInfo(); + BitVector ArgumentRegs = MRI.getArgumentRegs(MF); + + // Add call-used registers we could zero out. + auto AddRegs = [&](BitVector &Regs, const TargetRegisterClass &TRC) { + llvm::for_each(TRI->getAllocatableSet(MF, &TRC).set_bits(), + [&](MCRegister Reg) { Regs.set(Reg); }); + }; + + BitVector CallUsedRegs(MRI.getNumRegs()); + + // GPRs. + AddRegs(CallUsedRegs, X86::GR32RegClass); + + if (Is64Bit) + AddRegs(CallUsedRegs, X86::GR64RegClass); + + // *MM registers. + if (STI.hasSSE1()) + AddRegs(CallUsedRegs, X86::VR128RegClass); + if (STI.hasAVX()) + AddRegs(CallUsedRegs, X86::VR256RegClass); + if (STI.hasAVX512()) + AddRegs(CallUsedRegs, X86::VR512RegClass); + + // Floating-point stack registers. + AddRegs(CallUsedRegs, X86::RFP80RegClass); + + // TODO: MMX registers? + + BitVector UsedRegs(MRI.getNumRegs()); + llvm::for_each(MF, [&](const MachineBasicBlock &MBB) { + llvm::for_each(MBB, [&](const MachineInstr &MI) { + llvm::for_each(MI.operands(), [&](MachineOperand MO) { + if (MO.isReg() && (MO.isDef() || MO.isUse())) { + MCRegister Reg = MO.getReg(); + + if (X86::GR32RegClass.contains(Reg)) { + if (auto SReg = TRI->getMatchingSuperReg(Reg, X86::sub_32bit, + &X86::GR32RegClass)) + Reg = SReg; + } + UsedRegs.set(Reg); + } + }); + }); + }); + + BitVector RegsToZero(MRI.getNumRegs()); + for (auto Reg : CallUsedRegs.set_bits()) { + // Want only GPR register. + if (OnlyGPR && !X86::GR64RegClass.contains(Reg) && + !X86::GR32RegClass.contains(Reg)) + continue; + + // Want only used registers. + if (OnlyUsed && !UsedRegs[Reg]) + continue; + + // Want only registers used for arguments. + if (OnlyArg && !ArgumentRegs[Reg]) + continue; + + RegsToZero.set(Reg); + } + + // Remove registers are live when leaving the function. + llvm::for_each(MBB.terminators(), [&](MachineInstr &MI) { + if (!MI.isReturn()) + return; + + for (auto MO : MI.operands()) + if (MO.isReg()) + RegsToZero.reset(MO.getReg()); + }); + + // Don't clear registers that will just be reset before exiting. + const MachineFrameInfo &MFI = MF.getFrameInfo(); + for (auto CSI : MFI.getCalleeSavedInfo()) + RegsToZero.reset(CSI.getReg()); + + // Position to place register zeroing instructions. + MachineBasicBlock::iterator InsertPt = MBB.getFirstTerminator(); + + // Zero out FP stack if referenced. Do this outside of the loop below so that + // it's done only once. + for (MCRegister Reg : RegsToZero.set_bits()) { + if (!X86::RFP80RegClass.contains(Reg)) + continue; + + unsigned NumStackSlots = Is64Bit ? 8 : 7; + for (unsigned I = 0; I < NumStackSlots; ++I) + BuildMI(MBB, InsertPt, DebugLoc(), TII.get(X86::LD_F0)); + + for (unsigned I = 0; I < NumStackSlots; ++I) + BuildMI(MBB, InsertPt, DebugLoc(), TII.get(X86::ST_FPrr)) + .addReg(X86::ST0); + + break; + } + + for (auto Reg : RegsToZero.set_bits()) { + if (X86::RFP80RegClass.contains(Reg)) + // Handled above. + continue; + + unsigned XorOp; + if (X86::VR128RegClass.contains(Reg)) { // SSE + XorOp = X86::PXORrr; + } else if (X86::VR256RegClass.contains(Reg)) { // AVX + XorOp = X86::VPXORrr; + } else if (X86::VR512RegClass.contains(Reg)) { // AVX2 + XorOp = X86::VPXORYrr; + } else { + if (!X86::GR32RegClass.contains(Reg)) { + if (!UsedRegs[Reg]) + // Even in "all-*" mode, we only emit 64-bit registers if they're + // used. + continue; + + XorOp = X86::XOR64rr; + } else { + MCRegister SuperReg = + TRI->getMatchingSuperReg(Reg, X86::sub_32bit, &X86::GR64RegClass); + + if (UsedRegs[SuperReg]) + continue; + + XorOp = X86::XOR32rr; + } + } + + BuildMI(MBB, InsertPt, DebugLoc(), TII.get(XorOp), Reg) + .addReg(Reg, RegState::Undef) + .addReg(Reg, RegState::Undef); + } +} + void X86FrameLowering::emitStackProbe(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, @@ -2201,6 +2373,9 @@ } } + // Emit code for "zero-call-used-regs" attributes. + emitZeroCallUsedRegs(MBB, AfterPop, DL); + // Emit DWARF info specifying the restores of the callee-saved registers. // For epilogue with return inside or being other block without successor, // no need to generate .cfi_restore for callee-saved registers. diff --git a/llvm/lib/Target/X86/X86RegisterInfo.h b/llvm/lib/Target/X86/X86RegisterInfo.h --- a/llvm/lib/Target/X86/X86RegisterInfo.h +++ b/llvm/lib/Target/X86/X86RegisterInfo.h @@ -115,6 +115,12 @@ /// register scavenger to determine what registers are free. BitVector getReservedRegs(const MachineFunction &MF) const override; + /// getArgumentRegs - Returns a bitvector indexed by physical register number + /// indication if a register is used as part of the calling convention or not. + /// Only super registers are returned; subregisters--e.g. AL, BH, CX--are + /// omitted. + BitVector getArgumentRegs(const MachineFunction &MF) const; + void adjustStackMapLiveOutMask(uint32_t *Mask) const override; bool hasBasePointer(const MachineFunction &MF) const; diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -618,6 +618,68 @@ return Reserved; } +BitVector X86RegisterInfo::getArgumentRegs(const MachineFunction &MF) const { + const X86Subtarget &Subtarget = MF.getSubtarget(); + bool HasSSE = Subtarget.hasSSE1(); + bool HasAVX = Subtarget.hasAVX(); + bool HasAVX512 = Subtarget.hasAVX512(); + + BitVector ArgumentRegs(getNumRegs()); + + auto SetArgRegs = [&ArgumentRegs](ArrayRef Regs) { + for (auto Reg : Regs) + ArgumentRegs.set(Reg); + }; + + SetArgRegs({X86::EAX, X86::ECX, X86::EDX, X86::EDI, X86::ESI}); + + if (!Is64Bit) { + if (HasSSE) + SetArgRegs({X86::XMM0, X86::XMM1, X86::XMM2}); + + if (HasAVX) + SetArgRegs({X86::YMM0, X86::YMM1, X86::YMM2}); + + if (HasAVX512) + SetArgRegs({X86::ZMM0, X86::ZMM1, X86::ZMM2}); + + return ArgumentRegs; + } + + const Function &F = MF.getFunction(); + CallingConv::ID CC = F.getCallingConv(); + + if (CC == CallingConv::X86_64_SysV) + SetArgRegs({X86::RAX}); + + if (HasSSE) + SetArgRegs({X86::XMM0, X86::XMM1, X86::XMM2, X86::XMM3}); + + if (HasAVX) + SetArgRegs({X86::YMM0, X86::YMM1, X86::YMM2, X86::YMM3}); + + if (HasAVX512) + SetArgRegs({X86::ZMM0, X86::ZMM1, X86::ZMM2, X86::ZMM3}); + + if (IsWin64 || CC == CallingConv::Win64) { + SetArgRegs({X86::RCX, X86::RDX, X86::R8, X86::R9}); + } else { + SetArgRegs({X86::RDI, X86::RSI, X86::RDX, X86::RCX, X86::R8, X86::R9}); + SetArgRegs({X86::R8D, X86::R9D}); + + if (HasSSE) + SetArgRegs({X86::XMM5, X86::XMM6, X86::XMM7}); + + if (HasAVX) + SetArgRegs({X86::YMM5, X86::YMM6, X86::YMM7}); + + if (HasAVX512) + SetArgRegs({X86::ZMM5, X86::ZMM6, X86::ZMM7}); + } + + return ArgumentRegs; +} + void X86RegisterInfo::adjustStackMapLiveOutMask(uint32_t *Mask) const { // Check if the EFLAGS register is marked as live-out. This shouldn't happen, // because the calling convention defines the EFLAGS register as NOT diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-all-arg.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-all-arg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-all-arg.ll @@ -0,0 +1,40 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %ecx, %ecx +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: xorl %edx, %edx +; CHECK-NEXT: xorl %esi, %esi +; CHECK-NEXT: xorps %xmm0, %xmm0 +; CHECK-NEXT: xorps %xmm1, %xmm1 +; CHECK-NEXT: xorps %xmm2, %xmm2 +; CHECK-NEXT: xorps %xmm3, %xmm3 +; CHECK-NEXT: xorps %xmm5, %xmm5 +; CHECK-NEXT: xorps %xmm6, %xmm6 +; CHECK-NEXT: xorps %xmm7, %xmm7 +; CHECK-NEXT: xorl %r8d, %r8d +; CHECK-NEXT: xorl %r9d, %r9d +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="all-arg" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="all-arg" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr-arg.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr-arg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr-arg.ll @@ -0,0 +1,33 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %ecx, %ecx +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: xorl %edx, %edx +; CHECK-NEXT: xorl %esi, %esi +; CHECK-NEXT: xorl %r8d, %r8d +; CHECK-NEXT: xorl %r9d, %r9d +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="all-gpr-arg" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="all-gpr-arg" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr.ll @@ -0,0 +1,41 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %ebp, %ebp +; CHECK-NEXT: xorl %ebx, %ebx +; CHECK-NEXT: xorl %ecx, %ecx +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: xorl %edx, %edx +; CHECK-NEXT: xorl %esi, %esi +; CHECK-NEXT: xorl %r8d, %r8d +; CHECK-NEXT: xorl %r9d, %r9d +; CHECK-NEXT: xorl %r10d, %r10d +; CHECK-NEXT: xorl %r11d, %r11d +; CHECK-NEXT: xorl %r12d, %r12d +; CHECK-NEXT: xorl %r13d, %r13d +; CHECK-NEXT: xorl %r14d, %r14d +; CHECK-NEXT: xorl %r15d, %r15d +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="all-gpr" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="all-gpr" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-all.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-all.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-all.ll @@ -0,0 +1,73 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: fldz +; CHECK-NEXT: fldz +; CHECK-NEXT: fldz +; CHECK-NEXT: fldz +; CHECK-NEXT: fldz +; CHECK-NEXT: fldz +; CHECK-NEXT: fldz +; CHECK-NEXT: fldz +; CHECK-NEXT: fstp %st(0) +; CHECK-NEXT: fstp %st(0) +; CHECK-NEXT: fstp %st(0) +; CHECK-NEXT: fstp %st(0) +; CHECK-NEXT: fstp %st(0) +; CHECK-NEXT: fstp %st(0) +; CHECK-NEXT: fstp %st(0) +; CHECK-NEXT: fstp %st(0) +; CHECK-NEXT: xorl %ebp, %ebp +; CHECK-NEXT: xorl %ebx, %ebx +; CHECK-NEXT: xorl %ecx, %ecx +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: xorl %edx, %edx +; CHECK-NEXT: xorl %esi, %esi +; CHECK-NEXT: xorps %xmm0, %xmm0 +; CHECK-NEXT: xorps %xmm1, %xmm1 +; CHECK-NEXT: xorps %xmm2, %xmm2 +; CHECK-NEXT: xorps %xmm3, %xmm3 +; CHECK-NEXT: xorps %xmm4, %xmm4 +; CHECK-NEXT: xorps %xmm5, %xmm5 +; CHECK-NEXT: xorps %xmm6, %xmm6 +; CHECK-NEXT: xorps %xmm7, %xmm7 +; CHECK-NEXT: xorps %xmm8, %xmm8 +; CHECK-NEXT: xorps %xmm9, %xmm9 +; CHECK-NEXT: xorps %xmm10, %xmm10 +; CHECK-NEXT: xorps %xmm11, %xmm11 +; CHECK-NEXT: xorps %xmm12, %xmm12 +; CHECK-NEXT: xorps %xmm13, %xmm13 +; CHECK-NEXT: xorps %xmm14, %xmm14 +; CHECK-NEXT: xorps %xmm15, %xmm15 +; CHECK-NEXT: xorl %r8d, %r8d +; CHECK-NEXT: xorl %r9d, %r9d +; CHECK-NEXT: xorl %r10d, %r10d +; CHECK-NEXT: xorl %r11d, %r11d +; CHECK-NEXT: xorl %r12d, %r12d +; CHECK-NEXT: xorl %r13d, %r13d +; CHECK-NEXT: xorl %r14d, %r14d +; CHECK-NEXT: xorl %r15d, %r15d +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="all" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="all" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-fmod.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-fmod.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-fmod.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +define dso_local float @foo() local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: pushq %rax +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: xorps %xmm0, %xmm0 +; CHECK-NEXT: xorps %xmm1, %xmm1 +; CHECK-NEXT: callq fmod +; CHECK-NEXT: cvtsd2ss %xmm0, %xmm0 +; CHECK-NEXT: popq %rax +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: xorq %rax, %rax +; CHECK-NEXT: xorps %xmm1, %xmm1 +; CHECK-NEXT: retq +entry: + %call = call nnan ninf double @fmod(double 0.000000e+00, double 0.000000e+00) #2 + %conv = fptrunc double %call to float + ret float %conv +} + +declare dso_local double @fmod(double, double) local_unnamed_addr #1 + +attributes #0 = { mustprogress nofree nounwind uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used" } +attributes #1 = { mustprogress nofree nounwind willreturn "frame-pointer"="none" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used" } +attributes #2 = { nounwind } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-skip.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-skip.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-skip.ll @@ -0,0 +1,27 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="skip" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="skip" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-smoke-tests.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-smoke-tests.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-smoke-tests.ll @@ -0,0 +1,92 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +define dso_local void @test_skip_1() local_unnamed_addr #0 { +; CHECK-LABEL: test_skip_1: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: retq +entry: + ret void +} + +define dso_local void @test_skip_2() local_unnamed_addr #0 "zero-call-used-regs"="skip" { +; CHECK-LABEL: test_skip_2: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: retq +entry: + ret void +} + +define dso_local void @test_used_1() local_unnamed_addr #0 "zero-call-used-regs"="used" { +; CHECK-LABEL: test_used_1: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: retq +entry: + ret void +} + +define dso_local void @test_used_gpr_1() local_unnamed_addr #0 "zero-call-used-regs"="used-gpr" { +; CHECK-LABEL: test_used_gpr_1: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: retq +entry: + ret void +} + +define dso_local i32 @test_used_gpr_2(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="used-gpr" { +; CHECK-LABEL: test_used_gpr_2: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local void @test_all_gpr_1() local_unnamed_addr #0 "zero-call-used-regs"="all-gpr" { +; CHECK-LABEL: test_all_gpr_1: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: xorl %ebp, %ebp +; CHECK-NEXT: xorl %ebx, %ebx +; CHECK-NEXT: xorl %ecx, %ecx +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: xorl %edx, %edx +; CHECK-NEXT: xorl %esi, %esi +; CHECK-NEXT: xorl %r8d, %r8d +; CHECK-NEXT: xorl %r9d, %r9d +; CHECK-NEXT: xorl %r10d, %r10d +; CHECK-NEXT: xorl %r11d, %r11d +; CHECK-NEXT: xorl %r12d, %r12d +; CHECK-NEXT: xorl %r13d, %r13d +; CHECK-NEXT: xorl %r14d, %r14d +; CHECK-NEXT: xorl %r15d, %r15d +; CHECK-NEXT: retq +entry: + ret void +} + +define dso_local i32 @test_all_gpr_2(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="all-gpr" { +; CHECK-LABEL: test_all_gpr_2: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %ebp, %ebp +; CHECK-NEXT: xorl %ebx, %ebx +; CHECK-NEXT: xorl %ecx, %ecx +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: xorl %edx, %edx +; CHECK-NEXT: xorl %esi, %esi +; CHECK-NEXT: xorl %r8d, %r8d +; CHECK-NEXT: xorl %r9d, %r9d +; CHECK-NEXT: xorl %r10d, %r10d +; CHECK-NEXT: xorl %r11d, %r11d +; CHECK-NEXT: xorl %r12d, %r12d +; CHECK-NEXT: xorl %r13d, %r13d +; CHECK-NEXT: xorl %r14d, %r14d +; CHECK-NEXT: xorl %r15d, %r15d +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-used-arg.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-used-arg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-used-arg.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used-arg" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used-arg" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr-arg.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr-arg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr-arg.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used-gpr-arg" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used-gpr-arg" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used-gpr" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used-gpr" } diff --git a/llvm/test/CodeGen/X86/zero-call-used-regs-used.ll b/llvm/test/CodeGen/X86/zero-call-used-regs-used.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/zero-call-used-regs-used.ll @@ -0,0 +1,28 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s + +@result = dso_local global i32 0, align 4 + +define dso_local i32 @foo(i32 returned %x) local_unnamed_addr #0 { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: xorl %edi, %edi +; CHECK-NEXT: retq +entry: + ret i32 %x +} + +define dso_local i32 @main() local_unnamed_addr #1 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movl $2, result(%rip) +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +entry: + store volatile i32 2, i32* @result, align 4 + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used" } +attributes #1 = { nofree norecurse nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "zero-call-used-regs"="used" }