]> granicus.if.org Git - clang/commitdiff
[OPENMP]Delay emission of the error for unsupported types.
authorAlexey Bataev <a.bataev@hotmail.com>
Wed, 27 Feb 2019 20:29:45 +0000 (20:29 +0000)
committerAlexey Bataev <a.bataev@hotmail.com>
Wed, 27 Feb 2019 20:29:45 +0000 (20:29 +0000)
If the type is unsupported on the device side, it still must be emitted,
but we should emit errors for operations with such types.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@355027 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Sema/Sema.h
lib/AST/ASTContext.cpp
lib/CodeGen/TargetInfo.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOpenMP.cpp
lib/Sema/SemaType.cpp
test/OpenMP/nvptx_unsupported_type_codegen.cpp [new file with mode: 0644]
test/OpenMP/nvptx_unsupported_type_messages.cpp [new file with mode: 0644]

index 333f41dea5b0823b5b1fae1b111c00823529a07e..3408c9462e568a164aa342ecd780d15b6d33470b 100644 (file)
@@ -8805,6 +8805,10 @@ private:
   /// Check whether we're allowed to call Callee from the current function.
   void checkOpenMPDeviceFunction(SourceLocation Loc, FunctionDecl *Callee);
 
+  /// Check if the expression is allowed to be used in expressions for the
+  /// OpenMP devices.
+  void checkOpenMPDeviceExpr(const Expr *E);
+
   /// Checks if a type or a declaration is disabled due to the owning extension
   /// being disabled, and emits diagnostic messages if it is disabled.
   /// \param D type or declaration to be checked.
index f968c6260e8e96d7d152f43df682cb40d025c9a3..aa4bd4139c0e754d3620e50bbeca0d6c9943e482 100644 (file)
@@ -1902,8 +1902,16 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
       break;
     case BuiltinType::Float16:
     case BuiltinType::Half:
-      Width = Target->getHalfWidth();
-      Align = Target->getHalfAlign();
+      if (Target->hasFloat16Type() || !getLangOpts().OpenMP ||
+          !getLangOpts().OpenMPIsDevice) {
+        Width = Target->getHalfWidth();
+        Align = Target->getHalfAlign();
+      } else {
+        assert(getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice &&
+               "Expected OpenMP device compilation.");
+        Width = AuxTarget->getHalfWidth();
+        Align = AuxTarget->getHalfAlign();
+      }
       break;
     case BuiltinType::Float:
       Width = Target->getFloatWidth();
@@ -1918,8 +1926,16 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
       Align = Target->getLongDoubleAlign();
       break;
     case BuiltinType::Float128:
-      Width = Target->getFloat128Width();
-      Align = Target->getFloat128Align();
+      if (Target->hasFloat128Type() || !getLangOpts().OpenMP ||
+          !getLangOpts().OpenMPIsDevice) {
+        Width = Target->getFloat128Width();
+        Align = Target->getFloat128Align();
+      } else {
+        assert(getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice &&
+               "Expected OpenMP device compilation.");
+        Width = AuxTarget->getFloat128Width();
+        Align = AuxTarget->getFloat128Align();
+      }
       break;
     case BuiltinType::NullPtr:
       Width = Target->getPointerWidth(0); // C++ 3.9.1p11: sizeof(nullptr_t)
index a130dd3cfb4d0cd1bfe0147d6a51f915204dc1ce..ee8c88953570edf4cd66330b5e1c1debd685f368 100644 (file)
@@ -6268,10 +6268,56 @@ private:
   static void addNVVMMetadata(llvm::Function *F, StringRef Name, int Operand);
 };
 
