tools: add tms-bitaddr GEP scale and normalize tool
This commit is contained in:
1 parent
dbdb827c17
commit
93f90607bb
6 files changed
+356
No files matched your search
@@ -0,0 +1,316 @@
|
||||
// bitscale -- throwaway spike tool.
|
||||
//
|
||||
// Rewrites every non-constant getelementptr so that its byte offset is
|
||||
// multiplied by 8. The result is an IR in which every pointer value is a
|
||||
// *bit* address, while loads/stores still carry their normal LLVM byte-sized
|
||||
// types. A real TMS34010 backend would lower "load i8 at bit address A" as
|
||||
// MOVB [A] and "load i32 at bit address A" as MOVE [A],FS0-in-32-bit-mode.
|
||||
//
|
||||
// Build:
|
||||
// clang++-19 -std=c++17 $(llvm-config-19 --cxxflags) bitscale.cpp \
|
||||
// $(llvm-config-19 --ldflags --libs core irreader support) -o bitscale
|
||||
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/IR/GetElementPtrTypeIterator.h"
|
||||
#include "llvm/IR/Instructions.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/Operator.h"
|
||||
#include "llvm/IRReader/IRReader.h"
|
||||
#include "llvm/Support/SourceMgr.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
static unsigned GepShift = 3; // 3 for --scale, 0 for --normalize
|
||||
|
||||
struct Stats {
|
||||
unsigned geps = 0;
|
||||
unsigned const_geps = 0;
|
||||
unsigned vector_geps = 0;
|
||||
unsigned allocas = 0;
|
||||
unsigned memintrinsics = 0;
|
||||
unsigned vaarg = 0;
|
||||
unsigned byval_args = 0;
|
||||
unsigned indirect_calls = 0;
|
||||
unsigned loads = 0;
|
||||
unsigned stores = 0;
|
||||
unsigned ptr_subs = 0;
|
||||
unsigned const_ptr_subs = 0;
|
||||
};
|
||||
|
||||
static void count_constants(Value *V, Stats &S, unsigned depth = 0)
|
||||
{
|
||||
if (!V || depth > 64)
|
||||
return;
|
||||
auto *C = dyn_cast<Constant>(V);
|
||||
if (!C)
|
||||
return;
|
||||
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
|
||||
if (CE->getOpcode() == Instruction::Sub) {
|
||||
auto *L = dyn_cast<ConstantExpr>(CE->getOperand(0));
|
||||
auto *R = dyn_cast<ConstantExpr>(CE->getOperand(1));
|
||||
if (L && R && L->getOpcode() == Instruction::PtrToInt &&
|
||||
R->getOpcode() == Instruction::PtrToInt)
|
||||
++S.const_ptr_subs;
|
||||
}
|
||||
}
|
||||
if (auto *GEP = dyn_cast<GEPOperator>(C)) {
|
||||
++S.const_geps;
|
||||
count_constants(GEP->getPointerOperand(), S, depth + 1);
|
||||
for (auto &Op : GEP->indices())
|
||||
count_constants(Op.get(), S, depth + 1);
|
||||
return;
|
||||
}
|
||||
for (auto &Op : C->operands())
|
||||
count_constants(Op.get(), S, depth + 1);
|
||||
}
|
||||
|
||||
static void count_constant_geps(const Constant *C, Stats &S, unsigned depth = 0)
|
||||
{
|
||||
count_constants(const_cast<Constant *>(C), S, depth);
|
||||
}
|
||||
|
||||
static Stats collect(Module &M)
|
||||
{
|
||||
Stats S;
|
||||
for (Function &F : M) {
|
||||
for (Argument &A : F.args())
|
||||
if (A.hasAttribute(Attribute::ByVal))
|
||||
++S.byval_args;
|
||||
for (BasicBlock &BB : F) {
|
||||
for (Instruction &I : BB) {
|
||||
if (auto *GEP = dyn_cast<GetElementPtrInst>(&I)) {
|
||||
if (GEP->getType()->isVectorTy())
|
||||
++S.vector_geps;
|
||||
else
|
||||
++S.geps;
|
||||
}
|
||||
if (isa<AllocaInst>(&I))
|
||||
++S.allocas;
|
||||
if (isa<LoadInst>(&I))
|
||||
++S.loads;
|
||||
if (isa<StoreInst>(&I))
|
||||
++S.stores;
|
||||
if (auto *BO = dyn_cast<BinaryOperator>(&I)) {
|
||||
if (BO->getOpcode() == Instruction::Sub &&
|
||||
isa<PtrToIntOperator>(BO->getOperand(0)) &&
|
||||
isa<PtrToIntOperator>(BO->getOperand(1)))
|
||||
++S.ptr_subs;
|
||||
}
|
||||
if (auto *CB = dyn_cast<CallBase>(&I)) {
|
||||
if (Function *Callee = CB->getCalledFunction()) {
|
||||
if (Callee->isIntrinsic()) {
|
||||
StringRef N = Callee->getName();
|
||||
if (N.starts_with("llvm.memcpy") || N.starts_with("llvm.memmove") ||
|
||||
N.starts_with("llvm.memset"))
|
||||
++S.memintrinsics;
|
||||
if (N.starts_with("llvm.va_start") || N.starts_with("llvm.va_arg"))
|
||||
++S.vaarg;
|
||||
}
|
||||
} else {
|
||||
++S.indirect_calls;
|
||||
}
|
||||
for (unsigned i = 0; i < CB->arg_size(); ++i)
|
||||
if (CB->paramHasAttr(i, Attribute::ByVal))
|
||||
++S.byval_args;
|
||||
}
|
||||
for (Value *Op : I.operands())
|
||||
count_constants(Op, S);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (GlobalVariable &G : M.globals())
|
||||
if (G.hasInitializer())
|
||||
count_constant_geps(G.getInitializer(), S);
|
||||
return S;
|
||||
}
|
||||
|
||||
static bool scale_constant_gep(ConstantExpr *CE, const DataLayout &DL)
|
||||
{
|
||||
if (CE->getType()->isVectorTy())
|
||||
return false;
|
||||
|
||||
auto *GEP = cast<GEPOperator>(CE);
|
||||
SmallVector<Value *, 8> Indices;
|
||||
for (auto &Op : GEP->indices())
|
||||
Indices.push_back(Op.get());
|
||||
for (Value *Idx : Indices)
|
||||
if (!isa<ConstantInt>(Idx))
|
||||
return false; // cannot fold a non-constant constant-expression GEP
|
||||
|
||||
int64_t Off = DL.getIndexedOffsetInType(GEP->getSourceElementType(), Indices);
|
||||
Constant *Base = cast<Constant>(GEP->getPointerOperand());
|
||||
Constant *BaseInt = ConstantExpr::getPtrToInt(Base, Type::getInt32Ty(CE->getContext()));
|
||||
Constant *BitOff = ConstantInt::get(
|
||||
Type::getInt32Ty(CE->getContext()), Off * (1u << GepShift));
|
||||
Constant *BitAddr = ConstantExpr::getAdd(BaseInt, BitOff);
|
||||
Constant *New = ConstantExpr::getIntToPtr(BitAddr, CE->getType());
|
||||
CE->replaceAllUsesWith(New);
|
||||
return true;
|
||||
}
|
||||
|
||||
// base_int + (byte offset computed from the GEP's indices) << 3
|
||||
static bool scale_gep(GetElementPtrInst *GEP)
|
||||
{
|
||||
if (GEP->getType()->isVectorTy())
|
||||
return false;
|
||||
Value *Base = GEP->getPointerOperand();
|
||||
Type *PtrTy = GEP->getType();
|
||||
const DataLayout &DL = GEP->getModule()->getDataLayout();
|
||||
|
||||
IRBuilder<> B(GEP);
|
||||
Value *BaseInt = B.CreatePtrToInt(Base, B.getInt32Ty(), "bit.base");
|
||||
Value *ByteOff = ConstantInt::get(B.getInt32Ty(), 0);
|
||||
|
||||
for (auto GTI = gep_type_begin(GEP), GTE = gep_type_end(GEP); GTI != GTE; ++GTI) {
|
||||
Value *Idx = GTI.getOperand();
|
||||
Value *Idx32 = B.CreateSExtOrTrunc(Idx, B.getInt32Ty());
|
||||
if (StructType *STy = GTI.getStructTypeOrNull()) {
|
||||
unsigned FieldNo = cast<ConstantInt>(Idx)->getZExtValue();
|
||||
uint64_t FieldOff = DL.getStructLayout(STy)->getElementOffset(FieldNo);
|
||||
ByteOff = B.CreateAdd(ByteOff, ConstantInt::get(B.getInt32Ty(), FieldOff));
|
||||
} else {
|
||||
uint64_t Stride = GTI.getSequentialElementStride(DL);
|
||||
Value *Scaled = B.CreateMul(Idx32, ConstantInt::get(B.getInt32Ty(), Stride));
|
||||
ByteOff = B.CreateAdd(ByteOff, Scaled);
|
||||
}
|
||||
}
|
||||
|
||||
Value *BitOff = GepShift ? B.CreateShl(ByteOff, GepShift, "bit.off")
|
||||
: ByteOff;
|
||||
Value *BitAddr = B.CreateAdd(BaseInt, BitOff, "bit.addr");
|
||||
Value *NewPtr = B.CreateIntToPtr(BitAddr, PtrTy, "bit.ptr");
|
||||
|
||||
GEP->replaceAllUsesWith(NewPtr);
|
||||
GEP->eraseFromParent();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void collect_const_geps(Value *V, std::vector<ConstantExpr *> &Out,
|
||||
unsigned depth = 0)
|
||||
{
|
||||
if (!V || depth > 64)
|
||||
return;
|
||||
auto *C = dyn_cast<Constant>(V);
|
||||
if (!C)
|
||||
return;
|
||||
if (auto *CE = dyn_cast<ConstantExpr>(C)) {
|
||||
if (CE->getOpcode() == Instruction::GetElementPtr && !CE->getType()->isVectorTy()) {
|
||||
Out.push_back(CE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto &Op : C->operands())
|
||||
collect_const_geps(Op.get(), Out, depth + 1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
errs() << "usage: " << argv[0]
|
||||
<< " [--scale|--normalize] input.ll [output.ll]\n";
|
||||
return 2;
|
||||
}
|
||||
int Arg = 1;
|
||||
if (StringRef(argv[Arg]) == "--normalize") {
|
||||
GepShift = 0;
|
||||
++Arg;
|
||||
} else if (StringRef(argv[Arg]) == "--scale") {
|
||||
GepShift = 3;
|
||||
++Arg;
|
||||
}
|
||||
int Remaining = argc - Arg;
|
||||
if (Remaining != 1 && Remaining != 2) {
|
||||
errs() << "usage: " << argv[0]
|
||||
<< " [--scale|--normalize] input.ll [output.ll]\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
LLVMContext Ctx;
|
||||
SMDiagnostic Err;
|
||||
std::unique_ptr<Module> M = parseIRFile(argv[Arg], Err, Ctx);
|
||||
if (!M) {
|
||||
Err.print(argv[0], errs());
|
||||
return 1;
|
||||
}
|
||||
|
||||
Stats Before = collect(*M);
|
||||
|
||||
// Constant-expression GEPs (e.g. `"hello" + 2`) cannot be replaced by an
|
||||
// instruction, so rewrite them as constant inttoptr(add(ptrtoint, 8*off)).
|
||||
unsigned ConstScaled = 0;
|
||||
const DataLayout &DL = M->getDataLayout();
|
||||
bool Changed = true;
|
||||
while (Changed) {
|
||||
Changed = false;
|
||||
std::vector<ConstantExpr *> Work;
|
||||
for (GlobalVariable &G : M->globals())
|
||||
if (G.hasInitializer())
|
||||
collect_const_geps(G.getInitializer(), Work);
|
||||
for (Function &F : *M)
|
||||
for (BasicBlock &BB : F)
|
||||
for (Instruction &I : BB)
|
||||
for (Value *Op : I.operands())
|
||||
collect_const_geps(Op, Work);
|
||||
|
||||
std::set<ConstantExpr *> Seen;
|
||||
for (ConstantExpr *CE : Work) {
|
||||
if (!Seen.insert(CE).second)
|
||||
continue;
|
||||
if (scale_constant_gep(CE, DL)) {
|
||||
++ConstScaled;
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned Scaled = 0;
|
||||
for (Function &F : *M) {
|
||||
std::vector<GetElementPtrInst *> Work;
|
||||
for (BasicBlock &BB : F)
|
||||
for (Instruction &I : BB)
|
||||
if (auto *GEP = dyn_cast<GetElementPtrInst>(&I))
|
||||
Work.push_back(GEP);
|
||||
for (GetElementPtrInst *GEP : Work)
|
||||
if (scale_gep(GEP))
|
||||
++Scaled;
|
||||
}
|
||||
Stats After = collect(*M);
|
||||
|
||||
errs() << (GepShift ? "bitscale: scaled " : "bitscale: normalized ")
|
||||
<< Scaled << " GEPs, " << ConstScaled << " constant GEPs\n";
|
||||
errs() << " before: geps=" << Before.geps << " const_geps=" << Before.const_geps
|
||||
<< " vector_geps=" << Before.vector_geps << " allocas=" << Before.allocas
|
||||
<< " memintrinsics=" << Before.memintrinsics << " va=" << Before.vaarg
|
||||
<< " byval=" << Before.byval_args << " indirect=" << Before.indirect_calls
|
||||
<< " loads=" << Before.loads << " stores=" << Before.stores
|
||||
<< " ptr_subs=" << Before.ptr_subs << " const_ptr_subs=" << Before.const_ptr_subs << "\n";
|
||||
errs() << " after: geps=" << After.geps << " const_geps=" << After.const_geps
|
||||
<< " vector_geps=" << After.vector_geps << " allocas=" << After.allocas
|
||||
<< " memintrinsics=" << After.memintrinsics << " va=" << After.vaarg
|
||||
<< " byval=" << After.byval_args << " indirect=" << After.indirect_calls
|
||||
<< " loads=" << After.loads << " stores=" << After.stores
|
||||
<< " ptr_subs=" << After.ptr_subs << " const_ptr_subs=" << After.const_ptr_subs << "\n";
|
||||
|
||||
if (Remaining == 2) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream Out(argv[Arg + 1], EC);
|
||||
if (EC) {
|
||||
errs() << "cannot write " << argv[Arg + 1] << ": " << EC.message()
|
||||
<< "\n";
|
||||
return 1;
|
||||
}
|
||||
M->print(Out, nullptr);
|
||||
} else {
|
||||
M->print(outs(), nullptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
LLVM_CONFIG=${LLVM_CONFIG:-$ROOT/build/llvm-build/bin/llvm-config}
|
||||
cd "$(dirname "$0")"
|
||||
c++ -std=c++17 $("$LLVM_CONFIG" --cxxflags) bitscale.cpp \
|
||||
$("$LLVM_CONFIG" --ldflags --libs core irreader support --system-libs) \
|
||||
-o tms-bitaddr
|
||||
@@ -0,0 +1,8 @@
|
||||
; RUN: %tms-bitaddr --scale %s | FileCheck %s
|
||||
@g = global [4 x i8] zeroinitializer
|
||||
@s = global ptr getelementptr ([4 x i8], ptr @g, i32 0, i32 2)
|
||||
define ptr @c() {
|
||||
ret ptr getelementptr ([4 x i8], ptr @g, i32 0, i32 2)
|
||||
}
|
||||
; CHECK: @s = global ptr inttoptr (i32 add (i32 ptrtoint (ptr @g to i32), i32 16) to ptr)
|
||||
; CHECK: inttoptr (i32 add (i32 ptrtoint (ptr @g to i32), i32 16) to ptr)
|
||||
@@ -0,0 +1,8 @@
|
||||
; RUN: %tms-bitaddr --normalize %s | FileCheck %s
|
||||
define ptr @n(ptr %p) {
|
||||
%q = getelementptr i8, ptr %p, i32 16
|
||||
ret ptr %q
|
||||
}
|
||||
; CHECK-LABEL: @n
|
||||
; CHECK: %bit.addr = add i32 %bit.base, 16
|
||||
; CHECK-NOT: getelementptr
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
ROOT=$(cd "$(dirname "$0")/.." && pwd)
|
||||
TOOL=${TMS_BITADDR:-$ROOT/tms-bitaddr}
|
||||
FILECHECK=${FILECHECK:-$ROOT/../../build/llvm-build/bin/FileCheck}
|
||||
"$TOOL" --scale "$ROOT/tests/scale.ll" | "$FILECHECK" "$ROOT/tests/scale.ll"
|
||||
"$TOOL" --scale "$ROOT/tests/const-gep.ll" | "$FILECHECK" "$ROOT/tests/const-gep.ll"
|
||||
"$TOOL" --normalize "$ROOT/tests/normalize.ll" | "$FILECHECK" "$ROOT/tests/normalize.ll"
|
||||
@@ -0,0 +1,8 @@
|
||||
; RUN: %tms-bitaddr --scale %s | FileCheck %s
|
||||
define ptr @gep(ptr %p) {
|
||||
%q = getelementptr i8, ptr %p, i32 3
|
||||
ret ptr %q
|
||||
}
|
||||
; CHECK-LABEL: @gep
|
||||
; CHECK: %bit.addr = add i32 %bit.base, 24
|
||||
; CHECK-NOT: getelementptr
|
||||
Reference in new issue
Block a user