diff --git a/llvm/test/tools/sycl-post-link/assert-indirect-with-split-2.ll b/llvm/test/tools/sycl-post-link/assert-indirect-with-split-2.ll index bbbf417066a45..7630225833157 100644 --- a/llvm/test/tools/sycl-post-link/assert-indirect-with-split-2.ll +++ b/llvm/test/tools/sycl-post-link/assert-indirect-with-split-2.ll @@ -9,7 +9,8 @@ ; marked as using asserts. ; RUN: sycl-post-link -split=auto -symbols -S %s -o %t.table -; RUN: FileCheck %s -input-file=%t_0.prop +; RUN: FileCheck %s -input-file=%t_0.prop -check-prefix=PRESENCE-CHECK +; RUN: FileCheck %s -input-file=%t_0.prop -check-prefix=ABSENCE-CHECK target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024" target triple = "spir64-unknown-linux" @@ -22,9 +23,9 @@ target triple = "spir64-unknown-linux" @__spirv_BuiltInLocalInvocationId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32 @_ZL10assert_fmt = internal addrspace(2) constant [85 x i8] c"%s:%d: %s: global id: [%lu,%lu,%lu], local id: [%lu,%lu,%lu] Assertion `%s` failed.\0A\00", align 1 -; CHECK: [SYCL/assert used] +; PRESENCE-CHECK: [SYCL/assert used] -; CHECK-DAG: main_TU1_kernel1 +; PRESENCE-CHECK-DAG: main_TU1_kernel1 define dso_local spir_kernel void @main_TU1_kernel1() #2 { entry: call spir_func void @foo() @@ -38,6 +39,12 @@ entry: ret void } +; ABSENCE-CHECK-NOT: empty_kernel +define dso_local spir_kernel void @empty_kernel() { + %1 = ptrtoint void ()* @bar to i64 + ret void +} + define dso_local spir_func void @bar() #2 { entry: call spir_func void @_Z3foov() ; call assert @@ -45,7 +52,7 @@ entry: ret void } -; CHECK-DAG: main_TU0_kernel0 +; PRESENCE-CHECK-DAG: main_TU0_kernel0 define dso_local spir_kernel void @main_TU0_kernel0() #0 { entry: call spir_func void @_Z3foov() ; call assert @@ -63,13 +70,6 @@ entry: ret void } -; CHECK-NOT: main_TU0_kernel1 -define dso_local spir_kernel void @main_TU0_kernel1() #0 { -entry: - call spir_func void @_Z4foo1v() - ret void -} - ; Function Attrs: nounwind define dso_local spir_func void @_Z4foo1v() { entry: @@ -78,13 +78,19 @@ entry: ret void } -; CHECK-DAG: main_TU1_kernel0 +; PRESENCE-CHECK-DAG: main_TU1_kernel0 define dso_local spir_kernel void @main_TU1_kernel0() #2 { entry: call spir_func void @_Z3foov() ; call assert ret void } +; ABSENCE-CHECK-NOT: main_TU0_kernel1 +define dso_local spir_kernel void @main_TU0_kernel1() #0 { +entry: + call spir_func void @_Z4foo1v() + ret void +} ; This function is marked with "referenced-indirectly", but it doesn't call an assert ; Function Attrs: nounwind diff --git a/llvm/test/tools/sycl-post-link/assert-property-1.ll b/llvm/test/tools/sycl-post-link/assert-property-1.ll index df3bb294c6fe7..7a7a0024363f4 100644 --- a/llvm/test/tools/sycl-post-link/assert-property-1.ll +++ b/llvm/test/tools/sycl-post-link/assert-property-1.ll @@ -3,7 +3,8 @@ ; graph. ; RUN: sycl-post-link -split=auto -symbols -S %s -o %t.table -; RUN: FileCheck %s -input-file=%t_0.prop +; RUN: FileCheck %s -input-file=%t_0.prop -check-prefix=PRESENCE-CHECK +; RUN: FileCheck %s -input-file=%t_0.prop -check-prefix=ABSENCE-CHECK ; SYCL source: ; void foo() { @@ -48,7 +49,7 @@ target triple = "spir64_x86_64-unknown-unknown" @__spirv_BuiltInLocalInvocationId = external dso_local addrspace(1) constant <3 x i64>, align 32 @_ZL10assert_fmt = internal addrspace(2) constant [85 x i8] c"%s:%d: %s: global id: [%lu,%lu,%lu], local id: [%lu,%lu,%lu] Assertion `%s` failed.\0A\00", align 1 -; CHECK: [SYCL/assert used] +; PRESENCE-CHECK: [SYCL/assert used] ; Function Attrs: convergent norecurse nounwind mustprogress define dso_local spir_func void @_Z3foov() { @@ -57,7 +58,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel ; Function Attrs: convergent norecurse define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel"() #0 { entry: @@ -71,7 +72,7 @@ entry: ret void } -; CHECK-NOT: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE10TheKernel2 +; ABSENCE-CHECK-NOT: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE10TheKernel2 ; Function Attrs: norecurse define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE10TheKernel2"() #1 { entry: @@ -94,7 +95,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE10TheKernel3 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE10TheKernel3 ; Function Attrs: convergent norecurse define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE10TheKernel3"() #0 { entry: diff --git a/llvm/test/tools/sycl-post-link/assert-property-2.ll b/llvm/test/tools/sycl-post-link/assert-property-2.ll index 5a250734fead9..22c7bbc837746 100644 --- a/llvm/test/tools/sycl-post-link/assert-property-2.ll +++ b/llvm/test/tools/sycl-post-link/assert-property-2.ll @@ -3,7 +3,8 @@ ; graph. ; RUN: sycl-post-link -split=auto -symbols -S %s -o %t.table -; RUN: FileCheck %s -input-file=%t_0.prop +; RUN: FileCheck %s -input-file=%t_0.prop -check-prefix=PRESENCE-CHECK +; RUN: FileCheck %s -input-file=%t_0.prop -check-prefix=ABSENCE-CHECK ; SYCL source: ; void assert_func() { @@ -105,7 +106,7 @@ target triple = "spir64_x86_64-unknown-unknown" @__PRETTY_FUNCTION__._Z11assert_funcv = private unnamed_addr addrspace(1) constant [19 x i8] c"void assert_func()\00", align 1 @_ZL10assert_fmt = internal addrspace(2) constant [85 x i8] c"%s:%d: %s: global id: [%lu,%lu,%lu], local id: [%lu,%lu,%lu] Assertion `%s` failed.\0A\00", align 1 -; CHECK: [SYCL/assert used] +; PRESENCE-CHECK: [SYCL/assert used] ; Function Attrs: convergent noinline norecurse optnone mustprogress define dso_local spir_func void @_Z1Jv() #3 { @@ -122,7 +123,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE_clES2_E7Kernel9 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE_clES2_E7Kernel9 ; Function Attrs: convergent noinline norecurse mustprogress define weak_odr dso_local spir_kernel void @_ZTSZZ4mainENKUlRN2cl4sycl7handlerEE_clES2_E7Kernel9() #0 { entry: @@ -130,7 +131,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE_clES2_E8Kernel10 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENKUlRN2cl4sycl7handlerEE_clES2_E8Kernel10 ; Function Attrs: convergent noinline norecurse optnone mustprogress define weak_odr dso_local spir_kernel void @_ZTSZZ4mainENKUlRN2cl4sycl7handlerEE_clES2_E8Kernel10() #0 { entry: @@ -164,7 +165,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE6Kernel +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE6Kernel ; Function Attrs: convergent norecurse mustprogress define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE6Kernel"() local_unnamed_addr #0 { entry: @@ -186,7 +187,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel2 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel2 ; Function Attrs: convergent norecurse mustprogress define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel2"() local_unnamed_addr #0 { entry: @@ -216,7 +217,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel3 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel3 ; Function Attrs: convergent norecurse mustprogress define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel3"() local_unnamed_addr #0 { entry: @@ -244,7 +245,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel4 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel4 ; Function Attrs: convergent norecurse mustprogress define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel4"() local_unnamed_addr #0 { entry: @@ -252,7 +253,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel5 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel5 ; Function Attrs: convergent norecurse mustprogress define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel5"() local_unnamed_addr #0 { entry: @@ -267,15 +268,6 @@ entry: ret void } -; CHECK-NOT: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel -; Function Attrs: convergent norecurse mustprogress -define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel6"() local_unnamed_addr #0 { -entry: - call spir_func void @_Z6E_exclv() - call spir_func void @_Z6E_exclv() - ret void -} - ; Function Attrs: convergent norecurse nounwind mustprogress define dso_local spir_func void @_Z6F_inclv() local_unnamed_addr { entry: @@ -283,7 +275,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel7 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel7 ; Function Attrs: convergent norecurse mustprogress define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel7"() local_unnamed_addr #0 { entry: @@ -328,7 +320,7 @@ entry: ret void } -; CHECK: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel8 +; PRESENCE-CHECK-DAG: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel8 ; Function Attrs: convergent norecurse mustprogress define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel8"() local_unnamed_addr #0 { call spir_func void @_Z1Gv() @@ -336,6 +328,15 @@ define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7han ret void } +; ABSENCE-CHECK-NOT: _ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel6 +; Function Attrs: convergent norecurse mustprogress +define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE7Kernel6"() local_unnamed_addr #0 { +entry: + call spir_func void @_Z6E_exclv() + call spir_func void @_Z6E_exclv() + ret void +} + ; Function Attrs: convergent norecurse mustprogress define weak dso_local spir_func void @__assert_fail(i8 addrspace(4)* %expr, i8 addrspace(4)* %file, i32 %line, i8 addrspace(4)* %func) local_unnamed_addr { entry: diff --git a/llvm/test/tools/sycl-post-link/assert-property-with-split.ll b/llvm/test/tools/sycl-post-link/assert-property-with-split.ll index 0da502805f4bc..3209f03a56ab1 100644 --- a/llvm/test/tools/sycl-post-link/assert-property-with-split.ll +++ b/llvm/test/tools/sycl-post-link/assert-property-with-split.ll @@ -3,7 +3,8 @@ ; in their call graph. ; RUN: sycl-post-link -split=auto -symbols -S %s -o %t.table -; RUN: FileCheck %s -input-file=%t_0.prop +; RUN: FileCheck %s -input-file=%t_0.prop -check-prefix=PRESENCE-CHECK +; RUN: FileCheck %s -input-file=%t_0.prop -check-prefix=ABSENCE-CHECK target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024" target triple = "spir64-unknown-linux" @@ -16,9 +17,9 @@ target triple = "spir64-unknown-linux" @__spirv_BuiltInLocalInvocationId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32 @_ZL10assert_fmt = internal addrspace(2) constant [85 x i8] c"%s:%d: %s: global id: [%lu,%lu,%lu], local id: [%lu,%lu,%lu] Assertion `%s` failed.\0A\00", align 1 -; CHECK: [SYCL/assert used] +; PRESENCE-CHECK: [SYCL/assert used] -; CHECK: _ZTSZ4mainE11TU0_kernel0 +; PRESENCE-CHECK-DAG: _ZTSZ4mainE11TU0_kernel0 define dso_local spir_kernel void @_ZTSZ4mainE11TU0_kernel0() #0 { entry: call spir_func void @_Z3foov() @@ -36,7 +37,14 @@ entry: ret void } -; CHECK-NOT: _ZTSZ4mainE11TU0_kernel1 +; PRESENCE-CHECK-DAG: _ZTSZ4mainE10TU1_kernel +define dso_local spir_kernel void @_ZTSZ4mainE10TU1_kernel() #1 { +entry: + call spir_func void @_Z4foo2v() + ret void +} + +; ABSENCE-CHECK-NOT: _ZTSZ4mainE11TU0_kernel1 define dso_local spir_kernel void @_ZTSZ4mainE11TU0_kernel1() #0 { entry: call spir_func void @_Z4foo1v() @@ -51,13 +59,6 @@ entry: ret void } -; CHECK: _ZTSZ4mainE10TU1_kernel -define dso_local spir_kernel void @_ZTSZ4mainE10TU1_kernel() #1 { -entry: - call spir_func void @_Z4foo2v() - ret void -} - ; Function Attrs: nounwind define dso_local spir_func void @_Z4foo2v() { entry: diff --git a/llvm/tools/sycl-post-link/sycl-post-link.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp index e1f29cd1e683a..7b7cfd455bdff 100644 --- a/llvm/tools/sycl-post-link/sycl-post-link.cpp +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -46,7 +46,10 @@ #include #include #include +#include #include +#include +#include #include using namespace llvm; @@ -352,126 +355,75 @@ void groupEntryPoints(const Module &M, EntryPointGroupMap &EntryPointsGroups, EntryPointsGroups[GLOBAL_SCOPE_NAME] = {}; } -enum HasAssertStatus { No_Assert, Assert, Assert_Indirect }; - -// Go through function call graph searching for assert call. -HasAssertStatus hasAssertInFunctionCallGraph(const Function *Func) { - // Map holds the info about assertions in already examined functions: - // true - if there is an assertion in underlying functions, - // false - if there are definetely no assertions in underlying functions. - static std::map hasAssertionInCallGraphMap; - std::vector FuncCallStack; - - static std::vector isIndirectlyCalledInGraph; - - std::vector Workstack; - Workstack.push_back(Func); - - while (!Workstack.empty()) { - const Function *F = Workstack.back(); - Workstack.pop_back(); - if (F != Func) - FuncCallStack.push_back(F); - - bool HasIndirectlyCalledAttr = false; - if (std::find(isIndirectlyCalledInGraph.begin(), - isIndirectlyCalledInGraph.end(), - F) != isIndirectlyCalledInGraph.end()) - HasIndirectlyCalledAttr = true; - else if (F->hasFnAttribute("referenced-indirectly")) { - HasIndirectlyCalledAttr = true; - isIndirectlyCalledInGraph.push_back(F); - } - - bool IsLeaf = true; - for (const auto &I : instructions(F)) { - if (!isa(&I)) - continue; +// This function traverses over reversed call graph by BFS algorithm. +// It means that an edge links some function @func with functions +// which contain call of function @func. It starts from +// @StartingFunction and lifts up until it reach all reachable functions +// or it reaches some function containing "referenced-indirectly" attribute. +// If it reaches "referenced-indirectly" attribute than it returns an empty +// Optional. +// Otherwise, it returns an Optional containing a list of reached +// SPIR kernel function's names. +Optional> +TraverseCGToFindSPIRKernels(const Function *StartingFunction) { + std::queue FunctionsToVisit; + std::unordered_set VisitedFunctions; + FunctionsToVisit.push(StartingFunction); + std::vector KernelNames; + + while (!FunctionsToVisit.empty()) { + const Function *F = FunctionsToVisit.front(); + FunctionsToVisit.pop(); + + auto InsertionResult = VisitedFunctions.insert(F); + // It is possible that we insert some particular function several + // times in functionsToVisit queue. + if (!InsertionResult.second) + continue; - const Function *CF = cast(&I)->getCalledFunction(); - if (!CF) + for (const auto *U : F->users()) { + const CallInst *CI = dyn_cast(U); + if (!CI) continue; - bool IsIndirectlyCalled = - HasIndirectlyCalledAttr || - std::find(isIndirectlyCalledInGraph.begin(), - isIndirectlyCalledInGraph.end(), - CF) != isIndirectlyCalledInGraph.end(); - - // Return if we've already discovered if there are asserts in the - // function call graph. - auto HasAssert = hasAssertionInCallGraphMap.find(CF); - if (HasAssert != hasAssertionInCallGraphMap.end()) { - // If we know, that this function does not contain assert, we still - // should investigate another instructions in the function. - if (!HasAssert->second) - continue; - - return IsIndirectlyCalled ? Assert_Indirect : Assert; - } + const Function *ParentF = CI->getFunction(); - if (CF->getName().startswith("__devicelib_assert_fail")) { - // Mark all the functions above in call graph as ones that can call - // assert. - for (const auto *It : FuncCallStack) - hasAssertionInCallGraphMap[It] = true; - - hasAssertionInCallGraphMap[Func] = true; - hasAssertionInCallGraphMap[CF] = true; + if (VisitedFunctions.count(ParentF)) + continue; - return IsIndirectlyCalled ? Assert_Indirect : Assert; - } + if (ParentF->hasFnAttribute("referenced-indirectly")) + return {}; - if (!CF->isDeclaration()) { - Workstack.push_back(CF); - IsLeaf = false; - if (HasIndirectlyCalledAttr) - isIndirectlyCalledInGraph.push_back(CF); - } - } + if (ParentF->getCallingConv() == CallingConv::SPIR_KERNEL) + KernelNames.push_back(ParentF->getName()); - if (IsLeaf && !FuncCallStack.empty()) { - // Mark the leaf function as one that definetely does not call assert. - hasAssertionInCallGraphMap[FuncCallStack.back()] = false; - FuncCallStack.clear(); + FunctionsToVisit.push(ParentF); } } - return No_Assert; + + return std::move(KernelNames); } std::vector getKernelNamesUsingAssert(const Module &M) { - std::vector Result; + auto DevicelibAssertFailFunction = M.getFunction("__devicelib_assert_fail"); + if (!DevicelibAssertFailFunction) + return {}; - bool HasIndirectlyCalledAssert = false; - EntryPointGroup Kernels; - for (const auto &F : M.functions()) { - // TODO: handle SYCL_EXTERNAL functions for dynamic linkage. - // TODO: handle function pointers. - if (F.getCallingConv() != CallingConv::SPIR_KERNEL) - continue; + auto TraverseResult = + TraverseCGToFindSPIRKernels(DevicelibAssertFailFunction); - Kernels.push_back(&F); - if (HasIndirectlyCalledAssert) - continue; + if (TraverseResult.hasValue()) + return std::move(*TraverseResult); - HasAssertStatus HasAssert = hasAssertInFunctionCallGraph(&F); - switch (HasAssert) { - case Assert: - Result.push_back(F.getName()); - break; - case Assert_Indirect: - HasIndirectlyCalledAssert = true; - break; - case No_Assert: - break; - } + // Here we reached "referenced-indirectly", so we need to find all kernels and + // return them. + std::vector SPIRKernelNames; + for (const Function &F : M) { + if (F.getCallingConv() == CallingConv::SPIR_KERNEL) + SPIRKernelNames.push_back(F.getName()); } - if (HasIndirectlyCalledAssert) - for (const auto *F : Kernels) - Result.push_back(F->getName()); - - return Result; + return SPIRKernelNames; } // Gets reqd_work_group_size information for function Func.