[clang] [clang][bytecode] Implement fixed-point-to-int casts (PR #110417)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Sun Sep 29 02:37:54 PDT 2024


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/110417

And some cleanups around overflow handling.

>From 20b0c417f53bc3c2094ee2f37c0aae150d7a1932 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 29 Sep 2024 10:53:44 +0200
Subject: [PATCH] [clang][bytecode] Implement fixed-point-to-int casts

And some cleanups around overflow handling.
---
 clang/lib/AST/ByteCode/Boolean.h              |  1 +
 clang/lib/AST/ByteCode/Compiler.cpp           | 28 +++++-
 clang/lib/AST/ByteCode/Compiler.h             |  1 +
 clang/lib/AST/ByteCode/FixedPoint.h           | 19 ++--
 clang/lib/AST/ByteCode/Interp.cpp             | 13 +++
 clang/lib/AST/ByteCode/Interp.h               | 98 +++++++------------
 clang/lib/AST/ByteCode/Opcodes.td             |  4 +
 clang/test/AST/ByteCode/fixed-point.cpp       |  5 +
 .../Frontend/fixed_point_conversions_const.c  |  3 +
 9 files changed, 105 insertions(+), 67 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Boolean.h b/clang/lib/AST/ByteCode/Boolean.h
index f1914ddb9970dc..c568b557574e2b 100644
--- a/clang/lib/AST/ByteCode/Boolean.h
+++ b/clang/lib/AST/ByteCode/Boolean.h
@@ -30,6 +30,7 @@ class Boolean final {
 public:
   /// Zero-initializes a boolean.
   Boolean() : V(false) {}
+  Boolean(const llvm::APSInt &I) : V(!I.isZero()) {}
   explicit Boolean(bool V) : V(V) {}
 
   bool operator<(Boolean RHS) const { return V < RHS.V; }
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 44195a3dc33de4..db5b21f5b1aacd 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -697,6 +697,11 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
     const auto *TargetSemantics = &Ctx.getFloatSemantics(CE->getType());
     return this->emitCastFixedPointFloating(TargetSemantics, CE);
   }
+  case CK_FixedPointToIntegral: {
+    if (!this->visit(SubExpr))
+      return false;
+    return this->emitCastFixedPointIntegral(classifyPrim(CE->getType()), CE);
+  }
   case CK_FixedPointCast: {
     if (!this->visit(SubExpr))
       return false;
@@ -1562,6 +1567,25 @@ bool Compiler<Emitter>::VisitFixedPointBinOp(const BinaryOperator *E) {
   llvm_unreachable("unhandled binop opcode");
 }
 
+template <class Emitter>
+bool Compiler<Emitter>::VisitFixedPointUnaryOperator(const UnaryOperator *E) {
+  const Expr *SubExpr = E->getSubExpr();
+  assert(SubExpr->getType()->isFixedPointType());
+
+  switch (E->getOpcode()) {
+  case UO_Plus:
+    return this->delegate(SubExpr);
+  case UO_Minus:
+    if (!this->visit(SubExpr))
+      return false;
+    return this->emitNegFixedPoint(E);
+  default:
+    return false;
+  }
+
+  llvm_unreachable("Unhandled unary opcode");
+}
+
 template <class Emitter>
 bool Compiler<Emitter>::VisitImplicitValueInitExpr(
     const ImplicitValueInitExpr *E) {
@@ -3805,7 +3829,7 @@ bool Compiler<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
     return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
   case PT_FixedPoint: {
     auto Sem = Ctx.getASTContext().getFixedPointSemantics(E->getType());
-    return this->emitConstFixedPoint(FixedPoint::Zero(Sem), E);
+    return this->emitConstFixedPoint(FixedPoint::zero(Sem), E);
   }
     llvm_unreachable("Implement");
   }
@@ -5471,6 +5495,8 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
     return this->VisitComplexUnaryOperator(E);
   if (SubExpr->getType()->isVectorType())
     return this->VisitVectorUnaryOperator(E);
+  if (SubExpr->getType()->isFixedPointType())
+    return this->VisitFixedPointUnaryOperator(E);
   std::optional<PrimType> T = classify(SubExpr->getType());
 
   switch (E->getOpcode()) {
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 5349b184572b6e..22e078f3fe546f 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -133,6 +133,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
   bool VisitComplexBinOp(const BinaryOperator *E);
   bool VisitVectorBinOp(const BinaryOperator *E);
   bool VisitFixedPointBinOp(const BinaryOperator *E);
+  bool VisitFixedPointUnaryOperator(const UnaryOperator *E);
   bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E);
   bool VisitCallExpr(const CallExpr *E);
   bool VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinID);
diff --git a/clang/lib/AST/ByteCode/FixedPoint.h b/clang/lib/AST/ByteCode/FixedPoint.h
index 0fb4576c721266..c97a42401eaef3 100644
--- a/clang/lib/AST/ByteCode/FixedPoint.h
+++ b/clang/lib/AST/ByteCode/FixedPoint.h
@@ -33,17 +33,20 @@ class FixedPoint final {
       : V(APInt(0, 0ULL, false),
           llvm::FixedPointSemantics(0, 0, false, false, false)) {}
 
-  static FixedPoint Zero(llvm::FixedPointSemantics Sem) {
+  static FixedPoint zero(llvm::FixedPointSemantics Sem) {
     return FixedPoint(APInt(Sem.getWidth(), 0ULL, Sem.isSigned()), Sem);
   }
 
-  operator bool() const { return V.getBoolValue(); }
-  template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>>
-  explicit operator Ty() const {
-    // FIXME
-    return 0;
+  static FixedPoint from(const APSInt &I, llvm::FixedPointSemantics Sem,
+                         bool *Overflow) {
+    return FixedPoint(llvm::APFixedPoint::getFromIntValue(I, Sem, Overflow));
+  }
+  static FixedPoint from(const llvm::APFloat &I, llvm::FixedPointSemantics Sem,
+                         bool *Overflow) {
+    return FixedPoint(llvm::APFixedPoint::getFromFloatValue(I, Sem, Overflow));
   }
 
+  operator bool() const { return V.getBoolValue(); }
   void print(llvm::raw_ostream &OS) const { OS << V; }
 
   APValue toAPValue(const ASTContext &) const { return APValue(V); }
@@ -70,6 +73,10 @@ class FixedPoint final {
     return V.convertToFloat(*Sem);
   }
 
+  llvm::APSInt toInt(unsigned BitWidth, bool Signed, bool *Overflow) const {
+    return V.convertToInt(BitWidth, Signed, Overflow);
+  }
+
   std::string toDiagnosticString(const ASTContext &Ctx) const {
     return V.toString();
   }
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 798e0f3e96fa09..fd9a256843a0ec 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1393,6 +1393,19 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
   return false;
 }
 
+bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
+                              const FixedPoint &FP) {
+  const Expr *E = S.Current->getExpr(OpPC);
+  if (S.checkingForUndefinedBehavior()) {
+    S.getASTContext().getDiagnostics().Report(
+        E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
+        << FP.toDiagnosticString(S.getASTContext()) << E->getType();
+  }
+  S.CCEDiag(E, diag::note_constexpr_overflow)
+      << FP.toDiagnosticString(S.getASTContext()) << E->getType();
+  return S.noteUndefinedBehavior();
+}
+
 bool Interpret(InterpState &S, APValue &Result) {
   // The current stack frame when we started Interpret().
   // This is being used by the ops to determine wheter
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 89635f9c61e932..4d9d460c75174f 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -162,6 +162,15 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
              const CallExpr *CE);
 bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);
 
+template <typename T>
+static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) {
+  const Expr *E = S.Current->getExpr(OpPC);
+  S.CCEDiag(E, diag::note_constexpr_overflow) << SrcValue << E->getType();
+  return S.noteUndefinedBehavior();
+}
+bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
+                              const FixedPoint &FP);
+
 enum class ShiftDir { Left, Right };
 
 /// Checks if the shift operation is legal.
