[clang] 6c18f7d - For PR46800, implement the GCC __builtin_complex builtin.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 22 13:43:26 PDT 2020


Author: Richard Smith
Date: 2020-07-22T13:43:10-07:00
New Revision: 6c18f7db73a08f1ae39a76a86b414c5b0c24ee86

URL: https://github.com/llvm/llvm-project/commit/6c18f7db73a08f1ae39a76a86b414c5b0c24ee86
DIFF: https://github.com/llvm/llvm-project/commit/6c18f7db73a08f1ae39a76a86b414c5b0c24ee86.diff

LOG: For PR46800, implement the GCC __builtin_complex builtin.

glibc's implementation of the CMPLX macro uses it (with -fgnuc-version
set to 4.7 or later).

Added: 
    clang/test/CodeGen/builtin-complex.c
    clang/test/CodeGen/complex-builtins-3.c

Modified: 
    clang/docs/LanguageExtensions.rst
    clang/include/clang/Basic/Builtins.def
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/AST/ExprConstant.cpp
    clang/lib/CodeGen/CGBuiltin.cpp
    clang/lib/Sema/DeclSpec.cpp
    clang/lib/Sema/SemaChecking.cpp
    clang/test/Sema/Float16.c
    clang/test/Sema/builtins.c
    clang/test/Sema/fp16-sema.c
    clang/test/SemaCXX/builtins.cpp

Removed: 
    clang/test/CodeGen/complex-builtints.c


################################################################################
diff  --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 06ecc186c7dc..e12bc2ce9633 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1719,6 +1719,9 @@ This extension also works in C++ mode, as far as that goes, but does not apply
 to the C++ ``std::complex``.  (In C++11, list initialization allows the same
 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.
+
 Builtin Functions
 =================
 

diff  --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def
index 1416a64543a4..fb5b7ec22d07 100644
--- a/clang/include/clang/Basic/Builtins.def
+++ b/clang/include/clang/Basic/Builtins.def
@@ -395,6 +395,9 @@ BUILTIN(__builtin_ctanh, "XdXd", "Fne")
 BUILTIN(__builtin_ctanhf, "XfXf", "Fne")
 BUILTIN(__builtin_ctanhl, "XLdXLd", "Fne")
 
+// GCC-compatible C99 CMPLX implementation.
+BUILTIN(__builtin_complex, "v.", "nct")
+
 // FP Comparisons.
 BUILTIN(__builtin_isgreater     , "i.", "Fnct")
 BUILTIN(__builtin_isgreaterequal, "i.", "Fnct")

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 2e0791526aec..adfd4c207da8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8148,6 +8148,10 @@ def warn_cast_pointer_from_sel : Warning<
 def warn_function_def_in_objc_container : Warning<
   "function definition inside an Objective-C container is deprecated">,
   InGroup<FunctionDefInObjCContainer>;
+def err_typecheck_call_requires_real_fp : Error<
+  "argument type %0 is not a real floating point type">;
+def err_typecheck_call_
diff erent_arg_types : Error<
+  "arguments are of 
diff erent types%
diff { ($ vs $)|}0,1">;
 
 def warn_cast_calling_conv : Warning<
   "cast between incompatible calling conventions '%0' and '%1'; "

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 376765dc1138..0721720f7908 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12172,6 +12172,7 @@ class Sema final {
   bool SemaBuiltinVAStartARMMicrosoft(CallExpr *Call);
   bool SemaBuiltinUnorderedCompare(CallExpr *TheCall);
   bool SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs);
+  bool SemaBuiltinComplex(CallExpr *TheCall);
   bool SemaBuiltinVSX(CallExpr *TheCall);
   bool SemaBuiltinOSLogFormat(CallExpr *TheCall);
 

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 5f8ad18c0ed2..d0587cb723bc 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13281,6 +13281,7 @@ class ComplexExprEvaluator
   bool VisitBinaryOperator(const BinaryOperator *E);
   bool VisitUnaryOperator(const UnaryOperator *E);
   bool VisitInitListExpr(const InitListExpr *E);
+  bool VisitCallExpr(const CallExpr *E);
 };
 } // end anonymous namespace
 
