tbaeder updated this revision to Diff 469166.
tbaeder added a comment.
Another (and even longer) version.
Introduce special opcodes for floating point operations and pass the rounding
mode to them. Also create special int->float and float->int casts so we can
handle that properly.
This makes `clang/tests/SemaCXX/rouding-math.cpp` work. I've only enabled one
of the two RUN lines though. The other one fails because we don't handle
`CXXNewExpr`s yet.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D134859/new/
https://reviews.llvm.org/D134859
Files:
clang/lib/AST/CMakeLists.txt
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/Interp/Boolean.h
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/Context.cpp
clang/lib/AST/Interp/Descriptor.cpp
clang/lib/AST/Interp/Floating.cpp
clang/lib/AST/Interp/Floating.h
clang/lib/AST/Interp/Integral.h
clang/lib/AST/Interp/Interp.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/InterpStack.h
clang/lib/AST/Interp/InterpState.h
clang/lib/AST/Interp/Opcodes.td
clang/lib/AST/Interp/PrimType.h
clang/lib/AST/Interp/Primitives.h
clang/lib/AST/Interp/State.h
clang/test/AST/Interp/literals.cpp
clang/test/SemaCXX/rounding-math.cpp
Index: clang/test/SemaCXX/rounding-math.cpp
===================================================================
--- clang/test/SemaCXX/rounding-math.cpp
+++ clang/test/SemaCXX/rounding-math.cpp
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -triple x86_64-linux -verify=norounding -Wno-unknown-pragmas %s
// RUN: %clang_cc1 -triple x86_64-linux -verify=rounding %s -frounding-math -Wno-unknown-pragmas
+// RUN: %clang_cc1 -triple x86_64-linux -verify=rounding %s -frounding-math -fexperimental-new-constant-interpreter -Wno-unknown-pragmas
// rounding-no-diagnostics
#define fold(x) (__builtin_constant_p(x) ? (x) : (x))
Index: clang/test/AST/Interp/literals.cpp
===================================================================
--- clang/test/AST/Interp/literals.cpp
+++ clang/test/AST/Interp/literals.cpp
@@ -322,3 +322,42 @@
// expected-error{{wide character literals may not contain multiple characters}}
#pragma clang diagnostic pop
};
+
+namespace floats {
+ constexpr int i = 2;
+ constexpr float f = 1.0f;
+ static_assert(f == 1.0f, "");
+
+ constexpr float f2 = 1u * f;
+ static_assert(f2 == 1.0f, "");
+
+ static_assert(1.0f + 3u == 4, "");
+ static_assert(4.0f / 1.0f == 4, "");
+ static_assert(10.0f * false == 0, "");
+
+ constexpr float floats[] = {1.0f, 2.0f, 3.0f, 4.0f};
+
+ constexpr float m = 5.0f / 0.0f; // ref-error {{must be initialized by a constant expression}} \
+ // ref-note {{division by zero}} \
+ // expected-error {{must be initialized by a constant expression}} \
+ // expected-note {{division by zero}}
+
+ static_assert(~2.0f == 3, ""); // ref-error {{invalid argument type 'float' to unary expression}} \
+ // expected-error {{invalid argument type 'float' to unary expression}}
+
+ /// Initialized by a double.
+ constexpr float df = 0.0;
+ /// The other way around.
+ constexpr double fd = 0.0f;
+
+ static_assert(0.0f == -0.0f, "");
+
+ const int k = 3 * (1.0f / 3.0f);
+ static_assert(k == 1, "");
+
+ constexpr bool b = 1.0;
+ static_assert(b, "");
+
+ constexpr double db = true;
+ static_assert(db == 1.0, "");
+};
Index: clang/lib/AST/Interp/State.h
===================================================================
--- clang/lib/AST/Interp/State.h
+++ clang/lib/AST/Interp/State.h
@@ -71,6 +71,7 @@
virtual unsigned getCallStackDepth() = 0;
public:
+ State() : InConstantContext(false) {}
// Diagnose that the evaluation could not be folded (FF => FoldFailure)
OptionalDiagnostic
FFDiag(SourceLocation Loc,
@@ -118,6 +119,10 @@
const LangOptions &getLangOpts() const;
+ /// Whether or not we're in a context where the front end requires a
+ /// constant value.
+ bool InConstantContext;
+
private:
void addCallStack(unsigned Limit);
Index: clang/lib/AST/Interp/Primitives.h
===================================================================
--- /dev/null
+++ clang/lib/AST/Interp/Primitives.h
@@ -0,0 +1,36 @@
+//===------ Primitives.h - Types for the constexpr VM -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Utilities and helper functions for all primitive types:
+// - Integral
+// - Floating
+// - Boolean
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_PRIMITIVES_H
+#define LLVM_CLANG_AST_INTERP_PRIMITIVES_H
+
+#include "clang/AST/ComparisonCategories.h"
+
+namespace clang {
+namespace interp {
+
+/// Helper to compare two comparable types.
+template <typename T> ComparisonCategoryResult Compare(const T &X, const T &Y) {
+ if (X < Y)
+ return ComparisonCategoryResult::Less;
+ if (X > Y)
+ return ComparisonCategoryResult::Greater;
+ return ComparisonCategoryResult::Equal;
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif
Index: clang/lib/AST/Interp/PrimType.h
===================================================================
--- clang/lib/AST/Interp/PrimType.h
+++ clang/lib/AST/Interp/PrimType.h
@@ -13,11 +13,12 @@
#ifndef LLVM_CLANG_AST_INTERP_TYPE_H
#define LLVM_CLANG_AST_INTERP_TYPE_H
+#include "Boolean.h"
+#include "Floating.h"
+#include "Integral.h"
#include <climits>
#include <cstddef>
#include <cstdint>
-#include "Boolean.h"
-#include "Integral.h"
namespace clang {
namespace interp {
@@ -35,6 +36,7 @@
PT_Sint64,
PT_Uint64,
PT_Bool,
+ PT_Float,
PT_Ptr,
};
@@ -48,6 +50,7 @@
template <> struct PrimConv<PT_Uint32> { using T = Integral<32, false>; };
template <> struct PrimConv<PT_Sint64> { using T = Integral<64, true>; };
template <> struct PrimConv<PT_Uint64> { using T = Integral<64, false>; };
+template <> struct PrimConv<PT_Float> { using T = Floating; };
template <> struct PrimConv<PT_Bool> { using T = Boolean; };
template <> struct PrimConv<PT_Ptr> { using T = Pointer; };
@@ -70,6 +73,7 @@
case PT_Uint32:
case PT_Sint64:
case PT_Uint64:
+ case PT_Float:
return true;
default:
return false;
@@ -94,6 +98,7 @@
TYPE_SWITCH_CASE(PT_Uint32, B) \
TYPE_SWITCH_CASE(PT_Sint64, B) \
TYPE_SWITCH_CASE(PT_Uint64, B) \
+ TYPE_SWITCH_CASE(PT_Float, B) \
TYPE_SWITCH_CASE(PT_Bool, B) \
TYPE_SWITCH_CASE(PT_Ptr, B) \
} \
Index: clang/lib/AST/Interp/Opcodes.td
===================================================================
--- clang/lib/AST/Interp/Opcodes.td
+++ clang/lib/AST/Interp/Opcodes.td
@@ -25,6 +25,7 @@
def Uint32 : Type;
def Sint64 : Type;
def Uint64 : Type;
+def Float : Type;
def Ptr : Type;
//===----------------------------------------------------------------------===//
@@ -40,12 +41,15 @@
def ArgUint32 : ArgType { let Name = "uint32_t"; }
def ArgSint64 : ArgType { let Name = "int64_t"; }
def ArgUint64 : ArgType { let Name = "uint64_t"; }
+def ArgFloat : ArgType { let Name = "Floating"; }
def ArgBool : ArgType { let Name = "bool"; }
def ArgFunction : ArgType { let Name = "const Function *"; }
def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; }
def ArgRecordField : ArgType { let Name = "const Record::Field *"; }
def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; }
+def ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; }
+def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; }
//===----------------------------------------------------------------------===//
// Classes of types instructions operate on.
@@ -55,18 +59,21 @@
list<Type> Types;
}
-def NumberTypeClass : TypeClass {
+def IntegerTypeClass : TypeClass {
let Types = [Sint8, Uint8, Sint16, Uint16, Sint32,
Uint32, Sint64, Uint64];
}
-def IntegerTypeClass : TypeClass {
- let Types = [Sint8, Uint8, Sint16, Uint16, Sint32,
- Uint32, Sint64, Uint64];
+def NumberTypeClass : TypeClass {
+ let Types = !listconcat(IntegerTypeClass.Types, [Float]);
+}
+
+def FloatTypeClass : TypeClass {
+ let Types = [Float];
}
def AluTypeClass : TypeClass {
- let Types = !listconcat(NumberTypeClass.Types, [Bool]);
+ let Types = !listconcat(IntegerTypeClass.Types, [Bool]);
}
def PtrTypeClass : TypeClass {
@@ -77,12 +84,16 @@
let Types = [Bool];
}
+def NonPtrTypeClass : TypeClass {
+ let Types = !listconcat(IntegerTypeClass.Types, [Bool], [Float]);
+}
+
def AllTypeClass : TypeClass {
- let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types);
+ let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types, FloatTypeClass.Types);
}
def ComparableTypeClass : TypeClass {
- let Types = !listconcat(AluTypeClass.Types, [Ptr]);
+ let Types = !listconcat(AluTypeClass.Types, [Ptr], [Float]);
}
class SingletonTypeClass<Type Ty> : TypeClass {
@@ -109,6 +120,11 @@
let HasGroup = 1;
}
+class FloatOpcode : Opcode {
+ let Types = [];
+ let Args = [ArgRoundingMode];
+}
+
class IntegerOpcode : Opcode {
let Types = [IntegerTypeClass];
let HasGroup = 1;
@@ -194,6 +210,7 @@
def ConstUint32 : ConstOpcode<Uint32, ArgUint32>;
def ConstSint64 : ConstOpcode<Sint64, ArgSint64>;
def ConstUint64 : ConstOpcode<Uint64, ArgUint64>;
+def ConstFloat : ConstOpcode<Float, ArgFloat>;
def ConstBool : ConstOpcode<Bool, ArgBool>;
// [] -> [Integer]
@@ -391,9 +408,15 @@
//===----------------------------------------------------------------------===//
// [Pointer, Integral] -> [Pointer]
-def AddOffset : AluOpcode;
+def AddOffset : Opcode {
+ let Types = [IntegerTypeClass];
+ let HasGroup = 1;
+}
// [Pointer, Integral] -> [Pointer]
-def SubOffset : AluOpcode;
+def SubOffset : Opcode {
+ let Types = [IntegerTypeClass];
+ let HasGroup = 1;
+}
// Pointer, Pointer] - [Integral]
def SubPtr : Opcode {
@@ -406,19 +429,18 @@
//===----------------------------------------------------------------------===//
// [Real, Real] -> [Real]
-def Sub : AluOpcode;
-def Add : AluOpcode;
-def Mul : AluOpcode;
-def Rem : Opcode {
- let Types = [NumberTypeClass];
- let HasGroup = 1;
-}
+def Add : AluOpcode;
+def Addf : FloatOpcode;
+def Sub : AluOpcode;
+def Subf : FloatOpcode;
+def Mul : AluOpcode;
+def Mulf : FloatOpcode;
+def Rem : IntegerOpcode;
+def Div : IntegerOpcode;
+def Divf : FloatOpcode;
+
def BitAnd : IntegerOpcode;
def BitOr : IntegerOpcode;
-def Div : Opcode {
- let Types = [NumberTypeClass];
- let HasGroup = 1;
-}
//===----------------------------------------------------------------------===//
// Unary operators.
@@ -432,20 +454,19 @@
// [Real] -> [Real]
def Neg: Opcode {
- let Types = [AluTypeClass];
+ let Types = [NonPtrTypeClass];
let HasGroup = 1;
}
// [Real] -> [Real]
def Comp: Opcode {
- let Types = [NumberTypeClass];
+ let Types = [IntegerTypeClass];
let HasGroup = 1;
}
//===----------------------------------------------------------------------===//
-// Cast.
+// Cast, CastFP.
//===----------------------------------------------------------------------===//
-// TODO: Expand this to handle casts between more types.
def FromCastTypeClass : TypeClass {
let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
@@ -460,6 +481,25 @@
let HasGroup = 1;
}
+def CastFP : Opcode {
+ let Types = [];
+ let Args = [ArgFltSemantics, ArgRoundingMode];
+}
+
+// Cast an integer to a floating type
+def CastIntegralFloating : Opcode {
+ let Types = [AluTypeClass];
+ let Args = [ArgFltSemantics];
+ let HasGroup = 1;
+}
+
+// Cast a floating to an integer type
+def CastFloatingIntegral : Opcode {
+ let Types = [AluTypeClass];
+ let Args = [ArgRoundingMode];
+ let HasGroup = 1;
+}
+
//===----------------------------------------------------------------------===//
// Comparison opcodes.
//===----------------------------------------------------------------------===//
Index: clang/lib/AST/Interp/InterpState.h
===================================================================
--- clang/lib/AST/Interp/InterpState.h
+++ clang/lib/AST/Interp/InterpState.h
@@ -65,6 +65,7 @@
bool noteUndefinedBehavior() override {
return Parent.noteUndefinedBehavior();
}
+ bool inConstantContext() const { return Parent.InConstantContext; }
bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); }
void setActiveDiagnostic(bool Flag) override {
Parent.setActiveDiagnostic(Flag);
Index: clang/lib/AST/Interp/InterpStack.h
===================================================================
--- clang/lib/AST/Interp/InterpStack.h
+++ clang/lib/AST/Interp/InterpStack.h
@@ -160,6 +160,8 @@
else if constexpr (std::is_same_v<T, uint64_t> ||
std::is_same_v<T, Integral<64, false>>)
return PT_Uint64;
+ else if constexpr (std::is_same_v<T, Floating>)
+ return PT_Float;
llvm_unreachable("unknown type push()'ed into InterpStack");
}
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -91,6 +91,11 @@
/// Checks if a method is pure virtual.
bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);
+/// Checks if the result is a floating-point operation is valid
+/// in the current context.
+bool CheckFloatResult(InterpState &S, CodePtr OpPC, APFloat::opStatus Status,
+ const Floating &Result);
+
/// Checks if Div/Rem operation on LHS and RHS is valid.
template <typename T>
bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
@@ -161,6 +166,16 @@
return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
}
+inline bool Addf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &RHS = S.Stk.pop<Floating>();
+ const Floating &LHS = S.Stk.pop<Floating>();
+
+ Floating Result;
+ auto Status = Floating::add(LHS, RHS, RM, &Result);
+ S.Stk.push<Floating>(Result);
+ return CheckFloatResult(S, OpPC, Status, Result);
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Sub(InterpState &S, CodePtr OpPC) {
const T &RHS = S.Stk.pop<T>();
@@ -169,6 +184,16 @@
return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
}
+inline bool Subf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &RHS = S.Stk.pop<Floating>();
+ const Floating &LHS = S.Stk.pop<Floating>();
+
+ Floating Result;
+ auto Status = Floating::sub(LHS, RHS, RM, &Result);
+ S.Stk.push<Floating>(Result);
+ return CheckFloatResult(S, OpPC, Status, Result);
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Mul(InterpState &S, CodePtr OpPC) {
const T &RHS = S.Stk.pop<T>();
@@ -177,6 +202,15 @@
return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
}
+inline bool Mulf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &RHS = S.Stk.pop<Floating>();
+ const Floating &LHS = S.Stk.pop<Floating>();
+
+ Floating Result;
+ auto Status = Floating::mul(LHS, RHS, RM, &Result);
+ S.Stk.push<Floating>(Result);
+ return CheckFloatResult(S, OpPC, Status, Result);
+}
/// 1) Pops the RHS from the stack.
/// 2) Pops the LHS from the stack.
/// 3) Pushes 'LHS & RHS' on the stack
@@ -251,6 +285,19 @@
return false;
}
+inline bool Divf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &RHS = S.Stk.pop<Floating>();
+ const Floating &LHS = S.Stk.pop<Floating>();
+
+ if (!CheckDivRem(S, OpPC, LHS, RHS))
+ return false;
+
+ Floating Result;
+ auto Status = Floating::div(LHS, RHS, RM, &Result);
+ S.Stk.push<Floating>(Result);
+ return CheckFloatResult(S, OpPC, Status, Result);
+}
+
//===----------------------------------------------------------------------===//
// Inv
//===----------------------------------------------------------------------===//
@@ -1014,6 +1061,40 @@
return true;
}
+/// 1) Pops a Floating from the stack.
+/// 2) Pushes a new floating on the stack that uses the given semantics.
+/// Not templated, so implemented in Interp.cpp.
+bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem,
+ llvm::RoundingMode RM);
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastIntegralFloating(InterpState &S, CodePtr OpPC,
+ const llvm::fltSemantics *Sem) {
+ const T &From = S.Stk.pop<T>();
+ APSInt FromAP = From.toAPSInt();
+ Floating Result;
+
+ if (!Floating::fromIntegral(FromAP, *Sem, Result))
+ return false;
+
+ S.Stk.push<Floating>(Result);
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastFloatingIntegral(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &F = S.Stk.pop<Floating>();
+ APSInt Result(std::max(8u, T::bitWidth()), T::isSigned());
+ if (!F.convertToInteger(RM, Result)) {
+ return false;
+ }
+
+ llvm::errs() << F << " to int: " << Result << "\n";
+
+ S.Stk.push<T>(T(Result));
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Zero, Nullptr
//===----------------------------------------------------------------------===//
Index: clang/lib/AST/Interp/Interp.cpp
===================================================================
--- clang/lib/AST/Interp/Interp.cpp
+++ clang/lib/AST/Interp/Interp.cpp
@@ -399,6 +399,50 @@
return false;
}
+bool CheckFloatResult(InterpState &S, CodePtr OpPC, APFloat::opStatus Status,
+ const Floating &Result) {
+ // In a constant context, assume that any dynamic rounding mode or FP
+ // exception state matches the default floating-point environment.
+ if (S.inConstantContext())
+ return true;
+
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ FPOptions FPO = E.asExpr()->getFPFeaturesInEffect(S.Ctx.getLangOpts());
+
+ if ((Status & APFloat::opInexact) &&
+ FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
+ // Inexact result means that it depends on rounding mode. If the requested
+ // mode is dynamic, the evaluation cannot be made in compile time.
+ S.FFDiag(E, diag::note_constexpr_dynamic_rounding);
+ return false;
+ }
+
+ if ((Status != APFloat::opOK) &&
+ (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic ||
+ FPO.getExceptionMode() != LangOptions::FPE_Ignore ||
+ FPO.getAllowFEnvAccess())) {
+ S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
+ return false;
+ }
+
+ if ((Status & APFloat::opStatus::opInvalidOp) &&
+ FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
+ // There is no usefully definable result.
+ S.FFDiag(E);
+ return false;
+ }
+
+ return true;
+}
+
+bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem,
+ llvm::RoundingMode RM) {
+ Floating F = S.Stk.pop<Floating>();
+ Floating Result = F.toSemantics(Sem, RM);
+ S.Stk.push<Floating>(Result);
+ return true;
+}
+
bool Interpret(InterpState &S, APValue &Result) {
// The current stack frame when we started Interpret().
// This is being used by the ops to determine wheter
Index: clang/lib/AST/Interp/Integral.h
===================================================================
--- clang/lib/AST/Interp/Integral.h
+++ clang/lib/AST/Interp/Integral.h
@@ -21,33 +21,14 @@
#include <cstddef>
#include <cstdint>
+#include "Primitives.h"
+
namespace clang {
namespace interp {
using APInt = llvm::APInt;
using APSInt = llvm::APSInt;
-/// Helper to compare two comparable types.
-template <typename T>
-ComparisonCategoryResult Compare(const T &X, const T &Y) {
- if (X < Y)
- return ComparisonCategoryResult::Less;
- if (X > Y)
- return ComparisonCategoryResult::Greater;
- return ComparisonCategoryResult::Equal;
-}
-
-// Helper structure to select the representation.
-template <unsigned Bits, bool Signed> struct Repr;
-template <> struct Repr<8, false> { using Type = uint8_t; };
-template <> struct Repr<16, false> { using Type = uint16_t; };
-template <> struct Repr<32, false> { using Type = uint32_t; };
-template <> struct Repr<64, false> { using Type = uint64_t; };
-template <> struct Repr<8, true> { using Type = int8_t; };
-template <> struct Repr<16, true> { using Type = int16_t; };
-template <> struct Repr<32, true> { using Type = int32_t; };
-template <> struct Repr<64, true> { using Type = int64_t; };
-
/// Wrapper around numeric types.
///
/// These wrappers are required to shared an interface between APSint and
@@ -56,6 +37,16 @@
template <unsigned Bits, bool Signed> class Integral final {
private:
template <unsigned OtherBits, bool OtherSigned> friend class Integral;
+ // Helper structure to select the representation.
+ template <unsigned ReprBits, bool ReprSigned> struct Repr;
+ template <> struct Repr<8, false> { using Type = uint8_t; };
+ template <> struct Repr<16, false> { using Type = uint16_t; };
+ template <> struct Repr<32, false> { using Type = uint32_t; };
+ template <> struct Repr<64, false> { using Type = uint64_t; };
+ template <> struct Repr<8, true> { using Type = int8_t; };
+ template <> struct Repr<16, true> { using Type = int16_t; };
+ template <> struct Repr<32, true> { using Type = int32_t; };
+ template <> struct Repr<64, true> { using Type = int64_t; };
// The primitive representing the integral.
using ReprT = typename Repr<Bits, Signed>::Type;
@@ -102,6 +93,7 @@
explicit operator unsigned() const { return V; }
explicit operator int64_t() const { return V; }
explicit operator uint64_t() const { return V; }
+ explicit operator double() const { return V; }
APSInt toAPSInt() const {
return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed);
Index: clang/lib/AST/Interp/Floating.h
===================================================================
--- /dev/null
+++ clang/lib/AST/Interp/Floating.h
@@ -0,0 +1,153 @@
+//===--- Floating.h - Types for the constexpr VM ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the VM types and helpers operating on types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_FLOATING_H
+#define LLVM_CLANG_AST_INTERP_FLOATING_H
+
+#include "Primitives.h"
+#include "clang/AST/APValue.h"
+#include "llvm/ADT/APFloat.h"
+#include <limits>
+
+namespace clang {
+namespace interp {
+
+using APFloat = llvm::APFloat;
+using APSInt = llvm::APSInt;
+
+class Floating final {
+private:
+ // The underlying value storage.
+ APFloat F;
+
+ /// Construct a Floating from anything that is convertible to storage.
+ template <typename T> explicit Floating(T V) : F(V) {}
+
+public:
+ /// Zero-initializes a Floating.
+ Floating() : F(0.0f) {}
+ Floating(APFloat F) : F(F) {}
+
+ // Static constructors for special floating point values.
+ static Floating getInf(const llvm::fltSemantics &Sem) {
+ return Floating(APFloat::getInf(Sem));
+ }
+ static Floating zero() { return Floating(0.0f); }
+
+ bool operator<(Floating RHS) const { return F < RHS.F; }
+ bool operator>(Floating RHS) const { return F > RHS.F; }
+ bool operator<=(Floating RHS) const { return F <= RHS.F; }
+ bool operator>=(Floating RHS) const { return F >= RHS.F; }
+ bool operator==(Floating RHS) const { return F == RHS.F; }
+ bool operator!=(Floating RHS) const { return F != RHS.F; }
+ Floating operator-() const { return Floating(-F); }
+
+ bool convertToInteger(llvm::RoundingMode RM, APSInt &Result) const {
+ bool IsExact;
+ auto Status =
+ F.convertToInteger(Result, llvm::APFloat::rmTowardZero, &IsExact);
+ return Status == llvm::APFloatBase::opOK;
+ }
+
+ Floating toSemantics(const llvm::fltSemantics *Sem,
+ llvm::RoundingMode RM) const {
+ APFloat Copy = F;
+ bool LosesInfo;
+ Copy.convert(*Sem, RM, &LosesInfo);
+ (void)LosesInfo;
+ return Floating(Copy);
+ }
+ APSInt toAPSInt(unsigned NumBits = 0) const {
+ return APSInt(F.bitcastToAPInt());
+ }
+ APValue toAPValue() const { return APValue(F); }
+ void print(llvm::raw_ostream &OS) const { F.print(OS); }
+
+ unsigned bitWidth() const { return F.semanticsSizeInBits(F.getSemantics()); }
+
+ bool isSigned() const { return true; }
+ bool isNegative() const { return F.isNegative(); }
+ bool isPositive() const { return !F.isNegative(); }
+ bool isZero() const { return F.isZero(); }
+ bool isMin() const { return F.isSmallest(); }
+ bool isMinusOne() const { return F.isExactlyValue(-1.0); }
+ bool isNan() const { return F.isNaN(); }
+
+ ComparisonCategoryResult compare(const Floating &RHS) const {
+ return Compare(F, RHS.F);
+ }
+
+ // TODO: Properly implement this(?)
+ Floating truncate(unsigned TruncBits) const { return Floating(this->F); }
+
+ template <typename ValT> static Floating from(ValT Value) {
+ if constexpr (std::is_integral<ValT>::value ||
+ std::is_floating_point<ValT>::value)
+ return Floating(Value);
+ else if constexpr (std::is_same_v<ValT, Floating>) {
+ return Floating(Value.F);
+ } else
+ return Floating::from(static_cast<double>(Value));
+ }
+
+ static bool fromIntegral(APSInt Val, const llvm::fltSemantics &Sem,
+ Floating &Result) {
+ APFloat F = APFloat(Sem, 1);
+ APFloat::opStatus Status =
+ F.convertFromAPInt(Val, Val.isSigned(), APFloat::rmNearestTiesToEven);
+ Result = Floating(F);
+ return Status == APFloat::opOK;
+ }
+
+ template <typename T> static Floating from(T Value, unsigned NumBits) {
+ return Floating(Value);
+ }
+
+ // -------
+
+ static APFloat::opStatus add(Floating A, Floating B, llvm::RoundingMode RM,
+ Floating *R) {
+ *R = Floating(A.F);
+ return R->F.add(B.F, RM);
+ }
+
+ static APFloat::opStatus sub(Floating A, Floating B, llvm::RoundingMode RM,
+ Floating *R) {
+ *R = Floating(A.F);
+ return R->F.subtract(B.F, RM);
+ }
+
+ static APFloat::opStatus mul(Floating A, Floating B, llvm::RoundingMode RM,
+ Floating *R) {
+ *R = Floating(A.F);
+ return R->F.multiply(B.F, RM);
+ }
+
+ static APFloat::opStatus div(Floating A, Floating B, llvm::RoundingMode RM,
+ Floating *R) {
+ *R = Floating(A.F);
+ return R->F.divide(B.F, RM);
+ }
+
+ static bool neg(Floating A, Floating *R) {
+ *R = -A;
+ return false;
+ }
+};
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F);
+Floating getSwappedBytes(Floating F);
+
+} // namespace interp
+} // namespace clang
+
+#endif
Index: clang/lib/AST/Interp/Floating.cpp
===================================================================
--- /dev/null
+++ clang/lib/AST/Interp/Floating.cpp
@@ -0,0 +1,22 @@
+//===---- Floating.cpp - Support for floating point values ------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Floating.h"
+
+namespace clang {
+namespace interp {
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F) {
+ F.print(OS);
+ return OS;
+}
+
+Floating getSwappedBytes(Floating F) { return F; }
+
+} // namespace interp
+} // namespace clang
Index: clang/lib/AST/Interp/Descriptor.cpp
===================================================================
--- clang/lib/AST/Interp/Descriptor.cpp
+++ clang/lib/AST/Interp/Descriptor.cpp
@@ -162,6 +162,11 @@
}
static BlockCtorFn getCtorPrim(PrimType Type) {
+ // Floating types are special. They are primitives, but need their
+ // constructor called.
+ if (Type == PT_Float)
+ return ctorTy<PrimConv<PT_Float>::T>;
+
COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
}
Index: clang/lib/AST/Interp/Context.cpp
===================================================================
--- clang/lib/AST/Interp/Context.cpp
+++ clang/lib/AST/Interp/Context.cpp
@@ -113,6 +113,9 @@
if (T->isNullPtrType())
return PT_Ptr;
+ if (T->isFloatingType())
+ return PT_Float;
+
if (auto *AT = dyn_cast<AtomicType>(T))
return classify(AT->getValueType());
Index: clang/lib/AST/Interp/ByteCodeExprGen.h
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.h
+++ clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -68,6 +68,7 @@
// Expression visitors - result returned on interp stack.
bool VisitCastExpr(const CastExpr *E);
bool VisitIntegerLiteral(const IntegerLiteral *E);
+ bool VisitFloatingLiteral(const FloatingLiteral *E);
bool VisitParenExpr(const ParenExpr *E);
bool VisitBinaryOperator(const BinaryOperator *E);
bool VisitPointerArithBinOp(const BinaryOperator *E);
@@ -264,6 +265,15 @@
return T->getAsCXXRecordDecl();
}
+ llvm::RoundingMode getRoundingMode(const Expr *E) const {
+ FPOptions FPO = E->getFPFeaturesInEffect(Ctx.getLangOpts());
+
+ if (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic)
+ return llvm::RoundingMode::NearestTiesToEven;
+
+ return FPO.getRoundingMode();
+ }
+
protected:
/// Variable to storage mapping.
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -122,6 +122,41 @@
return this->emitGetPtrBase(ToBase->Offset, CE);
}
+ case CK_FloatingCast: {
+ if (!this->visit(SubExpr))
+ return false;
+ const auto *TargetSemantics =
+ &Ctx.getASTContext().getFloatTypeSemantics(CE->getType());
+ return this->emitCastFP(TargetSemantics, getRoundingMode(CE), CE);
+ }
+
+ case CK_IntegralToFloating: {
+ Optional<PrimType> FromT = classify(SubExpr->getType());
+ if (!FromT)
+ return false;
+
+ if (!this->visit(SubExpr))
+ return false;
+
+ const auto *TargetSemantics =
+ &Ctx.getASTContext().getFloatTypeSemantics(CE->getType());
+ return this->emitCastIntegralFloating(*FromT, TargetSemantics, CE);
+ }
+
+ case CK_FloatingToBoolean:
+ case CK_FloatingToIntegral: {
+ llvm::RoundingMode RM = getRoundingMode(CE);
+ Optional<PrimType> ToT = classify(CE->getType());
+
+ if (!ToT)
+ return false;
+
+ if (!this->visit(SubExpr))
+ return false;
+
+ return this->emitCastFloatingIntegral(*ToT, RM, CE);
+ }
+
case CK_ArrayToPointerDecay:
case CK_AtomicToNonAtomic:
case CK_ConstructorConversion:
@@ -165,6 +200,14 @@
return this->bail(LE);
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitFloatingLiteral(const FloatingLiteral *E) {
+ if (DiscardResult)
+ return true;
+
+ return this->emitConstFloat(E->getValue(), E);
+}
+
template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) {
return this->Visit(PE->getSubExpr());
@@ -225,14 +268,22 @@
case BO_GE:
return Discard(this->emitGE(*LT, BO));
case BO_Sub:
+ if (BO->getType()->isFloatingType())
+ return Discard(this->emitSubf(getRoundingMode(BO), BO));
return Discard(this->emitSub(*T, BO));
case BO_Add:
+ if (BO->getType()->isFloatingType())
+ return Discard(this->emitAddf(getRoundingMode(BO), BO));
return Discard(this->emitAdd(*T, BO));
case BO_Mul:
+ if (BO->getType()->isFloatingType())
+ return Discard(this->emitMulf(getRoundingMode(BO), BO));
return Discard(this->emitMul(*T, BO));
case BO_Rem:
return Discard(this->emitRem(*T, BO));
case BO_Div:
+ if (BO->getType()->isFloatingType())
+ return Discard(this->emitDivf(getRoundingMode(BO), BO));
return Discard(this->emitDiv(*T, BO));
case BO_Assign:
if (!this->emitStore(*T, BO))
@@ -557,6 +608,8 @@
return this->emitZeroUint64(E);
case PT_Ptr:
return this->emitNullPtr(E);
+ case PT_Float:
+ assert(false);
}
llvm_unreachable("unknown primitive type");
}
@@ -721,6 +774,7 @@
case PT_Bool:
return this->emitConstBool(Value.getBoolValue(), E);
case PT_Ptr:
+ case PT_Float:
llvm_unreachable("Invalid integral type");
break;
}
Index: clang/lib/AST/Interp/Boolean.h
===================================================================
--- clang/lib/AST/Interp/Boolean.h
+++ clang/lib/AST/Interp/Boolean.h
@@ -33,6 +33,7 @@
public:
/// Zero-initializes a boolean.
Boolean() : V(false) {}
+ Boolean(const APSInt I) : V(!I.isZero()) {}
bool operator<(Boolean RHS) const { return V < RHS.V; }
bool operator>(Boolean RHS) const { return V > RHS.V; }
@@ -55,6 +56,7 @@
explicit operator int64_t() const { return V; }
explicit operator uint64_t() const { return V; }
explicit operator bool() const { return V; }
+ explicit operator double() const { return V; }
APSInt toAPSInt() const {
return APSInt(APInt(1, static_cast<uint64_t>(V), false), true);
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -917,10 +917,6 @@
/// fold (not just why it's not strictly a constant expression)?
bool HasFoldFailureDiagnostic;
- /// Whether or not we're in a context where the front end requires a
- /// constant value.
- bool InConstantContext;
-
/// Whether we're checking that an expression is a potential constant
/// expression. If so, do not fail on constructs that could become constant
/// later on (such as a use of an undefined global).
@@ -976,8 +972,7 @@
BottomFrame(*this, SourceLocation(), nullptr, nullptr, CallRef()),
EvaluatingDecl((const ValueDecl *)nullptr),
EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false),
- HasFoldFailureDiagnostic(false), InConstantContext(false),
- EvalMode(Mode) {}
+ HasFoldFailureDiagnostic(false), EvalMode(Mode) {}
~EvalInfo() {
discardCleanups();
Index: clang/lib/AST/CMakeLists.txt
===================================================================
--- clang/lib/AST/CMakeLists.txt
+++ clang/lib/AST/CMakeLists.txt
@@ -72,6 +72,7 @@
Interp/EvalEmitter.cpp
Interp/Frame.cpp
Interp/Function.cpp
+ Interp/Floating.cpp
Interp/Interp.cpp
Interp/InterpBlock.cpp
Interp/InterpFrame.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits