Skip to content

Commit c8bfc33

Browse files
authored
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]>
1 parent 0fd9882 commit c8bfc33

File tree

4 files changed

+172
-0
lines changed

4 files changed

+172
-0
lines changed

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"

lib/SPIRV/SPIRVRegularizeLLVM.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,65 @@ void regularizeWithOverflowInstrinsics(StringRef MangledName, CallInst *Call,
495495
}
496496
ToErase.push_back(Call);
497497
}
498+
499+
// CacheControls(Load/Store)INTEL decorations can be represented as metadata
500+
// placed on memory accessing instruction with the following form:
501+
// !spirv.DecorationCacheControlINTEL !X
502+
// !X = !{i32 %decoration_kind%, i32 %level%, i32 %control%,
503+
// i32 %operand of the instruction to decorate%}
504+
// This function creates a dummy GEP accessing pointer operand of the
505+
// instruction and creates !spirv.Decorations metadata attached to it.
506+
void prepareCacheControlsTranslation(Metadata *MD, Instruction *Inst) {
507+
if (!Inst->mayReadOrWriteMemory())
508+
return;
509+
auto *ArgDecoMD = dyn_cast<MDNode>(MD);
510+
assert(ArgDecoMD && "Decoration list must be a metadata node");
511+
for (unsigned I = 0, E = ArgDecoMD->getNumOperands(); I != E; ++I) {
512+
auto *DecoMD = dyn_cast<MDNode>(ArgDecoMD->getOperand(I));
513+
if (!DecoMD) {
514+
assert(!"Decoration does not name metadata");
515+
return;
516+
}
517+
518+
constexpr size_t CacheControlsNumOps = 4;
519+
if (DecoMD->getNumOperands() != CacheControlsNumOps) {
520+
assert(!"Cache controls metadata on instruction must have 4 operands");
521+
return;
522+
}
523+
524+
auto *const KindMD = cast<ConstantAsMetadata>(DecoMD->getOperand(0));
525+
auto *const LevelMD = cast<ConstantAsMetadata>(DecoMD->getOperand(1));
526+
auto *const ControlMD = cast<ConstantAsMetadata>(DecoMD->getOperand(2));
527+
528+
const size_t TargetArgNo =
529+
mdconst::dyn_extract<ConstantInt>(DecoMD->getOperand(3))
530+
->getZExtValue();
531+
Value *PtrInstOp = Inst->getOperand(TargetArgNo);
532+
if (!PtrInstOp->getType()->isPointerTy()) {
533+
assert(!"Cache controls must decorate a pointer");
534+
return;
535+
}
536+
537+
// Create dummy GEP for SSA copy of the pointer operand. Lets do our best
538+
// to guess pointee type here, but if we won't - just pointer is also fine,
539+
// if necessary TypeScavenger will adjust types and create bitcasts.
540+
IRBuilder Builder(Inst);
541+
Type *GEPTy = PtrInstOp->getType();
542+
if (auto *LI = dyn_cast<LoadInst>(Inst))
543+
GEPTy = LI->getType();
544+
else if (auto *SI = dyn_cast<StoreInst>(Inst))
545+
GEPTy = SI->getValueOperand()->getType();
546+
auto *GEP =
547+
cast<Instruction>(Builder.CreateConstGEP1_32(GEPTy, PtrInstOp, 0));
548+
Inst->setOperand(TargetArgNo, GEP);
549+
550+
SmallVector<Metadata *, 4> MDs;
551+
std::vector<Metadata *> OPs = {KindMD, LevelMD, ControlMD};
552+
MDs.push_back(MDNode::get(Inst->getContext(), OPs));
553+
MDNode *MDList = MDNode::get(Inst->getContext(), MDs);
554+
GEP->setMetadata(SPIRV_MD_DECORATIONS, MDList);
555+
}
556+
}
498557
} // namespace
499558

500559
/// Remove entities not representable by SPIR-V
@@ -511,9 +570,12 @@ bool SPIRVRegularizeLLVMBase::regularize() {
511570
continue;
512571
}
513572

573+
// TODO: query intrinsic calls from their declarations
514574
std::vector<Instruction *> ToErase;
515575
for (BasicBlock &BB : *F) {
516576
for (Instruction &II : BB) {
577+
if (auto *MD = II.getMetadata(SPIRV_MD_INTEL_CACHE_DECORATIONS))
578+
prepareCacheControlsTranslation(MD, &II);
517579
if (auto *Call = dyn_cast<CallInst>(&II)) {
518580
Call->setTailCall(false);
519581
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)