Skip to content

Commit cf6cc66

Browse files
Fznamznonbader
authored andcommitted
[OpenMP][SYCL] Improve diagnosing of unsupported types usage
Summary: Diagnostic is emitted if some declaration of unsupported type declaration is used inside device code. Memcpy operations for structs containing member with unsupported type are allowed. Fixed crash on attempt to emit diagnostic outside of the functions. The approach is generalized between SYCL and OpenMP. CUDA/OMP deferred diagnostic interface is going to be used for SYCL device. Reviewers: rsmith, rjmccall, ABataev, erichkeane, bader, jdoerfert, aaron.ballman Reviewed By: jdoerfert Subscribers: guansong, sstefan1, yaxunl, mgorny, bader, ebevhan, Anastasia, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D74387
1 parent 0e265e3 commit cf6cc66

15 files changed

+347
-70
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10204,8 +10204,8 @@ def err_omp_invariant_or_linear_dependency : Error<
1020410204
"expected loop invariant expression or '<invariant1> * %0 + <invariant2>' kind of expression">;
1020510205
def err_omp_wrong_dependency_iterator_type : Error<
1020610206
"expected an integer or a pointer type of the outer loop counter '%0' for non-rectangular nests">;
10207-
def err_omp_unsupported_type : Error <
10208-
"host requires %0 bit size %1 type support, but device '%2' does not support it">;
10207+
def err_device_unsupported_type : Error <
10208+
"%0 requires %1 bit size %2 type support, but device '%3' does not support it">;
1020910209
def err_omp_lambda_capture_in_declare_target_not_to : Error<
1021010210
"variable captured in declare target region must appear in a to clause">;
1021110211
def err_omp_device_type_mismatch : Error<

