diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3920,6 +3920,10 @@ int bar(int y); // bar can be built with the stack protector. +A callee that has a stack protector will not be inlined into a +``__attribute__((no_stack_protector))`` caller, and vice-versa, even if the +callee is marked ``__attribute__((always_inline))``. + }]; } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1594,14 +1594,14 @@ if (!hasUnwindExceptions(LangOpts)) B.addAttribute(llvm::Attribute::NoUnwind); - if (!D || !D->hasAttr()) { - if (LangOpts.getStackProtector() == LangOptions::SSPOn) - B.addAttribute(llvm::Attribute::StackProtect); - else if (LangOpts.getStackProtector() == LangOptions::SSPStrong) - B.addAttribute(llvm::Attribute::StackProtectStrong); - else if (LangOpts.getStackProtector() == LangOptions::SSPReq) - B.addAttribute(llvm::Attribute::StackProtectReq); - } + if (D && D->hasAttr()) + B.addAttribute(llvm::Attribute::NoStackProtect); + else if (LangOpts.getStackProtector() == LangOptions::SSPOn) + B.addAttribute(llvm::Attribute::StackProtect); + else if (LangOpts.getStackProtector() == LangOptions::SSPStrong) + B.addAttribute(llvm::Attribute::StackProtectStrong); + else if (LangOpts.getStackProtector() == LangOptions::SSPReq) + B.addAttribute(llvm::Attribute::StackProtectReq); if (!D) { // If we don't have a declaration to control inlining, the function isn't diff --git a/clang/test/CodeGen/stack-protector.c b/clang/test/CodeGen/stack-protector.c --- a/clang/test/CodeGen/stack-protector.c +++ b/clang/test/CodeGen/stack-protector.c @@ -36,7 +36,7 @@ // SSPREQ: attributes #[[A]] = {{.*}} sspreq // SAFESTACK-NOSSP: attributes #[[A]] = {{.*}} safestack -// SAFESTACK-NOSSP-NOT: ssp +// SAFESTACK-NOSSP-NOT: attributes #[[A]] = {{.*}} ssp // SAFESTACK-SSP: attributes #[[A]] = {{.*}} safestack ssp{{ }} // SAFESTACK-SSPSTRONG: attributes #[[A]] = {{.*}} safestack sspstrong @@ -47,6 +47,11 @@ // SSPSTRONG-NOT: attributes #[[B]] = {{.*}} sspstrong // SSPREQ-NOT: attributes #[[B]] = {{.*}} sspreq +// NOSSP: attributes #[[B]] = {{.*}} nossp +// SSP: attributes #[[B]] = {{.*}} nossp +// SSPSTRONG: attributes #[[B]] = {{.*}} nossp +// SSPREQ: attributes #[[B]] = {{.*}} nossp + // SAFESTACK-SSP: attributes #[[B]] = {{.*}} safestack // SAFESTACK-SSP-NOT: attributes #[[B]] = {{.*}} safestack ssp{{ }} // SAFESTACK-SSPSTRONG: attributes #[[B]] = {{.*}} safestack diff --git a/clang/test/Frontend/optimization-remark-missed-inline-stack-protectors.c b/clang/test/Frontend/optimization-remark-missed-inline-stack-protectors.c new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/optimization-remark-missed-inline-stack-protectors.c @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -stack-protector 2 -Rpass-missed=inline -O2 -verify %s -emit-llvm-only + +void side_effect(void); + +void foo(void) { + side_effect(); +} + +// expected-remark@+3 {{foo will not be inlined into bar: stack protected callee but caller requested no stack protector}} +__attribute__((no_stack_protector)) +void bar(void) { + foo(); +} + +// expected-remark@+2 {{bar will not be inlined into baz: stack protected caller but callee requested no stack protector}} +void baz(void) { + bar(); +} + +void ssp_callee(void); + +// No issue; matching stack protections. +void ssp_caller(void) { + ssp_callee(); +} + +__attribute__((no_stack_protector)) +void nossp_callee(void); + +// No issue; matching stack protections. +__attribute__((no_stack_protector)) +void nossp_caller(void) { + nossp_callee(); +} diff --git a/llvm/bindings/go/llvm/ir_test.go b/llvm/bindings/go/llvm/ir_test.go --- a/llvm/bindings/go/llvm/ir_test.go +++ b/llvm/bindings/go/llvm/ir_test.go @@ -78,6 +78,7 @@ "returns_twice", "signext", "safestack", + "nossp", "ssp", "sspreq", "sspstrong", diff --git a/llvm/bindings/ocaml/llvm/llvm.ml b/llvm/bindings/ocaml/llvm/llvm.ml --- a/llvm/bindings/ocaml/llvm/llvm.ml +++ b/llvm/bindings/ocaml/llvm/llvm.ml @@ -122,6 +122,7 @@ | Noinline | Alwaysinline | Optsize + | Nossp | Ssp | Sspreq | Alignment of int diff --git a/llvm/docs/BitCodeFormat.rst b/llvm/docs/BitCodeFormat.rst --- a/llvm/docs/BitCodeFormat.rst +++ b/llvm/docs/BitCodeFormat.rst @@ -1070,6 +1070,7 @@ * code 68: ``noundef`` * code 69: ``byref`` * code 70: ``mustprogress`` +* code 71: ``nossp`` .. note:: The ``allocsize`` attribute has a special encoding for its arguments. Its two diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1824,6 +1824,22 @@ undefined behavior, the undefined behavior may be observed even if the call site is dead code. +``nossp`` + This attribute indicates the function should not emit a stack smashing + protector. This is useful for code that intentionally manipulates the stack + canary, such as operating system kernel code that must save/restore such + canary values on context switch. + + If a function with the ``nossp`` attribute calls a callee function that has + a stack protector function attribute, such as ``ssp``, ``sspreq``, or + ``sspstrong`` (or vice-versa), then the callee will not be inline + substituted into the caller. Even when the callee is ``alwaysinline``, the + above holds. + + Such inlining might break assumptions in the function that was built + without stack protection. This permits the functions that would have stack + protection to retain their stack protector. + ``ssp`` This attribute indicates that the function should emit a stack smashing protector. It is in the form of a "canary" --- a random value diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -651,6 +651,7 @@ ATTR_KIND_NOUNDEF = 68, ATTR_KIND_BYREF = 69, ATTR_KIND_MUSTPROGRESS = 70, + ATTR_KIND_NO_STACK_PROTECT = 71, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -179,6 +179,9 @@ /// Function can be speculated. def Speculatable : EnumAttr<"speculatable">; +/// Stack protection explicitly disabled. +def NoStackProtect : EnumAttr<"nossp">; + /// Stack protection. def StackProtect : EnumAttr<"ssp">; diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -678,6 +678,7 @@ KEYWORD(signext); KEYWORD(speculatable); KEYWORD(sret); + KEYWORD(nossp); KEYWORD(ssp); KEYWORD(sspreq); KEYWORD(sspstrong); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1338,6 +1338,9 @@ case lltok::kw_returns_twice: B.addAttribute(Attribute::ReturnsTwice); break; case lltok::kw_speculatable: B.addAttribute(Attribute::Speculatable); break; + case lltok::kw_nossp: + B.addAttribute(Attribute::NoStackProtect); + break; case lltok::kw_ssp: B.addAttribute(Attribute::StackProtect); break; case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break; case lltok::kw_sspstrong: @@ -1749,6 +1752,7 @@ case lltok::kw_sanitize_memory: case lltok::kw_sanitize_thread: case lltok::kw_speculative_load_hardening: + case lltok::kw_nossp: case lltok::kw_ssp: case lltok::kw_sspreq: case lltok::kw_sspstrong: @@ -1854,6 +1858,7 @@ case lltok::kw_sanitize_memory: case lltok::kw_sanitize_thread: case lltok::kw_speculative_load_hardening: + case lltok::kw_nossp: case lltok::kw_ssp: case lltok::kw_sspreq: case lltok::kw_sspstrong: diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -223,6 +223,7 @@ kw_returns_twice, kw_signext, kw_speculatable, + kw_nossp, kw_ssp, kw_sspreq, kw_sspstrong, diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -697,6 +697,8 @@ return bitc::ATTR_KIND_SPECULATABLE; case Attribute::StackAlignment: return bitc::ATTR_KIND_STACK_ALIGNMENT; + case Attribute::NoStackProtect: + return bitc::ATTR_KIND_NO_STACK_PROTECT; case Attribute::StackProtect: return bitc::ATTR_KIND_STACK_PROTECT; case Attribute::StackProtectReq: diff --git a/llvm/lib/CodeGen/StackProtector.cpp b/llvm/lib/CodeGen/StackProtector.cpp --- a/llvm/lib/CodeGen/StackProtector.cpp +++ b/llvm/lib/CodeGen/StackProtector.cpp @@ -270,13 +270,15 @@ /// regardless of size, functions with any buffer regardless of type and size, /// functions with aggregates that contain any buffer regardless of type and /// size, and functions that contain stack-based variables that have had their -/// address taken. +/// address taken. The heuristic will be disregarded for functions explicitly +/// marked nossp. bool StackProtector::RequiresStackProtector() { bool Strong = false; bool NeedsProtector = false; HasPrologue = findStackProtectorIntrinsic(*F); - if (F->hasFnAttribute(Attribute::SafeStack)) + if (F->hasFnAttribute(Attribute::SafeStack) || + F->hasFnAttribute(Attribute::NoStackProtect)) return false; // We are constructing the OptimizationRemarkEmitter on the fly rather than diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -425,6 +425,8 @@ return "speculative_load_hardening"; if (hasAttribute(Attribute::Speculatable)) return "speculatable"; + if (hasAttribute(Attribute::NoStackProtect)) + return "nossp"; if (hasAttribute(Attribute::StackProtect)) return "ssp"; if (hasAttribute(Attribute::StackProtectReq)) @@ -1939,9 +1941,17 @@ /// If the inlined function had a higher stack protection level than the /// calling function, then bump up the caller's stack protection level. static void adjustCallerSSPLevel(Function &Caller, const Function &Callee) { + assert(!(Callee.hasFnAttribute(Attribute::NoStackProtect) && + (Caller.hasFnAttribute(Attribute::StackProtect) || + Caller.hasFnAttribute(Attribute::StackProtectStrong) || + Caller.hasFnAttribute(Attribute::StackProtectReq))) && + "stack protected caller but callee requested no stack protector"); + assert(!(Caller.hasFnAttribute(Attribute::NoStackProtect) && + (Callee.hasFnAttribute(Attribute::StackProtect) || + Callee.hasFnAttribute(Attribute::StackProtectStrong) || + Callee.hasFnAttribute(Attribute::StackProtectReq))) && + "stack protected callee but caller requested no stack protector"); // If upgrading the SSP attribute, clear out the old SSP Attributes first. - // Having multiple SSP attributes doesn't actually hurt, but it adds useless - // clutter to the IR. AttrBuilder OldSSPAttr; OldSSPAttr.addAttribute(Attribute::StackProtect) .addAttribute(Attribute::StackProtectStrong) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1577,6 +1577,7 @@ case Attribute::NoInline: case Attribute::AlwaysInline: case Attribute::OptimizeForSize: + case Attribute::NoStackProtect: case Attribute::StackProtect: case Attribute::StackProtectReq: case Attribute::StackProtectStrong: @@ -1972,6 +1973,19 @@ CheckFailed( "\"patchable-function-entry\" takes an unsigned integer: " + S, V); } + { + unsigned N = 0; + if (Attrs.hasFnAttribute(Attribute::NoStackProtect)) + ++N; + if (Attrs.hasFnAttribute(Attribute::StackProtect)) + ++N; + if (Attrs.hasFnAttribute(Attribute::StackProtectReq)) + ++N; + if (Attrs.hasFnAttribute(Attribute::StackProtectStrong)) + ++N; + Assert(N < 2, + "nossp, ssp, sspreq, sspstrong fn attrs are mutually exclusive", V); + } } void Verifier::verifyFunctionMetadata( diff --git a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp @@ -68,6 +68,7 @@ .Case("sanitize_thread", Attribute::SanitizeThread) .Case("sanitize_memtag", Attribute::SanitizeMemTag) .Case("speculative_load_hardening", Attribute::SpeculativeLoadHardening) + .Case("nossp", Attribute::NoStackProtect) .Case("ssp", Attribute::StackProtect) .Case("sspreq", Attribute::StackProtectReq) .Case("sspstrong", Attribute::StackProtectStrong) diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -925,6 +925,7 @@ case Attribute::SanitizeHWAddress: case Attribute::SanitizeMemTag: case Attribute::SpeculativeLoadHardening: + case Attribute::NoStackProtect: case Attribute::StackProtect: case Attribute::StackProtectReq: case Attribute::StackProtectStrong: diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -1676,6 +1676,22 @@ return InlineResult::failure("incompatible GC"); } + // Inlining a function that explicitly should not have a stack protector may + // break the code if inlined into a function that does have a stack + // protector. + if (LLVM_UNLIKELY(Caller->hasFnAttribute(Attribute::NoStackProtect))) + if (CalledFunc->hasFnAttribute(Attribute::StackProtect) || + CalledFunc->hasFnAttribute(Attribute::StackProtectStrong) || + CalledFunc->hasFnAttribute(Attribute::StackProtectReq)) + return InlineResult::failure( + "stack protected callee but caller requested no stack protector"); + if (LLVM_UNLIKELY(CalledFunc->hasFnAttribute(Attribute::NoStackProtect))) + if (Caller->hasFnAttribute(Attribute::StackProtect) || + Caller->hasFnAttribute(Attribute::StackProtectStrong) || + Caller->hasFnAttribute(Attribute::StackProtectReq)) + return InlineResult::failure( + "stack protected caller but callee requested no stack protector"); + // Get the personality function from the callee if it contains a landing pad. Constant *CalledPersonality = CalledFunc->hasPersonalityFn() diff --git a/llvm/test/CodeGen/X86/stack-protector-2.ll b/llvm/test/CodeGen/X86/stack-protector-2.ll --- a/llvm/test/CodeGen/X86/stack-protector-2.ll +++ b/llvm/test/CodeGen/X86/stack-protector-2.ll @@ -1,5 +1,5 @@ ; RUN: llc -mtriple=x86_64-pc-linux-gnu -start-before=stack-protector -stop-after=stack-protector -o - < %s | FileCheck %s -; Bugs 42238/43308: Test some additional situations not caught previously. +; Bugs 42238/43308/47479: Test some additional situations not caught previously. define void @store_captures() #0 { ; CHECK-LABEL: @store_captures( @@ -162,4 +162,31 @@ declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) + +; Test that the same function does not get a canary if nossp fn attr is set. +declare dso_local void @foo(i8*) + +define dso_local void @bar_sspstrong(i64 %0) #0 { +; CHECK-LABEL: @bar_sspstrong +; CHECK-NEXT: %StackGuardSlot = alloca i8* + %2 = alloca i64, align 8 + store i64 %0, i64* %2, align 8 + %3 = load i64, i64* %2, align 8 + %4 = alloca i8, i64 %3, align 16 + call void @foo(i8* %4) + ret void +} + +define dso_local void @bar_nossp(i64 %0) #1 { +; CHECK-LABEL: @bar_nossp +; CHECK-NEXT: %2 = alloca i64 + %2 = alloca i64, align 8 + store i64 %0, i64* %2, align 8 + %3 = load i64, i64* %2, align 8 + %4 = alloca i8, i64 %3, align 16 + call void @foo(i8* %4) + ret void +} + attributes #0 = { sspstrong } +attributes #1 = { nossp } diff --git a/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll b/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll --- a/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll +++ b/llvm/test/Transforms/CodeExtractor/PartialInlineAttributes.ll @@ -73,10 +73,10 @@ attributes #0 = { inlinehint minsize noduplicate noimplicitfloat norecurse noredzone nounwind nonlazybind optsize safestack sanitize_address sanitize_hwaddress sanitize_memory - sanitize_thread ssp sspreq sspstrong strictfp uwtable "foo"="bar" + sanitize_thread sspstrong strictfp uwtable "foo"="bar" "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" } -; CHECK: attributes [[FN_ATTRS]] = { inlinehint minsize noduplicate noimplicitfloat norecurse noredzone nounwind nonlazybind optsize safestack sanitize_address sanitize_hwaddress sanitize_memory sanitize_thread ssp sspreq sspstrong strictfp uwtable "foo"="bar" "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" } +; CHECK: attributes [[FN_ATTRS]] = { inlinehint minsize noduplicate noimplicitfloat norecurse noredzone nounwind nonlazybind optsize safestack sanitize_address sanitize_hwaddress sanitize_memory sanitize_thread sspstrong strictfp uwtable "foo"="bar" "patchable-function"="prologue-short-redirect" "probe-stack"="_foo_guard" "stack-probe-size"="4096" } ; attributes to drop attributes #1 = { diff --git a/llvm/test/Transforms/Inline/inline_nossp.ll b/llvm/test/Transforms/Inline/inline_nossp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Inline/inline_nossp.ll @@ -0,0 +1,64 @@ +; RUN: opt -inline -o - -S %s | FileCheck %s +; RUN: opt -passes='cgscc(inline)' %s -S | FileCheck %s +; RUN: opt -always-inline -o - -S %s | FileCheck %s + +declare dso_local void @foo(i8*) + +define dso_local void @ssp(i64 %0) #0 { + %2 = alloca i64, align 8 + store i64 %0, i64* %2, align 8 + %3 = load i64, i64* %2, align 8 + %4 = alloca i8, i64 %3, align 16 + call void @foo(i8* %4) + ret void +} + +define dso_local void @ssp_alwaysinline(i64 %0) #1 { + %2 = alloca i64, align 8 + store i64 %0, i64* %2, align 8 + %3 = load i64, i64* %2, align 8 + %4 = alloca i8, i64 %3, align 16 + call void @foo(i8* %4) + ret void +} + +define dso_local void @nossp() #2 { +; Check that the calls to @ssp and @ssp_alwaysinline are not inlined into +; @nossp, since @nossp does not want a stack protector. +; CHECK-LABEL: @nossp +; CHECK-NEXT: call void @ssp +; CHECK-NEXT: call void @ssp_alwaysinline + call void @ssp(i64 1024) + call void @ssp_alwaysinline(i64 1024) + ret void +} + +define dso_local void @nossp_alwaysinline() #3 { + call void @ssp(i64 1024) + call void @ssp_alwaysinline(i64 1024) + ret void +} + +define dso_local void @nossp_caller() #2 { +; Permit nossp callee to be inlined into nossp caller. +; CHECK-LABEL: @nossp_caller +; CHECK-NEXT: call void @ssp +; CHECK-NEXT: call void @ssp_alwaysinline +; CHECK-NOT: call void @nossp_alwaysinline + call void @nossp_alwaysinline() + ret void +} + +define dso_local void @ssp2() #0 { +; Check the call to @nossp is not inlined, since @nossp should not have a stack +; protector. +; CHECK-LABEL: @ssp2 +; CHECK-NEXT: call void @nossp + call void @nossp() + ret void +} + +attributes #0 = { sspstrong } +attributes #1 = { sspstrong alwaysinline } +attributes #2 = { nossp } +attributes #3 = { nossp alwaysinline} diff --git a/llvm/test/Transforms/Inline/inline_ssp.ll b/llvm/test/Transforms/Inline/inline_ssp.ll --- a/llvm/test/Transforms/Inline/inline_ssp.ll +++ b/llvm/test/Transforms/Inline/inline_ssp.ll @@ -1,6 +1,10 @@ ; RUN: opt -inline %s -S | FileCheck %s ; RUN: opt -passes='cgscc(inline)' %s -S | FileCheck %s ; Ensure SSP attributes are propagated correctly when inlining. +; This test case covers callers that are unspecified in their level of stack +; protection. See also llvm/test/Transforms/Inline/inline_nossp.ll +; which tests callers with ``nossp`` function attribute which is stack +; protection explicitly disabled. @.str = private unnamed_addr constant [11 x i8] c"fun_nossp\0A\00", align 1 @.str1 = private unnamed_addr constant [9 x i8] c"fun_ssp\0A\00", align 1 diff --git a/llvm/test/Verifier/function-attribute-nossp-ssp-sspreq-sspstrong.ll b/llvm/test/Verifier/function-attribute-nossp-ssp-sspreq-sspstrong.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Verifier/function-attribute-nossp-ssp-sspreq-sspstrong.ll @@ -0,0 +1,58 @@ +; RUN: not opt -verify -o - %s 2>&1 | FileCheck %s +define void @test_1 () #1 { ret void } +define void @test_2 () #2 { ret void } +define void @test_3 () #3 { ret void } +define void @test_4 () #4 { ret void } +define void @test_5 () #5 { ret void } +define void @test_6 () #6 { ret void } +define void @test_7 () #7 { ret void } +define void @test_8 () #8 { ret void } +define void @test_9 () #9 { ret void } +define void @test_10 () #10 { ret void } +define void @test_11 () #10 { ret void } +define void @test_12 () #10 { ret void } +define void @test_13 () #10 { ret void } +define void @test_14 () #10 { ret void } + +attributes #0 = { nossp } +attributes #1 = { ssp } +attributes #2 = { sspreq } +attributes #3 = { sspstrong } + +attributes #4 = { nossp ssp } +attributes #5 = { nossp sspreq } +attributes #6 = { nossp sspstrong } + +attributes #7 = { ssp sspreq } +attributes #8 = { ssp sspstrong } + +attributes #9 = { sspreq sspstrong } + +attributes #10 = { nossp ssp sspreq } +attributes #11 = { nossp ssp sspstrong } +attributes #12 = { nossp sspreq sspstrong } +attributes #13 = { ssp sspreq sspstrong } +attributes #14 = { nossp ssp sspreq sspstrong } + +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_4 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_5 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_6 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_7 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_8 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_9 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_10 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_11 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_12 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_13 +; CHECK: fn attrs are mutually exclusive +; CHECK-NEXT: void ()* @test_14 diff --git a/llvm/utils/emacs/llvm-mode.el b/llvm/utils/emacs/llvm-mode.el --- a/llvm/utils/emacs/llvm-mode.el +++ b/llvm/utils/emacs/llvm-mode.el @@ -26,7 +26,7 @@ "inaccessiblemem_or_argmemonly" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "noduplicate" "nofree" "noimplicitfloat" "noinline" "nonlazybind" "noredzone" "noreturn" "norecurse" "noundef" "nounwind" "optnone" "optsize" "readnone" "readonly" "returns_twice" - "speculatable" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag" + "speculatable" "nossp" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag" "sanitize_thread" "sanitize_memory" "strictfp" "uwtable" "willreturn" "writeonly" "immarg") 'symbols) . font-lock-constant-face) ;; Variables '("%[-a-zA-Z$._][-a-zA-Z$._0-9]*" . font-lock-variable-name-face) diff --git a/llvm/utils/kate/llvm.xml b/llvm/utils/kate/llvm.xml --- a/llvm/utils/kate/llvm.xml +++ b/llvm/utils/kate/llvm.xml @@ -93,6 +93,7 @@ optsize readnone readonly + nossp ssp sspreq sspstrong diff --git a/llvm/utils/llvm.grm b/llvm/utils/llvm.grm --- a/llvm/utils/llvm.grm +++ b/llvm/utils/llvm.grm @@ -168,6 +168,7 @@ | noinline | alwaysinline | optsize + | nossp | ssp | sspreq | returns_twice diff --git a/llvm/utils/vim/syntax/llvm.vim b/llvm/utils/vim/syntax/llvm.vim --- a/llvm/utils/vim/syntax/llvm.vim +++ b/llvm/utils/vim/syntax/llvm.vim @@ -148,6 +148,7 @@ \ spir_func \ spir_kernel \ sret + \ nossp \ ssp \ sspreq \ sspstrong diff --git a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml --- a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml +++ b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml @@ -245,6 +245,7 @@ \\bspir_func\\b|\ \\bspir_kernel\\b|\ \\bsret\\b|\ + \\bnossp\\b|\ \\bssp\\b|\ \\bsspreq\\b|\ \\bsspstrong\\b|\