From 2343e3b228c02896f4779962a91aaa659356fe2a Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 31 Oct 2013 17:18:24 +0000 Subject: [PATCH] Lower stackmap intrinsics directly to their target opcode in the DAG builder. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@193769 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Target/Target.td | 13 ++ include/llvm/Target/TargetLowering.h | 2 + include/llvm/Target/TargetOpcodes.h | 14 +- .../SelectionDAG/SelectionDAGBuilder.cpp | 207 +++++++++++++++++- .../SelectionDAG/SelectionDAGBuilder.h | 7 + lib/CodeGen/SelectionDAG/TargetLowering.cpp | 13 ++ utils/TableGen/CodeGenTarget.cpp | 2 + 7 files changed, 246 insertions(+), 12 deletions(-) diff --git a/include/llvm/Target/Target.td b/include/llvm/Target/Target.td index c15a4abf383..0c0b1edfe9b 100644 --- a/include/llvm/Target/Target.td +++ b/include/llvm/Target/Target.td @@ -800,6 +800,19 @@ def LIFETIME_END : Instruction { let AsmString = "LIFETIME_END"; let neverHasSideEffects = 1; } +def STACKMAP : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins i32imm:$id, i32imm:$nbytes, variable_ops); + let isCall = 1; + let mayLoad = 1; +} +def PATCHPOINT : Instruction { + let OutOperandList = (outs); + let InOperandList = (ins i32imm:$id, i32imm:$nbytes, unknown:$callee, + i32imm:$nargs, variable_ops); + let isCall = 1; + let mayLoad = 1; +} } //===----------------------------------------------------------------------===// diff --git a/include/llvm/Target/TargetLowering.h b/include/llvm/Target/TargetLowering.h index 01b579b2233..43052e1c5b1 100644 --- a/include/llvm/Target/TargetLowering.h +++ b/include/llvm/Target/TargetLowering.h @@ -1920,6 +1920,8 @@ public: ArgListEntry() : isSExt(false), isZExt(false), isInReg(false), isSRet(false), isNest(false), isByVal(false), isReturned(false), Alignment(0) { } + + void setAttributes(ImmutableCallSite *CS, unsigned AttrIdx); }; typedef std::vector ArgListTy; diff --git a/include/llvm/Target/TargetOpcodes.h b/include/llvm/Target/TargetOpcodes.h index 86ac7dfb427..bd74cb9c0f4 100644 --- a/include/llvm/Target/TargetOpcodes.h +++ b/include/llvm/Target/TargetOpcodes.h @@ -92,7 +92,19 @@ namespace TargetOpcode { /// Lifetime markers. LIFETIME_START = 15, - LIFETIME_END = 16 + LIFETIME_END = 16, + + /// A Stackmap instruction captures the location of live variables at its + /// position in the instruction stream. It is followed by a shadow of bytes + /// that must lie within the function and not contain another stackmap. + STACKMAP = 17, + + /// Patchable call instruction - this instruction represents a call to a + /// constant address, followed by a series of NOPs. It is intended to + /// support optimizations for dynamic languages (such as javascript) that + /// rewrite calls to runtimes with more efficient code sequences. + /// This also implies a stack map. + PATCHPOINT = 18 }; } // end namespace TargetOpcode } // end namespace llvm diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 88572b50535..69c82d4f0fb 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5309,6 +5309,15 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { case Intrinsic::donothing: // ignore return 0; + case Intrinsic::experimental_stackmap: { + visitStackmap(I); + return 0; + } + case Intrinsic::experimental_patchpoint_void: + case Intrinsic::experimental_patchpoint_i64: { + visitPatchpoint(I); + return 0; + } } } @@ -5373,15 +5382,8 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee, SDValue ArgNode = getValue(V); Entry.Node = ArgNode; Entry.Ty = V->getType(); - unsigned attrInd = i - CS.arg_begin() + 1; - Entry.isSExt = CS.paramHasAttr(attrInd, Attribute::SExt); - Entry.isZExt = CS.paramHasAttr(attrInd, Attribute::ZExt); - Entry.isInReg = CS.paramHasAttr(attrInd, Attribute::InReg); - Entry.isSRet = CS.paramHasAttr(attrInd, Attribute::StructRet); - Entry.isNest = CS.paramHasAttr(attrInd, Attribute::Nest); - Entry.isByVal = CS.paramHasAttr(attrInd, Attribute::ByVal); - Entry.isReturned = CS.paramHasAttr(attrInd, Attribute::Returned); - Entry.Alignment = CS.getParamAlignment(attrInd); + // Skip the first return-type Attribute to get to params. + Entry.setAttributes(&CS, i - CS.arg_begin() + 1); Args.push_back(Entry); } @@ -5463,8 +5465,8 @@ void SelectionDAGBuilder::LowerCallTo(ImmutableCallSite CS, SDValue Callee, } if (!Result.second.getNode()) { - // As a special case, a null chain means that a tail call has been emitted and - // the DAG root is already updated. + // As a special case, a null chain means that a tail call has been emitted + // and the DAG root is already updated. HasTailCall = true; // Since there's no actual continuation from this block, nothing can be @@ -6721,6 +6723,189 @@ void SelectionDAGBuilder::visitVACopy(const CallInst &I) { DAG.getSrcValue(I.getArgOperand(1)))); } +/// \brief Lower an argument list according to the target calling convention. +/// +/// \return A tuple of +/// +/// This is a helper for lowering intrinsics that follow a target calling +/// convention or require stack pointer adjustment. Only a subset of the +/// intrinsic's operands need to participate in the calling convention. +std::pair +SelectionDAGBuilder::LowerCallOperands(const CallInst &CI, unsigned ArgIdx, + unsigned NumArgs, SDValue Callee) { + TargetLowering::ArgListTy Args; + Args.reserve(NumArgs); + + // Populate the argument list. + // Attributes for args start at offset 1, after the return attribute. + ImmutableCallSite CS(&CI); + for (unsigned ArgI = ArgIdx, ArgE = ArgIdx + NumArgs, AttrI = ArgIdx + 1; + ArgI != ArgE; ++ArgI) { + const Value *V = CI.getOperand(ArgI); + + assert(!V->getType()->isEmptyTy() && "Empty type passed to intrinsic."); + + TargetLowering::ArgListEntry Entry; + Entry.Node = getValue(V); + Entry.Ty = V->getType(); + Entry.setAttributes(&CS, AttrI); + Args.push_back(Entry); + } + + TargetLowering::CallLoweringInfo CLI(getRoot(), CI.getType(), + /*retSExt*/ false, /*retZExt*/ false, /*isVarArg*/ false, /*isInReg*/ false, + NumArgs, CI.getCallingConv(), /*isTailCall*/ false, /*doesNotReturn*/ false, + /*isReturnValueUsed*/ CI.use_empty(), Callee, Args, DAG, getCurSDLoc()); + + const TargetLowering *TLI = TM.getTargetLowering(); + return TLI->LowerCallTo(CLI); +} + +/// \brief Lower llvm.experimental.stackmap directly to its target opcode. +void SelectionDAGBuilder::visitStackmap(const CallInst &CI) { + // void @llvm.experimental.stackmap(i32 , i32 , + // [live variables...]) + + assert(CI.getType()->isVoidTy() && "Stackmap cannot return a value."); + + SDValue Callee = getValue(CI.getCalledValue()); + + // Lower into a call sequence with no args and no return value. + std::pair Result = LowerCallOperands(CI, 0, 0, Callee); + // Set the root to the target-lowered call chain. + SDValue Chain = Result.second; + DAG.setRoot(Chain); + + /// Get a call instruction from the call sequence chain. + /// Tail calls are not allowed. + SDNode *CallEnd = Chain.getNode(); + assert(CallEnd->getOpcode() == ISD::CALLSEQ_END && + "Expected a callseq node."); + SDNode *Call = CallEnd->getOperand(0).getNode(); + bool hasGlue = Call->getGluedNode(); + + assert(Call->getNumOperands() == hasGlue ? 2 : 1 && + "Unexpected extra stackmap call arguments."); + + // Replace the target specific call node with the stackmap intrinsic. + SmallVector Ops; + + // Add the and constants. + for (unsigned i = 0; i < 2; ++i) { + SDValue tmp = getValue(CI.getOperand(i)); + Ops.push_back(DAG.getTargetConstant( + cast(tmp)->getZExtValue(), MVT::i32)); + } + // Push live variables for the stack map. + for (unsigned i = 2, e = CI.getNumArgOperands(); i != e; ++i) + Ops.push_back(getValue(CI.getArgOperand(i))); + + // Push the chain (this is originally the first operand of the call, but + // becomes now the last or second to last operand). + Ops.push_back(*(Call->op_begin())); + + // Push the glue flag (last operand). + if (hasGlue) + Ops.push_back(*(Call->op_end()-1)); + + // Replace the target specific call node with STACKMAP in-place. This way we + // don't have to call ReplaceAllUsesWith and STACKMAP will take the call's + // place in the chain. + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + DAG.SelectNodeTo(Call, TargetOpcode::STACKMAP, NodeTys, &Ops[0], Ops.size()); +} + +/// \brief Lower llvm.experimental.patchpoint directly to its target opcode. +void SelectionDAGBuilder::visitPatchpoint(const CallInst &CI) { + // void|i64 @llvm.experimental.patchpoint.void|i64(i32 , + // i32 , + // i8* , i32 , + // [Args...], [live variables...]) + + SDValue Callee = getValue(CI.getOperand(2)); // + + // Get the real number of arguments participating in the call + unsigned NumArgs = + cast(getValue(CI.getArgOperand(3)))->getZExtValue(); + + // Skip the four meta args: , , , + assert(CI.getNumArgOperands() >= NumArgs + 4 && + "Not enough arguments provided to the patchpoint intrinsic"); + + std::pair Result = + LowerCallOperands(CI, 4, NumArgs, Callee); + // Set the root to the target-lowered call chain. + SDValue Chain = Result.second; + DAG.setRoot(Chain); + + SDNode *CallEnd = Chain.getNode(); + if (!CI.getType()->isVoidTy()) { + setValue(&CI, Result.first); + if (CallEnd->getOpcode() == ISD::CopyFromReg) + CallEnd = CallEnd->getOperand(0).getNode(); + } + /// Get a call instruction from the call sequence chain. + /// Tail calls are not allowed. + assert(CallEnd->getOpcode() == ISD::CALLSEQ_END && + "Expected a callseq node."); + SDNode *Call = CallEnd->getOperand(0).getNode(); + bool hasGlue = Call->getGluedNode(); + + // Replace the target specific call node with the patchable intrinsic. + SmallVector Ops; + + // Add the and constants. + for (unsigned i = 0; i < 2; ++i) { + SDValue tmp = getValue(CI.getOperand(i)); + Ops.push_back(DAG.getTargetConstant( + cast(tmp)->getZExtValue(), MVT::i32)); + } + // Assume that the Callee is a constant address. + Ops.push_back( + DAG.getIntPtrConstant(cast(Callee)->getZExtValue())); + + // Adjust to account for any stack arguments. + // Call Node: Chain, Target, {Args}, RegMask, [Glue] + unsigned NumCallArgs = Call->getNumOperands() - (hasGlue ? 4 : 3); + Ops.push_back(DAG.getTargetConstant(NumCallArgs, MVT::i32)); + + // Push the arguments from the call instruction. + SDNode::op_iterator e = hasGlue ? Call->op_end()-2 : Call->op_end()-1; + for (SDNode::op_iterator i = Call->op_begin()+2; i != e; ++i) + Ops.push_back(*i); + + // Push live variables for the stack map. + for (unsigned i = NumArgs + 4, e = CI.getNumArgOperands(); i != e; ++i) { + SDValue OpVal = getValue(CI.getArgOperand(i)); + if (ConstantSDNode *C = dyn_cast(OpVal)) { + Ops.push_back( + DAG.getTargetConstant(C->getSExtValue(), MVT::i64)); + } else + Ops.push_back(OpVal); + } + + // Push the register mask info. + if (hasGlue) + Ops.push_back(*(Call->op_end()-2)); + else + Ops.push_back(*(Call->op_end()-1)); + + // Push the chain (this is originally the first operand of the call, but + // becomes now the last or second to last operand). + Ops.push_back(*(Call->op_begin())); + + // Push the glue flag (last operand). + if (hasGlue) + Ops.push_back(*(Call->op_end()-1)); + + // Replace the target specific call node with PATCHPOINT in-place. This + // way we don't have to call ReplaceAllUsesWith and PATCHPOINT will + // take the call's place in the chain. + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + DAG.SelectNodeTo(Call, TargetOpcode::PATCHPOINT, NodeTys, &Ops[0], + Ops.size()); +} + /// TargetLowering::LowerCallTo - This is the default LowerCallTo /// implementation, which just calls LowerCall. /// FIXME: When all targets are diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h index 17a735bace7..6ecf5a0abbb 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -619,6 +619,11 @@ public: void LowerCallTo(ImmutableCallSite CS, SDValue Callee, bool IsTailCall, MachineBasicBlock *LandingPad = NULL); + std::pair LowerCallOperands(const CallInst &CI, + unsigned ArgIdx, + unsigned NumArgs, + SDValue Callee); + /// UpdateSplitBlock - When an MBB was split during scheduling, update the /// references that ned to refer to the last resulting block. void UpdateSplitBlock(MachineBasicBlock *First, MachineBasicBlock *Last); @@ -752,6 +757,8 @@ private: void visitVAArg(const VAArgInst &I); void visitVAEnd(const CallInst &I); void visitVACopy(const CallInst &I); + void visitStackmap(const CallInst &I); + void visitPatchpoint(const CallInst &I); void visitUserOp1(const Instruction &I) { llvm_unreachable("UserOp1 should not exist at instruction selection time!"); diff --git a/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 4d30e7b50c7..82b068d25c8 100644 --- a/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -64,6 +64,19 @@ bool TargetLowering::isInTailCallPosition(SelectionDAG &DAG, SDNode *Node, return isUsedByReturnOnly(Node, Chain); } +/// \brief Set CallLoweringInfo attribute flags based on a call instruction +/// and called function attributes. +void TargetLowering::ArgListEntry::setAttributes(ImmutableCallSite *CS, + unsigned AttrIdx) { + isSExt = CS->paramHasAttr(AttrIdx, Attribute::SExt); + isZExt = CS->paramHasAttr(AttrIdx, Attribute::ZExt); + isInReg = CS->paramHasAttr(AttrIdx, Attribute::InReg); + isSRet = CS->paramHasAttr(AttrIdx, Attribute::StructRet); + isNest = CS->paramHasAttr(AttrIdx, Attribute::Nest); + isByVal = CS->paramHasAttr(AttrIdx, Attribute::ByVal); + isReturned = CS->paramHasAttr(AttrIdx, Attribute::Returned); + Alignment = CS->getParamAlignment(AttrIdx); +} /// Generate a libcall taking the given operands as arguments and returning a /// result of type RetVT. diff --git a/utils/TableGen/CodeGenTarget.cpp b/utils/TableGen/CodeGenTarget.cpp index cd0b8c7b9e1..dd170595582 100644 --- a/utils/TableGen/CodeGenTarget.cpp +++ b/utils/TableGen/CodeGenTarget.cpp @@ -321,6 +321,8 @@ void CodeGenTarget::ComputeInstrsByEnum() const { "BUNDLE", "LIFETIME_START", "LIFETIME_END", + "STACKMAP", + "PATCHPOINT", 0 }; const DenseMap &Insts = getInstructions();