Skip to content
This repository has been archived by the owner on Oct 9, 2019. It is now read-only.

Commit

Permalink
Translation of LLVM intrinsics (#185)
Browse files Browse the repository at this point in the history
An LLVM intrinsic should not be transalated to SPIRV as a function call.
Either it's semantics is represented via SPIRV instruction(s)
or an error is reported.
  • Loading branch information
AlexeySotkin authored and yxsamliu committed Sep 26, 2016
1 parent c370052 commit 73e65fc
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 27 deletions.
113 changes: 86 additions & 27 deletions lib/SPIRV/SPIRVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
#include "llvm/IR/Function.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
#include "llvm/IR/Verifier.h"
Expand Down Expand Up @@ -165,6 +166,10 @@ class LLVMToSPIRV: public ModulePass {
DbgTran(nullptr, SMod){
}

virtual const char* getPassName() const {
return "LLVMToSPIRV";
}

bool runOnModule(Module &Mod) override {
M = &Mod;
Ctx = &M->getContext();
Expand All @@ -183,7 +188,7 @@ class LLVMToSPIRV: public ModulePass {
SPIRVType *transType(Type *T);
SPIRVType *transSPIRVOpaqueType(Type *T);

SPIRVValue *getTranslatedValue(Value *);
SPIRVValue *getTranslatedValue(Value *) const;

// Translation functions
bool transAddressingMode();
Expand All @@ -194,6 +199,7 @@ class LLVMToSPIRV: public ModulePass {
bool transSourceLanguage();
bool transExtension();
bool transBuiltinSet();
SPIRVValue *transIntrinsicInst(IntrinsicInst *Intrinsic, SPIRVBasicBlock *BB);
SPIRVValue *transCallInst(CallInst *Call, SPIRVBasicBlock *BB);
bool transDecoration(Value *V, SPIRVValue *BV);
SPIRVWord transFunctionControlMask(CallInst *);
Expand Down Expand Up @@ -310,8 +316,8 @@ class LLVMToSPIRV: public ModulePass {


SPIRVValue *
LLVMToSPIRV::getTranslatedValue(Value *V) {
LLVMToSPIRVValueMap::iterator Loc = ValueMap.find(V);
LLVMToSPIRV::getTranslatedValue(Value *V) const {
auto Loc = ValueMap.find(V);
if (Loc != ValueMap.end())
return Loc->second;
return nullptr;
Expand Down Expand Up @@ -628,9 +634,17 @@ LLVMToSPIRV::transSPIRVOpaqueType(Type *T) {

SPIRVFunction *
LLVMToSPIRV::transFunctionDecl(Function *F) {
if (auto BF= getTranslatedValue(F))
if (auto BF = getTranslatedValue(F))
return static_cast<SPIRVFunction *>(BF);

if (F->isIntrinsic()) {
// We should not translate LLVM intrinsics as a function
assert(none_of(F->user_begin(), F->user_end(),
[this](User *U){ return getTranslatedValue(U);}) &&
"LLVM intrinsics shouldn't be called in SPIRV");
return nullptr;
}

SPIRVTypeFunction *BFT = static_cast<SPIRVTypeFunction *>(transType(
getAnalysis<OCLTypeToSPIRV>().getAdaptedType(F)));
SPIRVFunction *BF = static_cast<SPIRVFunction *>(mapValue(F,
Expand Down Expand Up @@ -795,11 +809,11 @@ LLVMToSPIRV::transValue(Value *V, SPIRVBasicBlock *BB, bool CreateForward) {
"Invalid SPIRV BB");

auto BV = transValueWithoutDecoration(V, BB, CreateForward);
if (!BV || !transDecoration(V, BV))
return nullptr;
std::string name = V->getName();
if (!name.empty()) // Don't erase the name, which BM might already have
BM->setName(BV, name);
if(!transDecoration(V, BV))
return nullptr;
return BV;
}

Expand Down Expand Up @@ -1103,6 +1117,11 @@ LLVMToSPIRV::transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB,
BB));
}

if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(V)) {
SPIRVValue *BV = transIntrinsicInst(II, BB);
return BV ? mapValue(V, BV) : nullptr;
}

if (CallInst *CI = dyn_cast<CallInst>(V))
return mapValue(V, transCallInst(CI, BB));

Expand Down Expand Up @@ -1196,6 +1215,67 @@ LLVMToSPIRV::transSpcvCast(CallInst* CI, SPIRVBasicBlock *BB) {
return oclTransSpvcCastSampler(CI, BB);
}

SPIRVValue *
LLVMToSPIRV::transIntrinsicInst(IntrinsicInst *II, SPIRVBasicBlock *BB) {
auto getMemoryAccess = [](MemIntrinsic *MI)->std::vector<SPIRVWord> {
std::vector<SPIRVWord> MemoryAccess(1, MemoryAccessMaskNone);
if (SPIRVWord AlignVal = MI->getAlignment()) {
MemoryAccess[0] |= MemoryAccessAlignedMask;
MemoryAccess.push_back(AlignVal);
}
if (MI->isVolatile())
MemoryAccess[0] |= MemoryAccessVolatileMask;
return MemoryAccess;
};

switch (II->getIntrinsicID()) {
case Intrinsic::fmuladd : {
// For llvm.fmuladd.* fusion is not guaranteed. If a fused multiply-add
// is required the corresponding llvm.fma.* intrinsic function should be
// used instead.
SPIRVType *Ty = transType(II->getType());
SPIRVValue *Mul = BM->addBinaryInst(OpFMul, Ty,
transValue(II->getArgOperand(0), BB),
transValue(II->getArgOperand(1), BB),
BB);
return BM->addBinaryInst(OpFAdd, Ty, Mul,
transValue(II->getArgOperand(2), BB), BB);
}
case Intrinsic::memset : {
// If memset is used for zero-initialization, find a variable which is being
// initialized and store null constant of the same type to this variable
MemSetInst *MSI = cast<MemSetInst>(II);
AllocaInst *AI = dyn_cast<AllocaInst>(MSI->getDest());
ConstantInt *Val = dyn_cast<ConstantInt>(MSI->getValue());
ConstantInt *Len = dyn_cast<ConstantInt>(MSI->getLength());
if (AI && Val && Val->isZero() && Len &&
AI->getAlignment() == MSI->getAlignment() && Len->getZExtValue()*8 ==
M->getDataLayout()->getTypeSizeInBits(AI->getAllocatedType())) {
SPIRVValue *Var = transValue(MSI->getDest(), BB);
assert(Var && Var->isVariable());
auto *VarTy = static_cast<SPIRVTypePointer *>(Var->getType());
return BM->addStoreInst(Var, BM->addNullConstant(VarTy->getElementType()),
getMemoryAccess(MSI), BB);
}
assert(!"Can't translate llvm.memset with non-zero value argument");
}
break;
case Intrinsic::memcpy :
return BM->addCopyMemorySizedInst(
transValue(II->getOperand(0), BB),
transValue(II->getOperand(1), BB),
transValue(II->getOperand(2), BB),
getMemoryAccess(cast<MemIntrinsic>(II)),
BB);
default:
// LLVM intrinsic functions shouldn't get to SPIRV, because they
// would have no definition there.
BM->getErrorLog().checkError(false, SPIRVEC_InvalidFunctionCall,
II->getName().str(), "", __FILE__, __LINE__);
}
return nullptr;
}

SPIRVValue *
LLVMToSPIRV::transCallInst(CallInst *CI, SPIRVBasicBlock *BB) {
SPIRVExtInstSetKind ExtSetKind = SPIRVEIS_Count;
Expand All @@ -1207,27 +1287,6 @@ LLVMToSPIRV::transCallInst(CallInst *CI, SPIRVBasicBlock *BB) {
if (MangledName.startswith(SPCV_CAST))
return transSpcvCast(CI, BB);

if (MangledName.startswith("llvm.memcpy")) {
std::vector<SPIRVWord> MemoryAccess;

if (isa<ConstantInt>(CI->getOperand(4)) &&
dyn_cast<ConstantInt>(CI->getOperand(4))
->getZExtValue() == 1)
MemoryAccess.push_back(MemoryAccessVolatileMask);
if (isa<ConstantInt>(CI->getOperand(3))) {
MemoryAccess.push_back(MemoryAccessAlignedMask);
MemoryAccess.push_back(dyn_cast<ConstantInt>(CI->getOperand(3))
->getZExtValue());
}

return BM->addCopyMemorySizedInst(
transValue(CI->getOperand(0), BB),
transValue(CI->getOperand(1), BB),
transValue(CI->getOperand(2), BB),
MemoryAccess,
BB);
}

if (oclIsBuiltin(MangledName, &DemangledName) ||
isDecoratedSPIRVFunc(F, &DemangledName))
if (auto BV = transBuiltinToInst(DemangledName, MangledName, CI, BB))
Expand Down
1 change: 1 addition & 0 deletions lib/SPIRV/libSPIRV/SPIRVErrorEnum.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ _SPIRV_OP(InvalidAddressingModel, "Expects 0-2.")
_SPIRV_OP(InvalidMemoryModel, "Expects 0-3.")
_SPIRV_OP(InvalidFunctionControlMask,"")
_SPIRV_OP(InvalidBuiltinSetName, "Expects OpenCL.std.")
_SPIRV_OP(InvalidFunctionCall, "Unexpected llvm intrinsic:")
34 changes: 34 additions & 0 deletions test/SPIRV/llvm.fma.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
; Translator should not translate llvm intrinsic calls straight forward.
; It either represnts intrinsic's semantics with SPIRV instruction(s), or
; reports an error.
; XFAIL: *
; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv %t.bc

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64"
target triple = "spir64"

; Function Attrs: nounwind
define spir_func void @foo(float %a, float %b, float %c) #0 {
entry:
%0 = call float @llvm.fma.f32(float %a, float %b, float %c)
ret void
}

; Function Attrs: nounwind readnone
declare float @llvm.fma.f32(float, float, float) #1

attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone }

!opencl.enable.FP_CONTRACT = !{}
!opencl.spir.version = !{!0}
!opencl.ocl.version = !{!1}
!opencl.used.extensions = !{!2}
!opencl.used.optional.core.features = !{!3}
!opencl.compiler.options = !{!2}

!0 = !{i32 1, i32 2}
!1 = !{i32 2, i32 0}
!2 = !{}
!3 = !{!"cl_doubles"}
43 changes: 43 additions & 0 deletions test/SPIRV/llvm.fmuladd.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv %t.bc -spirv-text -o - | FileCheck %s

; CHECK-NOT: llvm.fmuladd

; CHECK: TypeFloat [[f32:[0-9]+]] 32
; CHECK: TypeFloat [[f64:[0-9]+]] 64

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64"
target triple = "spir64"

; Function Attrs: nounwind
define spir_func void @foo(float %a, float %b, float %c, double %x, double %y, double %z) #0 {
entry:
%0 = call float @llvm.fmuladd.f32(float %a, float %b, float %c)
; CHECK: FMul [[f32]] [[mul32:[0-9]+]] {{[0-9]+}} {{[0-9]+}}
; CHECK-NEXT: FAdd [[f32]] {{[0-9]+}} [[mul32]] {{[0-9]+}}
%1 = call double @llvm.fmuladd.f64(double %x, double %y, double %z)
; CHECK: FMul [[f64]] [[mul64:[0-9]+]] {{[0-9]+}} {{[0-9]+}}
; CHECK-NEXT: FAdd [[f64]] {{[0-9]+}} [[mul64]] {{[0-9]+}}
ret void
}

; Function Attrs: nounwind readnone
declare float @llvm.fmuladd.f32(float, float, float) #1

; Function Attrs: nounwind readnone
declare double @llvm.fmuladd.f64(double, double, double) #1

attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone }

!opencl.enable.FP_CONTRACT = !{}
!opencl.spir.version = !{!0}
!opencl.ocl.version = !{!1}
!opencl.used.extensions = !{!2}
!opencl.used.optional.core.features = !{!3}
!opencl.compiler.options = !{!2}

!0 = !{i32 1, i32 2}
!1 = !{i32 2, i32 0}
!2 = !{}
!3 = !{!"cl_doubles"}
42 changes: 42 additions & 0 deletions test/SPIRV/llvm.memset.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv %t.bc -spirv-text -o - | FileCheck %s

; CHECK-NOT: llvm.memset

; CHECK: TypeInt [[int32:[0-9]+]] 32
; CHECK: Constant [[int32]] [[fiftieen:[0-9]+]] 15
; CHECK: TypeArray [[int32x15:[0-9]+]] [[int32]] [[fiftieen]]
; CHECK: TypePointer [[int32x15Ptr:[0-9]+]] 7 [[int32x15]]
; CHECK: ConstantNull [[int32x15]] [[ConstantNull:[0-9]+]]

; CHECK: Variable [[int32x15Ptr:[0-9]+]] [[mem:[0-9]+]] 7
; CHECK: Store [[mem]] [[ConstantNull]]

target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64"
target triple = "spir"

; Function Attrs: nounwind
define spir_func void @test() #0 {
entry:
%mem = alloca [15 x i32], align 4
%0 = bitcast [15 x i32]* %mem to i8*
call void @llvm.memset.p0i8.i32(i8* %0, i8 0, i32 60, i32 4, i1 false)
ret void
}

; Function Attrs: nounwind
declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) #1

attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind }

!opencl.enable.FP_CONTRACT = !{}
!opencl.spir.version = !{!0}
!opencl.ocl.version = !{!1}
!opencl.used.extensions = !{!2}
!opencl.used.optional.core.features = !{!2}
!opencl.compiler.options = !{!2}

!0 = !{i32 1, i32 2}
!1 = !{i32 2, i32 0}
!2 = !{}

0 comments on commit 73e65fc

Please sign in to comment.