forked from OSchip/llvm-project
[WebAssembly] Add isEHScopeReturn instruction property
Summary: So far, `isReturn` property is used to mean both a return instruction from a functon and the end of an EH scope, a scope that starts with a EH scope entry BB and ends with a catchret or a cleanupret instruction. Because WinEH uses funclets, all EH-scope-ending instructions are also real return instruction from a function. But for wasm, they only serve as the end marker of an EH scope but not a return instruction that exits a function. This mismatch caused incorrect prolog and epilog generation in wasm EH scopes. This patch fixes this. This patch is in the same vein with rL333045, which splits `MachineBasicBlock::isEHFuncletEntry` into `isEHFuncletEntry` and `isEHScopeEntry`. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Differential Revision: https://reviews.llvm.org/D50653 llvm-svn: 340325
This commit is contained in:
parent
4a76d3e568
commit
ed5e06b0a7
|
@ -569,6 +569,12 @@ public:
|
|||
return !empty() && back().isReturn();
|
||||
}
|
||||
|
||||
/// Convenience function that returns true if the bock ends in a EH scope
|
||||
/// return instruction.
|
||||
bool isEHScopeReturnBlock() const {
|
||||
return !empty() && back().isEHScopeReturn();
|
||||
}
|
||||
|
||||
/// Split the critical edge from this block to the given successor block, and
|
||||
/// return the newly created block, or null if splitting is not possible.
|
||||
///
|
||||
|
|
|
@ -616,6 +616,12 @@ public:
|
|||
return hasProperty(MCID::Return, Type);
|
||||
}
|
||||
|
||||
/// Return true if this is an instruction that marks the end of an EH scope,
|
||||
/// i.e., a catchpad or a cleanuppad instruction.
|
||||
bool isEHScopeReturn(QueryType Type = AnyInBundle) const {
|
||||
return hasProperty(MCID::EHScopeReturn, Type);
|
||||
}
|
||||
|
||||
bool isCall(QueryType Type = AnyInBundle) const {
|
||||
return hasProperty(MCID::Call, Type);
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ enum Flag {
|
|||
HasOptionalDef,
|
||||
Pseudo,
|
||||
Return,
|
||||
EHScopeReturn,
|
||||
Call,
|
||||
Barrier,
|
||||
Terminator,
|
||||
|
|
|
@ -439,6 +439,7 @@ class Instruction {
|
|||
// instruction.
|
||||
bit isReturn = 0; // Is this instruction a return instruction?
|
||||
bit isBranch = 0; // Is this instruction a branch instruction?
|
||||
bit isEHScopeReturn = 0; // Does this instruction end an EH scope?
|
||||
bit isIndirectBranch = 0; // Is this instruction an indirect branch?
|
||||
bit isCompare = 0; // Is this instruction a comparison instruction?
|
||||
bit isMoveImm = 0; // Is this instruction a move immediate instruction?
|
||||
|
|
|
@ -650,7 +650,7 @@ static void collectEHScopeMembers(
|
|||
|
||||
// Returns are boundaries where scope transfer can occur, don't follow
|
||||
// successors.
|
||||
if (Visiting->isReturnBlock())
|
||||
if (Visiting->isEHScopeReturnBlock())
|
||||
continue;
|
||||
|
||||
for (const MachineBasicBlock *Succ : Visiting->successors())
|
||||
|
|
|
@ -185,10 +185,8 @@ defm CATCH_ALL : NRI<(outs), (ins), [], "catch_all", 0x05>;
|
|||
}
|
||||
|
||||
// Pseudo instructions: cleanupret / catchret
|
||||
// They are not return instructions in wasm, but setting 'isReturn' to true as
|
||||
// in X86 is necessary for computing EH scope membership.
|
||||
let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1,
|
||||
isCodeGenOnly = 1, isReturn = 1 in {
|
||||
isCodeGenOnly = 1, isEHScopeReturn = 1 in {
|
||||
defm CLEANUPRET : NRI<(outs), (ins), [(cleanupret)], "", 0>;
|
||||
defm CATCHRET : NRI<(outs), (ins bb_op:$dst, bb_op:$from),
|
||||
[(catchret bb:$dst, bb:$from)], "", 0>;
|
||||
|
|
|
@ -178,7 +178,7 @@ def EH_RETURN64 : I<0xC3, RawFrm, (outs), (ins GR64:$addr),
|
|||
}
|
||||
|
||||
let isTerminator = 1, hasSideEffects = 1, isBarrier = 1, hasCtrlDep = 1,
|
||||
isCodeGenOnly = 1, isReturn = 1 in {
|
||||
isCodeGenOnly = 1, isReturn = 1, isEHScopeReturn = 1 in {
|
||||
def CLEANUPRET : I<0, Pseudo, (outs), (ins), "# CLEANUPRET", [(cleanupret)]>;
|
||||
|
||||
// CATCHRET needs a custom inserter for SEH.
|
||||
|
|
|
@ -150,7 +150,71 @@ terminate10: ; preds = %ehcleanup7
|
|||
unreachable
|
||||
}
|
||||
|
||||
; Tests prologues and epilogues are not generated within EH scopes.
|
||||
; They should not be treated as funclets; BBs starting with a catch instruction
|
||||
; should not have a prologue, and BBs ending with a catchret/cleanupret should
|
||||
; not have an epilogue. This is separate from __stack_pointer restoring
|
||||
; instructions after a catch instruction.
|
||||
|
||||
; CHECK-LABEL: test_no_prolog_epilog_in_ehpad
|
||||
; CHECK: try
|
||||
; CHECK: call foo@FUNCTION
|
||||
; CHECK: i32.catch
|
||||
; CHECK-NOT: get_global $push{{.+}}=, __stack_pointer@GLOBAL
|
||||
; CHECK: try
|
||||
; CHECK: call foo@FUNCTION
|
||||
; CHECK: catch_all
|
||||
; TODO This should be removed too in a later patch
|
||||
; CHECK-NO T: get_global $push{{.+}}=, __stack_pointer@GLOBAL
|
||||
; CHECK: call __cxa_end_catch@FUNCTION
|
||||
; CHECK-NOT: set_global __stack_pointer@GLOBAL, $pop{{.+}}
|
||||
; CHECK: end_try
|
||||
; CHECK-NOT: set_global __stack_pointer@GLOBAL, $pop{{.+}}
|
||||
; CHECK: end_try
|
||||
define void @test_no_prolog_epilog_in_ehpad() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
|
||||
entry:
|
||||
%stack_var = alloca i32, align 4
|
||||
call void @bar(i32* %stack_var)
|
||||
invoke void @foo()
|
||||
to label %try.cont unwind label %catch.dispatch
|
||||
|
||||
catch.dispatch: ; preds = %entry
|
||||
%0 = catchswitch within none [label %catch.start] unwind to caller
|
||||
|
||||
catch.start: ; preds = %catch.dispatch
|
||||
%1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)]
|
||||
%2 = call i8* @llvm.wasm.get.exception(token %1)
|
||||
%3 = call i32 @llvm.wasm.get.ehselector(token %1)
|
||||
%4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
|
||||
%matches = icmp eq i32 %3, %4
|
||||
br i1 %matches, label %catch, label %rethrow
|
||||
|
||||
catch: ; preds = %catch.start
|
||||
%5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
|
||||
%6 = bitcast i8* %5 to float*
|
||||
%7 = load float, float* %6, align 4
|
||||
invoke void @foo() [ "funclet"(token %1) ]
|
||||
to label %invoke.cont1 unwind label %ehcleanup
|
||||
|
||||
invoke.cont1: ; preds = %catch
|
||||
call void @__cxa_end_catch() [ "funclet"(token %1) ]
|
||||
catchret from %1 to label %try.cont
|
||||
|
||||
rethrow: ; preds = %catch.start
|
||||
call void @__cxa_rethrow() [ "funclet"(token %1) ]
|
||||
unreachable
|
||||
|
||||
try.cont: ; preds = %entry, %invoke.cont1
|
||||
ret void
|
||||
|
||||
ehcleanup: ; preds = %catch
|
||||
%8 = cleanuppad within %1 []
|
||||
call void @__cxa_end_catch() [ "funclet"(token %8) ]
|
||||
cleanupret from %8 unwind to caller
|
||||
}
|
||||
|
||||
declare void @foo()
|
||||
declare void @bar(i32*)
|
||||
declare i32 @__gxx_wasm_personality_v0(...)
|
||||
declare i8* @llvm.wasm.get.exception(token)
|
||||
declare i32 @llvm.wasm.get.ehselector(token)
|
||||
|
|
|
@ -302,6 +302,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R)
|
|||
AsmString = R->getValueAsString("AsmString");
|
||||
|
||||
isReturn = R->getValueAsBit("isReturn");
|
||||
isEHScopeReturn = R->getValueAsBit("isEHScopeReturn");
|
||||
isBranch = R->getValueAsBit("isBranch");
|
||||
isIndirectBranch = R->getValueAsBit("isIndirectBranch");
|
||||
isCompare = R->getValueAsBit("isCompare");
|
||||
|
|
|
@ -222,6 +222,7 @@ template <typename T> class ArrayRef;
|
|||
|
||||
// Various boolean values we track for the instruction.
|
||||
bool isReturn : 1;
|
||||
bool isEHScopeReturn : 1;
|
||||
bool isBranch : 1;
|
||||
bool isIndirectBranch : 1;
|
||||
bool isCompare : 1;
|
||||
|
|
|
@ -100,6 +100,7 @@ void EmitInstrDocs(RecordKeeper &RK, raw_ostream &OS) {
|
|||
#define str(s) #s
|
||||
#define FLAG(f) if (II->f) { FlagStrings.push_back(str(f)); }
|
||||
FLAG(isReturn)
|
||||
FLAG(isEHScopeReturn)
|
||||
FLAG(isBranch)
|
||||
FLAG(isIndirectBranch)
|
||||
FLAG(isCompare)
|
||||
|
|
|
@ -569,6 +569,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num,
|
|||
// Emit all of the target independent flags...
|
||||
if (Inst.isPseudo) OS << "|(1ULL<<MCID::Pseudo)";
|
||||
if (Inst.isReturn) OS << "|(1ULL<<MCID::Return)";
|
||||
if (Inst.isEHScopeReturn) OS << "|(1ULL<<MCID::EHScopeReturn)";
|
||||
if (Inst.isBranch) OS << "|(1ULL<<MCID::Branch)";
|
||||
if (Inst.isIndirectBranch) OS << "|(1ULL<<MCID::IndirectBranch)";
|
||||
if (Inst.isCompare) OS << "|(1ULL<<MCID::Compare)";
|
||||
|
|
Loading…
Reference in New Issue