Skip to content

Commit 88bdd76

Browse files
authored
[SYCL][DeviceSanitizer] Add "asanUsed" property into "SYCL/misc properties" and driver support (#11989)
If the kernel is built with the flag "-fsanitize=address", we add a new property "asanUsed" into "SYCL/misc properties", so that we can enable the "UR_LAYER_ASAN" layer in the UR loader at piPluginInit.
1 parent 1dbee22 commit 88bdd76

File tree

13 files changed

+178
-20
lines changed

13 files changed

+178
-20
lines changed

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,24 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
11371137
return;
11381138
GPUSanitize = true;
11391139
}
1140+
// SPIR sanitizer support is experimental and will pass a fixed set of flags
1141+
if (TC.getTriple().isSPIR()) {
1142+
if (Sanitizers.has(SanitizerKind::Address)) {
1143+
CmdArgs.push_back("-fsanitize=address");
1144+
CmdArgs.push_back("-fsanitize-address-use-after-return=never");
1145+
CmdArgs.push_back("-fno-sanitize-address-use-after-scope");
1146+
1147+
// -fsanitize-address-outline-instrumentation
1148+
CmdArgs.push_back("-mllvm");
1149+
CmdArgs.push_back("-asan-instrumentation-with-call-threshold=0");
1150+
1151+
CmdArgs.push_back("-mllvm");
1152+
CmdArgs.push_back("-asan-stack=0");
1153+
CmdArgs.push_back("-mllvm");
1154+
CmdArgs.push_back("-asan-globals=0");
1155+
}
1156+
return;
1157+
}
11401158

11411159
// Translate available CoverageFeatures to corresponding clang-cc1 flags.
11421160
// Do it even if Sanitizers.empty() since some forms of coverage don't require

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5388,8 +5388,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
53885388

53895389
// Add -ffine-grained-bitfield-accesses option. This will be added
53905390
// only for SPIR based targets.
5391-
if (Triple.isSPIR())
5392-
CmdArgs.push_back("-ffine-grained-bitfield-accesses");
5391+
if (Triple.isSPIR()) {
5392+
// It cannot be enabled together with a sanitizer
5393+
if (!Args.getLastArg(options::OPT_fsanitize_EQ))
5394+
CmdArgs.push_back("-ffine-grained-bitfield-accesses");
5395+
}
53935396

53945397
if (!Args.hasFlag(options::OPT_fsycl_unnamed_lambda,
53955398
options::OPT_fno_sycl_unnamed_lambda, true))

clang/lib/Driver/ToolChains/SYCL.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,11 +1017,18 @@ SYCLToolChain::SYCLToolChain(const Driver &D, const llvm::Triple &Triple,
10171017
getProgramPaths().push_back(getDriver().Dir);
10181018

10191019
// Diagnose unsupported options only once.
1020-
// All sanitizer options are not currently supported.
1021-
for (auto A :
1022-
Args.filtered(options::OPT_fsanitize_EQ, options::OPT_fcf_protection_EQ))
1020+
// All sanitizer options are not currently supported, except AddressSanitizer
1021+
for (auto *A : Args.filtered(options::OPT_fsanitize_EQ,
1022+
options::OPT_fcf_protection_EQ)) {
1023+
if (A->getOption().getID() == options::OPT_fsanitize_EQ &&
1024+
A->getValues().size() == 1) {
1025+
std::string SanitizeVal = A->getValue();
1026+
if (SanitizeVal == "address")
1027+
continue;
1028+
}
10231029
D.getDiags().Report(clang::diag::warn_drv_unsupported_option_for_target)
10241030
<< A->getAsString(Args) << getTriple().str();
1031+
}
10251032
}
10261033

