[OpenCL] Add clang extension for function pointers.

The new clang internal extension '__cl_clang_function_pointers'
allows use of function pointers and other features that have
the same functionality:
- Use of member function pointers;
- Unrestricted use of references to functions;
- Virtual member functions.

This not a vendor extension and therefore it doesn't require any
special target support. Exposing this functionality fully
will require vendor or Khronos extension.

Tags: #clang

Differential Revision: https://reviews.llvm.org/D94021
This commit is contained in:
Anastasia Stulova 2021-01-06 19:05:09 +00:00
parent badc7606b0
commit 4fde2b6a0c
14 changed files with 153 additions and 27 deletions

View File

@ -1722,6 +1722,58 @@ syntax to be used with ``std::complex`` with the same meaning.)
For GCC compatibility, ``__builtin_complex(re, im)`` can also be used to
construct a complex number from the given real and imaginary components.
OpenCL Features
===============
Clang supports internal OpenCL extensions documented below.
``__cl_clang_function_pointers``
--------------------------------
With this extension it is possible to enable various language features that
are relying on function pointers using regular OpenCL extension pragma
mechanism detailed in `the OpenCL Extension Specification,
section 1.2
<https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Ext.html#extensions-overview>`_.
In C++ for OpenCL this also enables:
- Use of member function pointers;
- Unrestricted use of references to functions;
- Virtual member functions.
Such functionality is not conformant and does not guarantee to compile
correctly in any circumstances. It can be used if:
- the kernel source does not contain call expressions to (member-) function
pointers, or virtual functions. For example this extension can be used in
metaprogramming algorithms to be able to specify/detect types generically.
- the generated kernel binary does not contain indirect calls because they
are eliminated using compiler optimizations e.g. devirtualization.
- the selected target supports the function pointer like functionality e.g.
most CPU targets.
**Example of Use**:
.. code-block:: c++
#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
void foo()
{
void (*fp)(); // compiled - no diagnostic generated
}
#pragma OPENCL EXTENSION __cl_clang_function_pointers : disable
void bar()
{
void (*fp)(); // error - pointers to function are not allowed
}
Builtin Functions
=================

View File

@ -69,6 +69,7 @@ OPENCLEXT_INTERNAL(cl_khr_subgroups, 200, ~0U)
// Clang Extensions.
OPENCLEXT_INTERNAL(cl_clang_storage_class_specifiers, 100, ~0U)
OPENCLEXT_INTERNAL(__cl_clang_function_pointers, 100, ~0U)
// AMD OpenCL extensions
OPENCLEXT_INTERNAL(cl_amd_media_ops, 100, ~0U)

View File

