Skip to content
This repository has been archived by the owner on Mar 15, 2022. It is now read-only.

Commit

Permalink
Merge pull request #334 from JosephTremoulet/Continuations
Browse files Browse the repository at this point in the history
Implement (non-exceptional) finally continuation selection
  • Loading branch information
JosephTremoulet committed Mar 21, 2015
2 parents 00f4f8d + 5f6af8f commit 6dbef93
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 197 deletions.
20 changes: 8 additions & 12 deletions Documentation/llilc-jit-eh.md
Original file line number Diff line number Diff line change
Expand Up @@ -498,17 +498,12 @@ function correctly. Thus, in order to unblock progress in other areas of
LLILC during bring-up, initially the EH support will be stubbed out, with
just enough functionality for such test programs to pass. In particular,
code with EH constructs is expected to compile cleanly, but it is only
expected to behave correctly if it does not attempt to raise exceptions or
make nontrivial finally clause invocations at runtime. To avoid silent bad
code generation, any nontrivial finally clause invocations (i.e. invocations
where control upon exit from the finally needs to be transferred anywhere
other than the MSIL immediately following the finally handler) will be
detected and rejected at compile-time. The throw operator and the explicit
test/throw sequences for implicit MSIL exceptions will be implemented on top
of the stub support (with throws using `call` rather than `invoke`), to
reflect correct program semantics and allow compilation of code with
conditional exceptions that will execute correctly if the exception
conditions don't arise at run-time.
expected to behave correctly if it does not attempt to raise exceptions at
runtime. The throw operator and the explicit test/throw sequences for
implicit MSIL exceptions will be implemented on top of the stub support
(with throws using `call` rather than `invoke`), to reflect correct program
semantics and allow compilation of code with conditional exceptions that
will execute correctly if the exception conditions don't arise at run-time.

Once the stub support (with explicit and implicit exceptions) is in place,
the next steps will be to translate handlers in the reader and generate EH
Expand All @@ -529,8 +524,9 @@ for explicit throws and some but not all implicit exceptions.

In summary, the plan/status is:
1. [ ] Stub EH support
- [x] Reader discards handlers
- [x] Reader discards catch/filter/fault handlers
- [x] Explicit throw becomes helper call
- [x] Continuation passing for finally handlers invoked by `leave`
- [ ] Implicit exceptions expanded to explicit test/throw sequences
- [x] Null dereference
- [ ] Divide by zero
Expand Down
85 changes: 52 additions & 33 deletions include/Reader/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,12 @@ void rgnSetCatchTryRegion(EHRegion *CatchRegion, EHRegion *TryRegion);
mdToken rgnGetCatchClassToken(EHRegion *CatchRegion);
void rgnSetCatchClassToken(EHRegion *CatchRegion, mdToken Token);

/// Get the finally region attached to the given \p TryRegion, if any.
///
/// \param TryRegion Try region to check for finally handler
/// \returns The finally protecting this try if it exists; else nullptr
EHRegion *getFinallyRegion(EHRegion *TryRegion);

// Interface to GenIR defined Flow Graph structures.
// Implementation Supplied by Jit Client
EHRegion *fgNodeGetRegion(FlowGraphNode *FgNode);
Expand All @@ -596,11 +602,6 @@ IRNode *fgNodeGetStartIRNode(FlowGraphNode *FgNode);
// Get the first non-placekeeping node in block
IRNode *fgNodeGetStartInsertIRNode(FlowGraphNode *FgNode);

// Get the last non-placekeeping node in block
IRNode *fgNodeGetEndInsertIRNode(FlowGraphNode *FgNode);

IRNode *fgNodeGetEndIRInsertionPoint(FlowGraphNode *FgNode);

GlobalVerifyData *fgNodeGetGlobalVerifyData(FlowGraphNode *Fg);
void fgNodeSetGlobalVerifyData(FlowGraphNode *Fg, GlobalVerifyData *GvData);

