Support for trampolines, except for X86 codegen which is

still under discussion.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@40549 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Duncan Sands 2007-07-27 12:58:54 +00:00
parent ada779fb11
commit 36397f5034
20 changed files with 252 additions and 14 deletions

View File

@ -200,6 +200,12 @@
<li><a href="#int_memory_barrier">'<tt>llvm.memory.barrier</tt>' Intrinsic</a></li>
</ol>
</li>
<li><a href="#int_trampoline">Trampoline Intrinsics</a>
<ol>
<li><a href="#int_it">'<tt>llvm.init.trampoline</tt>' Intrinsic</a></li>
<li><a href="#int_at">'<tt>llvm.adjust.trampoline</tt>' Intrinsic</a></li>
</ol>
</li>
<li><a href="#int_general">General intrinsics</a>
<ol>
<li><a href="#int_var_annotation">
@ -5121,6 +5127,105 @@ declare void @llvm.memory.barrier( i1 &lt;ll&gt;, i1 &lt;ls&gt;, i1 &lt;sl&gt;,
</pre>
</div>
<!-- ======================================================================= -->
<div class="doc_subsection">
<a name="int_trampoline">Trampoline Intrinsics</a>
</div>
<div class="doc_text">
<p>
These intrinsics make it possible to excise one parameter, marked with
the <tt>nest</tt> attribute, from a function. The result is a callable
function pointer lacking the nest parameter - the caller does not need
to provide a value for it. Instead, the value to use is stored in
advance in a "trampoline", a block of memory usually allocated
on the stack, which also contains code to splice the nest value into the
argument list. This is used to implement the GCC nested function address
extension.
</p>
<p>
For example, if the function is
<tt>i32 f(i8* nest %c, i32 %x, i32 %y)</tt> then the resulting function
pointer has signature <tt>i32 (i32, i32)*</tt>. It can be created as follows:
<pre>
%tramp1 = alloca [10 x i8], align 4 ; size and alignment only correct for X86
%tramp = getelementptr [10 x i8]* %tramp1, i32 0, i32 0
call void @llvm.init.trampoline( i8* %tramp, i8* bitcast (i32 (i8* nest , i32, i32)* @f to i8*), i8* %nval )
%adj = call i8* @llvm.adjust.trampoline( i8* %tramp )
%fp = bitcast i8* %adj to i32 (i32, i32)*
</pre>
The call <tt>%val = call i32 %fp( i32 %x, i32 %y )</tt> is then equivalent to
<tt>%val = call i32 %f( i8* %nval, i32 %x, i32 %y )</tt>.
</p>
<p>
Trampolines are currently only supported on the X86 architecture.
</p>
</div>
<!-- _______________________________________________________________________ -->
<div class="doc_subsubsection">
<a name="int_it">'<tt>llvm.init.trampoline</tt>' Intrinsic</a>
</div>
<div class="doc_text">
<h5>Syntax:</h5>
<pre>
declare void @llvm.init.trampoline(i8* &lt;tramp&gt;, i8* &lt;func&gt;, i8* &lt;nval&gt;)
</pre>
<h5>Overview:</h5>
<p>
This initializes the memory pointed to by <tt>tramp</tt> as a trampoline.
</p>
<h5>Arguments:</h5>
<p>
The <tt>llvm.init.trampoline</tt> intrinsic takes three arguments, all
pointers. The <tt>tramp</tt> argument must point to a sufficiently large
and sufficiently aligned block of memory; this memory is written to by the
intrinsic. Currently LLVM provides no help in determining just how big and
aligned the memory needs to be. The <tt>func</tt> argument must hold a
function bitcast to an <tt>i8*</tt>.
</p>
<h5>Semantics:</h5>
<p>
The block of memory pointed to by <tt>tramp</tt> is filled with target
dependent code, turning it into a function.
The new function's signature is the same as that of <tt>func</tt> with
any arguments marked with the <tt>nest</tt> attribute removed. At most
one such <tt>nest</tt> argument is allowed, and it must be of pointer
type. Calling the new function is equivalent to calling <tt>func</tt>
with the same argument list, but with <tt>nval</tt> used for the missing
<tt>nest</tt> argument.
</p>
</div>
<!-- _______________________________________________________________________ -->
<div class="doc_subsubsection">
<a name="int_at">'<tt>llvm.adjust.trampoline</tt>' Intrinsic</a>
</div>
<div class="doc_text">
<h5>Syntax:</h5>
<pre>
declare i8* @llvm.adjust.trampoline(i8* &lt;tramp&gt;)
</pre>
<h5>Overview:</h5>
<p>
This intrinsic returns a function pointer suitable for executing
the trampoline code pointed to by <tt>tramp</tt>.
</p>
<h5>Arguments:</h5>
<p>
The <tt>llvm.adjust.trampoline</tt> takes one argument, a pointer to a
trampoline initialized by the
<a href="#int_it">'<tt>llvm.init.trampoline</tt>' intrinsic</a>.
</p>
<h5>Semantics:</h5>
<p>
A function pointer that can be used to execute the trampoline code in
<tt>tramp</tt> is returned. The returned value should be bitcast to an
<a href="#int_trampoline">appropriate function pointer type</a>
before being called.
</p>
</div>
<!-- ======================================================================= -->
<div class="doc_subsection">
<a name="int_general">General Intrinsics</a>

View File

@ -66,6 +66,8 @@ namespace ISD {
StructReturnOffs = 3,
ByVal = 1<<4, ///< Struct passed by value
ByValOffs = 4,
Nest = 1<<5, ///< Parameter is nested function static chain
NestOffs = 5,
OrigAlignment = 0x1F<<27,
OrigAlignmentOffs = 27
};
@ -528,7 +530,19 @@ namespace ISD {
// number, then a column then a file id (provided by MachineModuleInfo.) It
// produces a token chain as output.
DEBUG_LOC,
// ADJUST_TRAMP - This corresponds to the adjust_trampoline intrinsic.
// It takes a value as input and returns a value as output.
ADJUST_TRAMP,
// TRAMPOLINE - This corresponds to the init_trampoline intrinsic.
// It takes as input a token chain, the pointer to the trampoline,
// the pointer to the nested function, the pointer to pass for the
// 'nest' parameter, a SRCVALUE for the trampoline and another for
// the nested function (allowing targets to access the original
// Function*). It produces a token chain as output.
TRAMPOLINE,
// BUILTIN_OP_END - This must be the last enum value in this list.
BUILTIN_OP_END
};

View File

@ -247,6 +247,14 @@ def int_var_annotation : Intrinsic<[llvm_void_ty, llvm_ptr_ty, llvm_ptr_ty,
llvm_ptr_ty, llvm_i32_ty],
[], "llvm.var.annotation">;
//===------------------------ Trampoline Intrinsics -----------------------===//
//
def int_init_trampoline : Intrinsic<[llvm_void_ty, llvm_ptr_ty, llvm_ptr_ty,
llvm_ptr_ty], []>,
GCCBuiltin<"__builtin_init_trampoline">;
def int_adjust_trampoline : Intrinsic<[llvm_ptr_ty, llvm_ptr_ty], [IntrNoMem]>,
GCCBuiltin<"__builtin_adjust_trampoline">;
//===----------------------------------------------------------------------===//
// Target-specific intrinsics
//===----------------------------------------------------------------------===//

View File

@ -30,14 +30,15 @@ namespace ParamAttr {
/// @brief Function parameter attributes.
enum Attributes {
None = 0, ///< No attributes have been set
ZExt = 1 << 0, ///< zero extended before/after call
SExt = 1 << 1, ///< sign extended before/after call
NoReturn = 1 << 2, ///< mark the function as not returning
InReg = 1 << 3, ///< force argument to be passed in register
StructRet = 1 << 4, ///< hidden pointer to structure to return
ZExt = 1 << 0, ///< Zero extended before/after call
SExt = 1 << 1, ///< Sign extended before/after call
NoReturn = 1 << 2, ///< Mark the function as not returning
InReg = 1 << 3, ///< Force argument to be passed in register
StructRet = 1 << 4, ///< Hidden pointer to structure to return
NoUnwind = 1 << 5, ///< Function doesn't unwind stack
NoAlias = 1 << 6, ///< Considered to not alias after call.
ByVal = 1 << 7 ///< Pass structure by value
NoAlias = 1 << 6, ///< Considered to not alias after call
ByVal = 1 << 7, ///< Pass structure by value
Nest = 1 << 8 ///< Nested function static chain
};
}

View File

@ -812,8 +812,10 @@ public:
bool isZExt;
bool isInReg;
bool isSRet;
bool isNest;
ArgListEntry():isSExt(false), isZExt(false), isInReg(false), isSRet(false) { };
ArgListEntry() : isSExt(false), isZExt(false), isInReg(false),
isSRet(false), isNest(false) { };
};
typedef std::vector<ArgListEntry> ArgListTy;
virtual std::pair<SDOperand, SDOperand>

