Skip to content

Commit 229dfad

Browse files
MrSidimssys-ce-bb
authored andcommitted
Re-implement Intel cache controls decoration (#2587)
The patch adds CacheControls(Load/Store)INTEL decorations representation as metadata placed on memory accessing instruction with the following form: !spirv.DecorationCacheControlINTEL !X !X = !{i32 %decoration_kind%, i32 %level%, i32 %control%, i32 %operand of the instruction to decorate%} Also patch creates a dummy GEP accessing pointer operand of the instruction to perform SSA copy of the pointer and attaches !spirv.Decorations metadata to the GEP. Few notes about this implementation and other options. It is sub-optimal as it requires iteration over all instructions in the module. Alternatives are: 1. In SPIRVWriter during transDecorations rewrite already translated instruction (lets say load) with the new one, but with GEP operand. But while the translator provides API to erase instructions and rewriting SPIRVBasicBlocks, yet unfortunately it's not really usable outside of of SPIRVModule context; 2. In SPIRVWriter during transValueWithoutDecoration add special handling of the instructions with spirv.DecorationCacheControlINTEL metadata. And it would work with one exception - it also would require to unprivate some methods of SPIRVScavenger to create GEP with the correct type, which I'm not sure if it's a good idea. Note, in this implementation in the worst case scenario SPIRVScavenger would create a bitcast later during translation. Signed-off-by: Sidorov, Dmitry <[email protected]> Original commit: KhronosGroup/SPIRV-LLVM-Translator@c8bfc33ef8f9f27
1 parent 2111a4a commit 229dfad

File tree

4 files changed

+172
-0
lines changed

4 files changed

+172
-0
lines changed

llvm-spirv/lib/SPIRV/SPIRVInternal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ typedef SPIRVMap<SPIRVExtInstSetKind, std::string, SPIRVExtSetShortName>
271271

272272
#define SPIRV_MD_PARAMETER_DECORATIONS "spirv.ParameterDecorations"
273273
#define SPIRV_MD_DECORATIONS "spirv.Decorations"
274+
#define SPIRV_MD_INTEL_CACHE_DECORATIONS "spirv.DecorationCacheControlINTEL"
274275

275276
#define OCL_TYPE_NAME_SAMPLER_T "sampler_t"
276277
#define SPIR_TYPE_NAME_EVENT_T "opencl.event_t"

llvm-spirv/lib/SPIRV/SPIRVRegularizeLLVM.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,65 @@ void regularizeWithOverflowInstrinsics(StringRef MangledName, CallInst *Call,
556556
}
557557
ToErase.push_back(Call);
558558
}
559+
560+
// CacheControls(Load/Store)INTEL decorations can be represented as metadata
561+
// placed on memory accessing instruction with the following form:
562+
// !spirv.DecorationCacheControlINTEL !X
563+
// !X = !{i32 %decoration_kind%, i32 %level%, i32 %control%,
564+
// i32 %operand of the instruction to decorate%}
565+
// This function creates a dummy GEP accessing pointer operand of the
566+
// instruction and creates !spirv.Decorations metadata attached to it.
567+
void prepareCacheControlsTranslation(Metadata *MD, Instruction *Inst) {
568+
if (!Inst->mayReadOrWriteMemory())
569+
return;
570+
auto *ArgDecoMD = dyn_cast<MDNode>(MD);
571+
assert(ArgDecoMD && "Decoration list must be a metadata node");
572+
for (unsigned I = 0, E = ArgDecoMD->getNumOperands(); I != E; ++I) {
573+
auto *DecoMD = dyn_cast<MDNode>(ArgDecoMD->getOperand(I));
574+
if (!DecoMD) {
575+
assert(!"Decoration does not name metadata");
576+
return;
577+
}
578+
579+
constexpr size_t CacheControlsNumOps = 4;
580+
if (DecoMD->getNumOperands() != CacheControlsNumOps) {
581+
assert(!"Cache controls metadata on instruction must have 4 operands");
582+
return;
583+
}
584+
585+
auto *const KindMD = cast<ConstantAsMetadata>(DecoMD->getOperand(0));
586+
auto *const LevelMD = cast<ConstantAsMetadata>(DecoMD->getOperand(1));
587+
auto *const ControlMD = cast<ConstantAsMetadata>(DecoMD->getOperand(2));
588+
589+
const size_t TargetArgNo =
590+
mdconst::dyn_extract<ConstantInt>(DecoMD->getOperand(3))
591+
->getZExtValue();
592+
Value *PtrInstOp = Inst->getOperand(TargetArgNo);
593+
if (!PtrInstOp->getType()->isPointerTy()) {
594+
assert(!"Cache controls must decorate a pointer");
595+
return;
596+
}
597+
598+
// Create dummy GEP for SSA copy of the pointer operand. Lets do our best
599+
// to guess pointee type here, but if we won't - just pointer is also fine,
600+
// if necessary TypeScavenger will adjust types and create bitcasts.
601+
IRBuilder Builder(Inst);
602+
Type *GEPTy = PtrInstOp->getType();
603+
if (auto *LI = dyn_cast<LoadInst>(Inst))
604+
GEPTy = LI->getType();
605+
else if (auto *SI = dyn_cast<StoreInst>(Inst))
606+
GEPTy = SI->getValueOperand()->getType();
607+
auto *GEP =
608+
cast<Instruction>(Builder.CreateConstGEP1_32(GEPTy, PtrInstOp, 0));
609+
Inst->setOperand(TargetArgNo, GEP);
610+
611+
SmallVector<Metadata *, 4> MDs;
612+
std::vector<Metadata *> OPs = {KindMD, LevelMD, ControlMD};
613+
MDs.push_back(MDNode::get(Inst->getContext(), OPs));
614+
MDNode *MDList = MDNode::get(Inst->getContext(), MDs);
615+
GEP->setMetadata(SPIRV_MD_DECORATIONS, MDList);
616+
}
617+
}
559618
} // namespace
560619

