Skip to content

Commit 314fcb4

Browse files
authored
[SYCL] Fix ESIMD split detection in module properties computation (#15527)
We need to know if a module is an ESIMD split or not split at all to set some image properties. Right now we compute if a module is ESIMD or not by looking at the functions in the module and checking they are all ESIMD, except for allowed exceptions. A new exception was found related to using `assert` in user code where there is a scalar SYCL function that correctly remains in the ESIMD split. The end result is we don't set the ESIMD property because we don't think this is the ESIMD split and do the wrong thing at runtime. Instead of just adding this new exception to the list, I reworked what I consider to be flaky logic (that I wrote originally, whoops) to figure out the splits. Just save metadata in the module of what kind of split it is before we try to compute module properties. We already do something similar for the spec constant default split, and I moved that to a centralized place as part of this change. The reason we don't just pass in the `ModuleDesc` object as an argument is because we want to untie properties creation from the `sycl-post-link` tool so that it can be called in other places (which we will do for thinLTO). --------- Signed-off-by: Sarnie, Nick <[email protected]>
1 parent f18ed8c commit 314fcb4

File tree

6 files changed

+90
-17
lines changed

6 files changed

+90
-17
lines changed

llvm/include/llvm/SYCLLowerIR/ModuleSplitter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class OptionCategory;
3737

3838
namespace module_split {
3939

40+
constexpr char SYCL_ESIMD_SPLIT_MD_NAME[] = "sycl-esimd-split-status";
41+
4042
extern cl::OptionCategory &getModuleSplitCategory();
4143

4244
enum IRSplitMode {
@@ -221,6 +223,8 @@ class ModuleDesc {
221223
return *Reqs;
222224
}
223225

226+
void saveSplitInformationAsMetadata();
227+
224228
#ifndef NDEBUG
225229
void verifyESIMDProperty() const;
226230
void dump() const;

llvm/lib/SYCLLowerIR/ComputeModuleRuntimeInfo.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ constexpr int DebugModuleProps = 0;
2828
#endif
2929

3030
namespace llvm::sycl {
31+
namespace {
32+
module_split::SyclEsimdSplitStatus
33+
getSYCLESIMDSplitStatusFromMetadata(const Module &M) {
34+
auto *SplitMD = M.getNamedMetadata(module_split::SYCL_ESIMD_SPLIT_MD_NAME);
35+
assert(SplitMD && "Unexpected metadata");
36+
auto *MDOp = SplitMD->getOperand(0);
37+
assert(MDOp && "Unexpected metadata operand");
38+
const auto &MDConst = MDOp->getOperand(0);
39+
auto *MDVal = mdconst::dyn_extract_or_null<ConstantInt>(MDConst);
40+
uint8_t Val = MDVal->getZExtValue();
41+
assert(Val < 3 && "Unexpected value for split metadata");
42+
auto AsEnum = static_cast<module_split::SyclEsimdSplitStatus>(Val);
43+
return AsEnum;
44+
}
45+
} // namespace
3146

3247
bool isModuleUsingAsan(const Module &M) {
3348
for (const auto &F : M) {
@@ -305,16 +320,11 @@ PropSetRegTy computeModuleProperties(const Module &M,
305320
GV.getName());
306321
}
307322
}
308-
bool SeenESIMDFunction = false;
309-
bool SeenSYCLFunction = false;
310-
for (const auto &F : M) {
311-
if (llvm::module_split::isESIMDFunction(F))
312-
SeenESIMDFunction = true;
313-
else if (utils::isSYCLExternalFunction(&F) &&
314-
!F.getName().starts_with("__itt"))
315-
SeenSYCLFunction = true;
316-
}
317-
if (SeenESIMDFunction && !SeenSYCLFunction)
323+
324+
module_split::SyclEsimdSplitStatus SplitType =
325+
getSYCLESIMDSplitStatusFromMetadata(M);
326+
327+
if (SplitType == module_split::SyclEsimdSplitStatus::ESIMD_ONLY)
318328
PropSet.add(PropSetRegTy::SYCL_MISC_PROP, "isEsimdImage", true);
319329
{
320330
StringRef RegAllocModeAttr = "sycl-register-alloc-mode";
@@ -359,7 +369,7 @@ PropSetRegTy computeModuleProperties(const Module &M,
359369
// 'if' below essentially preserves the behavior (presumably mistakenly)
360370
// implemented in intel/llvm#8763: ignore 'optLevel' property for images which
361371
// were produced my merge after ESIMD split
362-
if (!SeenESIMDFunction || !SeenSYCLFunction) {
372+
if (SplitType != module_split::SyclEsimdSplitStatus::SYCL_AND_ESIMD) {
363373
// Handle sycl-optlevel property
364374
int OptLevel = -1;
365375
for (const Function *F : EntryPoints) {

llvm/lib/SYCLLowerIR/ModuleSplitter.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/SYCLLowerIR/DeviceGlobals.h"
2727
#include "llvm/SYCLLowerIR/LowerInvokeSimd.h"
2828
#include "llvm/SYCLLowerIR/SYCLUtils.h"
29+
#include "llvm/SYCLLowerIR/SpecConstants.h"
2930
#include "llvm/Support/CommandLine.h"
3031
#include "llvm/Support/Error.h"
3132
#include "llvm/Support/FileSystem.h"
@@ -798,6 +799,23 @@ void ModuleDesc::dump() const {
798799
}
799800
#endif // NDEBUG
800801

802+
void ModuleDesc::saveSplitInformationAsMetadata() {
803+
// Add metadata to the module so we can identify what kind of SYCL/ESIMD split
804+
// later.
805+
auto *SplitMD = M->getOrInsertNamedMetadata(SYCL_ESIMD_SPLIT_MD_NAME);
806+
auto *SplitMDOp = MDNode::get(
807+
M->getContext(), ConstantAsMetadata::get(ConstantInt::get(
808+
Type::getInt8Ty(M->getContext()),
809+
static_cast<uint8_t>(EntryPoints.Props.HasESIMD))));
810+
SplitMD->addOperand(SplitMDOp);
811+
812+
// Add metadata to the module so we can identify it as the default value spec
813+
// constants split later.
814+
if (isSpecConstantDefault())
815+
M->getOrInsertNamedMetadata(
816+
SpecConstantsPass::SPEC_CONST_DEFAULT_VAL_MODULE_MD_STRING);
817+
}
818+
801819
void EntryPointGroup::saveNames(std::vector<std::string> &Dest) const {
802820
Dest.reserve(Dest.size() + Functions.size());
803821
std::transform(Functions.begin(), Functions.end(),
@@ -1291,6 +1309,7 @@ static Expected<SplitModule> saveModuleDesc(ModuleDesc &MD, std::string Prefix,
12911309
bool OutputAssembly) {
12921310
SplitModule SM;
12931311
Prefix += OutputAssembly ? ".ll" : ".bc";
1312+
MD.saveSplitInformationAsMetadata();
12941313
Error E = saveModuleIRInFile(MD.getModule(), Prefix, OutputAssembly);
12951314
if (E)
12961315
return E;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
; RUN: sycl-post-link -properties -split-esimd -S < %s -o %t.table
2+
; RUN: FileCheck %s -input-file=%t_esimd_0.prop
3+
4+
; Verify we mark a image with an ESIMD kernel with the isEsimdImage property
5+
6+
; CHECK: isEsimdImage=1|1
7+
8+
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-G1"
9+
target triple = "spir64-unknown-unknown"
10+
11+
%"struct.sycl::_V1::detail::AssertHappened" = type { i32, [257 x i8], [257 x i8], [129 x i8], i32, i64, i64, i64, i64, i64, i64 }
12+
%"class.sycl::_V1::id" = type { %"class.sycl::_V1::detail::array" }
13+
%"class.sycl::_V1::detail::array" = type { [1 x i64] }
14+
15+
@.str = private unnamed_addr addrspace(1) constant [10 x i8] c"Id != 400\00", align 1
16+
@.str.1 = private unnamed_addr addrspace(1) constant [8 x i8] c"foo.cpp\00", align 1
17+
@__PRETTY_FUNCTION__ = private unnamed_addr addrspace(1) constant [56 x i8] c"auto main()::(anonymous class)::operator()(id<1>) const\00", align 1
18+
@SPIR_AssertHappenedMem = linkonce_odr dso_local addrspace(1) global %"struct.sycl::_V1::detail::AssertHappened" zeroinitializer, align 8
19+
20+
declare void @llvm.assume(i1 noundef) #2
21+
22+
define weak_odr dso_local spir_kernel void @esimd_kernel() local_unnamed_addr #0 !sycl_explicit_simd !0 {
23+
entry:
24+
tail call spir_func void @__assert_fail(ptr addrspace(4) noundef addrspacecast (ptr addrspace(1) @.str to ptr addrspace(4)), ptr addrspace(4) noundef addrspacecast (ptr addrspace(1) @.str.1 to ptr addrspace(4)), i32 noundef 13, ptr addrspace(4) noundef addrspacecast (ptr addrspace(1) @__PRETTY_FUNCTION__ to ptr addrspace(4))) #12
25+
ret void
26+
}
27+
28+
define weak dso_local spir_func void @__assert_fail(ptr addrspace(4) noundef %expr, ptr addrspace(4) noundef %file, i32 noundef %line, ptr addrspace(4) noundef %func) #1 {
29+
entry:
30+
tail call spir_func void @__devicelib_assert_fail(ptr addrspace(4) noundef %expr, ptr addrspace(4) noundef %file, i32 noundef %line, ptr addrspace(4) noundef %func) #1
31+
ret void
32+
}
33+
34+
define weak dso_local spir_func void @__devicelib_assert_fail(ptr addrspace(4) noundef %expr, ptr addrspace(4) noundef %file, i32 noundef %line, ptr addrspace(4) noundef %func) #2 {
35+
entry:
36+
ret void
37+
}
38+
39+
attributes #0 = { convergent mustprogress norecurse nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="foo.cpp" "sycl-optlevel"="2" "uniform-work-group-size"="true" }
40+
attributes #1 = { convergent mustprogress norecurse nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="bar.cpp" "sycl-optlevel"="2" }
41+
attributes #2 = { convergent nounwind }
42+
43+
!0 = !{}

llvm/test/tools/sycl-post-link/sycl-esimd/basic-esimd-lower.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ attributes #0 = { "sycl-module-id"="a.cpp" }
5555
; CHECK-NO-LOWERING: }
5656

5757
; With -O0, we only lower ESIMD code, but no other optimizations
58-
; CHECK-O0: define dso_local spir_kernel void @ESIMD_kernel() #{{[0-9]}} !sycl_explicit_simd !3 !intel_reqd_sub_group_size !4 {
58+
; CHECK-O0: define dso_local spir_kernel void @ESIMD_kernel() #{{[0-9]}} !sycl_explicit_simd !{{[0-9]}} !intel_reqd_sub_group_size !{{[0-9]}} {
5959
; CHECK-O0: entry:
6060
; CHECK-O0: %0 = load <3 x i64>, {{.*}} addrspacecast {{.*}} @__spirv_BuiltInGlobalInvocationId
6161
; CHECK-O0: %1 = extractelement <3 x i64> %0, i64 0

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ void saveModule(std::vector<std::unique_ptr<util::SimpleTable>> &OutTables,
419419
module_split::ModuleDesc &MD, int I, StringRef IRFilename) {
420420
IrPropSymFilenameTriple BaseTriple;
421421
StringRef Suffix = getModuleSuffix(MD);
422+
MD.saveSplitInformationAsMetadata();
422423
if (!IRFilename.empty()) {
423424
// don't save IR, just record the filename
424425
BaseTriple.Ir = IRFilename.str();
@@ -509,10 +510,6 @@ processSpecConstantsWithDefaultValues(const module_split::ModuleDesc &MD) {
509510
assert(NewModuleDesc->Props.SpecConstsMet &&
510511
"This property should be true since the presence of SpecConsts "
511512
"has been checked before the run of the pass");
512-
// Add metadata to the module so we can identify it as the default value split
513-
// later.
514-
NewModuleDesc->getModule().getOrInsertNamedMetadata(
515-
SpecConstantsPass::SPEC_CONST_DEFAULT_VAL_MODULE_MD_STRING);
516513
NewModuleDesc->rebuildEntryPoints();
517514
return NewModuleDesc;
518515
}
@@ -791,7 +788,7 @@ processInputModule(std::unique_ptr<Module> M) {
791788
// to keep the optimizer from wrongfully removing them. llvm.compiler.used
792789
// symbols are usually removed at backend lowering, but this is handled here
793790
// for SPIR-V since SYCL compilation uses llvm-spirv, not the SPIR-V backend.
794-
if (auto Triple = M->getTargetTriple().find("spir") != std::string::npos)
791+
if (M->getTargetTriple().find("spir") != std::string::npos)
795792
Modified |= removeDeviceGlobalFromCompilerUsed(*M.get());
796793

797794
// Instrument each image scope device globals if the module has been

0 commit comments

Comments
 (0)