Index: clang/lib/AST/Interp/Boolean.h =================================================================== --- clang/lib/AST/Interp/Boolean.h +++ clang/lib/AST/Interp/Boolean.h @@ -56,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 float() const { return V; } APSInt toAPSInt() const { return APSInt(APInt(1, static_cast(V), false), true); 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 VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -118,6 +118,8 @@ return this->Visit(SubExpr); case CK_IntegralToBoolean: + case CK_FloatingToIntegral: + case CK_IntegralToFloating: case CK_IntegralCast: { Optional FromT = classify(SubExpr->getType()); Optional ToT = classify(CE->getType()); @@ -151,6 +153,14 @@ return this->bail(LE); } +template +bool ByteCodeExprGen::VisitFloatingLiteral(const FloatingLiteral *E) { + if (DiscardResult) + return true; + + return this->emitConstFloat32(E->getValue().convertToFloat(), E); +} + template bool ByteCodeExprGen::VisitParenExpr(const ParenExpr *PE) { return this->Visit(PE->getSubExpr()); @@ -418,6 +428,8 @@ return this->emitZeroUint64(E); case PT_Ptr: return this->emitNullPtr(E); + case PT_Float32: + return this->emitZeroFloat32(E); } llvm_unreachable("unknown primitive type"); } @@ -582,6 +594,7 @@ case PT_Bool: return this->emitConstBool(Value.getBoolValue(), E); case PT_Ptr: + case PT_Float32: llvm_unreachable("Invalid integral type"); break; } Index: clang/lib/AST/Interp/Context.cpp =================================================================== --- clang/lib/AST/Interp/Context.cpp +++ clang/lib/AST/Interp/Context.cpp @@ -126,6 +126,13 @@ if (T->isNullPtrType()) return PT_Ptr; + if (T->isFloatingType()) { + unsigned Bytes = getASTContext().getTypeSize(T); + if (Bytes == 32) + return PT_Float32; + return None; + } + if (auto *AT = dyn_cast(T)) return classify(AT->getValueType()); Index: clang/lib/AST/Interp/Floating.h =================================================================== --- /dev/null +++ clang/lib/AST/Interp/Floating.h @@ -0,0 +1,146 @@ +//===--- 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 + +namespace clang { +namespace interp { + +using APFloat = llvm::APFloat; +using APSInt = llvm::APSInt; + +template class Floating final { +private: + template struct Repr; + template <> struct Repr<32> { using Type = float; }; + template <> struct Repr<64> { using Type = double; }; + + // The primitive representing the integral. + using ReprT = typename Repr::Type; + ReprT V; + + /// Primitive representing limits. + static constexpr auto Min = std::numeric_limits::min(); + static constexpr auto Max = std::numeric_limits::max(); + + /// Construct a Floating from anything that is convertible to storage. + template explicit Floating(T V) : V(V) {} + +public: + /// Zero-initializes a Floating. + Floating() : V(0) {} + + bool operator<(Floating RHS) const { return V < RHS.V; } + bool operator>(Floating RHS) const { return V > RHS.V; } + bool operator<=(Floating RHS) const { return V <= RHS.V; } + bool operator>=(Floating RHS) const { return V >= RHS.V; } + bool operator==(Floating RHS) const { return V == RHS.V; } + bool operator!=(Floating RHS) const { return V != RHS.V; } + Floating operator-() const { return Floating(-V); } + Floating operator~() const { return Floating(~V); } + + explicit operator int8_t() const { return V; } + explicit operator uint8_t() const { return V; } + explicit operator int16_t() const { return V; } + explicit operator uint16_t() const { return V; } + explicit operator int32_t() const { return V; } + explicit operator uint32_t() const { return V; } + explicit operator int64_t() const { return V; } + explicit operator uint64_t() const { return V; } + explicit operator bool() const { return V; } + explicit operator ReprT() const { return V; } + + APFloat toAPFloat() const { return APFloat(V); } + APSInt toAPSInt(unsigned NumBits = 0) const { return APSInt(V); } + APValue toAPValue() const { return APValue(toAPFloat()); } + void print(llvm::raw_ostream &OS) const { OS << V; } + + constexpr static unsigned bitWidth() { return Bits; } + static Floating zero() { return from(0); } + + bool isSigned() const { return true; } + bool isNegative() const { return V < 0; } + bool isPositive() const { return V >= 0; } + bool isZero() const { return V == 0; } + bool isMin() const { return V == Min; } + bool isMinusOne() const { return V == -1; } + + ComparisonCategoryResult compare(const Floating &RHS) const { + return Compare(V, RHS.V); + } + + // TODO: Properly implement this(?) + Floating truncate(unsigned TruncBits) const { + if (TruncBits >= Bits) + return *this; + + return Floating(this->V); + } + + template static Floating from(ValT Value) { + if constexpr (std::is_integral::value || + std::is_floating_point::value) + return Floating(Value); + else + return Floating::from(static_cast(Value)); + } + + template static Floating from(T Value, unsigned NumBits) { + return Floating(Value); + } + + // ------- + + static bool add(Floating A, Floating B, unsigned OpBits, Floating *R) { + *R = Floating(A.V + B.V); + return false; + } + static bool sub(Floating A, Floating B, unsigned OpBits, Floating *R) { + *R = Floating(A.V - B.V); + return false; + } + + static bool neg(Floating A, Floating *R) { + *R = -A; + return false; + } + + static bool div(Floating A, Floating B, unsigned OpBits, Floating *R) { + *R = Floating(A.V / B.V); + return false; + } + + static bool rem(Floating A, Floating B, unsigned OpBits, Floating *R) { + *R = Floating(A.V % B.V); + return false; + } + + static bool mul(Floating A, Floating B, unsigned OpBits, Floating *R) { + *R = Floating(A.V * B.V); + return false; + } +}; + +template +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F) { + F.print(OS); + return OS; +} + +} // namespace interp +} // namespace clang + +#endif Index: clang/lib/AST/Interp/Integral.h =================================================================== --- clang/lib/AST/Interp/Integral.h +++ clang/lib/AST/Interp/Integral.h @@ -21,33 +21,14 @@ #include #include +#include "Primitives.h" + namespace clang { namespace interp { using APInt = llvm::APInt; using APSInt = llvm::APSInt; -/// Helper to compare two comparable types. -template -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 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 class Integral final { private: template friend class Integral; + // Helper structure to select the representation. + template 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::Type; @@ -105,6 +96,7 @@ explicit operator unsigned() const { return V; } explicit operator int64_t() const { return V; } explicit operator uint64_t() const { return V; } + explicit operator float() const { return V; } APSInt toAPSInt() const { return APSInt(APInt(Bits, static_cast(V), Signed), !Signed); Index: clang/lib/AST/Interp/InterpStack.h =================================================================== --- clang/lib/AST/Interp/InterpStack.h +++ clang/lib/AST/Interp/InterpStack.h @@ -160,6 +160,9 @@ else if constexpr (std::is_same_v || std::is_same_v>) return PT_Uint64; + else if constexpr (std::is_same_v || + std::is_same_v>) + return PT_Float32; llvm_unreachable("unknown type push()'ed into InterpStack"); } 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 Float32 : Type; def Ptr : Type; //===----------------------------------------------------------------------===// @@ -40,6 +41,7 @@ def ArgUint32 : ArgType { let Name = "uint32_t"; } def ArgSint64 : ArgType { let Name = "int64_t"; } def ArgUint64 : ArgType { let Name = "uint64_t"; } +def ArgFloat32 : ArgType { let Name = "float"; } def ArgBool : ArgType { let Name = "bool"; } def ArgFunction : ArgType { let Name = "const Function *"; } @@ -54,11 +56,15 @@ list Types; } -def NumberTypeClass : TypeClass { +def IntegerTypeClass : TypeClass { let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, Uint32, Sint64, Uint64]; } +def NumberTypeClass : TypeClass { + let Types = !listconcat(IntegerTypeClass.Types, [Float32]); +} + def AluTypeClass : TypeClass { let Types = !listconcat(NumberTypeClass.Types, [Bool]); } @@ -192,6 +198,7 @@ def ConstUint32 : ConstOpcode; def ConstSint64 : ConstOpcode; def ConstUint64 : ConstOpcode; +def ConstFloat32 : ConstOpcode; def ConstBool : ConstOpcode; // [] -> [Integer] @@ -385,9 +392,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; +} //===----------------------------------------------------------------------===// // Binary operators. @@ -398,7 +411,7 @@ def Add : AluOpcode; def Mul : AluOpcode; def Rem : Opcode { - let Types = [NumberTypeClass]; + let Types = [IntegerTypeClass]; let HasGroup = 1; } def Div : Opcode { @@ -424,7 +437,7 @@ // [Real] -> [Real] def Not: Opcode { - let Types = [NumberTypeClass]; + let Types = [IntegerTypeClass]; let HasGroup = 1; } @@ -434,11 +447,11 @@ // TODO: Expand this to handle casts between more types. def FromCastTypeClass : TypeClass { - let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Float32, Bool]; } def ToCastTypeClass : TypeClass { - let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Float32, Bool]; } def Cast: Opcode { 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 #include #include -#include "Boolean.h" -#include "Integral.h" namespace clang { namespace interp { @@ -35,6 +36,7 @@ PT_Sint64, PT_Uint64, PT_Bool, + PT_Float32, PT_Ptr, }; @@ -48,6 +50,7 @@ template <> struct PrimConv { using T = Integral<32, false>; }; template <> struct PrimConv { using T = Integral<64, true>; }; template <> struct PrimConv { using T = Integral<64, false>; }; +template <> struct PrimConv { using T = Floating<32>; }; template <> struct PrimConv { using T = Boolean; }; template <> struct PrimConv { using T = Pointer; }; @@ -70,6 +73,7 @@ case PT_Uint32: case PT_Sint64: case PT_Uint64: + case PT_Float32: 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_Float32, B) \ TYPE_SWITCH_CASE(PT_Bool, B) \ TYPE_SWITCH_CASE(PT_Ptr, B) \ } \ Index: clang/lib/AST/Interp/Primitives.h =================================================================== --- /dev/null +++ clang/lib/AST/Interp/Primitives.h @@ -0,0 +1,34 @@ +//===------ 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 + +namespace clang { +namespace interp { + +/// Helper to compare two comparable types. +template 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/test/AST/Interp/literals.cpp =================================================================== --- clang/test/AST/Interp/literals.cpp +++ clang/test/AST/Interp/literals.cpp @@ -245,3 +245,24 @@ #endif }; + +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}} + +};