Add the IR attribute 'sspstrong'.

SSPStrong applies a heuristic to insert stack protectors in these situations:

* A Protector is required for functions which contain an array, regardless of
  type or length.

* A Protector is required for functions which contain a structure/union which
  contains an array, regardless of type or length.  Note, there is no limit to
  the depth of nesting.

* A protector is required when the address of a local variable (i.e., stack
  based variable) is exposed. (E.g., such as through a local whose address is
  taken as part of the RHS of an assignment or a local whose address is taken as
  part of a function argument.)

This patch implements the SSPString attribute to be equivalent to
SSPRequired. This will change in a subsequent patch.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@173230 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Bill Wendling 2013-01-23 06:41:41 +00:00
parent 28d65722d6
commit 114baee1fa
14 changed files with 856 additions and 44 deletions

View File

@ -837,8 +837,16 @@ example:
If a function that has an ``sspreq`` attribute is inlined into a If a function that has an ``sspreq`` attribute is inlined into a
function that doesn't have an ``sspreq`` attribute or which has an function that doesn't have an ``sspreq`` attribute or which has an
``ssp`` attribute, then the resulting function will have an ``ssp`` or ``sspstrong`` attribute, then the resulting function will have
``sspreq`` attribute. an ``sspreq`` attribute.
``sspstrong``
This attribute indicates that the function should emit a stack smashing
protector. Currently this attribute has the same effect as
``sspreq``. This overrides the ``ssp`` function attribute.
If a function that has an ``sspstrong`` attribute is inlined into a
function that doesn't have an ``sspstrong`` attribute, then the
resulting function will have an ``sspstrong`` attribute.
``uwtable`` ``uwtable``
This attribute indicates that the ABI being targeted requires that This attribute indicates that the ABI being targeted requires that
an unwind table entry be produce for this function even if we can an unwind table entry be produce for this function even if we can

View File

@ -173,10 +173,11 @@ typedef enum {
LLVMUWTable = 1 << 30, LLVMUWTable = 1 << 30,
LLVMNonLazyBind = 1 << 31 LLVMNonLazyBind = 1 << 31
/* FIXME: This attribute is currently not included in the C API as /* FIXME: These attributes are currently not included in the C API as
a temporary measure until the API/ABI impact to the C API is understood a temporary measure until the API/ABI impact to the C API is understood
and the path forward agreed upon. and the path forward agreed upon.
LLVMAddressSafety = 1ULL << 32 LLVMAddressSafety = 1ULL << 32,
LLVMStackProtectStrongAttribute = 1ULL<<33
*/ */
} LLVMAttribute; } LLVMAttribute;

View File

@ -90,6 +90,7 @@ public:
///< alignstack=(1)) ///< alignstack=(1))
StackProtect, ///< Stack protection. StackProtect, ///< Stack protection.
StackProtectReq, ///< Stack protection required. StackProtectReq, ///< Stack protection required.
StackProtectStrong, ///< Strong Stack protection.
StructRet, ///< Hidden pointer to structure to return StructRet, ///< Hidden pointer to structure to return
UWTable, ///< Function must be in a unwind table UWTable, ///< Function must be in a unwind table
ZExt, ///< Zero extended before/after call ZExt, ///< Zero extended before/after call
@ -463,6 +464,7 @@ public:
.removeAttribute(Attribute::OptimizeForSize) .removeAttribute(Attribute::OptimizeForSize)
.removeAttribute(Attribute::StackProtect) .removeAttribute(Attribute::StackProtect)
.removeAttribute(Attribute::StackProtectReq) .removeAttribute(Attribute::StackProtectReq)
.removeAttribute(Attribute::StackProtectStrong)
.removeAttribute(Attribute::NoRedZone) .removeAttribute(Attribute::NoRedZone)
.removeAttribute(Attribute::NoImplicitFloat) .removeAttribute(Attribute::NoImplicitFloat)
.removeAttribute(Attribute::Naked) .removeAttribute(Attribute::Naked)

View File

