зеркало из https://github.com/dotnet/llilc.git
Merge pull request #356 from AndyAyersMS/ConditionalDeref
Implement conditionalDerefAddress.
This commit is contained in:
Коммит
2a514caffc
|
@ -753,9 +753,7 @@ public:
|
|||
IRNode *derefAddress(IRNode *Address, bool DstIsGCPtr, bool IsConst,
|
||||
bool AddressMayBeNull = true) override;
|
||||
|
||||
IRNode *conditionalDerefAddress(IRNode *Address) override {
|
||||
throw NotYetImplementedException("conditionalDerefAddress");
|
||||
};
|
||||
IRNode *conditionalDerefAddress(IRNode *Address) override;
|
||||
|
||||
IRNode *getHelperCallAddress(CorInfoHelpFunc HelperId) override;
|
||||
|
||||
|
@ -927,6 +925,65 @@ private:
|
|||
void classifyCmpType(llvm::Type *Ty, uint32_t &Size, bool &IsPointer,
|
||||
bool &IsFloat);
|
||||
|
||||
/// \brief Create a basic block for use when expanding a single MSIL
|
||||
/// instruction.
|
||||
///
|
||||
/// Use this method to create a block when expanding a single MSIL
|
||||
/// instruction into an instruction sequence with control flow. Give the
|
||||
/// block the current MSIL offset at both begin and end since it represents
|
||||
/// code at a single point in the IL stream. Mark the block as not
|
||||
/// contributing an operand stack to any subsequent join.
|
||||
///
|
||||
/// \param BlockName Optional name for the new block.
|
||||
/// \returns Newly allocated block.
|
||||
llvm::BasicBlock *createPointBlock(const llvm::Twine &BlockName = "");
|
||||
|
||||
/// \brief Insert a conditional branch to a point block.
|
||||
///
|
||||
/// Split the current block after the current instruction, creating a
|
||||
/// continuation block. Insert a conditional branch to \p PointBlock if
|
||||
/// \p Condition is true. Insert an unconditional branch into \p PointBlock
|
||||
/// to the contiuation if \p Rejoin is true. Leave the insertion point in
|
||||
/// the continuation at the same logical point it was before the split.
|
||||
///
|
||||
/// \param Condition Predicate condition to use in the branch.
|
||||
/// \param PointBlock Block to branch to when \p Condition is true. If
|
||||
/// \p Rejoin is true, \p PointBlock must not have a
|
||||
/// terminator, and this method will add one. If \p
|
||||
/// Rejoin is false, \p PointBlock is left unmodified.
|
||||
/// \param Rejoin If true, insert a branch into \p PointBlock back
|
||||
/// to the continuation.
|
||||
///
|
||||
/// \returns The continuation block. Insertion point is left
|
||||
/// within this block at the instruction after the
|
||||
/// original split point.
|
||||
llvm::BasicBlock *insertConditionalPointBlock(llvm::Value *Condition,
|
||||
llvm::BasicBlock *PointBlock,
|
||||
bool Rejoin);
|
||||
|
||||
/// \brief Insert a PHI to merge two values
|
||||
///
|
||||
/// Create a \p PHINode in \p JoinBlock to merge \p Arg1 and \p Arg2.
|
||||
/// Insert the new PHINode after any existing PHINodes in the block.
|
||||
/// Return the new PHINode. Insertion point is unaffected.
|
||||
///
|
||||
/// \param JoinBlock Block to insert the PHI node.
|
||||
/// \param Arg1 First value to join.
|
||||
/// \param Block1 Predecessor block for \p JoinBlock that
|
||||
/// brings in \p Arg1.
|
||||
/// \param Arg2 Second value to join.
|
||||
/// \param Block2 Predecessor block for \p JoinBlock that
|
||||
/// brings in \p Arg2.
|
||||
/// \param NameStr Optional name for the resulting PHI.
|
||||
///
|
||||
/// \returns The PHINode that was inserted.
|
||||
llvm::PHINode *mergeConditionalResults(llvm::BasicBlock *JoinBlock,
|
||||
llvm::Value *Arg1,
|
||||
llvm::BasicBlock *Block1,
|
||||
llvm::Value *Arg2,
|
||||
llvm::BasicBlock *Block2,
|
||||
const llvm::Twine &NameStr = "");
|
||||
|
||||
/// Generate a call to the helper if the condition is met.
|
||||
///
|
||||
/// \param Condition Condition that will trigger the call.
|
||||
|
|
|
@ -3256,11 +3256,11 @@ IRNode *GenIR::callRuntimeHandleHelper(CorInfoHelpFunc Helper, IRNode *Arg1,
|
|||
// x = callhelper(Arg1, Arg2);
|
||||
// }
|
||||
// return x;
|
||||
BasicBlock *CallBlock = HelperCall->getParent();
|
||||
BasicBlock *CurrentBlock = LLVMBuilder->GetInsertBlock();
|
||||
PHINode *PHI = createPHINode(CurrentBlock, ReturnType, 2, "RuntimeHandle");
|
||||
PHI->addIncoming(NullCheckArg, SaveBlock);
|
||||
PHI->addIncoming(HelperCall, CallBlock);
|
||||
BasicBlock *CallBlock = HelperCall->getParent();
|
||||
PHINode *PHI =
|
||||
mergeConditionalResults(CurrentBlock, NullCheckArg, SaveBlock, HelperCall,
|
||||
CallBlock, "RuntimeHandle");
|
||||
return (IRNode *)PHI;
|
||||
};
|
||||
|
||||
|
@ -4138,67 +4138,22 @@ CallInst *GenIR::genConditionalHelperCall(Value *Condition,
|
|||
Type *ReturnType, IRNode *Arg1,
|
||||
IRNode *Arg2, bool CallReturns,
|
||||
const Twine &CallBlockName) {
|
||||
BasicBlock *CheckBlock = LLVMBuilder->GetInsertBlock();
|
||||
BasicBlock::iterator InsertPoint = LLVMBuilder->GetInsertPoint();
|
||||
Instruction *NextInstruction =
|
||||
(InsertPoint == CheckBlock->end() ? nullptr : (Instruction *)InsertPoint);
|
||||
|
||||
// Create the call block so we can reference it later.
|
||||
// Note: when using this helper to generate conditional throw
|
||||
// (CallReturns==false) we could generate much smaller IR by reusing the same
|
||||
// throw block for all throws of the same kind, but at the cost of not being
|
||||
// able to tell in a debugger which check caused the exception. The current
|
||||
// strategy is to favor debuggability.
|
||||
// TODO: Find a way to annotate the throw blocks as cold so they get laid out
|
||||
// out-of-line.
|
||||
BasicBlock *CallBlock =
|
||||
BasicBlock::Create(*JitContext->LLVMContext, CallBlockName, Function);
|
||||
|
||||
// Split the block. This creates a goto connecting the blocks that we'll
|
||||
// replace with the conditional branch.
|
||||
// Note that we split at offset NextInstrOffset rather than CurrInstrOffset.
|
||||
// We're already generating the IR for the instr at CurrInstrOffset, and using
|
||||
// NextInstrOffset here ensures that we won't redundantly try to add this
|
||||
// instruction again when processing moves to the new ContinueBlock.
|
||||
BasicBlock *ContinueBlock = ReaderBase::fgSplitBlock(
|
||||
(FlowGraphNode *)CheckBlock, NextInstrOffset, (IRNode *)NextInstruction);
|
||||
TerminatorInst *Goto = CheckBlock->getTerminator();
|
||||
|
||||
// Swap the conditional branch in place of the goto.
|
||||
LLVMBuilder->SetInsertPoint(Goto);
|
||||
BranchInst *Branch =
|
||||
LLVMBuilder->CreateCondBr(Condition, CallBlock, ContinueBlock);
|
||||
Goto->eraseFromParent();
|
||||
|
||||
// Fill in the call block.
|
||||
// Create the call block and fill it in.
|
||||
BasicBlock *CallBlock = createPointBlock(CallBlockName);
|
||||
IRBuilder<>::InsertPoint SavedInsertPoint = LLVMBuilder->saveIP();
|
||||
LLVMBuilder->SetInsertPoint(CallBlock);
|
||||
CallInst *HelperCall =
|
||||
(CallInst *)callHelperImpl(HelperId, ReturnType, Arg1, Arg2);
|
||||
if (CallReturns) {
|
||||
LLVMBuilder->CreateBr(ContinueBlock);
|
||||
} else {
|
||||
if (!CallReturns) {
|
||||
HelperCall->setDoesNotReturn();
|
||||
LLVMBuilder->CreateUnreachable();
|
||||
}
|
||||
LLVMBuilder->restoreIP(SavedInsertPoint);
|
||||
|
||||
// Give the call block equal start and end offsets so subsequent processing
|
||||
// won't try to translate MSIL into it.
|
||||
FlowGraphNode *CallFlowGraphNode = (FlowGraphNode *)CallBlock;
|
||||
fgNodeSetStartMSILOffset(CallFlowGraphNode, CurrInstrOffset);
|
||||
fgNodeSetEndMSILOffset(CallFlowGraphNode, CurrInstrOffset);
|
||||
|
||||
// CallBlock doesn't need an operand stack: it doesn't have any MSIL and
|
||||
// its successor block (if there is one) will get the stack propagated from
|
||||
// the check block.
|
||||
fgNodeSetPropagatesOperandStack(CallFlowGraphNode, false);
|
||||
|
||||
// Move the insert point back to the first instruction in the non-null path.
|
||||
if (NextInstruction == nullptr) {
|
||||
LLVMBuilder->SetInsertPoint(ContinueBlock);
|
||||
} else {
|
||||
LLVMBuilder->SetInsertPoint(ContinueBlock->getFirstInsertionPt());
|
||||
}
|
||||
// Splice it into the flow.
|
||||
insertConditionalPointBlock(Condition, CallBlock, CallReturns);
|
||||
|
||||
// Return the the call.
|
||||
return HelperCall;
|
||||
}
|
||||
|
||||
|
@ -4535,6 +4490,118 @@ IRNode *GenIR::derefAddress(IRNode *Address, bool DstIsGCPtr, bool IsConst,
|
|||
return (IRNode *)Result;
|
||||
}
|
||||
|
||||
// Create an empty block to hold IR for some conditional instructions at a
|
||||
// particular point in the MSIL (conditional LLVM instructions that are part
|
||||
// of the expansion of a single MSIL instruction)
|
||||
BasicBlock *GenIR::createPointBlock(const Twine &BlockName) {
|
||||
BasicBlock *Block =
|
||||
BasicBlock::Create(*JitContext->LLVMContext, BlockName, Function);
|
||||
|
||||
// Give the call block equal start and end offsets so subsequent processing
|
||||
// won't try to translate MSIL into it.
|
||||
FlowGraphNode *CallFlowGraphNode = (FlowGraphNode *)Block;
|
||||
fgNodeSetStartMSILOffset(CallFlowGraphNode, CurrInstrOffset);
|
||||
fgNodeSetEndMSILOffset(CallFlowGraphNode, CurrInstrOffset);
|
||||
|
||||
// Point blocks don't need an operand stack: they don't have any MSIL and
|
||||
// any successor block will get the stack propagated from the other
|
||||
// predecessor.
|
||||
fgNodeSetPropagatesOperandStack(CallFlowGraphNode, false);
|
||||
|
||||
return Block;
|
||||
}
|
||||
|
||||
// Split the current block, inserting a conditional branch to the PointBlock
|
||||
// based on Condition, and branch back from the PointBlock to the continuation
|
||||
// if Rejoin is true. Return the continuation.
|
||||
BasicBlock *GenIR::insertConditionalPointBlock(Value *Condition,
|
||||
BasicBlock *PointBlock,
|
||||
bool Rejoin) {
|
||||
BasicBlock *CurrentBlock = LLVMBuilder->GetInsertBlock();
|
||||
BasicBlock::iterator InsertPoint = LLVMBuilder->GetInsertPoint();
|
||||
Instruction *NextInstruction =
|
||||
(InsertPoint == CurrentBlock->end() ? nullptr
|
||||
: (Instruction *)InsertPoint);
|
||||
|
||||
// Split the current block. This creates a goto connecting the blocks that
|
||||
// we'll replace with the conditional branch.
|
||||
// Note that we split at offset NextInstrOffset rather than CurrInstrOffset.
|
||||
// We're already generating the IR for the instr at CurrInstrOffset, and using
|
||||
// NextInstrOffset here ensures that we won't redundantly try to add this
|
||||
// instruction again when processing moves to the new ContinueBlock.
|
||||
BasicBlock *ContinueBlock =
|
||||
ReaderBase::fgSplitBlock((FlowGraphNode *)CurrentBlock, NextInstrOffset,
|
||||
(IRNode *)NextInstruction);
|
||||
TerminatorInst *Goto = CurrentBlock->getTerminator();
|
||||
LLVMBuilder->SetInsertPoint(Goto);
|
||||
BranchInst *Branch =
|
||||
LLVMBuilder->CreateCondBr(Condition, PointBlock, ContinueBlock);
|
||||
Goto->eraseFromParent();
|
||||
|
||||
if (Rejoin) {
|
||||
ASSERT(PointBlock->getTerminator() == nullptr);
|
||||
LLVMBuilder->SetInsertPoint(PointBlock);
|
||||
LLVMBuilder->CreateBr(ContinueBlock);
|
||||
}
|
||||
|
||||
// Move the insert point to the first instruction in the continuation.
|
||||
if (NextInstruction == nullptr) {
|
||||
LLVMBuilder->SetInsertPoint(ContinueBlock);
|
||||
} else {
|
||||
LLVMBuilder->SetInsertPoint(ContinueBlock->getFirstInsertionPt());
|
||||
}
|
||||
|
||||
return ContinueBlock;
|
||||
}
|
||||
|
||||
// Add a PHI at the start of the JoinBlock to merge the two results.
|
||||
PHINode *GenIR::mergeConditionalResults(BasicBlock *JoinBlock, Value *Arg1,
|
||||
BasicBlock *Block1, Value *Arg2,
|
||||
BasicBlock *Block2,
|
||||
const Twine &NameStr) {
|
||||
PHINode *PHI = createPHINode(JoinBlock, Arg1->getType(), 2, NameStr);
|
||||
PHI->addIncoming(Arg1, Block1);
|
||||
PHI->addIncoming(Arg2, Block2);
|
||||
return PHI;
|
||||
}
|
||||
|
||||
// Handle case of an indirection from CORINFO_RUNTIME_LOOKUP where
|
||||
// testForFixup was true.
|
||||
//
|
||||
// If lowest bit of Address is set, clear it and dereference to obtain the
|
||||
// result. If not, just set the result to Address.
|
||||
IRNode *GenIR::conditionalDerefAddress(IRNode *Address) {
|
||||
// Build up the initial bit test
|
||||
BasicBlock *TestBlock = LLVMBuilder->GetInsertBlock();
|
||||
Type *AddressTy = Address->getType();
|
||||
Type *IntPtrTy = getType(CorInfoType::CORINFO_TYPE_NATIVEINT, nullptr);
|
||||
Value *AddressAsInt = LLVMBuilder->CreatePtrToInt(Address, IntPtrTy);
|
||||
Value *One = loadConstantI(1);
|
||||
Value *TestValue = LLVMBuilder->CreateAnd(AddressAsInt, One);
|
||||
Value *TestPredicate = LLVMBuilder->CreateICmpEQ(TestValue, One);
|
||||
|
||||
// Allocate the indirection block and fill it in.
|
||||
BasicBlock *IndirectionBlock = createPointBlock("CondDerefAddr");
|
||||
IRBuilder<>::InsertPoint SavedInsertPoint = LLVMBuilder->saveIP();
|
||||
LLVMBuilder->SetInsertPoint(IndirectionBlock);
|
||||
Value *Mask = LLVMBuilder->CreateNot(One);
|
||||
Value *ConditionalAddressAsInt = LLVMBuilder->CreateAnd(AddressAsInt, Mask);
|
||||
Value *ConditionalAddress = LLVMBuilder->CreateIntToPtr(
|
||||
ConditionalAddressAsInt, AddressTy->getPointerTo());
|
||||
Value *UpdatedAddress = LLVMBuilder->CreateLoad(ConditionalAddress);
|
||||
LLVMBuilder->restoreIP(SavedInsertPoint);
|
||||
|
||||
// Splice the indirection block in.
|
||||
BasicBlock *ContinueBlock =
|
||||
insertConditionalPointBlock(TestPredicate, IndirectionBlock, true);
|
||||
|
||||
// Merge the two addresses and return the result.
|
||||
PHINode *Result = mergeConditionalResults(ContinueBlock, Address, TestBlock,
|
||||
UpdatedAddress, ContinueBlock);
|
||||
|
||||
return (IRNode *)Result;
|
||||
}
|
||||
|
||||
IRNode *GenIR::loadVirtFunc(IRNode *Arg1, CORINFO_RESOLVED_TOKEN *ResolvedToken,
|
||||
CORINFO_CALL_INFO *CallInfo) {
|
||||
IRNode *TypeToken = genericTokenToNode(ResolvedToken, true);
|
||||
|
@ -4549,6 +4616,7 @@ IRNode *GenIR::loadVirtFunc(IRNode *Arg1, CORINFO_RESOLVED_TOKEN *ResolvedToken,
|
|||
return (IRNode *)LLVMBuilder->CreateIntToPtr(
|
||||
CodeAddress, getUnmanagedPointerType(FunctionType));
|
||||
}
|
||||
|
||||
IRNode *GenIR::getPrimitiveAddress(IRNode *Addr, CorInfoType CorInfoType,
|
||||
ReaderAlignType Alignment, uint32_t *Align) {
|
||||
ASSERTNR(isPrimitiveType(CorInfoType) || CorInfoType == CORINFO_TYPE_REFANY);
|
||||
|
|
Загрузка…
Ссылка в новой задаче