[IROutliner] Ensure that phi values that are passed in as arguments are remapped as arguments

Issue: https://github.com/llvm/llvm-project/issues/54430

For incoming values of phi nodes added to an outlined function to accommodate different exit paths in the function, when a value is a constant that is passed into the outlined function as an argument, we find the corresponding value in the first extracted function used to fill the overall outlined function. When this value is an argument, the corresponding value used will be the old value, prior to outlining. This patch maintains a mapping from these values to arguments, and uses this mapping to update the added phi node accordingly.

Reviewers: paquette

Recommit of d6eb480afb

Differential Revision: https://reviews.llvm.org/D122206
This commit is contained in:
Andrew Litteken 2022-03-21 15:56:50 -05:00
parent 38ec33d6b9
commit d7c56a076e
3 changed files with 124 additions and 0 deletions

View File

@ -85,6 +85,13 @@ struct OutlinableRegion {
DenseMap<unsigned, unsigned> ExtractedArgToAgg;
DenseMap<unsigned, unsigned> AggArgToExtracted;
/// Values in the outlined functions will often be replaced by arguments. When
/// finding corresponding values from one region to another, the found value
/// will be the value the argument previously replaced. This structure maps
/// any replaced values for the region to the aggregate aggregate argument
/// in the overall function.
DenseMap<Value *, Value *> RemappedArguments;
/// Marks whether we need to change the order of the arguments when mapping
/// the old extracted function call to the new aggregate outlined function
/// call.

View File

@ -1737,6 +1737,10 @@ findOrCreatePHIInBlock(PHINode &PN, OutlinableRegion &Region,
IncomingVal = findOutputMapping(OutputMappings, IncomingVal);
Value *Val = Region.findCorrespondingValueIn(*FirstRegion, IncomingVal);
assert(Val && "Value is nullptr?");
DenseMap<Value *, Value *>::iterator RemappedIt =
FirstRegion->RemappedArguments.find(Val);
if (RemappedIt != FirstRegion->RemappedArguments.end())
Val = RemappedIt->second;
NewPN->setIncomingValue(Idx, Val);
}
return NewPN;
@ -1780,6 +1784,8 @@ replaceArgumentUses(OutlinableRegion &Region,
<< *Region.ExtractedFunction << " with " << *AggArg
<< " in function " << *Group.OutlinedFunction << "\n");
Arg->replaceAllUsesWith(AggArg);
Value *V = Region.Call->getArgOperand(ArgIdx);
Region.RemappedArguments.insert(std::make_pair(V, AggArg));
continue;
}

View File

