Index: docs/UsersManual.rst =================================================================== --- docs/UsersManual.rst +++ docs/UsersManual.rst @@ -1325,7 +1325,8 @@ Generate code which only uses the general purpose registers. This option restricts the generated code to use general registers - only. This only applies to the AArch64 architecture. + only. This only applies to the AArch64 architecture. This restriction does + not apply to inline assembly. .. option:: -mcompact-branches=[values] Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6118,6 +6118,11 @@ def ext_freestanding_complex : Extension< "complex numbers are an extension in a freestanding C99 implementation">; +def err_non_general_ops_disabled : Error< + "use of floating-point or vector values is disabled by " + "'-mgeneral-regs-only'">; +def note_from_use_of_default_arg : Note<"from use of default argument here">; + // FIXME: Remove when we support imaginary. def err_imaginary_not_supported : Error<"imaginary types are not supported">; Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -139,6 +139,7 @@ LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly") LANGOPT(CoroutinesTS , 1, 0, "C++ coroutines TS") LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments") +LANGOPT(GeneralOpsOnly , 1, 0, "Whether to diagnose the use of floating-point or vector operations") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") Index: include/clang/Driver/CC1Options.td =================================================================== --- include/clang/Driver/CC1Options.td +++ include/clang/Driver/CC1Options.td @@ -210,6 +210,8 @@ HelpText<"Emit an error if a C++ static local initializer would need a guard variable">; def no_implicit_float : Flag<["-"], "no-implicit-float">, HelpText<"Don't generate implicit floating point instructions">; +def general_regs_only : Flag<["-"], "general-regs-only">, + HelpText<"Don't allow the generation of floating point or vector instructions">; def fdump_vtable_layouts : Flag<["-"], "fdump-vtable-layouts">, HelpText<"Dump the layouts of all vtables that will be emitted in a translation unit">; def fmerge_functions : Flag<["-"], "fmerge-functions">, Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -10301,6 +10301,13 @@ unsigned ByteNo) const; private: + // Tests to see if the given type is or contains a float or vector, as defined + // by -mgeneral-regs-only. + // + // `Cache` can be used shared across runs to potentially speed up later + // queries. + static bool typeHasFloatingOrVectorComponent(QualType Ty); + void CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr, const ArraySubscriptExpr *ASE=nullptr, bool AllowOnePastEnd=true, bool IndexNegated=false); Index: lib/Driver/ToolChains/Arch/AArch64.cpp =================================================================== --- lib/Driver/ToolChains/Arch/AArch64.cpp +++ lib/Driver/ToolChains/Arch/AArch64.cpp @@ -173,15 +173,13 @@ success = getAArch64MicroArchFeaturesFromMcpu( D, getAArch64TargetCPU(Args, A), Args, Features); - if (!success) - D.Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args); - if (Args.getLastArg(options::OPT_mgeneral_regs_only)) { - Features.push_back("-fp-armv8"); - Features.push_back("-crypto"); - Features.push_back("-neon"); + Features.push_back("+general-regs-only"); } + if (!success) + D.Diag(diag::err_drv_clang_unsupported) << A->getAsString(Args); + // En/disable crc if (Arg *A = Args.getLastArg(options::OPT_mcrc, options::OPT_mnocrc)) { if (A->getOption().matches(options::OPT_mcrc)) Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -1317,6 +1317,24 @@ } } +static void addGeneralRegsOnlyArgs(const Driver &D, const ArgList &Args, + ArgStringList &CmdArgs) { + if (Args.getLastArg(options::OPT_mgeneral_regs_only)) { + if (Args.hasFlag(options::OPT_mimplicit_float, + options::OPT_mno_implicit_float, false)) { + D.Diag(diag::err_drv_argument_not_allowed_with) << "-mimplicit-float" + << "-mgeneral-regs-only"; + return; + } + + CmdArgs.push_back("-general-regs-only"); + CmdArgs.push_back("-no-implicit-float"); + } else if (!Args.hasFlag(options::OPT_mimplicit_float, + options::OPT_mno_implicit_float, true)) { + CmdArgs.push_back("-no-implicit-float"); + } +} + void Clang::AddARMTargetArgs(const llvm::Triple &Triple, const ArgList &Args, ArgStringList &CmdArgs, bool KernelOrKext) const { // Select the ABI to use. @@ -1362,9 +1380,7 @@ CmdArgs.push_back("-arm-global-merge=true"); } - if (!Args.hasFlag(options::OPT_mimplicit_float, - options::OPT_mno_implicit_float, true)) - CmdArgs.push_back("-no-implicit-float"); + addGeneralRegsOnlyArgs(getToolChain().getDriver(), Args, CmdArgs); } void Clang::RenderTargetOptions(const llvm::Triple &EffectiveTriple, @@ -1452,9 +1468,7 @@ Args.hasArg(options::OPT_fapple_kext)) CmdArgs.push_back("-disable-red-zone"); - if (!Args.hasFlag(options::OPT_mimplicit_float, - options::OPT_mno_implicit_float, true)) - CmdArgs.push_back("-no-implicit-float"); + addGeneralRegsOnlyArgs(getToolChain().getDriver(), Args, CmdArgs); const char *ABIName = nullptr; if (Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2112,6 +2112,9 @@ Opts.CUDARelocatableDeviceCode = Args.hasArg(OPT_fcuda_rdc); + if (Args.hasArg(OPT_general_regs_only)) + Opts.GeneralOpsOnly = 1; + if (Opts.ObjC1) { if (Arg *arg = Args.getLastArg(OPT_fobjc_runtime_EQ)) { StringRef value = arg->getValue(); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -8668,6 +8668,19 @@ // Finally, we know we have the right number of parameters, install them. NewFD->setParams(Params); + if (getLangOpts().GeneralOpsOnly && + D.getFunctionDefinitionKind() == FDK_Definition) { + if (typeHasFloatingOrVectorComponent(NewFD->getReturnType())) { + SourceRange Range = NewFD->getReturnTypeSourceRange(); + Diag(Range.getBegin(), diag::err_non_general_ops_disabled) << Range; + } + + for (const ParmVarDecl *PVD : NewFD->parameters()) + if (typeHasFloatingOrVectorComponent(PVD->getType())) + Diag(PVD->getLocStart(), diag::err_non_general_ops_disabled) + << PVD->getSourceRange(); + } + if (D.getDeclSpec().isNoreturnSpecified()) NewFD->addAttr( ::new(Context) C11NoReturnAttr(D.getDeclSpec().getNoreturnSpecLoc(), Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -20,6 +20,7 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/RecursiveASTVisitor.h" @@ -7547,6 +7548,242 @@ return E; } +static bool typeHasFloatingOrVectorComponent(QualType Ty) { + if (Ty.isNull()) + return false; + + while (const auto *AT = Ty->getAsArrayTypeUnsafe()) + Ty = AT->getElementType(); + + if (Ty->isScalarType()) + return Ty->hasFloatingRepresentation(); + + if (Ty->isVectorType()) + return true; + + const auto *StructTy = Ty.getCanonicalType()->getAsStructureType(); + if (!StructTy) + return false; + + // Be conservative in the face of broken code (e.g. potentially recursive + // types) + if (StructTy->getDecl()->isInvalidDecl()) + return false; + + // We treat any union with mixed FP/vector and non-FP/vector elements as a bag + // of bits. + if (Ty->isUnionType()) { + return llvm::all_of(StructTy->getDecl()->fields(), [](const FieldDecl *FD) { + return typeHasFloatingOrVectorComponent(FD->getType()); + }); + } + + return llvm::any_of(StructTy->getDecl()->fields(), [](const FieldDecl *FD) { + return typeHasFloatingOrVectorComponent(FD->getType()); + }); +} + +bool Sema::typeHasFloatingOrVectorComponent(QualType Ty) { + return ::typeHasFloatingOrVectorComponent(Ty.getCanonicalType()); +} + +namespace { +// Diagnoses any uses of vector/floating-point values that we can't trivially +// fold to a non-vector/non-floating constant in codegen. +class NonGeneralOpDiagnoser + : public ConstEvaluatedExprVisitor { + using Super = ConstEvaluatedExprVisitor; + + struct DiagnosticInfo { + SourceLocation Loc; + SourceRange Range; + + // Default arguments are only diagnosed when they're used, but the error + // diagnostic points to the default argument itself. This contains the + // series of calls that brought us to that default arg. + llvm::TinyPtrVector DefaultArgLocs; + + // These should only exist in their fully-constructed form. + DiagnosticInfo() = delete; + + DiagnosticInfo(const Expr *E) + : Loc(E->getLocStart()), Range(E->getSourceRange()) {} + + DiagnosticInfo(const DiagnosticInfo &) = default; + DiagnosticInfo(DiagnosticInfo &&) = default; + + DiagnosticInfo &operator=(const DiagnosticInfo &) = default; + DiagnosticInfo &operator=(DiagnosticInfo &&) = default; + }; + + Sema &S; + llvm::SmallVector DiagnosticsToEmit; + + bool isGeneralType(const Expr *E) { + return !typeHasFloatingOrVectorComponent(E->getType().getCanonicalType()); + } + + void enqueueDiagnosticFor(const Expr *E) { + DiagnosticsToEmit.emplace_back(E); + } + + bool isRValueOfIllegalType(const Expr *E) { + return E->getValueKind() != VK_LValue && !isGeneralType(E); + } + + bool diagnoseIfNonGeneralRValue(const Expr *E) { + if (!isRValueOfIllegalType(E)) + return false; + + enqueueDiagnosticFor(E); + return true; + } + + static const Expr *ignoreParenImpFloatCastsAndSplats(const Expr *E) { + const Expr *Cur = E->IgnoreParens(); + if (const auto *Sub = dyn_cast(Cur)) + if (Sub->getCastKind() == CK_IntegralToFloating || + Sub->getCastKind() == CK_VectorSplat) + return Sub->getSubExpr()->IgnoreParens(); + return Cur; + } + + struct DiagnosticState { + unsigned InitialSize; + }; + + DiagnosticState saveDiagnosticState() const { + return {static_cast(DiagnosticsToEmit.size())}; + } + + MutableArrayRef + diagnosticsIssuedSince(const DiagnosticState &S) { + assert(S.InitialSize <= DiagnosticsToEmit.size() && + "DiagnosticsToEmit shouldn't shrink across saves!"); + return {DiagnosticsToEmit.begin() + S.InitialSize, DiagnosticsToEmit.end()}; + } + + void resetDiagnosticState(const DiagnosticState &S) { + DiagnosticsToEmit.erase(DiagnosticsToEmit.begin() + S.InitialSize, + DiagnosticsToEmit.end()); + } + + // Special case: Don't diagnose patterns that will ultimately turn into a + // memcpy in CodeGen. Returns true if we matched the pattern (and emitted + // diagnostics, if necessary). This is a small convenience to the user, so + // they don't have to write out memcpy() for simple cases. For that reason, + // it's very limited in what it will detect. + // + // N.B. this is C-specific, since C++ doesn't really deal in assignments of + // structs. + bool diagnoseTrivialRecordAssignmentExpr(const BinaryOperator *BO) { + if (BO->getOpcode() != BO_Assign || !BO->getType()->isRecordType()) + return false; + + const auto *RHS = dyn_cast(BO->getRHS()->IgnoreParens()); + if (!RHS || RHS->getCastKind() != CK_LValueToRValue) + return false; + + Visit(BO->getLHS()); + Visit(RHS->getSubExpr()); + return true; + } + +public: + NonGeneralOpDiagnoser(Sema &S) : Super(S.getASTContext()), S(S) {} + + void VisitExpr(const Expr *E) { + if (!diagnoseIfNonGeneralRValue(E)) + Super::VisitExpr(E); + } + + void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { + Visit(E->getExpr()); + } + + void VisitCallExpr(const CallExpr *E) { + diagnoseIfNonGeneralRValue(E); + Visit(E->getCallee()); + + for (const Expr *Arg : E->arguments()) { + DiagnosticState Saved = saveDiagnosticState(); + Visit(Arg); + if (Arg->isDefaultArgument()) + for (DiagnosticInfo &DI : diagnosticsIssuedSince(Saved)) + DI.DefaultArgLocs.push_back(E); + } + } + + void VisitCastExpr(const CastExpr *E) { + DiagnosticState InitialState = saveDiagnosticState(); + + Visit(E->getSubExpr()); + + bool IssuedDiagnostics = !diagnosticsIssuedSince(InitialState).empty(); + // Ignore diagnostics for subexpressions that we can trivially fold to a + // constant in CodeGen. + if (IssuedDiagnostics && isRValueOfIllegalType(E->getSubExpr()) && + !isRValueOfIllegalType(E) && + E->isConstantInitializer(S.getASTContext(), /*ForRef=*/false)) { + resetDiagnosticState(InitialState); + return; + } + + if (isRValueOfIllegalType(E) && !isRValueOfIllegalType(E->getSubExpr())) + enqueueDiagnosticFor(E); + } + + void VisitParenExpr(const ParenExpr *E) { Visit(E->getSubExpr()); } + void VisitDeclRefExpr(const DeclRefExpr *E) { diagnoseIfNonGeneralRValue(E); } + + void VisitBinaryOperator(const BinaryOperator *BO) { + if (isGeneralType(BO)) { + Visit(BO->getLHS()); + Visit(BO->getRHS()); + return; + } + + if (diagnoseTrivialRecordAssignmentExpr(BO)) + return; + + DiagnosticState InitialState = saveDiagnosticState(); + // Ignore implicit casts to illegal types so we minimize diagnostics for + // things like `1 + 2. + 3 + 4`. + Visit(ignoreParenImpFloatCastsAndSplats(BO->getLHS())); + Visit(ignoreParenImpFloatCastsAndSplats(BO->getRHS())); + + // Since we don't diagnose LValues, this is somewhat common. + if (diagnosticsIssuedSince(InitialState).empty()) + enqueueDiagnosticFor(BO); + } + + void VisitExprWithCleanups(const ExprWithCleanups *E) { + Visit(E->getSubExpr()); + } + + static void diagnoseExpr(Sema &S, const Expr *E) { + NonGeneralOpDiagnoser Diagnoser(S); + Diagnoser.Visit(E); + for (const DiagnosticInfo &DI : Diagnoser.DiagnosticsToEmit) { + SourceLocation Loc = DI.Loc; + SourceRange Range = DI.Range; + // There are rare cases (e.g. `(struct Foo){}`, where Foo + // isNonGeneralType), where the expression we're trying to point to + // doesn't exist. Fall back to complaining about the full expr. + if (Loc.isInvalid()) { + Loc = E->getLocStart(); + Range = E->getSourceRange(); + assert(!Loc.isInvalid()); + } + S.Diag(Loc, diag::err_non_general_ops_disabled) << Range; + + for (const CallExpr *CE : DI.DefaultArgLocs) + S.Diag(CE->getRParenLoc(), diag::note_from_use_of_default_arg); + } + } +}; +} // end anonymous namespace + ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC, bool DiscardedValue, bool IsConstexpr, @@ -7599,6 +7836,9 @@ CheckCompletedExpr(FullExpr.get(), CC, IsConstexpr); + if (getLangOpts().GeneralOpsOnly) + NonGeneralOpDiagnoser::diagnoseExpr(*this, FullExpr.get()); + // At the end of this full expression (which could be a deeply nested // lambda), if there is a potential capture within the nested lambda, // have the outer capture-able lambda try and capture it. Index: test/CodeGen/aarch64-mgeneral_regs_only.c =================================================================== --- /dev/null +++ test/CodeGen/aarch64-mgeneral_regs_only.c @@ -0,0 +1,64 @@ +// RUN: %clang -target aarch64 -mgeneral-regs-only %s -o - -S -emit-llvm | FileCheck %s +// %clang -target aarch64 -mgeneral-regs-only %s -o - -S | FileCheck %s --check-prefix=ASM + +// CHECK-LABEL: @j = dso_local constant i32 3 + +float arr[8]; + +// CHECK-LABEL: noimplicitfloat +// CHECK-NEXT: define dso_local void @foo +const int j = 1.0 + 2.0; +void foo(int *i) { + // CHECK: store i32 4 + *i = 1.0 + j; +} + +// CHECK-LABEL: noimplicitfloat +// CHECK-NEXT: define dso_local void @bar +void bar(float **j) { + __builtin_memcpy(arr, j, sizeof(arr)); + *j = arr; +} + +struct OneFloat { float i; }; +struct TwoFloats { float i, j; }; + +static struct OneFloat oneFloat; +static struct TwoFloats twoFloats; + +// CHECK-LABEL: testOneFloat +void testOneFloat(const struct OneFloat *o, struct OneFloat *f) { + // These memcpys are necessary, so we don't generate FP ops in LLVM. + // CHECK: @llvm.memcpy + // CHECK: @llvm.memcpy + *f = *o; + oneFloat = *o; +} + +// CHECK-LABEL: testTwoFloats +void testTwoFloats(const struct TwoFloats *o, struct TwoFloats *f) { + // CHECK: @llvm.memcpy + // CHECK: @llvm.memcpy + *f = *o; + twoFloats = *o; +} + +// -mgeneral-regs-only implies that the compiler can't invent +// floating-point/vector instructions. The user should be allowed to do that, +// though. +// +// CHECK-LABEL: baz +// ASM-LABEL: baz: +void baz(float *i) { + // CHECK: call float* asm sideeffect + // ASM: fmov s1, # + // ASM: fadd s0, s0, s1 + asm volatile("ldr s0, [%0]\n" + "fmov s1, #1.00000000\n" + "fadd s0, s0, s1\n" + "str s0, [%0]\n" + "ret\n" + : "=r"(i) + : + : "s0", "s1"); +} Index: test/Driver/aarch64-mgeneral_regs_only.c =================================================================== --- test/Driver/aarch64-mgeneral_regs_only.c +++ test/Driver/aarch64-mgeneral_regs_only.c @@ -4,6 +4,4 @@ // RUN: | FileCheck --check-prefix=CHECK-NO-FP %s // RUN: %clang -target arm64-linux-eabi -mgeneral-regs-only %s -### 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-NO-FP %s -// CHECK-NO-FP: "-target-feature" "-fp-armv8" -// CHECK-NO-FP: "-target-feature" "-crypto" -// CHECK-NO-FP: "-target-feature" "-neon" +// CHECK-NO-FP: "-target-feature" "+general-regs-only" Index: test/Sema/aarch64-mgeneral_regs_only.c =================================================================== --- /dev/null +++ test/Sema/aarch64-mgeneral_regs_only.c @@ -0,0 +1,252 @@ +// RUN: %clang_cc1 -triple aarch64-linux-eabi -general-regs-only %s -verify -DBANNED=int '-DVECATTR=__attribute__((ext_vector_type(2)))' -Wno-unused-value +// RUN: %clang_cc1 -triple aarch64-linux-eabi -general-regs-only %s -verify -DBANNED=float -Wno-unused-value +// RUN: %clang_cc1 -triple aarch64-linux-eabi -general-regs-only %s -verify -DBANNED=FloatTypedef -Wno-unused-value + +// Try to diagnose every use of a floating-point or vector operation that we +// can't trivially fold. Declaring these, passing their addresses around, etc. +// is OK, assuming we never actually use them in this TU. + +typedef float FloatTypedef; + +#ifndef VECATTR +#define VECATTR +#endif +typedef BANNED BannedTy VECATTR; + +// Whether or not this is the actual definition for uintptr_t doesn't matter. +typedef unsigned long long uintptr_t; + +// We only start caring when the user actually tries to do things with floats; +// declarations on their own are fine. +// This allows a user to #include some headers that happen to use floats. As +// long as they're never actually used, no one cares. +BannedTy foo(); +void bar(BannedTy); +extern BannedTy gBaz; + +void calls() { + __auto_type a = foo(); // expected-error{{use of floating-point or vector values is disabled}} + BannedTy b = foo(); // expected-error{{use of floating-point or vector values is disabled}} + foo(); // expected-error{{use of floating-point or vector values is disabled}} + (void)foo(); // expected-error{{use of floating-point or vector values is disabled}} + + bar(1); // expected-error{{use of floating-point or vector values is disabled}} + bar(1.); // expected-error{{use of floating-point or vector values is disabled}} + + gBaz = 1; // expected-error{{use of floating-point or vector values is disabled}} + gBaz = 1.; // expected-error{{use of floating-point or vector values is disabled}} +} + +BannedTy global_banned; + +void literals() { + volatile int i; + i = 1.; + i = 1.0 + 2; + i = (int)(1.0 + 2); + + BannedTy j = 1; // expected-error{{use of floating-point or vector values is disabled}} + BannedTy k = (BannedTy)1.1; // expected-error{{use of floating-point or vector values is disabled}} + BannedTy l; + (BannedTy)3; // expected-error{{use of floating-point or vector values is disabled}} +} + +struct Baz { + int i; + BannedTy f; +}; + +union Qux { + int i; + BannedTy j; + BannedTy *p; +}; + +struct Baz *getBaz(int i); + +void structs(void *p) { + struct Baz b; + union Qux q; + + q = (union Qux){}; + q.i = 1; + q.j = 2.; // expected-error{{use of floating-point or vector values is disabled}} + q.j = 2.f; // expected-error{{use of floating-point or vector values is disabled}} + q.j = 2; // expected-error{{use of floating-point or vector values is disabled}} + q.p = (BannedTy *)p; + q.p += 5; + *q.p += 5; // expected-error{{use of floating-point or vector values is disabled}} + + b = (struct Baz){}; // expected-error{{use of floating-point or vector values is disabled}} + b = *getBaz(2. + b.i); // expected-error{{use of floating-point or vector values is disabled}} + *getBaz(2. + b.i) = b; // expected-error{{use of floating-point or vector values is disabled}} +} + +struct Baz callBaz(struct Baz); +union Qux callQux(union Qux); +struct Baz *callBazPtr(struct Baz *); +union Qux *callQuxPtr(union Qux *); + +void structCalls() { + void *p; + callBazPtr((struct Baz *)p); + callQuxPtr((union Qux *)p); + + // One error for returning a `struct Baz`, one for taking a `struct Baz`, one + // for constructing a `struct Baz`. Not ideal, but... + callBaz((struct Baz){}); // expected-error 3{{use of floating-point or vector values is disabled}} + callQux((union Qux){}); +} + +extern BannedTy extern_arr[4]; +static BannedTy static_arr[4]; + +void arrays() { + BannedTy bannedArr[] = { // expected-error{{use of floating-point or vector values is disabled}} + 1, + 1.0, + 2.0f, + }; + int intArr[] = { 1.0, 2.0f }; + + intArr[0] = 1.0; + bannedArr[0] = 1; // expected-error{{use of floating-point or vector values is disabled}} +} + +BannedTy *getMemberPtr(struct Baz *b, int i) { + if (i) + return &b->f; + return &((struct Baz *)(uintptr_t)((uintptr_t)b + 1.0))->f; // expected-error{{use of floating-point or vector values is disabled}} +} + +void casts() { + void *volatile p; + + (BannedTy *)p; + (void)*(BannedTy *)p; // expected-error{{use of floating-point or vector values is disabled}} + + (void)*(struct Baz *)p; // expected-error{{use of floating-point or vector values is disabled}} + (void)*(union Qux *)p; + (void)((union Qux *)p)->i; + (void)((union Qux *)p)->j; // expected-error{{use of floating-point or vector values is disabled}} +} + +BannedTy returns() { // expected-error{{use of floating-point or vector values is disabled}} + return 0; // expected-error{{use of floating-point or vector values is disabled}} +} + +int unevaluated() { + return sizeof((BannedTy)0.0); +} + +void moreUnevaluated(int x) + __attribute__((diagnose_if(x + 1.1 == 2.1, "oh no", "warning"))) { + moreUnevaluated(3); + moreUnevaluated(1); // expected-warning{{oh no}} expected-note@-2{{from 'diagnose_if'}} +} + +void noSpam() { + float r = 1. + 2 + 3 + 4 + 5.; // expected-error 2{{use of floating-point or vector values is disabled}} + float r2 = 1. + r + 3 + 4 + 5.; // expected-error 3{{use of floating-point or vector values is disabled}} + float r3 = 1 + 2 + 3 + 4 + 5; // expected-error{{use of floating-point or vector values is disabled}} + + // no errors expected below: they can be trivially folded to a constant. + int i = 1. + 2 + 3 + 4 + 5.; // no error: we can trivially fold this to a constant. + int j = (int)(1. + 2 + 3) + 4; // no error: we can trivially fold this to a constant. + int k = (int)(1. + 2 + 3) + j; + int l = (int)(1. + 2 + 3) + + r; //expected-error {{use of floating-point or vector values is disabled}} + + const int cj = (int)(1. + 2 + 3) + 4; // no error: we can trivially fold this to a constant. + int ck = (int)(1. + cj + 3) + + r; // expected-error{{use of floating-point or vector values is disabled}} +} + +float fooFloat(); +int exprStmt() { + return ({ fooFloat() + 1 + 2; }) + 3; // expected-error 2{{use of floating-point or vector values is disabled}} +} + +int longExprs() { + // FIXME: Should we be able to fold this to a constant? + return 1 + ((struct Baz){.i = 1}).i; // expected-error{{use of floating-point or vector values is disabled}} +} + +struct RecursiveTy { // expected-note{{is not complete until}} + int a; + BannedTy b; + struct RecursiveTy ty; // expected-error{{field has incomplete type}} +}; + +struct StructRec { + int a; + BannedTy b; + struct RecursiveTy ty; +}; + +union UnionRec { + int a; + BannedTy b; + struct RecursiveTy ty; +}; + +struct UndefType; // expected-note 3{{forward declaration}} + +struct StructUndef { + int a; + BannedTy b; + struct UndefType s; // expected-error{{field has incomplete type}} +}; + +union UnionUndef { + int a; + BannedTy b; + struct UndefType s; // expected-error{{field has incomplete type}} +}; + +// ...Just be sure we don't crash on these. +void cornerCases() { + struct RecInl { // expected-note{{is not complete until the closing}} + struct RecInl s; // expected-error{{field has incomplete type}} + BannedTy f; + } inl; + __builtin_memset(&inl, 0, sizeof(inl)); + + BannedTy fs[] = { + ((struct RecursiveTy){}).a, + ((struct StructRec){}).a, + ((union UnionRec){}).a, + ((struct StructUndef){}).a, + ((union UnionUndef){}).a, + }; + + BannedTy fs2[] = { + ((struct RecursiveTy){}).b, + ((struct StructRec){}).b, + ((union UnionRec){}).b, + ((struct StructUndef){}).b, + ((union UnionUndef){}).b, + }; + + struct UndefType a = {}; // expected-error{{has incomplete type}} + struct RecursiveTy b = {}; + struct StructRec c = {}; + union UnionRec d = {}; + struct StructUndef e = {}; + union UnionUndef f = {}; + + __builtin_memset(&a, 0, sizeof(a)); + __builtin_memset(&b, 0, sizeof(b)); + __builtin_memset(&c, 0, sizeof(c)); + __builtin_memset(&d, 0, sizeof(d)); + __builtin_memset(&e, 0, sizeof(e)); + __builtin_memset(&f, 0, sizeof(f)); +} + +void floatFunctionDefIn(BannedTy a) {} // expected-error{{use of floating-point or vector values is disabled}} +BannedTy floatFunctionDefOut(void) {} // expected-error{{use of floating-point or vector values is disabled}} +BannedTy // expected-error{{use of floating-point or vector values is disabled}} +floatFunctionDefInOut(BannedTy a) {} // expected-error{{use of floating-point or vector values is disabled}} + +void floatInStructDefIn(struct Baz a) {} // expected-error{{use of floating-point or vector values is disabled}} +struct Baz floatInStructDefOut(void) {} // expected-error{{use of floating-point or vector values is disabled}} Index: test/SemaCXX/aarch64-mgeneral_regs_only.cpp =================================================================== --- /dev/null +++ test/SemaCXX/aarch64-mgeneral_regs_only.cpp @@ -0,0 +1,124 @@ +// RUN: %clang_cc1 -triple aarch64-linux-eabi -std=c++11 -general-regs-only %s -verify -DBANNED=float -Wno-unused-value +// RUN: %clang_cc1 -triple aarch64-linux-eabi -std=c++11 -general-regs-only %s -verify -DBANNED=int '-DVECATTR=__attribute__((ext_vector_type(2)))' -Wno-unused-value +// RUN: %clang_cc1 -triple aarch64-linux-eabi -std=c++11 -general-regs-only %s -verify -DBANNED=FloatTypedef -Wno-unused-value + +using FloatTypedef = float; + +#ifndef VECATTR +#define VECATTR +#define BannedToInt(x) (x) +#else +#define BannedToInt(x) ((x)[0]) +#endif + +typedef BANNED BannedTy VECATTR; + +namespace default_args { +int foo(BannedTy = 0); // expected-error 2{{use of floating-point or vector values is disabled}} +int bar(int = 1.0); + +void baz(int a = foo()); // expected-note{{from use of default argument here}} +void bazz(int a = bar()); + +void test() { + foo(); // expected-note{{from use of default argument here}} + bar(); + baz(); // expected-note{{from use of default argument here}} + baz(4); + bazz(); +} +} + +namespace conversions { +struct ConvertToFloat { explicit operator BannedTy(); }; +struct ConvertToFloatImplicit { /* implicit */ operator BannedTy(); }; +struct ConvertFromFloat { ConvertFromFloat(BannedTy); }; + +void takeFloat(BannedTy); +void takeConvertFromFloat(ConvertFromFloat); +void takeConvertFromFloatRef(const ConvertFromFloat &); + +void test() { + BannedTy(ConvertToFloat()); + + takeFloat(ConvertToFloatImplicit{}); // expected-error{{use of floating-point or vector values is disabled}} + + ConvertFromFloat(0); // expected-error{{use of floating-point or vector values is disabled}} + ConvertFromFloat(ConvertToFloatImplicit{}); // expected-error{{use of floating-point or vector values is disabled}} + + ConvertFromFloat{0}; // expected-error{{use of floating-point or vector values is disabled}} + ConvertFromFloat{ConvertToFloatImplicit{}}; // expected-error{{use of floating-point or vector values is disabled}} + + takeConvertFromFloat(0); // expected-error{{use of floating-point or vector values is disabled}} + takeConvertFromFloatRef(0); // expected-error{{use of floating-point or vector values is disabled}} + + takeConvertFromFloat(ConvertFromFloat(0)); // expected-error{{use of floating-point or vector values is disabled}} + takeConvertFromFloatRef(ConvertFromFloat(0)); // expected-error{{use of floating-point or vector values is disabled}} +} + + +void takeImplicitFloat(BannedTy = ConvertToFloatImplicit()); // expected-error{{use of floating-point or vector values is disabled}} +void test2() { + takeImplicitFloat(); // expected-note{{from use of default argument here}} +} +} + +namespace refs { + struct BannedRef { + const BannedTy &f; + BannedRef(const BannedTy &f): f(f) {} + }; + + BannedTy &getBanned(); + BannedTy getBannedVal(); + + void foo() { + BannedTy &a = getBanned(); + BannedTy b = getBanned(); // expected-error{{use of floating-point or vector values is disabled}} + const BannedTy &c = getBanned(); + const BannedTy &d = getBannedVal(); // expected-error{{use of floating-point or vector values is disabled}} + + const int &e = 1.0; + const int &f = BannedToInt(getBannedVal()); // expected-error{{use of floating-point or vector values is disabled}} + + BannedRef{getBanned()}; + BannedRef{getBannedVal()}; // expected-error{{use of floating-point or vector values is disabled}} + } +} + +namespace class_init { + struct Foo { + float f = 1.0; // expected-error{{use of floating-point or vector values is disabled}} + int i = 1.0; + float j; + + Foo(): + j(1) // expected-error{{use of floating-point or vector values is disabled}} + {} + }; +} + +namespace copy_move_assign { + struct Foo { float f; }; // expected-error 2{{use of floating-point or vector values is disabled}} + + void copyFoo(Foo &f) { + Foo a = f; // expected-error{{use of floating-point or vector values is disabled}} + Foo b(static_cast(f)); // expected-error{{use of floating-point or vector values is disabled}} + f = a; // expected-note{{in implicit copy assignment operator}} + f = static_cast(b); // expected-error{{use of floating-point or vector values is disabled}} expected-note {{in implicit move assignment operator}} + } +} + +namespace templates { +float bar(); + +template +T foo(int t = bar()) { // expected-error 2{{use of floating-point or vector values is disabled}} + return t; // expected-error{{use of floating-point or vector values is disabled}} +} + +void test() { + foo(9); // expected-error{{use of floating-point or vector values is disabled}} expected-note{{in instantiation of function template specialization}} + foo(); // expected-error{{use of floating-point or vector values is disabled}} expected-note{{in instantiation of default function argument}} expected-note{{from use of default argument}} +} +}