10271034
void SYCLToolChain::addClangTargetOptions(
@@ -1049,6 +1056,15 @@ SYCLToolChain::TranslateArgs(const llvm::opt::DerivedArgList &Args,
10491056
auto Opt(A->getOption().getID());
10501057
switch (Opt) {
10511058
case options::OPT_fsanitize_EQ:
1059+
if (A->getValues().size() == 1) {
1060+
std::string SanitizeVal = A->getValue();
1061+
if (SanitizeVal == "address") {
1062+
if (IsNewDAL)
1063+
DAL->append(A);
1064+
continue;
1065+
}
1066+
}
1067+
[[fallthrough]];
10521068
case options::OPT_fcf_protection_EQ:
10531069
if (!IsNewDAL)
10541070
DAL->eraseArg(Opt);
@@ -1428,3 +1444,7 @@ void SYCLToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &Args,
14281444
ArgStringList &CC1Args) const {
14291445
HostTC.AddClangCXXStdlibIncludeArgs(Args, CC1Args);
14301446
}
1447+
1448+
SanitizerMask SYCLToolChain::getSupportedSanitizers() const {
1449+
return SanitizerKind::Address;
1450+
}

clang/lib/Driver/ToolChains/SYCL.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ class LLVM_LIBRARY_VISIBILITY SYCLToolChain : public ToolChain {
215215
const llvm::opt::ArgList &Args,
216216
llvm::opt::ArgStringList &CC1Args) const override;
217217

218+
SanitizerMask getSupportedSanitizers() const override;
219+
218220
const ToolChain &HostTC;
219221
const bool IsSYCLNativeCPU;
220222

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %clangxx -fsycl -fsanitize=address -c %s -### 2>&1 \
2+
// RUN: | FileCheck --check-prefix=SYCL-ASAN %s
3+
// SYCL-ASAN: clang{{.*}} "-fsycl-is-device"
4+
// SYCL-ASAN-SAME: -fsanitize=address
5+
// SYCL-ASAN-SAME: -fsanitize-address-use-after-return=never
6+
// SYCL-ASAN-SAME: -fno-sanitize-address-use-after-scope
7+
// SYCL-ASAN-SAME: "-mllvm" "-asan-instrumentation-with-call-threshold=0"
8+
// SYCL-ASAN-SAME: "-mllvm" "-asan-stack=0"
9+
// SYCL-ASAN-SAME: "-mllvm" "-asan-globals=0"
10+
11+
// RUN: %clangxx -fsycl -fsanitize=address -mllvm -asan-stack=1 -c %s -### 2>&1 \
12+
// RUN: | FileCheck --check-prefix=SYCL-ASAN-FILTER %s
13+
// SYCL-ASAN-FILTER: clang{{.*}} "-fsycl-is-device"
14+
// SYCL-ASAN-FILTER-SAME: -fsanitize=address
15+
// SYCL-ASAN-FILTER-SAME: "-mllvm" "-asan-stack=0"

clang/test/Driver/sycl-unsupported.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,4 @@
11
/// Diagnose unsupported options specific to SYCL compilations
2-
// RUN: %clangxx -fsycl -fsanitize=address -fsycl-targets=spir64 -### %s 2>&1 \
3-
// RUN: | FileCheck %s -DARCH=spir64 -DOPT=-fsanitize=address
4-
// RUN: %clang_cl -fsycl -fsanitize=address -fsycl-targets=spir64 -### %s 2>&1 \
5-
// RUN: | FileCheck %s -DARCH=spir64 -DOPT=-fsanitize=address
6-
// RUN: %clangxx -fsycl -fsycl-targets=spir64_gen -fsanitize=address -### %s 2>&1 \
7-
// RUN: | FileCheck %s -DARCH=spir64_gen -DOPT=-fsanitize=address
8-
// RUN: %clangxx -fsycl -fsycl-targets=spir64_fpga -fsanitize=address -### %s 2>&1 \
9-
// RUN: | FileCheck %s -DARCH=spir64_fpga -DOPT=-fsanitize=address
10-
// RUN: %clangxx -fsycl -fsycl-targets=spir64_x86_64 -fsanitize=address -### %s 2>&1 \
11-
// RUN: | FileCheck %s -DARCH=spir64_x86_64 -DOPT=-fsanitize=address
122

133
// RUN: %clangxx -fsycl -fcf-protection -fsycl-targets=spir64 -### %s 2>&1 \
144
// RUN: | FileCheck %s -DARCH=spir64 -DOPT=-fcf-protection
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
; This test checks that the post-link tool properly generates "asanUsed=1"
2+
; in [SYCL/misc properties]
3+
4+
; RUN: sycl-post-link -split=kernel -symbols -S < %s -o %t.table
5+
; RUN: FileCheck %s -input-file=%t_0.prop
6+
; CHECK: [SYCL/misc properties]
7+
; CHECK: asanUsed=1
8+
9+
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"
10+
target triple = "spir64-unknown-unknown"
11+
12+
%struct.AssertHappened.10 = type { i32, [257 x i8], [257 x i8], [129 x i8], i32, i64, i64, i64, i64, i64, i64 }
13+
%"class.sycl::_V1::range.12" = type { %"class.sycl::_V1::detail::array.11" }
14+
%"class.sycl::_V1::detail::array.11" = type { [2 x i64] }
15+
%"class.sycl::_V1::detail::RoundedRangeIDGenerator.15" = type <{ %"class.sycl::_V1::range.12", %"class.sycl::_V1::range.12", %"class.sycl::_V1::range.12", %"class.sycl::_V1::range.12", i8, [7 x i8] }>
16+
%"class.sycl::_V1::detail::RoundedRangeKernel.17" = type <{ %"class.sycl::_V1::range.12", %class.anon.16, [7 x i8] }>
17+
%class.anon.16 = type { i8 }
18+
%struct.__devicelib_div_t_32.13 = type { i32, i32 }
19+
%struct.__devicelib_div_t_64.14 = type { i64, i64 }
20+
21+
@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @asan.module_ctor, ptr null }]
22+
@__spirv_BuiltInGlobalSize = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32
23+
@__spirv_BuiltInGlobalInvocationId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32
24+
@__spirv_BuiltInLocalInvocationId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32
25+
@SPIR_AssertHappenedMem = linkonce_odr dso_local addrspace(1) global %struct.AssertHappened.10 zeroinitializer, align 8
26+
@__spirv_BuiltInWorkgroupId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32
27+
@__spirv_BuiltInGlobalLinearId = external dso_local local_unnamed_addr addrspace(1) constant i64, align 8
28+
@__spirv_BuiltInWorkgroupSize = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32
29+
30+
; Function Attrs: nounwind uwtable
31+
define internal void @asan.module_ctor() #0 {
32+
call void @__asan_init()
33+
call void @__asan_version_mismatch_check_v8()
34+
ret void
35+
}
36+
37+
declare void @__asan_init()
38+
39+
declare void @__asan_version_mismatch_check_v8()
40+
41+
; Function Attrs: mustprogress norecurse nounwind sanitize_address uwtable
42+
define weak_odr dso_local spir_kernel void @_ZTSN4sycl3_V16detail19__pf_kernel_wrapperIZZ4mainENKUlRNS0_7handlerEE_clES4_E9TheKernelEE(ptr noundef byval(%"class.sycl::_V1::range.12") align 8 %_arg_UserRange) local_unnamed_addr #1 {
43+
entry:
44+
ret void
45+
}
46+
47+
attributes #0 = { nounwind uwtable "frame-pointer"="all" "sycl-optlevel"="2" }
48+
attributes #1 = { mustprogress norecurse nounwind sanitize_address uwtable "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="test.cpp" "sycl-optlevel"="2" "uniform-work-group-size"="true" }
49+
50+
!opencl.spir.version = !{!0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0}
51+
!spirv.Source = !{!1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1}
52+
!llvm.ident = !{!2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2}
53+
!llvm.module.flags = !{!3, !4, !5}
54+
!sycl.specialization-constants = !{}
55+
!sycl.specialization-constants-default-values = !{}
56+
57+
!0 = !{i32 1, i32 2}
58+
!1 = !{i32 4, i32 100000}
59+
!2 = !{!"clang version 18.0.0 (https://github.com/AllanZyne/llvm.git 97c052ed8efa30f750dacf8d89e8e64743ec03f7)"}
60+
!3 = !{i32 1, !"wchar_size", i32 4}
61+
!4 = !{i32 7, !"uwtable", i32 2}
62+
!5 = !{i32 7, !"frame-pointer", i32 2}
63+
!6 = !{i32 8544724}
64+
!7 = !{i32 -1}
65+
!8 = !{}
66+
!9 = !{i1 false, i1 true}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,11 @@ std::vector<StringRef> getKernelNamesUsingAssert(const Module &M) {
312312
return SPIRKernelNames;
313313
}
314314

