#include "llvm/Transforms/Utils/AMDGPUEmitPrintf.h"
#include "llvm/ADT/SparseBitVector.h"
#include "llvm/Analysis/ValueTracking.h"
using namespace llvm;
#define DEBUG_TYPE "amdgpu-emit-printf"
static Value *fitArgInto64Bits(IRBuilder<> &Builder, Value *Arg) {
auto Int64Ty = Builder.getInt64Ty();
auto Ty = Arg->getType();
if (auto IntTy = dyn_cast<IntegerType>(Ty)) {
switch (IntTy->getBitWidth()) {
case 32:
return Builder.CreateZExt(Arg, Int64Ty);
case 64:
return Arg;
}
}
if (Ty->getTypeID() == Type::DoubleTyID) {
return Builder.CreateBitCast(Arg, Int64Ty);
}
if (isa<PointerType>(Ty)) {
return Builder.CreatePtrToInt(Arg, Int64Ty);
}
llvm_unreachable("unexpected type");
}
static Value *callPrintfBegin(IRBuilder<> &Builder, Value *Version) {
auto Int64Ty = Builder.getInt64Ty();
auto M = Builder.GetInsertBlock()->getModule();
auto Fn = M->getOrInsertFunction("__ockl_printf_begin", Int64Ty, Int64Ty);
return Builder.CreateCall(Fn, Version);
}
static Value *callAppendArgs(IRBuilder<> &Builder, Value *Desc, int NumArgs,
Value *Arg0, Value *Arg1, Value *Arg2, Value *Arg3,
Value *Arg4, Value *Arg5, Value *Arg6,
bool IsLast) {
auto Int64Ty = Builder.getInt64Ty();
auto Int32Ty = Builder.getInt32Ty();
auto M = Builder.GetInsertBlock()->getModule();
auto Fn = M->getOrInsertFunction("__ockl_printf_append_args", Int64Ty,
Int64Ty, Int32Ty, Int64Ty, Int64Ty, Int64Ty,
Int64Ty, Int64Ty, Int64Ty, Int64Ty, Int32Ty);
auto IsLastValue = Builder.getInt32(IsLast);
auto NumArgsValue = Builder.getInt32(NumArgs);
return Builder.CreateCall(Fn, {Desc, NumArgsValue, Arg0, Arg1, Arg2, Arg3,
Arg4, Arg5, Arg6, IsLastValue});
}
static Value *appendArg(IRBuilder<> &Builder, Value *Desc, Value *Arg,
bool IsLast) {
auto Arg0 = fitArgInto64Bits(Builder, Arg);
auto Zero = Builder.getInt64(0);
return callAppendArgs(Builder, Desc, 1, Arg0, Zero, Zero, Zero, Zero, Zero,
Zero, IsLast);
}
static Value *getStrlenWithNull(IRBuilder<> &Builder, Value *Str) {
auto *Prev = Builder.GetInsertBlock();
Module *M = Prev->getModule();
auto CharZero = Builder.getInt8(0);
auto One = Builder.getInt64(1);
auto Zero = Builder.getInt64(0);
auto Int64Ty = Builder.getInt64Ty();
BasicBlock *Join = nullptr;
if (Prev->getTerminator()) {
Join = Prev->splitBasicBlock(Builder.GetInsertPoint(),
"strlen.join");
Prev->getTerminator()->eraseFromParent();
} else {
Join = BasicBlock::Create(M->getContext(), "strlen.join",
Prev->getParent());
}
BasicBlock *While =
BasicBlock::Create(M->getContext(), "strlen.while",
Prev->getParent(), Join);
BasicBlock *WhileDone = BasicBlock::Create(
M->getContext(), "strlen.while.done",
Prev->getParent(), Join);
Builder.SetInsertPoint(Prev);
auto CmpNull =
Builder.CreateICmpEQ(Str, Constant::getNullValue(Str->getType()));
BranchInst::Create(Join, While, CmpNull, Prev);
Builder.SetInsertPoint(While);
auto PtrPhi = Builder.CreatePHI(Str->getType(), 2);
PtrPhi->addIncoming(Str, Prev);
auto PtrNext = Builder.CreateGEP(Builder.getInt8Ty(), PtrPhi, One);
PtrPhi->addIncoming(PtrNext, While);
auto Data = Builder.CreateLoad(Builder.getInt8Ty(), PtrPhi);
auto Cmp = Builder.CreateICmpEQ(Data, CharZero);
Builder.CreateCondBr(Cmp, WhileDone, While);
Builder.SetInsertPoint(WhileDone, WhileDone->begin());
auto Begin = Builder.CreatePtrToInt(Str, Int64Ty);
auto End = Builder.CreatePtrToInt(PtrPhi, Int64Ty);
auto Len = Builder.CreateSub(End, Begin);
Len = Builder.CreateAdd(Len, One);
BranchInst::Create(Join, WhileDone);
Builder.SetInsertPoint(Join, Join->begin());
auto LenPhi = Builder.CreatePHI(Len->getType(), 2);
LenPhi->addIncoming(Len, WhileDone);
LenPhi->addIncoming(Zero, Prev);
return LenPhi;
}
static Value *callAppendStringN(IRBuilder<> &Builder, Value *Desc, Value *Str,
Value *Length, bool isLast) {
auto Int64Ty = Builder.getInt64Ty();
auto CharPtrTy = Builder.getInt8PtrTy();
auto Int32Ty = Builder.getInt32Ty();
auto M = Builder.GetInsertBlock()->getModule();
auto Fn = M->getOrInsertFunction("__ockl_printf_append_string_n", Int64Ty,
Int64Ty, CharPtrTy, Int64Ty, Int32Ty);
auto IsLastInt32 = Builder.getInt32(isLast);
return Builder.CreateCall(Fn, {Desc, Str, Length, IsLastInt32});
}
static Value *appendString(IRBuilder<> &Builder, Value *Desc, Value *Arg,
bool IsLast) {
Arg = Builder.CreateBitCast(
Arg, Builder.getInt8PtrTy(Arg->getType()->getPointerAddressSpace()));
auto Length = getStrlenWithNull(Builder, Arg);
return callAppendStringN(Builder, Desc, Arg, Length, IsLast);
}
static Value *processArg(IRBuilder<> &Builder, Value *Desc, Value *Arg,
bool SpecIsCString, bool IsLast) {
if (SpecIsCString && isa<PointerType>(Arg->getType())) {
return appendString(Builder, Desc, Arg, IsLast);
}
return appendArg(Builder, Desc, Arg, IsLast);
}
static void locateCStrings(SparseBitVector<8> &BV, Value *Fmt) {
StringRef Str;
if (!getConstantStringInfo(Fmt, Str) || Str.empty())
return;
static const char ConvSpecifiers[] = "diouxXfFeEgGaAcspn";
size_t SpecPos = 0;
unsigned ArgIdx = 1;
while ((SpecPos = Str.find_first_of('%', SpecPos)) != StringRef::npos) {
if (Str[SpecPos + 1] == '%') {
SpecPos += 2;
continue;
}
auto SpecEnd = Str.find_first_of(ConvSpecifiers, SpecPos);
if (SpecEnd == StringRef::npos)
return;
auto Spec = Str.slice(SpecPos, SpecEnd + 1);
ArgIdx += Spec.count('*');
if (Str[SpecEnd] == 's') {
BV.set(ArgIdx);
}
SpecPos = SpecEnd + 1;
++ArgIdx;
}
}
Value *llvm::emitAMDGPUPrintfCall(IRBuilder<> &Builder,
ArrayRef<Value *> Args) {
auto NumOps = Args.size();
assert(NumOps >= 1);
auto Fmt = Args[0];
SparseBitVector<8> SpecIsCString;
locateCStrings(SpecIsCString, Fmt);
auto Desc = callPrintfBegin(Builder, Builder.getIntN(64, 0));
Desc = appendString(Builder, Desc, Fmt, NumOps == 1);
for (unsigned int i = 1; i != NumOps; ++i) {
bool IsLast = i == NumOps - 1;
bool IsCString = SpecIsCString.test(i);
Desc = processArg(Builder, Desc, Args[i], IsCString, IsLast);
}
return Builder.CreateTrunc(Desc, Builder.getInt32Ty());
}