diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 06ecc186c7dc28..e12bc2ce963335 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 1416a64543a41c..fb5b7ec22d07aa 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 2e0791526aec74..adfd4c207da8b3 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; +def err_typecheck_call_requires_real_fp : Error< + "argument type %0 is not a real floating point type">; +def err_typecheck_call_different_arg_types : Error< + "arguments are of different 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 376765dc1138ef..0721720f790854 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 5f8ad18c0ed2bd..d0587cb723bcb8 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 f0191112074b8f..d572de1b8b79da 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 f4c30c90ad271c..f553b5ca607934 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 252690b436e3d9..f445272b020bf9 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_different_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 00000000000000..cf97b18d874717 --- /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 bdfb01702c3715..872bd732bd4184 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@+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 90c033e47cd179..4b445724f712a1 100644 --- a/clang/test/Sema/builtins.c +++ b/clang/test/Sema/builtins.c @@ -356,3 +356,22 @@ int test_cxx_builtin() { // expected-error@+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 different types ('double' vs 'float')}} + __builtin_complex(1.0f, 2.0); // expected-error {{arguments are of different 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 e678f9a829e469..b0f6348a52a43b 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 d08e673c04b775..80f75c8232c20f 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 void test_builtin_complex(T v, double d) { + (void)__builtin_complex(v, d); // expected-error {{different types}} expected-error {{not a real floating}} + (void)__builtin_complex(d, v); // expected-error {{different 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}}