View File

@ -233,6 +233,7 @@ nounwind { return NOUNWIND; }
noreturn { return NORETURN; }
noalias { return NOALIAS; }
byval { return BYVAL; }
nest { return NEST; }
void { RET_TY(Type::VoidTy, VOID); }
float { RET_TY(Type::FloatTy, FLOAT); }

View File

@ -1101,7 +1101,7 @@ Module *llvm::RunVMAsmParser(const char * AsmString, Module * M) {
%token <OtherOpVal> EXTRACTELEMENT INSERTELEMENT SHUFFLEVECTOR
// Function Attributes
%token SIGNEXT ZEROEXT NORETURN INREG SRET NOUNWIND NOALIAS BYVAL
%token SIGNEXT ZEROEXT NORETURN INREG SRET NOUNWIND NOALIAS BYVAL NEST
// Visibility Styles
%token DEFAULT HIDDEN PROTECTED
@ -1229,7 +1229,8 @@ ParamAttr : ZEROEXT { $$ = ParamAttr::ZExt; }
| INREG { $$ = ParamAttr::InReg; }
| SRET { $$ = ParamAttr::StructRet; }
| NOALIAS { $$ = ParamAttr::NoAlias; }
| BYVAL { $$ = ParamAttr::ByVal; }
| BYVAL { $$ = ParamAttr::ByVal; }
| NEST { $$ = ParamAttr::Nest; }
;
OptParamAttrs : /* empty */ { $$ = ParamAttr::None; }

View File

@ -3156,6 +3156,31 @@ SDOperand SelectionDAGLegalize::LegalizeOp(SDOperand Op) {
}
break;
}
case ISD::ADJUST_TRAMP: {
Tmp1 = LegalizeOp(Node->getOperand(0));
switch (TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0))) {
default: assert(0 && "This action is not supported yet!");
case TargetLowering::Custom:
Result = DAG.UpdateNodeOperands(Result, Tmp1);
Result = TLI.LowerOperation(Result, DAG);
if (Result.Val) break;
// FALL THROUGH
case TargetLowering::Expand:
Result = Tmp1;
break;
}
break;
}
case ISD::TRAMPOLINE: {
SDOperand Ops[6];
for (unsigned i = 0; i != 6; ++i)
Ops[i] = LegalizeOp(Node->getOperand(i));
Result = DAG.UpdateNodeOperands(Result, Ops, 6);
// The only option for this node is to custom lower it.
Result = TLI.LowerOperation(Result, DAG);
assert(Result.Val && "Should always custom lower!");
break;
}
}
assert(Result.getValueType() == Op.getValueType() &&

View File

@ -3513,6 +3513,10 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const {
case ISD::LOCATION: return "location";
case ISD::DEBUG_LOC: return "debug_loc";
// Trampolines
case ISD::ADJUST_TRAMP: return "adjust_tramp";
case ISD::TRAMPOLINE: return "trampoline";
case ISD::CONDCODE:
switch (cast<CondCodeSDNode>(this)->get()) {
default: assert(0 && "Unknown setcc condition!");

View File

@ -2863,6 +2863,28 @@ SelectionDAGLowering::visitIntrinsicCall(CallInst &I, unsigned Intrinsic) {
case Intrinsic::var_annotation:
// Discard annotate attributes
return 0;
case Intrinsic::adjust_trampoline: {
SDOperand Arg = getValue(I.getOperand(1));
setValue(&I, DAG.getNode(ISD::ADJUST_TRAMP, TLI.getPointerTy(), Arg));
return 0;
}
case Intrinsic::init_trampoline: {
const Function *F =
cast<Function>(IntrinsicInst::StripPointerCasts(I.getOperand(2)));
SDOperand Ops[6];
Ops[0] = getRoot();
Ops[1] = getValue(I.getOperand(1));
Ops[2] = getValue(I.getOperand(2));
Ops[3] = getValue(I.getOperand(3));
Ops[4] = DAG.getSrcValue(I.getOperand(1));
Ops[5] = DAG.getSrcValue(F);
DAG.setRoot(DAG.getNode(ISD::TRAMPOLINE, MVT::Other, Ops, 6));
return 0;
}
}
}
@ -2892,6 +2914,7 @@ void SelectionDAGLowering::LowerCallTo(Instruction &I,
Entry.isZExt = Attrs && Attrs->paramHasAttr(attrInd, ParamAttr::ZExt);
Entry.isInReg = Attrs && Attrs->paramHasAttr(attrInd, ParamAttr::InReg);
Entry.isSRet = Attrs && Attrs->paramHasAttr(attrInd, ParamAttr::StructRet);
Entry.isNest = Attrs && Attrs->paramHasAttr(attrInd, ParamAttr::Nest);
Args.push_back(Entry);
}
@ -3827,6 +3850,8 @@ TargetLowering::LowerArguments(Function &F, SelectionDAG &DAG) {
Flags |= ISD::ParamFlags::StructReturn;
if (Attrs && Attrs->paramHasAttr(j, ParamAttr::ByVal))
Flags |= ISD::ParamFlags::ByVal;
if (Attrs && Attrs->paramHasAttr(j, ParamAttr::Nest))
Flags |= ISD::ParamFlags::Nest;
Flags |= (OriginalAlignment << ISD::ParamFlags::OrigAlignmentOffs);
switch (getTypeAction(VT)) {
@ -3945,6 +3970,8 @@ TargetLowering::LowerCallTo(SDOperand Chain, const Type *RetTy,
Flags |= ISD::ParamFlags::InReg;
if (Args[i].isSRet)
Flags |= ISD::ParamFlags::StructReturn;
if (Args[i].isNest)
Flags |= ISD::ParamFlags::Nest;
Flags |= OriginalAlignment << ISD::ParamFlags::OrigAlignmentOffs;
switch (getTypeAction(VT)) {

View File

@ -190,7 +190,12 @@ ARMTargetLowering::ARMTargetLowering(TargetMachine &TM)
setOperationAction(ISD::MEMSET , MVT::Other, Expand);
setOperationAction(ISD::MEMCPY , MVT::Other, Custom);
setOperationAction(ISD::MEMMOVE , MVT::Other, Expand);
if (Subtarget->isThumb())
setOperationAction(ISD::ADJUST_TRAMP, MVT::i32, Custom);
else
setOperationAction(ISD::ADJUST_TRAMP, MVT::i32, Expand);
// Use the default implementation.
setOperationAction(ISD::VASTART , MVT::Other, Expand);
setOperationAction(ISD::VAARG , MVT::Other, Expand);
@ -1413,6 +1418,14 @@ SDOperand ARMTargetLowering::LowerMEMCPY(SDOperand Op, SelectionDAG &DAG) {
return Chain;
}
SDOperand ARMTargetLowering::LowerADJUST_TRAMP(SDOperand Op,
SelectionDAG &DAG) {
// Thumb trampolines should be entered in thumb mode, so set the bottom bit
// of the address.
return DAG.getNode(ISD::OR, MVT::i32, Op.getOperand(0),
DAG.getConstant(1, MVT::i32));
}
SDOperand ARMTargetLowering::LowerOperation(SDOperand Op, SelectionDAG &DAG) {
switch (Op.getOpcode()) {
default: assert(0 && "Don't know how to custom lower this!"); abort();
@ -1444,6 +1457,7 @@ SDOperand ARMTargetLowering::LowerOperation(SDOperand Op, SelectionDAG &DAG) {
case ISD::FRAMEADDR: break;
case ISD::GLOBAL_OFFSET_TABLE: return LowerGLOBAL_OFFSET_TABLE(Op, DAG);
case ISD::MEMCPY: return LowerMEMCPY(Op, DAG);
case ISD::ADJUST_TRAMP: return LowerADJUST_TRAMP(Op, DAG);
}
return SDOperand();
}

View File

@ -138,6 +138,7 @@ namespace llvm {
SDOperand LowerFORMAL_ARGUMENTS(SDOperand Op, SelectionDAG &DAG);
SDOperand LowerBR_JT(SDOperand Op, SelectionDAG &DAG);
SDOperand LowerMEMCPY(SDOperand Op, SelectionDAG &DAG);
SDOperand LowerADJUST_TRAMP(SDOperand Op, SelectionDAG &DAG);
};
}

View File

@ -124,6 +124,9 @@ AlphaTargetLowering::AlphaTargetLowering(TargetMachine &TM) : TargetLowering(TM)
setOperationAction(ISD::ExternalSymbol, MVT::i64, Custom);
setOperationAction(ISD::GlobalTLSAddress, MVT::i64, Custom);
setOperationAction(ISD::ADJUST_TRAMP, MVT::i32, Expand);
setOperationAction(ISD::ADJUST_TRAMP, MVT::i64, Expand);
setOperationAction(ISD::VASTART, MVT::Other, Custom);
setOperationAction(ISD::VAEND, MVT::Other, Expand);
setOperationAction(ISD::VACOPY, MVT::Other, Custom);

View File

@ -97,6 +97,8 @@ IA64TargetLowering::IA64TargetLowering(TargetMachine &TM)
setOperationAction(ISD::ROTR , MVT::i64 , Expand);
setOperationAction(ISD::BSWAP, MVT::i64 , Expand); // mux @rev
setOperationAction(ISD::ADJUST_TRAMP, MVT::i64, Expand);
// VASTART needs to be custom lowered to use the VarArgsFrameIndex
setOperationAction(ISD::VAARG , MVT::Other, Custom);
setOperationAction(ISD::VASTART , MVT::Other, Custom);

View File

@ -102,6 +102,8 @@ MipsTargetLowering(MipsTargetMachine &TM): TargetLowering(TM)
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
setOperationAction(ISD::ADJUST_TRAMP, MVT::i32, Expand);
setStackPointerRegisterToSaveRestore(Mips::SP);
computeRegisterProperties();
}

View File

@ -169,7 +169,10 @@ PPCTargetLowering::PPCTargetLowering(PPCTargetMachine &TM)
// RET must be custom lowered, to meet ABI requirements
setOperationAction(ISD::RET , MVT::Other, Custom);
setOperationAction(ISD::ADJUST_TRAMP, MVT::i32, Expand);
setOperationAction(ISD::ADJUST_TRAMP, MVT::i64, Expand);
// VASTART needs to be custom lowered to use the VarArgsFrameIndex
setOperationAction(ISD::VASTART , MVT::Other, Custom);

View File

@ -215,7 +215,9 @@ SparcTargetLowering::SparcTargetLowering(TargetMachine &TM)
// RET must be custom lowered, to meet ABI requirements
setOperationAction(ISD::RET , MVT::Other, Custom);
setOperationAction(ISD::ADJUST_TRAMP, MVT::i32, Expand);
// VASTART needs to be custom lowered to use the VarArgsFrameIndex.
setOperationAction(ISD::VASTART , MVT::Other, Custom);
// VAARG needs to be lowered to not do unaligned accesses for doubles.

View File

@ -45,6 +45,10 @@ class CCIfCC<string CC, CCAction A>
/// the specified action.
class CCIfInReg<CCAction A> : CCIf<"ArgFlags & ISD::ParamFlags::InReg", A> {}
/// CCIfNest - If this argument is marked with the 'nest' attribute, apply
/// the specified action.
class CCIfNest<CCAction A> : CCIf<"ArgFlags & ISD::ParamFlags::Nest", A> {}
/// CCIfNotVarArg - If the current function is not vararg - apply the action
class CCIfNotVarArg<CCAction A> : CCIf<"!State.isVarArg()", A> {}

View File

@ -105,6 +105,8 @@ ParamAttrsList::getParamAttrsText(uint16_t Attrs) {
Result += "sret ";
if (Attrs & ParamAttr::ByVal)
Result += "byval ";
if (Attrs & ParamAttr::Nest)
Result += "nest ";
return Result;
}

View File

@ -361,6 +361,7 @@ void Verifier::visitFunction(Function &F) {
if (const ParamAttrsList *Attrs = FT->getParamAttrs()) {
unsigned Idx = 1;
bool SawNest = false;
Assert1(!Attrs->paramHasAttr(0, ParamAttr::ByVal),
"Attribute ByVal should not apply to functions!", &F);
@ -368,6 +369,8 @@ void Verifier::visitFunction(Function &F) {
"Attribute SRet should not apply to functions!", &F);
Assert1(!Attrs->paramHasAttr(0, ParamAttr::InReg),
"Attribute InReg should not apply to functions!", &F);
Assert1(!Attrs->paramHasAttr(0, ParamAttr::Nest),
"Attribute Nest should not apply to functions!", &F);
for (FunctionType::param_iterator I = FT->param_begin(),
E = FT->param_end(); I != E; ++I, ++Idx) {
@ -391,6 +394,20 @@ void Verifier::visitFunction(Function &F) {
"Attribute ByVal should only apply to pointer to structs!", &F);
}
if (Attrs->paramHasAttr(Idx, ParamAttr::Nest)) {
Assert1(!SawNest, "More than one parameter has attribute Nest!", &F);
SawNest = true;
Assert1(isa<PointerType>(FT->getParamType(Idx-1)),
"Attribute Nest should only apply to Pointer type!", &F);
Assert1(!Attrs->paramHasAttr(Idx, ParamAttr::ByVal),
"Attributes Nest and ByVal are incompatible!", &F);
Assert1(!Attrs->paramHasAttr(Idx, ParamAttr::InReg),
"Attributes Nest and InReg are incompatible!", &F);
Assert1(!Attrs->paramHasAttr(Idx, ParamAttr::StructRet),
"Attributes Nest and StructRet are incompatible!", &F);
}
Assert1(!Attrs->paramHasAttr(Idx, ParamAttr::NoReturn),
"Attribute NoReturn should only be applied to function", &F);
Assert1(!Attrs->paramHasAttr(Idx, ParamAttr::NoUnwind),