@@ -385,13 +394,10 @@ bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
         << Trunc << Type << E->getSourceRange();
   }
 
-  S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type;
-
-  if (!S.noteUndefinedBehavior()) {
+  if (!handleOverflow(S, OpPC, Value)) {
     S.Stk.pop<T>();
     return false;
   }
-
   return true;
 }
 
@@ -741,8 +747,7 @@ bool Neg(InterpState &S, CodePtr OpPC) {
     return true;
   }
 
-  S.CCEDiag(E, diag::note_constexpr_overflow) << NegatedValue << Type;
-  return S.noteUndefinedBehavior();
+  return handleOverflow(S, OpPC, NegatedValue);
 }
 
 enum class PushVal : bool {
@@ -804,8 +809,7 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
     return true;
   }
 
-  S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type;
-  return S.noteUndefinedBehavior();
+  return handleOverflow(S, OpPC, APResult);
 }
 
 /// 1) Pops a pointer from the stack
@@ -2170,18 +2174,8 @@ inline bool CastFixedPoint(InterpState &S, CodePtr OpPC, uint32_t FPS) {
   bool Overflow;
   FixedPoint Result = Source.toSemantics(TargetSemantics, &Overflow);
 
-  if (Overflow) {
-    const Expr *E = S.Current->getExpr(OpPC);
-    if (S.checkingForUndefinedBehavior()) {
-      S.getASTContext().getDiagnostics().Report(
-          E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
-          << Result.toDiagnosticString(S.getASTContext()) << E->getType();
-    }
-    S.CCEDiag(E, diag::note_constexpr_overflow)
-        << Result.toDiagnosticString(S.getASTContext()) << E->getType();
-    if (!S.noteUndefinedBehavior())
-      return false;
-  }
+  if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
+    return false;
 
   S.Stk.push<FixedPoint>(Result);
   return true;
@@ -2257,13 +2251,8 @@ static inline bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC,
   auto Status = F.convertToInteger(Result);
 
   // Float-to-Integral overflow check.
-  if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
-    const Expr *E = S.Current->getExpr(OpPC);
-    QualType Type = E->getType();
-
-    S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
-    return S.noteUndefinedBehavior();
-  }
+  if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite())
+    return handleOverflow(S, OpPC, F.getAPFloat());
 
   FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
   S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
