diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index a13b0be8ff6d..62418cd647d1 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5714,6 +5714,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // Apply some call-site-specific attributes. // TODO: work this into building the attribute set. + if (getContext().getLangOpts().SYCLIsDevice && Callee.isVirtual()) { + // Annotate virtual calls in SYCL device code to help passes that emit + // diagnostics on incorrect uses of virtual functions. + Attrs = Attrs.addFnAttribute( + getLLVMContext(), + llvm::Attribute::get(getLLVMContext(), "virtual-call")); + } + // Apply always_inline to all calls within flatten functions. // FIXME: should this really take priority over __try, below? if (CurCodeDecl && CurCodeDecl->hasAttr() && diff --git a/clang/test/CodeGenSYCL/attrs-on-virtual-calls.cpp b/clang/test/CodeGenSYCL/attrs-on-virtual-calls.cpp new file mode 100644 index 000000000000..a8503f5b0df6 --- /dev/null +++ b/clang/test/CodeGenSYCL/attrs-on-virtual-calls.cpp @@ -0,0 +1,111 @@ +// Test verifies that clang codegen properly adds call site attributes to +// device code + +// RUN: %clang_cc1 -triple spir64 -fsycl-allow-virtual-functions \ +// RUN: -fsycl-is-device -emit-llvm %s -o %t.device +// RUN: FileCheck %s --input-file=%t.device +// RUN: %clang_cc1 -triple x86_64 -fsycl-allow-virtual-functions \ +// RUN: -fsycl-is-host -emit-llvm %s -o %t.host +// RUN: FileCheck %s --input-file=%t.host --check-prefix=CHECK-HOST + +// CHECK-HOST-NOT: attributes {{.*}} "virtual-call" + +#ifndef SYCL_EXTERNAL +#define SYCL_EXTERNAL +#endif + +SYCL_EXTERNAL bool rand(); + +class Base { +public: + virtual void display() {} +}; + +class Derived : public Base { +public: + void display() override {} + + // CHECK-LABEL: define {{.*}} @_ZN7Derived3foo + // CHECK: call {{.*}}void %[[#]]{{.*}} #[[#VIRTUAL_CALL_ATTR:]] + // CHECK: call {{.*}}void @_ZN4Base7display{{.*}} #[[#DIRECT_CALL_ATTR:]] + void foo() { + display(); // virtual call + Base::display(); // non-virtual call + } +}; + +SYCL_EXTERNAL void test_calls_with_implicit_this() { + Derived d; + d.foo(); +} + +// CHECK-LABEL: define {{.*}} @_Z21test_base_ref_to_base +// CHECK: call {{.*}}void %[[#]]{{.*}} #[[#VIRTUAL_CALL_ATTR]] +// CHECK: call {{.*}}void @_ZN4Base7display{{.*}} #[[#DIRECT_CALL_ATTR]] +SYCL_EXTERNAL void test_base_ref_to_base() { + Base b; + Base &br = b; + + br.display(); // virtual call + br.Base::display(); // non-virtual call +} + +// CHECK-LABEL: define {{.*}} @_Z24test_base_ref_to_derived +// CHECK: call {{.*}}void %[[#]]{{.*}} #[[#VIRTUAL_CALL_ATTR]] +// CHECK: call {{.*}}void @_ZN4Base7display{{.*}} #[[#DIRECT_CALL_ATTR]] +SYCL_EXTERNAL void test_base_ref_to_derived() { + Derived d; + Base &br = d; + + br.display(); // virtual call + br.Base::display(); // non-virtual call +} + +// CHECK-LABEL: define {{.*}} @_Z21test_base_ptr_to_base +// CHECK: call {{.*}}void %[[#]]{{.*}} #[[#VIRTUAL_CALL_ATTR]] +// CHECK: call {{.*}}void @_ZN4Base7display{{.*}} #[[#DIRECT_CALL_ATTR]] +SYCL_EXTERNAL void test_base_ptr_to_base() { + Base b; + Base *bp = &b; + + bp->display(); // virtual call + bp->Base::display(); // non-virtual call +} + +// CHECK-LABEL: define {{.*}} @_Z24test_base_ptr_to_derived +// CHECK: call {{.*}}void %[[#]]{{.*}} #[[#VIRTUAL_CALL_ATTR]] +// CHECK: call {{.*}}void @_ZN4Base7display{{.*}} #[[#DIRECT_CALL_ATTR]] +SYCL_EXTERNAL void test_base_ptr_to_derived() { + Derived d; + Base *bp = &d; + + bp->display(); // virtual call + bp->Base::display(); // non-virtual call +} + +// CHECK-LABEL: define {{.*}} @_Z9test_base +// CHECK: call {{.*}}void @_ZN4Base7display{{.*}} #[[#DIRECT_CALL_ATTR]] +// CHECK: call {{.*}}void @_ZN4Base7display{{.*}} #[[#DIRECT_CALL_ATTR]] +SYCL_EXTERNAL void test_base() { + Base b; + + // Strictly speaking, this is a virtual function call, but clang emits it + // as a direct call + b.display(); + b.Base::display(); // non-virtual call +} + +// CHECK-LABEL: define {{.*}} @_Z12test_derived +// CHECK: call {{.*}}void @_ZN7Derived7display{{.*}} #[[#DIRECT_CALL_ATTR]] +// CHECK: call {{.*}}void @_ZN4Base7display{{.*}} #[[#DIRECT_CALL_ATTR]] +SYCL_EXTERNAL void test_derived() { + Derived d; + + // Strictly speaking, this is a virtual function call, but clang emits it + // as a direct call + d.display(); + d.Base::display(); // non-virtual call +} + +// CHECK-NOT: attributes #[[#DIRECT_CALL_ATTR]] = {{.*}}"virtual-call" +// CHECK: attributes #[[#VIRTUAL_CALL_ATTR]] = {{.*}}"virtual-call"