Index: lib/Target/X86/X86WinEHState.cpp =================================================================== --- lib/Target/X86/X86WinEHState.cpp +++ lib/Target/X86/X86WinEHState.cpp @@ -15,14 +15,20 @@ //===----------------------------------------------------------------------===// #include "X86.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/Analysis/CFG.h" #include "llvm/Analysis/EHPersonalities.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/WinEHFuncInfo.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include using namespace llvm; @@ -82,6 +88,8 @@ // Per-function state EHPersonality Personality = EHPersonality::Unknown; Function *PersonalityFn = nullptr; + bool UseStackGuard = false; + int ParentBaseState; /// The stack allocation containing all EH data, including the link in the /// fs:00 chain and the current state. @@ -170,6 +178,7 @@ // Reset per-function state. PersonalityFn = nullptr; Personality = EHPersonality::Unknown; + UseStackGuard = false; return true; } @@ -247,7 +256,6 @@ // Struct type of RegNode. Used for GEPing. Type *RegNodeTy; - StringRef PersonalityName = PersonalityFn->getName(); IRBuilder<> Builder(&F->getEntryBlock(), F->getEntryBlock().begin()); Type *Int8PtrType = Builder.getInt8PtrTy(); if (Personality == EHPersonality::MSVC_CXX) { @@ -259,7 +267,8 @@ Builder.CreateStore(SP, Builder.CreateStructGEP(RegNodeTy, RegNode, 0)); // TryLevel = -1 StateFieldIndex = 2; - insertStateNumberStore(&*Builder.GetInsertPoint(), -1); + ParentBaseState = -1; + insertStateNumberStore(&*Builder.GetInsertPoint(), ParentBaseState); // Handler = __ehhandler$F Function *Trampoline = generateLSDAInEAXThunk(F); Link = Builder.CreateStructGEP(RegNodeTy, RegNode, 1); @@ -267,7 +276,6 @@ } else if (Personality == EHPersonality::MSVC_X86SEH) { // If _except_handler4 is in use, some additional guard checks and prologue // stuff is required. - bool UseStackGuard = (PersonalityName == "_except_handler4"); RegNodeTy = getSEHRegistrationType(); RegNode = Builder.CreateAlloca(RegNodeTy); // SavedESP = llvm.stacksave() @@ -276,7 +284,10 @@ Builder.CreateStore(SP, Builder.CreateStructGEP(RegNodeTy, RegNode, 0)); // TryLevel = -2 / -1 StateFieldIndex = 4; - insertStateNumberStore(&*Builder.GetInsertPoint(), UseStackGuard ? -2 : -1); + StringRef PersonalityName = PersonalityFn->getName(); + UseStackGuard = (PersonalityName == "_except_handler4"); + ParentBaseState = UseStackGuard ? -2 : -1; + insertStateNumberStore(&*Builder.GetInsertPoint(), ParentBaseState); // ScopeTable = llvm.x86.seh.lsda(F) Value *FI8 = Builder.CreateBitCast(F, Int8PtrType); Value *LSDA = Builder.CreateCall( @@ -405,38 +416,153 @@ // Iterate all the instructions and emit state number stores. DenseMap BlockColors = colorEHFunclets(F); - for (BasicBlock &BB : F) { - // Figure out what state we should assign calls in this block. + ReversePostOrderTraversal RPOT(&F); + + // Figure out what state we should assign calls in this block. + auto BaseStateForBB = [&](BasicBlock *BB) { int BaseState = -1; - auto &BBColors = BlockColors[&BB]; + auto &BBColors = BlockColors[BB]; - assert(BBColors.size() == 1 && - "multi-color BB not removed by preparation"); + assert(BBColors.size() == 1 && "multi-color BB not removed by preparation"); BasicBlock *FuncletEntryBB = BBColors.front(); if (auto *FuncletPad = dyn_cast(FuncletEntryBB->getFirstNonPHI())) { - // We do not support nesting funclets within cleanuppads. - if (isa(FuncletPad)) - continue; - auto BaseStateI = FuncInfo.FuncletBaseStateMap.find(FuncletPad); if (BaseStateI != FuncInfo.FuncletBaseStateMap.end()) BaseState = BaseStateI->second; } - for (Instruction &I : BB) { - if (auto *CI = dyn_cast(&I)) { - // Possibly throwing call instructions have no actions to take after - // an unwind. Ensure they are in the -1 state. - if (CI->doesNotThrow()) - continue; - insertStateNumberStore(CI, BaseState); - } else if (auto *II = dyn_cast(&I)) { - // Look up the state number of the landingpad this unwinds to. - assert(FuncInfo.InvokeStateMap.count(II) && "invoke has no state!"); - int State = FuncInfo.InvokeStateMap[II]; - insertStateNumberStore(II, State); - } + return BaseState; + }; + + // Calculate the state a call-site is in. + auto StateForCallSite = [&](CallSite CS) { + if (auto *II = dyn_cast(CS.getInstruction())) { + // Look up the state number of the EH pad this unwinds to. + assert(FuncInfo.InvokeStateMap.count(II) && "invoke has no state!"); + return FuncInfo.InvokeStateMap[II]; + } + // Possibly throwing call instructions have no actions to take after + // an unwind. Ensure they are in the -1 state. + return BaseStateForBB(CS.getParent()); + }; + + // InitialStates yields the state of the first call-site for a BasicBlock. + DenseMap InitialStates; + // FinalStates yields the state of the last call-site for a BasicBlock. + DenseMap FinalStates; + // Worklist used to revisit BasicBlocks with indeterminate + // Initial/Final-States. + std::deque Worklist; + // Fill in InitialStates and FinalStates for BasicBlocks with call-sites. + const int BadState = INT_MIN; + for (BasicBlock *BB : RPOT) { + int InitialState = BadState; + int FinalState; + if (&F.getEntryBlock() == BB) + InitialState = FinalState = ParentBaseState; + for (Instruction &I : *BB) { + CallSite CS(&I); + if (!CS || CS.doesNotThrow()) + continue; + + int State = StateForCallSite(CS); + if (InitialState == BadState) + InitialState = State; + FinalState = State; + } + // No call-sites in this basic block? That's OK, we will come back to these + // in a later pass. + if (InitialState == BadState) { + Worklist.push_back(BB); + continue; + } + DEBUG(dbgs() << "X86WinEHState: " << BB->getName() + << " InitialState=" << InitialState << '\n'); + DEBUG(dbgs() << "X86WinEHState: " << BB->getName() + << " FinalState=" << FinalState << '\n'); + InitialStates.insert({BB, InitialState}); + FinalStates.insert({BB, FinalState}); + } + + // Calculate the intersection of all the FinalStates for a BasicBlock's + // predecessor. + auto CalcPredState = [&](BasicBlock *BB) { + // The entry block has no predecessors but we know that the prologue always + // sets us up with a fixed state. + if (&F.getEntryBlock() == BB) + return ParentBaseState; + + // This is an EH Pad, conservatively give "I don't know" as our answer. + if (BB->isEHPad()) + return BadState; + + int CommonState = BadState; + for (BasicBlock *PredBB : predecessors(BB)) { + // We didn't manage to get a state for one of these predecessors, return + // BadState as a conservative approximation. + auto PredEndState = FinalStates.find(PredBB); + if (PredEndState == FinalStates.end()) + return BadState; + + // This code is reachable via exceptional control flow. Conservatively + // return BadState. + if (isa(PredBB->getTerminator())) + return BadState; + + int PredState = PredEndState->second; + if (CommonState == BadState) + CommonState = PredState; + + // At least two predecessors have different FinalStates, return BadState. + if (CommonState != PredState) + return BadState; + } + + return CommonState; + }; + + // Try to fill-in InitialStates and FinalStates which have no call-sites. + while (!Worklist.empty()) { + BasicBlock *BB = Worklist.front(); + Worklist.pop_front(); + // This BasicBlock has already been figured out, nothing more we can do. + if (InitialStates.count(BB) != 0) + continue; + + int PredState = CalcPredState(BB); + if (PredState == BadState) + continue; + + // We successfully inferred this BasicBlock's state via it's predecessors; + // enqueue it's successors to see if we can infer their states. + InitialStates.insert({BB, PredState}); + FinalStates.insert({BB, PredState}); + for (BasicBlock *SuccBB : successors(BB)) + Worklist.push_back(SuccBB); + } + + // Finally, insert state stores before call-sites which transition us to a new + // state. + for (BasicBlock *BB : RPOT) { + auto &BBColors = BlockColors[BB]; + BasicBlock *FuncletEntryBB = BBColors.front(); + if (isa(FuncletEntryBB->getFirstNonPHI())) + continue; + + int PrevState = CalcPredState(BB); + DEBUG(dbgs() << "X86WinEHState: " << BB->getName() + << " PrevState=" << PrevState << '\n'); + + for (Instruction &I : *BB) { + CallSite CS(&I); + if (!CS || CS.doesNotThrow()) + continue; + + int State = StateForCallSite(CS); + if (State != PrevState) + insertStateNumberStore(&I, State); + PrevState = State; } } } Index: test/CodeGen/WinEH/wineh-statenumbering.ll =================================================================== --- test/CodeGen/WinEH/wineh-statenumbering.ll +++ test/CodeGen/WinEH/wineh-statenumbering.ll @@ -28,7 +28,11 @@ ; CHECK: entry: ; CHECK: store i32 -1 ; CHECK: call void @g(i32 3) + ; CHECK-NEXT: call void @g(i32 4) + ; CHECK-NEXT: call void @g(i32 5) call void @g(i32 3) + call void @g(i32 4) + call void @g(i32 5) store i32 0, i32* %tmp, align 4 %0 = bitcast i32* %tmp to i8* ; CHECK: store i32 0 @@ -54,14 +58,22 @@ ; CHECK: catch.3: ; CHECK: store i32 3 ; CHECK: call void @g(i32 1) + ; CHECK-NEXT: call void @g(i32 2) + ; CHECK-NEXT: call void @g(i32 3) call void @g(i32 1) + call void @g(i32 2) + call void @g(i32 3) catchret from %2 to label %try.cont try.cont: ; preds = %catch.3 ; CHECK: try.cont: ; CHECK: store i32 1 ; CHECK: call void @g(i32 2) + ; CHECK-NEXT: call void @g(i32 3) + ; CHECK-NEXT: call void @g(i32 4) call void @g(i32 2) + call void @g(i32 3) + call void @g(i32 4) unreachable unreachable: ; preds = %catch @@ -111,6 +123,10 @@ ; CHECK: try.cont: ; CHECK: store i32 1 ; CHECK: call void @dtor() + ; CHECK-NEXT: call void @dtor() + ; CHECK-NEXT: call void @dtor() + call void @dtor() #3 [ "funclet"(token %1) ] + call void @dtor() #3 [ "funclet"(token %1) ] call void @dtor() #3 [ "funclet"(token %1) ] catchret from %1 to label %try.cont4