Skip to content

Commit b39e552

Browse files
MrSidimsbader
authored andcommitted
[SPIR-V] Initial support of FPGA loop attributes/pragmas
Following attributes/pragmas are supported: ivdep ivdep safelen (N) ii(N) max_concurrency_loop(N) LLVM IR ->SPIRV translation (appropriately the set order): !"llvm.loop.ivdep.enable -> 4 LoopMerge {{[0-9]+}} {{[0-9]+}} 4 !"llvm.loop.ivdep.safelen", i32 N -> 5 LoopMerge {{[0-9]+}} {{[0-9]+}} 8 N !"llvm.loop.ii.count", i32 N -> 6 LoopMerge {{[0-9]+}} {{[0-9]+}} 0x80000000 5889 N !"llvm.loop.max_concurrency.count", i32 N -> 6 LoopMerge {{[0-9]+}} {{[0-9]+}} 0x80000000 5890 N Also a case of multiple bit masks set for LoopControl is now supported. SPEC: KhronosGroup/SPIRV-Registry#28 Signed-off-by: Dmitry Sidorov [email protected]
1 parent 02531c4 commit b39e552

File tree

7 files changed

+270
-17
lines changed

7 files changed

+270
-17
lines changed

llvm-spirv/lib/SPIRV/SPIRVReader.cpp

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -550,27 +550,68 @@ void SPIRVToLLVM::setName(llvm::Value *V, SPIRVValue *BV) {
550550
void SPIRVToLLVM::setLLVMLoopMetadata(SPIRVLoopMerge *LM, BranchInst *BI) {
551551
if (!LM)
552552
return;
553-
llvm::MDString *Name = nullptr;
554553
auto Temp = MDNode::getTemporary(*Context, None);
555554
auto Self = MDNode::get(*Context, Temp.get());
556555
Self->replaceOperandWith(0, Self);
557-
558-
if (LM->getLoopControl() == LoopControlMaskNone) {
556+
SPIRVWord LC = LM->getLoopControl();
557+
if (LC == LoopControlMaskNone) {
559558
BI->setMetadata("llvm.loop", Self);
560559
return;
561-
} else if (LM->getLoopControl() == LoopControlUnrollMask)
562-
Name = llvm::MDString::get(*Context, "llvm.loop.unroll.full");
563-
else if (LM->getLoopControl() == LoopControlDontUnrollMask)
564-
Name = llvm::MDString::get(*Context, "llvm.loop.unroll.disable");
565-
else
566-
return;
560+
}
567561

568-
std::vector<llvm::Metadata *> OpValues(1, Name);
569-
SmallVector<llvm::Metadata *, 2> Metadata;
562+
std::vector<llvm::Metadata *> Metadata;
570563
Metadata.push_back(llvm::MDNode::get(*Context, Self));
571-
Metadata.push_back(llvm::MDNode::get(*Context, OpValues));
572-
564+
std::vector<SPIRVWord> LoopControlParameters;
565+
if (LC & LoopControlUnrollMask)
566+
Metadata.push_back(llvm::MDNode::get(
567+
*Context, llvm::MDString::get(*Context, "llvm.loop.unroll.full")));
568+
else if (LC & LoopControlDontUnrollMask)
569+
Metadata.push_back(llvm::MDNode::get(
570+
*Context, llvm::MDString::get(*Context, "llvm.loop.unroll.disable")));
571+
if (LC & LoopControlDependencyInfiniteMask)
572+
Metadata.push_back(llvm::MDNode::get(
573+
*Context, llvm::MDString::get(*Context, "llvm.loop.ivdep.enable")));
574+
if (LC & LoopControlDependencyLengthMask) {
575+
LoopControlParameters = LM->getLoopControlParameters();
576+
if (!LoopControlParameters.empty()) {
577+
llvm::Metadata *OpValues[] = {
578+
MDString::get(*Context, "llvm.loop.ivdep.safelen"),
579+
ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context),
580+
LoopControlParameters[0]))};
581+
Metadata.push_back(llvm::MDNode::get(*Context, OpValues));
582+
}
583+
}
584+
if (LC & LoopControlExtendedControlsMask) {
585+
LoopControlParameters = LM->getLoopControlParameters();
586+
for (auto LCP = LoopControlParameters.begin();
587+
LCP != LoopControlParameters.end(); ++LCP) {
588+
switch (*LCP) {
589+
case InitiationIntervalINTEL: {
590+
// To generate a correct integer part of metadata we skip a parameter
591+
// that encodes name of the metadata and take the next one
592+
llvm::Metadata *OpValues[] = {
593+
MDString::get(*Context, "llvm.loop.ii.count"),
594+
ConstantAsMetadata::get(
595+
ConstantInt::get(Type::getInt32Ty(*Context), *(++LCP)))};
596+
Metadata.push_back(llvm::MDNode::get(*Context, OpValues));
597+
break;
598+
}
599+
case MaxConcurrencyINTEL: {
600+
llvm::Metadata *OpValues[] = {
601+
MDString::get(*Context, "llvm.loop.max_concurrency.count"),
602+
ConstantAsMetadata::get(
603+
ConstantInt::get(Type::getInt32Ty(*Context), *(++LCP)))};
604+
Metadata.push_back(llvm::MDNode::get(*Context, OpValues));
605+
break;
606+
}
607+
default:
608+
break;
609+
}
610+
}
611+
}
573612
llvm::MDNode *Node = llvm::MDNode::get(*Context, Metadata);
613+
614+
// Set the first operand to refer itself
574615
Node->replaceOperandWith(0, Node);
575616
BI->setMetadata("llvm.loop", Node);
576617
}

