void updated this revision to Diff 404763.
void added a comment.

Remove attribute from "main" function.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D110869/new/

https://reviews.llvm.org/D110869

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/CodeGenOptions.def
  clang/include/clang/Driver/Options.td
  clang/lib/CodeGen/CGCall.cpp
  clang/lib/CodeGen/CodeGenFunction.cpp
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/test/CodeGen/zero-call-used-regs.c
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/zero_call_used_regs.c
  llvm/include/llvm/CodeGen/MachineRegisterInfo.h
  llvm/include/llvm/CodeGen/TargetFrameLowering.h
  llvm/include/llvm/CodeGen/TargetRegisterInfo.h
  llvm/include/llvm/Support/CodeGen.h
  llvm/include/llvm/Target/Target.td
  llvm/lib/CodeGen/MachineRegisterInfo.cpp
  llvm/lib/CodeGen/PrologEpilogInserter.cpp
  llvm/lib/Target/X86/X86FrameLowering.cpp
  llvm/lib/Target/X86/X86FrameLowering.h
  llvm/lib/Target/X86/X86RegisterInfo.cpp
  llvm/lib/Target/X86/X86RegisterInfo.h
  llvm/lib/Target/X86/X86RegisterInfo.td
  llvm/test/CodeGen/X86/zero-call-used-regs-fmod.ll
  llvm/test/CodeGen/X86/zero-call-used-regs.ll
  llvm/utils/TableGen/CodeGenRegisters.cpp
  llvm/utils/TableGen/CodeGenRegisters.h
  llvm/utils/TableGen/RegisterInfoEmitter.cpp

Index: llvm/utils/TableGen/RegisterInfoEmitter.cpp
===================================================================
--- llvm/utils/TableGen/RegisterInfoEmitter.cpp
+++ llvm/utils/TableGen/RegisterInfoEmitter.cpp
@@ -1176,6 +1176,10 @@
      << "unsigned RegUnit) const override;\n"
      << "  ArrayRef<const char *> getRegMaskNames() const override;\n"
      << "  ArrayRef<const uint32_t *> getRegMasks() const override;\n"
+     << "  bool isGeneralPurposeRegister(const MachineFunction &, "
+     << "MCRegister) const override;\n"
+     << "  bool isFixedRegister(const MachineFunction &, "
+     << "MCRegister) const override;\n"
      << "  /// Devirtualized TargetFrameLowering.\n"
      << "  static const " << TargetName << "FrameLowering *getFrameLowering(\n"
      << "      const MachineFunction &MF);\n"
@@ -1620,6 +1624,36 @@
   }
   OS << "}\n\n";
 
+  const std::list<CodeGenRegisterCategory> &RegCategories =
+      RegBank.getRegCategories();
+  OS << "bool " << ClassName << "::\n"
+     << "isGeneralPurposeRegister(const MachineFunction &MF, "
+     << "MCRegister PhysReg) const {\n"
+     << "  return\n";
+  for (const auto &Category : RegCategories)
+    if (Category.getName() == "GeneralPurposeRegisters") {
+      for (const auto *RC : Category.getClasses())
+        OS << "      " << RC->getQualifiedName()
+           << "RegClass.contains(PhysReg) ||\n";
+      break;
+    }
+  OS << "      false;\n";
+  OS << "}\n\n";
+
+  OS << "bool " << ClassName << "::\n"
+     << "isFixedRegister(const MachineFunction &MF, "
+     << "MCRegister PhysReg) const {\n"
+     << "  return\n";
+  for (const auto &Category : RegCategories)
+    if (Category.getName() == "FixedRegisters") {
+      for (const auto *RC : Category.getClasses())
+        OS << "      " << RC->getQualifiedName()
+           << "RegClass.contains(PhysReg) ||\n";
+      break;
+    }
+  OS << "      false;\n";
+  OS << "}\n\n";
+
   OS << "ArrayRef<const char *> " << ClassName
      << "::getRegMaskNames() const {\n";
   if (!CSRSets.empty()) {
Index: llvm/utils/TableGen/CodeGenRegisters.h
===================================================================
--- llvm/utils/TableGen/CodeGenRegisters.h
+++ llvm/utils/TableGen/CodeGenRegisters.h
@@ -476,6 +476,26 @@
     static void computeSubClasses(CodeGenRegBank&);
   };
 
+  // Register categories are used when we need to deterine the category a
+  // register falls into (GPR, vector, fixed, etc.) without having to know
+  // specific information about the target architecture.
+  class CodeGenRegisterCategory {
+    Record *TheDef;
+    std::string Name;
+    std::list<CodeGenRegisterClass *> Classes;
+
+  public:
+    CodeGenRegisterCategory(CodeGenRegBank &, Record *R);
+    CodeGenRegisterCategory(CodeGenRegisterCategory &) = delete;
+
+    // Return the Record that defined this class, or NULL if the class was
+    // created by TableGen.
+    Record *getDef() const { return TheDef; }
+
+    std::string getName() const { return Name; }
+    std::list<CodeGenRegisterClass *> getClasses() const { return Classes; }
+  };
+
   // Register units are used to model interference and register pressure.
   // Every register is assigned one or more register units such that two
   // registers overlap if and only if they have a register unit in common.
@@ -559,6 +579,13 @@
     typedef std::map<CodeGenRegisterClass::Key, CodeGenRegisterClass*> RCKeyMap;
     RCKeyMap Key2RC;
 
