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

Implemented conversion of llvm.bswap intrinsic to SPIR-V, closes #210 #221

Open
wants to merge 1 commit into
base: khronos/spirv-3.6.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions lib/SPIRV/SPIRVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,84 @@ LLVMToSPIRV::transIntrinsicInst(IntrinsicInst *II, SPIRVBasicBlock *BB) {
return transLifetimeIntrinsicInst(OpLifetimeStart, II, BB);
case Intrinsic::lifetime_end:
return transLifetimeIntrinsicInst(OpLifetimeStop, II, BB);
case Intrinsic::bswap: {
auto SrcType = II->getArgOperand(0)->getType();
assert(SrcType->isIntegerTy() && "Can't bswap a non-integer type!");
SPIRVValue *Arg = transValue(II->getArgOperand(0), BB);
SPIRVType *ArgType = transType(SrcType);
switch(SrcType->getPrimitiveSizeInBits()) {
//This is mostly the same logic as lib/CodeGen/IntrinsicLowering.cpp#LowerBSWAP
default: llvm_unreachable("Unhandled type size of value to byteswap!");
case 16: {
SPIRVValue *Offset8 = BM->addConstant(ArgType, 8);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does not guarantee the uniqueness of the constant, e.g., you can create two SPIR-V constants with different id's but the same value if you called it twice. Therefore it is users' responsibility to maintain the uniqueness of the constants. When it is called during LLVM/SPIR-V translation it is fine since a map is used to maintain the uniqueness. However it is not suitable to be used to create SPIR-V on the fly.

Ideally, it is desired to improve addConstant so that uniqueness is maintained, e.g. by using the LLVM FoldingSet.

A temporary workaround may be to create the LLVM constant first, then translate it to SPIR-V.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would implement the uniqueness of constants via a map (uint64_t to SPIRVValue*) or a set of SPIRVValue* values, which can be checked by addConstant whether a constant already exists and then either inserting a new one or returning the existing.

Is there any reason, you would prefer a LLVM FoldingSet?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLVM FoldingSet is a more generic solution. By implementing profile of a SPIRV entity, we can make any SPIRV entity unique, e.g. SpecConstantOp. Similar approach can be extended to SPIRV types and attributes. Essentially this can open the door for a generic solution of uniqueness for other SPIRV entities.

In the beginning, the SPIRV in-memory representation was designed to be independent of LLVM, in the hope of being useful to projects that do not want to depend on LLVM. However, it seems this restriction is not so useful and unnecessarily limits the utility available. Therefore I would like to allow SPIRV in-memory representation to use LLVM utilities.

SPIRVValue *Tmp1 = BM->addBinaryInst(OpShiftLeftLogical, ArgType, Arg,
Offset8, BB);
SPIRVValue *Tmp2 = BM->addBinaryInst(OpShiftRightLogical, ArgType, Arg,
Offset8, BB);
return BM->addBinaryInst(OpBitwiseOr, ArgType, Tmp1, Tmp2, BB);
}
case 32: {
SPIRVValue *Offset8 = BM->addConstant(ArgType, 8);
SPIRVValue *Offset24 = BM->addConstant(ArgType, 24);
SPIRVValue *Mask16 = BM->addConstant(ArgType, 0xFF00);
SPIRVValue *Mask24 = BM->addConstant(ArgType, 0xFF0000);
SPIRVValue *Tmp4 = BM->addBinaryInst(OpShiftLeftLogical, ArgType, Arg,
Offset24, BB);
SPIRVValue *Tmp3 = BM->addBinaryInst(OpShiftLeftLogical, ArgType, Arg,
Offset8, BB);
SPIRVValue *Tmp2 = BM->addBinaryInst(OpShiftRightLogical, ArgType, Arg,
Offset8, BB);
SPIRVValue *Tmp1 = BM->addBinaryInst(OpShiftRightLogical, ArgType, Arg,
Offset24, BB);
Tmp3 = BM->addBinaryInst(OpBitwiseAnd, ArgType, Tmp3, Mask24, BB);
Tmp2 = BM->addBinaryInst(OpBitwiseAnd, ArgType, Tmp2, Mask16, BB);
SPIRVValue *Res1 = BM->addBinaryInst(OpBitwiseOr, ArgType, Tmp4, Tmp3, BB);
SPIRVValue *Res2 = BM->addBinaryInst(OpBitwiseOr, ArgType, Tmp2, Tmp1, BB);
return BM->addBinaryInst(OpBitwiseOr, ArgType, Res1, Res2, BB);
}
case 64: {
SPIRVValue *Offset8 = BM->addConstant(ArgType, 8);
SPIRVValue *Offset24 = BM->addConstant(ArgType, 24);
SPIRVValue *Offset40 = BM->addConstant(ArgType, 40);
SPIRVValue *Offset56 = BM->addConstant(ArgType, 56);
SPIRVValue *Mask16 = BM->addConstant(ArgType, 0xFF00);
SPIRVValue *Mask24 = BM->addConstant(ArgType, 0xFF0000);
SPIRVValue *Mask32 = BM->addConstant(ArgType, 0xFF000000);
SPIRVValue *Mask40 = BM->addConstant(ArgType, 0xFF00000000);
SPIRVValue *Mask48 = BM->addConstant(ArgType, 0xFF0000000000);
SPIRVValue *Mask56 = BM->addConstant(ArgType, 0xFF000000000000);
SPIRVValue *Tmp8 = BM->addBinaryInst(OpShiftLeftLogical, ArgType, Arg,
Offset56, BB);
SPIRVValue *Tmp7 = BM->addBinaryInst(OpShiftLeftLogical, ArgType, Arg,
Offset40, BB);
SPIRVValue *Tmp6 = BM->addBinaryInst(OpShiftLeftLogical, ArgType, Arg,
Offset24, BB);
SPIRVValue *Tmp5 = BM->addBinaryInst(OpShiftLeftLogical, ArgType, Arg,
Offset8, BB);
SPIRVValue *Tmp4 = BM->addBinaryInst(OpShiftRightLogical, ArgType, Arg,
Offset8, BB);
SPIRVValue *Tmp3 = BM->addBinaryInst(OpShiftRightLogical, ArgType, Arg,
Offset24, BB);
SPIRVValue *Tmp2 = BM->addBinaryInst(OpShiftRightLogical, ArgType, Arg,
Offset40, BB);
SPIRVValue *Tmp1 = BM->addBinaryInst(OpShiftRightLogical, ArgType, Arg,
Offset56, BB);
Tmp7 = BM->addBinaryInst(OpBitwiseAnd, ArgType, Tmp7, Mask56, BB);
Tmp6 = BM->addBinaryInst(OpBitwiseAnd, ArgType, Tmp6, Mask48, BB);
Tmp5 = BM->addBinaryInst(OpBitwiseAnd, ArgType, Tmp5, Mask40, BB);
Tmp4 = BM->addBinaryInst(OpBitwiseAnd, ArgType, Tmp4, Mask32, BB);
Tmp3 = BM->addBinaryInst(OpBitwiseAnd, ArgType, Tmp3, Mask24, BB);
Tmp2 = BM->addBinaryInst(OpBitwiseAnd, ArgType, Tmp2, Mask16, BB);
SPIRVValue *Res1 = BM->addBinaryInst(OpBitwiseOr, ArgType, Tmp8, Tmp7, BB);
SPIRVValue *Res2 = BM->addBinaryInst(OpBitwiseOr, ArgType, Tmp6, Tmp5, BB);
SPIRVValue *Res3 = BM->addBinaryInst(OpBitwiseOr, ArgType, Tmp4, Tmp3, BB);
SPIRVValue *Res4 = BM->addBinaryInst(OpBitwiseOr, ArgType, Tmp2, Tmp1, BB);
Res1 = BM->addBinaryInst(OpBitwiseOr, ArgType, Res1, Res2, BB);
Res3 = BM->addBinaryInst(OpBitwiseOr, ArgType, Res3, Res4, BB);
return BM->addBinaryInst(OpBitwiseOr, ArgType, Res1, Res3, BB);
}
}
}
default:
// LLVM intrinsic functions shouldn't get to SPIRV, because they
// would have no definition there.
Expand Down
122 changes: 122 additions & 0 deletions test/SPIRV/transcoding/llvm.bswap.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv %t.bc -spirv-text -o %t.txt
; RUN: FileCheck < %t.txt %s --check-prefix=CHECK-SPIRV
; RUN: llvm-spirv %t.bc -o %t.spv
; RUN: llvm-spirv -r %t.spv -o %t.rev.bc
; RUN: llvm-dis < %t.rev.bc | FileCheck %s --check-prefix=CHECK-LLVM

; CHECK-SPIRV-NOT: llvm.bswap

; CHECK-SPIRV: ShiftLeftLogical [[shortType:[0-9]+]] [[tmpShort1:[0-9]+]]
; CHECK-SPIRV: ShiftRightLogical [[shortType]] [[tmpShort2:[0-9]+]]
; CHECK-SPIRV: BitwiseOr [[shortType]] {{[0-9]+}} [[tmpShort1]] [[tmpShort2]]

; CHECK-SPIRV: ShiftLeftLogical [[intType:[0-9]+]] [[tmpInt1:[0-9]+]]
; CHECK-SPIRV: ShiftLeftLogical [[intType]] [[tmpInt2:[0-9]+]]
; CHECK-SPIRV: ShiftRightLogical [[intType]] [[tmpInt3:[0-9]+]]
; CHECK-SPIRV: ShiftRightLogical [[intType]] [[tmpInt4:[0-9]+]]
; CHECK-SPIRV: BitwiseAnd [[intType]] [[tmpInt5:[0-9]+]] [[tmpInt2]]
; CHECK-SPIRV: BitwiseAnd [[intType]] [[tmpInt6:[0-9]+]] [[tmpInt3]]
; CHECK-SPIRV: BitwiseOr [[intType]] [[tmpInt7:[0-9]+]] [[tmpInt1]] [[tmpInt5]]
; CHECK-SPIRV: BitwiseOr [[intType]] [[tmpInt8:[0-9]+]] [[tmpInt6]] [[tmpInt4]]
; CHECK-SPIRV: BitwiseOr [[intType]] {{[0-9]+}} [[tmpInt7]] [[tmpInt8]]

; CHECK-SPIRV: ShiftLeftLogical [[longType:[0-9]+]] [[tmpLong1:[0-9]+]]
; CHECK-SPIRV: ShiftLeftLogical [[longType]] [[tmpLong2:[0-9]+]]
; CHECK-SPIRV: ShiftLeftLogical [[longType]] [[tmpLong3:[0-9]+]]
; CHECK-SPIRV: ShiftLeftLogical [[longType]] [[tmpLong4:[0-9]+]]
; CHECK-SPIRV: ShiftRightLogical [[longType]] [[tmpLong5:[0-9]+]]
; CHECK-SPIRV: ShiftRightLogical [[longType]] [[tmpLong6:[0-9]+]]
; CHECK-SPIRV: ShiftRightLogical [[longType]] [[tmpLong7:[0-9]+]]
; CHECK-SPIRV: ShiftRightLogical [[longType]] [[tmpLong8:[0-9]+]]
; CHECK-SPIRV: BitwiseAnd [[longType]] [[tmpLong9:[0-9]+]] [[tmpLong2]]
; CHECK-SPIRV: BitwiseAnd [[longType]] [[tmpLong10:[0-9]+]] [[tmpLong3]]
; CHECK-SPIRV: BitwiseAnd [[longType]] [[tmpLong11:[0-9]+]] [[tmpLong4]]
; CHECK-SPIRV: BitwiseAnd [[longType]] [[tmpLong12:[0-9]+]] [[tmpLong5]]
; CHECK-SPIRV: BitwiseAnd [[longType]] [[tmpLong13:[0-9]+]] [[tmpLong6]]
; CHECK-SPIRV: BitwiseAnd [[longType]] [[tmpLong14:[0-9]+]] [[tmpLong7]]
; CHECK-SPIRV: BitwiseOr [[longType]] [[tmpLong15:[0-9]+]] [[tmpLong1]] [[tmpLong9]]
; CHECK-SPIRV: BitwiseOr [[longType]] [[tmpLong16:[0-9]+]] [[tmpLong10]] [[tmpLong11]]
; CHECK-SPIRV: BitwiseOr [[longType]] [[tmpLong17:[0-9]+]] [[tmpLong12]] [[tmpLong13]]
; CHECK-SPIRV: BitwiseOr [[longType]] [[tmpLong18:[0-9]+]] [[tmpLong14]] [[tmpLong8]]
; CHECK-SPIRV: BitwiseOr [[longType]] [[tmpLong19:[0-9]+]] [[tmpLong15]] [[tmpLong16]]
; CHECK-SPIRV: BitwiseOr [[longType]] [[tmpLong20:[0-9]+]] [[tmpLong17]] [[tmpLong18]]
; CHECK-SPIRV: BitwiseOr [[longType]] {{[0-9]+}} [[tmpLong19]] [[tmpLong20]]


; CHECK-LLVM-NOT: llvm.bswap

; CHECK-LLVM: [[rShort1:%[0-9]+]] = shl i16 [[argShort:%[0-9a-z]+]], 8
; CHECK-LLVM: [[rShort2:%[0-9]+]] = lshr exact i16 [[argShort]], 8
; CHECK-LLVM: {{%[0-9a-z]+}} = or i16 [[rShort1]], [[rShort2]]

; CHECK-LLVM: [[rInt1:%[0-9]+]] = shl i32 [[argInt:%[0-9a-z]+]], 24
; CHECK-LLVM: [[rInt2:%[0-9]+]] = shl i32 [[argInt]], 8
; CHECK-LLVM: [[rInt3:%[0-9]+]] = lshr exact i32 [[argInt]], 8
; CHECK-LLVM: [[rInt4:%[0-9]+]] = lshr exact i32 [[argInt]], 24
; CHECK-LLVM: [[rInt5:%[0-9]+]] = and i32 [[rInt2]], 16711680
; CHECK-LLVM: [[rInt6:%[0-9]+]] = and i32 [[rInt3]], 65280
; CHECK-LLVM: [[rInt7:%[0-9]+]] = or i32 [[rInt1]], [[rInt5]]
; CHECK-LLVM: [[rInt8:%[0-9]+]] = or i32 [[rInt6]], [[rInt4]]
; CHECK-LLVM: {{%[0-9a-z]+}} = or i32 [[rInt7]], [[rInt8]]

; CHECK-LLVM: [[rLong1:%[0-9]+]] = shl i64 [[argLong:%[0-9a-z]+]], 56
; CHECK-LLVM: [[rLong2:%[0-9]+]] = shl i64 [[argLong]], 40
; CHECK-LLVM: [[rLong3:%[0-9]+]] = shl i64 [[argLong]], 24
; CHECK-LLVM: [[rLong4:%[0-9]+]] = shl i64 [[argLong]], 8
; CHECK-LLVM: [[rLong5:%[0-9]+]] = lshr exact i64 [[argLong]], 8
; CHECK-LLVM: [[rLong6:%[0-9]+]] = lshr exact i64 [[argLong]], 24
; CHECK-LLVM: [[rLong7:%[0-9]+]] = lshr exact i64 [[argLong]], 40
; CHECK-LLVM: [[rLong8:%[0-9]+]] = lshr exact i64 [[argLong]], 56
; CHECK-LLVM: [[rLong9:%[0-9]+]] = and i64 [[rLong2]], 71776119061217280
; CHECK-LLVM: [[rLong10:%[0-9]+]] = and i64 [[rLong3]], 280375465082880
; CHECK-LLVM: [[rLong11:%[0-9]+]] = and i64 [[rLong4]], 1095216660480
; CHECK-LLVM: [[rLong12:%[0-9]+]] = and i64 [[rLong5]], 4278190080
; CHECK-LLVM: [[rLong13:%[0-9]+]] = and i64 [[rLong6]], 16711680
; CHECK-LLVM: [[rLong14:%[0-9]+]] = and i64 [[rLong7]], 65280
; CHECK-LLVM: [[rLong15:%[0-9]+]] = or i64 [[rLong1]], [[rLong9]]
; CHECK-LLVM: [[rLong16:%[0-9]+]] = or i64 [[rLong10]], [[rLong11]]
; CHECK-LLVM: [[rLong17:%[0-9]+]] = or i64 [[rLong12]], [[rLong13]]
; CHECK-LLVM: [[rLong18:%[0-9]+]] = or i64 [[rLong14]], [[rLong8]]
; CHECK-LLVM: [[rLong19:%[0-9]+]] = or i64 [[rLong15]], [[rLong16]]
; CHECK-LLVM: [[rLong20:%[0-9]+]] = or i64 [[rLong17]], [[rLong18]]
; CHECK-LLVM: {{%[0-9a-z]+}} = or i64 [[rLong19]], [[rLong20]]



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-unknown-unknown"

declare i16 @llvm.bswap.i16(i16)
declare i32 @llvm.bswap.i32(i32)
declare i64 @llvm.bswap.i64(i64)

define i16 @test_bswap_i16(i16 %a) {
%bswap = call i16 @llvm.bswap.i16(i16 %a)
ret i16 %bswap
}

define i32 @test_bswap_i32(i32 %a) {
%bswap = call i32 @llvm.bswap.i32(i32 %a)
ret i32 %bswap
}

define i64 @test_bswap_i64(i64 %a) {
%bswap = call i64 @llvm.bswap.i64(i64 %a)
ret i64 %bswap
}

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 = !{!7}
!opencl.ocl.version = !{!7}
!opencl.used.extensions = !{!8}
!opencl.used.optional.core.features = !{!8}
!opencl.compiler.options = !{!8}
!llvm.ident = !{!9}

!7 = !{i32 1, i32 2}
!8 = !{}
!9 = !{!"clang version 3.6.1 (https://github.com/KhronosGroup/SPIR d7e44c3b27581e54ca0e522987d1ade2bd29b70d) (https://github.com/KhronosGroup/SPIRV-LLVM.git d42743684ea8338358504e44ef8363b9dc675c66)"}