Merge pull request #356 from AndyAyersMS/ConditionalDeref

Implement conditionalDerefAddress.
This commit is contained in:
Andy Ayers 2015-03-26 12:06:58 -07:00
Родитель e22eaa1763 94f2fadbf6
Коммит 2a514caffc
2 изменённых файлов: 185 добавлений и 60 удалений

Просмотреть файл

@ -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);