diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2374,7 +2374,8 @@ "IR_ForbiddenWeak", "IR_ARCForbiddenConversion", "IR_ARCInitReturnsUnrelated", - "IR_ARCFieldWithOwnership"], 1, /*fake*/ 1>]; + "IR_ARCFieldWithOwnership", + "IR_SYCLForbiddenType"], 1, /*fake*/ 1>]; let Documentation = [Undocumented]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9345,6 +9345,9 @@ "feature, not permitted in C++">; def err_type_unsupported : Error< "%0 is not supported on this target">; +def note_type_unavailable : Note< + "declaration is unavailable because it uses type %0 that is not supported on " + "this target">; def err_nsconsumed_attribute_mismatch : Error< "overriding method has mismatched ns_consumed attribute on its" " parameter">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -12308,6 +12308,40 @@ ConstructorDestructor, BuiltinFunction }; + /// Creates a DeviceDiagBuilder that emits the diagnostic if the current + /// context is "used as device code". + /// + /// - If CurLexicalContext is a kernel function or it is known that the + /// function will be emitted for the device, emits the diagnostics + /// immediately. + /// - If CurLexicalContext is a function and we are compiling + /// for the device, but we don't know that this function will be codegen'ed + /// for devive yet, creates a diagnostic which is emitted if and when we + /// realize that the function will be codegen'ed. + /// + /// Example usage: + /// + /// Diagnose __float128 type usage only from SYCL device code if the current + /// target doesn't support it + /// if (!S.Context.getTargetInfo().hasFloat128Type() && + /// S.getLangOpts().SYCLIsDevice) + /// SYCLDiagIfDeviceCode(Loc, diag::err_type_unsupported) << "__float128"; + DeviceDiagBuilder SYCLDiagIfDeviceCode(SourceLocation Loc, unsigned DiagID); + + /// Check whether we're allowed to call Callee from the current context. + /// + /// - If the call is never allowed in a semantically-correct program + /// emits an error and returns false. + /// + /// - If the call is allowed in semantically-correct programs, but only if + /// it's never codegen'ed, creates a deferred diagnostic to be emitted if + /// and when the caller is codegen'ed, and returns true. + /// + /// - Otherwise, returns true without emitting any diagnostics. + /// + /// Adds Callee to DeviceCallGraph if we don't know if its caller will be + /// codegen'ed yet. + bool checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee); }; /// RAII object that enters a new expression evaluation context. diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -59,6 +59,7 @@ SemaStmt.cpp SemaStmtAsm.cpp SemaStmtAttr.cpp + SemaSYCL.cpp SemaTemplate.cpp SemaTemplateDeduction.cpp SemaTemplateInstantiate.cpp diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -474,6 +474,16 @@ flagARCError(); diag_available_here = diag::note_arc_field_with_ownership; break; + + case UnavailableAttr::IR_SYCLForbiddenType: + diag_available_here = diag::note_type_unavailable; + if (S.getLangOpts().SYCLIsDevice) { + S.SYCLDiagIfDeviceCode(Loc, diag) << ReferringDecl; + S.SYCLDiagIfDeviceCode(NoteLocation, diag_available_here) + << cast(OffendingDecl)->getType(); + return; + } + 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 @@ -14394,7 +14394,7 @@ DiscardCleanupsInEvaluationContext(); } - if (LangOpts.OpenMP || LangOpts.CUDA) { + if (LangOpts.OpenMP || LangOpts.CUDA || LangOpts.SYCLIsDevice) { auto ES = getEmissionStatus(FD); if (ES == Sema::FunctionEmissionStatus::Emitted || ES == Sema::FunctionEmissionStatus::Unknown) @@ -18070,6 +18070,11 @@ Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD, bool Final) { + // Due to SYCL functions can be template we check if they have appropriate + // attribute prior to checking if it is a template + if (LangOpts.SYCLIsDevice && FD->hasAttr()) + return FunctionEmissionStatus::Emitted; + // Templates are emitted when they're instantiated. if (FD->isDependentContext()) return FunctionEmissionStatus::TemplateDiscarded; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7641,6 +7641,13 @@ static bool isForbiddenTypeAllowed(Sema &S, Decl *D, const DelayedDiagnostic &diag, UnavailableAttr::ImplicitReason &reason) { + // SYCL: delay the diagnostic until we know that the function will be actually + // emitted + if (S.getLangOpts().SYCLIsDevice) { + reason = UnavailableAttr::IR_SYCLForbiddenType; + return true; + } + // Private ivars are always okay. Unfortunately, people don't // always properly make their ivars private, even in system headers. // Plus we need to make fields okay, too. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -14884,6 +14884,9 @@ MarkFunctionReferenced(ConstructLoc, Constructor); if (getLangOpts().CUDA && !CheckCUDACall(ConstructLoc, Constructor)) return ExprError(); + if (getLangOpts().SYCLIsDevice && + !checkSYCLDeviceFunction(ConstructLoc, Constructor)) + return ExprError(); return CheckForImmediateInvocation( CXXConstructExpr::Create( diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -292,6 +292,9 @@ if (getLangOpts().CUDA && !CheckCUDACall(Loc, FD)) return true; + + if (getLangOpts().SYCLIsDevice && !checkSYCLDeviceFunction(Loc, FD)) + return true; } if (auto *MD = dyn_cast(D)) { @@ -16048,6 +16051,9 @@ if (getLangOpts().CUDA) CheckCUDACall(Loc, Func); + if (getLangOpts().SYCLIsDevice) + checkSYCLDeviceFunction(Loc, Func); + // If we need a definition, try to create one. if (NeedDefinition && !Func->getBody()) { runWithSufficientStackSpace(Loc, [&] { diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -0,0 +1,51 @@ +//===- SemaSYCL.cpp - Semantic Analysis for SYCL constructs ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// This implements Semantic Analysis for SYCL constructs. +//===----------------------------------------------------------------------===// + +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaDiagnostic.h" + +using namespace clang; + +// ----------------------------------------------------------------------------- +// SYCL device specific diagnostics implementation +// ----------------------------------------------------------------------------- + +Sema::DeviceDiagBuilder Sema::SYCLDiagIfDeviceCode(SourceLocation Loc, + unsigned DiagID) { + assert(getLangOpts().SYCLIsDevice && + "Should only be called during SYCL compilation"); + FunctionDecl *FD = dyn_cast(getCurLexicalContext()); + DeviceDiagBuilder::Kind DiagKind = [this, FD] { + if (!FD) + return DeviceDiagBuilder::K_Nop; + if (getEmissionStatus(FD) == Sema::FunctionEmissionStatus::Emitted) + return DeviceDiagBuilder::K_ImmediateWithCallStack; + return DeviceDiagBuilder::K_Deferred; + }(); + return DeviceDiagBuilder(DiagKind, Loc, DiagID, FD, *this); +} + +bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) { + assert(getLangOpts().SYCLIsDevice && + "Should only be called during SYCL compilation"); + assert(Callee && "Callee may not be null."); + + // Errors in unevaluated context don't need to be generated, + // so we can safely skip them. + if (isUnevaluatedContext() || isConstantEvaluated()) + return true; + + FunctionDecl *Caller = dyn_cast(getCurLexicalContext()); + + DeviceDiagBuilder::Kind DiagKind = DeviceDiagBuilder::K_Nop; + + return DiagKind != DeviceDiagBuilder::K_Immediate && + DiagKind != DeviceDiagBuilder::K_ImmediateWithCallStack; +} 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 @@ -1517,10 +1517,21 @@ break; case DeclSpec::TST_float128: if (!S.Context.getTargetInfo().hasFloat128Type() && + !S.getLangOpts().SYCLIsDevice && !(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice)) S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported) << "__float128"; Result = Context.Float128Ty; + if (!S.Context.getTargetInfo().hasFloat128Type() && + S.getLangOpts().SYCLIsDevice && + S.DelayedDiagnostics.shouldDelayDiagnostics()) { + S.DelayedDiagnostics.add(sema::DelayedDiagnostic::makeForbiddenType( + DS.getTypeSpecTypeLoc(), diag::err_type_unsupported, Result, + /*ignored*/ 0)); + S.SYCLDiagIfDeviceCode(DS.getTypeSpecTypeLoc(), + diag::err_type_unsupported) + << Result; + } break; case DeclSpec::TST_bool: Result = Context.BoolTy; break; // _Bool or bool break; diff --git a/clang/test/SemaSYCL/float128.cpp b/clang/test/SemaSYCL/float128.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaSYCL/float128.cpp @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -triple spir64 -fsycl -fsycl-is-device -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsycl -fsycl-is-device -fsyntax-only %s + +template +class Z { +public: + // TODO: If T is __float128 This might be a problem + T field; + // expected-note@+1 2{{declaration is unavailable because it uses type '__float128' that is not supported on this target}} + __float128 field1; +}; + +void host_ok(void) { + __float128 A; + int B = sizeof(__float128); + Z<__float128> C; + C.field1 = A; +} + +void usage() { + // expected-error@+2 {{'__float128' is not supported on this target}} + // expected-note@+1 4{{declaration is unavailable because it uses type '__float128' that is not supported on this target}} + __float128 A; + Z<__float128> C; + // expected-error@+2 {{'A' is unavailable}} + // expected-error@+1 {{'field1' is unavailable}} + C.field1 = A; + // expected-error@+1 {{'A' is unavailable}} + decltype(A) D; + + // expected-error@+1 {{'A' is unavailable}} + auto foo1 = [=]() { + // expected-error@+1 {{'__float128' is not supported on this target}} + __float128 AA; + // expected-error@+1 {{'A' is unavailable}} + auto BB = A; + BB += 1; + }; + + // expected-note@+1 {{called by 'usage'}} + foo1(); +} + +template +void foo2(){}; + +// expected-note@+1 {{declaration is unavailable because it uses type '__float128 (__float128)' that is not supported on this target}} +__float128 foo(__float128 P) { return P; } + +template +__attribute__((sycl_kernel)) void kernel(Func kernelFunc) { + // expected-note@+1 5{{called by 'kernel}} + kernelFunc(); + // expected-error@+1 {{'__float128' is not supported on this target}} + __float128 A; +} + +int main() { + // expected-note@+1 2{{declaration is unavailable because it uses type '__float128' that is not supported on this target}} + __float128 CapturedToDevice = 1; + host_ok(); + kernel([=]() { + // expected-error@+1 {{'CapturedToDevice' is unavailable}} + decltype(CapturedToDevice) D; + // expected-error@+1 {{'CapturedToDevice' is unavailable}} + auto C = CapturedToDevice; + // expected-error@+1 {{'__float128' is not supported on this target}} + __float128 BBBB; + Z<__float128> S; + // expected-error@+1 {{'field1' is unavailable}} + S.field1 += 1; + S.field = 1; + }); + + kernel([=]() { + // expected-note@+1 2{{called by 'operator()'}} + usage(); + // expected-error@+2 {{'__float128' is not supported on this target}} + // expected-note@+1 {{declaration is unavailable because it uses type '__float128' that is not supported on this target}} + __float128 BBBB; + // expected-error@+2 {{'BBBB' is unavailable}} + // expected-error@+1 {{'foo' is unavailable}} + auto A = foo(BBBB); + }); + + kernel([=]() { + Z<__float128> S; + foo2<__float128>(); + // TODO: this shouldn't be diagnosed + // expected-error@+1 {{'__float128' is not supported on this target}} + int E = sizeof(__float128); + }); + + return 0; +}