@ -549,6 +549,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(optsize); KEYWORD(optsize);
KEYWORD(ssp); KEYWORD(ssp);
KEYWORD(sspreq); KEYWORD(sspreq);
KEYWORD(sspstrong);
KEYWORD(noredzone); KEYWORD(noredzone);
KEYWORD(noimplicitfloat); KEYWORD(noimplicitfloat);
KEYWORD(naked); KEYWORD(naked);

View File

@ -956,6 +956,7 @@ bool LLParser::ParseOptionalFuncAttrs(AttrBuilder &B) {
case lltok::kw_returns_twice: B.addAttribute(Attribute::ReturnsTwice); break; case lltok::kw_returns_twice: B.addAttribute(Attribute::ReturnsTwice); break;
case lltok::kw_ssp: B.addAttribute(Attribute::StackProtect); break; case lltok::kw_ssp: B.addAttribute(Attribute::StackProtect); break;
case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break; case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break;
case lltok::kw_sspstrong: B.addAttribute(Attribute::StackProtectStrong); break;
case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break; case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break;
case lltok::kw_noduplicate: B.addAttribute(Attribute::NoDuplicate); break; case lltok::kw_noduplicate: B.addAttribute(Attribute::NoDuplicate); break;
@ -1050,11 +1051,11 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_readonly: case lltok::kw_inlinehint: case lltok::kw_readonly: case lltok::kw_inlinehint:
case lltok::kw_alwaysinline: case lltok::kw_optsize: case lltok::kw_alwaysinline: case lltok::kw_optsize:
case lltok::kw_ssp: case lltok::kw_sspreq: case lltok::kw_ssp: case lltok::kw_sspreq:
case lltok::kw_noredzone: case lltok::kw_noimplicitfloat: case lltok::kw_sspstrong: case lltok::kw_noimplicitfloat:
case lltok::kw_naked: case lltok::kw_nonlazybind: case lltok::kw_noredzone: case lltok::kw_naked:
case lltok::kw_address_safety: case lltok::kw_minsize: case lltok::kw_nonlazybind: case lltok::kw_address_safety:
case lltok::kw_alignstack: case lltok::kw_align: case lltok::kw_minsize: case lltok::kw_alignstack:
case lltok::kw_noduplicate: case lltok::kw_align: case lltok::kw_noduplicate:
HaveError |= Error(Lex.getLoc(), "invalid use of function-only attribute"); HaveError |= Error(Lex.getLoc(), "invalid use of function-only attribute");
break; break;
} }

View File

@ -110,6 +110,7 @@ namespace lltok {
kw_optsize, kw_optsize,
kw_ssp, kw_ssp,
kw_sspreq, kw_sspreq,
kw_sspstrong,
kw_noredzone, kw_noredzone,
kw_noimplicitfloat, kw_noimplicitfloat,
kw_naked, kw_naked,

View File

@ -141,6 +141,12 @@ bool StackProtector::RequiresStackProtector() const {
Attribute::StackProtectReq)) Attribute::StackProtectReq))
return true; return true;
// FIXME: Dummy SSP-strong implementation. Default to required until
// strong heuristic is implemented.
if (F->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtectStrong))
return true;
if (!F->getAttributes().hasAttribute(AttributeSet::FunctionIndex, if (!F->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtect)) Attribute::StackProtect))
return false; return false;

View File

@ -206,6 +206,8 @@ std::string Attribute::getAsString() const {
Result += "ssp "; Result += "ssp ";
if (hasAttribute(Attribute::StackProtectReq)) if (hasAttribute(Attribute::StackProtectReq))
Result += "sspreq "; Result += "sspreq ";
if (hasAttribute(Attribute::StackProtectStrong))
Result += "sspstrong ";
if (hasAttribute(Attribute::NoRedZone)) if (hasAttribute(Attribute::NoRedZone))
Result += "noredzone "; Result += "noredzone ";
if (hasAttribute(Attribute::NoImplicitFloat)) if (hasAttribute(Attribute::NoImplicitFloat))
@ -487,6 +489,7 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
case Attribute::AddressSafety: return 1ULL << 32; case Attribute::AddressSafety: return 1ULL << 32;
case Attribute::MinSize: return 1ULL << 33; case Attribute::MinSize: return 1ULL << 33;
case Attribute::NoDuplicate: return 1ULL << 34; case Attribute::NoDuplicate: return 1ULL << 34;
case Attribute::StackProtectStrong: return 1ULL << 35;
} }
llvm_unreachable("Unsupported attribute type"); llvm_unreachable("Unsupported attribute type");
} }

