[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