Skip to content

Commit 77044f9

Browse files
authored
[SeparateConstOffsetFromGEP] Decompose constant xor operand if possible (#150438)
Try to transform XOR(A, B+C) in to XOR(A,C) + B where XOR(A,C) is part of base for memory operations. This transformation can map these Xors in to better addressing mode and eventually decompose them in to geps.
1 parent be5554d commit 77044f9

File tree

2 files changed

+516
-4
lines changed

2 files changed

+516
-4
lines changed

llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,10 @@ class ConstantOffsetExtractor {
294294
bool CanTraceInto(bool SignExtended, bool ZeroExtended, BinaryOperator *BO,
295295
bool NonNegative);
296296

297+
/// Analyze XOR instruction to extract disjoint constant bits that behave
298+
/// like addition operations for improved address mode folding.
299+
APInt extractDisjointBitsFromXor(BinaryOperator *XorInst);
300+
297301
/// The path from the constant offset to the old GEP index. e.g., if the GEP
298302
/// index is "a * b + (c + 5)". After running function find, UserChain[0] will
299303
/// be the constant 5, UserChain[1] will be the subexpression "c + 5", and
@@ -596,6 +600,9 @@ APInt ConstantOffsetExtractor::find(Value *V, bool SignExtended,
596600
// Trace into subexpressions for more hoisting opportunities.
597601
if (CanTraceInto(SignExtended, ZeroExtended, BO, NonNegative))
598602
ConstantOffset = findInEitherOperand(BO, SignExtended, ZeroExtended);
603+
// Handle XOR with disjoint bits that can be treated as addition.
604+
else if (BO->getOpcode() == Instruction::Xor)
605+
ConstantOffset = extractDisjointBitsFromXor(BO);
599606
} else if (isa<TruncInst>(V)) {
600607
ConstantOffset =
601608
find(U->getOperand(0), SignExtended, ZeroExtended, NonNegative)
@@ -708,11 +715,20 @@ Value *ConstantOffsetExtractor::removeConstOffset(unsigned ChainIndex) {
708715
Value *NextInChain = removeConstOffset(ChainIndex - 1);
709716
Value *TheOther = BO->getOperand(1 - OpNo);
710717

711-
// If NextInChain is 0 and not the LHS of a sub, we can simplify the
712-
// sub-expression to be just TheOther.
713718
if (ConstantInt *CI = dyn_cast<ConstantInt>(NextInChain)) {
714-
if (CI->isZero() && !(BO->getOpcode() == Instruction::Sub && OpNo == 0))
715-
return TheOther;
719+
if (CI->isZero()) {
720+
// Custom XOR handling for disjoint bits - preserves original XOR
721+
// with non-disjoint constant bits.
722+
// TODO: The design should be updated to support partial constant
723+
// extraction.
724+
if (BO->getOpcode() == Instruction::Xor)
725+
return BO;
726+
727+
// If NextInChain is 0 and not the LHS of a sub, we can simplify the
728+
// sub-expression to be just TheOther.
729+
if (!(BO->getOpcode() == Instruction::Sub && OpNo == 0))
730+
return TheOther;
731+
}
716732
}
717733

718734
BinaryOperator::BinaryOps NewOp = BO->getOpcode();
@@ -743,6 +759,67 @@ Value *ConstantOffsetExtractor::removeConstOffset(unsigned ChainIndex) {
743759
return NewBO;
744760
}
745761

762+
/// Analyze XOR instruction to extract disjoint constant bits for address
763+
/// folding
764+
///
765+
/// This function identifies bits in an XOR constant operand that are disjoint
766+
/// from the base operand's known set bits. For these disjoint bits, XOR behaves
767+
/// identically to addition, allowing us to extract them as constant offsets
768+
/// that can be folded into addressing modes.
769+
///
770+
/// Transformation: `Base ^ Const` becomes `(Base ^ NonDisjointBits) +
771+
/// DisjointBits` where DisjointBits = Const & KnownZeros(Base)
772+
///
773+
/// Example with ptr having known-zero low bit:
774+
/// Original: `xor %ptr, 3` ; 3 = 0b11
775+
/// Analysis: DisjointBits = 3 & KnownZeros(%ptr) = 0b11 & 0b01 = 0b01
776+
/// Result: `(xor %ptr, 2) + 1` where 1 can be folded into address mode
777+
///
778+
/// \param XorInst The XOR binary operator to analyze
779+
/// \return APInt containing the disjoint bits that can be extracted as offset,
780+
/// or zero if no disjoint bits exist
781+
APInt ConstantOffsetExtractor::extractDisjointBitsFromXor(
782+
BinaryOperator *XorInst) {
783+
assert(XorInst && XorInst->getOpcode() == Instruction::Xor &&
784+
"Expected XOR instruction");
785+
786+
const unsigned BitWidth = XorInst->getType()->getScalarSizeInBits();
787+
Value *BaseOperand;
788+
ConstantInt *XorConstant;
789+
790+
// Match pattern: xor BaseOperand, Constant.
791+
if (!match(XorInst, m_Xor(m_Value(BaseOperand), m_ConstantInt(XorConstant))))
792+
return APInt::getZero(BitWidth);
793+
794+
// Compute known bits for the base operand.
795+
const SimplifyQuery SQ(DL);
796+
const KnownBits BaseKnownBits = computeKnownBits(BaseOperand, SQ);
797+
const APInt &ConstantValue = XorConstant->getValue();
798+
799+
// Identify disjoint bits: constant bits that are known zero in base.
800+
const APInt DisjointBits = ConstantValue & BaseKnownBits.Zero;
801+
802+
// Early exit if no disjoint bits found.
803+
if (DisjointBits.isZero())
804+
return APInt::getZero(BitWidth);
805+
806+
// Compute the remaining non-disjoint bits that stay in the XOR.
807+
const APInt NonDisjointBits = ConstantValue & ~DisjointBits;
808+
809+
// FIXME: Enhance XOR constant extraction to handle nested binary operations.
810+
// Currently we only extract disjoint bits from the immediate XOR constant,
811+
// but we could recursively process cases like:
812+
// xor (add %base, C1), C2 -> add %base, (C1 ^ disjoint_bits(C2))
813+
// This requires careful analysis to ensure the transformation preserves
814+
// semantics, particularly around sign extension and overflow behavior.
815+
816+
// Add the non-disjoint constant to the user chain for later transformation
817+
// This will replace the original constant in the XOR with the new
818+
// constant.
819+
UserChain.push_back(ConstantInt::get(XorInst->getType(), NonDisjointBits));
820+
return DisjointBits;
821+
}
822+
746823
/// A helper function to check if reassociating through an entry in the user
747824
/// chain would invalidate the GEP's nuw flag.
748825
static bool allowsPreservingNUW(const User *U) {

0 commit comments

Comments
 (0)