llvm-spirv/lib/SPIRV/SPIRVUtil.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,17 +1492,38 @@ spv::LoopControlMask getLoopControl(const BranchInst *Branch,
14921492
MDNode *LoopMD = Branch->getMetadata("llvm.loop");
14931493
if (!LoopMD)
14941494
return spv::LoopControlMaskNone;
1495+
1496+
size_t LoopControl = spv::LoopControlMaskNone;
14951497
for (const MDOperand &MDOp : LoopMD->operands()) {
14961498
if (MDNode *Node = dyn_cast<MDNode>(MDOp)) {
14971499
std::string S = getMDOperandAsString(Node, 0);
14981500
if (S == "llvm.loop.unroll.disable")
1499-
return spv::LoopControlDontUnrollMask;
1501+
LoopControl |= spv::LoopControlDontUnrollMask;
15001502
// TODO Express partial unrolling in SPIRV.
1501-
if (S == "llvm.loop.unroll.count" || S == "llvm.loop.unroll.full")
1502-
return spv::LoopControlUnrollMask;
1503+
else if (S == "llvm.loop.unroll.count" || S == "llvm.loop.unroll.full")
1504+
LoopControl |= spv::LoopControlUnrollMask;
1505+
if (S == "llvm.loop.ivdep.enable")
1506+
LoopControl |= spv::LoopControlDependencyInfiniteMask;
1507+
if (S == "llvm.loop.ivdep.safelen") {
1508+
size_t I = getMDOperandAsInt(Node, 1);
1509+
Parameters.push_back(I);
1510+
LoopControl |= spv::LoopControlDependencyLengthMask;
1511+
}
1512+
if (S == "llvm.loop.ii.count") {
1513+
Parameters.push_back(InitiationIntervalINTEL);
1514+
size_t I = getMDOperandAsInt(Node, 1);
1515+
Parameters.push_back(I);
1516+
LoopControl |= spv::LoopControlExtendedControlsMask;
1517+
}
1518+
if (S == "llvm.loop.max_concurrency.count") {
1519+
Parameters.push_back(MaxConcurrencyINTEL);
1520+
size_t I = getMDOperandAsInt(Node, 1);
1521+
Parameters.push_back(I);
1522+
LoopControl |= spv::LoopControlExtendedControlsMask;
1523+
}
15031524
}
15041525
}
1505-
return spv::LoopControlMaskNone;
1526+
return static_cast<spv::LoopControlMask>(LoopControl);
15061527
}
15071528

15081529
} // namespace SPIRV

