Skip to content

Commit

Permalink
Reland "[Windows SEH]: HARDWARE EXCEPTION HANDLING (MSVC -EHa) - Part 2"
Browse files Browse the repository at this point in the history
This reverts commit db6a979.

Reland D102817 without any change. The previous revert was a mistake.

Differential Revision: https://reviews.llvm.org/D102817
  • Loading branch information
phoebewang committed Mar 29, 2023
1 parent 4c55fd9 commit 0efe111
Show file tree
Hide file tree
Showing 15 changed files with 1,558 additions and 4 deletions.
3 changes: 3 additions & 0 deletions llvm/include/llvm/CodeGen/SelectionDAGISel.h
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,9 @@ class SelectionDAGISel : public MachineFunctionPass {
/// instruction selected, false if no code should be emitted for it.
bool PrepareEHLandingPad();

// Mark and Report IPToState for each Block under AsynchEH
void reportIPToStateForBlocks(MachineFunction *Fn);

/// Perform instruction selection on all basic blocks in the function.
void SelectAllBasicBlocks(const Function &Fn);

Expand Down
9 changes: 9 additions & 0 deletions llvm/include/llvm/CodeGen/WinEHFuncInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct WinEHFuncInfo {
DenseMap<const FuncletPadInst *, int> FuncletBaseStateMap;
DenseMap<const InvokeInst *, int> InvokeStateMap;
DenseMap<MCSymbol *, std::pair<int, MCSymbol *>> LabelToStateMap;
DenseMap<const BasicBlock *, int> BlockToStateMap; // for AsynchEH
SmallVector<CxxUnwindMapEntry, 4> CxxUnwindMap;
SmallVector<WinEHTryBlockMapEntry, 4> TryBlockMap;
SmallVector<SEHUnwindMapEntry, 4> SEHUnwindMap;
Expand All @@ -104,6 +105,8 @@ struct WinEHFuncInfo {
void addIPToStateRange(const InvokeInst *II, MCSymbol *InvokeBegin,
MCSymbol *InvokeEnd);

void addIPToStateRange(int State, MCSymbol *InvokeBegin, MCSymbol *InvokeEnd);

int EHRegNodeFrameIndex = std::numeric_limits<int>::max();
int EHRegNodeEndOffset = std::numeric_limits<int>::max();
int EHGuardFrameIndex = std::numeric_limits<int>::max();
Expand All @@ -123,6 +126,12 @@ void calculateSEHStateNumbers(const Function *ParentFn,

void calculateClrEHStateNumbers(const Function *Fn, WinEHFuncInfo &FuncInfo);

// For AsynchEH (VC++ option -EHa)
void calculateCXXStateForAsynchEH(const BasicBlock *BB, int State,
WinEHFuncInfo &FuncInfo);
void calculateSEHStateForAsynchEH(const BasicBlock *BB, int State,
WinEHFuncInfo &FuncInfo);

} // end namespace llvm

#endif // LLVM_CODEGEN_WINEHFUNCINFO_H
9 changes: 9 additions & 0 deletions llvm/include/llvm/IR/BasicBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,15 @@ class BasicBlock final : public Value, // Basic blocks are data objects also
.getNonConst();
}

/// Returns the first potential AsynchEH faulty instruction
/// currently it checks for loads/stores (which may dereference a null
/// pointer) and calls/invokes (which may propagate exceptions)
const Instruction* getFirstMayFaultInst() const;
Instruction* getFirstMayFaultInst() {
return const_cast<Instruction*>(
static_cast<const BasicBlock*>(this)->getFirstMayFaultInst());
}

/// Return a const iterator range over the instructions in the block, skipping
/// any debug instructions. Skip any pseudo operations as well if \c
/// SkipPseudoOp is true.
Expand Down
18 changes: 17 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1627,6 +1627,7 @@ void AsmPrinter::emitFunctionBody() {
// Print out code for the function.
bool HasAnyRealCode = false;
int NumInstsInFunction = 0;
bool IsEHa = MMI->getModule()->getModuleFlag("eh-asynch");

bool CanDoExtraAnalysis = ORE->allowExtraAnalysis(DEBUG_TYPE);
for (auto &MBB : *MF) {
Expand Down Expand Up @@ -1665,10 +1666,25 @@ void AsmPrinter::emitFunctionBody() {
emitFrameAlloc(MI);
break;
case TargetOpcode::ANNOTATION_LABEL:
case TargetOpcode::EH_LABEL:
case TargetOpcode::GC_LABEL:
OutStreamer->emitLabel(MI.getOperand(0).getMCSymbol());
break;
case TargetOpcode::EH_LABEL:
OutStreamer->emitLabel(MI.getOperand(0).getMCSymbol());
// For AsynchEH, insert a Nop if followed by a trap inst
// Or the exception won't be caught.
// (see MCConstantExpr::create(1,..) in WinException.cpp)
// Ignore SDiv/UDiv because a DIV with Const-0 divisor
// must have being turned into an UndefValue.
// Div with variable opnds won't be the first instruction in
// an EH region as it must be led by at least a Load
{
auto MI2 = std::next(MI.getIterator());
if (IsEHa && MI2 != MBB.end() &&
(MI2->mayLoadOrStore() || MI2->mayRaiseFPException()))
emitNops(1);
}
break;
case TargetOpcode::INLINEASM:
case TargetOpcode::INLINEASM_BR:
emitInlineAsm(&MI);
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/WinException.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,11 @@ void WinException::emitCXXFrameHandler3Table(const MachineFunction *MF) {
OS.emitInt32(0);

AddComment("EHFlags");
OS.emitInt32(1);
if (MMI->getModule()->getModuleFlag("eh-asynch")) {
OS.emitInt32(0);
} else {
OS.emitInt32(1);
}

// UnwindMapEntry {
// int32_t ToState;
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/CodeGen/BranchFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,7 @@ bool BranchFolder::OptimizeBranches(MachineFunction &MF) {
MadeChange |= OptimizeBlock(&MBB);

// If it is dead, remove it.
if (MBB.pred_empty()) {
if (MBB.pred_empty() && !MBB.isMachineBlockAddressTaken()) {
RemoveDeadBlock(&MBB);
MadeChange = true;
++NumDeadBlocks;
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2971,6 +2971,7 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
// catchswitch for successors.
MachineBasicBlock *Return = FuncInfo.MBBMap[I.getSuccessor(0)];
const BasicBlock *EHPadBB = I.getSuccessor(1);
MachineBasicBlock *EHPadMBB = FuncInfo.MBBMap[EHPadBB];

// Deopt bundles are lowered in LowerCallSiteWithDeoptBundle, and we don't
// have to do anything here to lower funclet bundles.
Expand All @@ -2995,6 +2996,10 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
case Intrinsic::seh_scope_begin:
case Intrinsic::seh_try_end:
case Intrinsic::seh_scope_end:
if (EHPadMBB)
// a block referenced by EH table
// so dtor-funclet not removed by opts
EHPadMBB->setMachineBlockAddressTaken();
break;
case Intrinsic::experimental_patchpoint_void:
case Intrinsic::experimental_patchpoint_i64:
Expand Down
42 changes: 42 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/CodeGen/ValueTypes.h"
#include "llvm/CodeGen/WinEHFuncInfo.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
Expand Down Expand Up @@ -1292,6 +1293,43 @@ bool SelectionDAGISel::PrepareEHLandingPad() {
return true;
}

// Mark and Report IPToState for each Block under IsEHa
void SelectionDAGISel::reportIPToStateForBlocks(MachineFunction *MF) {
MachineModuleInfo &MMI = MF->getMMI();
llvm::WinEHFuncInfo *EHInfo = MF->getWinEHFuncInfo();
if (!EHInfo)
return;
for (auto MBBI = MF->begin(), E = MF->end(); MBBI != E; ++MBBI) {
MachineBasicBlock *MBB = &*MBBI;
const BasicBlock *BB = MBB->getBasicBlock();
int State = EHInfo->BlockToStateMap[BB];
if (BB->getFirstMayFaultInst()) {
// Report IP range only for blocks with Faulty inst
auto MBBb = MBB->getFirstNonPHI();
MachineInstr *MIb = &*MBBb;
if (MIb->isTerminator())
continue;

// Insert EH Labels
MCSymbol *BeginLabel = MMI.getContext().createTempSymbol();
MCSymbol *EndLabel = MMI.getContext().createTempSymbol();
EHInfo->addIPToStateRange(State, BeginLabel, EndLabel);
BuildMI(*MBB, MBBb, SDB->getCurDebugLoc(),
TII->get(TargetOpcode::EH_LABEL))
.addSym(BeginLabel);
auto MBBe = MBB->instr_end();
MachineInstr *MIe = &*(--MBBe);
// insert before (possible multiple) terminators
while (MIe->isTerminator())
MIe = &*(--MBBe);
++MBBe;
BuildMI(*MBB, MBBe, SDB->getCurDebugLoc(),
TII->get(TargetOpcode::EH_LABEL))
.addSym(EndLabel);
}
}
}

/// isFoldedOrDeadInstruction - Return true if the specified instruction is
/// side-effect free and is either dead or folded into a generated instruction.
/// Return false if it needs to be emitted.
Expand Down Expand Up @@ -1649,6 +1687,10 @@ void SelectionDAGISel::SelectAllBasicBlocks(const Function &Fn) {
ElidedArgCopyInstrs.clear();
}

// AsynchEH: Report Block State under -AsynchEH
if (Fn.getParent()->getModuleFlag("eh-asynch"))
reportIPToStateForBlocks(MF);

SP.copyToMachineFrameInfo(MF->getFrameInfo());

SwiftError->propagateVRegs();
Expand Down
140 changes: 140 additions & 0 deletions llvm/lib/CodeGen/WinEHPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,127 @@ static void calculateStateNumbersForInvokes(const Function *Fn,
}
}

// See comments below for calculateSEHStateForAsynchEH().
// State - incoming State of normal paths
struct WorkItem {
const BasicBlock *Block;
int State;
WorkItem(const BasicBlock *BB, int St) {
Block = BB;
State = St;
}
};
void llvm::calculateCXXStateForAsynchEH(const BasicBlock *BB, int State,
WinEHFuncInfo &EHInfo) {
SmallVector<struct WorkItem *, 8> WorkList;
struct WorkItem *WI = new WorkItem(BB, State);
WorkList.push_back(WI);

while (!WorkList.empty()) {
WI = WorkList.pop_back_val();
const BasicBlock *BB = WI->Block;
int State = WI->State;
delete WI;
if (EHInfo.BlockToStateMap.count(BB) && EHInfo.BlockToStateMap[BB] <= State)
continue; // skip blocks already visited by lower State

const llvm::Instruction *I = BB->getFirstNonPHI();
const llvm::Instruction *TI = BB->getTerminator();
if (I->isEHPad())
State = EHInfo.EHPadStateMap[I];
EHInfo.BlockToStateMap[BB] = State; // Record state, also flag visiting

if ((isa<CleanupReturnInst>(TI) || isa<CatchReturnInst>(TI)) && State > 0) {
// Retrive the new State
State = EHInfo.CxxUnwindMap[State].ToState; // Retrive next State
} else if (isa<InvokeInst>(TI)) {
auto *Call = dyn_cast<CallBase>(TI);
const Function *Fn = Call->getCalledFunction();
if (Fn && Fn->isIntrinsic() &&
(Fn->getIntrinsicID() == Intrinsic::seh_scope_begin ||
Fn->getIntrinsicID() == Intrinsic::seh_try_begin))
// Retrive the new State from seh_scope_begin
State = EHInfo.InvokeStateMap[cast<InvokeInst>(TI)];
else if (Fn && Fn->isIntrinsic() &&
(Fn->getIntrinsicID() == Intrinsic::seh_scope_end ||
Fn->getIntrinsicID() == Intrinsic::seh_try_end)) {
// In case of conditional ctor, let's retrieve State from Invoke
State = EHInfo.InvokeStateMap[cast<InvokeInst>(TI)];
// end of current state, retrive new state from UnwindMap
State = EHInfo.CxxUnwindMap[State].ToState;
}
}
// Continue push successors into worklist
for (auto *SuccBB : successors(BB)) {
WI = new WorkItem(SuccBB, State);
WorkList.push_back(WI);
}
}
}

// The central theory of this routine is based on the following:
// A _try scope is always a SEME (Single Entry Multiple Exits) region
// as jumping into a _try is not allowed
// The single entry must start with a seh_try_begin() invoke with a
// correct State number that is the initial state of the SEME.
// Through control-flow, state number is propagated into all blocks.
// Side exits marked by seh_try_end() will unwind to parent state via
// existing SEHUnwindMap[].
// Side exits can ONLY jump into parent scopes (lower state number).
// Thus, when a block succeeds various states from its predecessors,
// the lowest State trumphs others.
// If some exits flow to unreachable, propagation on those paths terminate,
// not affecting remaining blocks.
void llvm::calculateSEHStateForAsynchEH(const BasicBlock *BB, int State,
WinEHFuncInfo &EHInfo) {
SmallVector<struct WorkItem *, 8> WorkList;
struct WorkItem *WI = new WorkItem(BB, State);
WorkList.push_back(WI);

while (!WorkList.empty()) {
WI = WorkList.pop_back_val();
const BasicBlock *BB = WI->Block;
int State = WI->State;
delete WI;
if (EHInfo.BlockToStateMap.count(BB) && EHInfo.BlockToStateMap[BB] <= State)
continue; // skip blocks already visited by lower State

const llvm::Instruction *I = BB->getFirstNonPHI();
const llvm::Instruction *TI = BB->getTerminator();
if (I->isEHPad())
State = EHInfo.EHPadStateMap[I];
EHInfo.BlockToStateMap[BB] = State; // Record state

if (isa<CatchPadInst>(I) && isa<CatchReturnInst>(TI)) {
const Constant *FilterOrNull = cast<Constant>(
cast<CatchPadInst>(I)->getArgOperand(0)->stripPointerCasts());
const Function *Filter = dyn_cast<Function>(FilterOrNull);
if (!Filter || !Filter->getName().startswith("__IsLocalUnwind"))
State = EHInfo.SEHUnwindMap[State].ToState; // Retrive next State
} else if ((isa<CleanupReturnInst>(TI) || isa<CatchReturnInst>(TI)) &&
State > 0) {
// Retrive the new State.
State = EHInfo.SEHUnwindMap[State].ToState; // Retrive next State
} else if (isa<InvokeInst>(TI)) {
auto *Call = dyn_cast<CallBase>(TI);
const Function *Fn = Call->getCalledFunction();
if (Fn && Fn->isIntrinsic() &&
Fn->getIntrinsicID() == Intrinsic::seh_try_begin)
// Retrive the new State from seh_try_begin
State = EHInfo.InvokeStateMap[cast<InvokeInst>(TI)];
else if (Fn && Fn->isIntrinsic() &&
Fn->getIntrinsicID() == Intrinsic::seh_try_end)
// end of current state, retrive new state from UnwindMap
State = EHInfo.SEHUnwindMap[State].ToState;
}
// Continue push successors into worklist
for (auto *SuccBB : successors(BB)) {
WI = new WorkItem(SuccBB, State);
WorkList.push_back(WI);
}
}
}

// Given BB which ends in an unwind edge, return the EHPad that this BB belongs
// to. If the unwind edge came from an invoke, return null.
static const BasicBlock *getEHPadFromPredecessor(const BasicBlock *BB,
Expand Down Expand Up @@ -276,6 +397,7 @@ static void calculateCXXStateNumbers(WinEHFuncInfo &FuncInfo,

for (const auto *CatchPad : Handlers) {
FuncInfo.FuncletBaseStateMap[CatchPad] = CatchLow;
FuncInfo.EHPadStateMap[CatchPad] = CatchLow;
for (const User *U : CatchPad->users()) {
const auto *UserI = cast<Instruction>(U);
if (auto *InnerCatchSwitch = dyn_cast<CatchSwitchInst>(UserI)) {
Expand Down Expand Up @@ -384,6 +506,7 @@ static void calculateSEHStateNumbers(WinEHFuncInfo &FuncInfo,

// Everything in the __try block uses TryState as its parent state.
FuncInfo.EHPadStateMap[CatchSwitch] = TryState;
FuncInfo.EHPadStateMap[CatchPad] = TryState;
LLVM_DEBUG(dbgs() << "Assigning state #" << TryState << " to BB "
<< CatchPadBB->getName() << '\n');
for (const BasicBlock *PredBlock : predecessors(BB))
Expand Down Expand Up @@ -464,6 +587,12 @@ void llvm::calculateSEHStateNumbers(const Function *Fn,
}

calculateStateNumbersForInvokes(Fn, FuncInfo);

bool IsEHa = Fn->getParent()->getModuleFlag("eh-asynch");
if (IsEHa) {
const BasicBlock *EntryBB = &(Fn->getEntryBlock());
calculateSEHStateForAsynchEH(EntryBB, -1, FuncInfo);
}
}

void llvm::calculateWinCXXEHStateNumbers(const Function *Fn,
Expand All @@ -482,6 +611,12 @@ void llvm::calculateWinCXXEHStateNumbers(const Function *Fn,
}

calculateStateNumbersForInvokes(Fn, FuncInfo);

bool IsEHa = Fn->getParent()->getModuleFlag("eh-asynch");
if (IsEHa) {
const BasicBlock *EntryBB = &(Fn->getEntryBlock());
calculateCXXStateForAsynchEH(EntryBB, -1, FuncInfo);
}
}

static int addClrEHHandler(WinEHFuncInfo &FuncInfo, int HandlerParentState,
Expand Down Expand Up @@ -1253,4 +1388,9 @@ void WinEHFuncInfo::addIPToStateRange(const InvokeInst *II,
LabelToStateMap[InvokeBegin] = std::make_pair(InvokeStateMap[II], InvokeEnd);
}

void WinEHFuncInfo::addIPToStateRange(int State, MCSymbol* InvokeBegin,
MCSymbol* InvokeEnd) {
LabelToStateMap[InvokeBegin] = std::make_pair(State, InvokeEnd);
}

WinEHFuncInfo::WinEHFuncInfo() = default;
9 changes: 9 additions & 0 deletions llvm/lib/IR/BasicBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,15 @@ const CallInst *BasicBlock::getPostdominatingDeoptimizeCall() const {
return BB->getTerminatingDeoptimizeCall();
}

const Instruction *BasicBlock::getFirstMayFaultInst() const {
if (InstList.empty())
return nullptr;
for (const Instruction &I : *this)
if (isa<LoadInst>(I) || isa<StoreInst>(I) || isa<CallBase>(I))
return &I;
return nullptr;
}

const Instruction* BasicBlock::getFirstNonPHI() const {
for (const Instruction &I : *this)
if (!isa<PHINode>(I))
Expand Down
Loading

0 comments on commit 0efe111

Please sign in to comment.