+    // Register categories.
+    std::list<CodeGenRegisterCategory> RegCategories;
+    DenseMap<Record *, CodeGenRegisterCategory *> Def2RCat;
+    typedef std::map<CodeGenRegisterClass::Key, CodeGenRegisterCategory *>
+        RCatKeyMap;
+    RCatKeyMap Key2RCat;
+
     // Remember each unique set of register units. Initially, this contains a
     // unique set for each register class. Simliar sets are coalesced with
     // pruneUnitSets and new supersets are inferred during computeRegUnitSets.
@@ -719,6 +746,14 @@
       return RegClasses;
     }
 
+    std::list<CodeGenRegisterCategory> &getRegCategories() {
+      return RegCategories;
+    }
+
+    const std::list<CodeGenRegisterCategory> &getRegCategories() const {
+      return RegCategories;
+    }
+
     // Find a register class from its def.
     CodeGenRegisterClass *getRegClass(const Record *) const;
 
Index: llvm/utils/TableGen/CodeGenRegisters.cpp
===================================================================
--- llvm/utils/TableGen/CodeGenRegisters.cpp
+++ llvm/utils/TableGen/CodeGenRegisters.cpp
@@ -1105,6 +1105,17 @@
                    std::back_inserter(RegUnits));
 }
 
+//===----------------------------------------------------------------------===//
+//                           CodeGenRegisterCategory
+//===----------------------------------------------------------------------===//
+
+CodeGenRegisterCategory::CodeGenRegisterCategory(CodeGenRegBank &RegBank,
+                                                 Record *R)
+    : TheDef(R), Name(std::string(R->getName())) {
+  for (Record *RegClass : R->getValueAsListOfDefs("Classes"))
+    Classes.push_back(RegBank.getRegClass(RegClass));
+}
+
 //===----------------------------------------------------------------------===//
 //                               CodeGenRegBank
 //===----------------------------------------------------------------------===//
@@ -1222,6 +1233,12 @@
   for (auto &RC : RegClasses)
     RC.EnumValue = i++;
   CodeGenRegisterClass::computeSubClasses(*this);
+
+  // Read in the register category definitions.
+  std::vector<Record *> RCats =
+      Records.getAllDerivedDefinitions("RegisterCategory");
+  for (auto *R : RCats)
+    RegCategories.emplace_back(*this, R);
 }
 
 // Create a synthetic CodeGenSubRegIndex without a corresponding Record.
