Skip to content

Commit

Permalink
[SYCL] Fix crash caused by functor without call operator as Kernel (#…
Browse files Browse the repository at this point in the history
…7104)

When a named function object without a call operator defined is used as
a kernel, the compiler causes a `crash`.
With the changes in this PR, a compiler `error` is thrown when a lambda
or function object without a call operator defined is used as a kernel.

Co-authored-by: premanandrao <premanand.m.rao@intel.com>
  • Loading branch information
srividya-sundaram and premanandrao committed Oct 28, 2022
1 parent c49eeda commit 490ee55
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 16 deletions.
43 changes: 27 additions & 16 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,28 @@ static QualType GetSYCLKernelObjectType(const FunctionDecl *KernelCaller) {
return KernelParamTy.getUnqualifiedType();
}

static CXXMethodDecl *getOperatorParens(const CXXRecordDecl *Rec) {
for (auto *MD : Rec->methods()) {
if (MD->getOverloadedOperator() == OO_Call)
return MD;
}
return nullptr;
}

// Fetch the associated call operator of the kernel object
// (of either the lambda or the function object).
static CXXMethodDecl *
GetCallOperatorOfKernelObject(const CXXRecordDecl *KernelObjType) {
CXXMethodDecl *CallOperator = nullptr;
if (!KernelObjType)
return CallOperator;
if (KernelObjType->isLambda())
CallOperator = KernelObjType->getLambdaCallOperator();
else
CallOperator = getOperatorParens(KernelObjType);
return CallOperator;
}

/// Creates a kernel parameter descriptor
/// \param Src field declaration to construct name from
/// \param Ty the desired parameter type
Expand Down Expand Up @@ -2775,14 +2797,6 @@ class SyclOptReportCreator : public SyclKernelFieldHandler {
}
};

static CXXMethodDecl *getOperatorParens(const CXXRecordDecl *Rec) {
for (auto *MD : Rec->methods()) {
if (MD->getOverloadedOperator() == OO_Call)
return MD;
}
return nullptr;
}

static bool isESIMDKernelType(const CXXRecordDecl *KernelObjType) {
const CXXMethodDecl *OpParens = getOperatorParens(KernelObjType);
return (OpParens != nullptr) && OpParens->hasAttr<SYCLSimdAttr>();
Expand Down Expand Up @@ -2871,13 +2885,10 @@ class SyclKernelBodyCreator : public SyclKernelFieldHandler {

// Fetch the kernel object and the associated call operator
// (of either the lambda or the function object).
CXXRecordDecl *KernelObj =
const CXXRecordDecl *KernelObj =
GetSYCLKernelObjectType(KernelCallerFunc)->getAsCXXRecordDecl();
CXXMethodDecl *WGLambdaFn = nullptr;
if (KernelObj->isLambda())
WGLambdaFn = KernelObj->getLambdaCallOperator();
else
WGLambdaFn = getOperatorParens(KernelObj);
CXXMethodDecl *WGLambdaFn = GetCallOperatorOfKernelObject(KernelObj);

assert(WGLambdaFn && "non callable object is passed as kernel obj");
// Mark the function that it "works" in a work group scope:
// NOTE: In case of parallel_for_work_item the marker call itself is
Expand Down Expand Up @@ -3534,7 +3545,7 @@ class SyclKernelBodyCreator : public SyclKernelFieldHandler {
static bool IsSYCLUnnamedKernel(Sema &SemaRef, const FunctionDecl *FD) {
if (!SemaRef.getLangOpts().SYCLUnnamedLambda)
return false;
QualType FunctorTy = GetSYCLKernelObjectType(FD);
const QualType FunctorTy = GetSYCLKernelObjectType(FD);
QualType TmplArgTy = calculateKernelNameType(SemaRef.Context, FD);
return SemaRef.Context.hasSameType(FunctorTy, TmplArgTy);
}
Expand Down Expand Up @@ -3960,7 +3971,7 @@ void Sema::CheckSYCLKernelCall(FunctionDecl *KernelFunc,
const CXXRecordDecl *KernelObj =
GetSYCLKernelObjectType(KernelFunc)->getAsCXXRecordDecl();

if (!KernelObj) {
if (!GetCallOperatorOfKernelObject(KernelObj)) {
Diag(Args[0]->getExprLoc(), diag::err_sycl_kernel_not_function_object);
KernelFunc->setInvalidDecl();
return;
Expand Down
40 changes: 40 additions & 0 deletions clang/test/SemaSYCL/undefined-functor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -sycl-std=2020 -verify -fsyntax-only %s
// This test checks that an error is thrown when a functor without a call operator defined is used as a kernel.

#include "sycl.hpp"

using namespace sycl;
queue q;

struct FunctorWithoutCallOperator; // expected-note {{forward declaration of 'FunctorWithoutCallOperator'}}

struct StructDefined {
int x;
};

class FunctorWithCallOpDefined {
int x;
public:
void operator()() const {}
};

int main() {

q.submit([&](sycl::handler &cgh) {
// expected-error@#KernelSingleTask {{kernel parameter must be a lambda or function object}}
// expected-error@+2 {{invalid use of incomplete type 'FunctorWithoutCallOperator'}}
// expected-note@+1 {{in instantiation of function template specialization}}
cgh.single_task(FunctorWithoutCallOperator{});
});

q.submit([&](sycl::handler &cgh) {
// expected-error@#KernelSingleTask {{kernel parameter must be a lambda or function object}}
// expected-note@+1 {{in instantiation of function template specialization}}
cgh.single_task(StructDefined{});
});

q.submit([&](sycl::handler &cgh) {
cgh.single_task(FunctorWithCallOpDefined{});
});

}

0 comments on commit 490ee55

Please sign in to comment.