george.burgess.iv updated this revision to Diff 201047.
george.burgess.iv marked 10 inline comments as done.
george.burgess.iv added a comment.
Addressed feedback, modulo the constant foldable comment thread.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D38479/new/
https://reviews.llvm.org/D38479
Files:
clang/docs/UsersManual.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/LangOptions.def
clang/include/clang/Driver/CC1Options.td
clang/include/clang/Sema/Sema.h
clang/lib/Driver/ToolChains/Arch/AArch64.cpp
clang/lib/Driver/ToolChains/Clang.cpp
clang/lib/Frontend/CompilerInvocation.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/test/CodeGen/aarch64-mgeneral_regs_only.c
clang/test/Sema/aarch64-mgeneral_regs_only.c
clang/test/SemaCXX/aarch64-mgeneral_regs_only.cpp
Index: clang/test/SemaCXX/aarch64-mgeneral_regs_only.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/aarch64-mgeneral_regs_only.cpp
@@ -0,0 +1,186 @@
+// 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 default_args
+
+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 conversions
+
+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 refs
+
+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 class_init
+
+namespace copy_move_assign {
+struct Foo { // expected-error 2{{use of floating-point or vector values is disabled}}
+ float f;
+};
+
+void copyFoo(Foo &f) {
+ Foo a = f; // expected-error{{use of floating-point or vector values is disabled}}
+ Foo b(static_cast<Foo &&>(f)); // expected-error{{use of floating-point or vector values is disabled}}
+ f = a; // expected-note{{in implicit copy assignment operator}}
+ f = static_cast<Foo &&>(b); // expected-error{{use of floating-point or vector values is disabled}} expected-note {{in implicit move assignment operator}}
+}
+} // namespace copy_move_assign
+
+namespace templates {
+float bar();
+
+template <typename T>
+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<float>(9); // expected-error{{use of floating-point or vector values is disabled}} expected-note{{in instantiation of function template specialization}}
+ foo<float>(); // 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}}
+}
+} // namespace templates
+
+namespace base_classes {
+struct Foo {
+ BannedTy f;
+};
+
+struct Bar : Foo {};
+struct Baz : virtual Foo {};
+
+struct Nothing {};
+struct Qux : Nothing, Baz {};
+
+Foo getFoo() { // expected-error{{use of floating-point or vector values is disabled}}
+ __builtin_trap();
+}
+Bar getBar() { // expected-error{{use of floating-point or vector values is disabled}}
+ __builtin_trap();
+}
+Baz getBaz() { // expected-error{{use of floating-point or vector values is disabled}}
+ __builtin_trap();
+}
+Qux getQux() { // expected-error{{use of floating-point or vector values is disabled}}
+ __builtin_trap();
+}
+} // namespace base_classes
+
+namespace constexprs {
+constexpr float foo = 1.0;
+constexpr int bar = 1.0;
+
+constexpr int getFoo() { return 1; }
+
+constexpr float baz = getFoo();
+constexpr int qux = baz + foo + bar;
+
+int getVal() {
+ return qux;
+}
+
+int getVal2() {
+ return baz;
+}
+
+constexpr int transformFoo(float foo) { // expected-error{{use of floating-point or vector values is disabled}}
+ return foo + 1; // expected-error{{use of floating-point or vector values is disabled}}
+}
+
+int getVal3() {
+ constexpr int val = transformFoo(1);
+
+#if 0
+ // DO NOT COMMIT: This is currently failing, as it should.
+ int val2 = transformFoo(1); // expected-error{{use of floating-point or vector values is disabled}}
+#endif
+}
+} // namespace constexprs
Index: clang/test/Sema/aarch64-mgeneral_regs_only.c
===================================================================
--- /dev/null
+++ clang/test/Sema/aarch64-mgeneral_regs_only.c
@@ -0,0 +1,255 @@
+// 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) {
+ 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}}
+
+ struct Baz b;
+ // FIXME: Ideally, this would only emit one warning, but it currently emits
+ // two (one for the initializer, which receives its type from the cast, and
+ // one for the cast, since the initializer is an lvalue, and we only "look
+ // through" rvalues).
+ b = (struct Baz){}; // expected-error 2{{use of floating-point or vector values is disabled}}
+ b = *getBaz(b.i); // expected-error{{use of floating-point or vector values is disabled}}
+ *getBaz(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: clang/test/CodeGen/aarch64-mgeneral_regs_only.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/aarch64-mgeneral_regs_only.c
@@ -0,0 +1,63 @@
+// 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) {
+ // CHECK: @llvm.memcpy
+ __builtin_memcpy(f, o, sizeof(*o));
+}
+
+// CHECK-LABEL: testTwoFloats
+void testTwoFloats(const struct TwoFloats *o, struct TwoFloats *f) {
+ // CHECK: @llvm.memcpy
+ __builtin_memcpy(f, o, sizeof(*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: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -19,6 +19,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"
@@ -7823,6 +7824,231 @@
return E;
}
+static bool typeHasFloatingOrVectorComponent(QualType Ty);
+
+static bool recordHasFloatingOrVectorComponent(const RecordDecl *Record) {
+ Record = Record->getDefinition();
+ // Be conservative in the face of broken code (e.g. undefined types,
+ // recursive types, ...)
+ if (!Record || Record->isInvalidDecl())
+ return false;
+
+ auto FieldHasFPOrVectorComponent = [](const FieldDecl *FD) {
+ return typeHasFloatingOrVectorComponent(FD->getType());
+ };
+
+ // We treat any union with mixed FP/vector and non-FP/vector elements as a bag
+ // of bits.
+ if (Record->isUnion())
+ return llvm::all_of(Record->fields(), FieldHasFPOrVectorComponent);
+
+ if (llvm::any_of(Record->fields(), FieldHasFPOrVectorComponent))
+ return true;
+
+ if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(Record))
+ return llvm::any_of(CXXRD->bases(), [](const CXXBaseSpecifier &Base) {
+ return typeHasFloatingOrVectorComponent(Base.getType());
+ });
+
+ return false;
+}
+
+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;
+
+ if (const auto *Record = Ty.getCanonicalType()->getAsRecordDecl())
+ return recordHasFloatingOrVectorComponent(Record);
+
+ return false;
+}
+
+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<NonGeneralOpDiagnoser> {
+ using Super = ConstEvaluatedExprVisitor<NonGeneralOpDiagnoser>;
+
+ 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<const CallExpr *> DefaultArgLocs;
+
+ // These should only exist in their fully-constructed form.
+ DiagnosticInfo() = delete;
+
+ DiagnosticInfo(const Expr *E)
+ : Loc(E->getBeginLoc()), Range(E->getSourceRange()) {}
+
+ DiagnosticInfo(const DiagnosticInfo &) = default;
+ DiagnosticInfo(DiagnosticInfo &&) = default;
+
+ DiagnosticInfo &operator=(const DiagnosticInfo &) = default;
+ DiagnosticInfo &operator=(DiagnosticInfo &&) = default;
+ };
+
+ Sema &S;
+ llvm::SmallVector<DiagnosticInfo, 8> 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<ImplicitCastExpr>(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<unsigned>(DiagnosticsToEmit.size())};
+ }
+
+ MutableArrayRef<DiagnosticInfo>
+ 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());
+ }
+
+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;
+ }
+
+ 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->getBeginLoc();
+ Range = E->getSourceRange();
+ assert(!Loc.isInvalid());
+ }
+ S.Diag(Loc, diag::err_non_general_regs_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) {
@@ -7860,6 +8086,9 @@
CheckCompletedExpr(FullExpr.get(), CC, IsConstexpr);
+ if (!IsConstexpr && getLangOpts().GeneralRegsOnly)
+ 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: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -8823,6 +8823,19 @@
// Finally, we know we have the right number of parameters, install them.
NewFD->setParams(Params);
+ if (getLangOpts().GeneralRegsOnly &&
+ D.getFunctionDefinitionKind() == FDK_Definition) {
+ if (typeHasFloatingOrVectorComponent(NewFD->getReturnType())) {
+ SourceRange Range = NewFD->getReturnTypeSourceRange();
+ Diag(Range.getBegin(), diag::err_non_general_regs_disabled) << Range;
+ }
+
+ for (const ParmVarDecl *PVD : NewFD->parameters())
+ if (typeHasFloatingOrVectorComponent(PVD->getType()))
+ Diag(PVD->getBeginLoc(), diag::err_non_general_regs_disabled)
+ << PVD->getSourceRange();
+ }
+
if (D.getDeclSpec().isNoreturnSpecified())
NewFD->addAttr(
::new(Context) C11NoReturnAttr(D.getDeclSpec().getNoreturnSpecLoc(),
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -2504,6 +2504,9 @@
if (Args.hasArg(OPT_pthread))
Opts.POSIXThreads = 1;
+ if (Args.hasArg(OPT_general_regs_only))
+ Opts.GeneralRegsOnly = 1;
+
// The value-visibility mode defaults to "default".
if (Arg *visOpt = Args.getLastArg(OPT_fvisibility)) {
Opts.setValueVisibilityMode(parseVisibility(visOpt, Args, Diags));
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -1387,6 +1387,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 {
RenderARMABI(Triple, Args, CmdArgs);
@@ -1420,9 +1438,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);
if (Args.getLastArg(options::OPT_mcmse))
CmdArgs.push_back("-mcmse");
@@ -1579,9 +1595,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);
RenderAArch64ABI(Triple, Args, CmdArgs);
Index: clang/lib/Driver/ToolChains/Arch/AArch64.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Arch/AArch64.cpp
+++ clang/lib/Driver/ToolChains/Arch/AArch64.cpp
@@ -192,6 +192,9 @@
Features.push_back("-fp-armv8");
Features.push_back("-crypto");
Features.push_back("-neon");
+ // FIXME: Ideally, we'd also like to pass a `+general-regs-only` feature,
+ // and do extra checking in the backend to be sure that we haven't
+ // accidentally introduced any FP ops.
}
if (Arg *A = Args.getLastArg(options::OPT_mtp_mode_EQ)) {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -10732,6 +10732,10 @@
unsigned ByteNo) const;
private:
+ // Tests to see if the given type is or contains a float or vector, as defined
+ // by -mgeneral-regs-only.
+ static bool typeHasFloatingOrVectorComponent(QualType Ty);
+
void CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
const ArraySubscriptExpr *ASE=nullptr,
bool AllowOnePastEnd=true, bool IndexNegated=false);
Index: clang/include/clang/Driver/CC1Options.td
===================================================================
--- clang/include/clang/Driver/CC1Options.td
+++ clang/include/clang/Driver/CC1Options.td
@@ -227,6 +227,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: clang/include/clang/Basic/LangOptions.def
===================================================================
--- clang/include/clang/Basic/LangOptions.def
+++ clang/include/clang/Basic/LangOptions.def
@@ -140,6 +140,7 @@
LANGOPT(Coroutines , 1, 0, "C++20 coroutines")
LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods")
LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments")
+LANGOPT(GeneralRegsOnly , 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: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -6159,6 +6159,11 @@
def ext_freestanding_complex : Extension<
"complex numbers are an extension in a freestanding C99 implementation">;
+def err_non_general_regs_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: clang/docs/UsersManual.rst
===================================================================
--- clang/docs/UsersManual.rst
+++ clang/docs/UsersManual.rst
@@ -1339,7 +1339,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]
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits