Skip to content

Commit ea2111c

Browse files
[SYCL] Re-implement diagnostics about virtual calls (#14141)
With `sycl_ext_oneapi_virtual_functions` extensions we would like to allow certain kernels to perform virtual function calls. That is if they were submitted with the right properties. That means that instead of simply checking for presence of virtual function calls in device code, we need to analyze call chain to see how exactly a kernel performing such call is defined. This is not a task for the front-end and therefore the diagnostics mechanism is moved to a pass, as suggested by the implementation design proposed in #10540
1 parent e0b4c7f commit ea2111c

28 files changed

+404
-79
lines changed

clang/include/clang/Basic/DiagnosticFrontendKinds.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,16 @@ def err_target_unsupported_type_for_abi
308308
: Error<"%0 requires %1 type support, but ABI '%2' does not support it">;
309309
}
310310

311+
def err_sycl_illegal_virtual_call
312+
: Error<"kernel '%0' performs a virtual function call and they are illegal "
313+
"per the core SYCL 2020 specification. To enable their support, "
314+
"submit a kernel using the 'calls_indirectly' property, see "
315+
"the sycl_ext_oneapi_virtual_functions extension for more "
316+
"information">,
317+
BackendInfo;
318+
def note_sycl_virtual_call_done_from : Note<"performed by function '%1'">,
319+
BackendInfo;
320+
311321
def err_alias_to_undefined : Error<
312322
"%select{alias|ifunc}0 must point to a defined "
313323
"%select{variable or |}1function">;