Expand Down Expand Up @@ -855,11 +856,6 @@ class ReaderBase {
// SEQUENCE POINT Info
ReaderBitVector *CustomSequencePoints;

// EH Info
CORINFO_EH_CLAUSE *EhClauseInfo; // raw eh clause info
EHRegion *EhRegionTree;
EHRegionList *AllRegionList;

// Fg Info - unused after fg is built

// NodeOffsetListArray is an ordered array of FlowGraphNodeOffsetList*.
Expand All @@ -878,6 +874,11 @@ class ReaderBase {
protected:
uint32_t CurrentBranchDepth;

// EH Info
CORINFO_EH_CLAUSE *EhClauseInfo; // raw eh clause info
EHRegion *EhRegionTree;
EHRegionList *AllRegionList;

// \brief Indicates that null checks use explicit compare+branch IR sequences
//
// Compiling with this set to false isn't really supported (the generated IR
Expand Down Expand Up @@ -1117,6 +1118,36 @@ class ReaderBase {
void
readBytesForFlowGraphNodeHelper(ReadBytesForFlowGraphNodeHelperParam *Param);

protected:
/// \brief Create or return a flow graph node for the indicated offset
///
/// This method sees if there is an existing flow graph node that begins at
/// the indicated target. If so, \p Node is set to this block. If not, a
/// temporary block is allocated to use as a target, and an entry is added
/// to the \p NodeOffsetListArray so a subsequent pass can update the
/// temporary target blocks to real target blocks.
///
/// \param Node [out] Node to use as the branch target
/// \param TargetOffset MSIL offset of the branch target
/// \returns List node of target in offset list
FlowGraphNodeOffsetList *fgAddNodeMSILOffset(FlowGraphNode **Node,
uint32_t TargetOffset);

/// Get the innermost finally region enclosing the given \p Offset
///
/// \param Offset MSIL offset of interest
/// \returns The innermost finally region enclosing \p Offset if one exists;
/// nullptr otherwise
EHRegion *getInnermostFinallyRegion(uint32_t Offset);

/// Find the next-innermost region enclosing the given \p Offset
///
/// \param OuterRegion Limit search to regions nested inside OuterRegion
/// \param Offset Limit search to regions enclosing Offset
/// \returns The outermost region that is nested inside \p OuterRegion and
/// that includes \p Offset, if such a region exists; else nullptr
EHRegion *getInnerEnclosingRegion(EHRegion *OuterRegion, uint32_t Offset);

private:
/// \brief Perform special processing for blocks that start EH regions.
///
Expand Down Expand Up @@ -1300,10 +1331,11 @@ class ReaderBase {
/// there can be more than one of these in a finally region.
///
/// \param BlockNode the block that is the end of the finally
/// \param CurentOffset msil offset for the endfinally instruction
/// \param IsLexicalEnd true if the endfinally is at the end of the finally
IRNode *fgMakeEndFinallyHelper(IRNode *BlockNode, uint32_t Offset,
bool IsLexicalEnd);
/// \param FinallyRegion the finally region being ended
/// \param Offset msil offset for the endfinally instruction
/// \returns the branch generated to terminate the block for this endfinally
IRNode *fgMakeEndFinallyHelper(IRNode *BlockNode, EHRegion *FinallyRegion,
uint32_t Offset);

/// \brief Remove all unreachable blocks
///
Expand All @@ -1325,20 +1357,6 @@ class ReaderBase {
/// there are large amounts of MSIL for the method.
int32_t *FgGetRegionCanonicalExitOffsetBuff;

/// \brief Create or return a flow graph node for the indicated offset
///
/// This method sees if there is an existing flow graph node that begins at
/// the indicated target. If so, \p Node is set to this block. If not, a
/// temporary block is allocated to use as a target, and an entry is added
/// to the \p NodeOffsetListArray so a subsequent pass can update the
/// temporary target blocks to real target blocks.
///
/// \param Node [out] Node to use as the branch target
/// \param TargetOffset MSIL offset of the branch target
/// \returns List node of target in offset list
FlowGraphNodeOffsetList *fgAddNodeMSILOffset(FlowGraphNode **Node,
uint32_t TargetOffset);

/// Determine if a leave exits the enclosing EH region in a non-local manner.
///
/// \param Fg flow graph node containing the leave
Expand Down Expand Up @@ -2116,6 +2134,9 @@ class ReaderBase {
virtual void jmp(ReaderBaseNS::CallOpcode Opcode, mdToken Token, bool HasThis,
bool HasVarArg) = 0;

virtual uint32_t updateLeaveOffset(uint32_t LeaveOffset, uint32_t NextOffset,
FlowGraphNode *LeaveBlock,
uint32_t TargetOffset) = 0;
virtual void leave(uint32_t TargetOffset, bool IsNonLocal,
bool EndsWithNonLocalGoto) = 0;
virtual IRNode *loadArg(uint32_t ArgOrdinal, bool IsJmp) = 0;
Expand Down Expand Up @@ -2380,6 +2401,7 @@ class ReaderBase {
virtual void fgDeleteBlock(FlowGraphNode *Block) = 0;
virtual void fgDeleteEdge(FlowGraphEdgeList *Arc) = 0;
virtual void fgDeleteNodesFromBlock(FlowGraphNode *Block) = 0;
virtual IRNode *fgNodeGetEndInsertIRNode(FlowGraphNode *FgNode) = 0;

// Returns true iff client considers the JMP recursive and wants a
// loop back-edge rather than a forward edge to the exit label.
Expand All @@ -2400,8 +2422,8 @@ class ReaderBase {
virtual IRNode *fgMakeBranch(IRNode *LabelNode, IRNode *BlockNode,
uint32_t CurrentOffset, bool IsConditional,
bool IsNominal) = 0;
virtual IRNode *fgMakeEndFinally(IRNode *BlockNode, uint32_t CurrentOffset,
bool IsLexicalEnd) = 0;
virtual IRNode *fgMakeEndFinally(IRNode *BlockNode, EHRegion *FinallyRegion,
uint32_t CurrentOffset) = 0;

// turns an unconditional branch to the entry label into a fall-through
// or a branch to the exit label, depending on whether it was a recursive
Expand Down Expand Up @@ -2562,9 +2584,6 @@ class ReaderBase {
char DummyLastBaseField;
// Fields after this one will not be initialized in the constructor.
///////////////////////////////////////////////////////////////////////

// Deferred NYI map for Leave instructions (temporary)
std::map<uint32_t, const char *> NyiLeaveMap;
};

/// \brief The exception that is thrown when a particular operation is not yet
Expand Down
26 changes: 23 additions & 3 deletions include/Reader/readerir.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,12 @@ class GenIR : public ReaderBase {
throw NotYetImplementedException("jmp");
};

virtual uint32_t updateLeaveOffset(uint32_t LeaveOffset, uint32_t NextOffset,
FlowGraphNode *LeaveBlock,
uint32_t TargetOffset) override;
uint32_t updateLeaveOffset(EHRegion *Region, uint32_t LeaveOffset,
uint32_t NextOffset, FlowGraphNode *LeaveBlock,
uint32_t TargetOffset, bool &IsInHandler);
void leave(uint32_t TargetOffset, bool IsNonLocal,
bool EndsWithNonLocalGoto) override;
IRNode *loadArg(uint32_t ArgOrdinal, bool IsJmp) override;
Expand Down Expand Up @@ -579,6 +585,7 @@ class GenIR : public ReaderBase {
throw NotYetImplementedException("fgDeleteEdge");
};
void fgDeleteNodesFromBlock(FlowGraphNode *Block) override;
IRNode *fgNodeGetEndInsertIRNode(FlowGraphNode *FgNode) override;

bool commonTailCallChecks(CORINFO_METHOD_HANDLE DeclaredMethod,
CORINFO_METHOD_HANDLE ExactMethod,
Expand Down Expand Up @@ -609,8 +616,8 @@ class GenIR : public ReaderBase {
IRNode *fgMakeBranch(IRNode *LabelNode, IRNode *InsertNode,
uint32_t CurrentOffset, bool IsConditional,
bool IsNominal) override;
IRNode *fgMakeEndFinally(IRNode *InsertNode, uint32_t CurrentOffset,
bool IsLexicalEnd) override;
IRNode *fgMakeEndFinally(IRNode *InsertNode, EHRegion *FinallyRegion,
uint32_t CurrentOffset) override;

// turns an unconditional branch to the entry label into a fall-through
// or a branch to the exit label, depending on whether it was a recursive
Expand Down Expand Up @@ -974,8 +981,10 @@ class GenIR : public ReaderBase {
/// used anywhere within the method.
///
/// \param Ty Type for the new variable.
/// \param Name Optional name for the new variable.
/// \returns Instruction establishing the variable's location.
llvm::Instruction *createTemporary(llvm::Type *Ty);
llvm::Instruction *createTemporary(llvm::Type *Ty,
const llvm::Twine &Name = "");

IRNode *
loadManagedAddress(const std::vector<llvm::Value *> &UnmanagedAddresses,
Expand Down Expand Up @@ -1067,6 +1076,15 @@ class GenIR : public ReaderBase {
private:
LLILCJitContext *JitContext;
llvm::Function *Function;
// The LLVMBuilder has a notion of a current insertion point. During the
// first-pass flow-graph construction, each method sets the insertion point
// explicitly before inserting IR (the fg- methods typically take an
// InsertNode parameter indicating where to set it). During the second pass
// translation of the non-flow instructions, the insertion point is
// explicitly set at the start of each block (in beginFlowGraphNode), and
// translation methods assume that the builder's current insertion point is
// where they should be inserted (the gen- methods do not take explicit
// insertion point parameters).
llvm::IRBuilder<> *LLVMBuilder;
std::map<CORINFO_CLASS_HANDLE, llvm::Type *> *ClassTypeMap;
std::map<std::tuple<CorInfoType, CORINFO_CLASS_HANDLE, uint32_t>,
Expand All @@ -1077,6 +1095,8 @@ class GenIR : public ReaderBase {
std::vector<CorInfoType> LocalVarCorTypes;
std::vector<llvm::Value *> Arguments;
std::vector<CorInfoType> ArgumentCorTypes;
llvm::DenseMap<uint32_t, llvm::StoreInst *> ContinuationStoreMap;
llvm::BasicBlock *UnreachableContinuationBlock;
CorInfoType ReturnCorType;
bool HasThis;
bool HasTypeParameter;
Expand Down
2 changes: 0 additions & 2 deletions lib/Reader/GenIRStubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ IRNode *fgNodeGetStartInsertIRNode(FlowGraphNode *FgNode) {
return fgNodeGetStartIRNode(FgNode);
}

IRNode *fgNodeGetEndIRInsertionPoint(FlowGraphNode *FgNode) { return nullptr; }

GlobalVerifyData *fgNodeGetGlobalVerifyData(FlowGraphNode *Fg) {
throw NotYetImplementedException("fgNodeGetGlobalVerifyData");
}
Expand Down
Loading

0 comments on commit 6dbef93

Please sign in to comment.