Skip to content

Commit b4d6f0b

Browse files
author
Leonid Pauzin
authored
Implemented SPIR-V support for loopcount attributes (#1062)
* Implemented SPIR-V support for loopcount attrs There are 3 metadata, which map on LoopControlLoopCountINTELMask. Mask has 3 parameters. Default values are -1. Signed-off-by: Leonid Pauzin <[email protected]>
1 parent 2b8b4a8 commit b4d6f0b

File tree

7 files changed

+203
-8
lines changed

7 files changed

+203
-8
lines changed

lib/SPIRV/SPIRVInternal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,7 @@ std::vector<Value *> getInt32(Module *M, const std::vector<int> &Value);
803803
ConstantInt *getSizet(Module *M, uint64_t Value);
804804

805805
/// Get metadata operand as int.
806-
int getMDOperandAsInt(MDNode *N, unsigned I);
806+
int64_t getMDOperandAsInt(MDNode *N, unsigned I);
807807

808808
/// Get metadata operand as string.
809809
std::string getMDOperandAsString(MDNode *N, unsigned I);

lib/SPIRV/SPIRVReader.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,16 @@ SPIRVToLLVM::getMetadataFromNameAndParameter(std::string Name,
739739
ConstantInt::get(Type::getInt32Ty(*Context), Parameter))};
740740
}
741741