+/// Checks if the type is unsupported directly by the current target.
+static bool isUnsupportedType(ASTContext &Context, QualType T) {
+  if (!Context.getTargetInfo().hasFloat16Type() && T->isFloat16Type())
+    return true;
+  if (!Context.getTargetInfo().hasFloat128Type() && T->isFloat128Type())
+    return true;
+  if (!Context.getTargetInfo().hasInt128Type() && T->isIntegerType() &&
+      Context.getTypeSize(T) > 64)
+    return true;
+  if (const auto *AT = T->getAsArrayTypeUnsafe())
+    return isUnsupportedType(Context, AT->getElementType());
+  const auto *RT = T->getAs<RecordType>();
+  if (!RT)
+    return false;
+  const RecordDecl *RD = RT->getDecl();
+
+  // If this is a C++ record, check the bases first.
+  if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+    for (const CXXBaseSpecifier &I : CXXRD->bases())
+      if (isUnsupportedType(Context, I.getType()))
+        return true;
+
+  for (const FieldDecl *I : RD->fields())
+    if (isUnsupportedType(Context, I->getType()))
+      return true;
+  return false;
+}
+
+/// Coerce the given type into an array with maximum allowed size of elements.
+static ABIArgInfo coerceToIntArrayWithLimit(QualType Ty, ASTContext &Context,
+                                            llvm::LLVMContext &LLVMContext,
+                                            unsigned MaxSize) {
+  // Alignment and Size are measured in bits.
+  const uint64_t Size = Context.getTypeSize(Ty);
+  const uint64_t Alignment = Context.getTypeAlign(Ty);
+  const unsigned Div = std::min<unsigned>(MaxSize, Alignment);
+  llvm::Type *IntType = llvm::Type::getIntNTy(LLVMContext, Div);
+  const uint64_t NumElements = (Size + Div - 1) / Div;
+  return ABIArgInfo::getDirect(llvm::ArrayType::get(IntType, NumElements));
+}
+
 ABIArgInfo NVPTXABIInfo::classifyReturnType(QualType RetTy) const {
   if (RetTy->isVoidType())
     return ABIArgInfo::getIgnore();
 
+  if (getContext().getLangOpts().OpenMP &&
+      getContext().getLangOpts().OpenMPIsDevice &&
+      isUnsupportedType(getContext(), RetTy))
+    return coerceToIntArrayWithLimit(RetTy, getContext(), getVMContext(), 64);
+
   // note: this is different from default ABI
   if (!RetTy->isScalarType())
     return ABIArgInfo::getDirect();
index 266120dfdc763b9597d365715d87ce5739cff487..157ef0b94261c5b564c0ba2c066220fb94743848 100644 (file)
@@ -12432,6 +12432,14 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
     }
   }
 
+  // Diagnose operations on the unsupported types for OpenMP device compilation.
+  if (getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice) {
+    if (Opc != BO_Assign && Opc != BO_Comma) {
+      checkOpenMPDeviceExpr(LHSExpr);
+      checkOpenMPDeviceExpr(RHSExpr);
+    }
+  }
+
   switch (Opc) {
   case BO_Assign:
     ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, QualType());
@@ -13027,6 +13035,13 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
                        << Input.get()->getSourceRange());
     }
   }
+  // Diagnose operations on the unsupported types for OpenMP device compilation.
+  if (getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice) {
+    if (UnaryOperator::isIncrementDecrementOp(Opc) ||
+        UnaryOperator::isArithmeticOp(Opc))
+      checkOpenMPDeviceExpr(InputExpr);
+  }
+
   switch (Opc) {
   case UO_PreInc:
   case UO_PreDec:
index 76141afd595a9fb875c1272d3d5a0b183609f4f7..7fed3725edd4c72e5c8368083ec96d59e4deb477 100644 (file)
@@ -1443,6 +1443,18 @@ void Sema::checkOpenMPDeviceFunction(SourceLocation Loc, FunctionDecl *Callee) {
     DeviceCallGraph[Caller].insert({Callee, Loc});
 }
 
+void Sema::checkOpenMPDeviceExpr(const Expr *E) {
+  assert(getLangOpts().OpenMP && getLangOpts().OpenMPIsDevice &&
+         "OpenMP device compilation mode is expected.");
+  QualType Ty = E->getType();
+  if ((Ty->isFloat16Type() && !Context.getTargetInfo().hasFloat16Type()) ||
+      (Ty->isFloat128Type() && !Context.getTargetInfo().hasFloat128Type()) ||
+      (Ty->isIntegerType() && Context.getTypeSize(Ty) == 128 &&
+       !Context.getTargetInfo().hasInt128Type()))
+    targetDiag(E->getExprLoc(), diag::err_type_unsupported)
+        << Ty << E->getSourceRange();
+}
+
 bool Sema::isOpenMPCapturedByRef(const ValueDecl *D, unsigned Level) const {
   assert(LangOpts.OpenMP && "OpenMP is not allowed");
 
index f314cb0abeaffd1cf2cb6090d37c586dbb136322..3591add23da6f52df41e032fe2982c002940ccec 100644 (file)
@@ -1433,7 +1433,8 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
     break;
   }
   case DeclSpec::TST_int128:
-    if (!S.Context.getTargetInfo().hasInt128Type())
+    if (!S.Context.getTargetInfo().hasInt128Type() &&
+        !(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice))
       S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported)
         << "__int128";
     if (DS.getTypeSpecSign() == DeclSpec::TSS_unsigned)
@@ -1445,7 +1446,8 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
     // CUDA host and device may have different _Float16 support, therefore
     // do not diagnose _Float16 usage to avoid false alarm.
     // ToDo: more precise diagnostics for CUDA.
-    if (!S.Context.getTargetInfo().hasFloat16Type() && !S.getLangOpts().CUDA)
+    if (!S.Context.getTargetInfo().hasFloat16Type() && !S.getLangOpts().CUDA &&
+        !(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice))
       S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported)
         << "_Float16";
     Result = Context.Float16Ty;