561620
/// Remove entities not representable by SPIR-V
@@ -579,9 +638,12 @@ bool SPIRVRegularizeLLVMBase::regularize() {
579638
continue;
580639
}
581640

641+
// TODO: query intrinsic calls from their declarations
582642
std::vector<Instruction *> ToErase;
583643
for (BasicBlock &BB : *F) {
584644
for (Instruction &II : BB) {
645+
if (auto *MD = II.getMetadata(SPIRV_MD_INTEL_CACHE_DECORATIONS))
646+
prepareCacheControlsTranslation(MD, &II);
585647
if (auto *Call = dyn_cast<CallInst>(&II)) {
586648
Call->setTailCall(false);
587649
Function *CF = Call->getCalledFunction();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; RUN: llvm-as %s -o %t.bc
2+
; RUN: llvm-spirv --spirv-ext=+SPV_INTEL_cache_controls -spirv-text %t.bc -o - | FileCheck %s --check-prefix=CHECK-SPIRV
3+
; RUN: llvm-spirv --spirv-ext=+SPV_INTEL_cache_controls %t.bc -o %t.spv
4+
; RUN: llvm-spirv -r %t.spv --spirv-target-env=SPV-IR -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-LLVM
5+
6+
; CHECK-SPIRV-DAG: TypeInt [[#Int32:]] 32 0
7+
; CHECK-SPIRV-DAG: Constant [[#Int32]] [[#Zero:]] 0
8+
; CHECK-SPIRV-DAG: Decorate [[#Load1GEPPtr:]] CacheControlLoadINTEL 0 1
9+
; CHECK-SPIRV-DAG: Decorate [[#Load2GEPPtr:]] CacheControlLoadINTEL 1 1
10+
; CHECK-SPIRV-DAG: Decorate [[#Store1GEPPtr:]] CacheControlStoreINTEL 0 1
11+
; CHECK-SPIRV-DAG: Decorate [[#Store2GEPPtr:]] CacheControlStoreINTEL 1 1
12+
13+
; CHECK-SPIRV: FunctionParameter [[#]] [[#Buffer:]]
14+
; CHECK-SPIRV: PtrAccessChain [[#]] [[#Load1GEPPtr:]] [[#Buffer]] [[#Zero]]
15+
; CHECK-SPIRV: Load [[#]] [[#]] [[#Load1GEPPtr]]
16+
; CHECK-SPIRV: PtrAccessChain [[#]] [[#Load2GEPPtr:]] [[#]] [[#Zero]]
17+
; CHECK-SPIRV: Load [[#]] [[#]] [[#Load2GEPPtr]]
18+
; CHECK-SPIRV: PtrAccessChain [[#]] [[#Store1GEPPtr:]] [[#]] [[#Zero]]
19+
; CHECK-SPIRV: Store [[#Store1GEPPtr]]
20+
; CHECK-SPIRV: PtrAccessChain [[#]] [[#Store2GEPPtr:]] [[#]] [[#Zero]]
21+
; CHECK-SPIRV: Store [[#Store2GEPPtr]]
22+
23+
; CHECK-LLVM: %[[#GEPLoad1:]] = getelementptr i32, ptr addrspace(1) %{{.*}}, i32 0, !spirv.Decorations ![[#Cache1:]]
24+
; CHECK-LLVM: load i32, ptr addrspace(1) %[[#GEPLoad1]], align 4
25+
; CHECK-LLVM: %[[#GEPLoad2:]] = getelementptr i32, ptr addrspace(1) %{{.*}}, i32 0, !spirv.Decorations ![[#Cache2:]]
26+
; CHECK-LLVM: load i32, ptr addrspace(1) %[[#GEPLoad2]], align 4
27+
; CHECK-LLVM: %[[#GEPStore1:]] = getelementptr i32, ptr addrspace(1) %{{.*}}, i32 0, !spirv.Decorations ![[#Cache3:]]
28+
; CHECK-LLVM: store i32 %[[#]], ptr addrspace(1) %[[#GEPStore1]], align 4
29+
; CHECK-LLVM: %[[#GEPStore2:]] = getelementptr i32, ptr addrspace(1) %{{.*}}, i32 0, !spirv.Decorations ![[#Cache4:]]
30+
; CHECK-LLVM: store i32 %[[#]], ptr addrspace(1) %[[#GEPStore2]], align 4
31+
; CHECK-LLVM: ![[#Cache1]] = !{![[#DecLoad1:]]}
32+
; CHECK-LLVM: ![[#DecLoad1]] = !{i32 6442, i32 0, i32 1}
33+
; CHECK-LLVM: ![[#Cache2]] = !{![[#DecLoad2:]]}
34+
; CHECK-LLVM: ![[#DecLoad2]] = !{i32 6442, i32 1, i32 1}
35+
; CHECK-LLVM: ![[#Cache3:]] = !{![[#DecStore1:]]}
36+
; CHECK-LLVM: ![[#DecStore1]] = !{i32 6443, i32 0, i32 1}
37+
; CHECK-LLVM: ![[#Cache4:]] = !{![[#DecStore2:]]}
38+
; CHECK-LLVM: ![[#DecStore2]] = !{i32 6443, i32 1, i32 1}
39+
40+
target triple = "spir64-unknown-unknown"
41+
42+
define spir_kernel void @test(ptr addrspace(1) %buffer) {
43+
entry:
44+
%0 = load i32, ptr addrspace(1) %buffer, align 4, !spirv.DecorationCacheControlINTEL !3
45+
%1 = load i32, ptr addrspace(1) %buffer, align 4, !spirv.DecorationCacheControlINTEL !5
46+
store i32 %0, ptr addrspace(1) %buffer, align 4, !spirv.DecorationCacheControlINTEL !7
47+
store i32 %1, ptr addrspace(1) %buffer, align 4, !spirv.DecorationCacheControlINTEL !9
48+
ret void
49+
}
50+
51+
!spirv.MemoryModel = !{!0}
52+
!spirv.Source = !{!1}
53+
!opencl.spir.version = !{!2}
54+
!opencl.ocl.version = !{!2}
55+
56+
!0 = !{i32 2, i32 2}
57+
!1 = !{i32 3, i32 102000}
58+
!2 = !{i32 1, i32 2}
59+
!3 = !{!4}
60+
!4 = !{i32 6442, i32 0, i32 1, i32 0}
61+
!5 = !{!6}
62+
!6 = !{i32 6442, i32 1, i32 1, i32 0}
63+
!7 = !{!8}
64+
!8 = !{i32 6443, i32 0, i32 1, i32 1}
65+
!9 = !{!10}
66+
!10 = !{i32 6443, i32 1, i32 1, i32 1}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
; RUN: llvm-as %s -o %t.bc
2+
; RUN: llvm-spirv --spirv-ext=+SPV_INTEL_cache_controls -spirv-text %t.bc -o - | FileCheck %s --check-prefix=CHECK-SPIRV
3+
; RUN: llvm-spirv --spirv-ext=+SPV_INTEL_cache_controls %t.bc -o %t.spv
4+
; RUN: llvm-spirv -r %t.spv --spirv-target-env=SPV-IR -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-LLVM
5+
6+
; CHECK-SPIRV: Decorate [[#GEP1:]] CacheControlLoadINTEL 0 1
7+
; CHECK-SPIRV: Decorate [[#GEP2:]] CacheControlStoreINTEL 0 1
8+
; CHECK-SPIRV: TypeInt [[#Int32Ty:]] 32 0
9+
; CHECK-SPIRV: Constant [[#Int32Ty]] [[#Zero:]] 0
10+
; CHECK-SPIRV: PtrAccessChain [[#]] [[#GEP1]] [[#]] [[#Zero]]
11+
; CHECK-SPIRV: PtrAccessChain [[#]] [[#GEP2]] [[#]] [[#Zero]]
12+
; CHECK-SPIRV: FunctionCall [[#]] [[#]] [[#]] [[#GEP1]] [[#GEP2]]
13+
14+
; CHECK-LLVM: %[[#GEP1:]] = getelementptr ptr addrspace(1), ptr addrspace(1) %{{.*}}, i32 0, !spirv.Decorations ![[#Cache1:]]
15+
; CHECK-LLVM: %[[#GEP2:]] = getelementptr ptr addrspace(1), ptr addrspace(1) %{{.*}}, i32 0, !spirv.Decorations ![[#Cache2:]]
16+
; CHECK-LLVM: call spir_func void @foo(ptr addrspace(1) %[[#GEP1]], ptr addrspace(1) %[[#GEP2]])
17+
; CHECK-LLVM: ![[#Cache1]] = !{![[#LoadCache:]]}
18+
; CHECK-LLVM: ![[#LoadCache]] = !{i32 6442, i32 0, i32 1}
19+
; CHECK-LLVM: ![[#Cache2]] = !{![[#StoreCache:]]}
20+
; CHECK-LLVM: ![[#StoreCache]] = !{i32 6443, i32 0, i32 1}
21+
22+
target triple = "spir64-unknown-unknown"
23+
24+
define spir_kernel void @test(ptr addrspace(1) %buffer1, ptr addrspace(1) %buffer2) {
25+
entry:
26+
call void @foo(ptr addrspace(1) %buffer1, ptr addrspace(1) %buffer2), !spirv.DecorationCacheControlINTEL !3
27+
ret void
28+
}
29+
30+
declare void @foo(ptr addrspace(1), ptr addrspace(1))
31+
32+
!spirv.MemoryModel = !{!0}
33+
!spirv.Source = !{!1}
34+
!opencl.spir.version = !{!2}
35+
!opencl.ocl.version = !{!2}
36+
37+
!0 = !{i32 2, i32 2}
38+
!1 = !{i32 3, i32 102000}
39+
!2 = !{i32 1, i32 2}
40+
!3 = !{!4, !5}
41+
!4 = !{i32 6442, i32 0, i32 1, i32 0}
42+
!5 = !{i32 6443, i32 0, i32 1, i32 1}
43+

0 commit comments

Comments
 (0)