diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 97413588fea15..4553c75945c0d 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4248,6 +4248,39 @@ assignment can happen automatically. to a variable, have its address taken, or passed into or returned from a function, because doing so violates bounds safety conventions. +.. _builtin_stack_address-doc: + +``__builtin_stack_address`` +--------------------------- + +``__builtin_stack_address`` returns the address that separates the current +function's (i.e. the one calling the builtin) stack space and the region of the +stack that may be modified by called functions. The semantics match those of GCC's builtin of the same name. + +**Note:** Support for this builtin is currently limited to the following architectures: x86_64, x86. + +**Syntax**: + +.. code-block:: c++ + + void *__builtin_stack_address() + +**Example**: + +.. code-block:: c++ + + void *sp = __builtin_stack_address(); + +**Description**: + +The address returned by ``__builtin_stack_address`` identifies the starting +address of the stack region that may be used by called functions. + +On some architectures (e.g. x86), it's sufficient to return the value in the stack pointer register +directly. On others (e.g. SPARCv9), adjustments are required to the value of the stack pointer +register. ``__builtin_stack_address`` performs the necessary adjustments and returns the correct +boundary address. + Multiprecision Arithmetic Builtins ---------------------------------- diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b35f4ea42818a..02f735f9303dc 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -111,6 +111,9 @@ Resolutions to C++ Defect Reports C Language Changes ------------------ +- Clang now supports the :ref:`__builtin_stack_address ` () builtin. + The semantics match those of GCC's builtin with the same name. + C2y Feature Support ^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 604c9cddfe051..86862c371cd00 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -917,6 +917,12 @@ def FrameAddress : Builtin { let Prototype = "void*(_Constant unsigned int)"; } +def StackAddress : Builtin { + let Spellings = ["__builtin_stack_address"]; + let Attributes = [NoThrow]; + let Prototype = "void*()"; +} + def ClearCache : Builtin { let Spellings = ["__builtin___clear_cache"]; let Attributes = [NoThrow]; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index a1f2a874f010d..cbffeab0beb78 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4700,6 +4700,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Function *F = CGM.getIntrinsic(Intrinsic::frameaddress, AllocaInt8PtrTy); return RValue::get(Builder.CreateCall(F, Depth)); } + case Builtin::BI__builtin_stack_address: { + return RValue::get(Builder.CreateCall( + CGM.getIntrinsic(Intrinsic::stackaddress, AllocaInt8PtrTy))); + } case Builtin::BI__builtin_extract_return_addr: { Value *Address = EmitScalarExpr(E->getArg(0)); Value *Result = getTargetHooks().decodeReturnAddress(*this, Address); diff --git a/clang/test/CodeGen/builtin-stackaddress.c b/clang/test/CodeGen/builtin-stackaddress.c new file mode 100644 index 0000000000000..6acc9c61d9d9b --- /dev/null +++ b/clang/test/CodeGen/builtin-stackaddress.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s + +// CHECK-LABEL: define {{[^@]+}} @a() +// CHECK: call {{[^@]+}} @llvm.stackaddress.p0() +void *a() { + return __builtin_stack_address(); +} diff --git a/clang/test/CodeGenCXX/builtin-stackaddress.cpp b/clang/test/CodeGenCXX/builtin-stackaddress.cpp new file mode 100644 index 0000000000000..20940d98a304e --- /dev/null +++ b/clang/test/CodeGenCXX/builtin-stackaddress.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -o - | llvm-cxxfilt | FileCheck %s --check-prefixes=COMMON,NO-OPT +// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm %s -O3 -o - | llvm-cxxfilt | FileCheck %s --check-prefixes=COMMON,OPT + +struct S { + void *a(); +}; + +// COMMON-LABEL: @S::a() +// COMMON: call ptr @llvm.stackaddress.p0() +void *S::a() { + return __builtin_stack_address(); +} + +// COMMON-LABEL: define {{[^@]+}} @two() +void *two() { + + // The compiler is allowed to inline a function calling `__builtin_stack_address`. + // + // OPT-NOT: define {{[^@]+}} @"two()::$_0::operator()() const" + // OPT: call {{[^@]+}} @llvm.stackaddress.p0() + // + // NO-OPT-DAG: define {{[^@]+}} @"two()::$_0::operator()() const" + // NO-OPT-DAG: call {{[^@]+}} @"two()::$_0::operator()() const" + auto l = []() { return __builtin_stack_address(); }; + return l(); +} diff --git a/clang/test/Sema/builtin-stackaddress.c b/clang/test/Sema/builtin-stackaddress.c index ecdc64d899af5..f95751daf8a01 100644 --- a/clang/test/Sema/builtin-stackaddress.c +++ b/clang/test/Sema/builtin-stackaddress.c @@ -36,3 +36,8 @@ void* h(unsigned x) { // expected-error@+1 {{argument value 1048575 is outside the valid range [0, 65535]}} return __builtin_frame_address(0xFFFFF); } + +void *i() { + // expected-error@+1 {{too many arguments to function call, expected 0, have 1}} + return __builtin_stack_address(0); +} diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index a71eefd1eb685..1d1dd2a59c7aa 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -14389,6 +14389,34 @@ Semantics: Note this intrinsic is only verified on AArch64 and ARM. +'``llvm.stackaddress``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare ptr @llvm.stackaddress() + +Overview: +""""""""" + +The '``llvm.stackaddress``' instrinsic returns the starting address of the stack region that may be +used by called functions. + +Semantics: +"""""""""" + +This intrinsic returns the *logical* value of the stack pointer register, that is, the address +separating the stack space of the current function from the stack space that may be modified by +called functions. + +On certain targets (e.g. x86), the logical and actual (or physical) values of the stack pointer +register are the same. However, on other architectures (e.g. SPARCv9), the logical value of the +stack pointer register may differ from the physical value. '``llvm.stackaddress``' handles this +discrepancy and returns the correct boundary address. + '``llvm.frameaddress``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h index 465e4a0a9d0d8..4705710d035ff 100644 --- a/llvm/include/llvm/CodeGen/ISDOpcodes.h +++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h @@ -121,6 +121,11 @@ enum NodeType { /// function calling this intrinsic. SPONENTRY, + /// STACKADDRESS - Represents the llvm.stackaddress intrinsic. Takes no + /// argument and returns the starting address of the stack region that may be + /// used by called functions. + STACKADDRESS, + /// LOCAL_RECOVER - Represents the llvm.localrecover intrinsic. /// Materializes the offset from the local object pointer of another /// function to a particular local object passed to llvm.localescape. The diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index e0ee12391b31d..aece080520515 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -853,6 +853,7 @@ def int_addressofreturnaddress : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [], [In def int_frameaddress : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_i32_ty], [IntrNoMem, ImmArg>]>; def int_sponentry : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [], [IntrNoMem]>; +def int_stackaddress : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [], []>; def int_read_register : DefaultAttrsIntrinsic<[llvm_anyint_ty], [llvm_metadata_ty], [IntrReadMem], "llvm.read_register">; def int_write_register : Intrinsic<[], [llvm_metadata_ty, llvm_anyint_ty], diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index bcfc2c5dc9f83..357cfc333b921 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -1012,6 +1012,7 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) { case ISD::INTRINSIC_WO_CHAIN: case ISD::INTRINSIC_VOID: case ISD::STACKSAVE: + case ISD::STACKADDRESS: Action = TLI.getOperationAction(Node->getOpcode(), MVT::Other); break; case ISD::GET_DYNAMIC_AREA_OFFSET: @@ -3680,6 +3681,7 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) { Results.push_back(Tmp1); break; } + case ISD::STACKADDRESS: case ISD::STACKSAVE: // Expand to CopyFromReg if the target set // StackPointerRegisterToSaveRestore. diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 2a1ef2b980ac4..fb276566036d9 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -7326,10 +7326,13 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, setValue(&I, DAG.getNode(ISD::UCMP, sdl, DestVT, Op1, Op2)); break; } + case Intrinsic::stackaddress: case Intrinsic::stacksave: { + unsigned SDOpcode = Intrinsic == Intrinsic::stackaddress ? ISD::STACKADDRESS + : ISD::STACKSAVE; SDValue Op = getRoot(); EVT VT = TLI.getValueType(DAG.getDataLayout(), I.getType()); - Res = DAG.getNode(ISD::STACKSAVE, sdl, DAG.getVTList(VT, MVT::Other), Op); + Res = DAG.getNode(SDOpcode, sdl, DAG.getVTList(VT, MVT::Other), Op); setValue(&I, Res); DAG.setRoot(Res.getValue(1)); return; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp index 900da7645504f..b14ff3b1a1939 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -148,6 +148,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const { case ISD::ADDROFRETURNADDR: return "ADDROFRETURNADDR"; case ISD::FRAMEADDR: return "FRAMEADDR"; case ISD::SPONENTRY: return "SPONENTRY"; + case ISD::STACKADDRESS: return "STACKADDRESS"; case ISD::LOCAL_RECOVER: return "LOCAL_RECOVER"; case ISD::READ_REGISTER: return "READ_REGISTER"; case ISD::WRITE_REGISTER: return "WRITE_REGISTER"; diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp index 350948a92a3ae..6c9a17fd27fa0 100644 --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -956,6 +956,10 @@ void TargetLoweringBase::initActions() { // This one by default will call __clear_cache unless the target // wants something different. setOperationAction(ISD::CLEAR_CACHE, MVT::Other, LibCall); + + // By default, STACKADDRESS nodes are expanded to STACKSAVE nodes. + // On SPARC targets, custom lowering is required. + setOperationAction(ISD::STACKADDRESS, MVT::Other, Expand); } MVT TargetLoweringBase::getScalarShiftAmountTy(const DataLayout &DL, diff --git a/llvm/lib/Target/Sparc/SparcISelLowering.cpp b/llvm/lib/Target/Sparc/SparcISelLowering.cpp index dd221327dbdc6..ef5fc0154fbe0 100644 --- a/llvm/lib/Target/Sparc/SparcISelLowering.cpp +++ b/llvm/lib/Target/Sparc/SparcISelLowering.cpp @@ -1861,6 +1861,7 @@ SparcTargetLowering::SparcTargetLowering(const TargetMachine &TM, setOperationAction(ISD::STACKSAVE , MVT::Other, Expand); setOperationAction(ISD::STACKRESTORE , MVT::Other, Expand); setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32 , Custom); + setOperationAction(ISD::STACKADDRESS, MVT::Other, Custom); setStackPointerRegisterToSaveRestore(SP::O6); @@ -2720,6 +2721,30 @@ static SDValue LowerVAARG(SDValue Op, SelectionDAG &DAG) { Align(std::min(PtrVT.getFixedSizeInBits(), VT.getFixedSizeInBits()) / 8)); } +static SDValue LowerSTACKADDRESS(SDValue Op, SelectionDAG &DAG, + const SparcSubtarget *Subtarget) { + SDValue Chain = Op.getOperand(0); + EVT VT = Op->getValueType(0); + SDLoc DL(Op); + unsigned OffsetToStackStart = 0; + + unsigned SPReg = SP::O6; + SDValue SP = DAG.getCopyFromReg(Chain, DL, SPReg, VT); + + // Unbias the stack pointer register. + OffsetToStackStart += Subtarget->getStackPointerBias(); + // Move past the register save area: 8 in registers + 8 local registers. + OffsetToStackStart += 16 * (Subtarget->is64Bit() ? 8 : 4); + // Move past the struct return address slot (4 bytes) on SPARC 32-bit. + if (!Subtarget->is64Bit()) { + OffsetToStackStart += 4; + } + + SDValue StackAddr = DAG.getNode(ISD::ADD, DL, VT, SP, + DAG.getConstant(OffsetToStackStart, DL, VT)); + return DAG.getMergeValues({StackAddr, Chain}, DL); +} + static SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG, const SparcSubtarget *Subtarget) { SDValue Chain = Op.getOperand(0); @@ -3117,6 +3142,8 @@ LowerOperation(SDValue Op, SelectionDAG &DAG) const { case ISD::VAARG: return LowerVAARG(Op, DAG); case ISD::DYNAMIC_STACKALLOC: return LowerDYNAMIC_STACKALLOC(Op, DAG, Subtarget); + case ISD::STACKADDRESS: + return LowerSTACKADDRESS(Op, DAG, Subtarget); case ISD::LOAD: return LowerLOAD(Op, DAG); case ISD::STORE: return LowerSTORE(Op, DAG); diff --git a/llvm/test/CodeGen/ARM/stackaddress.ll b/llvm/test/CodeGen/ARM/stackaddress.ll new file mode 100644 index 0000000000000..e2fc0926cd143 --- /dev/null +++ b/llvm/test/CodeGen/ARM/stackaddress.ll @@ -0,0 +1,14 @@ +; RUN: llc < %s -mtriple=armv7 | FileCheck %s --check-prefix=armv7 +; RUN: llc < %s -mtriple=aarch64 | FileCheck %s --check-prefix=aarch64 + +declare ptr @llvm.stackaddress.p0() + +define ptr @test() { +; armv7: mov r0, sp +; armv7: bx lr + +; aarch64: mov x0, sp +; aarch64: ret + %sp = call ptr @llvm.stackaddress.p0() + ret ptr %sp +} diff --git a/llvm/test/CodeGen/SPARC/stackaddress.ll b/llvm/test/CodeGen/SPARC/stackaddress.ll new file mode 100644 index 0000000000000..fce5f1cba8fb1 --- /dev/null +++ b/llvm/test/CodeGen/SPARC/stackaddress.ll @@ -0,0 +1,16 @@ +; RUN: llc < %s -mtriple=sparc | FileCheck --check-prefix=sparc32 %s +; RUN: llc < %s -mtriple=sparcv9 | FileCheck --check-prefix=sparc64 %s + +declare ptr @llvm.stackaddress.p0() + +define ptr @test() { +; sparc32: save %sp, -96, %sp +; sparc32: ret +; sparc32: restore %sp, 68, %o0 +; +; sparc64: save %sp, -128, %sp +; sparc64: ret +; sparc64: restore %sp, 2175, %o0 + %sp = call ptr @llvm.stackaddress.p0() + ret ptr %sp +} diff --git a/llvm/test/CodeGen/X86/stackaddress.ll b/llvm/test/CodeGen/X86/stackaddress.ll new file mode 100644 index 0000000000000..6d73b2ec82c22 --- /dev/null +++ b/llvm/test/CodeGen/X86/stackaddress.ll @@ -0,0 +1,14 @@ +; RUN: llc < %s -mtriple=x86_64-linux-gnu -o - | FileCheck --check-prefix=x86_64 %s +; RUN: llc < %s -mtriple=i386-linux-gnu -o - | FileCheck --check-prefix=i386 %s + +declare ptr @llvm.stackaddress.p0() + +define ptr @test() { +; x86_64: movq %rsp, %rax +; x86_64: retq + +; i386: movl %esp, %eax +; i386: retl + %sp = call ptr @llvm.stackaddress.p0() + ret ptr %sp +}