[SimplifyCFG] 'merge compatible invokes': fully support indirect invokes

As long as *all* the invokes in the set are indirect,
we can merge them, but don't merge direct invokes into the set,
even though it would be legal to do.
This commit is contained in:
Roman Lebedev 2022-02-08 21:17:23 +03:00
parent 414b47645d
commit c8ba2b67a0
No known key found for this signature in database
GPG Key ID: 083C3EBB4A1689E0
2 changed files with 63 additions and 59 deletions

View File

@ -2284,16 +2284,25 @@ bool CompatibleSets::shouldBelongToSameSet(ArrayRef<InvokeInst *> Invokes) {
if (any_of(Invokes, IsIllegalToMerge)) if (any_of(Invokes, IsIllegalToMerge))
return false; return false;
// All callees must be identical. // Either both `invoke`s must be direct,
// FIXME: support indirect callees? // or both `invoke`s must be indirect.
Value *Callee = nullptr; auto IsIndirectCall = [](InvokeInst *II) { return II->isIndirectCall(); };
for (InvokeInst *II : Invokes) { bool HaveIndirectCalls = any_of(Invokes, IsIndirectCall);
Value *CurrCallee = II->getCalledOperand(); bool AllCallsAreIndirect = all_of(Invokes, IsIndirectCall);
assert(CurrCallee && "There is always a called operand."); if (HaveIndirectCalls) {
if (!Callee) if (!AllCallsAreIndirect)
Callee = CurrCallee;
else if (Callee != CurrCallee)
return false; return false;
} else {
// All callees must be identical.
Value *Callee = nullptr;
for (InvokeInst *II : Invokes) {
Value *CurrCallee = II->getCalledOperand();
assert(CurrCallee && "There is always a called operand.");
if (!Callee)
Callee = CurrCallee;
else if (Callee != CurrCallee)
return false;
}
} }
// Either both `invoke`s must not have a normal destination, // Either both `invoke`s must not have a normal destination,
@ -2436,8 +2445,17 @@ static void MergeCompatibleInvokesImpl(ArrayRef<InvokeInst *> Invokes,
{DominatorTree::Delete, II->getParent(), SuccOfPredBB}); {DominatorTree::Delete, II->getParent(), SuccOfPredBB});
} }
// Form the merged data operands for the merged invoke. bool IsIndirectCall = Invokes[0]->isIndirectCall();
for (Use &U : MergedInvoke->data_ops()) {
// Form the merged operands for the merged invoke.
for (Use &U : MergedInvoke->operands()) {
// Only PHI together the indirect callees and data operands.
if (MergedInvoke->isCallee(&U)) {
if (!IsIndirectCall)
continue;
} else if (!MergedInvoke->isDataOperand(&U))
continue;
// Don't create trivial PHI's with all-identical incoming values. // Don't create trivial PHI's with all-identical incoming values.
bool NeedPHI = any_of(Invokes, [&U](InvokeInst *II) { bool NeedPHI = any_of(Invokes, [&U](InvokeInst *II) {
return II->getOperand(U.getOperandNo()) != U.get(); return II->getOperand(U.getOperandNo()) != U.get();
@ -2448,10 +2466,8 @@ static void MergeCompatibleInvokesImpl(ArrayRef<InvokeInst *> Invokes,
// Form a PHI out of all the data ops under this index. // Form a PHI out of all the data ops under this index.
PHINode *PN = PHINode::Create( PHINode *PN = PHINode::Create(
U->getType(), /*NumReservedValues=*/Invokes.size(), "", MergedInvoke); U->getType(), /*NumReservedValues=*/Invokes.size(), "", MergedInvoke);
for (InvokeInst *II : Invokes) { for (InvokeInst *II : Invokes)
Use *IVU = II->data_operands_begin() + MergedInvoke->getDataOperandNo(&U); PN->addIncoming(II->getOperand(U.getOperandNo()), II->getParent());
PN->addIncoming(IVU->get(), II->getParent());
}
U.set(PN); U.set(PN);
} }

View File

@ -2166,12 +2166,7 @@ define void @t36_different_indirect_callees(void()* %callee0, void()* %callee1)
; CHECK-LABEL: @t36_different_indirect_callees( ; CHECK-LABEL: @t36_different_indirect_callees(
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C0]], label [[IF_THEN0:%.*]], label [[IF_ELSE:%.*]] ; CHECK-NEXT: br i1 [[C0]], label [[IF_THEN1_INVOKE:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then0:
; CHECK-NEXT: invoke void [[CALLEE0:%.*]]()
; CHECK-NEXT: to label [[INVOKE_CONT0:%.*]] unwind label [[LPAD:%.*]]
; CHECK: invoke.cont0:
; CHECK-NEXT: unreachable
; CHECK: lpad: ; CHECK: lpad:
; CHECK-NEXT: [[EH:%.*]] = landingpad { i8*, i32 } ; CHECK-NEXT: [[EH:%.*]] = landingpad { i8*, i32 }
; CHECK-NEXT: cleanup ; CHECK-NEXT: cleanup
@ -2179,11 +2174,12 @@ define void @t36_different_indirect_callees(void()* %callee0, void()* %callee1)
; CHECK-NEXT: resume { i8*, i32 } [[EH]] ; CHECK-NEXT: resume { i8*, i32 } [[EH]]
; CHECK: if.else: ; CHECK: if.else:
; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C1]], label [[IF_THEN1:%.*]], label [[IF_END:%.*]] ; CHECK-NEXT: br i1 [[C1]], label [[IF_THEN1_INVOKE]], label [[IF_END:%.*]]
; CHECK: if.then1: ; CHECK: if.then1.invoke:
; CHECK-NEXT: invoke void [[CALLEE1:%.*]]() ; CHECK-NEXT: [[TMP0:%.*]] = phi void ()* [ [[CALLEE1:%.*]], [[IF_ELSE]] ], [ [[CALLEE0:%.*]], [[ENTRY:%.*]] ]
; CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[LPAD]] ; CHECK-NEXT: invoke void [[TMP0]]()
; CHECK: invoke.cont2: ; CHECK-NEXT: to label [[IF_THEN1_CONT:%.*]] unwind label [[LPAD:%.*]]
; CHECK: if.then1.cont:
; CHECK-NEXT: unreachable ; CHECK-NEXT: unreachable
; CHECK: if.end: ; CHECK: if.end:
; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: call void @sideeffect()
@ -2295,12 +2291,7 @@ define void @t38_different_arguments_and_operand_bundes_are_fine(void(i32)* %cal
; CHECK-LABEL: @t38_different_arguments_and_operand_bundes_are_fine( ; CHECK-LABEL: @t38_different_arguments_and_operand_bundes_are_fine(
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C0]], label [[IF_THEN0:%.*]], label [[IF_ELSE:%.*]] ; CHECK-NEXT: br i1 [[C0]], label [[IF_THEN1_INVOKE:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then0:
; CHECK-NEXT: invoke void [[CALLEE0:%.*]](i32 0)
; CHECK-NEXT: to label [[INVOKE_CONT0:%.*]] unwind label [[LPAD:%.*]]
; CHECK: invoke.cont0:
; CHECK-NEXT: unreachable
; CHECK: lpad: ; CHECK: lpad:
; CHECK-NEXT: [[EH:%.*]] = landingpad { i8*, i32 } ; CHECK-NEXT: [[EH:%.*]] = landingpad { i8*, i32 }
; CHECK-NEXT: cleanup ; CHECK-NEXT: cleanup
@ -2308,11 +2299,13 @@ define void @t38_different_arguments_and_operand_bundes_are_fine(void(i32)* %cal
; CHECK-NEXT: resume { i8*, i32 } [[EH]] ; CHECK-NEXT: resume { i8*, i32 } [[EH]]
; CHECK: if.else: ; CHECK: if.else:
; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C1]], label [[IF_THEN1:%.*]], label [[IF_END:%.*]] ; CHECK-NEXT: br i1 [[C1]], label [[IF_THEN1_INVOKE]], label [[IF_END:%.*]]
; CHECK: if.then1: ; CHECK: if.then1.invoke:
; CHECK-NEXT: invoke void [[CALLEE1:%.*]](i32 42) ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 42, [[IF_ELSE]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[LPAD]] ; CHECK-NEXT: [[TMP1:%.*]] = phi void (i32)* [ [[CALLEE1:%.*]], [[IF_ELSE]] ], [ [[CALLEE0:%.*]], [[ENTRY]] ]
; CHECK: invoke.cont2: ; CHECK-NEXT: invoke void [[TMP1]](i32 [[TMP0]])
; CHECK-NEXT: to label [[IF_THEN1_CONT:%.*]] unwind label [[LPAD:%.*]]
; CHECK: if.then1.cont:
; CHECK-NEXT: unreachable ; CHECK-NEXT: unreachable
; CHECK: if.end: ; CHECK: if.end:
; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: call void @sideeffect()
@ -2353,12 +2346,7 @@ define void @t39_different_arguments_and_operand_bundes_are_fine(void()* %callee
; CHECK-LABEL: @t39_different_arguments_and_operand_bundes_are_fine( ; CHECK-LABEL: @t39_different_arguments_and_operand_bundes_are_fine(
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C0]], label [[IF_THEN0:%.*]], label [[IF_ELSE:%.*]] ; CHECK-NEXT: br i1 [[C0]], label [[IF_THEN1_INVOKE:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then0:
; CHECK-NEXT: invoke void [[CALLEE0:%.*]]() [ "abc"(i32 42) ]
; CHECK-NEXT: to label [[INVOKE_CONT0:%.*]] unwind label [[LPAD:%.*]]
; CHECK: invoke.cont0:
; CHECK-NEXT: unreachable
; CHECK: lpad: ; CHECK: lpad:
; CHECK-NEXT: [[EH:%.*]] = landingpad { i8*, i32 } ; CHECK-NEXT: [[EH:%.*]] = landingpad { i8*, i32 }
; CHECK-NEXT: cleanup ; CHECK-NEXT: cleanup
@ -2366,11 +2354,13 @@ define void @t39_different_arguments_and_operand_bundes_are_fine(void()* %callee
; CHECK-NEXT: resume { i8*, i32 } [[EH]] ; CHECK-NEXT: resume { i8*, i32 } [[EH]]
; CHECK: if.else: ; CHECK: if.else:
; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C1]], label [[IF_THEN1:%.*]], label [[IF_END:%.*]] ; CHECK-NEXT: br i1 [[C1]], label [[IF_THEN1_INVOKE]], label [[IF_END:%.*]]
; CHECK: if.then1: ; CHECK: if.then1.invoke:
; CHECK-NEXT: invoke void [[CALLEE1:%.*]]() [ "abc"(i32 0) ] ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 0, [[IF_ELSE]] ], [ 42, [[ENTRY:%.*]] ]
; CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[LPAD]] ; CHECK-NEXT: [[TMP1:%.*]] = phi void ()* [ [[CALLEE1:%.*]], [[IF_ELSE]] ], [ [[CALLEE0:%.*]], [[ENTRY]] ]
; CHECK: invoke.cont2: ; CHECK-NEXT: invoke void [[TMP1]]() [ "abc"(i32 [[TMP0]]) ]
; CHECK-NEXT: to label [[IF_THEN1_CONT:%.*]] unwind label [[LPAD:%.*]]
; CHECK: if.then1.cont:
; CHECK-NEXT: unreachable ; CHECK-NEXT: unreachable
; CHECK: if.end: ; CHECK: if.end:
; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: call void @sideeffect()
@ -2411,12 +2401,7 @@ define void @t40_different_arguments_and_operand_bundes_are_fine(void(i32)* %cal
; CHECK-LABEL: @t40_different_arguments_and_operand_bundes_are_fine( ; CHECK-LABEL: @t40_different_arguments_and_operand_bundes_are_fine(
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: [[C0:%.*]] = call i1 @cond() ; CHECK-NEXT: [[C0:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C0]], label [[IF_THEN0:%.*]], label [[IF_ELSE:%.*]] ; CHECK-NEXT: br i1 [[C0]], label [[IF_THEN1_INVOKE:%.*]], label [[IF_ELSE:%.*]]
; CHECK: if.then0:
; CHECK-NEXT: invoke void [[CALLEE0:%.*]](i32 0) [ "abc"(i32 42) ]
; CHECK-NEXT: to label [[INVOKE_CONT0:%.*]] unwind label [[LPAD:%.*]]
; CHECK: invoke.cont0:
; CHECK-NEXT: unreachable
; CHECK: lpad: ; CHECK: lpad:
; CHECK-NEXT: [[EH:%.*]] = landingpad { i8*, i32 } ; CHECK-NEXT: [[EH:%.*]] = landingpad { i8*, i32 }
; CHECK-NEXT: cleanup ; CHECK-NEXT: cleanup
@ -2424,11 +2409,14 @@ define void @t40_different_arguments_and_operand_bundes_are_fine(void(i32)* %cal
; CHECK-NEXT: resume { i8*, i32 } [[EH]] ; CHECK-NEXT: resume { i8*, i32 } [[EH]]
; CHECK: if.else: ; CHECK: if.else:
; CHECK-NEXT: [[C1:%.*]] = call i1 @cond() ; CHECK-NEXT: [[C1:%.*]] = call i1 @cond()
; CHECK-NEXT: br i1 [[C1]], label [[IF_THEN1:%.*]], label [[IF_END:%.*]] ; CHECK-NEXT: br i1 [[C1]], label [[IF_THEN1_INVOKE]], label [[IF_END:%.*]]
; CHECK: if.then1: ; CHECK: if.then1.invoke:
; CHECK-NEXT: invoke void [[CALLEE1:%.*]](i32 42) [ "abc"(i32 0) ] ; CHECK-NEXT: [[TMP0:%.*]] = phi i32 [ 42, [[IF_ELSE]] ], [ 0, [[ENTRY:%.*]] ]
; CHECK-NEXT: to label [[INVOKE_CONT2:%.*]] unwind label [[LPAD]] ; CHECK-NEXT: [[TMP1:%.*]] = phi i32 [ 0, [[IF_ELSE]] ], [ 42, [[ENTRY]] ]
; CHECK: invoke.cont2: ; CHECK-NEXT: [[TMP2:%.*]] = phi void (i32)* [ [[CALLEE1:%.*]], [[IF_ELSE]] ], [ [[CALLEE0:%.*]], [[ENTRY]] ]
; CHECK-NEXT: invoke void [[TMP2]](i32 [[TMP0]]) [ "abc"(i32 [[TMP1]]) ]
; CHECK-NEXT: to label [[IF_THEN1_CONT:%.*]] unwind label [[LPAD:%.*]]
; CHECK: if.then1.cont:
; CHECK-NEXT: unreachable ; CHECK-NEXT: unreachable
; CHECK: if.end: ; CHECK: if.end:
; CHECK-NEXT: call void @sideeffect() ; CHECK-NEXT: call void @sideeffect()