Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

**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
----------------------------------

Expand Down
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ Resolutions to C++ Defect Reports
C Language Changes
------------------

- Clang now supports the
:ref:`__builtin_stack_address <builtin_stack_address-doc>` () builtin.
The semantics match those of GCC's builtin with the same name.

C2y Feature Support
^^^^^^^^^^^^^^^^^^^

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions clang/test/CodeGen/builtin-stackaddress.c
Original file line number Diff line number Diff line change
@@ -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();
}
26 changes: 26 additions & 0 deletions clang/test/CodeGenCXX/builtin-stackaddress.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
5 changes: 5 additions & 0 deletions clang/test/Sema/builtin-stackaddress.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
29 changes: 29 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14389,6 +14389,35 @@ Semantics:

Note this intrinsic is only verified on AArch64 and ARM.

'``llvm.stackaddress``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare ptr @llvm.stackaddress.p0()

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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/ISDOpcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,7 @@ def int_addressofreturnaddress : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [], [In
def int_frameaddress : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_i32_ty],
[IntrNoMem, ImmArg<ArgIndex<0>>]>;
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],
Expand Down
9 changes: 9 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand All @@ -3690,6 +3692,13 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
} else {
Results.push_back(DAG.getUNDEF(Node->getValueType(0)));
Results.push_back(Node->getOperand(0));

const char *IntrinsicName = Node->getOpcode() == ISD::STACKADDRESS
? "llvm.stackaddress"
: "llvm.stacksave";
DAG.getContext()->diagnose(DiagnosticInfoLegalizationFailure(
Twine(IntrinsicName) + " is not supported on this target.",
DAG.getMachineFunction().getFunction(), dl.getDebugLoc()));
}
break;
case ISD::STACKRESTORE:
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/CodeGen/TargetLoweringBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 like STACKSAVE nodes.
// On SPARC targets, custom lowering is required.
setOperationAction(ISD::STACKADDRESS, MVT::Other, Expand);
}

MVT TargetLoweringBase::getScalarShiftAmountTy(const DataLayout &DL,
Expand Down
25 changes: 25 additions & 0 deletions llvm/lib/Target/Sparc/SparcISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -2720,6 +2721,28 @@ 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 SPReg = SP::O6;
SDValue SP = DAG.getCopyFromReg(Chain, DL, SPReg, VT);

// Unbias the stack pointer register.
unsigned 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);
Expand Down Expand Up @@ -3117,6 +3140,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);
Expand Down
10 changes: 10 additions & 0 deletions llvm/test/CodeGen/AArch64/stackaddress.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
; RUN: llc < %s -mtriple=aarch64 | FileCheck %s

declare ptr @llvm.stackaddress.p0()

define ptr @test() {
; CHECK: mov x0, sp
; CHECK: ret
%sp = call ptr @llvm.stackaddress.p0()
ret ptr %sp
}
10 changes: 10 additions & 0 deletions llvm/test/CodeGen/ARM/stackaddress.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
; RUN: llc < %s -mtriple=armv7 | FileCheck %s

declare ptr @llvm.stackaddress.p0()

define ptr @test() {
; CHECK: mov r0, sp
; CHECK: bx lr
%sp = call ptr @llvm.stackaddress.p0()
ret ptr %sp
}
9 changes: 9 additions & 0 deletions llvm/test/CodeGen/NVPTX/stackaddress.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
; RUN: not llc < %s -mtriple nvptx 2>&1 | FileCheck %s

declare ptr @llvm.stackaddress.p0()

define ptr @test() {
; CHECK: error: <unknown>:0:0: llvm.stackaddress is not supported on this target.
%sp = call ptr @llvm.stackaddress.p0()
ret ptr %sp
}
16 changes: 16 additions & 0 deletions llvm/test/CodeGen/SPARC/stackaddress.ll
Original file line number Diff line number Diff line change
@@ -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
}
14 changes: 14 additions & 0 deletions llvm/test/CodeGen/X86/stackaddress.ll
Original file line number Diff line number Diff line change
@@ -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
}