View File

@ -499,6 +499,7 @@ void CppWriter::printAttributes(const AttributeSet &PAL,
HANDLE_ATTR(OptimizeForSize); HANDLE_ATTR(OptimizeForSize);
HANDLE_ATTR(StackProtect); HANDLE_ATTR(StackProtect);
HANDLE_ATTR(StackProtectReq); HANDLE_ATTR(StackProtectReq);
HANDLE_ATTR(StackProtectStrong);
HANDLE_ATTR(NoCapture); HANDLE_ATTR(NoCapture);
HANDLE_ATTR(NoRedZone); HANDLE_ATTR(NoRedZone);
HANDLE_ATTR(NoImplicitFloat); HANDLE_ATTR(NoImplicitFloat);

View File

@ -72,6 +72,40 @@ void Inliner::getAnalysisUsage(AnalysisUsage &AU) const {
typedef DenseMap<ArrayType*, std::vector<AllocaInst*> > typedef DenseMap<ArrayType*, std::vector<AllocaInst*> >
InlinedArrayAllocasTy; InlinedArrayAllocasTy;
/// \brief 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, Function *Callee) {
// 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 B;
B.addAttribute(Attribute::StackProtect)
.addAttribute(Attribute::StackProtectStrong);
AttributeSet OldSSPAttr = AttributeSet::get(Caller->getContext(),
AttributeSet::FunctionIndex,
B);
AttributeSet CallerAttr = Caller->getAttributes(),
CalleeAttr = Callee->getAttributes();
if (CalleeAttr.hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtectReq)) {
Caller->removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
Caller->addFnAttr(Attribute::StackProtectReq);
} else if (CalleeAttr.hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtectStrong) &&
!CallerAttr.hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtectReq)) {
Caller->removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
Caller->addFnAttr(Attribute::StackProtectStrong);
} else if (CalleeAttr.hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtect) &&
!CallerAttr.hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtectReq) &&
!CallerAttr.hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtectStrong))
Caller->addFnAttr(Attribute::StackProtect);
}
/// InlineCallIfPossible - If it is possible to inline the specified call site, /// InlineCallIfPossible - If it is possible to inline the specified call site,
/// do so and update the CallGraph for this operation. /// do so and update the CallGraph for this operation.
/// ///
@ -91,16 +125,7 @@ static bool InlineCallIfPossible(CallSite CS, InlineFunctionInfo &IFI,
if (!InlineFunction(CS, IFI, InsertLifetime)) if (!InlineFunction(CS, IFI, InsertLifetime))
return false; return false;
// If the inlined function had a higher stack protection level than the AdjustCallerSSPLevel(Caller, Callee);
// calling function, then bump up the caller's stack protection level.
if (Callee->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtectReq))
Caller->addFnAttr(Attribute::StackProtectReq);
else if (Callee->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtect) &&
!Caller->getAttributes().hasAttribute(AttributeSet::FunctionIndex,
Attribute::StackProtectReq))
Caller->addFnAttr(Attribute::StackProtect);
// Look at all of the allocas that we inlined through this call site. If we // Look at all of the allocas that we inlined through this call site. If we
// have already inlined other allocas through other calls into this function, // have already inlined other allocas through other calls into this function,

View File