742+
inline llvm::MDNode *
743+
SPIRVToLLVM::getMetadataFromNameAndParameter(std::string Name,
744+
int64_t Parameter) {
745+
std::vector<llvm::Metadata *> Metadata = {
746+
MDString::get(*Context, Name),
747+
ConstantAsMetadata::get(
748+
ConstantInt::get(Type::getInt64Ty(*Context), Parameter))};
749+
return llvm::MDNode::get(*Context, Metadata);
750+
}
751+
742752
template <typename LoopInstType>
743753
void SPIRVToLLVM::setLLVMLoopMetadata(const LoopInstType *LM,
744754
const Loop *LoopObj) {
@@ -987,6 +997,39 @@ void SPIRVToLLVM::setLLVMLoopMetadata(const LoopInstType *LM,
987997
}
988998
if (LC & LoopControlNoFusionINTELMask)
989999
Metadata.push_back(getMetadataFromName("llvm.loop.fusion.disable"));
1000+
if (LC & spv::internal::LoopControlLoopCountINTELMask) {
1001+
// LoopCountINTELMask parameters are int64 and each parameter is stored
1002+
// as 2 SPIRVWords (int32)
1003+
assert(NumParam + 6 <= LoopControlParameters.size() &&
1004+
"Missing loop control parameter!");
1005+
1006+
uint64_t LoopCountMin =
1007+
static_cast<uint64_t>(LoopControlParameters[NumParam++]);
1008+
LoopCountMin |= static_cast<uint64_t>(LoopControlParameters[NumParam++])
1009+
<< 32;
1010+
if (static_cast<int64_t>(LoopCountMin) >= 0) {
1011+
Metadata.push_back(getMetadataFromNameAndParameter(
1012+
"llvm.loop.intel.loopcount_min", static_cast<int64_t>(LoopCountMin)));
1013+
}
1014+
1015+
uint64_t LoopCountMax =
1016+
static_cast<uint64_t>(LoopControlParameters[NumParam++]);
1017+
LoopCountMax |= static_cast<uint64_t>(LoopControlParameters[NumParam++])
1018+
<< 32;
1019+
if (static_cast<int64_t>(LoopCountMax) >= 0) {
1020+
Metadata.push_back(getMetadataFromNameAndParameter(
1021+
"llvm.loop.intel.loopcount_max", static_cast<int64_t>(LoopCountMax)));
1022+
}
1023+
1024+
uint64_t LoopCountAvg =
1025+
static_cast<uint64_t>(LoopControlParameters[NumParam++]);
1026+
LoopCountAvg |= static_cast<uint64_t>(LoopControlParameters[NumParam++])
1027+
<< 32;
1028+
if (static_cast<int64_t>(LoopCountAvg) >= 0) {
1029+
Metadata.push_back(getMetadataFromNameAndParameter(
1030+
"llvm.loop.intel.loopcount_avg", static_cast<int64_t>(LoopCountAvg)));
1031+
}
1032+
}
9901033
llvm::MDNode *Node = llvm::MDNode::get(*Context, Metadata);
9911034

9921035
// Set the first operand to refer itself

lib/SPIRV/SPIRVReader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ class SPIRVToLLVM {
258258
inline llvm::Metadata *getMetadataFromName(std::string Name);
259259
inline std::vector<llvm::Metadata *>
260260
getMetadataFromNameAndParameter(std::string Name, SPIRVWord Parameter);
261+
inline MDNode *getMetadataFromNameAndParameter(std::string Name,
262+
int64_t Parameter);
261263
void insertImageNameAccessQualifier(SPIRV::SPIRVTypeImage *ST,
262264
std::string &Name);
263265
template <class Source, class Func> bool foreachFuncCtlMask(Source, Func);

lib/SPIRV/SPIRVUtil.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ ConstantInt *getSizet(Module *M, uint64_t Value) {
842842
// Functions for getting metadata
843843
//
844844
///////////////////////////////////////////////////////////////////////////////
845-
int getMDOperandAsInt(MDNode *N, unsigned I) {
845+
int64_t getMDOperandAsInt(MDNode *N, unsigned I) {
846846
return mdconst::dyn_extract<ConstantInt>(N->getOperand(I))->getZExtValue();
847847
}
848848

lib/SPIRV/SPIRVWriter.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,12 @@ LLVMToSPIRVBase::getLoopControl(const BranchInst *Branch,
11131113

11141114
size_t LoopControl = spv::LoopControlMaskNone;
11151115
std::vector<std::pair<SPIRVWord, SPIRVWord>> ParametersToSort;
1116+
// If only a subset of loop count parameters is defined in metadata
1117+
// then undefined ones should have a default value -1 in SPIR-V.
1118+
// Preset all loop count parameters with the default value.
1119+
struct LoopCountInfo {
1120+
int64_t Min = -1, Max = -1, Avg = -1;
1121+
} LoopCount;
11161122

11171123
// Unlike with most of the cases, some loop metadata specifications
11181124
// can occur multiple times - for these, all correspondent tokens
@@ -1212,11 +1218,41 @@ LLVMToSPIRVBase::getLoopControl(const BranchInst *Branch,
12121218
BM->addExtension(ExtensionID::SPV_INTEL_fpga_loop_controls);
12131219
BM->addCapability(CapabilityFPGALoopControlsINTEL);
12141220
LoopControl |= spv::LoopControlNoFusionINTELMask;
1221+
} else if (S == "llvm.loop.intel.loopcount_min") {
1222+
BM->addExtension(ExtensionID::SPV_INTEL_fpga_loop_controls);
1223+
BM->addCapability(CapabilityFPGALoopControlsINTEL);
1224+
LoopCount.Min = getMDOperandAsInt(Node, 1);
1225+
LoopControl |= spv::internal::LoopControlLoopCountINTELMask;
1226+
} else if (S == "llvm.loop.intel.loopcount_max") {
1227+
BM->addExtension(ExtensionID::SPV_INTEL_fpga_loop_controls);
1228+
BM->addCapability(CapabilityFPGALoopControlsINTEL);
1229+
LoopCount.Max = getMDOperandAsInt(Node, 1);
1230+
LoopControl |= spv::internal::LoopControlLoopCountINTELMask;
1231+
} else if (S == "llvm.loop.intel.loopcount_avg") {
1232+
BM->addExtension(ExtensionID::SPV_INTEL_fpga_loop_controls);
1233+
BM->addCapability(CapabilityFPGALoopControlsINTEL);
1234+
LoopCount.Avg = getMDOperandAsInt(Node, 1);
1235+
LoopControl |= spv::internal::LoopControlLoopCountINTELMask;
12151236
}
12161237
}
12171238
}
12181239
}
1219-
1240+
if (LoopControl & spv::internal::LoopControlLoopCountINTELMask) {
1241+
// LoopCountINTELMask have int64 literal parameters and we need to store
1242+
// int64 into 2 SPIRVWords
1243+
ParametersToSort.emplace_back(spv::internal::LoopControlLoopCountINTELMask,
1244+
static_cast<SPIRVWord>(LoopCount.Min));
1245+
ParametersToSort.emplace_back(spv::internal::LoopControlLoopCountINTELMask,
1246+
static_cast<SPIRVWord>(LoopCount.Min >> 32));
1247+
ParametersToSort.emplace_back(spv::internal::LoopControlLoopCountINTELMask,
1248+
static_cast<SPIRVWord>(LoopCount.Max));
1249+
ParametersToSort.emplace_back(spv::internal::LoopControlLoopCountINTELMask,
1250+
static_cast<SPIRVWord>(LoopCount.Max >> 32));
1251+
ParametersToSort.emplace_back(spv::internal::LoopControlLoopCountINTELMask,
1252+
static_cast<SPIRVWord>(LoopCount.Avg));
1253+
ParametersToSort.emplace_back(spv::internal::LoopControlLoopCountINTELMask,
1254+
static_cast<SPIRVWord>(LoopCount.Avg >> 32));
1255+
}
12201256
// If any loop control parameters were held back until fully collected,
12211257
// now is the time to move the information to the main parameters collection
12221258
if (!DependencyArrayParameters.empty()) {
@@ -1235,11 +1271,11 @@ LLVMToSPIRVBase::getLoopControl(const BranchInst *Branch,
12351271
LoopControl |= spv::LoopControlDependencyArrayINTELMask;
12361272
}
12371273

1238-
std::sort(ParametersToSort.begin(), ParametersToSort.end(),
1239-
[](const std::pair<SPIRVWord, SPIRVWord> &CompareLeft,
1240-
const std::pair<SPIRVWord, SPIRVWord> &CompareRight) {
1241-
return CompareLeft.first < CompareRight.first;
1242-
});
1274+
std::stable_sort(ParametersToSort.begin(), ParametersToSort.end(),
1275+
[](const std::pair<SPIRVWord, SPIRVWord> &CompareLeft,
1276+
const std::pair<SPIRVWord, SPIRVWord> &CompareRight) {
1277+
return CompareLeft.first < CompareRight.first;
1278+
});
12431279
for (auto Param : ParametersToSort)
12441280
Parameters.push_back(Param.second);
12451281

lib/SPIRV/libSPIRV/spirv_internal.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ enum InternalMemoryAccessMask {
7171

7272
enum InternalExecutionMode { IExecModeFastCompositeKernelINTEL = 6088 };
7373

74+
enum InternalLoopControlMask { ILoopControlLoopCountINTELMask = 0x1000000 };
75+
7476
constexpr LinkageType LinkageTypeInternal =
7577
static_cast<LinkageType>(ILTInternal);
7678

@@ -121,6 +123,9 @@ constexpr MemoryAccessMask MemoryAccessNoAliasINTELMask =
121123
constexpr ExecutionMode ExecutionModeFastCompositeKernelINTEL =
122124
static_cast<ExecutionMode>(IExecModeFastCompositeKernelINTEL);
123125

126+
constexpr LoopControlMask LoopControlLoopCountINTELMask =
127+
static_cast<LoopControlMask>(ILoopControlLoopCountINTELMask);
128+
124129
} // namespace internal
125130
} // namespace spv
126131

test/transcoding/SPV_INTEL_fpga_loop_controls/FPGALoopAttr.ll

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,96 @@ for.end36: ; preds = %for.cond29
168168
ret void
169169
}
170170

171+
; Function Attrs: convergent norecurse nounwind mustprogress
172+
define linkonce_odr dso_local spir_func void @_Z18loop_count_controlILi12EEvv() #0 {
173+
entry:
174+
%a = alloca [10 x i32], align 4
175+
%a.ascast = addrspacecast [10 x i32]* %a to [10 x i32] addrspace(4)*
176+
%i = alloca i32, align 4
177+
%i.ascast = addrspacecast i32* %i to i32 addrspace(4)*
178+
%cleanup.dest.slot = alloca i32, align 4
179+
%i1 = alloca i32, align 4
180+
%i1.ascast = addrspacecast i32* %i1 to i32 addrspace(4)*
181+
%cleanup.dest.slot5 = alloca i32, align 4
182+
%0 = bitcast [10 x i32]* %a to i8*
183+
call void @llvm.lifetime.start.p0i8(i64 40, i8* %0)
184+
%1 = bitcast i32* %i to i8*
185+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %1)
186+
store i32 0, i32 addrspace(4)* %i.ascast, align 4
187+
br label %for.cond
188+
; Per SPIR-V spec extension INTEL/SPV_INTEL_fpga_loop_controls,
189+
; LoopControlLoopCountINTELMask = 0x1000000 (16777216)
190+
; CHECK-SPIRV: LoopMerge [[#]] [[#]] 16777216 4294967295 4294967295 4294967295 4294967295 12 0
191+
; CHECK-SPIRV-NEXT: BranchConditional [[#]] [[#]] [[#]]
192+
; CHECK-SPIRV-NEGATIVE-NOT: LoopMerge [[#]] [[#]] 16777216
193+
for.cond: ; preds = %for.inc, %entry
194+
%2 = load i32, i32 addrspace(4)* %i.ascast, align 4
195+
%cmp = icmp ne i32 %2, 10
196+
br i1 %cmp, label %for.body, label %for.cond.cleanup
197+
198+
for.cond.cleanup: ; preds = %for.cond
199+
%3 = bitcast i32* %i to i8*
200+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %3)
201+
br label %for.end
202+
203+
for.body: ; preds = %for.cond
204+
%4 = load i32, i32 addrspace(4)* %i.ascast, align 4
205+
%idxprom = sext i32 %4 to i64
206+
%arrayidx = getelementptr inbounds [10 x i32], [10 x i32] addrspace(4)* %a.ascast, i64 0, i64 %idxprom
207+
store i32 0, i32 addrspace(4)* %arrayidx, align 4
208+
br label %for.inc
209+
210+
for.inc: ; preds = %for.body
211+
%5 = load i32, i32 addrspace(4)* %i.ascast, align 4
212+
%inc = add nsw i32 %5, 1
213+
store i32 %inc, i32 addrspace(4)* %i.ascast, align 4
214+
br label %for.cond, !llvm.loop !12
215+
216+
for.end: ; preds = %for.cond.cleanup
217+
%6 = bitcast i32* %i1 to i8*
218+
call void @llvm.lifetime.start.p0i8(i64 4, i8* %6)
219+
store i32 0, i32 addrspace(4)* %i1.ascast, align 4
220+
br label %for.cond2
221+
222+
; Per SPIR-V spec extension INTEL/SPV_INTEL_fpga_loop_controls,
223+
; spv::internal::LoopControlLoopCountINTELMask = 0x1000000 (16777216)
224+
; Parameters 4 0 = 4, 100 1 = 4294967396, 21 0 = 21
225+
; CHECK-SPIRV: LoopMerge [[#]] [[#]] 16777216 4 0 100 1 21 0
226+
; CHECK-SPIRV-NEXT: BranchConditional [[#]] [[#]] [[#]]
227+
; CHECK-SPIRV-NEGATIVE-NOT: LoopMerge [[#]] [[#]] 16777216
228+
for.cond2: ; preds = %for.inc9, %for.end
229+
%7 = load i32, i32 addrspace(4)* %i1.ascast, align 4
230+
%cmp3 = icmp ne i32 %7, 10
231+
br i1 %cmp3, label %for.body6, label %for.cond.cleanup4
232+
233+
for.cond.cleanup4: ; preds = %for.cond2
234+
%8 = bitcast i32* %i1 to i8*
235+
call void @llvm.lifetime.end.p0i8(i64 4, i8* %8)
236+
br label %for.end11
237+
238+
for.body6: ; preds = %for.cond2
239+
%9 = load i32, i32 addrspace(4)* %i1.ascast, align 4
240+
%idxprom7 = sext i32 %9 to i64
241+
%arrayidx8 = getelementptr inbounds [10 x i32], [10 x i32] addrspace(4)* %a.ascast, i64 0, i64 %idxprom7
242+
store i32 0, i32 addrspace(4)* %arrayidx8, align 4
243+
br label %for.inc9
244+
245+
for.inc9: ; preds = %for.body6
246+
%10 = load i32, i32 addrspace(4)* %i1.ascast, align 4
247+
%inc10 = add nsw i32 %10, 1
248+
store i32 %inc10, i32 addrspace(4)* %i1.ascast, align 4
249+
br label %for.cond2, !llvm.loop !15
250+
251+
for.end11: ; preds = %for.cond.cleanup4
252+
%11 = bitcast [10 x i32]* %a to i8*
253+
call void @llvm.lifetime.end.p0i8(i64 40, i8* %11)
254+
ret void
255+
}
256+
257+
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture)
258+
259+
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture)
260+
171261
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" }
172262

173263
!llvm.module.flags = !{!0}
@@ -186,12 +276,22 @@ attributes #0 = { convergent noinline nounwind optnone "correctly-rounded-divide
186276
!9 = distinct !{!9, !10}
187277
!10 = !{!"llvm.loop.max_concurrency.count", i32 2}
188278
!11 = distinct !{!11, !8, !10}
279+
!12 = distinct !{!12, !13, !14}
280+
!13 = !{!"llvm.loop.mustprogress"}
281+
!14 = !{!"llvm.loop.intel.loopcount_avg", i64 12}
282+
!15 = distinct !{!15, !13, !16, !17, !18}
283+
!16 = !{!"llvm.loop.intel.loopcount_min", i64 4}
284+
;4294967396 = 2^32 + 100
285+
!17 = !{!"llvm.loop.intel.loopcount_max", i64 4294967396}
286+
!18 = !{!"llvm.loop.intel.loopcount_avg", i64 21}
189287

190288
; CHECK-LLVM: br label %for.cond{{[0-9]*}}, !llvm.loop ![[MD_A:[0-9]+]]
191289
; CHECK-LLVM: br label %for.cond{{[0-9]+}}, !llvm.loop ![[MD_B:[0-9]+]]
192290
; CHECK-LLVM: br label %for.cond{{[0-9]+}}, !llvm.loop ![[MD_C:[0-9]+]]
193291
; CHECK-LLVM: br label %for.cond{{[0-9]+}}, !llvm.loop ![[MD_D:[0-9]+]]
194292
; CHECK-LLVM: br label %for.cond{{[0-9]+}}, !llvm.loop ![[MD_E:[0-9]+]]
293+
; CHECK-LLVM: br label %for.cond{{.*}}, !llvm.loop ![[#MD_F:]]
294+
; CHECK-LLVM: br label %for.cond{{.*}}, !llvm.loop ![[#MD_G:]]
195295

196296
; CHECK-LLVM-NEGATIVE: br label %for.cond{{[0-9]*}}, !llvm.loop ![[MD_A:[0-9]+]]
197297
; CHECK-LLVM-NEGATIVE: br label %for.cond{{[0-9]+}}, !llvm.loop ![[MD_B:[0-9]+]]
@@ -206,10 +306,19 @@ attributes #0 = { convergent noinline nounwind optnone "correctly-rounded-divide
206306
; CHECK-LLVM: ![[MD_D]] = distinct !{![[MD_D]], ![[MD_max_concurrency:[0-9]+]]}
207307
; CHECK-LLVM: ![[MD_max_concurrency]] = !{!"llvm.loop.max_concurrency.count", i32 2}
208308
; CHECK-LLVM: ![[MD_E]] = distinct !{![[MD_E]], ![[MD_ii:[0-9]+]], ![[MD_max_concurrency:[0-9]+]]}
309+
; CHECK-LLVM: ![[#MD_F]] = distinct !{![[#MD_F]], ![[#MD_loop_count_avg:]]}
310+
; CHECK-LLVM: ![[#MD_loop_count_avg]] = !{!"llvm.loop.intel.loopcount_avg", i64 12}
311+
; CHECK-LLVM: ![[#MD_G]] = distinct !{![[#MD_G]], ![[#MD_loop_count_min:]], ![[#MD_loop_count_max:]], ![[#MD_loop_count_avg_1:]]}
312+
; CHECK-LLVM: ![[#MD_loop_count_min]] = !{!"llvm.loop.intel.loopcount_min", i64 4}
313+
; CHECK-LLVM: ![[#MD_loop_count_max]] = !{!"llvm.loop.intel.loopcount_max", i64 4294967396}
314+
; CHECK-LLVM: ![[#MD_loop_count_avg_1]] = !{!"llvm.loop.intel.loopcount_avg", i64 21}
209315

210316
; CHECK-LLVM-NEGATIVE: ![[MD_A]] = distinct !{![[MD_A]], ![[MD_ivdep_enable:[0-9]+]]}
211317
; CHECK-LLVM-NEGATIVE: ![[MD_ivdep_enable]] = !{!"llvm.loop.ivdep.enable"}
212318
; CHECK-LLVM-NEGATIVE: ![[MD_B]] = distinct !{![[MD_B]], ![[MD_ivdep:[0-9]+]]}
213319
; CHECK-LLVM-NEGATIVE: ![[MD_ivdep]] = !{!"llvm.loop.ivdep.safelen", i32 2}
214320
; CHECK-LLVM-NEGATIVE-NOT: !{{.*}} = !{!"llvm.loop.ii.count"{{.*}}}
215321
; CHECK-LLVM-NEGATIVE-NOT: !{{.*}} = !{!"llvm.loop.max_concurrency.count"{{.*}}}
322+
; CHECK-LLVM-NEGATIVE-NOT: !{{.*}} = !{!"llvm.loop.intel.loopcount_min"{{.*}}}
323+
; CHECK-LLVM-NEGATIVE-NOT: !{{.*}} = !{!"llvm.loop.intel.loopcount_max"{{.*}}}
324+
; CHECK-LLVM-NEGATIVE-NOT: !{{.*}} = !{!"llvm.loop.intel.loopcount_avg"{{.*}}}

0 commit comments

Comments
 (0)