[llvm] [DirectX] Legalize Freeze instruction (PR #136043)

Farzon Lotfi via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 16 15:05:54 PDT 2025


https://github.com/farzonl created https://github.com/llvm/llvm-project/pull/136043

fixes #135719

LLVM 3.7 did not have a freeze instruction
Further this instruction is really only used as syntactic sugar
in LLVM's optimizer passes to not aggressively optimize things
that could be undef or poison ie x*2 to x+x.
Most backends treat it as a no-op so we will do the same
by removing it and replacing its uses with its input.

>From 27dca03e052e655423aa8b1de2bad816ee7bdbd4 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Wed, 16 Apr 2025 14:46:19 -0700
Subject: [PATCH 1/2] [DirectX] Legalize Freeze

legalize free by emulating what freeze would have done."
---
 llvm/lib/Target/DirectX/DXILLegalizePass.cpp | 67 ++++++++++++++++
 llvm/test/CodeGen/DirectX/legalize-freeze.ll | 81 ++++++++++++++++++++
 2 files changed, 148 insertions(+)
 create mode 100644 llvm/test/CodeGen/DirectX/legalize-freeze.ll

diff --git a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp
index 395311e430fbb..55e4fbcd20bb6 100644
--- a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp
+++ b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp
@@ -8,6 +8,7 @@
 
 #include "DXILLegalizePass.h"
 #include "DirectX.h"
+#include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstIterator.h"
@@ -20,6 +21,71 @@
 
 using namespace llvm;
 
+// The goal here will be to emulate freeze by Forcing SSA materialization.
+// We will do this by making the input bound to a real SSA value,
+// not a symbolic poison or undef. The implementation creates a dummy
+// control-flow split that always takes PathA and forces the inputs
+// through a phi node. Creating a dimond CFG makes the compiler
+// commit to one value for the input.
+//             entry(%x)
+//                |
+//            +---+---+
+//            |       |
+//        pathA(%x)   pathB(%x)
+//            |       |
+//            \______/
+//               |
+//             merge
+//   %frozen = phi [ %x, %pathA ], [ %x, %pathB ]
+
+static void lowerFreeze(FreezeInst *FI) {
+  Type *Ty = FI->getType();
+  LLVMContext &Ctx = FI->getContext();
+  BasicBlock *OrigBB = FI->getParent();
+  Value *Input = FI->getOperand(0);
+  Function *F = FI->getFunction();
+
+  // Split the block to isolate the freeze instruction
+  BasicBlock *MergeBB = OrigBB->splitBasicBlock(FI->getNextNode(), "merge");
+
+  // Remove the unconditional branch inserted by splitBasicBlock
+  OrigBB->getTerminator()->eraseFromParent();
+  BasicBlock *PathA = BasicBlock::Create(Ctx, "pathA", F, MergeBB);
+  BasicBlock *PathB = BasicBlock::Create(Ctx, "pathB", F, MergeBB);
+
+  IRBuilder<> Builder(OrigBB);
+  Builder.CreateCondBr(ConstantInt::getTrue(Ctx), PathA, PathB);
+
+  IRBuilder<> BuilderA(PathA);
+  BuilderA.CreateBr(MergeBB);
+  IRBuilder<> BuilderB(PathB);
+  BuilderB.CreateBr(MergeBB);
+
+  IRBuilder<> BuilderMerge(&MergeBB->front());
+  PHINode *Phi = BuilderMerge.CreatePHI(Ty, 2, "frozen");
+  Phi->addIncoming(Input, PathA);
+  Phi->addIncoming(Input, PathB);
+
+  FI->replaceAllUsesWith(Phi);
+}
+
+static void legalizeFreeze(Instruction &I,
+                           SmallVectorImpl<Instruction *> &ToRemove,
+                           DenseMap<Value *, Value *>) {
+  auto *FI = dyn_cast<FreezeInst>(&I);
+  if (!FI)
+    return;
+
+  Value *Input = FI->getOperand(0);
+
+  if (isGuaranteedNotToBeUndefOrPoison(Input))
+    FI->replaceAllUsesWith(Input);
+  else
+    lowerFreeze(FI);
+
+  ToRemove.push_back(FI);
+}
+
 static void fixI8TruncUseChain(Instruction &I,
                                SmallVectorImpl<Instruction *> &ToRemove,
                                DenseMap<Value *, Value *> &ReplacedValues) {
@@ -169,6 +235,7 @@ class DXILLegalizationPipeline {
   void initializeLegalizationPipeline() {
     LegalizationPipeline.push_back(fixI8TruncUseChain);
     LegalizationPipeline.push_back(downcastI64toI32InsertExtractElements);
+    LegalizationPipeline.push_back(legalizeFreeze);
   }
 };
 
diff --git a/llvm/test/CodeGen/DirectX/legalize-freeze.ll b/llvm/test/CodeGen/DirectX/legalize-freeze.ll
new file mode 100644
index 0000000000000..edc4de3d2e36b
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/legalize-freeze.ll
@@ -0,0 +1,81 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+
+; RUN: opt -S -passes='dxil-legalize' -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+
+define i32 @test_remove_freeze(i32 %x) {
+; CHECK-LABEL: define i32 @test_remove_freeze(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br i1 true, label %[[PATHA:.*]], label %[[PATHB:.*]]
+; CHECK:       [[PATHA]]:
+; CHECK-NEXT:    br label %[[MERGE:.*]]
+; CHECK:       [[PATHB]]:
+; CHECK-NEXT:    br label %[[MERGE]]
+; CHECK:       [[MERGE]]:
+; CHECK-NEXT:    [[FROZEN:%.*]] = phi i32 [ [[X]], %[[PATHA]] ], [ [[X]], %[[PATHB]] ]
+; CHECK-NEXT:    [[Y:%.*]] = add i32 [[FROZEN]], 1
+; CHECK-NEXT:    ret i32 [[Y]]
+;
+entry:
+  %f = freeze i32 %x
+  %y = add i32 %f, 1
+  ret i32 %y
+}
+
+define i32 @test_remove_freeze_safe(i32 %x) {
+; CHECK-LABEL: define i32 @test_remove_freeze_safe(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[FROZEN:%.*]] = add i32 1, 0
+; CHECK-NEXT:    [[Y:%.*]] = add i32 [[FROZEN]], 1
+; CHECK-NEXT:    ret i32 [[Y]]
+;
+entry:
+  %safe = add i32 1, 0
+  %f = freeze i32 %safe
+  %y = add i32 %f, 1
+  ret i32 %y
+}
+
+define i32 @test_freeze_poison() {
+; CHECK-LABEL: define i32 @test_freeze_poison() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[X1:%.*]] = select i1 true, i32 42, i32 poison
+; CHECK-NEXT:    br i1 true, label %[[PATHA:.*]], label %[[PATHB:.*]]
+; CHECK:       [[PATHA]]:
+; CHECK-NEXT:    br label %[[MERGE:.*]]
+; CHECK:       [[PATHB]]:
+; CHECK-NEXT:    br label %[[MERGE]]
+; CHECK:       [[MERGE]]:
+; CHECK-NEXT:    [[X:%.*]] = phi i32 [ [[X1]], %[[PATHA]] ], [ [[X1]], %[[PATHB]] ]
+; CHECK-NEXT:    [[Y:%.*]] = add i32 [[X]], 1
+; CHECK-NEXT:    ret i32 [[Y]]
+;
+entry:
+  %x = select i1 true, i32 42, i32 poison
+  %f = freeze i32 %x
+  %y = add i32 %f, 1
+  ret i32 %y
+}
+
+define i32 @test_freeze_undef() {
+; CHECK-LABEL: define i32 @test_freeze_undef() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[X:%.*]] = select i1 undef, i32 42, i32 2
+; CHECK-NEXT:    br i1 true, label %[[PATHA:.*]], label %[[PATHB:.*]]
+; CHECK:       [[PATHA]]:
+; CHECK-NEXT:    br label %[[MERGE:.*]]
+; CHECK:       [[PATHB]]:
+; CHECK-NEXT:    br label %[[MERGE]]
+; CHECK:       [[MERGE]]:
+; CHECK-NEXT:    [[FROZEN:%.*]] = phi i32 [ [[X]], %[[PATHA]] ], [ [[X]], %[[PATHB]] ]
+; CHECK-NEXT:    [[Y:%.*]] = add i32 [[FROZEN]], 1
+; CHECK-NEXT:    ret i32 [[Y]]
+;
+entry:
+  %x = select i1 undef, i32 42, i32 2
+  %f = freeze i32 %x
+  %y = add i32 %f, 1
+  ret i32 %y
+}

>From 2d1801f4d0b0407b6a909df77cf3b534e20ed8cd Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Wed, 16 Apr 2025 15:00:03 -0700
Subject: [PATCH 2/2] legalize freeze by removing it

---
 llvm/lib/Target/DirectX/DXILLegalizePass.cpp | 55 +----------------
 llvm/test/CodeGen/DirectX/legalize-freeze.ll | 64 --------------------
 2 files changed, 1 insertion(+), 118 deletions(-)

diff --git a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp
index 55e4fbcd20bb6..c133e59d2e3c6 100644
--- a/llvm/lib/Target/DirectX/DXILLegalizePass.cpp
+++ b/llvm/lib/Target/DirectX/DXILLegalizePass.cpp
@@ -21,54 +21,6 @@
 
 using namespace llvm;
 
-// The goal here will be to emulate freeze by Forcing SSA materialization.
-// We will do this by making the input bound to a real SSA value,
-// not a symbolic poison or undef. The implementation creates a dummy
-// control-flow split that always takes PathA and forces the inputs
-// through a phi node. Creating a dimond CFG makes the compiler
-// commit to one value for the input.
-//             entry(%x)
-//                |
-//            +---+---+
-//            |       |
-//        pathA(%x)   pathB(%x)
-//            |       |
-//            \______/
-//               |
-//             merge
-//   %frozen = phi [ %x, %pathA ], [ %x, %pathB ]
-
-static void lowerFreeze(FreezeInst *FI) {
-  Type *Ty = FI->getType();
-  LLVMContext &Ctx = FI->getContext();
-  BasicBlock *OrigBB = FI->getParent();
-  Value *Input = FI->getOperand(0);
-  Function *F = FI->getFunction();
-
-  // Split the block to isolate the freeze instruction
-  BasicBlock *MergeBB = OrigBB->splitBasicBlock(FI->getNextNode(), "merge");
-
-  // Remove the unconditional branch inserted by splitBasicBlock
-  OrigBB->getTerminator()->eraseFromParent();
-  BasicBlock *PathA = BasicBlock::Create(Ctx, "pathA", F, MergeBB);
-  BasicBlock *PathB = BasicBlock::Create(Ctx, "pathB", F, MergeBB);
-
-  IRBuilder<> Builder(OrigBB);
-  Builder.CreateCondBr(ConstantInt::getTrue(Ctx), PathA, PathB);
-
-  IRBuilder<> BuilderA(PathA);
-  BuilderA.CreateBr(MergeBB);
-  IRBuilder<> BuilderB(PathB);
-  BuilderB.CreateBr(MergeBB);
-
-  IRBuilder<> BuilderMerge(&MergeBB->front());
-  PHINode *Phi = BuilderMerge.CreatePHI(Ty, 2, "frozen");
-  Phi->addIncoming(Input, PathA);
-  Phi->addIncoming(Input, PathB);
-
-  FI->replaceAllUsesWith(Phi);
-}
-
 static void legalizeFreeze(Instruction &I,
                            SmallVectorImpl<Instruction *> &ToRemove,
                            DenseMap<Value *, Value *>) {
@@ -77,12 +29,7 @@ static void legalizeFreeze(Instruction &I,
     return;
 
   Value *Input = FI->getOperand(0);
-
-  if (isGuaranteedNotToBeUndefOrPoison(Input))
-    FI->replaceAllUsesWith(Input);
-  else
-    lowerFreeze(FI);
-
+  FI->replaceAllUsesWith(Input);
   ToRemove.push_back(FI);
 }
 
diff --git a/llvm/test/CodeGen/DirectX/legalize-freeze.ll b/llvm/test/CodeGen/DirectX/legalize-freeze.ll
index edc4de3d2e36b..29446adeef215 100644
--- a/llvm/test/CodeGen/DirectX/legalize-freeze.ll
+++ b/llvm/test/CodeGen/DirectX/legalize-freeze.ll
@@ -7,74 +7,10 @@ define i32 @test_remove_freeze(i32 %x) {
 ; CHECK-LABEL: define i32 @test_remove_freeze(
 ; CHECK-SAME: i32 [[X:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    br i1 true, label %[[PATHA:.*]], label %[[PATHB:.*]]
-; CHECK:       [[PATHA]]:
-; CHECK-NEXT:    br label %[[MERGE:.*]]
-; CHECK:       [[PATHB]]:
-; CHECK-NEXT:    br label %[[MERGE]]
-; CHECK:       [[MERGE]]:
-; CHECK-NEXT:    [[FROZEN:%.*]] = phi i32 [ [[X]], %[[PATHA]] ], [ [[X]], %[[PATHB]] ]
-; CHECK-NEXT:    [[Y:%.*]] = add i32 [[FROZEN]], 1
-; CHECK-NEXT:    ret i32 [[Y]]
-;
-entry:
-  %f = freeze i32 %x
-  %y = add i32 %f, 1
-  ret i32 %y
-}
-
-define i32 @test_remove_freeze_safe(i32 %x) {
-; CHECK-LABEL: define i32 @test_remove_freeze_safe(
-; CHECK-SAME: i32 [[X:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[FROZEN:%.*]] = add i32 1, 0
-; CHECK-NEXT:    [[Y:%.*]] = add i32 [[FROZEN]], 1
-; CHECK-NEXT:    ret i32 [[Y]]
-;
-entry:
-  %safe = add i32 1, 0
-  %f = freeze i32 %safe
-  %y = add i32 %f, 1
-  ret i32 %y
-}
-
-define i32 @test_freeze_poison() {
-; CHECK-LABEL: define i32 @test_freeze_poison() {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[X1:%.*]] = select i1 true, i32 42, i32 poison
-; CHECK-NEXT:    br i1 true, label %[[PATHA:.*]], label %[[PATHB:.*]]
-; CHECK:       [[PATHA]]:
-; CHECK-NEXT:    br label %[[MERGE:.*]]
-; CHECK:       [[PATHB]]:
-; CHECK-NEXT:    br label %[[MERGE]]
-; CHECK:       [[MERGE]]:
-; CHECK-NEXT:    [[X:%.*]] = phi i32 [ [[X1]], %[[PATHA]] ], [ [[X1]], %[[PATHB]] ]
 ; CHECK-NEXT:    [[Y:%.*]] = add i32 [[X]], 1
 ; CHECK-NEXT:    ret i32 [[Y]]
 ;
 entry:
-  %x = select i1 true, i32 42, i32 poison
-  %f = freeze i32 %x
-  %y = add i32 %f, 1
-  ret i32 %y
-}
-
-define i32 @test_freeze_undef() {
-; CHECK-LABEL: define i32 @test_freeze_undef() {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[X:%.*]] = select i1 undef, i32 42, i32 2
-; CHECK-NEXT:    br i1 true, label %[[PATHA:.*]], label %[[PATHB:.*]]
-; CHECK:       [[PATHA]]:
-; CHECK-NEXT:    br label %[[MERGE:.*]]
-; CHECK:       [[PATHB]]:
-; CHECK-NEXT:    br label %[[MERGE]]
-; CHECK:       [[MERGE]]:
-; CHECK-NEXT:    [[FROZEN:%.*]] = phi i32 [ [[X]], %[[PATHA]] ], [ [[X]], %[[PATHB]] ]
-; CHECK-NEXT:    [[Y:%.*]] = add i32 [[FROZEN]], 1
-; CHECK-NEXT:    ret i32 [[Y]]
-;
-entry:
-  %x = select i1 undef, i32 42, i32 2
   %f = freeze i32 %x
   %y = add i32 %f, 1
   ret i32 %y



More information about the llvm-commits mailing list