clang/include/clang/Sema/Sema.h

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9868,10 +9868,6 @@ class Sema final {
98689868
/// Pop OpenMP function region for non-capturing function.
98699869
void popOpenMPFunctionRegion(const sema::FunctionScopeInfo *OldFSI);
98709870

9871-
/// Check if the expression is allowed to be used in expressions for the
9872-
/// OpenMP devices.
9873-
void checkOpenMPDeviceExpr(const Expr *E);
9874-
98759871
/// Checks if a type or a declaration is disabled due to the owning extension
98769872
/// being disabled, and emits diagnostic messages if it is disabled.
98779873
/// \param D type or declaration to be checked.
@@ -11654,6 +11650,10 @@ class Sema final {
1165411650

1165511651
DeviceDiagBuilder targetDiag(SourceLocation Loc, unsigned DiagID);
1165611652

11653+
/// Check if the expression is allowed to be used in expressions for the
11654+
/// offloading devices.
11655+
void checkDeviceDecl(const ValueDecl *D, SourceLocation Loc);
11656+
1165711657
enum CUDAFunctionTarget {
1165811658
CFT_Device,
1165911659
CFT_Global,
@@ -12396,6 +12396,40 @@ class Sema final {
1239612396
ConstructorDestructor,
1239712397
BuiltinFunction
1239812398
};
12399+
/// Creates a DeviceDiagBuilder that emits the diagnostic if the current
12400+
/// context is "used as device code".
12401+
///
12402+
/// - If CurLexicalContext is a kernel function or it is known that the
12403+
/// function will be emitted for the device, emits the diagnostics
12404+
/// immediately.
12405+
/// - If CurLexicalContext is a function and we are compiling
12406+
/// for the device, but we don't know that this function will be codegen'ed
12407+
/// for devive yet, creates a diagnostic which is emitted if and when we
12408+
/// realize that the function will be codegen'ed.
12409+
///
12410+
/// Example usage:
12411+
///
12412+
/// Diagnose __float128 type usage only from SYCL device code if the current
12413+
/// target doesn't support it
12414+
/// if (!S.Context.getTargetInfo().hasFloat128Type() &&
12415+
/// S.getLangOpts().SYCLIsDevice)
12416+
/// SYCLDiagIfDeviceCode(Loc, diag::err_type_unsupported) << "__float128";
12417+
DeviceDiagBuilder SYCLDiagIfDeviceCode(SourceLocation Loc, unsigned DiagID);
12418+
12419+
/// Check whether we're allowed to call Callee from the current context.
12420+
///
12421+
/// - If the call is never allowed in a semantically-correct program
12422+
/// emits an error and returns false.
12423+
///
12424+
/// - If the call is allowed in semantically-correct programs, but only if
12425+
/// it's never codegen'ed, creates a deferred diagnostic to be emitted if
12426+
/// and when the caller is codegen'ed, and returns true.
12427+
///
12428+
/// - Otherwise, returns true without emitting any diagnostics.
12429+
///
12430+
/// Adds Callee to DeviceCallGraph if we don't know if its caller will be
12431+
/// codegen'ed yet.
12432+
bool checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee);
1239912433
};
1240012434

1240112435
/// RAII object that enters a new expression evaluation context.

clang/lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ add_clang_library(clangSema
6161
SemaStmt.cpp
6262
SemaStmtAsm.cpp
6363
SemaStmtAttr.cpp
64+
SemaSYCL.cpp
6465
SemaTemplate.cpp
6566
SemaTemplateDeduction.cpp
6667
SemaTemplateInstantiate.cpp

clang/lib/Sema/Sema.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,10 +1698,56 @@ Sema::DeviceDiagBuilder Sema::targetDiag(SourceLocation Loc, unsigned DiagID) {
16981698
if (getLangOpts().CUDA)
16991699
return getLangOpts().CUDAIsDevice ? CUDADiagIfDeviceCode(Loc, DiagID)
17001700
: CUDADiagIfHostCode(Loc, DiagID);
1701+
1702+
if (getLangOpts().SYCLIsDevice)
1703+
return SYCLDiagIfDeviceCode(Loc, DiagID);
1704+
17011705
return DeviceDiagBuilder(DeviceDiagBuilder::K_Immediate, Loc, DiagID,
17021706
getCurFunctionDecl(), *this);
17031707
}
17041708

1709+
void Sema::checkDeviceDecl(const ValueDecl *D, SourceLocation Loc) {
1710+
if (isUnevaluatedContext())
1711+
return;
1712+
1713+
Decl *C = cast<Decl>(getCurLexicalContext());
1714+
1715+
// Memcpy operations for structs containing a member with unsupported type
1716+
// are ok, though.
1717+
if (const auto *MD = dyn_cast<CXXMethodDecl>(C)) {
1718+
if ((MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()) &&
1719+
MD->isTrivial())
1720+
return;
1721+
1722+
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(MD))
1723+
if (Ctor->isCopyOrMoveConstructor() && Ctor->isTrivial())
1724+
return;
1725+
}
1726+
1727+
auto CheckType = [&](QualType Ty) {
1728+
if ((Ty->isFloat16Type() && !Context.getTargetInfo().hasFloat16Type()) ||
1729+
((Ty->isFloat128Type() ||
1730+
(Ty->isRealFloatingType() && Context.getTypeSize(Ty) == 128)) &&
1731+
!Context.getTargetInfo().hasFloat128Type()) ||
1732+
(Ty->isIntegerType() && Context.getTypeSize(Ty) == 128 &&
1733+
!Context.getTargetInfo().hasInt128Type())) {
1734+
targetDiag(Loc, diag::err_device_unsupported_type)
1735+
<< D << static_cast<unsigned>(Context.getTypeSize(Ty)) << Ty
1736+
<< Context.getTargetInfo().getTriple().str();
1737+
targetDiag(D->getLocation(), diag::note_defined_here) << D;
1738+
}
1739+
};
1740+
1741+
QualType Ty = D->getType();
1742+
CheckType(Ty);
1743+
1744+
if (const auto *FPTy = dyn_cast<FunctionProtoType>(Ty)) {
1745+
for (const auto &ParamTy : FPTy->param_types())
1746+
CheckType(ParamTy);
1747+
CheckType(FPTy->getReturnType());
1748+
}
1749+
}
1750+
17051751
/// Looks through the macro-expansion chain for the given
17061752
/// location, looking for a macro expansion with the given name.
17071753
/// If one is found, returns true and sets the location to that

clang/lib/Sema/SemaDecl.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14439,7 +14439,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1443914439
DiscardCleanupsInEvaluationContext();
1444014440
}
1444114441