@@ -2278,13 +2267,8 @@ static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC,
   auto Status = F.convertToInteger(Result);
 
   // Float-to-Integral overflow check.
-  if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
-    const Expr *E = S.Current->getExpr(OpPC);
-    QualType Type = E->getType();
-
-    S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
-    return S.noteUndefinedBehavior();
-  }
+  if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite())
+    return handleOverflow(S, OpPC, F.getAPFloat());
 
   FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
   S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
@@ -2347,20 +2331,10 @@ static inline bool CastIntegralFixedPoint(InterpState &S, CodePtr OpPC,
   std::memcpy(&Sem, &FPS, sizeof(Sem));
 
   bool Overflow;
-  llvm::APFixedPoint Result =
-      llvm::APFixedPoint::getFromIntValue(Int.toAPSInt(), Sem, &Overflow);
+  FixedPoint Result = FixedPoint::from(Int.toAPSInt(), Sem, &Overflow);
 
-  if (Overflow) {
-    const Expr *E = S.Current->getExpr(OpPC);
-    if (S.checkingForUndefinedBehavior()) {
-      S.getASTContext().getDiagnostics().Report(
-          E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
-          << Result.toString() << E->getType();
-    }
-    S.CCEDiag(E, diag::note_constexpr_overflow) << Result << E->getType();
-    if (!S.noteUndefinedBehavior())
-      return false;
-  }
+  if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
+    return false;
 
   S.Stk.push<FixedPoint>(Result);
   return true;
@@ -2374,20 +2348,10 @@ static inline bool CastFloatingFixedPoint(InterpState &S, CodePtr OpPC,
   std::memcpy(&Sem, &FPS, sizeof(Sem));
 
   bool Overflow;
-  llvm::APFixedPoint Result =
-      llvm::APFixedPoint::getFromFloatValue(Float.getAPFloat(), Sem, &Overflow);
+  FixedPoint Result = FixedPoint::from(Float.getAPFloat(), Sem, &Overflow);
 
-  if (Overflow) {
-    const Expr *E = S.Current->getExpr(OpPC);
-    if (S.checkingForUndefinedBehavior()) {
-      S.getASTContext().getDiagnostics().Report(
-          E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
-          << Result.toString() << E->getType();
-    }
-    S.CCEDiag(E, diag::note_constexpr_overflow) << Result << E->getType();
-    if (!S.noteUndefinedBehavior())
-      return false;
-  }
+  if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
+    return false;
 
   S.Stk.push<FixedPoint>(Result);
   return true;
@@ -2401,6 +2365,20 @@ static inline bool CastFixedPointFloating(InterpState &S, CodePtr OpPC,
   return true;
 }
 
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+static inline bool CastFixedPointIntegral(InterpState &S, CodePtr OpPC) {
+  const auto &Fixed = S.Stk.pop<FixedPoint>();
+
+  bool Overflow;
+  APSInt Int = Fixed.toInt(T::bitWidth(), T::isSigned(), &Overflow);
+
+  if (Overflow && !handleOverflow(S, OpPC, Int))
+    return false;
+
+  S.Stk.push<T>(Int);
+  return true;
+}
+
 static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) {
   const auto &Ptr = S.Stk.peek<Pointer>();
 
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 2955bc5cf8084c..601ff95d973a27 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -689,6 +689,10 @@ def CastFloatingFixedPoint : Opcode {
 def CastFixedPointFloating : Opcode {
   let Args = [ArgFltSemantics];
 }
+def CastFixedPointIntegral : Opcode {
+  let Types = [FixedSizeIntegralTypes];
+  let HasGroup = 1;
+}
 
 def PtrPtrCast : Opcode {
   let Args = [ArgBool];
diff --git a/clang/test/AST/ByteCode/fixed-point.cpp b/clang/test/AST/ByteCode/fixed-point.cpp
index d515b7fe1594a9..31c4008fd4df12 100644
--- a/clang/test/AST/ByteCode/fixed-point.cpp
+++ b/clang/test/AST/ByteCode/fixed-point.cpp
@@ -26,6 +26,11 @@ namespace IntToFixedPointCast {
   static_assert(sf == -1);
 }
 
+namespace FixedPointToIntCasts {
+  constexpr int I = A;
+  static_assert(I == -13);
+}
+
 namespace FloatToFixedPointCast {
   constexpr _Fract sf = 1.0; // both-error {{must be initialized by a constant expression}} \
                              // both-note {{outside the range of representable values of type 'const _Fract'}}
diff --git a/clang/test/Frontend/fixed_point_conversions_const.c b/clang/test/Frontend/fixed_point_conversions_const.c
index e6e89ded534fe1..889486e5eb8066 100644
--- a/clang/test/Frontend/fixed_point_conversions_const.c
+++ b/clang/test/Frontend/fixed_point_conversions_const.c
@@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED
 // RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s --check-prefixes=CHECK,UNSIGNED
 
+// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK,SIGNED
+// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK,UNSIGNED
+
 // Between different fixed point types
 short _Accum sa_const = 2.5hk;
 // CHECK-DAG: @sa_const  = {{.*}}global i16 320, align 2



More information about the cfe-commits mailing list