@@ -13749,6 +13750,23 @@ bool ComplexExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
   return ExprEvaluatorBaseTy::VisitInitListExpr(E);
 }
 
+bool ComplexExprEvaluator::VisitCallExpr(const CallExpr *E) {
+  switch (E->getBuiltinCallee()) {
+  case Builtin::BI__builtin_complex:
+    Result.makeComplexFloat();
+    if (!EvaluateFloat(E->getArg(0), Result.FloatReal, Info))
+      return false;
+    if (!EvaluateFloat(E->getArg(1), Result.FloatImag, Info))
+      return false;
+    return true;
+
+  default:
+    break;
+  }
+
+  return ExprEvaluatorBaseTy::VisitCallExpr(E);
+}
+
 //===----------------------------------------------------------------------===//
 // Atomic expression evaluation, essentially just handling the NonAtomicToAtomic
 // implicit conversion.

diff  --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index f0191112074b..d572de1b8b79 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -1978,6 +1978,11 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     Value *Result = Builder.CreateSelect(CmpResult, NegOp, ArgValue, "abs");
     return RValue::get(Result);
   }
+  case Builtin::BI__builtin_complex: {
+    Value *Real = EmitScalarExpr(E->getArg(0));
+    Value *Imag = EmitScalarExpr(E->getArg(1));
+    return RValue::getComplex({Real, Imag});
+  }
   case Builtin::BI__builtin_conj:
   case Builtin::BI__builtin_conjf:
   case Builtin::BI__builtin_conjl:

