[prev in list] [next in list] [prev in thread] [next in thread]
List: llvm-commits
Subject: [PATCH] D110869: [X86] Implement -fzero-call-used-regs option
From: Bill Wendling via Phabricator via llvm-commits <llvm-commits () lists ! llvm ! org>
Date: 2021-09-30 22:31:17
Message-ID: c2lR0QdrScetWy7YuXd2-g () geopod-ismtpd-6-2
[Download RAW message or body]
void updated this revision to Diff 376386.
void marked 3 inline comments as done.
void added a comment.
Fixed some spelling and variable names.
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/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/Support/CodeGen.h
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/test/CodeGen/X86/zero-call-used-regs-all-arg.ll
llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr-arg.ll
llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr.ll
llvm/test/CodeGen/X86/zero-call-used-regs-all.ll
llvm/test/CodeGen/X86/zero-call-used-regs-fmod.ll
llvm/test/CodeGen/X86/zero-call-used-regs-skip.ll
llvm/test/CodeGen/X86/zero-call-used-regs-smoke-tests.ll
llvm/test/CodeGen/X86/zero-call-used-regs-used-arg.ll
llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr-arg.ll
llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr.ll
llvm/test/CodeGen/X86/zero-call-used-regs-used.ll
["D110869.376386.patch" (D110869.376386.patch)]
Index: llvm/test/CodeGen/X86/zero-call-used-regs-used.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-used-gpr-arg.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-used-arg.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-smoke-tests.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-skip.ll
===================================================================
--- /dev/null
+++ 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" }
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: 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 }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-all.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-all-gpr-arg.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/test/CodeGen/X86/zero-call-used-regs-all-arg.ll
===================================================================
--- /dev/null
+++ 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" }
Index: llvm/lib/Target/X86/X86RegisterInfo.h
===================================================================
--- llvm/lib/Target/X86/X86RegisterInfo.h
+++ 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;
Index: llvm/lib/Target/X86/X86RegisterInfo.cpp
===================================================================
--- llvm/lib/Target/X86/X86RegisterInfo.cpp
+++ llvm/lib/Target/X86/X86RegisterInfo.cpp
@@ -618,6 +618,68 @@
return Reserved;
}
+BitVector X86RegisterInfo::getArgumentRegs(const MachineFunction &MF) const {
+ const X86Subtarget &Subtarget = MF.getSubtarget<X86Subtarget>();
+ bool HasSSE = Subtarget.hasSSE1();
+ bool HasAVX = Subtarget.hasAVX();
+ bool HasAVX512 = Subtarget.hasAVX512();
+
+ BitVector ArgumentRegs(getNumRegs());
+
+ auto SetArgRegs = [&ArgumentRegs](ArrayRef<MCRegister> 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
Index: llvm/lib/Target/X86/X86FrameLowering.h
===================================================================
--- llvm/lib/Target/X86/X86FrameLowering.h
+++ 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.
Index: llvm/lib/Target/X86/X86FrameLowering.cpp
===================================================================
--- llvm/lib/Target/X86/X86FrameLowering.cpp
+++ 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<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 Is64Bit = STI.is64Bit();
+ 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 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.
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: 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
@@ -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.
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
@@ -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<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);
@@ -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:
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ 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<PreprocessJobAction>(JA) || Output.getType() != types::TY_PP_Asm)
Index: clang/lib/CodeGen/CGCall.cpp
===================================================================
--- clang/lib/CodeGen/CGCall.cpp
+++ 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<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
@@ -2856,6 +2856,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
@@ -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
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -6101,3 +6101,36 @@
}
}];
}
+
+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.
+- ``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
@@ -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];
[Attachment #4 (text/plain)]
_______________________________________________
llvm-commits mailing list
llvm-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic