Compare commits

...

2 Commits

Author SHA1 Message Date
Elad 4f37124c1d LLVM: Add short fallback for get_known_bits
Supports: when value is the immediate result of either a bitwise OR operation or a bitwise AND when either operands is a constant.

Prevents some false positives when the value has PHI nodes in its ancestors.
2026-07-21 18:42:57 +03:00
RipleyTom 24e1ce4ceb Fix: get_known_bits fix 2026-07-21 17:44:40 +03:00
2 changed files with 180 additions and 1 deletions
+134
View File
@@ -610,4 +610,138 @@ void cpu_translator::erase_stores(llvm::ArrayRef<llvm::Value*> args)
}
}
llvm::KnownBits cpu_translator::get_known_bits_fallback(llvm::Value* value)
{
// TODO: Improve it - add support for integer addition/subtraction and more stuff
const auto type = value->getType();
if (!type->isVectorTy())
{
if (const auto it = llvm::dyn_cast<llvm::IntegerType>(type))
{
if (auto bin_inst = llvm::dyn_cast<llvm::BinaryOperator>(value))
{
llvm::Value* lhs = ensure(bin_inst->getOperand(0));
llvm::Value* rhs = ensure(bin_inst->getOperand(1));
llvm::ConstantInt* constant_value = llvm::dyn_cast<llvm::ConstantInt>(rhs) ? llvm::dyn_cast<llvm::ConstantInt>(rhs) : llvm::dyn_cast<llvm::ConstantInt>(lhs);
if (!constant_value)
{
return llvm::KnownBits(type->getScalarSizeInBits());
}
if (bin_inst->getOpcode() == llvm::Instruction::Or)
{
llvm::KnownBits ret(type->getScalarSizeInBits());
ret.One = constant_value->getValue();
return ret;
}
if (bin_inst->getOpcode() == llvm::Instruction::And)
{
llvm::KnownBits ret(type->getScalarSizeInBits());
ret.Zero = constant_value->getValue();
ret.Zero.flipAllBits();
return ret;
}
return llvm::KnownBits(type->getScalarSizeInBits());
}
}
fmt::throw_exception("Bad KnownBits type: i%ux", type->getScalarSizeInBits());
}
if (auto v = llvm::cast<llvm::FixedVectorType>(type); v->getScalarSizeInBits() * v->getNumElements() != 128)
{
// Unsupported
return llvm::KnownBits(type->getScalarSizeInBits());
}
const auto cv = llvm::dyn_cast<llvm::ConstantDataVector>(value);
if (!cv)
{
if (llvm::isa<llvm::ConstantAggregateZero>(value))
{
llvm::KnownBits ret(type->getScalarSizeInBits());
ret.Zero.setAllBits();
return ret;
}
}
const auto original_value = peek_through_bitcasts(value);
const auto original_type = original_value->getType();
auto bin_inst = llvm::dyn_cast<llvm::BinaryOperator>(original_value);
if (!bin_inst)
{
return llvm::KnownBits(type->getScalarSizeInBits());
}
llvm::Value* lhs = ensure(bin_inst->getOperand(0));
llvm::Value* rhs = ensure(bin_inst->getOperand(1));
llvm::Value* constant_value = llvm::dyn_cast<llvm::ConstantDataVector>(rhs) ? llvm::dyn_cast<llvm::ConstantDataVector>(rhs) : llvm::dyn_cast<llvm::ConstantDataVector>(lhs);
if (!constant_value)
{
return llvm::KnownBits(value->getType()->getScalarSizeInBits());
}
const auto [ok, v128_const] = get_const_vector(constant_value, -1);
ensure(ok);
llvm::APInt dest{};
auto combine_bits = [&](const auto& array, u32 size)
{
auto first = +array[0];
for (u32 i = 0; i < size; i++)
{
first &= +array[i];
}
return first;
};
if (type->getScalarType()->isIntegerTy(8))
{
dest = llvm::APInt(8, combine_bits(v128_const._u8, 16));
}
else if (type->getScalarType()->isIntegerTy(16))
{
dest = llvm::APInt(16, combine_bits(v128_const._u16, 8));
}
else if (type->getScalarType()->isIntegerTy(32))
{
dest = llvm::APInt(32, combine_bits(v128_const._u32, 4));
}
else // if (type->getScalarType()->isIntegerTy(64))
{
return llvm::KnownBits(type->getScalarSizeInBits());
}
if (bin_inst->getOpcode() == llvm::Instruction::Or)
{
llvm::KnownBits ret(type->getScalarSizeInBits());
ret.One = dest;
return ret;
}
if (bin_inst->getOpcode() == llvm::Instruction::And)
{
llvm::KnownBits ret(type->getScalarSizeInBits());
ret.Zero = dest;
ret.Zero.flipAllBits();
return ret;
}
return llvm::KnownBits(type->getScalarSizeInBits());
}
#endif
+46 -1
View File
@@ -4271,10 +4271,55 @@ template <typename T1, typename T2, typename T3>
template <typename T = v128>
llvm::Constant* make_const_vector(T, llvm::Type*, u32 = __builtin_LINE());
// IR is emitted in a single pass: phi nodes may still be missing their back-edge incoming
// values, so any known bits computeKnownBits derives through a phi are unsound for the
// final IR. Whether a phi is complete cannot be queried (the CFG edges from not-yet-emitted
// predecessors don't exist either), so reject every value whose bits may derive from a phi.
static bool is_known_bits_safe(llvm::Value* value)
{
llvm::SmallPtrSet<const llvm::Value*, 32> visited;
llvm::SmallVector<const llvm::Value*, 32> worklist{value};
while (!worklist.empty())
{
const llvm::Value* v = worklist.pop_back_val();
if (!visited.insert(v).second)
{
continue;
}
if (llvm::isa<llvm::PHINode>(v) || visited.size() > 256)
{
return false;
}
// Loads don't propagate operand bits; constants and arguments are leaves
if (auto i = llvm::dyn_cast<llvm::Instruction>(v); i && !llvm::isa<llvm::LoadInst>(i))
{
for (const llvm::Use& op : i->operands())
{
worklist.push_back(op.get());
}
}
}
return true;
}
llvm::KnownBits get_known_bits_fallback(llvm::Value* value);
template <typename T>
llvm::KnownBits get_known_bits(T a)
{
return llvm::computeKnownBits(a.eval(m_ir), m_module->getDataLayout());
llvm::Value* value = a.eval(m_ir);
if (!is_known_bits_safe(value))
{
return get_known_bits_fallback(value);
}
return llvm::computeKnownBits(value, m_module->getDataLayout());
}
template <typename T>