Skip to content

Commit 57cece2

Browse files
authored
[SYCL][sycl-post-link] Emit helpful warning message for undefined user functions in a fully linked device image (#17346)
In the following simple example (simplicity for sake of discussion), ```c++ #include <sycl/sycl.hpp> SYCL_EXTERNAL int external_f1(int A, int B); void hostf(...) { ... cgh.parallel_for<class Test>(range, [=](sycl::id<1> ID) { accC[ID] = external_f1(accA[ID], accB[ID]); }); }); } ``` "external_f1" is a user function which is not defined in this module. Linking this module stand-alone will successfully create an executable. During run-time. there will be a run-time error saying that there is an unresolved symbol. This PR addresses a user request to alert the user of this behavior during compile-time by emitting a warning message. Thanks --------- Signed-off-by: Arvind Sudarsanam <arvind.sudarsanam@intel.com>
1 parent 2027c94 commit 57cece2

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

llvm/lib/SYCLLowerIR/ModuleSplitter.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "llvm/Support/Error.h"
3333
#include "llvm/Support/FileSystem.h"
3434
#include "llvm/Support/LineIterator.h"
35+
#include "llvm/Support/WithColor.h"
3536
#include "llvm/Transforms/IPO.h"
3637
#include "llvm/Transforms/IPO/GlobalDCE.h"
3738
#include "llvm/Transforms/IPO/Internalize.h"
@@ -298,6 +299,23 @@ void collectFunctionsAndGlobalVariablesToExtract(
298299
}
299300
}
300301

302+
static bool isIntrinsicOrBuiltin(const Function &F) {
303+
return F.isIntrinsic() || F.getName().starts_with("__") ||
304+
isSpirvSyclBuiltin(F.getName()) || isESIMDBuiltin(F.getName());
305+
}
306+
307+
// Checks for use of undefined user functions and emits a warning message.
308+
static void checkForCallsToUndefinedFunctions(const Module &M) {
309+
if (AllowDeviceImageDependencies)
310+
return;
311+
for (const Function &F : M) {
312+
if (!isIntrinsicOrBuiltin(F) && F.isDeclaration() && !F.use_empty())
313+
WithColor::warning() << "Undefined function " << F.getName()
314+
<< " found in " << M.getName()
315+
<< ". This may result in runtime errors.\n";
316+
}
317+
}
318+
301319
// Check "spirv.ExecutionMode" named metadata in the module and remove nodes
302320
// that reference kernels that have dead prototypes or don't reference any
303321
// kernel at all (nullptr). Dead prototypes are removed as well.
@@ -382,6 +400,7 @@ ModuleDesc extractCallGraph(const ModuleDesc &MD,
382400
// GenXSPIRVWriterAdaptor pass that relies on this cleanup. This cleanup call
383401
// can be removed once that pass no longer depends on this cleanup.
384402
SplitM.cleanup();
403+
checkForCallsToUndefinedFunctions(SplitM.getModule());
385404

386405
return SplitM;
387406
}
@@ -1452,8 +1471,7 @@ bool canBeImportedFunction(const Function &F) {
14521471
// in a header file. Thus SYCL IR that is a declaration
14531472
// will be considered as SYCL_EXTERNAL for the purposes of
14541473
// this function.
1455-
if (F.isIntrinsic() || F.getName().starts_with("__") ||
1456-
isSpirvSyclBuiltin(F.getName()) || isESIMDBuiltin(F.getName()) ||
1474+
if (isIntrinsicOrBuiltin(F) ||
14571475
(!F.isDeclaration() && !llvm::sycl::utils::isSYCLExternalFunction(&F)))
14581476
return false;
14591477

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clangxx %s -fsycl -fsycl-link 2>&1 | FileCheck %s --check-prefix=CHECK-WARNING
2+
// RUN: %clangxx %s -fsycl -fsycl-link -fsycl-allow-device-image-dependencies 2>&1 | FileCheck --allow-empty %s --check-prefix=CHECK-WARNING-DYNAMIC
3+
// This test is intended to check that we emit a helpful warning message for
4+
// undefined user functions in a fully linked device image after the
5+
// sycl-post-link stage of compilation.
6+
7+
// CHECK-WARNING: warning: Undefined function _Z11external_f1ii found in
8+
// CHECK-WARNING-DYNAMIC-NOT: warning: Undefined function _Z11external_f1ii found in
9+
10+
#include <sycl/sycl.hpp>
11+
12+
SYCL_EXTERNAL int external_f1(int A, int B);
13+
14+
void hostf(unsigned Size, sycl::buffer<int, 1> &bufA,
15+
sycl::buffer<int, 1> &bufB, sycl::buffer<int, 1> &bufC) {
16+
sycl::range<1> range{Size};
17+
sycl::queue().submit([&](sycl::handler &cgh) {
18+
auto accA = bufA.get_access<sycl::access::mode::read>(cgh);
19+
auto accB = bufB.get_access<sycl::access::mode::read>(cgh);
20+
auto accC = bufC.get_access<sycl::access::mode::write>(cgh);
21+
cgh.parallel_for<class Test>(range, [=](sycl::id<1> ID) {
22+
accC[ID] = external_f1(accA[ID], accB[ID]);
23+
});
24+
});
25+
}

0 commit comments

Comments
 (0)