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/InterpBuiltin.cpp Interp/InterpBlock.cpp 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 double() 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 @@ -122,6 +122,33 @@ 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, CE); + } + + case CK_IntegralToFloating: { + // First cast the integral to a Floating, THEN cast the + // Floating to Floating with the same semantics. + Optional FromT = classify(SubExpr->getType()); + if (!FromT) + return false; + + if (!this->visit(SubExpr)) + return false; + + if (!this->emitCast(*FromT, PT_Float, CE)) + return false; + + const auto *TargetSemantics = + &Ctx.getASTContext().getFloatTypeSemantics(CE->getType()); + return this->emitCastFP(TargetSemantics, CE); + } + case CK_ArrayToPointerDecay: case CK_AtomicToNonAtomic: case CK_ConstructorConversion: @@ -133,6 +160,7 @@ return this->Visit(SubExpr); case CK_IntegralToBoolean: + case CK_FloatingToIntegral: case CK_IntegralCast: { Optional FromT = classify(SubExpr->getType()); Optional ToT = classify(CE->getType()); @@ -166,6 +194,14 @@ return this->bail(LE); } +template +bool ByteCodeExprGen::VisitFloatingLiteral(const FloatingLiteral *E) { + if (DiscardResult) + return true; + + return this->emitConstFloat(E->getValue(), E); +} + template bool ByteCodeExprGen::VisitParenExpr(const ParenExpr *PE) { return this->Visit(PE->getSubExpr()); @@ -438,6 +474,8 @@ return this->emitZeroUint64(E); case PT_Ptr: return this->emitNullPtr(E); + case PT_Float: + return this->emitZeroFloat(E); } llvm_unreachable("unknown primitive type"); } @@ -602,6 +640,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/Context.cpp =================================================================== --- clang/lib/AST/Interp/Context.cpp +++ clang/lib/AST/Interp/Context.cpp @@ -126,6 +126,9 @@ if (T->isNullPtrType()) return PT_Ptr; + if (T->isFloatingType()) + return PT_Float; + if (auto *AT = dyn_cast(T)) return classify(AT->getValueType()); 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::T>; + COMPOSITE_TYPE_SWITCH(Type, return ctorTy, return nullptr); } Index: clang/lib/AST/Interp/Floating.h =================================================================== --- /dev/null +++ clang/lib/AST/Interp/Floating.h @@ -0,0 +1,147 @@ +//===--- 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 + +namespace clang { +namespace interp { + +using APFloat = llvm::APFloat; +using APSInt = llvm::APSInt; + +class Floating final { +private: + // The underlying value storage. + APFloat F; + + /// 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) : 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); } + + explicit operator int8_t() const { return toAPSInt().getExtValue(); } + explicit operator uint8_t() const { return toAPSInt().getExtValue(); } + explicit operator int16_t() const { return toAPSInt().getExtValue(); } + explicit operator uint16_t() const { return toAPSInt().getExtValue(); } + explicit operator int32_t() const { return toAPSInt().getExtValue(); } + explicit operator uint32_t() const { return toAPSInt().getExtValue(); } + explicit operator int64_t() const { return toAPSInt().getExtValue(); } + explicit operator uint64_t() const { return toAPSInt().getExtValue(); } + explicit operator bool() const { return F.isNonZero(); } + + Floating toSemantics(const llvm::fltSemantics *Sem) const { + APFloat Copy = F; + bool LosesInfo; + Copy.convert(*Sem, llvm::RoundingMode::TowardZero, &LosesInfo); + (void)LosesInfo; + return Floating(Copy); + } + APFloat toAPFloat() const { return F; } + APSInt toAPSInt(unsigned NumBits = 0) const { + return APSInt(F.bitcastToAPInt()); + } + APValue toAPValue() const { return APValue(toAPFloat()); } + void print(llvm::raw_ostream &OS) const { F.print(OS); } + + constexpr static unsigned bitWidth() { return 32; } + + 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 false; } // TODO + bool isMinusOne() const { return F == APFloat(-1.0f); } + 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 static Floating from(ValT Value) { + if constexpr (std::is_integral::value || + std::is_floating_point::value) + return Floating(Value); + else if constexpr (std::is_same_v) { + return Floating(Value.F); + } 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.F + B.F); + return false; + } + + static bool sub(Floating A, Floating B, unsigned OpBits, Floating *R) { + *R = Floating(A.F - B.F); + 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.F / B.F); + return false; + } + + static bool mul(Floating A, Floating B, unsigned OpBits, Floating *R) { + *R = Floating(A.F * B.F); + 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,16 @@ + + +#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/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 double() const { return V; } APSInt toAPSInt() const { return APSInt(APInt(Bits, static_cast(V), Signed), !Signed); Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -979,6 +979,11 @@ 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); + //===----------------------------------------------------------------------===// // Zero, Nullptr //===----------------------------------------------------------------------===// Index: clang/lib/AST/Interp/Interp.cpp =================================================================== --- clang/lib/AST/Interp/Interp.cpp +++ clang/lib/AST/Interp/Interp.cpp @@ -418,6 +418,13 @@ return false; } +bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem) { + Floating F = S.Stk.pop(); + Floating Result = Floating(F.toSemantics(Sem)); + S.Stk.push(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/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 || std::is_same_v>) return PT_Uint64; + else if constexpr (std::is_same_v) + return PT_Float; 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 Float : Type; def Ptr : Type; //===----------------------------------------------------------------------===// @@ -40,11 +41,13 @@ 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 ArgFltSemantics : ArgType { let Name = "const llvm::fltSemantics *"; } //===----------------------------------------------------------------------===// // Classes of types instructions operate on. @@ -54,11 +57,19 @@ list Types; } -def NumberTypeClass : TypeClass { +def IntegerTypeClass : TypeClass { let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, Uint32, Sint64, Uint64]; } +def FloatTypeClass : TypeClass { + let Types = [Float]; +} + +def NumberTypeClass : TypeClass { + let Types = !listconcat(IntegerTypeClass.Types, [Float]); +} + def AluTypeClass : TypeClass { let Types = !listconcat(NumberTypeClass.Types, [Bool]); } @@ -204,6 +215,7 @@ def ConstUint32 : ConstOpcode; def ConstSint64 : ConstOpcode; def ConstUint64 : ConstOpcode; +def ConstFloat : ConstOpcode; def ConstBool : ConstOpcode; // [] -> [Integer] @@ -397,9 +409,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. @@ -410,7 +428,7 @@ def Add : AluOpcode; def Mul : AluOpcode; def Rem : Opcode { - let Types = [NumberTypeClass]; + let Types = [IntegerTypeClass]; let HasGroup = 1; } def BitAnd : IntegerOpcode; @@ -437,21 +455,21 @@ // [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]; + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Float, 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, Float, Bool]; } def Cast: Opcode { @@ -459,6 +477,11 @@ let HasGroup = 1; } +def CastFP : Opcode { + let Types = []; + let Args = [ArgFltSemantics]; +} + //===----------------------------------------------------------------------===// // Comparison opcodes. //===----------------------------------------------------------------------===// 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_Float, 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; }; 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_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/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 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 @@ -270,3 +270,26 @@ static_assert((1337 & -1) == 1337, ""); static_assert((0 & gimme(12)) == 0, ""); }; + +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}} +};