@@ -1459,7 +1461,8 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
       Result = Context.DoubleTy;
     break;
   case DeclSpec::TST_float128:
-    if (!S.Context.getTargetInfo().hasFloat128Type())
+    if (!S.Context.getTargetInfo().hasFloat128Type() &&
+        !(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice))
       S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported)
         << "__float128";
     Result = Context.Float128Ty;
diff --git a/test/OpenMP/nvptx_unsupported_type_codegen.cpp b/test/OpenMP/nvptx_unsupported_type_codegen.cpp
new file mode 100644 (file)
index 0000000..61accc3
--- /dev/null
@@ -0,0 +1,64 @@
+// Test target codegen - host bc file has to be created first.
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-host.bc
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple nvptx64-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -aux-triple x86_64-unknown-linux -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-host.bc -o - | FileCheck %s
+// expected-no-diagnostics
+
+// CHECK-DAG: [[T:%.+]] = type {{.+}}, fp128,
+// CHECK-DAG: [[T1:%.+]] = type {{.+}}, i128, i128,
+
+struct T {
+  char a;
+  __float128 f;
+  char c;
+  T() : a(12), f(15) {}
+  T &operator+(T &b) { f += b.a; return *this;}
+};
+
+struct T1 {
+  char a;
+  __int128 f;
+  __int128 f1;
+  char c;
+  T1() : a(12), f(15) {}
+  T1 &operator+(T1 &b) { f += b.a; return *this;}
+};
+
+#pragma omp declare target
+T a = T();
+T f = a;
+// CHECK: define void @{{.+}}foo{{.+}}([[T]]* byval align {{.+}})
+void foo(T a = T()) {
+  return;
+}
+// CHECK: define [6 x i64] @{{.+}}bar{{.+}}()
+T bar() {
+// CHECK:      bitcast [[T]]* %{{.+}} to [6 x i64]*
+// CHECK-NEXT: load [6 x i64], [6 x i64]* %{{.+}},
+// CHECK-NEXT: ret [6 x i64]
+  return T();
+}
+// CHECK: define void @{{.+}}baz{{.+}}()
+void baz() {
+// CHECK:      call [6 x i64] @{{.+}}bar{{.+}}()
+// CHECK-NEXT: bitcast [[T]]* %{{.+}} to [6 x i64]*
+// CHECK-NEXT: store [6 x i64] %{{.+}}, [6 x i64]* %{{.+}},
+  T t = bar();
+}
+T1 a1 = T1();
+T1 f1 = a1;
+// CHECK: define void @{{.+}}foo1{{.+}}([[T1]]* byval align {{.+}})
+void foo1(T1 a = T1()) {
+  return;
+}
+// CHECK: define [[T1]] @{{.+}}bar1{{.+}}()
+T1 bar1() {
+// CHECK:      load [[T1]], [[T1]]*
+// CHECK-NEXT: ret [[T1]]
+  return T1();
+}
+// CHECK: define void @{{.+}}baz1{{.+}}()
+void baz1() {
+// CHECK: call [[T1]] @{{.+}}bar1{{.+}}()
+  T1 t = bar1();
+}
+#pragma omp end declare target
diff --git a/test/OpenMP/nvptx_unsupported_type_messages.cpp b/test/OpenMP/nvptx_unsupported_type_messages.cpp
new file mode 100644 (file)
index 0000000..6e0fa3b
--- /dev/null
@@ -0,0 +1,47 @@
+// Test target codegen - host bc file has to be created first.
+// RUN: %clang_cc1 -fopenmp -x c++ -triple x86_64-unknown-linux -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-host.bc
+// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple nvptx64-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-host.bc -fsyntax-only
+
+struct T {
+  char a;
+  __float128 f;
+  char c;
+  T() : a(12), f(15) {}
+  T &operator+(T &b) { f += b.a; return *this;} // expected-error {{'__float128' is not supported on this target}}
+};
+
+struct T1 {
+  char a;
+  __int128 f;
+  __int128 f1;
+  char c;
+  T1() : a(12), f(15) {}
+  T1 &operator/(T1 &b) { f /= b.a; return *this;}
+};
+
+#pragma omp declare target
+T a = T();
+T f = a;
+void foo(T a = T()) {
+  a = a + f; // expected-note {{called by 'foo'}}
+  return;
+}
+T bar() {
+  return T();
+}
+void baz() {
+  T t = bar();
+}
+T1 a1 = T1();
+T1 f1 = a1;
+void foo1(T1 a = T1()) {
+  a = a / f1;
+  return;
+}
+T1 bar1() {
+  return T1();
+}
+void baz1() {
+  T1 t = bar1();
+}
+#pragma omp end declare target