Skip to content

Commit e45f0aa

Browse files
committed
Decompose Xors that are fed to GEPs
NOTE: This patch is not to be merged, just for evaluation.
1 parent 5df9658 commit e45f0aa

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed

llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@
160160
#include "llvm/ADT/DenseMap.h"
161161
#include "llvm/ADT/DepthFirstIterator.h"
162162
#include "llvm/ADT/SmallVector.h"
163+
#include "llvm/Analysis/AssumptionCache.h"
163164
#include "llvm/Analysis/LoopInfo.h"
164165
#include "llvm/Analysis/MemoryBuiltins.h"
165166
#include "llvm/Analysis/TargetLibraryInfo.h"
@@ -198,6 +199,8 @@
198199
using namespace llvm;
199200
using namespace llvm::PatternMatch;
200201

202+
#define DEBUG_TYPE "separate-offset-gep"
203+
201204
static cl::opt<bool> DisableSeparateConstOffsetFromGEP(
202205
"disable-separate-const-offset-from-gep", cl::init(false),
203206
cl::desc("Do not separate the constant offset from a GEP instruction"),
@@ -484,6 +487,9 @@ class SeparateConstOffsetFromGEP {
484487

485488
DenseMap<ExprKey, SmallVector<Instruction *, 2>> DominatingAdds;
486489
DenseMap<ExprKey, SmallVector<Instruction *, 2>> DominatingSubs;
490+
491+
bool decomposeXor(Function &F);
492+
Value *tryFoldXorToOrDisjoint(Instruction &I);
487493
};
488494

489495
} // end anonymous namespace
@@ -1162,6 +1168,162 @@ bool SeparateConstOffsetFromGEP::splitGEP(GetElementPtrInst *GEP) {
11621168
return true;
11631169
}
11641170

