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 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; } Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -62,6 +62,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); @@ -257,6 +258,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 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 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 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 +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()); @@ -224,14 +267,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 (DiscardResult) @@ -624,6 +675,8 @@ return this->emitZeroUint64(E); case PT_Ptr: return this->emitNullPtr(E); + case PT_Float: + assert(false); } llvm_unreachable("unknown primitive type"); } @@ -788,6 +841,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 @@ -113,6 +113,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 @@ -167,6 +167,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/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; Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -94,6 +94,11 @@ /// Checks that all fields are initialized after a constructor call. bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This); +/// 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 bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) { @@ -164,6 +169,16 @@ return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); } +inline bool Addf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + const Floating &RHS = S.Stk.pop(); + const Floating &LHS = S.Stk.pop(); + + Floating Result; + auto Status = Floating::add(LHS, RHS, RM, &Result); + S.Stk.push(Result); + return CheckFloatResult(S, OpPC, Status, Result); +} + template ::T> bool Sub(InterpState &S, CodePtr OpPC) { const T &RHS = S.Stk.pop(); @@ -172,6 +187,16 @@ return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); } +inline bool Subf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + const Floating &RHS = S.Stk.pop(); + const Floating &LHS = S.Stk.pop(); + + Floating Result; + auto Status = Floating::sub(LHS, RHS, RM, &Result); + S.Stk.push(Result); + return CheckFloatResult(S, OpPC, Status, Result); +} + template ::T> bool Mul(InterpState &S, CodePtr OpPC) { const T &RHS = S.Stk.pop(); @@ -180,6 +205,15 @@ return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); } +inline bool Mulf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + const Floating &RHS = S.Stk.pop(); + const Floating &LHS = S.Stk.pop(); + + Floating Result; + auto Status = Floating::mul(LHS, RHS, RM, &Result); + S.Stk.push(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 @@ -271,6 +305,19 @@ return false; } +inline bool Divf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + const Floating &RHS = S.Stk.pop(); + const Floating &LHS = S.Stk.pop(); + + if (!CheckDivRem(S, OpPC, LHS, RHS)) + return false; + + Floating Result; + auto Status = Floating::div(LHS, RHS, RM, &Result); + S.Stk.push(Result); + return CheckFloatResult(S, OpPC, Status, Result); +} + //===----------------------------------------------------------------------===// // Inv //===----------------------------------------------------------------------===// @@ -1137,6 +1184,38 @@ 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 ::T> +bool CastIntegralFloating(InterpState &S, CodePtr OpPC, + const llvm::fltSemantics *Sem) { + const T &From = S.Stk.pop(); + APSInt FromAP = From.toAPSInt(); + Floating Result; + + if (!Floating::fromIntegral(FromAP, *Sem, Result)) + return false; + + S.Stk.push(Result); + return true; +} + +template ::T> +bool CastFloatingIntegral(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { + const Floating &F = S.Stk.pop(); + APSInt Result(std::max(8u, T::bitWidth()), T::isSigned()); + + if (!F.convertToInteger(RM, Result)) + return false; + + S.Stk.push(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 @@ -479,6 +479,50 @@ return CheckFieldsInitialized(S, OpPC, This, R); } +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 Result = F.toSemantics(Sem, RM); + 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,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 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 : 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; def ConstSint64 : ConstOpcode; def ConstUint64 : ConstOpcode; +def ConstFloat : ConstOpcode; def ConstBool : ConstOpcode; // [] -> [Integer] @@ -406,13 +423,19 @@ //===----------------------------------------------------------------------===// // [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 BitXor : IntegerOpcode; def Shl : Opcode { let Types = [IntegerTypeClass, IntegerTypeClass]; @@ -424,14 +447,6 @@ let HasGroup = 1; } -def BitAnd : IntegerOpcode; -def BitOr : IntegerOpcode; -def Div : Opcode { - let Types = [NumberTypeClass]; - let HasGroup = 1; -} -def BitXor : IntegerOpcode; - //===----------------------------------------------------------------------===// // Unary operators. //===----------------------------------------------------------------------===// @@ -449,20 +464,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]; @@ -477,6 +491,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/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/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))