diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -327,6 +327,15 @@ /// by sanitizer coverage pass. std::vector SanitizeCoverageAllowlistFiles; + /// The guard style used for stack protector to get a initial value, this + /// value usually be gotten from TLS or get from __stack_chk_guard, or some + /// other styles we may implement in the future. + std::string StackProtectorGuard; + + /// The TLS base register when StackProtectorGuard is "tls". + /// On x86 this can be "fs" or "gs". + std::string StackProtectorGuardReg; + /// Path to blocklist file specifying which objects /// (files, functions) listed for instrumentation by sanitizer /// coverage pass should actually not be instrumented. diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -356,6 +356,9 @@ /// Bit size of immediate TLS offsets (0 == use the default). VALUE_CODEGENOPT(TLSSize, 8, 0) +/// The default stack protector guard offset to use. +VALUE_CODEGENOPT(StackProtectorGuardOffset, 32, (unsigned)-1) + /// Number of path components to strip when emitting checks. (0 == full /// filename) VALUE_CODEGENOPT(EmitCheckPathComponentsToStrip, 32, 0) diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -213,6 +213,7 @@ def err_drv_invalid_value : Error<"invalid value '%1' in '%0'">; def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">; +def err_drv_invalid_value_with_suggestion : Error<"invalid value '%1' in '%0','%2'">; def err_drv_invalid_remap_file : Error< "invalid option '%0' not of the form ;">; def err_drv_invalid_gcc_output_type : Error< diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2606,6 +2606,12 @@ def mrecip_EQ : CommaJoined<["-"], "mrecip=">, Group, Flags<[CC1Option]>; def mprefer_vector_width_EQ : Joined<["-"], "mprefer-vector-width=">, Group, Flags<[CC1Option]>, HelpText<"Specifies preferred vector width for auto-vectorization. Defaults to 'none' which allows target specific decisions.">; +def mstack_protector_guard_EQ : Joined<["-"], "mstack-protector-guard=">, Group, Flags<[CC1Option]>, + HelpText<"Use the given guard (global, tls) for addressing the stack-protector guard">; +def mstack_protector_guard_offset_EQ : Joined<["-"], "mstack-protector-guard-offset=">, Group, Flags<[CC1Option]>, + HelpText<"Use the given offset for addressing the stack-protector guard">; +def mstack_protector_guard_reg_EQ : Joined<["-"], "mstack-protector-guard-reg=">, Group, Flags<[CC1Option]>, + HelpText<"Use the given reg for addressing the stack-protector guard">; def mpie_copy_relocations : Flag<["-"], "mpie-copy-relocations">, Group, Flags<[CC1Option]>, HelpText<"Use copy relocations support for PIE builds">; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -521,6 +521,14 @@ Options.UniqueSectionNames = CodeGenOpts.UniqueSectionNames; Options.UniqueBasicBlockSectionNames = CodeGenOpts.UniqueBasicBlockSectionNames; + Options.StackProtectorGuard = + llvm::StringSwitch(CodeGenOpts + .StackProtectorGuard) + .Case("tls", llvm::StackProtectorGuards::TLS) + .Case("global", llvm::StackProtectorGuards::Global) + .Default(llvm::StackProtectorGuards::None); + Options.StackProtectorGuardOffset = CodeGenOpts.StackProtectorGuardOffset; + Options.StackProtectorGuardReg = CodeGenOpts.StackProtectorGuardReg; Options.TLSSize = CodeGenOpts.TLSSize; Options.EmulatedTLS = CodeGenOpts.EmulatedTLS; Options.ExplicitEmulatedTLS = CodeGenOpts.ExplicitEmulatedTLS; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -2984,8 +2984,9 @@ Args.AddAllArgValues(CmdArgs, options::OPT_Xanalyzer); } -static void RenderSSPOptions(const ToolChain &TC, const ArgList &Args, - ArgStringList &CmdArgs, bool KernelOrKext) { +static void RenderSSPOptions(const Driver &D, const ToolChain &TC, + const ArgList &Args, ArgStringList &CmdArgs, + bool KernelOrKext) { const llvm::Triple &EffectiveTriple = TC.getEffectiveTriple(); // NVPTX doesn't support stack protectors; from the compiler's perspective, it @@ -3030,6 +3031,50 @@ A->claim(); } } + + // First support "tls" and "global" for X86 target. + // TODO: Support "sysreg" for AArch64. + const std::string &TripleStr = EffectiveTriple.getTriple(); + if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_EQ)) { + StringRef Value = A->getValue(); + if (!EffectiveTriple.isX86() && !EffectiveTriple.isAArch64()) + D.Diag(diag::err_drv_unsupported_opt_for_target) + << A->getAsString(Args) << TripleStr; + if (Value != "tls" && Value != "global") { + D.Diag(diag::err_drv_invalid_value_with_suggestion) + << A->getOption().getName() << Value + << "valid arguments to '-mstack-protector-guard=' are:tls global"; + return; + } + A->render(Args, CmdArgs); + } + + if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_offset_EQ)) { + StringRef Value = A->getValue(); + if (!EffectiveTriple.isX86()) + D.Diag(diag::err_drv_unsupported_opt_for_target) + << A->getAsString(Args) << TripleStr; + unsigned Offset; + if (Value.getAsInteger(10, Offset)) { + D.Diag(diag::err_drv_invalid_value) << A->getOption().getName() << Value; + return; + } + A->render(Args, CmdArgs); + } + + if (Arg *A = Args.getLastArg(options::OPT_mstack_protector_guard_reg_EQ)) { + StringRef Value = A->getValue(); + if (!EffectiveTriple.isX86()) + D.Diag(diag::err_drv_unsupported_opt_for_target) + << A->getAsString(Args) << TripleStr; + if (EffectiveTriple.isX86() && (Value != "fs" && Value != "gs")) { + D.Diag(diag::err_drv_invalid_value_with_suggestion) + << A->getOption().getName() << Value + << "for X86, valid arguments to '-mstack-protector-guard-reg=' are:fs gs"; + return; + } + A->render(Args, CmdArgs); + } } static void RenderSCPOptions(const ToolChain &TC, const ArgList &Args, @@ -5467,7 +5512,7 @@ options::OPT_mno_speculative_load_hardening, false)) CmdArgs.push_back(Args.MakeArgString("-mspeculative-load-hardening")); - RenderSSPOptions(TC, Args, CmdArgs, KernelOrKext); + RenderSSPOptions(D, TC, Args, CmdArgs, KernelOrKext); RenderSCPOptions(TC, Args, CmdArgs); RenderTrivialAutoVarInitOptions(D, TC, Args, CmdArgs); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1254,6 +1254,21 @@ } Opts.SSPBufferSize = getLastArgIntValue(Args, OPT_stack_protector_buffer_size, 8, Diags); + + Opts.StackProtectorGuard = + std::string(Args.getLastArgValue(OPT_mstack_protector_guard_EQ)); + + if (Arg *A = Args.getLastArg(OPT_mstack_protector_guard_offset_EQ)) { + StringRef Val = A->getValue(); + unsigned Offset = Opts.StackProtectorGuardOffset; + Val.getAsInteger(10, Offset); + Opts.StackProtectorGuardOffset = Offset; + } + + Opts.StackProtectorGuardReg = + std::string(Args.getLastArgValue(OPT_mstack_protector_guard_reg_EQ, + "none")); + Opts.StackRealignment = Args.hasArg(OPT_mstackrealign); if (Arg *A = Args.getLastArg(OPT_mstack_alignment)) { StringRef Val = A->getValue(); diff --git a/clang/test/Driver/stack-protector-guard.c b/clang/test/Driver/stack-protector-guard.c new file mode 100644 --- /dev/null +++ b/clang/test/Driver/stack-protector-guard.c @@ -0,0 +1,43 @@ +// RUN: %clang -### -target x86_64-unknown-unknown -mstack-protector-guard=tls %s 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-TLS %s +// RUN: %clang -### -target x86_64-unknown-unknown -mstack-protector-guard=global %s 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-GLOBAL %s +// RUN: %clang -### -target x86_64-unknown-unknown -mstack-protector-guard=local %s 2>&1 | \ +// RUN: FileCheck -check-prefix=INVALID-VALUE %s + +// CHECK-TLS: "-cc1" {{.*}}"-mstack-protector-guard=tls" +// CHECK-GLOBAL: "-cc1" {{.*}}"-mstack-protector-guard=global" +// INVALID-VALUE: error: invalid value 'local' in 'mstack-protector-guard=','valid arguments to '-mstack-protector-guard=' are:tls global' + +// RUN: %clang -### -target x86_64-unknown-unknown -mstack-protector-guard-reg=fs %s 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-FS %s +// RUN: %clang -### -target x86_64-unknown-unknown -mstack-protector-guard-reg=gs %s 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-GS %s + +// Invalid arch +// RUN: not %clang -target arm-eabi-c -mstack-protector-guard=tls %s 2>&1 | \ +// RUN: FileCheck -check-prefix=INVALID-ARCH %s +// INVALID-ARCH: unsupported option '-mstack-protector-guard=tls' for target + +// RUN: not %clang -target powerpc64le-linux-gnu -mstack-protector-guard-reg=fs %s 2>&1 | \ +// RUN: FileCheck -check-prefix=INVALID-ARCH2 %s +// INVALID-ARCH2: unsupported option '-mstack-protector-guard-reg=fs' for target + +// RUN: not %clang -target aarch64-linux-gnu -mstack-protector-guard-offset=10 %s 2>&1 | \ +// RUN: FileCheck -check-prefix=INVALID-ARCH3 %s +// INVALID-ARCH3: unsupported option '-mstack-protector-guard-offset=10' for target + +// Invalid option value +// RUN: not %clang -target x86_64-unknown-unknown -c -mstack-protector-guard-reg=cs %s 2>&1 | \ +// RUN: FileCheck -check-prefix=INVALID-REG %s +// RUN: not %clang -target x86_64-unknown-unknown -c -mstack-protector-guard-reg=ds %s 2>&1 | \ +// RUN: FileCheck -check-prefix=INVALID-REG %s + +// CHECK-FS: "-cc1" {{.*}}"-mstack-protector-guard-reg=fs" +// CHECK-GS: "-cc1" {{.*}}"-mstack-protector-guard-reg=gs" +// INVALID-REG: error: invalid value {{.*}} in 'mstack-protector-guard-reg=','for X86, valid arguments to '-mstack-protector-guard-reg=' are:fs gs' + +// RUN: %clang -### -target x86_64-unknown-unknown -mstack-protector-guard-offset=30 %s 2>&1 | \ +// RUN: FileCheck -check-prefix=CHECK-OFFSET %s + +// CHECK-OFFSET: "-cc1" {{.*}}"-mstack-protector-guard-offset=30" diff --git a/llvm/include/llvm/CodeGen/CommandFlags.h b/llvm/include/llvm/CodeGen/CommandFlags.h --- a/llvm/include/llvm/CodeGen/CommandFlags.h +++ b/llvm/include/llvm/CodeGen/CommandFlags.h @@ -99,6 +99,10 @@ std::string getBBSections(); +std::string getStackProtectorGuard(); +unsigned getStackProtectorGuardOffset(); +std::string getStackProtectorGuardReg(); + unsigned getTLSSize(); bool getEmulatedTLS(); @@ -135,6 +139,9 @@ llvm::BasicBlockSection getBBSectionsMode(llvm::TargetOptions &Options); +llvm::StackProtectorGuards +getStackProtectorGuardMode(llvm::TargetOptions &Options); + /// Common utility function tightly tied to the options listed here. Initializes /// a TargetOptions object with CodeGen flags and returns it. /// \p TheTriple is used to determine the default value for options if diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h --- a/llvm/include/llvm/Target/TargetOptions.h +++ b/llvm/include/llvm/Target/TargetOptions.h @@ -73,6 +73,12 @@ None // Do not use Basic Block Sections. }; + enum class StackProtectorGuards { + None, + TLS, + Global + }; + enum class EABI { Unknown, Default, // Default means not specified @@ -307,6 +313,16 @@ /// Emit XRay Function Index section unsigned XRayOmitFunctionIndex : 1; + /// Stack protector guard offset to use. + unsigned StackProtectorGuardOffset : 32; + + /// Stack protector guard mode to use, e.g. tls, global. + StackProtectorGuards StackProtectorGuard = + StackProtectorGuards::None; + + /// Stack protector guard reg to use, e.g. usually fs or gs in X86. + std::string StackProtectorGuardReg = "None"; + /// FloatABIType - This setting is set by -float-abi=xxx option is specfied /// on the command line. This setting may either be Default, Soft, or Hard. /// Default selects the target's default behavior. Soft selects the ABI for diff --git a/llvm/lib/CodeGen/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp --- a/llvm/lib/CodeGen/CommandFlags.cpp +++ b/llvm/lib/CodeGen/CommandFlags.cpp @@ -76,6 +76,9 @@ CGOPT_EXP(bool, FunctionSections) CGOPT(bool, IgnoreXCOFFVisibility) CGOPT(std::string, BBSections) +CGOPT(std::string, StackProtectorGuard) +CGOPT(unsigned, StackProtectorGuardOffset) +CGOPT(std::string, StackProtectorGuardReg) CGOPT(unsigned, TLSSize) CGOPT(bool, EmulatedTLS) CGOPT(bool, UniqueSectionNames) @@ -348,6 +351,21 @@ cl::init("none")); CGBINDOPT(BBSections); + static cl::opt StackProtectorGuard( + "stack-protector-guard", cl::desc("Stack protector guard mode"), + cl::init("none")); + CGBINDOPT(StackProtectorGuard); + + static cl::opt StackProtectorGuardReg( + "stack-protector-guard-reg", cl::desc("Stack protector guard register"), + cl::init("none")); + CGBINDOPT(StackProtectorGuardReg); + + static cl::opt StackProtectorGuardOffset( + "stack-protector-guard-offset", cl::desc("Stack protector guard offset"), + cl::init((unsigned)-1)); + CGBINDOPT(StackProtectorGuardOffset); + static cl::opt TLSSize( "tls-size", cl::desc("Bit size of immediate TLS offsets"), cl::init(0)); CGBINDOPT(TLSSize); @@ -459,6 +477,24 @@ } } +llvm::StackProtectorGuards +codegen::getStackProtectorGuardMode(llvm::TargetOptions &Options) { + if (getStackProtectorGuard() == "tls") + return StackProtectorGuards::TLS; + if (getStackProtectorGuard() == "global") + return StackProtectorGuards::Global; + if (getStackProtectorGuard() != "none") { + ErrorOr> MBOrErr = + MemoryBuffer::getFile(getStackProtectorGuard()); + if (!MBOrErr) + errs() << "error illegal stack protector guard mode: " + << MBOrErr.getError().message() << "\n"; + else + Options.BBSectionsFuncListBuf = std::move(*MBOrErr); + } + return StackProtectorGuards::None; +} + // Common utility function tightly tied to the options listed here. Initializes // a TargetOptions object with CodeGen flags and returns it. TargetOptions @@ -493,6 +529,9 @@ Options.BBSections = getBBSectionsMode(Options); Options.UniqueSectionNames = getUniqueSectionNames(); Options.UniqueBasicBlockSectionNames = getUniqueBasicBlockSectionNames(); + Options.StackProtectorGuard = getStackProtectorGuardMode(Options); + Options.StackProtectorGuardOffset = getStackProtectorGuardOffset(); + Options.StackProtectorGuardReg = getStackProtectorGuardReg(); Options.TLSSize = getTLSSize(); Options.EmulatedTLS = getEmulatedTLS(); Options.ExplicitEmulatedTLS = EmulatedTLSView->getNumOccurrences() > 0; 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 @@ -381,7 +381,10 @@ static Value *getStackGuard(const TargetLoweringBase *TLI, Module *M, IRBuilder<> &B, bool *SupportsSelectionDAGSP = nullptr) { - if (Value *Guard = TLI->getIRStackGuard(B)) + Value *Guard = TLI->getIRStackGuard(B); + auto GuardMode = TLI->getTargetMachine().Options.StackProtectorGuard; + if ((GuardMode == llvm::StackProtectorGuards::TLS || + GuardMode == llvm::StackProtectorGuards::None) && Guard) return B.CreateLoad(B.getInt8PtrTy(), Guard, true, "StackGuard"); // Use SelectionDAG SSP handling, since there isn't an IR guard. diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -2469,13 +2469,23 @@ // defines ZX_TLS_STACK_GUARD_OFFSET with this value. return SegmentOffset(IRB, 0x10, getAddressSpace()); } else { + unsigned AddressSpace = getAddressSpace(); + // Specially, some users may customize the base reg and offset. + unsigned Offset = getTargetMachine().Options.StackProtectorGuardOffset; + // If we don't set -stack-protector-guard-offset value: // %fs:0x28, unless we're using a Kernel code model, in which case // it's %gs:0x28. gs:0x14 on i386. - unsigned Offset = (Subtarget.is64Bit()) ? 0x28 : 0x14; - return SegmentOffset(IRB, Offset, getAddressSpace()); + if (Offset == (unsigned)-1) + Offset = (Subtarget.is64Bit()) ? 0x28 : 0x14; + + auto GuardReg = getTargetMachine().Options.StackProtectorGuardReg; + if (GuardReg == "fs") + AddressSpace = X86AS::FS; + else if (GuardReg == "gs") + AddressSpace = X86AS::GS; + return SegmentOffset(IRB, Offset, AddressSpace); } } - return TargetLowering::getIRStackGuard(IRB); } @@ -2497,8 +2507,13 @@ } return; } + + auto GuardMode = getTargetMachine().Options.StackProtectorGuard; + // glibc, bionic, and Fuchsia have a special slot for the stack guard. - if (hasStackGuardSlotTLS(Subtarget.getTargetTriple())) + if ((GuardMode == llvm::StackProtectorGuards::TLS || + GuardMode == llvm::StackProtectorGuards::None) + && hasStackGuardSlotTLS(Subtarget.getTargetTriple())) return; TargetLowering::insertSSPDeclarations(M); } diff --git a/llvm/test/CodeGen/X86/stack-protector-3.ll b/llvm/test/CodeGen/X86/stack-protector-3.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/stack-protector-3.ll @@ -0,0 +1,67 @@ +; RUN: llc -mtriple=x86_64-pc-linux-gnu -o - < %s | FileCheck --check-prefix=CHECK-TLS-FS-40 %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard=tls -o - < %s | FileCheck --check-prefix=CHECK-TLS-FS-40 %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard=global -o - < %s | FileCheck --check-prefix=CHECK-GLOBAL %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard-reg=fs -o - < %s | FileCheck --check-prefix=CHECK-TLS-FS-40 %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard-reg=gs -o - < %s | FileCheck --check-prefix=CHECK-GS %s +; RUN: llc -mtriple=x86_64-pc-linux-gnu -stack-protector-guard-offset=20 -o - < %s | FileCheck --check-prefix=CHECK-OFFSET %s + +; CHECK-TLS-FS-40: movq %fs:40, %rax +; CHECK-TLS-FS-40: movq %fs:40, %rax +; CHECK-TLS-FS-40-NEXT: cmpq 16(%rsp), %rax +; CHECK-TLS-FS-40-NEXT: jne .LBB0_2 +; CHECK-TLS-FS-40: .LBB0_2: +; CHECK-TLS-FS-40-NEXT: .cfi_def_cfa_offset 32 +; CHECK-TLS-FS-40-NEXT: callq __stack_chk_fail + +; CHECK-GS: movq %gs:40, %rax +; CHECK-GS: movq %gs:40, %rax +; CHECK-GS-NEXT: cmpq 16(%rsp), %rax +; CHECK-GS-NEXT: jne .LBB0_2 +; CHECK-GS: .LBB0_2: +; CHECK-GS-NEXT: .cfi_def_cfa_offset 32 +; CHECK-GS-NEXT: callq __stack_chk_fail + +; CHECK-OFFSET: movq %fs:20, %rax +; CHECK-OFFSET: movq %fs:20, %rax +; CHECK-OFFSET-NEXT: cmpq 16(%rsp), %rax +; CHECK-OFFSET-NEXT: jne .LBB0_2 +; CHECK-OFFSET: .LBB0_2: +; CHECK-OFFSET-NEXT: .cfi_def_cfa_offset 32 +; CHECK-OFFSET-NEXT: callq __stack_chk_fail + +; CHECK-GLOBAL: movq __stack_chk_guard(%rip), %rax +; CHECK-GLOBAL: movq __stack_chk_guard(%rip), %rax +; CHECK-GLOBAL-NEXT: cmpq 16(%rsp), %rax +; CHECK-GLOBAL-NEXT: jne .LBB0_2 +; CHECK-GLOBAL: .LBB0_2: +; CHECK-GLOBAL-NEXT: .cfi_def_cfa_offset 32 +; CHECK-GLOBAL-NEXT: callq __stack_chk_fail + +; ModuleID = 't.c' +@.str = private unnamed_addr constant [14 x i8] c"stackoverflow\00", align 1 +@a = dso_local local_unnamed_addr global i8* null, align 8 + +; Function Attrs: nounwind sspreq uwtable writeonly +define dso_local i32 @main() local_unnamed_addr #0 { +entry: + %array = alloca [5 x i8], align 1 + %0 = getelementptr inbounds [5 x i8], [5 x i8]* %array, i64 0, i64 0 + call void @llvm.lifetime.start.p0i8(i64 5, i8* nonnull %0) #2 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(14) %0, i8* nonnull align 1 dereferenceable(14) getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0), i64 14, i1 false) #2 + store i8* %0, i8** @a, align 8 + call void @llvm.lifetime.end.p0i8(i64 5, i8* nonnull %0) #2 + ret i32 0 +} + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +attributes #0 = { nounwind sspreq uwtable writeonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind willreturn } +attributes #2 = { nounwind }