1171+
bool SeparateConstOffsetFromGEP::decomposeXor(Function &F) {
1172+
bool FunctionChanged = false;
1173+
SmallVector<std::pair<Instruction *, Value *>, 16> ReplacementsToMake;
1174+
1175+
for (BasicBlock &BB : F) {
1176+
for (Instruction &I : BB) {
1177+
if (I.getOpcode() == Instruction::Xor) {
1178+
if (Value *Replacement = tryFoldXorToOrDisjoint(I)) {
1179+
ReplacementsToMake.push_back({&I, Replacement});
1180+
FunctionChanged = true;
1181+
}
1182+
}
1183+
}
1184+
}
1185+
1186+
if (!ReplacementsToMake.empty()) {
1187+
LLVM_DEBUG(dbgs() << "Applying " << ReplacementsToMake.size()
1188+
<< " XOR->OR Disjoint replacements in " << F.getName()
1189+
<< "\n");
1190+
for (auto &Pair : ReplacementsToMake) {
1191+
Pair.first->replaceAllUsesWith(Pair.second);
1192+
}
1193+
for (auto &Pair : ReplacementsToMake) {
1194+
Pair.first->eraseFromParent();
1195+
}
1196+
}
1197+
1198+
return FunctionChanged;
1199+
}
1200+
1201+
static llvm::Instruction *findClosestSequentialXor(Value *A, Instruction &I) {
1202+
llvm::Instruction *ClosestUser = nullptr;
1203+
for (llvm::User *User : A->users()) {
1204+
if (auto *UserInst = llvm::dyn_cast<llvm::Instruction>(User)) {
1205+
if (UserInst->getOpcode() != Instruction::Xor || UserInst == &I)
1206+
continue;
1207+
if (!ClosestUser) {
1208+
ClosestUser = UserInst;
1209+
} else {
1210+
// Compare instruction positions.
1211+
if (UserInst->comesBefore(ClosestUser)) {
1212+
ClosestUser = UserInst;
1213+
}
1214+
}
1215+
}
1216+
}
1217+
return ClosestUser;
1218+
}
1219+
1220+
/// Try to transform I = xor(A, C1) into or disjoint(Y, C2)
1221+
/// where Y = xor(A, C0) is another existing instruction dominating I,
1222+
/// C2 = C0 ^ C1, and A is known to be disjoint with C2.
1223+
///
1224+
/// @param I The XOR instruction being visited.
1225+
/// @return The replacement Value* if successful, nullptr otherwise.
1226+
Value *SeparateConstOffsetFromGEP::tryFoldXorToOrDisjoint(Instruction &I) {
1227+
assert(I.getOpcode() == Instruction::Xor && "Instruction must be XOR");
1228+
1229+
// Check if I has at least one GEP user.
1230+
bool HasGepUser = false;
1231+
for (User *U : I.users()) {
1232+
if (isa<GetElementPtrInst>(U)) {
1233+
HasGepUser = true;
1234+
break;
1235+
}
1236+
}
1237+
// If no user is a GEP instruction, abort the transformation.
1238+
if (!HasGepUser) {
1239+
LLVM_DEBUG(
1240+
dbgs() << "SeparateConstOffsetFromGEP: Skipping XOR->OR DISJOINT for "
1241+
<< I << " because it has no GEP users.\n");
1242+
return nullptr;
1243+
}
1244+
1245+
Value *Op0 = I.getOperand(0);
1246+
Value *Op1 = I.getOperand(1);
1247+
ConstantInt *C1 = dyn_cast<ConstantInt>(Op1);
1248+
Value *A = Op0;
1249+
1250+
// Bail out of there is not constant operand.
1251+
if (!C1) {
1252+
C1 = dyn_cast<ConstantInt>(Op0);
1253+
if (!C1)
1254+
return nullptr;
1255+
A = Op1;
1256+
}
1257+
1258+
if (isa<UndefValue>(A))
1259+
return nullptr;
1260+
1261+
APInt C1_APInt = C1->getValue();
1262+
unsigned BitWidth = C1_APInt.getBitWidth();
1263+
Type *Ty = I.getType();
1264+
1265+
// --- Step 2: Find Dominating Y = xor A, C0 ---
1266+
Instruction *FoundUserInst = nullptr; // Instruction Y
1267+
APInt C0_APInt;
1268+
1269+
auto UserInst = findClosestSequentialXor(A, I);
1270+
1271+
BinaryOperator *UserBO = cast<BinaryOperator>(UserInst);
1272+
Value *UserOp0 = UserBO->getOperand(0);
1273+
Value *UserOp1 = UserBO->getOperand(1);
1274+
ConstantInt *UserC = nullptr;
1275+
if (UserOp0 == A)
1276+
UserC = dyn_cast<ConstantInt>(UserOp1);
1277+
else if (UserOp1 == A)
1278+
UserC = dyn_cast<ConstantInt>(UserOp0);
1279+
if (UserC) {
1280+
if (DT->dominates(UserInst, &I)) {
1281+
FoundUserInst = UserInst;
1282+
C0_APInt = UserC->getValue();
1283+
}
1284+
}
1285+
if (!FoundUserInst)
1286+
return nullptr;
1287+
1288+
// Calculate C2.
1289+
APInt C2_APInt = C0_APInt ^ C1_APInt;
1290+
1291+
// Check Disjointness A & C2 == 0.
1292+
KnownBits KnownA(BitWidth);
1293+
AssumptionCache *AC = nullptr;
1294+
computeKnownBits(A, KnownA, *DL, 0, AC, &I, DT);
1295+
1296+
if ((KnownA.Zero & C2_APInt) != C2_APInt)
1297+
return nullptr;
1298+
1299+
IRBuilder<> Builder(&I);
1300+
Builder.SetInsertPoint(&I); // Access Builder directly
1301+
Constant *C2_Const = ConstantInt::get(Ty, C2_APInt);
1302+
Twine Name = I.getName(); // Create Twine explicitly
1303+
Value *NewOr = BinaryOperator::CreateDisjointOr(FoundUserInst, C2_Const, Name,
1304+
I.getIterator());
1305+
// Transformation Conditions Met.
1306+
LLVM_DEBUG(dbgs() << "SeparateConstOffsetFromGEP: Replacing " << I
1307+
<< " (used by GEP) with " << *NewOr << " based on "
1308+
<< *FoundUserInst << "\n");
1309+
1310+
#if 0
1311+
// Preserve metadata
1312+
if (Instruction *NewOrInst = dyn_cast<Instruction>(NewOr)) {
1313+
NewOrInst->copyMetadata(I);
1314+
} else {
1315+
assert(false && "CreateNUWOr did not return an Instruction");
1316+
if (NewOr)
1317+
NewOr->deleteValue();
1318+
return nullptr;
1319+
}
1320+
#endif
1321+
1322+
// Return the replacement value. runOnFunction will handle replacement &
1323+
// deletion.
1324+
return NewOr;
1325+
}
1326+
11651327
bool SeparateConstOffsetFromGEPLegacyPass::runOnFunction(Function &F) {
11661328
if (skipFunction(F))
11671329
return false;
@@ -1181,6 +1343,10 @@ bool SeparateConstOffsetFromGEP::run(Function &F) {
11811343

11821344
DL = &F.getDataLayout();
11831345
bool Changed = false;
1346+
1347+
// Decompose xor in to "or disjoint" if possible.
1348+
Changed |= decomposeXor(F);
1349+
11841350
for (BasicBlock &B : F) {
11851351
if (!DT->isReachableFromEntry(&B))
11861352
continue;

0 commit comments

Comments
 (0)