clang/include/clang/Basic/LangOptions.def

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,6 @@ ENUM_LANGOPT(SYCLRangeRounding, SYCLRangeRoundingPreference, 2,
316316
LANGOPT(SYCLExperimentalRangeRounding, 1, 0, "Use experimental parallel for range rounding")
317317
LANGOPT(SYCLEnableIntHeaderDiags, 1, 0, "Enable diagnostics that require the "
318318
"SYCL integration header")
319-
LANGOPT(SYCLAllowVirtualFunctions, 1, 0,
320-
"Allow virtual functions calls in code for SYCL device")
321319
LANGOPT(SYCLIsNativeCPU , 1, 0, "Generate code for SYCL Native CPU")
322320

323321
LANGOPT(HIPUseNewLaunchAPI, 1, 0, "Use new kernel launching API for HIP")

clang/include/clang/Driver/Options.td

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8454,9 +8454,6 @@ def fsycl_use_main_file_name : Flag<["-"], "fsycl-use-main-file-name">,
84548454
HelpText<"Tells compiler that -main-file-name contains an absolute path and "
84558455
"file specified there should be used for checksum calculation.">,
84568456
MarshallingInfoFlag<CodeGenOpts<"SYCLUseMainFileName">>;
8457-
def fsycl_allow_virtual_functions : Flag<["-"], "fsycl-allow-virtual-functions">,
8458-
HelpText<"Allow virtual functions calls in code for SYCL device">,
8459-
MarshallingInfoFlag<LangOpts<"SYCLAllowVirtualFunctions">>;
84608457
def fsycl_is_native_cpu : Flag<["-"], "fsycl-is-native-cpu">,
84618458
HelpText<"Perform device compilation for Native CPU.">,
84628459
Visibility<[CC1Option]>,

clang/lib/CodeGen/BackendConsumer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ class BackendConsumer : public ASTConsumer {
160160
/// Note that misexpect remarks are emitted through ORE
161161
void MisExpectDiagHandler(const llvm::DiagnosticInfoMisExpect &D);
162162
void AspectMismatchDiagHandler(const llvm::DiagnosticInfoAspectsMismatch &D);
163+
void SYCLIllegalVirtualCallDiagHandler(
164+
const llvm::DiagnosticInfoIllegalVirtualCall &D);
163165
};
164166

165167
} // namespace clang

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "llvm/SYCLLowerIR/SYCLConditionalCallOnDevice.h"
5959
#include "llvm/SYCLLowerIR/SYCLPropagateAspectsUsage.h"
6060
#include "llvm/SYCLLowerIR/SYCLPropagateJointMatrixUsage.h"
61+
#include "llvm/SYCLLowerIR/SYCLVirtualFunctionsAnalysis.h"
6162
#include "llvm/SYCLLowerIR/UtilsSYCLNativeCPU.h"
6263
#include "llvm/Support/BuryPointer.h"
6364
#include "llvm/Support/CommandLine.h"
@@ -992,6 +993,7 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
992993
if (LangOpts.SYCLIsDevice)
993994
PB.registerPipelineStartEPCallback([&](ModulePassManager &MPM,
994995
OptimizationLevel Level) {
996+
MPM.addPass(SYCLVirtualFunctionsAnalysisPass());
995997
MPM.addPass(ESIMDVerifierPass(LangOpts.SYCLESIMDForceStatelessMem));
996998
if (Level == OptimizationLevel::O0)
997999
MPM.addPass(ESIMDRemoveOptnoneNoinlinePass());

clang/lib/CodeGen/CodeGenAction.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,28 @@ void BackendConsumer::AspectMismatchDiagHandler(
806806
}
807807
}
808808

809+
void BackendConsumer::SYCLIllegalVirtualCallDiagHandler(
810+
const llvm::DiagnosticInfoIllegalVirtualCall &D) {
811+
const llvm::SmallVector<std::pair<StringRef, unsigned>, 8> &CallChain =
812+
D.getCallChain();
813+
auto &KI = CallChain.front();
814+
815+
SourceLocation LocCookie = SourceLocation::getFromRawEncoding(KI.second);
816+
assert(LocCookie.isValid() &&
817+
"Invalid location for kernel in illegal virtual call diagnostic");
818+
Diags.Report(LocCookie, diag::err_sycl_illegal_virtual_call)
819+
<< llvm::demangle(KI.first.str());
820+
821+
for (size_t I = 1; I < CallChain.size(); ++I) {
822+
auto &CalleeInfo = CallChain[I];
823+
LocCookie = SourceLocation::getFromRawEncoding(CalleeInfo.second);
824+
assert(LocCookie.isValid() &&
825+
"Invalid location for callee in illegal virtual call diagnostic");
826+
Diags.Report(LocCookie, diag::note_sycl_virtual_call_done_from)
827+
<< /* function */ 0 << llvm::demangle(CalleeInfo.first.str());
828+
}
829+
}
830+
809831
void BackendConsumer::MisExpectDiagHandler(
810832
const llvm::DiagnosticInfoMisExpect &D) {
811833
StringRef Filename;
@@ -910,6 +932,10 @@ void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) {
910932
case llvm::DK_AspectMismatch:
911933
AspectMismatchDiagHandler(cast<DiagnosticInfoAspectsMismatch>(DI));
912934
return;
935+
case llvm::DK_SYCLIllegalVirtualCall:
936+
SYCLIllegalVirtualCallDiagHandler(
937+
cast<DiagnosticInfoIllegalVirtualCall>(DI));
938+
return;
913939
default:
914940
// Plugin IDs are not bound to any value as they are set dynamically.
915941
ComputeDiagRemarkID(Severity, backend_plugin, DiagID);

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -598,12 +598,6 @@ class DiagDeviceFunction : public RecursiveASTVisitor<DiagDeviceFunction> {
598598
<< SemaSYCL::KernelCallRecursiveFunction;
599599
}
600600

601-
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Callee))
602-
if (Method->isVirtual() &&
603-
!SemaSYCLRef.getLangOpts().SYCLAllowVirtualFunctions)
604-
SemaSYCLRef.Diag(e->getExprLoc(), diag::err_sycl_restrict)
605-
<< SemaSYCL::KernelCallVirtualFunction;
606-
607601
if (auto const *FD = dyn_cast<FunctionDecl>(Callee)) {
608602
// FIXME: We need check all target specified attributes for error if
609603
// that function with attribute can not be called from sycl kernel. The

clang/test/CodeGenSYCL/add_ir_attributes_function_virtual.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -internal-isystem %S/Inputs -triple spir64-unknown-unknown -fsycl-is-device \
2-
// RUN: -fsycl-allow-virtual-functions -emit-llvm %s -o - | FileCheck %s
1+
// RUN: %clang_cc1 -internal-isystem %S/Inputs -triple spir64-unknown-unknown \
2+
// RUN: -fsycl-is-device -emit-llvm %s -o - | FileCheck %s
33

44
// Test IR generated for add_ir_attributes_function on virtual functions.
55

@@ -47,4 +47,4 @@ void foo() {
4747
// CHECK-NOT: PropDerived
4848
// CHECK: }
4949
// CHECK: attributes #[[BaseAttrs]] = { {{.*}}"PropBase"="PropVal"{{.*}} }
50-
// CHECK: attributes #[[Derived1Attrs]] = { {{.*}}"PropDerived"="PropVal"{{.*}} }
50+
// CHECK: attributes #[[Derived1Attrs]] = { {{.*}}"PropDerived"="PropVal"{{.*}} }

clang/test/CodeGenSYCL/attrs-on-virtual-calls.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
// Test verifies that clang codegen properly adds call site attributes to
22
// device code
33

4-
// RUN: %clang_cc1 -triple spir64 -fsycl-allow-virtual-functions \
5-
// RUN: -fsycl-is-device -emit-llvm %s -o %t.device
4+
// RUN: %clang_cc1 -triple spir64 -fsycl-is-device -emit-llvm %s -o %t.device
65
// RUN: FileCheck %s --input-file=%t.device
7-
// RUN: %clang_cc1 -triple x86_64 -fsycl-allow-virtual-functions \
8-
// RUN: -fsycl-is-host -emit-llvm %s -o %t.host
6+
// RUN: %clang_cc1 -triple x86_64 -fsycl-is-host -emit-llvm %s -o %t.host
97
// RUN: FileCheck %s --input-file=%t.host --check-prefix=CHECK-HOST
108

119
// CHECK-HOST-NOT: attributes {{.*}} "virtual-call"

clang/test/CodeGenSYCL/force-emit-device-virtual-funcs.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -internal-isystem %S/Inputs -triple spir64-unknown-unknown -fsycl-is-device \
2-
// RUN: -fsycl-allow-virtual-functions -emit-llvm %s -o %t.ll
1+
// RUN: %clang_cc1 -internal-isystem %S/Inputs -triple spir64-unknown-unknown \
2+
// RUN: -fsycl-is-device -emit-llvm %s -o %t.ll
33
// RUN: FileCheck %s --input-file=%t.ll --implicit-check-not _ZN7Derived3baz \
44
// RUN: --implicit-check-not _ZN4Base4baz --implicit-check-not _ZN4Base3foo
55
//

clang/test/CodeGenSYCL/simple-sycl-virtual-function.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
// 2. Virtual table elements are generated in AS4.
44
// 3. Runtime Global Variables are generated in AS1.
55

6-
// RUN: %clang_cc1 -triple spir64 -fsycl-allow-virtual-functions -fsycl-is-device -emit-llvm %s -o - | FileCheck %s --implicit-check-not _ZTI4Base --implicit-check-not _ZTI8Derived1 -check-prefix VTABLE
7-
// RUNx: %clang_cc1 -triple spir64 -fsycl-allow-virtual-functions -fsycl-is-device -fexperimental-relative-c++-abi-vtables -emit-llvm %s -o - | FileCheck %s --implicit-check-not _ZTI4Base --implicit-check-not _ZTI8Derived1
6+
// RUN: %clang_cc1 -triple spir64 -fsycl-is-device -emit-llvm %s -o - | FileCheck %s --implicit-check-not _ZTI4Base --implicit-check-not _ZTI8Derived1 -check-prefix VTABLE
7+
// RUNx: %clang_cc1 -triple spir64 -fsycl-is-device -fexperimental-relative-c++-abi-vtables -emit-llvm %s -o - | FileCheck %s --implicit-check-not _ZTI4Base --implicit-check-not _ZTI8Derived1
88

99
// Since experimental-relative-c++-abi-vtables is some experimental option, temporary disabling the check for now
1010
// until we emit proper address spaces (and casts) everywhere.

clang/test/CodeGenSYCL/sycl-device-only-virtual-functions.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
// [[intel::device_indirectly_callable]] attribute or SYCL_EXTERNAL macro.
44
//
55
// RUN: %clang_cc1 -emit-llvm -o - -fsycl-is-device \
6-
// RUN: -fsycl-allow-virtual-functions -internal-isystem %S/Inputs \
7-
// RUN: -triple spir64 %s -o %t.ll
6+
// RUN: -internal-isystem %S/Inputs -triple spir64 %s -o %t.ll
87
// RUN: FileCheck %s --input-file %t.ll --implicit-check-not host \
98
// RUN: --implicit-check-not _ZN8Derived416maybe_device_barEv
109
//

clang/test/SemaSYCL/no-vtables2.cpp

Lines changed: 0 additions & 39 deletions
This file was deleted.

clang/test/SemaSYCL/sycl-pseudo-dtor.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fsycl-is-device -verify -fsyntax-only %s
2+
// expected-no-diagnostics
23

34
template <typename functor_t>
45
struct functor_wrapper{
@@ -14,17 +15,13 @@ struct T { virtual ~T(); };
1415

1516
template <typename name, typename Func>
1617
__attribute__((sycl_kernel)) void kernel_single_task(const Func &kernelFunc) {
17-
// expected-no-note@+1
1818
using DATA_I = int;
1919
using DATA_S = S;
2020
using DATA_T = T;
2121
// this expression should be okay
2222
auto functor = [](DATA_I & v1, DATA_S &v2, DATA_T& v3) {
23-
// expected-no-error@+1
2423
v1.~DATA_I();
2524
v2.~DATA_S();
26-
// expected-error@+1{{SYCL kernel cannot call a virtual function}}
27-
v3.~DATA_T();
2825
};
2926
auto wrapped_functor = functor_wrapper<decltype(functor)>{functor};
3027
wrapped_functor();

clang/test/SemaSYCL/sycl-restrict.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,6 @@ __attribute__((sycl_kernel)) void kernel1(const L &l) {
103103
}
104104
} // namespace Check_RTTI_Restriction
105105

106-
typedef struct Base {
107-
virtual void f() const {}
108-
} b_type;
109-
110106
typedef struct A {
111107
static int stat_member;
112108
const static int const_stat_member;
@@ -117,8 +113,6 @@ typedef struct A {
117113
}
118114
} a_type;
119115

120-
b_type b;
121-
122116
using myFuncDef = int(int, int);
123117

124118
// defines (early and late)
@@ -225,8 +219,7 @@ void usage(myFuncDef functionPtr) {
225219
// expected-error@+2 {{SYCL kernel cannot call through a function pointer}}
226220
#endif
227221
if ((*functionPtr)(1, 2))
228-
// expected-error@+1 {{SYCL kernel cannot use a non-const global variable}}
229-
b.f(); // expected-error {{SYCL kernel cannot call a virtual function}}
222+
/* no-op */;
230223

231224
Check_RTTI_Restriction::kernel1<class kernel_name>([]() { //#call_rtti_kernel
232225
Check_RTTI_Restriction::A *a;

llvm/include/llvm/IR/DiagnosticInfo.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ enum DiagnosticKind {
8787
DK_DontCall,
8888
DK_MisExpect,
8989
DK_AspectMismatch,
90+
DK_SYCLIllegalVirtualCall,
9091
DK_FirstPluginKind // Must be last value to work with
9192
// getNextAvailablePluginDiagnosticKind
9293
};
@@ -1151,6 +1152,28 @@ class DiagnosticInfoAspectsMismatch : public DiagnosticInfo {
11511152
return DI->getKind() == DK_AspectMismatch;
11521153
}
11531154
};
1155+
1156+
void diagnoseSYCLIllegalVirtualFunctionCall(
1157+
const SmallVector<const Function *> &CallChain);
1158+
1159+
// Diagnostic information for SYCL virtual functions
1160+
class DiagnosticInfoIllegalVirtualCall : public DiagnosticInfo {
1161+
llvm::SmallVector<std::pair<StringRef, unsigned>, 8> CallChain;
1162+
1163+
public:
1164+
DiagnosticInfoIllegalVirtualCall(
1165+
const llvm::SmallVector<std::pair<StringRef, unsigned>, 8> &CallChain)
1166+
: DiagnosticInfo(DK_SYCLIllegalVirtualCall, DiagnosticSeverity::DS_Error),
1167+
CallChain(CallChain) {}
1168+
const llvm::SmallVector<std::pair<StringRef, unsigned>, 8> &
1169+
getCallChain() const {
1170+
return CallChain;
1171+
}
1172+
void print(DiagnosticPrinter &DP) const override;
1173+
static bool classof(const DiagnosticInfo *DI) {
1174+
return DI->getKind() == DK_SYCLIllegalVirtualCall;
1175+
}
1176+
};
11541177
} // end namespace llvm
11551178

11561179
#endif // LLVM_IR_DIAGNOSTICINFO_H
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===---------------- SYCLVirtualFunctionsAnalysis.h ----------------------===//
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+
// Emits diagnostics for improper use of virtual functions in SYCL device code.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_SYCL_VIRTUAL_FUNCTIONS_ANALYSIS_H
14+
#define LLVM_SYCL_VIRTUAL_FUNCTIONS_ANALYSIS_H
15+
16+
#include "llvm/IR/PassManager.h"
17+
18+
namespace llvm {
19+
20+
class SYCLVirtualFunctionsAnalysisPass
21+
: public PassInfoMixin<SYCLVirtualFunctionsAnalysisPass> {
22+
public:
23+
PreservedAnalyses run(Module &M, ModuleAnalysisManager &);
24+
};
25+
26+
} // namespace llvm
27+
28+
#endif // LLVM_SYCL_VIRTUAL_FUNCTIONS_ANALYSIS_H

llvm/lib/IR/DiagnosticInfo.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,3 +483,25 @@ void DiagnosticInfoAspectsMismatch::print(DiagnosticPrinter &DP) const {
483483
<< "\" but does not specify that aspect as available in its "
484484
"\"sycl::device_has\" attribute";
485485
}
486+
487+
void llvm::diagnoseSYCLIllegalVirtualFunctionCall(
488+
const SmallVector<const Function *> &CallChain) {
489+
llvm::SmallVector<std::pair<StringRef, unsigned>, 8> LoweredCallChain;
490+
for (const Function *Callee : CallChain) {
491+
unsigned CalleeLocCookie = 0;
492+
if (MDNode *MD = Callee->getMetadata("srcloc"))
493+
CalleeLocCookie =
494+
mdconst::extract<ConstantInt>(MD->getOperand(0))->getZExtValue();
495+
LoweredCallChain.push_back(
496+
std::make_pair(Callee->getName(), CalleeLocCookie));
497+
}
498+
499+
DiagnosticInfoIllegalVirtualCall D(LoweredCallChain);
500+
CallChain.front()->getContext().diagnose(D);
501+
}
502+
503+
void DiagnosticInfoIllegalVirtualCall::print(DiagnosticPrinter &DP) const {
504+
DP << CallChain.front().first
505+
<< " performs virtual function call, but a kernel that is called from is "
506+
"not submitted with \"calls_indirectly\" property";
507+
}

llvm/lib/Passes/PassBuilder.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
#include "llvm/SYCLLowerIR/SYCLConditionalCallOnDevice.h"
131131
#include "llvm/SYCLLowerIR/SYCLPropagateAspectsUsage.h"
132132
#include "llvm/SYCLLowerIR/SYCLPropagateJointMatrixUsage.h"
133+
#include "llvm/SYCLLowerIR/SYCLVirtualFunctionsAnalysis.h"
133134
#include "llvm/Support/CommandLine.h"
134135
#include "llvm/Support/Debug.h"
135136
#include "llvm/Support/ErrorHandling.h"

llvm/lib/Passes/PassRegistry.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ MODULE_PASS("compile-time-properties", CompileTimePropertiesPass())
162162
MODULE_PASS("cleanup-sycl-metadata", CleanupSYCLMetadataPass())
163163
MODULE_PASS("lower-slm-reservation-calls", ESIMDLowerSLMReservationCalls())
164164
MODULE_PASS("record-sycl-aspect-names", RecordSYCLAspectNamesPass())
165+
MODULE_PASS("sycl-virtual-functions-analysis",
166+
SYCLVirtualFunctionsAnalysisPass())
165167
#undef MODULE_PASS
166168

167169
#ifndef MODULE_PASS_WITH_PARAMS

llvm/lib/SYCLLowerIR/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ add_llvm_component_library(LLVMSYCLLowerIR
6565
SYCLKernelParamOptInfo.cpp
6666
SYCLPropagateAspectsUsage.cpp
6767
SYCLPropagateJointMatrixUsage.cpp
68+
SYCLVirtualFunctionsAnalysis.cpp
6869
SYCLUtils.cpp
6970
SanitizeDeviceGlobal.cpp
7071

0 commit comments

Comments
 (0)