Skip to content

[SCCP] Remove masking operations #142736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 48 additions & 22 deletions llvm/lib/Transforms/Utils/SCCPSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/Analysis/ValueLatticeUtils.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/PatternMatch.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
Expand All @@ -30,6 +31,7 @@
#include <vector>

using namespace llvm;
using namespace PatternMatch;

#define DEBUG_TYPE "sccp"

Expand Down Expand Up @@ -83,20 +85,28 @@ bool SCCPSolver::tryToReplaceWithConstant(Value *V) {
return true;
}

/// Helper for getting ranges from \p Solver. Instructions inserted during
/// simplification are unavailable in the solver, so we return a full range for
/// them.
static ConstantRange getRange(Value *Op, SCCPSolver &Solver,
const SmallPtrSetImpl<Value *> &InsertedValues) {
if (auto *Const = dyn_cast<Constant>(Op))
return Const->toConstantRange();
if (InsertedValues.contains(Op)) {
unsigned Bitwidth = Op->getType()->getScalarSizeInBits();
return ConstantRange::getFull(Bitwidth);
}
return Solver.getLatticeValueFor(Op).asConstantRange(Op->getType(),
/*UndefAllowed=*/false);
}

/// Try to use \p Inst's value range from \p Solver to infer the NUW flag.
static bool refineInstruction(SCCPSolver &Solver,
const SmallPtrSetImpl<Value *> &InsertedValues,
Instruction &Inst) {
bool Changed = false;
auto GetRange = [&Solver, &InsertedValues](Value *Op) {
if (auto *Const = dyn_cast<Constant>(Op))
return Const->toConstantRange();
if (InsertedValues.contains(Op)) {
unsigned Bitwidth = Op->getType()->getScalarSizeInBits();
return ConstantRange::getFull(Bitwidth);
}
return Solver.getLatticeValueFor(Op).asConstantRange(
Op->getType(), /*UndefAllowed=*/false);
return getRange(Op, Solver, InsertedValues);
};

if (isa<OverflowingBinaryOperator>(Inst)) {
Expand Down Expand Up @@ -167,16 +177,8 @@ static bool replaceSignedInst(SCCPSolver &Solver,
SmallPtrSetImpl<Value *> &InsertedValues,
Instruction &Inst) {
// Determine if a signed value is known to be >= 0.
auto isNonNegative = [&Solver](Value *V) {
// If this value was constant-folded, it may not have a solver entry.
// Handle integers. Otherwise, return false.
if (auto *C = dyn_cast<Constant>(V)) {
auto *CInt = dyn_cast<ConstantInt>(C);
return CInt && !CInt->isNegative();
}
const ValueLatticeElement &IV = Solver.getLatticeValueFor(V);
return IV.isConstantRange(/*UndefAllowed=*/false) &&
IV.getConstantRange().isAllNonNegative();
auto isNonNegative = [&Solver, &InsertedValues](Value *V) {
return getRange(V, Solver, InsertedValues).isAllNonNegative();
};

Instruction *NewInst = nullptr;
Expand All @@ -185,7 +187,7 @@ static bool replaceSignedInst(SCCPSolver &Solver,
case Instruction::SExt: {
// If the source value is not negative, this is a zext/uitofp.
Value *Op0 = Inst.getOperand(0);
if (InsertedValues.count(Op0) || !isNonNegative(Op0))
if (!isNonNegative(Op0))
return false;
NewInst = CastInst::Create(Inst.getOpcode() == Instruction::SExt
? Instruction::ZExt
Expand All @@ -197,7 +199,7 @@ static bool replaceSignedInst(SCCPSolver &Solver,
case Instruction::AShr: {
// If the shifted value is not negative, this is a logical shift right.
Value *Op0 = Inst.getOperand(0);
if (InsertedValues.count(Op0) || !isNonNegative(Op0))
if (!isNonNegative(Op0))
return false;
NewInst = BinaryOperator::CreateLShr(Op0, Inst.getOperand(1), "", Inst.getIterator());
NewInst->setIsExact(Inst.isExact());
Expand All @@ -207,8 +209,7 @@ static bool replaceSignedInst(SCCPSolver &Solver,
case Instruction::SRem: {
// If both operands are not negative, this is the same as udiv/urem.
Value *Op0 = Inst.getOperand(0), *Op1 = Inst.getOperand(1);
if (InsertedValues.count(Op0) || InsertedValues.count(Op1) ||
!isNonNegative(Op0) || !isNonNegative(Op1))
if (!isNonNegative(Op0) || !isNonNegative(Op1))
return false;
auto NewOpcode = Inst.getOpcode() == Instruction::SDiv ? Instruction::UDiv
: Instruction::URem;
Expand All @@ -232,6 +233,26 @@ static bool replaceSignedInst(SCCPSolver &Solver,
return true;
}

/// Try to use \p Inst's value range from \p Solver to simplify it.
static Value *simplifyInstruction(SCCPSolver &Solver,
SmallPtrSetImpl<Value *> &InsertedValues,
Instruction &Inst) {
auto GetRange = [&Solver, &InsertedValues](Value *Op) {
return getRange(Op, Solver, InsertedValues);
};

Value *X;
const APInt *RHSC;
// Remove masking operations.
if (match(&Inst, m_And(m_Value(X), m_LowBitMask(RHSC)))) {
ConstantRange LRange = GetRange(Inst.getOperand(0));
if (LRange.getUnsignedMax().ule(*RHSC))
return X;
}

return nullptr;
}

bool SCCPSolver::simplifyInstsInBlock(BasicBlock &BB,
SmallPtrSetImpl<Value *> &InsertedValues,
Statistic &InstRemovedStat,
Expand All @@ -251,6 +272,11 @@ bool SCCPSolver::simplifyInstsInBlock(BasicBlock &BB,
++InstReplacedStat;
} else if (refineInstruction(*this, InsertedValues, Inst)) {
MadeChanges = true;
} else if (auto *V = simplifyInstruction(*this, InsertedValues, Inst)) {
Inst.replaceAllUsesWith(V);
Inst.eraseFromParent();
++InstRemovedStat;
MadeChanges = true;
}
}
return MadeChanges;
Expand Down
51 changes: 40 additions & 11 deletions llvm/test/Transforms/SCCP/conditions-ranges-with-undef.ll
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ false:

declare void @use.i32(i32)

; It is not allowed to use the range information from the condition to remove
; %a.127 = and ... in the true block, as %a could be undef.
define void @val_undef_range() {
; CHECK-LABEL: @val_undef_range(
; CHECK-NEXT: entry:
Expand All @@ -46,8 +44,7 @@ define void @val_undef_range() {
; CHECK-NEXT: br i1 [[BC_1]], label [[TRUE:%.*]], label [[FALSE:%.*]]
; CHECK: true:
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[A_127:%.*]] = and i32 [[A]], 127
; CHECK-NEXT: call void @use.i32(i32 [[A_127]])
; CHECK-NEXT: call void @use.i32(i32 [[A]])
; CHECK-NEXT: ret void
; CHECK: false:
; CHECK-NEXT: ret void
Expand Down Expand Up @@ -82,8 +79,7 @@ define void @val_singlecrfromundef_range(i1 %cond) {
; CHECK-NEXT: br label [[TRUE:%.*]]
; CHECK: true:
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[P_127:%.*]] = and i32 10, 127
; CHECK-NEXT: call void @use.i32(i32 [[P_127]])
; CHECK-NEXT: call void @use.i32(i32 10)
; CHECK-NEXT: ret void
;
entry:
Expand Down Expand Up @@ -113,9 +109,6 @@ false:
ret void
}


; It is not allowed to use the information from the condition ([0, 128))
; to remove a.127.2 = and i32 %p, 127, as %p might be undef.
define void @val_undef_to_cr_to_overdef_range(i32 %a, i1 %cond) {
; CHECK-LABEL: @val_undef_to_cr_to_overdef_range(
; CHECK-NEXT: entry:
Expand All @@ -131,8 +124,7 @@ define void @val_undef_to_cr_to_overdef_range(i32 %a, i1 %cond) {
; CHECK-NEXT: br i1 [[BC_1]], label [[TRUE:%.*]], label [[FALSE:%.*]]
; CHECK: true:
; CHECK-NEXT: call void @use(i1 false)
; CHECK-NEXT: [[P_127:%.*]] = and i32 [[P]], 127
; CHECK-NEXT: call void @use.i32(i32 [[P_127]])
; CHECK-NEXT: call void @use.i32(i32 [[P]])
; CHECK-NEXT: ret void
; CHECK: false:
; CHECK-NEXT: ret void
Expand Down Expand Up @@ -164,6 +156,43 @@ false:
ret void
}

; It is not allowed to use the range information from the condition to remove
; %p.127 = and i32 %p, 127, as %p could be undef.
define void @masked_incoming_val_with_undef(i32 %a, i1 %cond) {
; CHECK-LABEL: @masked_incoming_val_with_undef(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[A_127:%.*]] = and i32 [[A:%.*]], 127
; CHECK-NEXT: br i1 [[COND:%.*]], label [[INC1:%.*]], label [[INC2:%.*]]
; CHECK: inc1:
; CHECK-NEXT: br label [[IF:%.*]]
; CHECK: inc2:
; CHECK-NEXT: br label [[IF]]
; CHECK: if:
; CHECK-NEXT: [[P:%.*]] = phi i32 [ [[A_127]], [[INC1]] ], [ undef, [[INC2]] ]
; CHECK-NEXT: [[P_127:%.*]] = and i32 [[P]], 127
; CHECK-NEXT: call void @use.i32(i32 [[P_127]])
; CHECK-NEXT: ret void
;
entry:
%a.127 = and i32 %a, 127
br i1 %cond, label %inc1, label %inc2

inc1:
br label %if

inc2:
br label %if

if:
%p = phi i32 [ %a.127, %inc1 ], [ undef, %inc2 ]
%p.127 = and i32 %p, 127
call void @use.i32(i32 %p.127)
ret void

false:
ret void
}

; All uses of %p can be replaced by a constant (10), we are allowed to use it
; as a bound too.
define void @bound_singlecrfromundef(i32 %a, i1 %cond) {
Expand Down
5 changes: 2 additions & 3 deletions llvm/test/Transforms/SCCP/range-and.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --verbose
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes=sccp %s | FileCheck %s

declare void @use(i1)
Expand Down Expand Up @@ -140,9 +140,8 @@ define i64 @constant_range_and_255_100(i1 %cond, i64 %a) {
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: [[P:%.*]] = phi i64 [ [[R_1]], [[BB1]] ], [ [[R_2]], [[BB2]] ]
; CHECK-NEXT: [[P_AND:%.*]] = and i64 [[P]], 255
; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: ret i64 [[P_AND]]
; CHECK-NEXT: ret i64 [[P]]
;
entry:
br i1 %cond, label %bb1, label %bb2
Expand Down
3 changes: 1 addition & 2 deletions llvm/test/Transforms/SCCP/range-with-undef.ll
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ define i8 @test_binop(i1 %cond, i8 %a) {
; CHECK-NEXT: [[A_EXT:%.*]] = zext i8 [[A]] to i16
; CHECK-NEXT: br label %[[JOIN]]
; CHECK: [[JOIN]]:
; CHECK-NEXT: [[PHI:%.*]] = phi i16 [ undef, %[[ENTRY]] ], [ [[A_EXT]], %[[IF]] ]
; CHECK-NEXT: [[AND:%.*]] = and i16 [[PHI]], -1
; CHECK-NEXT: [[AND:%.*]] = phi i16 [ undef, %[[ENTRY]] ], [ [[A_EXT]], %[[IF]] ]
; CHECK-NEXT: [[TRUNC:%.*]] = trunc i16 [[AND]] to i8
; CHECK-NEXT: ret i8 [[TRUNC]]
;
Expand Down
Loading