[IROutliner] Ensure that phi values that are passed in as arguments are remapped...
authorAndrew Litteken <andrew.litteken@gmail.com>
Mon, 21 Mar 2022 20:56:50 +0000 (15:56 -0500)
committerAndrew Litteken <andrew.litteken@gmail.com>
Thu, 14 Apr 2022 17:16:23 +0000 (12:16 -0500)
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

Differential Revision: https://reviews.llvm.org/D122206

llvm/include/llvm/Transforms/IPO/IROutliner.h
llvm/lib/Transforms/IPO/IROutliner.cpp
llvm/test/Transforms/IROutliner/exit-phi-nodes-incoming-value-constant-argument.ll [new file with mode: 0644]

index a75a5046f3c6ba837e0f63a72d8906b386e2f0b0..315587e0f9228e5db117712be22612240114011c 100644 (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.
index ab6b74f9899ec4f1d9f53e970a51e06540d5fb71..9bde8bcf9cf074dd90d9057abc1fcf6ecb95289a 100644 (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;
     }
 
diff --git a/llvm/test/Transforms/IROutliner/exit-phi-nodes-incoming-value-constant-argument.ll b/llvm/test/Transforms/IROutliner/exit-phi-nodes-incoming-value-constant-argument.ll
new file mode 100644 (file)
index 0000000..1c0b68a
--- /dev/null
@@ -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 %bb5s
+
+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
+;