1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
/*========================== begin_copyright_notice ============================
Copyright (C) 2025 Intel Corporation
SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/
#include "SinkPointerConstAdd.h"
#include "common/LLVMWarningsPush.hpp"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/IRBuilder.h"
#include "common/LLVMWarningsPop.hpp"
// Simple pass which sinks constant add operations in pointer calculations
// It changes following pattern:
// %addr_part1 = <base1> + const
// %addr_part2 = <base2> + %addr_part1
// %ptr = inttoptr %addr_part2
// to:
// %addr_part1 = <base1> + <base2>
// %addr_part2 = <addr_part1> + const
// %ptr = inttoptr %addr_part2
// This helps other optimizations like GVN to eliminate redundant calculations.
using namespace llvm;
class SinkPointerConstAddPass : public llvm::FunctionPass {
public:
SinkPointerConstAddPass() : llvm::FunctionPass(ID) {
initializeSinkPointerConstAddPassPass(*llvm::PassRegistry::getPassRegistry());
}
virtual bool runOnFunction(llvm::Function &F) override;
virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { AU.setPreservesCFG(); }
virtual llvm::StringRef getPassName() const override { return "SinkPointerConstAdd"; }
static char ID;
private:
bool getConstantOffset(llvm::Value *value, int &offset);
};
#define PASS_FLAG "igc-sink-ptr-const-add"
#define PASS_DESCRIPTION "Sink pointer const add"
#define PASS_CFG_ONLY false
#define PASS_ANALYSIS false
IGC_INITIALIZE_PASS(SinkPointerConstAddPass, PASS_FLAG, PASS_DESCRIPTION, PASS_CFG_ONLY, PASS_ANALYSIS)
bool SinkPointerConstAddPass::getConstantOffset(llvm::Value *value, int &offset) {
// Recursively search for constant add operations - this will stop after the first const add found,
// and should be called repeatedly until no more const adds can be sunk.
if (value->getNumUses() > 1) {
return false; // We cannot sink constant add if the value is used more than once.
}
if (llvm::Instruction *instr = llvm::dyn_cast<llvm::Instruction>(value)) {
if (instr->getOpcode() == llvm::Instruction::Trunc || instr->getOpcode() == llvm::Instruction::ZExt ||
instr->getOpcode() == llvm::Instruction::SExt) {
// Skip cast instructions
llvm::Instruction *op = llvm::dyn_cast<llvm::Instruction>(instr->getOperand(0));
// This is a simple pass, only sink within the same basic block
if (op && instr->getParent() == op->getParent()) {
return getConstantOffset(instr->getOperand(0), offset);
} else {
return false;
}
} else if (instr->getOpcode() == llvm::Instruction::Add || instr->getOpcode() == llvm::Instruction::Sub) {
if (llvm::ConstantInt *constInt = llvm::dyn_cast<llvm::ConstantInt>(instr->getOperand(1))) {
offset = instr->getOpcode() == llvm::Instruction::Add ? offset + constInt->getSExtValue()
: offset - constInt->getSExtValue();
instr->replaceAllUsesWith(instr->getOperand(0));
instr->eraseFromParent();
return true;
} else if (llvm::ConstantInt *constInt = llvm::dyn_cast<llvm::ConstantInt>(instr->getOperand(0))) {
if (instr->getOpcode() == llvm::Instruction::Sub) {
// Cannot sink constant on the left side of subtraction
return false;
}
offset += constInt->getSExtValue();
instr->replaceAllUsesWith(instr->getOperand(1));
instr->eraseFromParent();
return true;
} else {
llvm::Instruction *op0 = llvm::dyn_cast<llvm::Instruction>(instr->getOperand(0));
llvm::Instruction *op1 = llvm::dyn_cast<llvm::Instruction>(instr->getOperand(1));
// This is a simple pass, only sink within the same basic block
return (op0 && instr->getParent() == op0->getParent() && getConstantOffset(op0, offset)) ? true
: (op1 && instr->getParent() == op1->getParent()) ? getConstantOffset(op1, offset)
: false;
}
}
}
return false;
}
bool SinkPointerConstAddPass::runOnFunction(llvm::Function &F) {
bool changed = false;
std::vector<llvm::IntToPtrInst *> intToPtrInsts;
intToPtrInsts.reserve(50);
// Collect all inttoptr instructions first
for (llvm::BasicBlock &BB : F) {
for (llvm::Instruction &inst : BB) {
if (llvm::IntToPtrInst *intrinsic = llvm::dyn_cast<llvm::IntToPtrInst>(&inst)) {
intToPtrInsts.push_back(intrinsic);
}
}
}
for (auto &intrinsic : intToPtrInsts) {
int offset = 0;
// Keep sinking constant adds until no more can be sunk
while (getConstantOffset(intrinsic->getOperand(0), offset)) {
changed = true;
}
// If we found any constant offset, create new pointer calculation
if (offset != 0) {
llvm::IRBuilder<> builder(intrinsic);
llvm::Value *newPtr =
builder.CreateIntToPtr(builder.CreateAdd(intrinsic->getOperand(0),
llvm::ConstantInt::get(intrinsic->getOperand(0)->getType(), offset)),
intrinsic->getType());
intrinsic->replaceAllUsesWith(newPtr);
intrinsic->eraseFromParent();
}
}
return changed;
}
char SinkPointerConstAddPass::ID = 0;
llvm::FunctionPass *createSinkPointerConstAddPass() { return new SinkPointerConstAddPass(); }
|