315+
bool isModuleUsingAsan(const Module &M) {
316+
auto *AsanInitFunction = M.getFunction("__asan_init");
317+
return AsanInitFunction;
318+
}
319+
315320
// Gets reqd_work_group_size information for function Func.
316321
std::vector<uint32_t> getKernelReqdWorkGroupSizeMetadata(const Function &Func) {
317322
MDNode *ReqdWorkGroupSizeMD = Func.getMetadata("reqd_work_group_size");
@@ -537,6 +542,11 @@ std::string saveModuleProperties(module_split::ModuleDesc &MD,
537542
PropSet[PropSetRegTy::SYCL_ASSERT_USED].insert({FName, true});
538543
}
539544

545+
{
546+
if (isModuleUsingAsan(M))
547+
PropSet[PropSetRegTy::SYCL_MISC_PROP].insert({"asanUsed", true});
548+
}
549+
540550
if (GlobProps.EmitDeviceGlobalPropSet) {
541551
// Extract device global maps per module
542552
auto DevGlobalPropertyMap = collectDeviceGlobalProperties(M);

sycl/include/sycl/detail/pi.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,6 +2693,13 @@ __SYCL_EXPORT pi_result piextSignalExternalSemaphore(
26932693
pi_uint32 num_events_in_wait_list, const pi_event *event_wait_list,
26942694
pi_event *event);
26952695

2696+
typedef enum {
2697+
_PI_SANITIZE_TYPE_NONE = 0x0,
2698+
_PI_SANITIZE_TYPE_ADDRESS = 0x1,
2699+
_PI_SANITIZE_TYPE_MEMORY = 0x2,
2700+
_PI_SANITIZE_TYPE_THREAD = 0x3
2701+
} _pi_sanitize_type;
2702+
26962703
struct _pi_plugin {
26972704
// PI version supported by host passed to the plugin. The Plugin
26982705
// checks and writes the appropriate Function Pointers in
@@ -2709,6 +2716,8 @@ struct _pi_plugin {
27092716
#define _PI_API(api) decltype(::api) *api;
27102717
#include <sycl/detail/pi.def>
27112718
} PiFunctionTable;
2719+
2720+
_pi_sanitize_type SanitizeType;
27122721
};
27132722

27142723
#ifdef __cplusplus

sycl/plugins/unified_runtime/pi_unified_runtime.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1302,7 +1302,15 @@ __SYCL_EXPORT pi_result piPluginInit(pi_plugin *PluginInit) {
13021302
strncpy(PluginInit->PluginVersion, SupportedVersion, PluginVersionSize);
13031303

13041304
// Initialize UR and discover adapters
1305-
HANDLE_ERRORS(urLoaderInit(0, nullptr));
1305+
ur_loader_config_handle_t LoaderConfig{};
1306+
HANDLE_ERRORS(urLoaderConfigCreate(&LoaderConfig));
1307+
1308+
if (PluginInit->SanitizeType == _PI_SANITIZE_TYPE_ADDRESS) {
1309+
HANDLE_ERRORS(urLoaderConfigEnableLayer(LoaderConfig, "UR_LAYER_ASAN"));
1310+
}
1311+
1312+
HANDLE_ERRORS(urLoaderInit(0, LoaderConfig));
1313+
13061314
uint32_t NumAdapters;
13071315
HANDLE_ERRORS(urAdapterGet(0, nullptr, &NumAdapters));
13081316
if (NumAdapters > 0) {

sycl/source/detail/pi.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,10 +455,15 @@ static void initializePlugins(std::vector<PluginPtr> &Plugins) {
455455
std::vector<std::tuple<std::string, backend, void *>> LoadedPlugins =
456456
loadPlugins(std::move(PluginNames));
457457

458+
bool IsAsanUsed = ProgramManager::getInstance().kernelUsesAsan();
459+
458460
for (auto &[Name, Backend, Library] : LoadedPlugins) {
459-
std::shared_ptr<PiPlugin> PluginInformation = std::make_shared<PiPlugin>(
460-
PiPlugin{_PI_H_VERSION_STRING, _PI_H_VERSION_STRING,
461-
/*Targets=*/nullptr, /*FunctionPointers=*/{}});
461+
std::shared_ptr<PiPlugin> PluginInformation =
462+
std::make_shared<PiPlugin>(PiPlugin{
463+
_PI_H_VERSION_STRING, _PI_H_VERSION_STRING,
464+
/*Targets=*/nullptr, /*FunctionPointers=*/{},
465+
/*IsAsanUsed*/
466+
IsAsanUsed ? _PI_SANITIZE_TYPE_ADDRESS : _PI_SANITIZE_TYPE_NONE});
462467

463468
if (!Library) {
464469
if (trace(PI_TRACE_ALL)) {

sycl/source/detail/program_manager/program_manager.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,7 @@ loadDeviceLibFallback(const ContextImplPtr Context, DeviceLibExt Extension,
10191019
return LibProg;
10201020
}
10211021

1022-
ProgramManager::ProgramManager() {
1022+
ProgramManager::ProgramManager() : m_AsanFoundInImage(false) {
10231023
const char *SpvFile = std::getenv(UseSpvEnv);
10241024
// If a SPIR-V file is specified with an environment variable,
10251025
// register the corresponding image
@@ -1437,6 +1437,13 @@ void ProgramManager::addImages(pi_device_binaries DeviceBinary) {
14371437

14381438
cacheKernelUsesAssertInfo(*Img);
14391439

1440+
// check if kernel uses asan
1441+
{
1442+
pi_device_binary_property Prop = Img->getProperty("asanUsed");
1443+
m_AsanFoundInImage |=
1444+
Prop && (detail::DeviceBinaryProperty(Prop).asUint32() != 0);
1445+
}
1446+
14401447
// Sort kernel ids for faster search
14411448
std::sort(m_BinImg2KernelIDs[Img.get()]->begin(),
14421449
m_BinImg2KernelIDs[Img.get()]->end(), LessByHash<kernel_id>{});

sycl/source/detail/program_manager/program_manager.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ class ProgramManager {
291291

292292
bool kernelUsesAssert(const std::string &KernelName) const;
293293

294+
bool kernelUsesAsan() const { return m_AsanFoundInImage; }
295+
294296
std::set<RTDeviceBinaryImage *>
295297
getRawDeviceImages(const std::vector<kernel_id> &KernelIDs);
296298

@@ -399,6 +401,9 @@ class ProgramManager {
399401

400402
std::set<std::string> m_KernelUsesAssert;
401403

404+
// True iff there is a device image compiled with AddressSanitizer
405+
bool m_AsanFoundInImage;
406+
402407
// Maps between device_global identifiers and associated information.
403408
std::unordered_map<std::string, std::unique_ptr<DeviceGlobalMapEntry>>
404409
m_DeviceGlobals;

0 commit comments

Comments
 (0)