Skip to content

Commit a172c3a

Browse files
committed
[SYCL][sycl-post-link] Implement transformation of sycl-properties annotations
This commit adds the transformation from llvm.ptr.annotations calls with "sycl-properties" as the annotation string into llvm.ptr.annotations calls with decoration IDs and values in a format the SPIR-V translator is able to translate into SPIR-V decorations. Signed-off-by: Steffen Larsen <[email protected]>
1 parent a54d6d8 commit a172c3a

File tree

3 files changed

+233
-0
lines changed

3 files changed

+233
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
; RUN: sycl-post-link --device-globals --ir-output-only -S %s -o %t.ll
2+
; RUN: FileCheck %s -input-file=%t.ll
3+
;
4+
; TODO: Remove --device-globals once other features start using compile-time
5+
; properties.
6+
;
7+
; Tests the translation of "sycl-properties" pointer annotations to pointer
8+
; annotations the SPIR-V translator will produce decorations from.
9+
; NOTE: These use SYCL property meta-names that are currently only intended for
10+
; use in attributes-to-metadata translations, but sycl-post-link does not
11+
; currently make the distinction so we will use them for the purpose of
12+
; testing the transformations.
13+
14+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64"
15+
target triple = "spir64-unknown-unknown"
16+
17+
%struct.foo = type { i32 addrspace(4)*, i32 addrspace(4)*, i32 addrspace(4)*, i32 addrspace(4)*, i32 addrspace(4)*, i32 addrspace(4)*, i32 addrspace(4)* }
18+
19+
$_ZTSZ4mainEUlvE_ = comdat any
20+
21+
@.str = private unnamed_addr constant [16 x i8] c"sycl-properties\00", section "llvm.metadata"
22+
@.str.1 = private unnamed_addr constant [36 x i8] c"sycl-properties-ptr-annotations.cpp\00", section "llvm.metadata"
23+
@.str.2 = private unnamed_addr constant [15 x i8] c"sycl-init-mode\00", section "llvm.metadata"
24+
@.str.3 = private unnamed_addr constant [2 x i8] c"1\00", section "llvm.metadata"
25+
@.str.4 = private unnamed_addr constant [22 x i8] c"sycl-implement-in-csr\00", section "llvm.metadata"
26+
@.str.5 = private unnamed_addr constant [5 x i8] c"true\00", section "llvm.metadata"
27+
@.args = private unnamed_addr constant { [15 x i8]*, [2 x i8]*, [22 x i8]*, [5 x i8]* } { [15 x i8]* @.str.2, [2 x i8]*@.str.3, [22 x i8]* @.str.4, [5 x i8]* @.str.5 }, section "llvm.metadata"
28+
@.args.6 = private unnamed_addr constant { [15 x i8]*, [2 x i8]* } { [15 x i8]* @.str.2, [2 x i8]* @.str.3 }, section "llvm.metadata"
29+
@.args.7 = private unnamed_addr constant { [22 x i8]*, [5 x i8]* } { [22 x i8]* @.str.4, [5 x i8]* @.str.5 }, section "llvm.metadata"
30+
@.args.8 = private unnamed_addr constant { [15 x i8]*, [2 x i8]*, [22 x i8]*, [5 x i8]* } { [15 x i8]* @.str.2, [2 x i8]* @.str.3, [22 x i8]* @.str.4, [5 x i8]* @.str.5 }, section "llvm.metadata"
31+
@.str.9 = private unnamed_addr constant [18 x i8] c"sycl-unrecognized\00", section "llvm.metadata"
32+
@.args.10 = private unnamed_addr constant { [15 x i8]*, [2 x i8]*, [18 x i8]*, i8* } { [15 x i8]* @.str.2, [2 x i8]* @.str.3, [18 x i8]* @.str.9, i8* null }, section "llvm.metadata"
33+
@.args.11 = private unnamed_addr constant { [18 x i8]*, i8* } { [18 x i8]* @.str.9, i8* null }, section "llvm.metadata"
34+
35+
;CHECK: @[[NewAnnotStr1:.*]] = private unnamed_addr constant [24 x i8] c"{6148:\221\22}{6149:\22true\22}\00", section "llvm.metadata"
36+
;CHECK: @[[NewAnnotStr2:.*]] = private unnamed_addr constant [11 x i8] c"{6148:\221\22}\00", section "llvm.metadata"
37+
;CHECK: @[[NewAnnotStr3:.*]] = private unnamed_addr constant [14 x i8] c"{6149:\22true\22}\00", section "llvm.metadata"
38+
39+
; Function Attrs: mustprogress norecurse
40+
define weak_odr dso_local spir_kernel void @_ZTSZ4mainEUlvE_() local_unnamed_addr #0 comdat !kernel_arg_buffer_location !7 !sycl_kernel_omit_args !7 {
41+
entry:
42+
%x.i = alloca %struct.foo, align 8
43+
%x.ascast.i = addrspacecast %struct.foo* %x.i to %struct.foo addrspace(4)*
44+
%0 = bitcast %struct.foo* %x.i to i8*
45+
%1 = addrspacecast i8* %0 to i8 addrspace(4)*
46+
%2 = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %1, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 5, i8* bitcast ({ [15 x i8]*, [2 x i8]*, [22 x i8]*, [5 x i8]* }* @.args to i8*)) #2
47+
; CHECK: %{{.*}} = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %1, i8* getelementptr inbounds ([24 x i8], [24 x i8]* @[[NewAnnotStr1]], i32 0, i32 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 5, i8* null)
48+
%b.i = getelementptr inbounds %struct.foo, %struct.foo addrspace(4)* %x.ascast.i, i64 0, i32 1
49+
%3 = bitcast i32 addrspace(4)* addrspace(4)* %b.i to i8 addrspace(4)*
50+
%4 = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %3, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 6, i8* bitcast ({ [15 x i8]*, [2 x i8]* }* @.args.6 to i8*)) #2
51+
; CHECK: %{{.*}} = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %3, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @[[NewAnnotStr2]], i32 0, i32 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 6, i8* null)
52+
%c.i = getelementptr inbounds %struct.foo, %struct.foo addrspace(4)* %x.ascast.i, i64 0, i32 2
53+
%5 = bitcast i32 addrspace(4)* addrspace(4)* %c.i to i8 addrspace(4)*
54+
%6 = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %5, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 7, i8* bitcast ({ [22 x i8]*, [5 x i8]* }* @.args.7 to i8*)) #2
55+
; CHECK: %{{.*}} = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %5, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @[[NewAnnotStr3]], i32 0, i32 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 7, i8* null)
56+
%d.i = getelementptr inbounds %struct.foo, %struct.foo addrspace(4)* %x.ascast.i, i64 0, i32 3
57+
%7 = bitcast i32 addrspace(4)* addrspace(4)* %d.i to i8 addrspace(4)*
58+
%8 = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %7, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 8, i8* bitcast ({ [15 x i8]*, [2 x i8]*, [22 x i8]*, [5 x i8]* }* @.args.8 to i8*)) #2
59+
; CHECK: %{{.*}} = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %7, i8* getelementptr inbounds ([24 x i8], [24 x i8]* @[[NewAnnotStr1]], i32 0, i32 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 8, i8* null)
60+
%e.i = getelementptr inbounds %struct.foo, %struct.foo addrspace(4)* %x.ascast.i, i64 0, i32 4
61+
%9 = bitcast i32 addrspace(4)* addrspace(4)* %e.i to i8 addrspace(4)*
62+
%10 = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %9, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 9, i8* bitcast ({ [15 x i8]*, [2 x i8]*, [18 x i8]*, i8* }* @.args.10 to i8*)) #2
63+
; CHECK: %{{.*}} = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %9, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @[[NewAnnotStr2]], i32 0, i32 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 9, i8* null)
64+
%f.i = getelementptr inbounds %struct.foo, %struct.foo addrspace(4)* %x.ascast.i, i64 0, i32 5
65+
%11 = bitcast i32 addrspace(4)* addrspace(4)* %f.i to i8 addrspace(4)*
66+
%12 = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %11, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 10, i8* bitcast ({ [18 x i8]*, i8* }* @.args.11 to i8*)) #2
67+
; CHECK-NOT: %{{.*}} = call i8 addrspace(4)* @llvm.ptr.annotation.
68+
%g.i = getelementptr inbounds %struct.foo, %struct.foo addrspace(4)* %x.ascast.i, i64 0, i32 6
69+
%13 = bitcast i32 addrspace(4)* addrspace(4)* %g.i to i8 addrspace(4)*
70+
%14 = call i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)* %13, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.1, i64 0, i64 0), i32 11, i8* null) #2
71+
ret void
72+
}
73+
74+
; Function Attrs: inaccessiblememonly nofree nosync nounwind willreturn
75+
declare i8 addrspace(4)* @llvm.ptr.annotation.p4i8(i8 addrspace(4)*, i8*, i8*, i32, i8*) #1
76+
77+
attributes #0 = { mustprogress norecurse "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="sycl-properties-ptr-annotations.cpp" "uniform-work-group-size"="true" }
78+
attributes #1 = { inaccessiblememonly nofree nosync nounwind willreturn }
79+
attributes #2 = { nounwind }
80+
81+
!opencl.spir.version = !{!0, !0, !0, !0, !0, !0}
82+
!spirv.Source = !{!1, !1, !1, !1, !1, !1}
83+
!llvm.ident = !{!2, !3, !3, !3, !4, !3}
84+
!llvm.module.flags = !{!5, !6}
85+
86+
!0 = !{i32 1, i32 2}
87+
!1 = !{i32 4, i32 100000}
88+
!2 = !{!"clang version 15.0.0"}
89+
!3 = !{!"clang version 15.0.0"}
90+
!4 = !{!"clang version 15.0.0"}
91+
!5 = !{i32 1, !"wchar_size", i32 4}
92+
!6 = !{i32 7, !"frame-pointer", i32 2}
93+
!7 = !{}

llvm/tools/sycl-post-link/CompileTimePropertiesPass.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "llvm/ADT/StringMap.h"
1616
#include "llvm/ADT/StringRef.h"
1717
#include "llvm/IR/Module.h"
18+
#include "llvm/IR/Operator.h"
1819

1920
using namespace llvm;
2021

@@ -90,6 +91,15 @@ MDNode *buildSpirvDecorMetadata(LLVMContext &Ctx, uint32_t OpCode,
9091
return MDNode::get(Ctx, MD);
9192
}
9293

94+
Optional<StringRef> getGlobalVariableString(const Value *StringV) {
95+
if (const auto *StringGV = dyn_cast<GlobalVariable>(StringV))
96+
if (const auto *StringData =
97+
dyn_cast<ConstantDataSequential>(StringGV->getInitializer()))
98+
if (StringData->isCString())
99+
return StringData->getAsCString();
100+
return {};
101+
}
102+
93103
} // anonymous namespace
94104

95105
PreservedAnalyses CompileTimePropertiesPass::run(Module &M,
@@ -142,9 +152,124 @@ PreservedAnalyses CompileTimePropertiesPass::run(Module &M,
142152
}
143153
}
144154

155+
// Check pointer annotations.
156+
SmallVector<IntrinsicInst *, 4> RemovableAnnots;
157+
for (Function &F : M)
158+
for (BasicBlock &BB : F)
159+
for (Instruction &Inst : BB)
160+
if (auto *IntrInst = dyn_cast<IntrinsicInst>(&Inst))
161+
if (IntrInst->getIntrinsicID() == Intrinsic::ptr_annotation &&
162+
transformSYCLPropertiesAnnotation(M, IntrInst, RemovableAnnots))
163+
CompileTimePropertiesMet = true;
164+
165+
// Remove irrelevant "sycl-properties" annotations after the transformations.
166+
for (IntrinsicInst *IntrInst : RemovableAnnots) {
167+
assert(IntrInst->getNumUses() == 0);
168+
IntrInst->eraseFromParent();
169+
}
170+
145171
// The pass just adds some metadata to the module, it should not ruin
146172
// any analysis, but we need return PreservedAnalyses::none() to inform
147173
// the caller that at least one compile-time property was met.
148174
return CompileTimePropertiesMet ? PreservedAnalyses::none()
149175
: PreservedAnalyses::all();
150176
}
177+
178+
bool CompileTimePropertiesPass::transformSYCLPropertiesAnnotation(
179+
Module &M, IntrinsicInst *IntrInst,
180+
SmallVector<IntrinsicInst *, 4> &RemovableAnnotations) {
181+
assert(IntrInst->getIntrinsicID() == Intrinsic::ptr_annotation &&
182+
"Intrinsic is not a pointer annotation.");
183+
184+
// Get the global variable with the annotation string.
185+
const GlobalVariable *AnnotStrArgGV = nullptr;
186+
if (auto *GEP = dyn_cast<GEPOperator>(IntrInst->getArgOperand(1)))
187+
if (auto *C = dyn_cast<Constant>(GEP->getOperand(0)))
188+
AnnotStrArgGV = dyn_cast<GlobalVariable>(C);
189+
if (!AnnotStrArgGV)
190+
return false;
191+
192+
// We only need to consider annotations with "sycl-properties" annotation
193+
// string.
194+
Optional<StringRef> AnnotStr = getGlobalVariableString(AnnotStrArgGV);
195+
if (!AnnotStr || AnnotStr->str() != "sycl-properties")
196+
return false;
197+
198+
// Read the annotation values and create the new annotation string.
199+
std::string NewAnnotString = "";
200+
if (const auto *Cast =
201+
dyn_cast<BitCastOperator>(IntrInst->getArgOperand(4))) {
202+
if (const auto *AnnotValsGV =
203+
dyn_cast<GlobalVariable>(Cast->getOperand(0))) {
204+
if (const auto *AnnotValsAggr =
205+
dyn_cast<ConstantAggregate>(AnnotValsGV->getInitializer())) {
206+
assert(
207+
(AnnotValsAggr->getNumOperands() & 1) == 0 &&
208+
"sycl-properties annotation must have an even number of annotation "
209+
"values.");
210+
211+
// Iterate over the pairs of property meta-names and meta-values.
212+
for (size_t I = 0; I < AnnotValsAggr->getNumOperands(); I += 2) {
213+
Optional<StringRef> PropMetaName =
214+
getGlobalVariableString(AnnotValsAggr->getOperand(I));
215+
Optional<StringRef> PropMetaValue =
216+
getGlobalVariableString(AnnotValsAggr->getOperand(I + 1));
217+
218+
assert(PropMetaName &&
219+
"Unexpected format for property name in annotation.");
220+
221+
auto DecorIt = SpirvDecorMap.find(*PropMetaName);
222+
if (DecorIt == SpirvDecorMap.end())
223+
continue;
224+
uint32_t DecorCode = DecorIt->second.Code;
225+
226+
// Expected format is '{X}' or '{X:Y}' where X is decoration ID and
227+
// Y is the value if present. It encloses Y in " to ensure that
228+
// string values are handled correctly. Note that " around values are
229+
// always valid, even if the decoration parameters are not strings.
230+
NewAnnotString += "{" + std::to_string(DecorCode);
231+
if (PropMetaValue)
232+
NewAnnotString += ":\"" + PropMetaValue->str() + "\"";
233+
NewAnnotString += "}";
234+
}
235+
}
236+
}
237+
}
238+
239+
// If the new annotation string is empty there is no reason to keep it, so
240+
// replace it with the first operand and mark it for removal.
241+
if (NewAnnotString == "") {
242+
IntrInst->replaceAllUsesWith(IntrInst->getOperand(0));
243+
RemovableAnnotations.push_back(IntrInst);
244+
return true;
245+
}
246+
247+
// Either reuse a previously generated one or create a new global variable
248+
// with the new annotation string.
249+
GlobalVariable *NewAnnotStringGV = nullptr;
250+
auto ExistingNewAnnotStringIt = NewAnnotationStrings.find(NewAnnotString);
251+
if (ExistingNewAnnotStringIt != NewAnnotationStrings.end()) {
252+
NewAnnotStringGV = ExistingNewAnnotStringIt->second;
253+
} else {
254+
Constant *NewAnnotStringData =
255+
ConstantDataArray::getString(M.getContext(), NewAnnotString);
256+
NewAnnotStringGV = new GlobalVariable(M, NewAnnotStringData->getType(),
257+
true, GlobalValue::PrivateLinkage,
258+
NewAnnotStringData, ".str");
259+
NewAnnotStringGV->setSection(AnnotStrArgGV->getSection());
260+
NewAnnotStringGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
261+
NewAnnotationStrings.insert({NewAnnotString, NewAnnotStringGV});
262+
}
263+
264+
// Replace the annotation string with a bitcast of the new global variable.
265+
Type *Int8Ty = IntegerType::getInt8Ty(M.getContext());
266+
IntrInst->setArgOperand(
267+
1, ConstantExpr::getBitCast(NewAnnotStringGV, Int8Ty->getPointerTo(0)));
268+
269+
// The values are not in the annotation string, so we can remove the original
270+
// annotation value.
271+
unsigned DefaultAS = M.getDataLayout().getDefaultGlobalsAddressSpace();
272+
PointerType *Int8DefaultASPtrTy = Int8Ty->getPointerTo(DefaultAS);
273+
IntrInst->setArgOperand(4, ConstantPointerNull::get(Int8DefaultASPtrTy));
274+
return true;
275+
}

llvm/tools/sycl-post-link/CompileTimePropertiesPass.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414

1515
#pragma once
1616

17+
#include "llvm/IR/IntrinsicInst.h"
1718
#include "llvm/IR/PassManager.h"
1819

1920
#include <cassert>
21+
#include <unordered_map>
2022

2123
namespace llvm {
2224

@@ -26,6 +28,19 @@ class CompileTimePropertiesPass
2628
// Enriches the module with metadata that describes the found variables for
2729
// the SPIRV-LLVM Translator.
2830
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
31+
32+
private:
33+
// Transforms llvm.ptr.annotations with the "sycl-properties" annotation
34+
// string into another llvm.ptr.annotations call in a format consumable by
35+
// the SPIR-V translator.
36+
bool transformSYCLPropertiesAnnotation(
37+
Module &M, IntrinsicInst *IntrInst,
38+
SmallVector<IntrinsicInst *, 4> &RemovableAnnotations);
39+
40+
// Map for keeping track of global variables generated for annotation strings.
41+
// This allows reuse for annotations with the same generated annotation
42+
// strings.
43+
std::unordered_map<std::string, GlobalVariable *> NewAnnotationStrings;
2944
};
3045

3146
namespace detail {

0 commit comments

Comments
 (0)