Index: llvm/test/CodeGen/X86/zero-call-used-regs.ll
===================================================================
--- /dev/null
+++ llvm/test/CodeGen/X86/zero-call-used-regs.ll
@@ -0,0 +1,292 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=i386-unknown-linux-gnu | FileCheck %s --check-prefix=I386
+; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s --check-prefix=X86-64
+
+@result = dso_local global i32 0, align 4
+
+define dso_local i32 @skip(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="skip" {
+; I386-LABEL: skip:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: skip:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+define dso_local i32 @used_gpr_arg(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="used-gpr-arg" {
+; I386-LABEL: used_gpr_arg:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: used_gpr_arg:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    xorl %edi, %edi
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+define dso_local i32 @used_gpr(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="used-gpr" {
+; I386-LABEL: used_gpr:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: used_gpr:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    xorl %edi, %edi
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+define dso_local i32 @used_arg(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="used-arg" {
+; I386-LABEL: used_arg:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: used_arg:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    xorl %edi, %edi
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+define dso_local i32 @used(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="used" {
+; I386-LABEL: used:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: used:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    xorl %edi, %edi
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+define dso_local i32 @all_gpr_arg(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="all-gpr-arg" {
+; I386-LABEL: all_gpr_arg:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    xorl %ecx, %ecx
+; I386-NEXT:    xorl %edx, %edx
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: all_gpr_arg:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    xorq %rcx, %rcx
+; X86-64-NEXT:    xorq %rdi, %rdi
+; X86-64-NEXT:    xorq %rdx, %rdx
+; X86-64-NEXT:    xorq %rsi, %rsi
+; X86-64-NEXT:    xorq %r8, %r8
+; X86-64-NEXT:    xorq %r9, %r9
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+define dso_local i32 @all_gpr(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="all-gpr" {
+; I386-LABEL: all_gpr:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    xorl %ebp, %ebp
+; I386-NEXT:    xorl %ebx, %ebx
+; I386-NEXT:    xorl %ecx, %ecx
+; I386-NEXT:    xorl %edi, %edi
+; I386-NEXT:    xorl %edx, %edx
+; I386-NEXT:    xorl %esi, %esi
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: all_gpr:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    xorq %rbp, %rbp
+; X86-64-NEXT:    xorq %rbx, %rbx
+; X86-64-NEXT:    xorq %rcx, %rcx
+; X86-64-NEXT:    xorq %rdi, %rdi
+; X86-64-NEXT:    xorq %rdx, %rdx
+; X86-64-NEXT:    xorq %rsi, %rsi
+; X86-64-NEXT:    xorq %r8, %r8
+; X86-64-NEXT:    xorq %r9, %r9
+; X86-64-NEXT:    xorq %r10, %r10
+; X86-64-NEXT:    xorq %r11, %r11
+; X86-64-NEXT:    xorq %r12, %r12
+; X86-64-NEXT:    xorq %r13, %r13
+; X86-64-NEXT:    xorq %r14, %r14
+; X86-64-NEXT:    xorq %r15, %r15
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+define dso_local i32 @all_arg(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="all-arg" {
+; I386-LABEL: all_arg:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    xorl %ecx, %ecx
+; I386-NEXT:    xorl %edx, %edx
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: all_arg:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    xorq %rcx, %rcx
+; X86-64-NEXT:    xorq %rdi, %rdi
+; X86-64-NEXT:    xorq %rdx, %rdx
+; X86-64-NEXT:    xorq %rsi, %rsi
+; X86-64-NEXT:    xorq %r8, %r8
+; X86-64-NEXT:    xorq %r9, %r9
+; X86-64-NEXT:    xorps %xmm0, %xmm0
+; X86-64-NEXT:    xorps %xmm1, %xmm1
+; X86-64-NEXT:    xorps %xmm2, %xmm2
+; X86-64-NEXT:    xorps %xmm3, %xmm3
+; X86-64-NEXT:    xorps %xmm4, %xmm4
+; X86-64-NEXT:    xorps %xmm5, %xmm5
+; X86-64-NEXT:    xorps %xmm6, %xmm6
+; X86-64-NEXT:    xorps %xmm7, %xmm7
+; X86-64-NEXT:    xorps %xmm8, %xmm8
+; X86-64-NEXT:    xorps %xmm9, %xmm9
+; X86-64-NEXT:    xorps %xmm10, %xmm10
+; X86-64-NEXT:    xorps %xmm11, %xmm11
+; X86-64-NEXT:    xorps %xmm12, %xmm12
+; X86-64-NEXT:    xorps %xmm13, %xmm13
+; X86-64-NEXT:    xorps %xmm14, %xmm14
+; X86-64-NEXT:    xorps %xmm15, %xmm15
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+define dso_local i32 @all(i32 returned %x) local_unnamed_addr #0 "zero-call-used-regs"="all" {
+; I386-LABEL: all:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl {{[0-9]+}}(%esp), %eax
+; I386-NEXT:    fldz
+; I386-NEXT:    fldz
+; I386-NEXT:    fldz
+; I386-NEXT:    fldz
+; I386-NEXT:    fldz
+; I386-NEXT:    fldz
+; I386-NEXT:    fldz
+; I386-NEXT:    fstp %st(0)
+; I386-NEXT:    fstp %st(0)
+; I386-NEXT:    fstp %st(0)
+; I386-NEXT:    fstp %st(0)
+; I386-NEXT:    fstp %st(0)
+; I386-NEXT:    fstp %st(0)
+; I386-NEXT:    fstp %st(0)
+; I386-NEXT:    xorl %ebp, %ebp
+; I386-NEXT:    xorl %ebx, %ebx
+; I386-NEXT:    xorl %ecx, %ecx
+; I386-NEXT:    xorl %edi, %edi
+; I386-NEXT:    xorl %edx, %edx
+; I386-NEXT:    xorl %esi, %esi
+; I386-NEXT:    xorps %xmm0, %xmm0
+; I386-NEXT:    xorps %xmm1, %xmm1
+; I386-NEXT:    xorps %xmm2, %xmm2
+; I386-NEXT:    xorps %xmm3, %xmm3
+; I386-NEXT:    xorps %xmm4, %xmm4
+; I386-NEXT:    xorps %xmm5, %xmm5
+; I386-NEXT:    xorps %xmm6, %xmm6
+; I386-NEXT:    xorps %xmm7, %xmm7
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: all:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl %edi, %eax
+; X86-64-NEXT:    fldz
+; X86-64-NEXT:    fldz
+; X86-64-NEXT:    fldz
+; X86-64-NEXT:    fldz
+; X86-64-NEXT:    fldz
+; X86-64-NEXT:    fldz
+; X86-64-NEXT:    fldz
+; X86-64-NEXT:    fldz
+; X86-64-NEXT:    fstp %st(0)
+; X86-64-NEXT:    fstp %st(0)
+; X86-64-NEXT:    fstp %st(0)
+; X86-64-NEXT:    fstp %st(0)
+; X86-64-NEXT:    fstp %st(0)
+; X86-64-NEXT:    fstp %st(0)
+; X86-64-NEXT:    fstp %st(0)
+; X86-64-NEXT:    fstp %st(0)
+; X86-64-NEXT:    xorq %rbp, %rbp
+; X86-64-NEXT:    xorq %rbx, %rbx
+; X86-64-NEXT:    xorq %rcx, %rcx
+; X86-64-NEXT:    xorq %rdi, %rdi
+; X86-64-NEXT:    xorq %rdx, %rdx
+; X86-64-NEXT:    xorq %rsi, %rsi
+; X86-64-NEXT:    xorq %r8, %r8
+; X86-64-NEXT:    xorq %r9, %r9
+; X86-64-NEXT:    xorq %r10, %r10
+; X86-64-NEXT:    xorq %r11, %r11
+; X86-64-NEXT:    xorq %r12, %r12
+; X86-64-NEXT:    xorq %r13, %r13
+; X86-64-NEXT:    xorq %r14, %r14
+; X86-64-NEXT:    xorq %r15, %r15
+; X86-64-NEXT:    xorps %xmm0, %xmm0
+; X86-64-NEXT:    xorps %xmm1, %xmm1
+; X86-64-NEXT:    xorps %xmm2, %xmm2
+; X86-64-NEXT:    xorps %xmm3, %xmm3
+; X86-64-NEXT:    xorps %xmm4, %xmm4
+; X86-64-NEXT:    xorps %xmm5, %xmm5
+; X86-64-NEXT:    xorps %xmm6, %xmm6
+; X86-64-NEXT:    xorps %xmm7, %xmm7
+; X86-64-NEXT:    xorps %xmm8, %xmm8
+; X86-64-NEXT:    xorps %xmm9, %xmm9
+; X86-64-NEXT:    xorps %xmm10, %xmm10
+; X86-64-NEXT:    xorps %xmm11, %xmm11
+; X86-64-NEXT:    xorps %xmm12, %xmm12
+; X86-64-NEXT:    xorps %xmm13, %xmm13
+; X86-64-NEXT:    xorps %xmm14, %xmm14
+; X86-64-NEXT:    xorps %xmm15, %xmm15
+; X86-64-NEXT:    retq
+
+entry:
+  ret i32 %x
+}
+
+; Don't emit zeroing registers in "main" function.
+define dso_local i32 @main() local_unnamed_addr #1 {
+; I386-LABEL: main:
+; I386:       # %bb.0: # %entry
+; I386-NEXT:    movl $2, result
+; I386-NEXT:    xorl %eax, %eax
+; I386-NEXT:    retl
+;
+; X86-64-LABEL: main:
+; X86-64:       # %bb.0: # %entry
+; X86-64-NEXT:    movl $2, result(%rip)
+; X86-64-NEXT:    xorl %eax, %eax
+; X86-64-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" }
+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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-fmod.ll
===================================================================
--- /dev/null
+++ 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:    xorq %rax, %rax
+; CHECK-NEXT:    xorps %xmm1, %xmm1
+; CHECK-NEXT:    popq %rax
+; CHECK-NEXT:    .cfi_def_cfa_offset 8
+; 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 }
Index: llvm/lib/Target/X86/X86RegisterInfo.td
===================================================================
--- llvm/lib/Target/X86/X86RegisterInfo.td
+++ llvm/lib/Target/X86/X86RegisterInfo.td
@@ -638,3 +638,14 @@
 let CopyCost = -1 in // Don't allow copying of tile registers
 def TILE : RegisterClass<"X86", [x86amx], 8192,
                          (sequence "TMM%u", 0, 7)> {let Size = 8192;}
+
+//===----------------------------------------------------------------------===//
+// Register categories.
+//
+
+// The TILE and VK*PAIR registers may not be "fixed", but we don't want them
+// anyway.
+def FixedRegisters : RegisterCategory<[DEBUG_REG, CONTROL_REG, CCR, FPCCR,
+                                       DFCCR, TILE, VK1PAIR, VK2PAIR, VK4PAIR,
+                                       VK8PAIR, VK16PAIR]>;
+def GeneralPurposeRegisters : RegisterCategory<[GR64, GR32, GR16, GR8]>;
Index: llvm/lib/Target/X86/X86RegisterInfo.h
===================================================================
--- llvm/lib/Target/X86/X86RegisterInfo.h
+++ llvm/lib/Target/X86/X86RegisterInfo.h
@@ -115,6 +115,15 @@
   /// register scavenger to determine what registers are free.
   BitVector getReservedRegs(const MachineFunction &MF) const override;
 
+  /// isArgumentReg - Returns true if Reg can be used as an argument to a
+  /// function.
+  bool isArgumentRegister(const MachineFunction &MF,
+                          MCRegister Reg) const override;
+
+  /// Returns true if PhysReg is a fixed register.
+  bool isFixedRegister(const MachineFunction &MF,
+                       MCRegister PhysReg) const override;
+
   void adjustStackMapLiveOutMask(uint32_t *Mask) const override;
 
   bool hasBasePointer(const MachineFunction &MF) const;
Index: llvm/lib/Target/X86/X86RegisterInfo.cpp
===================================================================
--- llvm/lib/Target/X86/X86RegisterInfo.cpp
+++ llvm/lib/Target/X86/X86RegisterInfo.cpp
@@ -618,6 +618,58 @@
   return Reserved;
 }
 
+bool X86RegisterInfo::isArgumentRegister(const MachineFunction &MF,
+                                         MCRegister Reg) const {
+  const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();
+  const TargetRegisterInfo &TRI = *ST.getRegisterInfo();
+  auto IsSubReg = [&](MCRegister RegA, MCRegister RegB) {
+    return TRI.isSuperOrSubRegisterEq(RegA, RegB);
+  };
+
+  if (!ST.is64Bit())
+    return llvm::any_of(
+               SmallVector<MCRegister>{X86::EAX, X86::ECX, X86::EDX},
+               [&](MCRegister &RegA) { return IsSubReg(RegA, Reg); }) ||
+           (ST.hasMMX() && X86::VR64RegClass.contains(Reg));
+
+  CallingConv::ID CC = MF.getFunction().getCallingConv();
+
+  if (CC == CallingConv::X86_64_SysV && IsSubReg(X86::RAX, Reg))
+    return true;
+
+  if (llvm::any_of(
+          SmallVector<MCRegister>{X86::RDX, X86::RCX, X86::R8, X86::R9},
+          [&](MCRegister &RegA) { return IsSubReg(RegA, Reg); }))
+    return true;
+
+  if (CC != CallingConv::Win64 &&
+      llvm::any_of(SmallVector<MCRegister>{X86::RDI, X86::RSI},
+                   [&](MCRegister &RegA) { return IsSubReg(RegA, Reg); }))
+    return true;
+
+  if (ST.hasSSE1() && X86::VR128RegClass.contains(Reg))
+    return true;
+
+  return false;
+}
+
+bool X86RegisterInfo::isFixedRegister(const MachineFunction &MF,
+                                      MCRegister PhysReg) const {
+  const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();
+  const TargetRegisterInfo &TRI = *ST.getRegisterInfo();
+
+  // Stack pointer.
+  if (TRI.isSuperOrSubRegisterEq(X86::RSP, PhysReg))
+    return true;
+
+  // Don't use the frame pointer if it's being used.
+  const X86FrameLowering &TFI = *getFrameLowering(MF);
+  if (TFI.hasFP(MF) && TRI.isSuperOrSubRegisterEq(X86::RBP, PhysReg))
+    return true;
+
+  return X86GenRegisterInfo::isFixedRegister(MF, PhysReg);
+}
+
 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
Index: llvm/lib/Target/X86/X86FrameLowering.h
===================================================================
--- llvm/lib/Target/X86/X86FrameLowering.h
+++ llvm/lib/Target/X86/X86FrameLowering.h
@@ -233,6 +233,10 @@
                                        const DebugLoc &DL, uint64_t Offset,
                                        uint64_t Align) const;
 
+  /// Emit target zero call-used regs.
+  void emitZeroCallUsedRegs(BitVector RegsToZero,
+                            MachineBasicBlock &MBB) const override;
+
   void adjustFrameForMsvcCxxEh(MachineFunction &MF) const;
 
   /// Aligns the stack pointer by ANDing it with -MaxAlign.
Index: llvm/lib/Target/X86/X86FrameLowering.cpp
===================================================================
--- llvm/lib/Target/X86/X86FrameLowering.cpp
+++ llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "X86FrameLowering.h"
+#include "MCTargetDesc/X86MCTargetDesc.h"
 #include "X86InstrBuilder.h"
 #include "X86InstrInfo.h"
 #include "X86MachineFunctionInfo.h"
@@ -492,6 +493,133 @@
   }
 }
 
+static MachineBasicBlock::iterator
+skipCalleeSavedPopInstrs(MachineBasicBlock &MBB,
+                         MachineBasicBlock::iterator MBBI) {
+  // Skip the callee-saved pop instructions.
+  MachineBasicBlock::iterator FirstCSPop = MBBI;
+  while (MBBI != MBB.begin()) {
+    MachineBasicBlock::iterator PI = std::prev(MBBI);
+    unsigned Opc = PI->getOpcode();
+
+    if (Opc != X86::DBG_VALUE && !PI->isTerminator()) {
+      if ((Opc != X86::POP32r || !PI->getFlag(MachineInstr::FrameDestroy)) &&
+          (Opc != X86::POP64r || !PI->getFlag(MachineInstr::FrameDestroy)) &&
+          (Opc != X86::BTR64ri8 || !PI->getFlag(MachineInstr::FrameDestroy)) &&
+          (Opc != X86::ADD64ri8 || !PI->getFlag(MachineInstr::FrameDestroy)))
+        break;
+      FirstCSPop = PI;
+    }
+
+    --MBBI;
+  }
+  return MBBI = FirstCSPop;
+}
+
+void X86FrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
+                                            MachineBasicBlock &MBB) const {
+  const MachineFunction &MF = *MBB.getParent();
+  const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();
+
+  // Don't clear registers that will just be reset before exiting.
+  llvm::for_each(MF.getFrameInfo().getCalleeSavedInfo(), [&](auto CSReg) {
+    llvm::for_each(TRI->sub_and_superregs_inclusive(CSReg.getReg()),
+                   [&](auto Reg) { RegsToZero.reset(Reg); });
+  });
+
+  // Insertion point.
+  MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator();
+  MBBI = skipCalleeSavedPopInstrs(MBB, MBBI);
+
+  DebugLoc DL;
+  if (MBBI != MBB.end())
+    DL = MBBI->getDebugLoc();
+
+  // 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 NumFPRegs = ST.is64Bit() ? 8 : 7;
+    for (unsigned i = 0; i != NumFPRegs; ++i)
+      BuildMI(MBB, MBBI, DL, TII.get(X86::LD_F0));
+
+    for (unsigned i = 0; i != NumFPRegs; ++i)
+      BuildMI(MBB, MBBI, DL, TII.get(X86::ST_FPrr)).addReg(X86::ST0);
+    break;
+  }
+
+  // For GPRs, we only care to clear out the largest register: 32-bit or 64-bit.
+  for (MCRegister Reg : RegsToZero.set_bits())
+    if (TRI->isGeneralPurposeRegister(MF, Reg)) {
+      if (!IsLP64 || TRI->getRegSizeInBits(Reg, MF.getRegInfo()) <= 32)
+        Reg = getX86SubSuperRegisterOrZero(Reg, 32);
+
+      if (!RegsToZero[getX86SubSuperRegisterOrZero(Reg, 64)])
+        RegsToZero.set(Reg);
+      else if (!IsLP64)
+        RegsToZero.reset(getX86SubSuperRegisterOrZero(Reg, 64));
+
+      llvm::for_each(TRI->subregs(Reg),
+                     [&](MCRegister SReg) { RegsToZero.reset(SReg); });
+    }
+
+  // Now zero out the GPRs.
+  for (MCRegister Reg : RegsToZero.set_bits())
+    if (TRI->isGeneralPurposeRegister(MF, Reg)) {
+      unsigned XorOp = TRI->getRegSizeInBits(Reg, MF.getRegInfo()) == 64
+                           ? X86::XOR64rr
+                           : X86::XOR32rr;
+      BuildMI(MBB, MBBI, DL, TII.get(XorOp), Reg)
+          .addReg(Reg, RegState::Undef)
+          .addReg(Reg, RegState::Undef);
+    }
+
+  // Zero out vector registers.
+  for (auto Reg : RegsToZero.set_bits()) {
+    if (X86::RFP80RegClass.contains(Reg))
+      // Handled above.
+      continue;
+
+    if (ST.hasMMX() && X86::VR64RegClass.contains(Reg))
+      // FIXME: Ignore MMX registers?
+      continue;
+
+    unsigned XorOp;
+    if (X86::VR128RegClass.contains(Reg)) {
+      // XMM#
+      if (!ST.hasSSE1())
+        continue;
+      XorOp = X86::PXORrr;
+    } else if (X86::VR256RegClass.contains(Reg)) {
+      // YMM#
+      if (!ST.hasAVX())
+        continue;
+      XorOp = X86::VPXORrr;
+    } else if (X86::VR512RegClass.contains(Reg)) {
+      // ZMM#
+      if (!ST.hasAVX512())
+        continue;
+      XorOp = X86::VPXORYrr;
+    } else if (X86::VK1RegClass.contains(Reg) ||
+               X86::VK2RegClass.contains(Reg) ||
+               X86::VK4RegClass.contains(Reg) ||
+               X86::VK8RegClass.contains(Reg) ||
+               X86::VK16RegClass.contains(Reg)) {
+      if (!ST.hasVLX())
+        continue;
+      XorOp = ST.hasBWI() ? X86::KXORQrr : X86::KXORWrr;
+    } else {
+      continue;
+    }
+
+    BuildMI(MBB, MBBI, DL, TII.get(XorOp), Reg)
+        .addReg(Reg, RegState::Undef)
+        .addReg(Reg, RegState::Undef);
+  }
+}
+
 void X86FrameLowering::emitStackProbe(
     MachineFunction &MF, MachineBasicBlock &MBB,
     MachineBasicBlock::iterator MBBI, const DebugLoc &DL, bool InProlog,
@@ -2157,24 +2285,8 @@
     }
   }
 
+  MBBI = skipCalleeSavedPopInstrs(MBB, MBBI);
   MachineBasicBlock::iterator FirstCSPop = MBBI;
-  // Skip the callee-saved pop instructions.
-  while (MBBI != MBB.begin()) {
-    MachineBasicBlock::iterator PI = std::prev(MBBI);
-    unsigned Opc = PI->getOpcode();
-
-    if (Opc != X86::DBG_VALUE && !PI->isTerminator()) {
-      if ((Opc != X86::POP32r || !PI->getFlag(MachineInstr::FrameDestroy)) &&
-          (Opc != X86::POP64r || !PI->getFlag(MachineInstr::FrameDestroy)) &&
-          (Opc != X86::BTR64ri8 || !PI->getFlag(MachineInstr::FrameDestroy)) &&
-          (Opc != X86::ADD64ri8 || !PI->getFlag(MachineInstr::FrameDestroy)))
-        break;
-      FirstCSPop = PI;
-    }
-
-    --MBBI;
-  }
-  MBBI = FirstCSPop;
 
   if (IsFunclet && Terminator->getOpcode() == X86::CATCHRET)
     emitCatchRetReturnValue(MBB, FirstCSPop, &*Terminator);
Index: llvm/lib/CodeGen/PrologEpilogInserter.cpp
===================================================================
--- llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -130,6 +130,7 @@
   void replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF,
                            int &SPAdj);
   void insertPrologEpilogCode(MachineFunction &MF);
+  void insertZeroCallUsedRegs(MachineFunction &MF);
 };
 
 } // end anonymous namespace
@@ -1141,6 +1142,9 @@
   for (MachineBasicBlock *SaveBlock : SaveBlocks)
     TFI.emitPrologue(MF, *SaveBlock);
 
+  // Zero call used registers before restoring callee-saved registers.
+  insertZeroCallUsedRegs(MF);
+
   // Add epilogue to restore the callee-save registers in each exiting block.
   for (MachineBasicBlock *RestoreBlock : RestoreBlocks)
     TFI.emitEpilogue(MF, *RestoreBlock);
@@ -1171,6 +1175,99 @@
       TFI.adjustForHiPEPrologue(MF, *SaveBlock);
 }
 
+/// insertZeroCallUsedRegs - Zero out call used registers.
+void PEI::insertZeroCallUsedRegs(MachineFunction &MF) {
+  const Function &F = MF.getFunction();
+
+  if (!F.hasFnAttribute("zero-call-used-regs"))
+    return;
+
+  using namespace ZeroCallUsedRegs;
+
+  ZeroCallUsedRegsKind ZeroRegsKind =
+      StringSwitch<ZeroCallUsedRegsKind>(
+          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 OnlyGPR = static_cast<unsigned>(ZeroRegsKind) & ONLY_GPR;
+  const bool OnlyUsed = static_cast<unsigned>(ZeroRegsKind) & ONLY_USED;
+  const bool OnlyArg = static_cast<unsigned>(ZeroRegsKind) & ONLY_ARG;
+
+  const auto &ST = MF.getSubtarget();
+  const TargetRegisterInfo &TRI = *ST.getRegisterInfo();
+  const BitVector AllocatableSet(TRI.getAllocatableSet(MF));
+
+  // Mark all used, allocatable registers.
+  BitVector UsedRegs(TRI.getNumRegs());
+  if (OnlyUsed) {
+    llvm::for_each(MF, [&](const MachineBasicBlock &MBB) {
+      llvm::for_each(MBB, [&](const MachineInstr &MI) {
+        llvm::for_each(MI.operands(), [&](MachineOperand MO) {
+          if (!MO.isReg())
+            return;
+
+          MCRegister Reg = MO.getReg();
+          if ((MO.isDef() || MO.isUse()) && AllocatableSet[Reg])
+            UsedRegs.set(Reg);
+        });
+      });
+    });
+  }
+
+  BitVector RegsToZero(TRI.getNumRegs());
+  for (auto Reg : AllocatableSet.set_bits()) {
+    // Skip over fixed registers.
+    if (TRI.isFixedRegister(MF, Reg))
+      continue;
+
+    // Want only general purpose registers.
+    if (OnlyGPR && !TRI.isGeneralPurposeRegister(MF, Reg))
+      continue;
+
+    // Want only used registers.
+    if (OnlyUsed && !UsedRegs[Reg])
+      continue;
+
+    // Want only registers used for arguments.
+    if (OnlyArg && !TRI.isArgumentRegister(MF, Reg))
+      continue;
+
+    RegsToZero.set(Reg);
+  }
+
+  // Remove registers that are live when leaving the function.
+  llvm::for_each(MF, [&](const MachineBasicBlock &MBB) {
+    llvm::for_each(MBB.terminators(), [&](const MachineInstr &MI) {
+      if (!MI.isReturn())
+        return;
+
+      for (auto MO : MI.operands()) {
+        if (!MO.isReg())
+          continue;
+
+        for (const MCPhysReg &SReg :
+             TRI.sub_and_superregs_inclusive(MO.getReg()))
+          RegsToZero.reset(SReg);
+      }
+    });
+  });
+
+  const TargetFrameLowering &TFI = *MF.getSubtarget().getFrameLowering();
+  for (auto RestoreBlock : RestoreBlocks)
+    TFI.emitZeroCallUsedRegs(RegsToZero, *RestoreBlock);
+}
+
 /// replaceFrameIndices - Replace all MO_FrameIndex operands with physical
 /// register references and actual offsets.
 void PEI::replaceFrameIndices(MachineFunction &MF) {
Index: llvm/lib/CodeGen/MachineRegisterInfo.cpp
===================================================================
--- llvm/lib/CodeGen/MachineRegisterInfo.cpp
+++ llvm/lib/CodeGen/MachineRegisterInfo.cpp
@@ -651,3 +651,18 @@
   }
   return false;
 }
+
+bool MachineRegisterInfo::isArgumentRegister(const MachineFunction &MF,
+                                             MCRegister Reg) const {
+  return getTargetRegisterInfo()->isArgumentRegister(MF, Reg);
+}
+
+bool MachineRegisterInfo::isFixedRegister(const MachineFunction &MF,
+                                          MCRegister Reg) const {
+  return getTargetRegisterInfo()->isFixedRegister(MF, Reg);
+}
+
+bool MachineRegisterInfo::isGeneralPurposeRegister(const MachineFunction &MF,
+                                                   MCRegister Reg) const {
+  return getTargetRegisterInfo()->isGeneralPurposeRegister(MF, Reg);
+}
Index: llvm/include/llvm/Target/Target.td
===================================================================
--- llvm/include/llvm/Target/Target.td
+++ llvm/include/llvm/Target/Target.td
@@ -389,6 +389,14 @@
   list<string> RegAsmNames = RegNames;
 }
 
+// RegisterCategory - This class is a list of RegisterClasses that belong to a
+// general cateogry --- e.g. "general purpose" or "fixed" registers. This is
+// useful for identifying registers in a generic way instead of having
+// information about a specific target's registers.
+class RegisterCategory<list<RegisterClass> classes> {
+  // Classes - A list of register classes that fall within the category.
+  list<RegisterClass> Classes = classes;
+}
 
 //===----------------------------------------------------------------------===//
 // DwarfRegNum - This class provides a mapping of the llvm register enumeration
Index: llvm/include/llvm/Support/CodeGen.h
===================================================================
--- llvm/include/llvm/Support/CodeGen.h
+++ 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
Index: llvm/include/llvm/CodeGen/TargetRegisterInfo.h
===================================================================
--- llvm/include/llvm/CodeGen/TargetRegisterInfo.h
+++ llvm/include/llvm/CodeGen/TargetRegisterInfo.h
@@ -567,6 +567,24 @@
   virtual bool isCalleeSavedPhysReg(MCRegister PhysReg,
                                     const MachineFunction &MF) const;
 
+  /// Returns true if PhysReg can be used as an argument to a function.
+  virtual bool isArgumentRegister(const MachineFunction &MF,
+                                  MCRegister PhysReg) const {
+    return false;
+  }
+
+  /// Returns true if PhysReg is a fixed register.
+  virtual bool isFixedRegister(const MachineFunction &MF,
+                               MCRegister PhysReg) const {
+    return false;
+  }
+
+  /// Returns true if PhysReg is a general purpose register.
+  virtual bool isGeneralPurposeRegister(const MachineFunction &MF,
+                                        MCRegister PhysReg) const {
+    return false;
+  }
+
   /// Prior to adding the live-out mask to a stackmap or patchpoint
   /// instruction, provide the target the opportunity to adjust it (mainly to
   /// remove pseudo-registers that should be ignored).
Index: llvm/include/llvm/CodeGen/TargetFrameLowering.h
===================================================================
--- llvm/include/llvm/CodeGen/TargetFrameLowering.h
+++ llvm/include/llvm/CodeGen/TargetFrameLowering.h
@@ -213,6 +213,10 @@
   virtual void emitEpilogue(MachineFunction &MF,
                             MachineBasicBlock &MBB) const = 0;
 
+  /// emitZeroCallUsedRegs - Zeros out call used registers.
+  virtual void emitZeroCallUsedRegs(BitVector RegsToZero,
+                                    MachineBasicBlock &MBB) const {}
+
   /// With basic block sections, emit callee saved frame moves for basic blocks
   /// that are in a different section.
   virtual void
Index: llvm/include/llvm/CodeGen/MachineRegisterInfo.h
===================================================================
--- llvm/include/llvm/CodeGen/MachineRegisterInfo.h
+++ llvm/include/llvm/CodeGen/MachineRegisterInfo.h
@@ -229,6 +229,16 @@
   /// Returns true if the updated CSR list was initialized and false otherwise.
   bool isUpdatedCSRsInitialized() const { return IsUpdatedCSRsInitialized; }
 
+  /// Returns true if a register can be used as an argument to a function.
+  bool isArgumentRegister(const MachineFunction &MF, MCRegister Reg) const;
+
+  /// Returns true if a register is a fixed register.
+  bool isFixedRegister(const MachineFunction &MF, MCRegister Reg) const;
+
+  /// Returns true if a register is a general purpose register.
+  bool isGeneralPurposeRegister(const MachineFunction &MF,
+                                MCRegister Reg) const;
+
   /// Disables the register from the list of CSRs.
   /// I.e. the register will not appear as part of the CSR mask.
   /// \see UpdatedCalleeSavedRegs.
Index: clang/test/Sema/zero_call_used_regs.c
===================================================================
--- /dev/null
+++ 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");
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -192,4 +192,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.
Index: clang/test/CodeGen/zero-call-used-regs.c
===================================================================
--- /dev/null
+++ 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(kind) __attribute__((zero_call_used_regs(kind)))
+
+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() {
+}
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -7837,6 +7837,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<ZeroCallUsedRegsAttr>();
+  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<FunctionDecl>(D);
@@ -8581,6 +8599,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:
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -5884,6 +5884,16 @@
   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);
+
+  if (Arg *A = Args.getLastArg(options::OPT_fzero_call_used_regs_EQ)) {
+    // FIXME: There's no reason for this to be restricted to X86. The backend
+    // code needs to be changed to include the appropriate function calls
+    // automatically.
+    if (!Triple.isX86())
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << A->getAsString(Args) << TripleStr;
+  }
 
   // AltiVec-like language extensions aren't relevant for assembling.
   if (!isa<PreprocessJobAction>(JA) || Output.getType() != types::TY_PP_Asm)
Index: clang/lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.cpp
+++ clang/lib/CodeGen/CodeGenFunction.cpp
@@ -981,6 +981,10 @@
              CGM.getCodeGenOpts().StackAlignment))
     Fn->addFnAttr("stackrealign");
 