14442-
if (LangOpts.OpenMP || LangOpts.CUDA) {
14442+
if (LangOpts.OpenMP || LangOpts.CUDA || LangOpts.SYCLIsDevice) {
1444314443
auto ES = getEmissionStatus(FD);
1444414444
if (ES == Sema::FunctionEmissionStatus::Emitted ||
1444514445
ES == Sema::FunctionEmissionStatus::Unknown)
@@ -18119,6 +18119,11 @@ Decl *Sema::getObjCDeclContext() const {
1811918119

1812018120
Sema::FunctionEmissionStatus Sema::getEmissionStatus(FunctionDecl *FD,
1812118121
bool Final) {
18122+
// SYCL functions can be template, so we check if they have appropriate
18123+
// attribute prior to checking if it is a template.
18124+
if (LangOpts.SYCLIsDevice && FD->hasAttr<SYCLKernelAttr>())
18125+
return FunctionEmissionStatus::Emitted;
18126+
1812218127
// Templates are emitted when they're instantiated.
1812318128
if (FD->isDependentContext())
1812418129
return FunctionEmissionStatus::TemplateDiscarded;

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14915,6 +14915,9 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
1491514915
MarkFunctionReferenced(ConstructLoc, Constructor);
1491614916
if (getLangOpts().CUDA && !CheckCUDACall(ConstructLoc, Constructor))
1491714917
return ExprError();
14918+
if (getLangOpts().SYCLIsDevice &&
14919+
!checkSYCLDeviceFunction(ConstructLoc, Constructor))
14920+
return ExprError();
1491814921

1491914922
return CheckForImmediateInvocation(
1492014923
CXXConstructExpr::Create(

clang/lib/Sema/SemaExpr.cpp

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
293293

294294
if (getLangOpts().CUDA && !CheckCUDACall(Loc, FD))
295295
return true;
296+
297+
if (getLangOpts().SYCLIsDevice && !checkSYCLDeviceFunction(Loc, FD))
298+
return true;
296299
}
297300

298301
if (auto *MD = dyn_cast<CXXMethodDecl>(D)) {
@@ -352,6 +355,10 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
352355

353356
diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc);
354357

358+
if (LangOpts.SYCLIsDevice || (LangOpts.OpenMP && LangOpts.OpenMPIsDevice))
359+
if (const auto *VD = dyn_cast<ValueDecl>(D))
360+
checkDeviceDecl(VD, Loc);
361+
355362
if (isa<ParmVarDecl>(D) && isa<RequiresExprBodyDecl>(D->getDeclContext()) &&
356363
!isUnevaluatedContext()) {
357364
// C++ [expr.prim.req.nested] p3
@@ -13511,14 +13518,6 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
1351113518
}
1351213519
}
1351313520

13514-
// Diagnose operations on the unsupported types for OpenMP device compilation.
13515-
if (getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice) {
13516-
if (Opc != BO_Assign && Opc != BO_Comma) {
13517-
checkOpenMPDeviceExpr(LHSExpr);
13518-
checkOpenMPDeviceExpr(RHSExpr);
13519-
}
13520-
}
13521-
1352213521
switch (Opc) {
1352313522
case BO_Assign:
1352413523
ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType());
@@ -14131,12 +14130,6 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
1413114130
<< Input.get()->getSourceRange());
1413214131
}
1413314132
}
14134-
// Diagnose operations on the unsupported types for OpenMP device compilation.
14135-
if (getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice) {
14136-
if (UnaryOperator::isIncrementDecrementOp(Opc) ||
14137-
UnaryOperator::isArithmeticOp(Opc))
14138-
checkOpenMPDeviceExpr(InputExpr);
14139-
}
1414014133

1414114134
switch (Opc) {
1414214135
case UO_PreInc:
@@ -16395,6 +16388,9 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
1639516388
if (getLangOpts().CUDA)
1639616389
CheckCUDACall(Loc, Func);
1639716390

16391+
if (getLangOpts().SYCLIsDevice)
16392+
checkSYCLDeviceFunction(Loc, Func);
16393+
1639816394
// If we need a definition, try to create one.
1639916395
if (NeedDefinition && !Func->getBody()) {
1640016396
runWithSufficientStackSpace(Loc, [&] {

clang/lib/Sema/SemaOpenMP.cpp

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,23 +1832,28 @@ Sema::DeviceDiagBuilder Sema::diagIfOpenMPDeviceCode(SourceLocation Loc,
18321832
unsigned DiagID) {
18331833
assert(LangOpts.OpenMP && LangOpts.OpenMPIsDevice &&
18341834
"Expected OpenMP device compilation.");
1835-
FunctionEmissionStatus FES = getEmissionStatus(getCurFunctionDecl());
1835+
1836+
FunctionDecl *FD = getCurFunctionDecl();
18361837
DeviceDiagBuilder::Kind Kind = DeviceDiagBuilder::K_Nop;
1837-
switch (FES) {
1838-
case FunctionEmissionStatus::Emitted:
1839-
Kind = DeviceDiagBuilder::K_Immediate;
1840-
break;
1841-
case FunctionEmissionStatus::Unknown:
1842-
Kind = isOpenMPDeviceDelayedContext(*this) ? DeviceDiagBuilder::K_Deferred
1843-
: DeviceDiagBuilder::K_Immediate;
1844-
break;
1845-
case FunctionEmissionStatus::TemplateDiscarded:
1846-
case FunctionEmissionStatus::OMPDiscarded:
1847-
Kind = DeviceDiagBuilder::K_Nop;
1848-
break;
1849-
case FunctionEmissionStatus::CUDADiscarded:
1850-
llvm_unreachable("CUDADiscarded unexpected in OpenMP device compilation");
1851-
break;
1838+
if (FD) {
1839+
FunctionEmissionStatus FES = getEmissionStatus(FD);
1840+
switch (FES) {
1841+
case FunctionEmissionStatus::Emitted:
1842+
Kind = DeviceDiagBuilder::K_Immediate;
1843+
break;
1844+
case FunctionEmissionStatus::Unknown:
1845+
Kind = isOpenMPDeviceDelayedContext(*this)
1846+
? DeviceDiagBuilder::K_Deferred
1847+
: DeviceDiagBuilder::K_Immediate;
1848+
break;
1849+
case FunctionEmissionStatus::TemplateDiscarded:
1850+
case FunctionEmissionStatus::OMPDiscarded:
1851+
Kind = DeviceDiagBuilder::K_Nop;
1852+
break;
1853+
case FunctionEmissionStatus::CUDADiscarded:
1854+
llvm_unreachable("CUDADiscarded unexpected in OpenMP device compilation");
1855+
break;
1856+
}
18521857
}
18531858

18541859
return DeviceDiagBuilder(Kind, Loc, DiagID, getCurFunctionDecl(), *this);
@@ -1877,21 +1882,6 @@ Sema::DeviceDiagBuilder Sema::diagIfOpenMPHostCode(SourceLocation Loc,
18771882
return DeviceDiagBuilder(Kind, Loc, DiagID, getCurFunctionDecl(), *this);
18781883
}
18791884

1880-
void Sema::checkOpenMPDeviceExpr(const Expr *E) {
1881-
assert(getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice &&
1882-
"OpenMP device compilation mode is expected.");
1883-
QualType Ty = E->getType();
1884-
if ((Ty->isFloat16Type() && !Context.getTargetInfo().hasFloat16Type()) ||
1885-
((Ty->isFloat128Type() ||
1886-
(Ty->isRealFloatingType() && Context.getTypeSize(Ty) == 128)) &&
1887-
!Context.getTargetInfo().hasFloat128Type()) ||
1888-
(Ty->isIntegerType() && Context.getTypeSize(Ty) == 128 &&
1889-
!Context.getTargetInfo().hasInt128Type()))
1890-
targetDiag(E->getExprLoc(), diag::err_omp_unsupported_type)
1891-
<< static_cast<unsigned>(Context.getTypeSize(Ty)) << Ty
1892-
<< Context.getTargetInfo().getTriple().str() << E->getSourceRange();
1893-
}
1894-
18951885
static OpenMPDefaultmapClauseKind
18961886
getVariableCategoryFromDecl(const LangOptions &LO, const ValueDecl *VD) {
18971887
if (LO.OpenMP <= 45) {

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===- SemaSYCL.cpp - Semantic Analysis for SYCL constructs ---------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// This implements Semantic Analysis for SYCL constructs.
9+
//===----------------------------------------------------------------------===//
10+
11+
#include "clang/Sema/Sema.h"
12+
#include "clang/Sema/SemaDiagnostic.h"
13+
14+
using namespace clang;
15+
16+
// -----------------------------------------------------------------------------
17+
// SYCL device specific diagnostics implementation
18+
// -----------------------------------------------------------------------------
19+
20+
Sema::DeviceDiagBuilder Sema::SYCLDiagIfDeviceCode(SourceLocation Loc,
21+
unsigned DiagID) {
22+
assert(getLangOpts().SYCLIsDevice &&
23+
"Should only be called during SYCL compilation");
24+
FunctionDecl *FD = dyn_cast<FunctionDecl>(getCurLexicalContext());
25+
DeviceDiagBuilder::Kind DiagKind = [this, FD] {
26+
if (!FD)
27+
return DeviceDiagBuilder::K_Nop;
28+
if (getEmissionStatus(FD) == Sema::FunctionEmissionStatus::Emitted)
29+
return DeviceDiagBuilder::K_ImmediateWithCallStack;
30+
return DeviceDiagBuilder::K_Deferred;
31+
}();
32+
return DeviceDiagBuilder(DiagKind, Loc, DiagID, FD, *this);
33+
}
34+
35+
bool Sema::checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) {
36+
assert(getLangOpts().SYCLIsDevice &&
37+
"Should only be called during SYCL compilation");
38+
assert(Callee && "Callee may not be null.");
39+
40+
// Errors in unevaluated context don't need to be generated,
41+
// so we can safely skip them.
42+
if (isUnevaluatedContext() || isConstantEvaluated())
43+
return true;
44+
45+
DeviceDiagBuilder::Kind DiagKind = DeviceDiagBuilder::K_Nop;
46+
47+
return DiagKind != DeviceDiagBuilder::K_Immediate &&
48+
DiagKind != DeviceDiagBuilder::K_ImmediateWithCallStack;
49+
}

clang/lib/Sema/SemaType.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
15301530
break;
15311531
case DeclSpec::TST_float128:
15321532
if (!S.Context.getTargetInfo().hasFloat128Type() &&
1533+
!S.getLangOpts().SYCLIsDevice &&
15331534
!(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice))
15341535
S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported)
15351536
<< "__float128";

clang/test/Headers/nvptx_device_math_sin.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#include <math.h>
99

10-
double math(float f, double d, long double ld) {
10+
double math(float f, double d) {
1111
double r = 0;
1212
// SLOW: call float @__nv_sinf(float
1313
// FAST: call fast float @__nv_fast_sinf(float
@@ -20,8 +20,8 @@ double math(float f, double d, long double ld) {
2020

2121
long double foo(float f, double d, long double ld) {
2222
double r = ld;
23-
r += math(f, d, ld);
23+
r += math(f, d);
2424
#pragma omp target map(r)
25-
{ r += math(f, d, ld); }
25+
{ r += math(f, d); }
2626
return r;
2727
}

clang/test/Headers/nvptx_device_math_sin.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#include <cmath>
99

10-
double math(float f, double d, long double ld) {
10+
double math(float f, double d) {
1111
double r = 0;
1212
// SLOW: call float @__nv_sinf(float
1313
// FAST: call fast float @__nv_fast_sinf(float
@@ -20,8 +20,8 @@ double math(float f, double d, long double ld) {
2020

2121
long double foo(float f, double d, long double ld) {
2222
double r = ld;
23-
r += math(f, d, ld);
23+
r += math(f, d);
2424
#pragma omp target map(r)
25-
{ r += math(f, d, ld); }
25+
{ r += math(f, d); }
2626
return r;
2727
}

0 commit comments

Comments
 (0)