@ -285,6 +285,7 @@ public:
void setSupportedOpenCLOpts() override {
auto &Opts = getSupportedOpenCLOpts();
Opts.support("cl_clang_storage_class_specifiers");
Opts.support("__cl_clang_function_pointers");
bool IsAMDGCN = isAMDGCN(getTriple());

View File

@ -128,6 +128,7 @@ public:
void setSupportedOpenCLOpts() override {
auto &Opts = getSupportedOpenCLOpts();
Opts.support("cl_clang_storage_class_specifiers");
Opts.support("__cl_clang_function_pointers");
Opts.support("cl_khr_fp64");
Opts.support("cl_khr_byte_addressable_store");

View File

@ -3630,12 +3630,13 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
case tok::kw_virtual:
// C++ for OpenCL does not allow virtual function qualifier, to avoid
// function pointers restricted in OpenCL v2.0 s6.9.a.
if (getLangOpts().OpenCLCPlusPlus) {
if (getLangOpts().OpenCLCPlusPlus &&
!getActions().getOpenCLOptions().isEnabled(
"__cl_clang_function_pointers")) {
DiagID = diag::err_openclcxx_virtual_function;
PrevSpec = Tok.getIdentifierInfo()->getNameStart();
isInvalid = true;
}
else {
} else {
isInvalid = DS.setFunctionSpecVirtual(Loc, PrevSpec, DiagID);
}
break;

View File

@ -6748,14 +6748,16 @@ static bool diagnoseOpenCLTypes(Scope *S, Sema &Se, Declarator &D,
}
// OpenCL v1.0 s6.8.a.3: Pointers to functions are not allowed.
QualType NR = R;
while (NR->isPointerType() || NR->isMemberFunctionPointerType()) {
if (NR->isFunctionPointerType() || NR->isMemberFunctionPointerType()) {
Se.Diag(D.getIdentifierLoc(), diag::err_opencl_function_pointer);
D.setInvalidType();
return false;
if (!Se.getOpenCLOptions().isEnabled("__cl_clang_function_pointers")) {
QualType NR = R;
while (NR->isPointerType() || NR->isMemberFunctionPointerType()) {
if (NR->isFunctionPointerType() || NR->isMemberFunctionPointerType()) {
Se.Diag(D.getIdentifierLoc(), diag::err_opencl_function_pointer);
D.setInvalidType();
return false;
}
NR = NR->getPointeeType();
}
NR = NR->getPointeeType();
}
if (!Se.getOpenCLOptions().isEnabled("cl_khr_fp16")) {

View File

@ -2089,7 +2089,8 @@ QualType Sema::BuildPointerType(QualType T,
return QualType();
}
if (T->isFunctionType() && getLangOpts().OpenCL) {
if (T->isFunctionType() && getLangOpts().OpenCL &&
!getOpenCLOptions().isEnabled("__cl_clang_function_pointers")) {
Diag(Loc, diag::err_opencl_function_pointer);
return QualType();
}

View File

@ -12,7 +12,12 @@
#ifndef cl_clang_storage_class_specifiers
#error "Missing cl_clang_storage_class_specifiers define"
#endif
#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers: enable
#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable
#ifndef __cl_clang_function_pointers
#error "Missing __cl_clang_function_pointers define"
#endif
#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
#ifndef cl_khr_fp16
#error "Missing cl_khr_fp16 define"

View File

@ -20,7 +20,12 @@
#ifndef cl_clang_storage_class_specifiers
#error "Missing cl_clang_storage_class_specifiers define"
#endif
#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers: enable
#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable
#ifndef __cl_clang_function_pointers
#error "Missing __cl_clang_function_pointers define"
#endif
#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
#ifdef cl_khr_fp16
#error "Incorrect cl_khr_fp16 define"

View File

@ -28,7 +28,12 @@
#ifndef cl_clang_storage_class_specifiers
#error "Missing cl_clang_storage_class_specifiers define"
#endif
#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers: enable
#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable
#ifndef __cl_clang_function_pointers
#error "Missing __cl_clang_function_pointers define"
#endif
#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
#ifdef cl_khr_fp16
#error "Incorrect cl_khr_fp16 define"

View File

@ -1,19 +1,32 @@
// RUN: %clang_cc1 %s -triple spir-unknown-unknown -cl-std=clc++ -fsyntax-only -verify
// RUN: %clang_cc1 %s -triple spir-unknown-unknown -cl-std=clc++ -fsyntax-only -verify -DFUNCPTREXT
// Test that virtual functions and abstract classes are rejected.
#ifdef FUNCPTREXT
#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
//expected-no-diagnostics
#endif
// Test that virtual functions and abstract classes are rejected
// unless specific clang extension is used.
class virtual_functions {
virtual void bad1() {}
//expected-error@-1 {{virtual functions are not supported in C++ for OpenCL}}
#ifndef FUNCPTREXT
//expected-error@-2 {{virtual functions are not supported in C++ for OpenCL}}
#endif
virtual void bad2() = 0;
//expected-error@-1 {{virtual functions are not supported in C++ for OpenCL}}
//expected-error@-2 {{'bad2' is not virtual and cannot be declared pure}}
#ifndef FUNCPTREXT
//expected-error@-2 {{virtual functions are not supported in C++ for OpenCL}}
//expected-error@-3 {{'bad2' is not virtual and cannot be declared pure}}
#endif
};
template <typename T>
class X {
virtual T f();
//expected-error@-1 {{virtual functions are not supported in C++ for OpenCL}}
#ifndef FUNCPTREXT
//expected-error@-2 {{virtual functions are not supported in C++ for OpenCL}}
#endif
};
// Test that virtual base classes are allowed.

View File

@ -17,7 +17,12 @@
#ifndef cl_clang_storage_class_specifiers
#error "Missing cl_clang_storage_class_specifiers define"
#endif
#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers: enable
#pragma OPENCL EXTENSION cl_clang_storage_class_specifiers : enable
#ifndef __cl_clang_function_pointers
#error "Missing __cl_clang_function_pointers define"
#endif
#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
#ifndef cl_khr_fp16
#error "Missing cl_khr_fp16 define"

View File

@ -1,16 +1,26 @@
// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only -triple spir-unknown-unknown
// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only -triple spir-unknown-unknown -DFUNCPTREXT
#ifdef FUNCPTREXT
#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
#endif
// Variadic functions
void vararg_f(int, ...); // expected-error {{invalid prototype, variadic arguments are not allowed in OpenCL}}
void __vararg_f(int, ...);
typedef void (*vararg_fptr_t)(int, ...); // expected-error {{invalid prototype, variadic arguments are not allowed in OpenCL}}
// expected-error@-1{{pointers to functions are not allowed}}
#ifndef FUNCPTREXT
// expected-error@-2 {{pointers to functions are not allowed}}
#endif
int printf(__constant const char *st, ...); // expected-error {{invalid prototype, variadic arguments are not allowed in OpenCL}}
// Struct type with function pointer field
typedef struct s
{
void (*f)(struct s *self, int *i); // expected-error{{pointers to functions are not allowed}}
void (*f)(struct s *self, int *i);
#ifndef FUNCPTREXT
// expected-error@-2 {{pointers to functions are not allowed}}
#endif
} s_t;
//Function pointer
@ -22,7 +32,10 @@ void bar();
void bar()
{
// declaring a function pointer is an error
void (*fptr)(int); // expected-error{{pointers to functions are not allowed}}
void (*fptr)(int);
#ifndef FUNCPTREXT
// expected-error@-2 {{pointers to functions are not allowed}}
#endif
// taking the address of a function is an error
foo((void*)foo); // expected-error{{taking address of function is not allowed}}

View File

@ -1,6 +1,13 @@
//RUN: %clang_cc1 %s -triple spir -cl-std=clc++ -verify -fsyntax-only
//RUN: %clang_cc1 %s -triple spir -cl-std=clc++ -verify -fsyntax-only -DFUNCPTREXT
#ifdef FUNCPTREXT
#pragma OPENCL EXTENSION __cl_clang_function_pointers : enable
//expected-no-diagnostics
#endif
// Check that pointer to member functions are diagnosed
// unless specific clang extension is enabled.
struct C {
void f(int n);
};
@ -12,11 +19,25 @@ template <class T> struct remove_reference<T &> { typedef T type; };
template <typename T>
void templ_test() {
typename remove_reference<T>::type *ptr; //expected-error{{pointers to functions are not allowed}}
typename remove_reference<T>::type *ptr;
#ifndef FUNCPTREXT
//expected-error@-2{{pointers to functions are not allowed}}
#endif
}
void test() {
void (C::*p)(int); //expected-error{{pointers to functions are not allowed}}
p_t p1; //expected-error{{pointers to functions are not allowed}}
templ_test<int (&)()>(); //expected-note{{in instantiation of function template specialization}}
void (C::*p)(int);
#ifndef FUNCPTREXT
//expected-error@-2{{pointers to functions are not allowed}}
#endif
p_t p1;
#ifndef FUNCPTREXT
//expected-error@-2{{pointers to functions are not allowed}}
#endif
templ_test<int (&)()>();
#ifndef FUNCPTREXT
//expected-note@-2{{in instantiation of function template specialization}}
#endif
}