+  // "main" doesn't need to zero out call-used registers.
+  if (FD && FD->isMain())
+    Fn->removeFnAttr("zero-call-used-regs");
+
   llvm::BasicBlock *EntryBB = createBasicBlock("entry", CurFn);
 
   // Create a marker to make it easy to insert allocas into the entryblock
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ clang/lib/CodeGen/CGCall.cpp
@@ -1868,6 +1868,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()) {
@@ -2156,6 +2187,15 @@
       FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening);
     if (TargetDecl->hasAttr<NoSplitStackAttr>())
       FuncAttrs.removeAttribute("split-stack");
+    if (TargetDecl->hasAttr<ZeroCallUsedRegsAttr>()) {
+      // A function "__attribute__((...))" overrides the command-line flag.
+      auto Kind =
+          TargetDecl->getAttr<ZeroCallUsedRegsAttr>()->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.
Index: clang/include/clang/Driver/Options.td
===================================================================
--- clang/include/clang/Driver/Options.td
+++ clang/include/clang/Driver/Options.td
@@ -2899,6 +2899,14 @@
     HelpText<"Enable matrix data type and related builtin functions">,
     MarshallingInfoFlag<LangOpts<"MatrixTypes">>;
 
+def fzero_call_used_regs_EQ
+    : Joined<["-"], "fzero-call-used-regs=">, Group<f_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<CodeGenOpts<"ZeroCallUsedRegs">, "Skip">;
 
 def fdebug_types_section: Flag <["-"], "fdebug-types-section">, Group<f_Group>,
   HelpText<"Place debug types in their own section (ELF Only)">;
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -464,6 +464,10 @@
 /// Whether to skip RAX setup when passing variable arguments (x86 only).
 CODEGENOPT(SkipRaxSetup, 1, 0)
 
+/// 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
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -6263,3 +6263,37 @@
   }
   }];
 }
+
+def ZeroCallUsedRegsDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+This attribute, when attached to a function, 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. This choice overrides any
+  command-line arguments.
+- ``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
+  }];
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -2561,6 +2561,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];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to