@ -0,0 +1,111 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --include-generated-funcs
; RUN: opt -S -verify -iroutliner -ir-outlining-no-cost < %s | FileCheck %s
; When consolidating PHINodes, the outliner replaces the incoming value with
; a corresponding value from the first outlined section. When this replaced
; value is passed in as an argument, the corresponding value is found outside
; of the outlined region, and must be replaced with an argument to avoid
; dominating value errors. This checks that we use the argument to replace
; the incoming value.
define void @func1(i32 %0, i32 %1) local_unnamed_addr #0 {
bb1:
br label %bb5
bb2:
%a = add i32 %0, %1
%b = add i32 %0, %1
%c = icmp eq i32 %b, %a
br i1 %c, label %bb5, label %bb3
bb3:
%d = add i32 %0, %1
br label %bb5
bb4:
%e = sub i32 %0, %1
br label %bb2
bb5:
ret void
}
define void @func2(i32 %0, i32 %1) local_unnamed_addr #0 {
bb1:
br label %bb5
bb2:
%a = sub i32 %0, %1
%b = add i32 %0, %1
%c = icmp eq i32 %b, 1
br i1 %c, label %bb5, label %bb3
bb3:
%d = add i32 %0, %1
br label %bb5
bb4:
%e = add i32 %0, %1
br label %bb2
bb5:
%f = phi i32 [ 0, %bb1 ], [ 1, %bb2 ], [ 1, %bb3 ]
ret void
}
; CHECK-LABEL: @func1(
; CHECK-NEXT: bb1:
; CHECK-NEXT: br label [[BB5:%.*]]
; CHECK: bb2:
; CHECK-NEXT: [[A:%.*]] = add i32 [[TMP0:%.*]], [[TMP1:%.*]]
; CHECK-NEXT: call void @outlined_ir_func_0(i32 [[TMP0]], i32 [[TMP1]], i32 [[A]], i32* null, i32 -1)
; CHECK-NEXT: br label [[BB5]]
; CHECK: bb4:
; CHECK-NEXT: [[E:%.*]] = sub i32 [[TMP0]], [[TMP1]]
; CHECK-NEXT: br label [[BB2:%.*]]
; CHECK: bb5:
; CHECK-NEXT: ret void
;
;
; CHECK-LABEL: @func2(
; CHECK-NEXT: bb1:
; CHECK-NEXT: [[F_CE_LOC:%.*]] = alloca i32, align 4
; CHECK-NEXT: br label [[BB5:%.*]]
; CHECK: bb2:
; CHECK-NEXT: [[A:%.*]] = sub i32 [[TMP0:%.*]], [[TMP1:%.*]]
; CHECK-NEXT: [[LT_CAST:%.*]] = bitcast i32* [[F_CE_LOC]] to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 -1, i8* [[LT_CAST]])
; CHECK-NEXT: call void @outlined_ir_func_0(i32 [[TMP0]], i32 [[TMP1]], i32 1, i32* [[F_CE_LOC]], i32 0)
; CHECK-NEXT: [[F_CE_RELOAD:%.*]] = load i32, i32* [[F_CE_LOC]], align 4
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[LT_CAST]])
; CHECK-NEXT: br label [[BB5]]
; CHECK: bb4:
; CHECK-NEXT: [[E:%.*]] = add i32 [[TMP0]], [[TMP1]]
; CHECK-NEXT: br label [[BB2:%.*]]
; CHECK: bb5:
; CHECK-NEXT: [[F:%.*]] = phi i32 [ 0, [[BB1:%.*]] ], [ [[F_CE_RELOAD]], [[BB2]] ]
; CHECK-NEXT: ret void
;
;
; CHECK-LABEL: define internal void @outlined_ir_func_0(
; CHECK-NEXT: newFuncRoot:
; CHECK-NEXT: br label [[BB2_TO_OUTLINE:%.*]]
; CHECK: bb2_to_outline:
; CHECK-NEXT: [[B:%.*]] = add i32 [[TMP0:%.*]], [[TMP1:%.*]]
; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[B]], [[TMP2:%.*]]
; CHECK-NEXT: br i1 [[C]], label [[PHI_BLOCK:%.*]], label [[BB3:%.*]]
; CHECK: bb3:
; CHECK-NEXT: [[D:%.*]] = add i32 [[TMP0]], [[TMP1]]
; CHECK-NEXT: br label [[PHI_BLOCK]]
; CHECK: bb5.exitStub:
; CHECK-NEXT: switch i32 [[TMP4:%.*]], label [[FINAL_BLOCK_0:%.*]] [
; CHECK-NEXT: i32 0, label [[OUTPUT_BLOCK_1_0:%.*]]
; CHECK-NEXT: ]
; CHECK: output_block_1_0:
; CHECK-NEXT: store i32 [[TMP5:%.*]], i32* [[TMP3:%.*]], align 4
; CHECK-NEXT: br label [[FINAL_BLOCK_0]]
; CHECK: phi_block:
; CHECK-NEXT: [[TMP5]] = phi i32 [ [[TMP2]], [[BB2_TO_OUTLINE]] ], [ [[TMP2]], [[BB3]] ]
; CHECK-NEXT: br label [[BB5_EXITSTUB:%.*]]
; CHECK: final_block_0:
; CHECK-NEXT: ret void
;