@ -1,28 +1,635 @@
; RUN: llc -mtriple=i386-pc-linux-gnu < %s -o - | grep %gs: ; RUN: llc -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck --check-prefix=LINUX-I386 %s
; RUN: llc -mtriple=x86_64-pc-linux-gnu < %s -o - | grep %fs: ; RUN: llc -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck --check-prefix=LINUX-X64 %s
; RUN: llc -code-model=kernel -mtriple=x86_64-pc-linux-gnu < %s -o - | grep %gs: ; RUN: llc -code-model=kernel -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck --check-prefix=LINUX-KERNEL-X64 %s
; RUN: llc -mtriple=x86_64-apple-darwin < %s -o - | grep "__stack_chk_guard" ; RUN: llc -mtriple=x86_64-apple-darwin < %s -o - | FileCheck --check-prefix=DARWIN-X64 %s
; RUN: llc -mtriple=x86_64-apple-darwin < %s -o - | grep "__stack_chk_fail" ; FIXME: Update and expand test when strong heuristic is implemented.
@"\01LC" = internal constant [11 x i8] c"buf == %s\0A\00" ; <[11 x i8]*> [#uses=1] %struct.foo = type { [16 x i8] }
%struct.foo.0 = type { [4 x i8] }
define void @test(i8* %a) nounwind ssp { @.str = private unnamed_addr constant [4 x i8] c"%s\0A\00", align 1
; test1a: array of [16 x i8]
; no ssp attribute
; Requires no protector.
define void @test1a(i8* %a) nounwind uwtable {
entry: entry:
%a_addr = alloca i8* ; <i8**> [#uses=2] ; LINUX-I386: test1a:
%buf = alloca [8 x i8] ; <[8 x i8]*> [#uses=2] ; LINUX-I386-NOT: calll __stack_chk_fail
%"alloca point" = bitcast i32 0 to i32 ; <i32> [#uses=0] ; LINUX-I386: .cfi_endproc
store i8* %a, i8** %a_addr
%buf1 = bitcast [8 x i8]* %buf to i8* ; <i8*> [#uses=1]
%0 = load i8** %a_addr, align 4 ; <i8*> [#uses=1]
%1 = call i8* @strcpy(i8* %buf1, i8* %0) nounwind ; <i8*> [#uses=0]
%buf2 = bitcast [8 x i8]* %buf to i8* ; <i8*> [#uses=1]
%2 = call i32 (i8*, ...)* @printf(i8* getelementptr ([11 x i8]* @"\01LC", i32 0, i32 0), i8* %buf2) nounwind ; <i32> [#uses=0]
br label %return
return: ; preds = %entry ; LINUX-X64: test1a:
; LINUX-X64-NOT: callq __stack_chk_fail
; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test1a:
; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test1a:
; DARWIN-X64-NOT: callq ___stack_chk_fail
; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
%buf = alloca [16 x i8], align 16
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void ret void
} }
declare i8* @strcpy(i8*, i8*) nounwind ; test1b: array of [16 x i8]
; ssp attribute
; Requires protector.
define void @test1b(i8* %a) nounwind uwtable ssp {
entry:
; LINUX-I386: test1b:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
declare i32 @printf(i8*, ...) nounwind ; LINUX-X64: test1b:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test1b:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test1b:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%buf = alloca [16 x i8], align 16
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void
}
; test1c: array of [16 x i8]
; sspstrong attribute
; Requires protector.
define void @test1c(i8* %a) nounwind uwtable sspstrong {
entry:
; LINUX-I386: test1c:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test1c:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test1c:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test1c:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%buf = alloca [16 x i8], align 16
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void
}
; test1d: array of [16 x i8]
; sspreq attribute
; Requires protector.
define void @test1d(i8* %a) nounwind uwtable sspreq {
entry:
; LINUX-I386: test1d:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test1d:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test1d:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test1d:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%buf = alloca [16 x i8], align 16
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void
}
; test2a: struct { [16 x i8] }
; no ssp attribute
; Requires no protector.
define void @test2a(i8* %a) nounwind uwtable {
entry:
; LINUX-I386: test2a:
; LINUX-I386-NOT: calll __stack_chk_fail
; LINUX-I386: .cfi_endproc
; LINUX-X64: test2a:
; LINUX-X64-NOT: callq __stack_chk_fail
; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test2a:
; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test2a:
; DARWIN-X64-NOT: callq ___stack_chk_fail
; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
%b = alloca %struct.foo, align 1
store i8* %a, i8** %a.addr, align 8
%buf = getelementptr inbounds %struct.foo* %b, i32 0, i32 0
%arraydecay = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%buf1 = getelementptr inbounds %struct.foo* %b, i32 0, i32 0
%arraydecay2 = getelementptr inbounds [16 x i8]* %buf1, i32 0, i32 0
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay2)
ret void
}
; test2b: struct { [16 x i8] }
; ssp attribute
; Requires protector.
define void @test2b(i8* %a) nounwind uwtable ssp {
entry:
; LINUX-I386: test2b:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test2b:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test2b:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test2b:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%b = alloca %struct.foo, align 1
store i8* %a, i8** %a.addr, align 8
%buf = getelementptr inbounds %struct.foo* %b, i32 0, i32 0
%arraydecay = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%buf1 = getelementptr inbounds %struct.foo* %b, i32 0, i32 0
%arraydecay2 = getelementptr inbounds [16 x i8]* %buf1, i32 0, i32 0
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay2)
ret void
}
; test2c: struct { [16 x i8] }
; sspstrong attribute
; Requires protector.
define void @test2c(i8* %a) nounwind uwtable sspstrong {
entry:
; LINUX-I386: test2c:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test2c:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test2c:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test2c:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%b = alloca %struct.foo, align 1
store i8* %a, i8** %a.addr, align 8
%buf = getelementptr inbounds %struct.foo* %b, i32 0, i32 0
%arraydecay = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%buf1 = getelementptr inbounds %struct.foo* %b, i32 0, i32 0
%arraydecay2 = getelementptr inbounds [16 x i8]* %buf1, i32 0, i32 0
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay2)
ret void
}
; test2d: struct { [16 x i8] }
; sspreq attribute
; Requires protector.
define void @test2d(i8* %a) nounwind uwtable sspreq {
entry:
; LINUX-I386: test2d:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test2d:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test2d:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test2d:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%b = alloca %struct.foo, align 1
store i8* %a, i8** %a.addr, align 8
%buf = getelementptr inbounds %struct.foo* %b, i32 0, i32 0
%arraydecay = getelementptr inbounds [16 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%buf1 = getelementptr inbounds %struct.foo* %b, i32 0, i32 0
%arraydecay2 = getelementptr inbounds [16 x i8]* %buf1, i32 0, i32 0
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay2)
ret void
}
; test3a: array of [4 x i8]
; no ssp attribute
; Requires no protector.
define void @test3a(i8* %a) nounwind uwtable {
entry:
; LINUX-I386: test3a:
; LINUX-I386-NOT: calll __stack_chk_fail
; LINUX-I386: .cfi_endproc
; LINUX-X64: test3a:
; LINUX-X64-NOT: callq __stack_chk_fail
; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test3a:
; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test3a:
; DARWIN-X64-NOT: callq ___stack_chk_fail
; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
%buf = alloca [4 x i8], align 1
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void
}
; test3b: array [4 x i8]
; ssp attribute
; Requires no protector.
define void @test3b(i8* %a) nounwind uwtable ssp {
entry:
; LINUX-I386: test3b:
; LINUX-I386-NOT: calll __stack_chk_fail
; LINUX-I386: .cfi_endproc
; LINUX-X64: test3b:
; LINUX-X64-NOT: callq __stack_chk_fail
; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test3b:
; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test3b:
; DARWIN-X64-NOT: callq ___stack_chk_fail
; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
%buf = alloca [4 x i8], align 1
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void
}
; test3c: array of [4 x i8]
; sspstrong attribute
; Requires protector.
define void @test3c(i8* %a) nounwind uwtable sspstrong {
entry:
; LINUX-I386: test3c:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test3c:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test3c:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test3c:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%buf = alloca [4 x i8], align 1
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void
}
; test3d: array of [4 x i8]
; sspreq attribute
; Requires protector.
define void @test3d(i8* %a) nounwind uwtable sspreq {
entry:
; LINUX-I386: test3d:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test3d:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test3d:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test3d:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%buf = alloca [4 x i8], align 1
store i8* %a, i8** %a.addr, align 8
%arraydecay = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%arraydecay1 = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay1)
ret void
}
; test4a: struct { [4 x i8] }
; no ssp attribute
; Requires no protector.
define void @test4a(i8* %a) nounwind uwtable {
entry:
; LINUX-I386: test4a:
; LINUX-I386-NOT: calll __stack_chk_fail
; LINUX-I386: .cfi_endproc
; LINUX-X64: test4a:
; LINUX-X64-NOT: callq __stack_chk_fail
; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test4a:
; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test4a:
; DARWIN-X64-NOT: callq ___stack_chk_fail
; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
%b = alloca %struct.foo.0, align 1
store i8* %a, i8** %a.addr, align 8
%buf = getelementptr inbounds %struct.foo.0* %b, i32 0, i32 0
%arraydecay = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%buf1 = getelementptr inbounds %struct.foo.0* %b, i32 0, i32 0
%arraydecay2 = getelementptr inbounds [4 x i8]* %buf1, i32 0, i32 0
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay2)
ret void
}
; test4b: struct { [4 x i8] }
; ssp attribute
; Requires no protector.
define void @test4b(i8* %a) nounwind uwtable ssp {
entry:
; LINUX-I386: test4b:
; LINUX-I386-NOT: calll __stack_chk_fail
; LINUX-I386: .cfi_endproc
; LINUX-X64: test4b:
; LINUX-X64-NOT: callq __stack_chk_fail
; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test4b:
; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test4b:
; DARWIN-X64-NOT: callq ___stack_chk_fail
; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
%b = alloca %struct.foo.0, align 1
store i8* %a, i8** %a.addr, align 8
%buf = getelementptr inbounds %struct.foo.0* %b, i32 0, i32 0
%arraydecay = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%buf1 = getelementptr inbounds %struct.foo.0* %b, i32 0, i32 0
%arraydecay2 = getelementptr inbounds [4 x i8]* %buf1, i32 0, i32 0
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay2)
ret void
}
; test4c: struct { [4 x i8] }
; sspstrong attribute
; Requires protector.
define void @test4c(i8* %a) nounwind uwtable sspstrong {
entry:
; LINUX-I386: test4c:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test4c:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test4c:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test4c:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%b = alloca %struct.foo.0, align 1
store i8* %a, i8** %a.addr, align 8
%buf = getelementptr inbounds %struct.foo.0* %b, i32 0, i32 0
%arraydecay = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%buf1 = getelementptr inbounds %struct.foo.0* %b, i32 0, i32 0
%arraydecay2 = getelementptr inbounds [4 x i8]* %buf1, i32 0, i32 0
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay2)
ret void
}
; test4d: struct { [4 x i8] }
; sspreq attribute
; Requires protector.
define void @test4d(i8* %a) nounwind uwtable sspreq {
entry:
; LINUX-I386: test4d:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test4d:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test4d:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test4d:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
%b = alloca %struct.foo.0, align 1
store i8* %a, i8** %a.addr, align 8
%buf = getelementptr inbounds %struct.foo.0* %b, i32 0, i32 0
%arraydecay = getelementptr inbounds [4 x i8]* %buf, i32 0, i32 0
%0 = load i8** %a.addr, align 8
%call = call i8* @strcpy(i8* %arraydecay, i8* %0)
%buf1 = getelementptr inbounds %struct.foo.0* %b, i32 0, i32 0
%arraydecay2 = getelementptr inbounds [4 x i8]* %buf1, i32 0, i32 0
%call3 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %arraydecay2)
ret void
}
; test5a: no arrays / no nested arrays
; no ssp attribute
; Requires no protector.
define void @test5a(i8* %a) nounwind uwtable {
entry:
; LINUX-I386: test5a:
; LINUX-I386-NOT: calll __stack_chk_fail
; LINUX-I386: .cfi_endproc
; LINUX-X64: test5a:
; LINUX-X64-NOT: callq __stack_chk_fail
; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test5a:
; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test5a:
; DARWIN-X64-NOT: callq ___stack_chk_fail
; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
store i8* %a, i8** %a.addr, align 8
%0 = load i8** %a.addr, align 8
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %0)
ret void
}
; test5b: no arrays / no nested arrays
; ssp attribute
; Requires no protector.
define void @test5b(i8* %a) nounwind uwtable ssp {
entry:
; LINUX-I386: test5b:
; LINUX-I386-NOT: calll __stack_chk_fail
; LINUX-I386: .cfi_endproc
; LINUX-X64: test5b:
; LINUX-X64-NOT: callq __stack_chk_fail
; LINUX-X64: .cfi_endproc
; LINUX-KERNEL-X64: test5b:
; LINUX-KERNEL-X64-NOT: callq __stack_chk_fail
; LINUX-KERNEL-X64: .cfi_endproc
; DARWIN-X64: test5b:
; DARWIN-X64-NOT: callq ___stack_chk_fail
; DARWIN-X64: .cfi_endproc
%a.addr = alloca i8*, align 8
store i8* %a, i8** %a.addr, align 8
%0 = load i8** %a.addr, align 8
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %0)
ret void
}
; test5c: no arrays / no nested arrays
; sspstrong attribute
; Requires protector.
; FIXME: Once strong heuristic is implemented, this should _not_ require
; a protector
define void @test5c(i8* %a) nounwind uwtable sspstrong {
entry:
; LINUX-I386: test5c:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test5c:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test5c:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test5c:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
store i8* %a, i8** %a.addr, align 8
%0 = load i8** %a.addr, align 8
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %0)
ret void
}
; test5d: no arrays / no nested arrays
; sspreq attribute
; Requires protector.
define void @test5d(i8* %a) nounwind uwtable sspreq {
entry:
; LINUX-I386: test5d:
; LINUX-I386: mov{{l|q}} %gs:
; LINUX-I386: calll __stack_chk_fail
; LINUX-X64: test5d:
; LINUX-X64: mov{{l|q}} %fs:
; LINUX-X64: callq __stack_chk_fail
; LINUX-KERNEL-X64: test5d:
; LINUX-KERNEL-X64: mov{{l|q}} %gs:
; LINUX-KERNEL-X64: callq __stack_chk_fail
; DARWIN-X64: test5d:
; DARWIN-X64: mov{{l|q}} ___stack_chk_guard
; DARWIN-X64: callq ___stack_chk_fail
%a.addr = alloca i8*, align 8
store i8* %a, i8** %a.addr, align 8
%0 = load i8** %a.addr, align 8
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([4 x i8]* @.str, i32 0, i32 0), i8* %0)
ret void
}
declare i8* @strcpy(i8*, i8*)
declare i32 @printf(i8*, ...)

