mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-29 14:20:29 +00:00
[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
a3219fe801
commit
8e7bbf9abf
@ -569,6 +569,12 @@ public:
|
|||||||
return !empty() && back().isReturn();
|
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
|
/// 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.
|
/// return the newly created block, or null if splitting is not possible.
|
||||||
///
|
///
|
||||||
|
@ -616,6 +616,12 @@ public:
|
|||||||
return hasProperty(MCID::Return, Type);
|
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 {
|
bool isCall(QueryType Type = AnyInBundle) const {
|
||||||
return hasProperty(MCID::Call, Type);
|
return hasProperty(MCID::Call, Type);
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,7 @@ enum Flag {
|
|||||||
HasOptionalDef,
|
HasOptionalDef,
|
||||||
Pseudo,
|
Pseudo,
|
||||||
Return,
|
Return,
|
||||||
|
EHScopeReturn,
|
||||||
Call,
|
Call,
|
||||||
Barrier,
|
Barrier,
|
||||||
Terminator,
|
Terminator,
|
||||||
|
@ -439,6 +439,7 @@ class Instruction {
|
|||||||
// instruction.
|
// instruction.
|
||||||
bit isReturn = 0; // Is this instruction a return instruction?
|
bit isReturn = 0; // Is this instruction a return instruction?
|
||||||
bit isBranch = 0; // Is this instruction a branch 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 isIndirectBranch = 0; // Is this instruction an indirect branch?
|
||||||
bit isCompare = 0; // Is this instruction a comparison instruction?
|
bit isCompare = 0; // Is this instruction a comparison instruction?
|
||||||
bit isMoveImm = 0; // Is this instruction a move immediate 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
|
// Returns are boundaries where scope transfer can occur, don't follow
|
||||||
// successors.
|
// successors.
|
||||||
if (Visiting->isReturnBlock())
|
if (Visiting->isEHScopeReturnBlock())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (const MachineBasicBlock *Succ : Visiting->successors())
|
for (const MachineBasicBlock *Succ : Visiting->successors())
|
||||||
|
@ -185,10 +185,8 @@ defm CATCH_ALL : NRI<(outs), (ins), [], "catch_all", 0x05>;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pseudo instructions: cleanupret / catchret
|
// 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,
|
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 CLEANUPRET : NRI<(outs), (ins), [(cleanupret)], "", 0>;
|
||||||
defm CATCHRET : NRI<(outs), (ins bb_op:$dst, bb_op:$from),
|
defm CATCHRET : NRI<(outs), (ins bb_op:$dst, bb_op:$from),
|
||||||
[(catchret bb:$dst, bb:$from)], "", 0>;
|
[(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,
|
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)]>;
|
def CLEANUPRET : I<0, Pseudo, (outs), (ins), "# CLEANUPRET", [(cleanupret)]>;
|
||||||
|
|
||||||
// CATCHRET needs a custom inserter for SEH.
|
// CATCHRET needs a custom inserter for SEH.
|
||||||
|
@ -150,7 +150,71 @@ terminate10: ; preds = %ehcleanup7
|
|||||||
unreachable
|
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 @foo()
|
||||||
|
declare void @bar(i32*)
|
||||||
declare i32 @__gxx_wasm_personality_v0(...)
|
declare i32 @__gxx_wasm_personality_v0(...)
|
||||||
declare i8* @llvm.wasm.get.exception(token)
|
declare i8* @llvm.wasm.get.exception(token)
|
||||||
declare i32 @llvm.wasm.get.ehselector(token)
|
declare i32 @llvm.wasm.get.ehselector(token)
|
||||||
|
@ -302,6 +302,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R)
|
|||||||
AsmString = R->getValueAsString("AsmString");
|
AsmString = R->getValueAsString("AsmString");
|
||||||
|
|
||||||
isReturn = R->getValueAsBit("isReturn");
|
isReturn = R->getValueAsBit("isReturn");
|
||||||
|
isEHScopeReturn = R->getValueAsBit("isEHScopeReturn");
|
||||||
isBranch = R->getValueAsBit("isBranch");
|
isBranch = R->getValueAsBit("isBranch");
|
||||||
isIndirectBranch = R->getValueAsBit("isIndirectBranch");
|
isIndirectBranch = R->getValueAsBit("isIndirectBranch");
|
||||||
isCompare = R->getValueAsBit("isCompare");
|
isCompare = R->getValueAsBit("isCompare");
|
||||||
|
@ -222,6 +222,7 @@ template <typename T> class ArrayRef;
|
|||||||
|
|
||||||
// Various boolean values we track for the instruction.
|
// Various boolean values we track for the instruction.
|
||||||
bool isReturn : 1;
|
bool isReturn : 1;
|
||||||
|
bool isEHScopeReturn : 1;
|
||||||
bool isBranch : 1;
|
bool isBranch : 1;
|
||||||
bool isIndirectBranch : 1;
|
bool isIndirectBranch : 1;
|
||||||
bool isCompare : 1;
|
bool isCompare : 1;
|
||||||
|
@ -100,6 +100,7 @@ void EmitInstrDocs(RecordKeeper &RK, raw_ostream &OS) {
|
|||||||
#define str(s) #s
|
#define str(s) #s
|
||||||
#define FLAG(f) if (II->f) { FlagStrings.push_back(str(f)); }
|
#define FLAG(f) if (II->f) { FlagStrings.push_back(str(f)); }
|
||||||
FLAG(isReturn)
|
FLAG(isReturn)
|
||||||
|
FLAG(isEHScopeReturn)
|
||||||
FLAG(isBranch)
|
FLAG(isBranch)
|
||||||
FLAG(isIndirectBranch)
|
FLAG(isIndirectBranch)
|
||||||
FLAG(isCompare)
|
FLAG(isCompare)
|
||||||
|
@ -569,6 +569,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num,
|
|||||||
// Emit all of the target independent flags...
|
// Emit all of the target independent flags...
|
||||||
if (Inst.isPseudo) OS << "|(1ULL<<MCID::Pseudo)";
|
if (Inst.isPseudo) OS << "|(1ULL<<MCID::Pseudo)";
|
||||||
if (Inst.isReturn) OS << "|(1ULL<<MCID::Return)";
|
if (Inst.isReturn) OS << "|(1ULL<<MCID::Return)";
|
||||||
|
if (Inst.isEHScopeReturn) OS << "|(1ULL<<MCID::EHScopeReturn)";
|
||||||
if (Inst.isBranch) OS << "|(1ULL<<MCID::Branch)";
|
if (Inst.isBranch) OS << "|(1ULL<<MCID::Branch)";
|
||||||
if (Inst.isIndirectBranch) OS << "|(1ULL<<MCID::IndirectBranch)";
|
if (Inst.isIndirectBranch) OS << "|(1ULL<<MCID::IndirectBranch)";
|
||||||
if (Inst.isCompare) OS << "|(1ULL<<MCID::Compare)";
|
if (Inst.isCompare) OS << "|(1ULL<<MCID::Compare)";
|
||||||
|
Loading…
Reference in New Issue
Block a user