diff  --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index f4c30c90ad27..f553b5ca6079 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1279,6 +1279,7 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
         S.Diag(TSTLoc, diag::ext_integer_complex);
     } else if (TypeSpecType != TST_float && TypeSpecType != TST_double &&
                TypeSpecType != TST_float128) {
+      // FIXME: _Float16, __fp16?
       S.Diag(TSCLoc, diag::err_invalid_complex_spec)
         << getSpecifierName((TST)TypeSpecType, Policy);
       TypeSpecComplex = TSC_unspecified;

diff  --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 252690b436e3..f445272b020b 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1582,6 +1582,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
     if (checkArgCount(*this, TheCall, 1)) return true;
     TheCall->setType(Context.IntTy);
     break;
+  case Builtin::BI__builtin_complex:
+    if (SemaBuiltinComplex(TheCall))
+      return ExprError();
+    break;
   case Builtin::BI__builtin_constant_p: {
     if (checkArgCount(*this, TheCall, 1)) return true;
     ExprResult Arg = DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
@@ -5786,6 +5790,61 @@ bool Sema::SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs) {
   return false;
 }
 
+/// Perform semantic analysis for a call to __builtin_complex.
+bool Sema::SemaBuiltinComplex(CallExpr *TheCall) {
+  if (checkArgCount(*this, TheCall, 2))
+    return true;
+
+  bool Dependent = false;
+  for (unsigned I = 0; I != 2; ++I) {
+    Expr *Arg = TheCall->getArg(I);
+    QualType T = Arg->getType();
+    if (T->isDependentType()) {
+      Dependent = true;
+      continue;
+    }
+
+    // Despite supporting _Complex int, GCC requires a real floating point type
+    // for the operands of __builtin_complex.
+    if (!T->isRealFloatingType()) {
+      return Diag(Arg->getBeginLoc(), diag::err_typecheck_call_requires_real_fp)
+             << Arg->getType() << Arg->getSourceRange();
+    }
+
+    ExprResult Converted = DefaultLvalueConversion(Arg);
+    if (Converted.isInvalid())
+      return true;
+    TheCall->setArg(I, Converted.get());
+  }
+
+  if (Dependent) {
+    TheCall->setType(Context.DependentTy);
+    return false;
+  }
+
+  Expr *Real = TheCall->getArg(0);
+  Expr *Imag = TheCall->getArg(1);
+  if (!Context.hasSameType(Real->getType(), Imag->getType())) {
+    return Diag(Real->getBeginLoc(),
+                diag::err_typecheck_call_
diff erent_arg_types)
+           << Real->getType() << Imag->getType()
+           << Real->getSourceRange() << Imag->getSourceRange();
+  }
+
+  // We don't allow _Complex _Float16 nor _Complex __fp16 as type specifiers;
+  // don't allow this builtin to form those types either.
+  // FIXME: Should we allow these types?
+  if (Real->getType()->isFloat16Type())
+    return Diag(TheCall->getBeginLoc(), diag::err_invalid_complex_spec)
+           << "_Float16";
+  if (Real->getType()->isHalfType())
+    return Diag(TheCall->getBeginLoc(), diag::err_invalid_complex_spec)
+           << "half";
+
+  TheCall->setType(Context.getComplexType(Real->getType()));
+  return false;
+}
+
 // Customized Sema Checking for VSX builtins that have the following signature:
 // vector [...] builtinName(vector [...], vector [...], const int);
 // Which takes the same type of vectors (any legal vector type) for the first

diff  --git a/clang/test/CodeGen/builtin-complex.c b/clang/test/CodeGen/builtin-complex.c
new file mode 100644
index 000000000000..cf97b18d8747
--- /dev/null
+++ b/clang/test/CodeGen/builtin-complex.c
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple x86_64-linux -w -S -o - -emit-llvm -DT=float %s | FileCheck %s --check-prefixes=CHECK,CHECK-FLOAT
+// RUN: %clang_cc1 -triple x86_64-linux -w -S -o - -emit-llvm -DT=double %s | FileCheck %s --check-prefixes=CHECK,CHECK-DOUBLE
+// RUN: %clang_cc1 -triple x86_64-linux -w -S -o - -emit-llvm -DT="long double" %s | FileCheck %s --check-prefixes=CHECK,CHECK-FP80
+// RUN: %clang_cc1 -triple x86_64-linux -w -S -o - -emit-llvm -DT=__float128 %s | FileCheck %s --check-prefixes=CHECK,CHECK-FP128
+// FIXME: If we start to support _Complex __fp16 or _Complex _Float16, add tests for them too.
+
+// CHECK-FLOAT: @global = global { [[T:float]], [[T]] } { [[T]] 1.0{{.*}}, [[T]] 2.0{{.*}} }
+// CHECK-DOUBLE: @global = global { [[T:double]], [[T]] } { [[T]] 1.0{{.*}}, [[T]] 2.0{{.*}} }
+// CHECK-FP80: @global = global { [[T:x86_fp80]], [[T]] } { [[T]] 0xK3FFF8000000000000000, [[T]] 0xK40008000000000000000 }
+// CHECK-FP128: @global = global { [[T:fp128]], [[T]] } { [[T]] 0xL00000000000000003FFF000000000000, [[T]] 0xL00000000000000004000000000000000 }
+_Complex T global = __builtin_complex(1.0, 2.0);
+
+// CHECK-LABEL: @test
+_Complex T test(T a, T b) {
+  return __builtin_complex(a, b);
+  // CHECK: %[[A:.*]] = load [[T]], [[T]]* %a.addr,
+  // CHECK: %[[B:.*]] = load [[T]], [[T]]* %b.addr,
+  // CHECK: %[[RET_RE:.*]] = getelementptr inbounds { [[T]], [[T]] }, { [[T]], [[T]] }* %[[RET:[^,]*]], i32 0, i32 0
+  // CHECK: %[[RET_IM:.*]] = getelementptr inbounds { [[T]], [[T]] }, { [[T]], [[T]] }* %[[RET]], i32 0, i32 1
+  // CHECK: store [[T]] %[[A]], [[T]]* %[[RET_RE]],
+  // CHECK: store [[T]] %[[B]], [[T]]* %[[RET_IM]],
+}

diff  --git a/clang/test/CodeGen/complex-builtints.c b/clang/test/CodeGen/complex-builtins-3.c
similarity index 100%
rename from clang/test/CodeGen/complex-builtints.c
rename to clang/test/CodeGen/complex-builtins-3.c

diff  --git a/clang/test/Sema/Float16.c b/clang/test/Sema/Float16.c
index bdfb01702c37..872bd732bd41 100644
--- a/clang/test/Sema/Float16.c
+++ b/clang/test/Sema/Float16.c
@@ -3,9 +3,16 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -triple armv7a-linux-gnu %s -DHAVE
 // RUN: %clang_cc1 -fsyntax-only -verify -triple aarch64-linux-gnu %s -DHAVE
 
-#ifdef HAVE
-// expected-no-diagnostics
-#else
+#ifndef HAVE
 // expected-error at +2{{_Float16 is not supported on this target}}
 #endif // HAVE
 _Float16 f;
+
+#ifdef HAVE
+// FIXME: Should this be valid?
+_Complex _Float16 a; // expected-error {{'_Complex _Float16' is invalid}}
+void builtin_complex() {
+  _Float16 a = 0;
+  (void)__builtin_complex(a, a); // expected-error {{'_Complex _Float16' is invalid}}
+}
+#endif

diff  --git a/clang/test/Sema/builtins.c b/clang/test/Sema/builtins.c
index 90c033e47cd1..4b445724f712 100644
--- a/clang/test/Sema/builtins.c
+++ b/clang/test/Sema/builtins.c
@@ -356,3 +356,22 @@ int test_cxx_builtin() {
   // expected-error at +1 {{use of unknown builtin '__builtin_is_constant_evaluated'}}
   return __builtin_is_constant_evaluated();
 }
+
+void test_builtin_complex() {
+  __builtin_complex(); // expected-error {{too few}}
+  __builtin_complex(1); // expected-error {{too few}}
+  __builtin_complex(1, 2, 3); // expected-error {{too many}}
+
+  _Static_assert(_Generic(__builtin_complex(1.0f, 2.0f), _Complex float: 1, default: 0), "");
+  _Static_assert(_Generic(__builtin_complex(1.0, 2.0), _Complex double: 1, default: 0), "");
+  _Static_assert(_Generic(__builtin_complex(1.0l, 2.0l), _Complex long double: 1, default: 0), "");
+
+  __builtin_complex(1, 2); // expected-error {{argument type 'int' is not a real floating point type}}
+  __builtin_complex(1, 2.0); // expected-error {{argument type 'int' is not a real floating point type}}
+  __builtin_complex(1.0, 2); // expected-error {{argument type 'int' is not a real floating point type}}
+
+  __builtin_complex(1.0, 2.0f); // expected-error {{arguments are of 
diff erent types ('double' vs 'float')}}
+  __builtin_complex(1.0f, 2.0); // expected-error {{arguments are of 
diff erent types ('float' vs 'double')}}
+}
+
+_Complex double builtin_complex_static_init = __builtin_complex(1.0, 2.0);