View File

@ -0,0 +1,155 @@
; RUN: opt -inline %s -S | FileCheck %s
; Ensure SSP attributes are propagated correctly when inlining.
@.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
@.str2 = private unnamed_addr constant [15 x i8] c"fun_sspstrong\0A\00", align 1
@.str3 = private unnamed_addr constant [12 x i8] c"fun_sspreq\0A\00", align 1
; These first four functions (@fun_sspreq, @fun_sspstrong, @fun_ssp, @fun_nossp)
; are used by the remaining functions to ensure that the SSP attributes are
; propagated correctly. The caller should have its SSP attribute set as:
; strictest(caller-ssp-attr, callee-ssp-attr), where strictness is ordered as:
; sspreq > sspstrong > ssp > [no ssp]
define internal void @fun_sspreq() nounwind uwtable sspreq {
entry:
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([12 x i8]* @.str3, i32 0, i32 0))
ret void
}
define internal void @fun_sspstrong() nounwind uwtable sspstrong {
entry:
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str2, i32 0, i32 0))
ret void
}
define internal void @fun_ssp() nounwind uwtable ssp {
entry:
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @.str1, i32 0, i32 0))
ret void
}
define internal void @fun_nossp() nounwind uwtable {
entry:
%call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0))
ret void
}
; Tests start below
define void @inline_req_req() nounwind uwtable sspreq {
entry:
; CHECK: @inline_req_req() nounwind uwtable sspreq
call void @fun_sspreq()
ret void
}
define void @inline_req_strong() nounwind uwtable sspstrong {
entry:
; CHECK: @inline_req_strong() nounwind uwtable sspreq
call void @fun_sspreq()
ret void
}
define void @inline_req_ssp() nounwind uwtable ssp {
entry:
; CHECK: @inline_req_ssp() nounwind uwtable sspreq
call void @fun_sspreq()
ret void
}
define void @inline_req_nossp() nounwind uwtable {
entry:
; CHECK: @inline_req_nossp() nounwind uwtable sspreq
call void @fun_sspreq()
ret void
}
define void @inline_strong_req() nounwind uwtable sspreq {
entry:
; CHECK: @inline_strong_req() nounwind uwtable sspreq
call void @fun_sspstrong()
ret void
}
define void @inline_strong_strong() nounwind uwtable sspstrong {
entry:
; CHECK: @inline_strong_strong() nounwind uwtable sspstrong
call void @fun_sspstrong()
ret void
}
define void @inline_strong_ssp() nounwind uwtable ssp {
entry:
; CHECK: @inline_strong_ssp() nounwind uwtable sspstrong
call void @fun_sspstrong()
ret void
}
define void @inline_strong_nossp() nounwind uwtable {
entry:
; CHECK: @inline_strong_nossp() nounwind uwtable sspstrong
call void @fun_sspstrong()
ret void
}
define void @inline_ssp_req() nounwind uwtable sspreq {
entry:
; CHECK: @inline_ssp_req() nounwind uwtable sspreq
call void @fun_ssp()
ret void
}
define void @inline_ssp_strong() nounwind uwtable sspstrong {
entry:
; CHECK: @inline_ssp_strong() nounwind uwtable sspstrong
call void @fun_ssp()
ret void
}
define void @inline_ssp_ssp() nounwind uwtable ssp {
entry:
; CHECK: @inline_ssp_ssp() nounwind uwtable ssp
call void @fun_ssp()
ret void
}
define void @inline_ssp_nossp() nounwind uwtable {
entry:
; CHECK: @inline_ssp_nossp() nounwind uwtable ssp
call void @fun_ssp()
ret void
}
define void @inline_nossp_req() nounwind uwtable sspreq {
entry:
; CHECK: @inline_nossp_req() nounwind uwtable sspreq
call void @fun_nossp()
ret void
}
define void @inline_nossp_strong() nounwind uwtable sspstrong {
entry:
; CHECK: @inline_nossp_strong() nounwind uwtable sspstrong
call void @fun_nossp()
ret void
}
define void @inline_nossp_ssp() nounwind uwtable ssp {
entry:
; CHECK: @inline_nossp_ssp() nounwind uwtable ssp
call void @fun_nossp()
ret void
}
define void @inline_nossp_nossp() nounwind uwtable {
entry:
; CHECK: @inline_nossp_nossp() nounwind uwtable
call void @fun_nossp()
ret void
}
declare i32 @printf(i8*, ...)

