Implement conditionalDerefAddress.

This method checks the low bit of address, and if set, clears it and dereferences. Otherwise it just returns the unmodified address.

This generates plausible looking code but the method that contains it also has a convertHandle, so the codegen hasn't been tested at runtime yet.

As part of this I refactored the existing conditional expansion code somewhat. No diffs in IR other than changing the failure reason for one method.

Closes #356.
This commit is contained in:
Andy Ayers 2015-03-25 00:30:13 -07:00
Родитель 64641a5412
Коммит 94f2fadbf6
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;
};
@ -3983,67 +3983,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;
}
@ -4380,6 +4335,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);
@ -4394,6 +4461,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);