[LLVMdev] Destination register needs to be valid after callee saved register restore when tail calling

Arnold Schwaighofer arnold.schwaighofer at gmail.com
Wed Aug 8 13:54:14 PDT 2007


On 8 Aug 2007, at 21:12, Anton Korobeynikov wrote:

> Hello, Arnold.
>
>> with the sentence i tried to express the question whether there is a
>> way to persuade the code generator to use another register to load  
>> (or
>> move) the function pointer to (right before the callee saved register
>> restore) but thinking a little further that's nonsense.
> Why don't define some special op for callee address and custom  
> lower it?

so what you are saying there is that instead of

X86TargetLowering::LowerCCCCallTo(SDOperand Op, SelectionDAG &DAG,
                                             unsigned CC)
...
  Chain = DAG.getNode(isTailCall ? X86ISD::TAILCALL : X86ISD::CALL,
                       NodeTys, &Ops[0], Ops.size());
...

do something along the lines

X86TargetLowering::LowerCCCCallTo(SDOperand Op, SelectionDAG &DAG,
                                             unsigned CC)
...
if (isTailCall) {
  SDOperand CustomCallee = DAG.getNode(X86ISD:TCLOWERCALLEEADDRESS,  
Op.getOperand(6/* callee*/));
  Ops[6] = CustomCalleed;
  Chain = DAG.getNode( X86ISD::TAILCALL ,
                       NodeTys, &Ops[0], Ops.size());
...

And later lower it such that it performs the move {register holding  
callee} to {legitimate register holding the callee}
Problem here is that it is not known (when lowering   
X86ISD:TCLOWERCALLEEADDRESS )whether the call will really be a tail  
call followed by a return. when i treat it later when
dagcombining the ret_flag instruction i have all that information.  
(see code below)


> I really suggest you to look into eh_return. It's used in some pretty
> tricky situtation inside eh runtime: it it used to return from some eh
> runtime code. We already know, how much we should unwind the stack,  
> and
> what is the handler (sounds similar, right?). Also %eax and %edx are
> used to return eh data and should be preserved in such function.  
> So, in
> general, code for eh_return looks like (intel notation here):
>
>          mov ecx, ebp
>          add ecx, offset
>          mov [ecx], handler
>          .... (just usual epilogue)
>          mov esp, ecx
>          ret
>
> (in real we have slightly different code, because we need to  
> compensate
> stack adjustment due to ebp save in epilogue, but idea is the same)
>
> We cannot just jump to handler because we need to change the %esp  
> here.
> Also, offset and handler are calculated by code of routine, that's why
> such code looks like to be the most painless way to do the things. The
> tail call emission has many similar bits, but everything (both handler
> and offset) is known during codegen time, that's why we can  
> simplify the
> code much.
>

I'll have a look at it.

at the moment i perform the dag transformations in post legalize  
phase of the dagcombiner.
SDOperand X86TargetLowering::PerformDAGCombine(SDNode *N,
                                                DAGCombinerInfo &DCI)  
const {
   SelectionDAG &DAG = DCI.DAG;
   switch (N->getOpcode()) {
   default: break;
   case ISD::VECTOR_SHUFFLE:
     return PerformShuffleCombine(N, DAG, Subtarget);
   case ISD::SELECT:
     return PerformSELECTCombine(N, DAG, Subtarget);
   case X86ISD::RET_FLAG:
     return PerformRETCombine(N, DCI, Subtarget, this);
   }

static SDOperand PerformRETCombine(SDNode * node,
                                    TargetLowering::DAGCombinerInfo  
&DCI,
                                    const X86Subtarget *Subtarget,
                                    const X86TargetLowering * TLI) {

   SDOperand RetNode;
   bool ReturnRegularCall =false;
   do {
     if( !DCI.isBeforeLegalize() && PerformTailCallOpt) {
		// check whether there are any instructions between RET and  
CALLSEQ_END if yes break out
		// look for tail call before CALLSEQ_END
		// Adjust ESP
		// remove instructions after tailcall (the moves from integer/  
float result) RetNode = TAILCALL node
		// here i could insert the before mentioned move {register holding  
callee} to {legitimate register holding the callee}
		// at the moment: hack suggested by dale
		SDOperand TCKillRegOps [] = {AdjStackChain, AdjStackFlag};
	        SDOperand TCKillReg = DCI.DAG.getNode 
(X86ISD::TAILCALL_REG_KILL, DCI.DAG.getVTList(MVT::i32, MVT::Other,  
MVT::Flag), TCKillRegOps, 2);
                if (RetNode.getNumOperands() ==5) {
                   // no registers for argument passing
                  SDOperand OpsTailCall [] = {TCKillReg,  
RetNode.getOperand(1),RetNode.getOperand(2), RetNode.getOperand(3),   
SDOperand(TCKillReg.Val, 1)};
                  RetNode = DCI.DAG.getNode(X86ISD::TAILCALL, TCVTs,  
OpsTailCall, 3);
                 } else if (RetNode.getNumOperands()==4){
                 }...
	        // update argument stores: store relative to framepointer of  
current function (the one that is tailcalling)
		// save RET_ADDR to new location
		// and probably some more stuff
	}
while(0);
return RetNode;

>> the downside here is that ECX is no longer free for passing function
>> arguments. (i am using the x86_fastcall semantics at the moment with
>> first two arguments stored in ecx,edx)
> This is the most problematic stuff here. Some notes:
> 1. You cannot emit a tail call to something returning a struct.
I would have added the code for returning a struct. (additional  
pointer pushed by caller and popped by callee or so my understanding  
is a the moment) but honestly have not look at this part yet.
> 2. It can be tricky to emit a call to function having arguments in
> registers (emitting something target-dependent for register holding
> function pointer seems to be the right way to do the things: you can
> always check, what are livein registers for function and try to emit
> something safe).
>
the implementation i have at the moment only works if caller and  
(tail) callee have the same calling convention. so i don't see/ 
understand where there would be a problem. (if ecx/edx is used for  
param passing always)
but i am not taking exception handling into account. (there would be  
problems if i understand you correctly)

> I'd suggest to start from plain functions with C calling convention.
>
>> and sorry if i am bothering you with questions whose answer should be
>> obvious. i am really a total newbie greenhorn :)
> You're welcome :) Many of such questions are not so easy to answer.
> --
> WBR, Anton Korobeynikov
> _______________________________________________
> LLVM Developers mailing list
> LLVMdev at cs.uiuc.edu         http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev




More information about the llvm-dev mailing list