View File

@ -90,6 +90,7 @@
<item> readonly </item> <item> readonly </item>
<item> ssp </item> <item> ssp </item>
<item> sspreq </item> <item> sspreq </item>
<item> sspstrong </item>
</list> </list>
<list name="types"> <list name="types">
<item> float </item> <item> float </item>

View File

@ -51,10 +51,10 @@ syn keyword llvmKeyword noimplicitfloat noinline nonlazybind noredzone noreturn
syn keyword llvmKeyword nounwind optsize personality private protected syn keyword llvmKeyword nounwind optsize personality private protected
syn keyword llvmKeyword ptx_device ptx_kernel readnone readonly release syn keyword llvmKeyword ptx_device ptx_kernel readnone readonly release
syn keyword llvmKeyword returns_twice section seq_cst sideeffect signext syn keyword llvmKeyword returns_twice section seq_cst sideeffect signext
syn keyword llvmKeyword singlethread spir_func spir_kernel sret ssp sspreq tail syn keyword llvmKeyword singlethread spir_func spir_kernel sret ssp sspreq
syn keyword llvmKeyword target thread_local to triple unnamed_addr unordered syn keyword llvmKeyword sspstrong tail target thread_local to triple
syn keyword llvmKeyword uwtable volatile weak weak_odr x86_fastcallcc syn keyword llvmKeyword unnamed_addr unordered uwtable volatile weak weak_odr
syn keyword llvmKeyword x86_stdcallcc x86_thiscallcc zeroext syn keyword llvmKeyword x86_fastcallcc x86_stdcallcc x86_thiscallcc zeroext
" Obsolete keywords. " Obsolete keywords.
syn keyword llvmError getresult begin end syn keyword llvmError getresult begin end