diff  --git a/clang/test/Sema/fp16-sema.c b/clang/test/Sema/fp16-sema.c
index e678f9a829e4..b0f6348a52a4 100644
--- a/clang/test/Sema/fp16-sema.c
+++ b/clang/test/Sema/fp16-sema.c
@@ -28,3 +28,9 @@ extern __fp16 *(*gf1) (void);
 typedef __fp16 (*tf1) (void); // expected-error {{function return value cannot have __fp16 type; did you forget * ?}}
 typedef __fp16 *(*tg1) (void);
 
+void testComplex() {
+  // FIXME: Should these be valid?
+  _Complex __fp16 a; // expected-error {{'_Complex half' is invalid}}
+  __fp16 b;
+  a = __builtin_complex(b, b); // expected-error {{'_Complex half' is invalid}}
+}

diff  --git a/clang/test/SemaCXX/builtins.cpp b/clang/test/SemaCXX/builtins.cpp
index d08e673c04b7..80f75c8232c2 100644
--- a/clang/test/SemaCXX/builtins.cpp
+++ b/clang/test/SemaCXX/builtins.cpp
@@ -144,3 +144,12 @@ void test_noexcept(int *i) {
 }
 #undef TEST_TYPE
 } // end namespace test_launder
+
+template<typename T> void test_builtin_complex(T v, double d) {
+  (void)__builtin_complex(v, d); // expected-error {{
diff erent types}} expected-error {{not a real floating}}
+  (void)__builtin_complex(d, v); // expected-error {{
diff erent types}} expected-error {{not a real floating}}
+  (void)__builtin_complex(v, v); // expected-error {{not a real floating}}
+}
+template void test_builtin_complex(double, double);
+template void test_builtin_complex(float, double); // expected-note {{instantiation of}}
+template void test_builtin_complex(int, double); // expected-note {{instantiation of}}


        


More information about the cfe-commits mailing list