diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1722,6 +1722,58 @@ 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 +`_. + +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 ================= diff --git a/clang/include/clang/Basic/OpenCLExtensions.def b/clang/include/clang/Basic/OpenCLExtensions.def --- a/clang/include/clang/Basic/OpenCLExtensions.def +++ b/clang/include/clang/Basic/OpenCLExtensions.def @@ -69,6 +69,7 @@ // 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) diff --git a/clang/lib/Basic/Targets/AMDGPU.h b/clang/lib/Basic/Targets/AMDGPU.h --- a/clang/lib/Basic/Targets/AMDGPU.h +++ b/clang/lib/Basic/Targets/AMDGPU.h @@ -285,6 +285,7 @@ void setSupportedOpenCLOpts() override { auto &Opts = getSupportedOpenCLOpts(); Opts.support("cl_clang_storage_class_specifiers"); + Opts.support("__cl_clang_function_pointers"); bool IsAMDGCN = isAMDGCN(getTriple()); diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -128,6 +128,7 @@ 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"); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3630,12 +3630,13 @@ 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; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6748,14 +6748,16 @@ } // 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")) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2089,7 +2089,8 @@ 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(); } diff --git a/clang/test/Misc/amdgcn.languageOptsOpenCL.cl b/clang/test/Misc/amdgcn.languageOptsOpenCL.cl --- a/clang/test/Misc/amdgcn.languageOptsOpenCL.cl +++ b/clang/test/Misc/amdgcn.languageOptsOpenCL.cl @@ -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" diff --git a/clang/test/Misc/nvptx.languageOptsOpenCL.cl b/clang/test/Misc/nvptx.languageOptsOpenCL.cl --- a/clang/test/Misc/nvptx.languageOptsOpenCL.cl +++ b/clang/test/Misc/nvptx.languageOptsOpenCL.cl @@ -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" diff --git a/clang/test/Misc/r600.languageOptsOpenCL.cl b/clang/test/Misc/r600.languageOptsOpenCL.cl --- a/clang/test/Misc/r600.languageOptsOpenCL.cl +++ b/clang/test/Misc/r600.languageOptsOpenCL.cl @@ -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" diff --git a/clang/test/Parser/opencl-cxx-virtual.cl b/clang/test/Parser/opencl-cxx-virtual.cl --- a/clang/test/Parser/opencl-cxx-virtual.cl +++ b/clang/test/Parser/opencl-cxx-virtual.cl @@ -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 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. diff --git a/clang/test/SemaOpenCL/extension-version.cl b/clang/test/SemaOpenCL/extension-version.cl --- a/clang/test/SemaOpenCL/extension-version.cl +++ b/clang/test/SemaOpenCL/extension-version.cl @@ -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" diff --git a/clang/test/SemaOpenCL/func.cl b/clang/test/SemaOpenCL/func.cl --- a/clang/test/SemaOpenCL/func.cl +++ b/clang/test/SemaOpenCL/func.cl @@ -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() { // 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}} diff --git a/clang/test/SemaOpenCLCXX/members.cl b/clang/test/SemaOpenCLCXX/members.cl --- a/clang/test/SemaOpenCLCXX/members.cl +++ b/clang/test/SemaOpenCLCXX/members.cl @@ -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 void templ_test() { - typename remove_reference::type *ptr; //expected-error{{pointers to functions are not allowed}} + typename remove_reference::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(); //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(); +#ifndef FUNCPTREXT +//expected-note@-2{{in instantiation of function template specialization}} +#endif }