llvm-spirv/lib/SPIRV/libSPIRV/SPIRVInstruction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,9 @@ class SPIRVLoopMerge : public SPIRVInstruction {
10351035
SPIRVId getMergeBlock() { return MergeBlock; }
10361036
SPIRVId getContinueTarget() { return ContinueTarget; }
10371037
SPIRVWord getLoopControl() { return LoopControl; }
1038+
std::vector<SPIRVWord> getLoopControlParameters() {
1039+
return LoopControlParameters;
1040+
}
10381041

10391042
void setWordCount(SPIRVWord TheWordCount) override {
10401043
SPIRVEntry::setWordCount(TheWordCount);

llvm-spirv/lib/SPIRV/libSPIRV/SPIRVIsValidEnum.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ inline bool isValid(spv::Capability V) {
556556
case CapabilityNamedBarrier:
557557
case CapabilityPipeStorage:
558558
case CapabilityFPGAMemoryAttributesINTEL:
559+
case CapabilityFPGALoopControlsINTEL:
559560
return true;
560561
default:
561562
return false;
@@ -1042,6 +1043,7 @@ inline bool isValidLoopControlMask(SPIRVWord Mask) {
10421043
ValidMask |= LoopControlDontUnrollMask;
10431044
ValidMask |= LoopControlDependencyInfiniteMask;
10441045
ValidMask |= LoopControlDependencyLengthMask;
1046+
ValidMask |= LoopControlExtendedControlsMask;
10451047

10461048
return (Mask & ~ValidMask) == 0;
10471049
}

llvm-spirv/lib/SPIRV/libSPIRV/SPIRVNameMapEnum.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ template <> inline void SPIRVMap<Capability, std::string>::init() {
484484
add(CapabilitySubgroupAvcMotionEstimationChromaINTEL,
485485
"SubgroupAvcMotionEstimationChromaINTEL");
486486
add(CapabilityFPGAMemoryAttributesINTEL, "FPGAMemoryAttributesINTEL");
487+
add(CapabilityFPGALoopControlsINTEL, "FPGALoopControlsINTEL");
487488
}
488489
SPIRV_DEF_NAMEMAP(Capability, SPIRVCapabilityNameMap)
489490

llvm-spirv/lib/SPIRV/libSPIRV/spirv.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,12 @@ enum LoopControlMask {
484484
LoopControlDontUnrollMask = 0x00000002,
485485
LoopControlDependencyInfiniteMask = 0x00000004,
486486
LoopControlDependencyLengthMask = 0x00000008,
487+
LoopControlExtendedControlsMask = 0x80000000,
488+
};
489+
490+
enum ExtendedControls {
491+
InitiationIntervalINTEL = 5889,
492+
MaxConcurrencyINTEL = 5890,
487493
};
488494

489495
enum FunctionControlShift {
@@ -664,6 +670,7 @@ enum Capability {
664670
CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697,
665671
CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698,
666672
CapabilityFPGAMemoryAttributesINTEL = 5824,
673+
CapabilityFPGALoopControlsINTEL = 5888,
667674
CapabilityMax = 0x7fffffff,
668675
};
669676

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
; RUN: llvm-as < %s > %t.bc
2+
; RUN: llvm-spirv %t.bc -o - -spirv-text | FileCheck %s --check-prefix=CHECK-SPIRV
3+
4+
; RUN: llvm-spirv %t.bc -o %t.spv
5+
; RUN: llvm-spirv -r %t.spv -o %t.rev.bc
6+
; RUN: llvm-dis < %t.rev.bc | FileCheck %s --check-prefix=CHECK-LLVM
7+
8+
; ModuleID = 'FPGALoopAttr.cl'
9+
source_filename = "FPGALoopAttr.cl"
10+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
11+
target triple = "spir64-unknown-unknown-unknown"
12+
13+
; CHECK-SPIRV: Function
14+
; Function Attrs: convergent noinline nounwind optnone
15+
define spir_kernel void @test_ivdep() #0 {
16+
entry:
17+
%a = alloca [10 x i32], align 4
18+
%i = alloca i32, align 4
19+
%i1 = alloca i32, align 4
20+
%i10 = alloca i32, align 4
21+
%i19 = alloca i32, align 4
22+
%i28 = alloca i32, align 4
23+
store i32 0, i32* %i, align 4
24+
br label %for.cond
25+
; CHECK-SPIRV: 4 LoopMerge {{[0-9]+}} {{[0-9]+}} 4
26+
; CHECK-SPIRV: 4 BranchConditional {{[0-9]+}} {{[0-9]+}} {{[0-9]+}}
27+
for.cond: ; preds = %for.inc, %entry
28+
%0 = load i32, i32* %i, align 4
29+
%cmp = icmp ne i32 %0, 10
30+
br i1 %cmp, label %for.body, label %for.end
31+
32+
for.body: ; preds = %for.cond
33+
%1 = load i32, i32* %i, align 4
34+
%idxprom = sext i32 %1 to i64
35+
%arrayidx = getelementptr inbounds [10 x i32], [10 x i32]* %a, i64 0, i64 %idxprom
36+
store i32 0, i32* %arrayidx, align 4
37+
br label %for.inc
38+
39+
for.inc: ; preds = %for.body
40+
%2 = load i32, i32* %i, align 4
41+
%inc = add nsw i32 %2, 1
42+
store i32 %inc, i32* %i, align 4
43+
br label %for.cond, !llvm.loop !3
44+
45+
for.end: ; preds = %for.cond
46+
store i32 0, i32* %i1, align 4
47+
br label %for.cond2
48+
49+
; CHECK-SPIRV: 5 LoopMerge {{[0-9]+}} {{[0-9]+}} 8 2
50+
; CHECK-SPIRV: 4 BranchConditional {{[0-9]+}} {{[0-9]+}} {{[0-9]+}}
51+
for.cond2: ; preds = %for.inc7, %for.end
52+
%3 = load i32, i32* %i1, align 4
53+
%cmp3 = icmp ne i32 %3, 10
54+
br i1 %cmp3, label %for.body4, label %for.end9
55+
56+
for.body4: ; preds = %for.cond2
57+
%4 = load i32, i32* %i1, align 4
58+
%idxprom5 = sext i32 %4 to i64
59+
%arrayidx6 = getelementptr inbounds [10 x i32], [10 x i32]* %a, i64 0, i64 %idxprom5
60+
store i32 0, i32* %arrayidx6, align 4
61+
br label %for.inc7
62+
63+
for.inc7: ; preds = %for.body4
64+
%5 = load i32, i32* %i1, align 4
65+
%inc8 = add nsw i32 %5, 1
66+
store i32 %inc8, i32* %i1, align 4
67+
br label %for.cond2, !llvm.loop !5
68+
69+
for.end9: ; preds = %for.cond2
70+
store i32 0, i32* %i10, align 4
71+
br label %for.cond11
72+
73+
; CHECK-SPIRV: 6 LoopMerge {{[0-9]+}} {{[0-9]+}} 2147483648 5889 2
74+
; CHECK-SPIRV: 4 BranchConditional {{[0-9]+}} {{[0-9]+}} {{[0-9]+}}
75+
for.cond11: ; preds = %for.inc16, %for.end9
76+
%6 = load i32, i32* %i10, align 4
77+
%cmp12 = icmp ne i32 %6, 10
78+
br i1 %cmp12, label %for.body13, label %for.end18
79+
80+
for.body13: ; preds = %for.cond11
81+
%7 = load i32, i32* %i10, align 4
82+
%idxprom14 = sext i32 %7 to i64
83+
%arrayidx15 = getelementptr inbounds [10 x i32], [10 x i32]* %a, i64 0, i64 %idxprom14
84+
store i32 0, i32* %arrayidx15, align 4
85+
br label %for.inc16
86+
87+
for.inc16: ; preds = %for.body13
88+
%8 = load i32, i32* %i10, align 4
89+
%inc17 = add nsw i32 %8, 1
90+
store i32 %inc17, i32* %i10, align 4
91+
br label %for.cond11, !llvm.loop !7
92+
93+
for.end18: ; preds = %for.cond11
94+
store i32 0, i32* %i19, align 4
95+
br label %for.cond20
96+
97+
; CHECK-SPIRV: 6 LoopMerge {{[0-9]+}} {{[0-9]+}} 2147483648 5890 2
98+
; CHECK-SPIRV: 4 BranchConditional {{[0-9]+}} {{[0-9]+}} {{[0-9]+}}
99+
for.cond20: ; preds = %for.inc25, %for.end18
100+
%9 = load i32, i32* %i19, align 4
101+
%cmp21 = icmp ne i32 %9, 10
102+
br i1 %cmp21, label %for.body22, label %for.end27
103+
104+
for.body22: ; preds = %for.cond20
105+
%10 = load i32, i32* %i19, align 4
106+
%idxprom23 = sext i32 %10 to i64
107+
%arrayidx24 = getelementptr inbounds [10 x i32], [10 x i32]* %a, i64 0, i64 %idxprom23
108+
store i32 0, i32* %arrayidx24, align 4
109+
br label %for.inc25
110+
111+
for.inc25: ; preds = %for.body22
112+
%11 = load i32, i32* %i19, align 4
113+
%inc26 = add nsw i32 %11, 1
114+
store i32 %inc26, i32* %i19, align 4
115+
br label %for.cond20, !llvm.loop !9
116+
117+
for.end27: ; preds = %for.cond20
118+
store i32 0, i32* %i28, align 4
119+
br label %for.cond29
120+
121+
; CHECK-SPIRV: 8 LoopMerge {{[0-9]+}} {{[0-9]+}} 2147483648 5889 2 5890 2
122+
; CHECK-SPIRV: 4 BranchConditional {{[0-9]+}} {{[0-9]+}} {{[0-9]+}}
123+
for.cond29: ; preds = %for.inc34, %for.end27
124+
%12 = load i32, i32* %i28, align 4
125+
%cmp30 = icmp ne i32 %12, 10
126+
br i1 %cmp30, label %for.body31, label %for.end36
127+
128+
for.body31: ; preds = %for.cond29
129+
%13 = load i32, i32* %i28, align 4
130+
%idxprom32 = sext i32 %13 to i64
131+
%arrayidx33 = getelementptr inbounds [10 x i32], [10 x i32]* %a, i64 0, i64 %idxprom32
132+
store i32 0, i32* %arrayidx33, align 4
133+
br label %for.inc34
134+
135+
for.inc34: ; preds = %for.body31
136+
%14 = load i32, i32* %i28, align 4
137+
%inc35 = add nsw i32 %14, 1
138+
store i32 %inc35, i32* %i28, align 4
139+
br label %for.cond29, !llvm.loop !11
140+
141+
for.end36: ; preds = %for.cond29
142+
ret void
143+
}
144+
145+
attributes #0 = { convergent noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "denorms-are-zero"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "uniform-work-group-size"="true" "unsafe-fp-math"="false" "use-soft-float"="false" }
146+
147+
!llvm.module.flags = !{!0}
148+
!opencl.enable.FP_CONTRACT = !{}
149+
!opencl.ocl.version = !{!1}
150+
!opencl.spir.version = !{!1}
151+
152+
!0 = !{i32 1, !"wchar_size", i32 4}
153+
!1 = !{i32 1, i32 2}
154+
!3 = distinct !{!3, !4}
155+
!4 = !{!"llvm.loop.ivdep.enable"}
156+
!5 = distinct !{!5, !6}
157+
!6 = !{!"llvm.loop.ivdep.safelen", i32 2}
158+
!7 = distinct !{!7, !8}
159+
!8 = !{!"llvm.loop.ii.count", i32 2}
160+
!9 = distinct !{!9, !10}
161+
!10 = !{!"llvm.loop.max_concurrency.count", i32 2}
162+
!11 = distinct !{!11, !8, !10}
163+
164+
; CHECK-LLVM: br i1 %cmp, label %for.body, label %for.end, !llvm.loop ![[MD_A:[0-9]+]]
165+
; CHECK-LLVM: br i1 %cmp{{[0-9]+}}, label %for.body{{[0-9]+}}, label %for.end{{[0-9]+}}, !llvm.loop ![[MD_B:[0-9]+]]
166+
; CHECK-LLVM: br i1 %cmp{{[0-9]+}}, label %for.body{{[0-9]+}}, label %for.end{{[0-9]+}}, !llvm.loop ![[MD_C:[0-9]+]]
167+
; CHECK-LLVM: br i1 %cmp{{[0-9]+}}, label %for.body{{[0-9]+}}, label %for.end{{[0-9]+}}, !llvm.loop ![[MD_D:[0-9]+]]
168+
; CHECK-LLVM: br i1 %cmp{{[0-9]+}}, label %for.body{{[0-9]+}}, label %for.end{{[0-9]+}}, !llvm.loop ![[MD_E:[0-9]+]]
169+
170+
; CHECK-LLVM: ![[MD_A]] = distinct !{![[MD_A]], ![[MD_ivdep_enable:[0-9]+]]}
171+
; CHECK-LLVM: ![[MD_ivdep_enable]] = !{!"llvm.loop.ivdep.enable"}
172+
; CHECK-LLVM: ![[MD_B]] = distinct !{![[MD_B]], ![[MD_ivdep:[0-9]+]]}
173+
; CHECK-LLVM: ![[MD_ivdep]] = !{!"llvm.loop.ivdep.safelen", i32 2}
174+
; CHECK-LLVM: ![[MD_C]] = distinct !{![[MD_C]], ![[MD_ii:[0-9]+]]}
175+
; CHECK-LLVM: ![[MD_ii]] = !{!"llvm.loop.ii.count", i32 2}
176+
; CHECK-LLVM: ![[MD_D]] = distinct !{![[MD_D]], ![[MD_max_concurrency:[0-9]+]]}
177+
; CHECK-LLVM: ![[MD_max_concurrency]] = !{!"llvm.loop.max_concurrency.count", i32 2}
178+
; CHECK-LLVM: ![[MD_E]] = distinct !{![[MD_E]], ![[MD_ii:[0-9]+]], ![[MD_max_concurrency:[0-9]+]]}

0 commit comments

Comments
 (0)