george.burgess.iv updated this revision to Diff 200388.
george.burgess.iv added a comment.
Rebased


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/Driver/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,130 @@
+// 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 {
+  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<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
Index: clang/test/Sema/aarch64-mgeneral_regs_only.c
===================================================================
--- /dev/null
+++ clang/test/Sema/aarch64-mgeneral_regs_only.c
@@ -0,0 +1,253 @@
+// 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: clang/test/Driver/aarch64-mgeneral_regs_only.c
===================================================================
--- clang/test/Driver/aarch64-mgeneral_regs_only.c
+++ clang/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: clang/test/CodeGen/aarch64-mgeneral_regs_only.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/aarch64-mgeneral_regs_only.c
@@ -0,0 +1,68 @@
+// 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: 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,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<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());
+  }
+
+  // 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<ImplicitCastExpr>(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->getBeginLoc();
+        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) {
@@ -7860,6 +8097,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: 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().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->getBeginLoc(), diag::err_non_general_ops_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.GeneralOpsOnly = 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);
 }
 
 void Clang::RenderTargetOptions(const llvm::Triple &EffectiveTriple,
@@ -1576,9 +1592,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
@@ -189,9 +189,7 @@
     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 (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
@@ -10729,6 +10729,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(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: 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_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: 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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to