Skip to content

Commit ac7c9ac

Browse files
author
Pavel Samolysov
authored
[sycl-post-link] Implement the CompileTimePropertiesPass module pass (#5409)
The pass enriches the LLVM IR with the !spirv.Decorations metadata for the properties of every device global variable. The implementation is in accordance to the "Changes to the sycl-post-link tool" chapter of the design document [1] and the "Property on a global variable" chapter of the design document [2]. We suppose the enumeration orders in the host_access and init_mode enumerations in the SYCL headers are the same as in the descriptions of the HostAccessINTEL and InitModeINTEL decorations in the SPV_INTEL_global_variable_decorations extension. [1] https://github.com/intel/llvm/blob/sycl/sycl/doc/DeviceGlobal.md#changes-to-the-sycl-post-link-tool [2] https://github.com/intel/llvm/blob/sycl/sycl/doc/CompileTimeProperties.md#property-on-a-global-variable
1 parent 96929b2 commit ac7c9ac

File tree

9 files changed

+442
-77
lines changed

9 files changed

+442
-77
lines changed

llvm/test/tools/sycl-post-link/device-globals/test_global_variable.ll

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
; RUN: sycl-post-link --device-globals -S %s -o %t.files.table
22
; RUN: FileCheck %s -input-file=%t.files_0.prop --check-prefix CHECK-PROP
3+
; RUN: FileCheck %s -input-file=%t.files_0.ll --check-prefix CHECK-IR
4+
; RUN: sycl-post-link --ir-output-only --device-globals %s -S -o - | FileCheck %s --check-prefix CHECK-IR
35

46
; This test is intended to check that DeviceGlobalPass adds all the required
7+
; metadata nodes to every device global variable as well as the required
58
; properties in the 'SYCL/device globals' property set and handles the
69
; 'device_image_scope' attribute written in any allowed form.
710

@@ -14,10 +17,15 @@ target triple = "spir64-unknown-unknown"
1417
%class.anon.0 = type { i8 }
1518

1619
@_ZL7dg_int1 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.0" zeroinitializer, align 8 #0
20+
; CHECK-IR: @_ZL7dg_int1 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.0" zeroinitializer, align 8, !spirv.Decorations ![[#MN0:]]
1721
@_ZL7dg_int2 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.0" zeroinitializer, align 8 #1
22+
; CHECK-IR: @_ZL7dg_int2 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.0" zeroinitializer, align 8, !spirv.Decorations ![[#MN4:]]
1823
@_ZL8dg_bool3 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.1" zeroinitializer, align 1 #2
24+
; CHECK-IR: @_ZL8dg_bool3 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.1" zeroinitializer, align 1, !spirv.Decorations ![[#MN8:]]
1925
@_ZL8dg_bool4 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.1" zeroinitializer, align 1 #3
26+
; CHECK-IR: @_ZL8dg_bool4 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.1" zeroinitializer, align 1, !spirv.Decorations ![[#MN10:]]
2027
@_ZL7no_dg_int1 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.0" zeroinitializer, align 8 #6
28+
; CHECK-IR: @_ZL7no_dg_int1 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.0" zeroinitializer, align 8, !spirv.Decorations ![[#MN12:]]
2129

2230
define internal spir_func void @_ZZ4mainENKUlvE_clEv(%class.anon.0 addrspace(4)* align 1 dereferenceable_or_null(1) %this) #4 align 2 {
2331
entry:
@@ -39,13 +47,15 @@ declare spir_func align 4 dereferenceable(4) i32 addrspace(4)* @_ZNK2cl4sycl3ext
3947
declare spir_func align 1 dereferenceable(1) i8 addrspace(4)* @_ZNK2cl4sycl3ext6oneapi13device_globalIbJNS2_8PropertyIXadsoKcL_ZL5Name1EEEXadsoS5_L_ZL6Value1EEEEENS4_IXadsoS5_L_ZL5Name2EEEXadsoS5_L_ZL6Value2EEEEENS4_IXadsoS5_L_ZL5Name3EEEXadsoS5_L_ZL6Value3EEEEENS4_IXadsoS5_L_ZL5Name4EEEXadsoS5_L_ZL6Value4EEEEEEE3getEv(%"class.cl::sycl::ext::oneapi::device_global.1" addrspace(4)* align 1 dereferenceable_or_null(1) %this) #4 align 2
4048

4149
attributes #0 = { "sycl-unique-id"="6da74a122db9f35d____ZL7dg_int1" "device_image_scope"="false" "host_access"="1" "implement_in_csr"="true" "init_mode"="0" "sycl-device-global-size"="4" }
42-
attributes #1 = { "sycl-unique-id"="7da74a1187b9f35d____ZL7dg_int2" "host_access"="1" "implement_in_csr"="true" "init_mode"="0" "sycl-device-global-size"="4" }
43-
attributes #2 = { "sycl-unique-id"="9d329ad59055e972____ZL8dg_bool3" "device_image_scope"="true" "host_access"="1" "implement_in_csr"="true" "init_mode"="0" "sycl-device-global-size"="1" }
44-
attributes #3 = { "sycl-unique-id"="dda2bad52c45c432____ZL8dg_bool4" "device_image_scope" "host_access"="1" "implement_in_csr"="true" "init_mode"="0" "sycl-device-global-size"="1" }
50+
attributes #1 = { "sycl-unique-id"="7da74a1187b9f35d____ZL7dg_int2" "implement_in_csr"="false" "init_mode"="1" "sycl-device-global-size"="4" }
51+
attributes #2 = { "sycl-unique-id"="9d329ad59055e972____ZL8dg_bool3" "device_image_scope"="true" "host_access"="0" "implement_in_csr" "init_mode"="0" "sycl-device-global-size"="1" }
52+
attributes #3 = { "sycl-unique-id"="dda2bad52c45c432____ZL8dg_bool4" "device_image_scope" "host_access"="2" "sycl-device-global-size"="1" }
4553
attributes #4 = { convergent mustprogress noinline norecurse nounwind optnone "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
4654
attributes #5 = { convergent nounwind }
47-
; no the sycl-device-global-size attribute, this is not a device global variable
55+
; no sycl-device-global-size attribute, this is not a device global variable but it contains compile-time properties,
56+
; a metadata node will be generated.
4857
attributes #6 = { "sycl-unique-id"="6da74a122db9f35d____ZL7no_dg_int1" "device_image_scope"="false" "host_access"="1" "implement_in_csr"="true" "init_mode"="0" }
58+
4959
!llvm.dependent-libraries = !{!0}
5060
!llvm.module.flags = !{!1, !2}
5161
!opencl.spir.version = !{!3}
@@ -59,6 +69,27 @@ attributes #6 = { "sycl-unique-id"="6da74a122db9f35d____ZL7no_dg_int1" "device_i
5969
!4 = !{i32 4, i32 100000}
6070
!5 = !{!"clang version 14.0.0"}
6171

72+
; Ensure that the generated metadata nodes are correct
73+
; CHECK-IR-DAG: ![[#MN0]] = !{![[#MN1:]], ![[#MN2:]], ![[#MN3:]]}
74+
; CHECK-IR-DAG: ![[#MN1]] = !{i32 6149, i32 1}
75+
; CHECK-IR-DAG: ![[#MN2]] = !{i32 6148, i32 0}
76+
; CHECK-IR-DAG: ![[#MN3]] = !{i32 6147, i32 1, !"6da74a122db9f35d____ZL7dg_int1"}
77+
78+
; CHECK-IR-DAG: ![[#MN4]] = !{![[#MN5:]], ![[#MN6:]], ![[#MN7:]]}
79+
; CHECK-IR-DAG: ![[#MN5]] = !{i32 6149, i32 0}
80+
; CHECK-IR-DAG: ![[#MN6]] = !{i32 6148, i32 1}
81+
; CHECK-IR-DAG: ![[#MN7]] = !{i32 6147, i32 2, !"7da74a1187b9f35d____ZL7dg_int2"}
82+
83+
; CHECK-IR-DAG: ![[#MN8]] = !{![[#MN1]], ![[#MN2]], ![[#MN9:]]}
84+
; CHECK-IR-DAG: ![[#MN9]] = !{i32 6147, i32 0, !"9d329ad59055e972____ZL8dg_bool3"}
85+
86+
; CHECK-IR-DAG: ![[#MN10]] = !{![[#MN11:]]}
87+
; CHECK-IR-DAG: ![[#MN11]] = !{i32 6147, i32 2, !"dda2bad52c45c432____ZL8dg_bool4"}
88+
89+
; For not a device global variable, only actually present compile-time
90+
; properties are handled
91+
; CHECK-IR-DAG: ![[#MN12]] = !{![[#MN1]], ![[#MN2]]}
92+
6293
; Ensure that the default values are correct.
6394
; ABAAAAAAAAABAAAAAxxxxx is decoded to
6495
; "40 00 00 00 00 00 00 00 | 04 00 00 00 | 00 | xx xx xx" which consists of:
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
; RUN: sycl-post-link --ir-output-only --device-globals %s -S -o - | FileCheck %s --implicit-check-not "!spirv.Decorations"
2+
3+
; This test is intended to check that sycl-post-link doesn't add metadata nodes
4+
; for a non device global variable.
5+
6+
source_filename = "test_global_variable.cpp"
7+
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"
8+
target triple = "spir64-unknown-unknown"
9+
10+
%"class.cl::sycl::ext::oneapi::device_global.0" = type { i32 addrspace(4)* }
11+
%class.anon.0 = type { i8 }
12+
13+
@_ZL7dg_int1 = internal addrspace(1) constant %"class.cl::sycl::ext::oneapi::device_global.0" zeroinitializer, align 8 #0
14+
15+
define internal spir_func void @_ZZ4mainENKUlvE_clEv(%class.anon.0 addrspace(4)* align 1 dereferenceable_or_null(1) %this) #1 align 2 {
16+
entry:
17+
%this.addr = alloca %class.anon.0 addrspace(4)*, align 8
18+
%this.addr.ascast = addrspacecast %class.anon.0 addrspace(4)** %this.addr to %class.anon.0 addrspace(4)* addrspace(4)*
19+
store %class.anon.0 addrspace(4)* %this, %class.anon.0 addrspace(4)* addrspace(4)* %this.addr.ascast, align 8
20+
%this1 = load %class.anon.0 addrspace(4)*, %class.anon.0 addrspace(4)* addrspace(4)* %this.addr.ascast, align 8
21+
%call1 = call spir_func align 4 dereferenceable(4) i32 addrspace(4)* @_ZNK2cl4sycl3ext6oneapi13device_globalIiJNS2_8PropertyIXadsoKcL_ZL5Name1EEEXadsoS5_L_ZL6Value1EEEEENS4_IXadsoS5_L_ZL5Name2EEEXadsoS5_L_ZL6Value2EEEEENS4_IXadsoS5_L_ZL5Name3EEEXadsoS5_L_ZL6Value3EEEEENS4_IXadsoS5_L_ZL5Name4EEEXadsoS5_L_ZL6Value4EEEEEEE3getEv(%"class.cl::sycl::ext::oneapi::device_global.0" addrspace(4)* align 8 dereferenceable_or_null(8) addrspacecast (%"class.cl::sycl::ext::oneapi::device_global.0" addrspace(1)* @_ZL7dg_int1 to %"class.cl::sycl::ext::oneapi::device_global.0" addrspace(4)*)) #2
22+
ret void
23+
}
24+
25+
; Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
26+
declare spir_func align 4 dereferenceable(4) i32 addrspace(4)* @_ZNK2cl4sycl3ext6oneapi13device_globalIiJNS2_8PropertyIXadsoKcL_ZL5Name1EEEXadsoS5_L_ZL6Value1EEEEENS4_IXadsoS5_L_ZL5Name2EEEXadsoS5_L_ZL6Value2EEEEENS4_IXadsoS5_L_ZL5Name3EEEXadsoS5_L_ZL6Value3EEEEENS4_IXadsoS5_L_ZL5Name4EEEXadsoS5_L_ZL6Value4EEEEEEE3getEv(%"class.cl::sycl::ext::oneapi::device_global.0" addrspace(4)* align 8 dereferenceable_or_null(8) %this) #1 align 2
27+
28+
; this is not a device global variable
29+
attributes #0 = { "sycl-unique-id"="6da74a122db9f35d____ZL7dg_int1" "device_image_scope"="false" }
30+
attributes #1 = { convergent mustprogress noinline norecurse nounwind optnone "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
31+
attributes #2 = { convergent nounwind }
32+
33+
!llvm.dependent-libraries = !{!0}
34+
!llvm.module.flags = !{!1, !2}
35+
!opencl.spir.version = !{!3}
36+
!spirv.Source = !{!4}
37+
!llvm.ident = !{!5}
38+
39+
!0 = !{!"libcpmt"}
40+
!1 = !{i32 1, !"wchar_size", i32 2}
41+
!2 = !{i32 7, !"frame-pointer", i32 2}
42+
!3 = !{i32 1, i32 2}
43+
!4 = !{i32 4, i32 100000}
44+
!5 = !{!"clang version 14.0.0"}
45+
46+
; CHECK-NOT: ![[#MN1:]] = !{i32 6147
47+
; CHECK-NOT: ![[#MN2:]] = !{i32 6148
48+
; CHECK-NOT: ![[#MN3:]] = !{i32 6149

llvm/tools/sycl-post-link/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ include_directories(
1919

2020
add_llvm_tool(sycl-post-link
2121
sycl-post-link.cpp
22+
CompileTimePropertiesPass.cpp
2223
DeviceGlobals.cpp
2324
SpecConstants.cpp
2425
SYCLDeviceLibReqMask.cpp
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*=- CompileTimeProperties.def - Compile-time properties registry-*- C++ -*-= *\
2+
|*
3+
|* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
|* See https://llvm.org/LICENSE.txt for license information.
5+
|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
|*
7+
\*===----------------------------------------------------------------------===*/
8+
9+
#ifndef SYCL_COMPILE_TIME_PROPERTY
10+
#error "SYCL_COMPILE_TIME_PROPERTY(PropertyName, Decoration, ValueType) is not defined."
11+
#endif
12+
13+
// The corresponding SPIR-V OpCodes for the init_mode and implement_in_csr
14+
// properties are documented in the SPV_INTEL_global_variable_decorations design
15+
// document: https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/DeviceGlobal/SPV_INTEL_global_variable_decorations.asciidoc#decoration
16+
SYCL_COMPILE_TIME_PROPERTY("init_mode", 6148, DecorValueTy::uint32)
17+
SYCL_COMPILE_TIME_PROPERTY("implement_in_csr", 6149, DecorValueTy::boolean)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//===---- CompileTimePropertiesPass.cpp - SYCL Compile Time Props Pass ----===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// See comments in the header.
9+
//===----------------------------------------------------------------------===//
10+
11+
#include "CompileTimePropertiesPass.h"
12+
#include "DeviceGlobals.h"
13+
14+
#include "llvm/ADT/APInt.h"
15+
#include "llvm/ADT/StringMap.h"
16+
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/IR/Module.h"
18+
19+
using namespace llvm;
20+
21+
namespace {
22+
23+
constexpr StringRef SYCL_HOST_ACCESS_ATTR = "host_access";
24+
25+
constexpr StringRef SPIRV_DECOR_MD_KIND = "spirv.Decorations";
26+
// The corresponding SPIR-V OpCode for the host_access property is documented
27+
// in the SPV_INTEL_global_variable_decorations design document:
28+
// https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/DeviceGlobal/SPV_INTEL_global_variable_decorations.asciidoc#decoration
29+
constexpr uint32_t SPIRV_HOST_ACCESS_DECOR = 6147;
30+
constexpr uint32_t SPIRV_HOST_ACCESS_DEFAULT_VALUE = 2; // Read/Write
31+
32+
enum class DecorValueTy {
33+
uint32,
34+
boolean,
35+
};
36+
37+
struct Decor {
38+
uint32_t Code;
39+
DecorValueTy Type;
40+
};
41+
42+
#define SYCL_COMPILE_TIME_PROPERTY(PropertyName, Decoration, ValueType) \
43+
{PropertyName, {Decoration, ValueType}},
44+
45+
const StringMap<Decor> SpirvDecorMap = {
46+
#include "CompileTimeProperties.def"
47+
};
48+
#undef SYCL_COMPILE_TIME_PROPERTY
49+
50+
/// Builds a metadata node for a SPIR-V decoration (both decoration code
51+
/// and value are \c uint32_t integers).
52+
///
53+
/// @param Ctx [in] the LLVM Context.
54+
/// @param OpCode [in] the SPIR-V OpCode code.
55+
/// @param Value [in] the SPIR-V decoration value.
56+
///
57+
/// @returns a pointer to the metadata node created for the required decoration
58+
/// and its value.
59+
MDNode *buildSpirvDecorMetadata(LLVMContext &Ctx, uint32_t OpCode,
60+
uint32_t Value) {
61+
auto *Ty = Type::getInt32Ty(Ctx);
62+
SmallVector<Metadata *, 2> MD;
63+
MD.push_back(ConstantAsMetadata::get(
64+
Constant::getIntegerValue(Ty, APInt(32, OpCode))));
65+
MD.push_back(
66+
ConstantAsMetadata::get(Constant::getIntegerValue(Ty, APInt(32, Value))));
67+
return MDNode::get(Ctx, MD);
68+
}
69+
70+
/// Builds a metadata node for a SPIR-V decoration (both decoration code
71+
/// and value are \c uint32_t integers, and the secondary extra operand is a
72+
/// string).
73+
///
74+
/// @param Ctx [in] the LLVM Context.
75+
/// @param OpCode [in] the SPIR-V OpCode code.
76+
/// @param Value [in] the SPIR-V decoration value.
77+
/// @param Secondary [in] the secondary "extra operands" (\c StringRef).
78+
///
79+
/// @returns a pointer to the metadata node created for the required decoration
80+
/// and its value.
81+
MDNode *buildSpirvDecorMetadata(LLVMContext &Ctx, uint32_t OpCode,
82+
uint32_t Value, StringRef Secondary) {
83+
auto *Ty = Type::getInt32Ty(Ctx);
84+
SmallVector<Metadata *, 3> MD;
85+
MD.push_back(ConstantAsMetadata::get(
86+
Constant::getIntegerValue(Ty, APInt(32, OpCode))));
87+
MD.push_back(
88+
ConstantAsMetadata::get(Constant::getIntegerValue(Ty, APInt(32, Value))));
89+
MD.push_back(MDString::get(Ctx, Secondary));
90+
return MDNode::get(Ctx, MD);
91+
}
92+
93+
} // anonymous namespace
94+
95+
PreservedAnalyses CompileTimePropertiesPass::run(Module &M,
96+
ModuleAnalysisManager &MAM) {
97+
LLVMContext &Ctx = M.getContext();
98+
unsigned MDKindID = Ctx.getMDKindID(SPIRV_DECOR_MD_KIND);
99+
bool CompileTimePropertiesMet = false;
100+
101+
// Let's process all the globals
102+
for (auto &GV : M.globals()) {
103+
// we suppose the enumeration orders in every enumeration in the SYCL
104+
// headers are the same as in the descriptions of the corresponding
105+
// decorations in the SPV_INTEL_* extensions.
106+
SmallVector<Metadata *, 8> MDOps;
107+
for (auto &Attribute : GV.getAttributes()) {
108+
// Currently, only string attributes are supported
109+
if (!Attribute.isStringAttribute())
110+
continue;
111+
auto DecorIt = SpirvDecorMap.find(Attribute.getKindAsString());
112+
if (DecorIt == SpirvDecorMap.end())
113+
continue;
114+
auto Decor = DecorIt->second;
115+
auto DecorCode = Decor.Code;
116+
auto DecorValue = Decor.Type == DecorValueTy::uint32
117+
? getAttributeAsInteger<uint32_t>(Attribute)
118+
: hasProperty(Attribute);
119+
MDOps.push_back(buildSpirvDecorMetadata(Ctx, DecorCode, DecorValue));
120+
}
121+
122+
// Some properties should be handled specially.
123+
124+
// The host_access property is handled specially for device global variables
125+
// because the SPIR-V decoration requires two "extra operands". The second
126+
// SPIR-V operand is the "name" (the value of the "sycl-unique-id" property)
127+
// of the variable.
128+
if (isDeviceGlobalVariable(GV)) {
129+
auto HostAccessDecorValue =
130+
GV.hasAttribute(SYCL_HOST_ACCESS_ATTR)
131+
? getAttributeAsInteger<uint32_t>(GV, SYCL_HOST_ACCESS_ATTR)
132+
: SPIRV_HOST_ACCESS_DEFAULT_VALUE;
133+
auto VarName = getGlobalVariableUniqueId(GV);
134+
MDOps.push_back(buildSpirvDecorMetadata(Ctx, SPIRV_HOST_ACCESS_DECOR,
135+
HostAccessDecorValue, VarName));
136+
}
137+
138+
// Add the generated metadata to the variable
139+
if (!MDOps.empty()) {
140+
GV.addMetadata(MDKindID, *MDNode::get(Ctx, MDOps));
141+
CompileTimePropertiesMet = true;
142+
}
143+
}
144+
145+
// The pass just adds some metadata to the module, it should not ruin
146+
// any analysis, but we need return PreservedAnalyses::none() to inform
147+
// the caller that at least one compile-time property was met.
148+
return CompileTimePropertiesMet ? PreservedAnalyses::none()
149+
: PreservedAnalyses::all();
150+
}

0 commit comments

Comments
 (0)