Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -1947,6 +1947,9 @@ return getQualifiedType(type.getUnqualifiedType(), Qs); } + unsigned char getFixedPointScale(const QualType &Ty) const; + unsigned char getFixedPointIBits(const QualType &Ty) const; + DeclarationNameInfo getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const; Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -1351,6 +1351,47 @@ } }; +class FixedPointLiteral : public Expr, public APIntStorage { + SourceLocation Loc; + unsigned Scale; + + /// \brief Construct an empty integer literal. + explicit FixedPointLiteral(EmptyShell Empty) + : Expr(FixedPointLiteralClass, Empty) {} + + public: + FixedPointLiteral(const ASTContext &C, const llvm::APInt &V, QualType type, + SourceLocation l, unsigned Scale); + + // Store the int as is without any bit shifting. + static FixedPointLiteral *CreateFromRawInt(const ASTContext &C, + const llvm::APInt &V, + QualType type, SourceLocation l, + unsigned Scale); + + SourceLocation getLocStart() const LLVM_READONLY { return Loc; } + SourceLocation getLocEnd() const LLVM_READONLY { return Loc; } + + /// \brief Retrieve the location of the literal. + SourceLocation getLocation() const { return Loc; } + + void setLocation(SourceLocation Location) { Loc = Location; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == FixedPointLiteralClass; + } + + std::string getValueAsString(unsigned Radix) const; + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } +}; + class CharacterLiteral : public Expr { public: enum CharacterKind { Index: include/clang/AST/OperationKinds.def =================================================================== --- include/clang/AST/OperationKinds.def +++ include/clang/AST/OperationKinds.def @@ -197,6 +197,10 @@ /// float f = i; CAST_OPERATION(IntegralToFloating) +/// CK_IntegralToFixedPoint - Integral to fixed point. +/// (short _Accum) i; +CAST_OPERATION(IntegralToFixedPoint) + /// CK_FloatingToIntegral - Floating point to integral. Rounds /// towards zero, discarding any fractional component. /// (int) f Index: include/clang/AST/RecursiveASTVisitor.h =================================================================== --- include/clang/AST/RecursiveASTVisitor.h +++ include/clang/AST/RecursiveASTVisitor.h @@ -2585,6 +2585,7 @@ // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) +DEF_TRAVERSE_STMT(FixedPointLiteral, {}) DEF_TRAVERSE_STMT(CharacterLiteral, {}) DEF_TRAVERSE_STMT(FloatingLiteral, {}) DEF_TRAVERSE_STMT(ImaginaryLiteral, {}) Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -1918,7 +1918,8 @@ STK_Integral, STK_Floating, STK_IntegralComplex, - STK_FloatingComplex + STK_FloatingComplex, + STK_FixedPoint }; /// Given that this is a scalar type, classify it. @@ -6546,6 +6547,11 @@ return cast(Decayed)->getPointeeType(); } +// Return the string representation of an unsigned fixed point type, represented +// as a scaled integer. +std::string FixedPointValueToString(unsigned Radix, unsigned Scale, + uint64_t Val); + } // namespace clang #endif // LLVM_CLANG_AST_TYPE_H Index: include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- include/clang/Basic/DiagnosticCommonKinds.td +++ include/clang/Basic/DiagnosticCommonKinds.td @@ -168,6 +168,10 @@ InGroup; def ext_clang_diagnose_if : Extension<"'diagnose_if' is a clang extension">, InGroup; +def err_integral_part_on_fract : Error<"a _Fract type cannot have an " + "integral part">; +def err_accum_too_large : Error< + "this value is too large for this %select{signed|unsigned}0 _Accum type">; def err_fixed_point_not_enabled : Error<"compile with " "'-ffixed-point' to enable fixed point types">; Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -303,6 +303,8 @@ COMPATIBLE_VALUE_LANGOPT(FunctionAlignment, 5, 0, "Default alignment for functions") LANGOPT(FixedPoint, 1, 0, "fixed point types") +LANGOPT(SameFBits, 1, 0, + "unsigned and signed fixed point type having the same number of fractional bits") #undef LANGOPT #undef COMPATIBLE_LANGOPT Index: include/clang/Basic/StmtNodes.td =================================================================== --- include/clang/Basic/StmtNodes.td +++ include/clang/Basic/StmtNodes.td @@ -57,6 +57,7 @@ def PredefinedExpr : DStmt; def DeclRefExpr : DStmt; def IntegerLiteral : DStmt; +def FixedPointLiteral : DStmt; def FloatingLiteral : DStmt; def ImaginaryLiteral : DStmt; def StringLiteral : DStmt; Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -74,12 +74,31 @@ unsigned char LargeArrayMinWidth, LargeArrayAlign; unsigned char LongWidth, LongAlign; unsigned char LongLongWidth, LongLongAlign; + + // Fixed point bit widths unsigned char ShortAccumWidth, ShortAccumAlign; unsigned char AccumWidth, AccumAlign; unsigned char LongAccumWidth, LongAccumAlign; unsigned char ShortFractWidth, ShortFractAlign; unsigned char FractWidth, FractAlign; unsigned char LongFractWidth, LongFractAlign; + + // Fixed point integral and fractional bit sizes + // Saturated types share the same integral/fractional bits as their + // corresponding unsaturated types. + unsigned char ShortAccumFBits, ShortAccumIBits; + unsigned char AccumFBits, AccumIBits; + unsigned char LongAccumFBits, LongAccumIBits; + unsigned char UnsignedShortAccumFBits, UnsignedShortAccumIBits; + unsigned char UnsignedAccumFBits, UnsignedAccumIBits; + unsigned char UnsignedLongAccumFBits, UnsignedLongAccumIBits; + unsigned char ShortFractFBits; + unsigned char FractFBits; + unsigned char LongFractFBits; + unsigned char UnsignedShortFractFBits; + unsigned char UnsignedFractFBits; + unsigned char UnsignedLongFractFBits; + unsigned char SuitableAlign; unsigned char DefaultAlignForAttributeAligned; unsigned char MinGlobalAlign; @@ -394,6 +413,60 @@ unsigned getLongFractWidth() const { return LongFractWidth; } unsigned getLongFractAlign() const { return LongFractAlign; } + /// getShortAccumFBits/IBits - Return the number of fractional/integral bits + /// in a 'signed short _Accum' type. + unsigned getShortAccumFBits() const { return ShortAccumFBits; } + unsigned getShortAccumIBits() const { return ShortAccumIBits; } + + /// getAccumFBits/IBits - Return the number of fractional/integral bits + /// in a 'signed _Accum' type. + unsigned getAccumFBits() const { return AccumFBits; } + unsigned getAccumIBits() const { return AccumIBits; } + + /// getLongAccumFBits/IBits - Return the number of fractional/integral bits + /// in a 'signed long _Accum' type. + unsigned getLongAccumFBits() const { return LongAccumFBits; } + unsigned getLongAccumIBits() const { return LongAccumIBits; } + + /// getUnsignedShortAccumFBits/IBits - Return the number of fractional/integral bits + /// in a 'unsigned short _Accum' type. + unsigned getUnsignedShortAccumFBits() const { return UnsignedShortAccumFBits; } + unsigned getUnsignedShortAccumIBits() const { return UnsignedShortAccumIBits; } + + /// getUnsignedAccumFBits/IBits - Return the number of fractional/integral bits + /// in a 'unsigned _Accum' type. + unsigned getUnsignedAccumFBits() const { return UnsignedAccumFBits; } + unsigned getUnsignedAccumIBits() const { return UnsignedAccumIBits; } + + /// getUnsignedLongAccumFBits/IBits - Return the number of fractional/integral bits + /// in a 'unsigned long _Accum' type. + unsigned getUnsignedLongAccumFBits() const { return UnsignedLongAccumFBits; } + unsigned getUnsignedLongAccumIBits() const { return UnsignedLongAccumIBits; } + + /// getShortFractFBits - Return the number of fractional bits + /// in a 'signed short _Fract' type. + unsigned getShortFractFBits() const { return ShortFractFBits; } + + /// getFractFBits - Return the number of fractional bits + /// in a 'signed _Fract' type. + unsigned getFractFBits() const { return FractFBits; } + + /// getLongFractFBits - Return the number of fractional bits + /// in a 'signed long _Fract' type. + unsigned getLongFractFBits() const { return LongFractFBits; } + + /// getUnsignedShortFractFBits - Return the number of fractional bits + /// in a 'unsigned short _Fract' type. + unsigned getUnsignedShortFractFBits() const { return UnsignedShortFractFBits; } + + /// getUnsignedFractFBits - Return the number of fractional bits + /// in a 'unsigned _Fract' type. + unsigned getUnsignedFractFBits() const { return UnsignedFractFBits; } + + /// getUnsignedLongFractFBits - Return the number of fractional bits + /// in a 'unsigned long _Fract' type. + unsigned getUnsignedLongFractFBits() const { return UnsignedLongFractFBits; } + /// Determine whether the __int128 type is supported on this target. virtual bool hasInt128Type() const { return (getPointerWidth(0) >= 64) || getTargetOpts().ForceEnableInt128; @@ -1189,6 +1262,11 @@ virtual ArrayRef getGCCAddlRegNames() const { return None; } + + private: + // Assert the values for the fractional and integral bits for each fixed point + // type follow the restrictions given in clause 6.2.6.3 of N1169. + void CheckFixedPointBits() const; }; } // end namespace clang Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -891,6 +891,10 @@ Flags<[CC1Option]>, HelpText<"Enable fixed point types">; def fno_fixed_point : Flag<["-"], "fno-fixed-point">, Group, HelpText<"Disable fixed point types">; +def fsame_fbits : Flag<["-"], "fsame-fbits">, Group, + Flags<[CC1Option]>, + HelpText<"Force each unsigned fixed point type to have the same number of fractional bits as its corresponding signed type">; +def fno_same_fbits : Flag<["-"], "fno-same-fbits">, Group; // Begin sanitizer flags. These should all be core options exposed in all driver // modes. Index: include/clang/Lex/LiteralSupport.h =================================================================== --- include/clang/Lex/LiteralSupport.h +++ include/clang/Lex/LiteralSupport.h @@ -50,7 +50,7 @@ unsigned radix; - bool saw_exponent, saw_period, saw_ud_suffix; + bool saw_exponent, saw_period, saw_ud_suffix, saw_fixed_point_suffix; SmallString<32> UDSuffixBuf; @@ -69,11 +69,16 @@ bool isFloat128 : 1; // 1.0q uint8_t MicrosoftInteger; // Microsoft suffix extension i8, i16, i32, or i64. + bool isFract : 1; // 1.0hr/r/lr/uhr/ur/ulr + bool isAccum : 1; // 1.0hk/k/lk/uhk/uk/ulk + + bool isFixedPointLiteral() const { return saw_fixed_point_suffix; } + bool isIntegerLiteral() const { - return !saw_period && !saw_exponent; + return !saw_period && !saw_exponent && !isFixedPointLiteral(); } bool isFloatingLiteral() const { - return saw_period || saw_exponent; + return (saw_period || saw_exponent) && !isFixedPointLiteral(); } bool hasUDSuffix() const { @@ -105,6 +110,11 @@ /// literal exactly, and false otherwise. llvm::APFloat::opStatus GetFloatValue(llvm::APFloat &Result); + /// GetFixedPointValue - Convert this numeric literal value into a + /// scaled integer that represents this value. Returns true if an overflow + /// occurred when calculating the integral part of the scaled integer. + bool GetFixedPointValue(uint64_t &Val, unsigned Scale); + private: void ParseNumberStartingWithZero(SourceLocation TokLoc); @@ -157,7 +167,6 @@ ptr++; return ptr; } - }; /// CharLiteralParser - Perform interpretation and semantic analysis of a Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -10131,3 +10131,92 @@ clang::LazyGenerationalUpdatePtr< const Decl *, Decl *, &ExternalASTSource::CompleteRedeclChain>::makeValue( const clang::ASTContext &Ctx, Decl *Value); + +unsigned char ASTContext::getFixedPointScale(const QualType &Ty) const { + assert(Ty->isFixedPointType()); + + const auto *BT = Ty->getAs(); + const TargetInfo &Target = getTargetInfo(); + switch (BT->getKind()) { + default: + llvm_unreachable("Not a fixed point type!"); + case BuiltinType::ShortAccum: + case BuiltinType::SatShortAccum: + return Target.getShortAccumFBits(); + case BuiltinType::Accum: + case BuiltinType::SatAccum: + return Target.getAccumFBits(); + case BuiltinType::LongAccum: + case BuiltinType::SatLongAccum: + return Target.getLongAccumFBits(); + case BuiltinType::UShortAccum: + case BuiltinType::SatUShortAccum: + return Target.getUnsignedShortAccumFBits(); + case BuiltinType::UAccum: + case BuiltinType::SatUAccum: + return Target.getUnsignedAccumFBits(); + case BuiltinType::ULongAccum: + case BuiltinType::SatULongAccum: + return Target.getUnsignedLongAccumFBits(); + case BuiltinType::ShortFract: + case BuiltinType::SatShortFract: + return Target.getShortFractFBits(); + case BuiltinType::Fract: + case BuiltinType::SatFract: + return Target.getFractFBits(); + case BuiltinType::LongFract: + case BuiltinType::SatLongFract: + return Target.getLongFractFBits(); + case BuiltinType::UShortFract: + case BuiltinType::SatUShortFract: + return Target.getUnsignedShortFractFBits(); + case BuiltinType::UFract: + case BuiltinType::SatUFract: + return Target.getUnsignedFractFBits(); + case BuiltinType::ULongFract: + case BuiltinType::SatULongFract: + return Target.getUnsignedLongFractFBits(); + } +} + +unsigned char ASTContext::getFixedPointIBits(const QualType &Ty) const { + assert(Ty->isFixedPointType()); + + const auto *BT = Ty->getAs(); + const TargetInfo &Target = getTargetInfo(); + switch (BT->getKind()) { + default: + llvm_unreachable("Not a fixed point type!"); + case BuiltinType::ShortAccum: + case BuiltinType::SatShortAccum: + return Target.getShortAccumIBits(); + case BuiltinType::Accum: + case BuiltinType::SatAccum: + return Target.getAccumIBits(); + case BuiltinType::LongAccum: + case BuiltinType::SatLongAccum: + return Target.getLongAccumIBits(); + case BuiltinType::UShortAccum: + case BuiltinType::SatUShortAccum: + return Target.getUnsignedShortAccumIBits(); + case BuiltinType::UAccum: + case BuiltinType::SatUAccum: + return Target.getUnsignedAccumIBits(); + case BuiltinType::ULongAccum: + case BuiltinType::SatULongAccum: + return Target.getUnsignedLongAccumIBits(); + case BuiltinType::ShortFract: + case BuiltinType::SatShortFract: + case BuiltinType::Fract: + case BuiltinType::SatFract: + case BuiltinType::LongFract: + case BuiltinType::SatLongFract: + case BuiltinType::UShortFract: + case BuiltinType::SatUShortFract: + case BuiltinType::UFract: + case BuiltinType::SatUFract: + case BuiltinType::ULongFract: + case BuiltinType::SatULongFract: + return 0; + } +} Index: lib/AST/ASTDumper.cpp =================================================================== --- lib/AST/ASTDumper.cpp +++ lib/AST/ASTDumper.cpp @@ -525,6 +525,7 @@ void VisitPredefinedExpr(const PredefinedExpr *Node); void VisitCharacterLiteral(const CharacterLiteral *Node); void VisitIntegerLiteral(const IntegerLiteral *Node); + void VisitFixedPointLiteral(const FixedPointLiteral *Node); void VisitFloatingLiteral(const FloatingLiteral *Node); void VisitStringLiteral(const StringLiteral *Str); void VisitInitListExpr(const InitListExpr *ILE); @@ -2177,6 +2178,13 @@ OS << " " << Node->getValue().toString(10, isSigned); } +void ASTDumper::VisitFixedPointLiteral(const FixedPointLiteral *Node) { + VisitExpr(Node); + + ColorScope Color(*this, ValueColor); + OS << " " << Node->getValueAsString(/*Radix=*/10); +} + void ASTDumper::VisitFloatingLiteral(const FloatingLiteral *Node) { VisitExpr(Node); ColorScope Color(*this, ValueColor); Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -755,6 +755,30 @@ return new (C) IntegerLiteral(Empty); } +FixedPointLiteral::FixedPointLiteral(const ASTContext &C, const llvm::APInt &V, + QualType type, SourceLocation l, + unsigned Scale) + : Expr(FixedPointLiteralClass, type, VK_RValue, OK_Ordinary, false, false, + false, false), + Loc(l), Scale(Scale) { + assert(type->isFixedPointType() && "Illegal type in FixedPointLiteral"); + assert(V.getBitWidth() == C.getTypeInfo(type).Width && + "Fixed point type is not the correct size for constant."); + setValue(C, V); +} + +FixedPointLiteral *FixedPointLiteral::CreateFromRawInt(const ASTContext &C, + const llvm::APInt &V, + QualType type, + SourceLocation l, + unsigned Scale) { + return new (C) FixedPointLiteral(C, V, type, l, Scale); +} + +std::string FixedPointLiteral::getValueAsString(unsigned Radix) const { + return FixedPointValueToString(Radix, Scale, getValue().getZExtValue()); +} + FloatingLiteral::FloatingLiteral(const ASTContext &C, const llvm::APFloat &V, bool isexact, QualType Type, SourceLocation L) : Expr(FloatingLiteralClass, Type, VK_RValue, OK_Ordinary, false, false, @@ -1610,6 +1634,7 @@ case CK_ZeroToOCLEvent: case CK_ZeroToOCLQueue: case CK_IntToOCLSampler: + case CK_IntegralToFixedPoint: assert(!getType()->isBooleanType() && "unheralded conversion to bool"); goto CheckNoBasePath; @@ -2991,6 +3016,7 @@ case ObjCIvarRefExprClass: case PredefinedExprClass: case IntegerLiteralClass: + case FixedPointLiteralClass: case FloatingLiteralClass: case ImaginaryLiteralClass: case StringLiteralClass: Index: lib/AST/ExprClassification.cpp =================================================================== --- lib/AST/ExprClassification.cpp +++ lib/AST/ExprClassification.cpp @@ -161,6 +161,7 @@ case Expr::ShuffleVectorExprClass: case Expr::ConvertVectorExprClass: case Expr::IntegerLiteralClass: + case Expr::FixedPointLiteralClass: case Expr::CharacterLiteralClass: case Expr::AddrLabelExprClass: case Expr::CXXDeleteExprClass: Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -7220,6 +7220,73 @@ // FIXME: Missing: array subscript of vector, member of vector }; + +class FixedPointExprEvaluator + : public ExprEvaluatorBase { + APValue &Result; + + public: + FixedPointExprEvaluator(EvalInfo &info, APValue &result) + : ExprEvaluatorBaseTy(info), Result(result) {} + + bool Success(const llvm::APSInt &SI, const Expr *E, APValue &Result) { + assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); + assert(SI.isSigned() == E->getType()->isSignedFixedPointType() && + "Invalid evaluation result."); + assert(SI.getBitWidth() == Info.Ctx.getIntWidth(E->getType()) && + "Invalid evaluation result."); + Result = APValue(SI); + return true; + } + bool Success(const llvm::APSInt &SI, const Expr *E) { + return Success(SI, E, Result); + } + + bool Success(const llvm::APInt &I, const Expr *E, APValue &Result) { + assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); + assert(I.getBitWidth() == Info.Ctx.getIntWidth(E->getType()) && + "Invalid evaluation result."); + Result = APValue(APSInt(I)); + Result.getInt().setIsUnsigned(E->getType()->isUnsignedFixedPointType()); + return true; + } + bool Success(const llvm::APInt &I, const Expr *E) { + return Success(I, E, Result); + } + + bool Success(uint64_t Value, const Expr *E, APValue &Result) { + assert(E->getType()->isFixedPointType() && "Invalid evaluation result."); + Result = APValue(Info.Ctx.MakeIntValue(Value, E->getType())); + return true; + } + bool Success(uint64_t Value, const Expr *E) { + return Success(Value, E, Result); + } + + bool Success(CharUnits Size, const Expr *E) { + return Success(Size.getQuantity(), E); + } + + bool Success(const APValue &V, const Expr *E) { + if (V.isLValue() || V.isAddrLabelDiff()) { + Result = V; + return true; + } + return Success(V.getInt(), E); + } + + bool ZeroInitialization(const Expr *E) { return Success(0, E); } + + //===--------------------------------------------------------------------===// + // Visitor Methods + //===--------------------------------------------------------------------===// + + bool VisitFixedPointLiteral(const FixedPointLiteral *E) { + return Success(E->getValue(), E); + } + + bool VisitUnaryOperator(const UnaryOperator *E); +}; } // end anonymous namespace /// EvaluateIntegerOrLValue - Evaluate an rvalue integral-typed expression, and @@ -9166,6 +9233,9 @@ QualType SrcType = SubExpr->getType(); switch (E->getCastKind()) { + case CK_IntegralToFixedPoint: + llvm_unreachable( + "IntExprEvaluator::VisitCastExpr CK_IntegralToFixedPoint"); // TODO case CK_BaseToDerived: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: @@ -9342,6 +9412,37 @@ return Success(E->getValue(), E); } +bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { + switch (E->getOpcode()) { + default: + // Invalid unary operators + return Error(E); + case UO_Plus: + // The result is just the value. + return Visit(E->getSubExpr()); + case UO_Minus: { + if (!Visit(E->getSubExpr())) return false; + if (!Result.isInt()) return Error(E); + const APSInt &Value = Result.getInt(); + if (Value.isSigned() && Value.isMinSignedValue() && E->canOverflow()) { + std::string Val = + "-" + FixedPointValueToString( + /*Radix=*/10, Info.Ctx.getTypeInfo(E->getType()).Width, + Value.getExtValue()); + Info.CCEDiag(E, diag::note_constexpr_overflow) << Val << E->getType(); + if (Info.noteUndefinedBehavior()) return false; + } + return Success(-Value, E); + } + case UO_LNot: { + bool bres; + if (!EvaluateAsBooleanCondition(E->getSubExpr(), bres, Info)) + return false; + return Success(!bres, E); + } + } +} + //===----------------------------------------------------------------------===// // Float Evaluation //===----------------------------------------------------------------------===// @@ -9659,6 +9760,9 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { switch (E->getCastKind()) { + case CK_IntegralToFixedPoint: + llvm_unreachable( + "ComplexExprEvaluator::VisitCastExpr CK_IntegralToFixedPoint"); // TODO case CK_BitCast: case CK_BaseToDerived: case CK_DerivedToBase: @@ -10193,6 +10297,8 @@ if (!EvaluateComplex(E, C, Info)) return false; C.moveInto(Result); + } else if (T->isFixedPointType()) { + if (!FixedPointExprEvaluator(Info, Result).Visit(E)) return false; } else if (T->isMemberPointerType()) { MemberPtr P; if (!EvaluateMemberPointer(E, P, Info)) @@ -10641,6 +10747,7 @@ case Expr::GenericSelectionExprClass: return CheckICE(cast(E)->getResultExpr(), Ctx); case Expr::IntegerLiteralClass: + case Expr::FixedPointLiteralClass: case Expr::CharacterLiteralClass: case Expr::ObjCBoolLiteralExprClass: case Expr::CXXBoolLiteralExprClass: Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -3445,6 +3445,8 @@ recurse: switch (E->getStmtClass()) { + case Expr::FixedPointLiteralClass: + llvm_unreachable("Unknown mangling for FixedPointLiteralClass"); case Expr::NoStmtClass: #define ABSTRACT_STMT(Type) #define EXPR(Type, Base) Index: lib/AST/StmtPrinter.cpp =================================================================== --- lib/AST/StmtPrinter.cpp +++ lib/AST/StmtPrinter.cpp @@ -1529,6 +1529,53 @@ } } +void StmtPrinter::VisitFixedPointLiteral(FixedPointLiteral *Node) { + if (Policy.ConstantsAsWritten && printExprAsWritten(OS, Node, Context)) + return; + OS << Node->getValueAsString(/*Radix=*/10); + + switch (Node->getType()->getAs()->getKind()) { + default: + llvm_unreachable("Unexpected type for fixed point literal!"); + case BuiltinType::ShortFract: + OS << "hr"; + break; + case BuiltinType::ShortAccum: + OS << "hk"; + break; + case BuiltinType::UShortFract: + OS << "uhr"; + break; + case BuiltinType::UShortAccum: + OS << "uhk"; + break; + case BuiltinType::Fract: + OS << "r"; + break; + case BuiltinType::Accum: + OS << "k"; + break; + case BuiltinType::UFract: + OS << "ur"; + break; + case BuiltinType::UAccum: + OS << "uk"; + break; + case BuiltinType::LongFract: + OS << "lr"; + break; + case BuiltinType::LongAccum: + OS << "lk"; + break; + case BuiltinType::ULongFract: + OS << "ulr"; + break; + case BuiltinType::ULongAccum: + OS << "ulk"; + break; + } +} + static void PrintFloatingLiteral(raw_ostream &OS, FloatingLiteral *Node, bool PrintSuffix) { SmallString<16> Str; Index: lib/AST/StmtProfile.cpp =================================================================== --- lib/AST/StmtProfile.cpp +++ lib/AST/StmtProfile.cpp @@ -1007,6 +1007,12 @@ ID.AddInteger(S->getType()->castAs()->getKind()); } +void StmtProfiler::VisitFixedPointLiteral(const FixedPointLiteral *S) { + VisitExpr(S); + S->getValue().Profile(ID); + ID.AddInteger(S->getType()->castAs()->getKind()); +} + void StmtProfiler::VisitCharacterLiteral(const CharacterLiteral *S) { VisitExpr(S); ID.AddInteger(S->getKind()); Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -50,6 +50,7 @@ #include #include #include +#include using namespace clang; @@ -1936,6 +1937,7 @@ if (BT->getKind() == BuiltinType::NullPtr) return STK_CPointer; if (BT->isInteger()) return STK_Integral; if (BT->isFloatingPoint()) return STK_Floating; + if (BT->isFixedPointType()) return STK_FixedPoint; llvm_unreachable("unknown scalar builtin type"); } else if (isa(T)) { return STK_CPointer; @@ -3986,3 +3988,19 @@ CXXRecordDecl *MemberPointerType::getMostRecentCXXRecordDecl() const { return getClass()->getAsCXXRecordDecl()->getMostRecentNonInjectedDecl(); } + +std::string clang::FixedPointValueToString(unsigned Radix, unsigned Scale, + uint64_t Val) { + std::ostringstream s; + uint64_t ScaleVal = 1ULL << Scale; + uint64_t IntPart = Val / ScaleVal; + uint64_t FractPart = Val % ScaleVal; + + s << IntPart; + s << "."; + do { + s << (FractPart * Radix / ScaleVal); + FractPart = (FractPart * Radix) % ScaleVal; + } while (FractPart); + return s.str(); +} Index: lib/Basic/TargetInfo.cpp =================================================================== --- lib/Basic/TargetInfo.cpp +++ lib/Basic/TargetInfo.cpp @@ -40,12 +40,26 @@ IntWidth = IntAlign = 32; LongWidth = LongAlign = 32; LongLongWidth = LongLongAlign = 64; + + // Fixed point default bit widths ShortAccumWidth = ShortAccumAlign = 16; AccumWidth = AccumAlign = 32; LongAccumWidth = LongAccumAlign = 64; ShortFractWidth = ShortFractAlign = 16; FractWidth = FractAlign = 32; LongFractWidth = LongFractAlign = 64; + + // Fixed point default integral and fractional bit sizes + ShortAccumFBits = ShortFractFBits = 7; + ShortAccumIBits = UnsignedShortAccumIBits = 8; + AccumFBits = FractFBits = 15; + AccumIBits = UnsignedAccumIBits = 16; + LongAccumFBits = LongFractFBits = 31; + LongAccumIBits = UnsignedLongAccumIBits = 32; + UnsignedShortAccumFBits = UnsignedShortFractFBits = 8; + UnsignedAccumFBits = UnsignedFractFBits = 16; + UnsignedLongAccumFBits = UnsignedLongFractFBits = 32; + SuitableAlign = 64; DefaultAlignForAttributeAligned = 128; MinGlobalAlign = 0; @@ -303,6 +317,7 @@ return false; }; } +#include "llvm/Support/raw_ostream.h" /// adjust - Set forced language options. /// Apply changes to the target information with respect to certain @@ -362,6 +377,17 @@ if (Opts.NewAlignOverride) NewAlign = Opts.NewAlignOverride * getCharWidth(); + + // Each unsigned fixed point type has the same number of fractional bits as + // its corresponding signed type. + if (Opts.SameFBits) { + UnsignedShortAccumFBits = ShortAccumFBits; + UnsignedAccumFBits = AccumFBits; + UnsignedLongAccumFBits = LongAccumFBits; + UnsignedShortFractFBits = ShortFractFBits; + UnsignedFractFBits = FractFBits; + UnsignedLongFractFBits = LongFractFBits; + } } bool TargetInfo::initFeatureMap( @@ -716,3 +742,62 @@ return true; } + +void TargetInfo::CheckFixedPointBits() const { + // Check that the number of fractional and integral bits (and maybe sign) can + // fit into the bits given for a fixed point type. + assert(ShortAccumFBits + ShortAccumIBits + 1 <= ShortAccumWidth); + assert(AccumFBits + AccumIBits + 1 <= AccumWidth); + assert(LongAccumFBits + LongAccumIBits + 1 <= LongAccumWidth); + assert(UnsignedShortAccumFBits + UnsignedShortAccumIBits <= ShortAccumWidth); + assert(UnsignedAccumFBits + UnsignedAccumIBits <= AccumWidth); + assert(UnsignedLongAccumFBits + UnsignedLongAccumIBits <= LongAccumWidth); + + assert(ShortFractFBits + 1 <= ShortFractWidth); + assert(FractFBits + 1 <= FractWidth); + assert(LongFractFBits + 1 <= LongFractWidth); + assert(UnsignedShortFractFBits <= ShortFractWidth); + assert(UnsignedFractFBits <= FractWidth); + assert(UnsignedLongFractFBits <= LongFractWidth); + + // Each unsigned fract type has either the same number of fractional bits + // as, or one more fractional bit than, its corresponding signed fract type. + assert(ShortFractFBits == UnsignedShortFractFBits || + ShortFractFBits == UnsignedShortFractFBits - 1); + assert(FractFBits == UnsignedFractFBits || + FractFBits == UnsignedFractFBits - 1); + assert(LongFractFBits == UnsignedLongFractFBits || + LongFractFBits == UnsignedLongFractFBits - 1); + + // When arranged in order of increasing rank (see 6.3.1.3a), the number of + // fractional bits is nondecreasing for each of the following sets of + // fixed-point types: + // - signed fract types + // - unsigned fract types + // - signed accum types + // - unsigned accum types. + assert(LongFractFBits >= FractFBits && FractFBits >= ShortFractFBits); + assert(UnsignedLongFractFBits >= UnsignedFractFBits && + UnsignedFractFBits >= UnsignedShortFractFBits); + assert(LongAccumFBits >= AccumFBits && AccumFBits >= ShortAccumFBits); + assert(UnsignedLongAccumFBits >= UnsignedAccumFBits && + UnsignedAccumFBits >= UnsignedShortAccumFBits); + + // When arranged in order of increasing rank (see 6.3.1.3a), the number of + // integral bits is nondecreasing for each of the following sets of + // fixed-point types: + // - signed accum types + // - unsigned accum types + assert(LongAccumIBits >= AccumIBits && AccumIBits >= ShortAccumIBits); + assert(UnsignedLongAccumIBits >= UnsignedAccumIBits && + UnsignedAccumIBits >= UnsignedShortAccumIBits); + + // Each signed accum type has at least as many integral bits as its + // corresponding unsigned accum type. + assert(ShortAccumIBits == UnsignedShortAccumIBits || + ShortAccumIBits == UnsignedShortAccumIBits - 1); + assert(AccumIBits == UnsignedAccumIBits || + AccumIBits == UnsignedAccumIBits - 1); + assert(LongAccumIBits == UnsignedLongAccumIBits || + LongAccumIBits == UnsignedLongAccumIBits - 1); +} Index: lib/Basic/Targets.cpp =================================================================== --- lib/Basic/Targets.cpp +++ lib/Basic/Targets.cpp @@ -652,5 +652,7 @@ if (!Target->validateTarget(Diags)) return nullptr; + Target->CheckFixedPointBits(); + return Target.release(); } Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -4051,6 +4051,9 @@ /// cast from scalar to union. LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { switch (E->getCastKind()) { + case CK_IntegralToFixedPoint: + llvm_unreachable( + "CodeGenFunction::EmitCastLValue CK_IntegralToFixedPoint"); // TODO case CK_ToVoid: case CK_BitCast: case CK_ArrayToPointerDecay: Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -684,12 +684,12 @@ CGF.EmitDynamicCast(LV.getAddress(), cast(E)); else CGF.CGM.ErrorUnsupported(E, "non-simple lvalue dynamic_cast"); - + if (!Dest.isIgnored()) CGF.CGM.ErrorUnsupported(E, "lvalue dynamic_cast with a destination"); break; } - + case CK_ToUnion: { // Evaluate even if the destination is ignored. if (Dest.isIgnored()) { @@ -851,6 +851,7 @@ case CK_ZeroToOCLQueue: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_IntegralToFixedPoint: llvm_unreachable("cast kind invalid for aggregate types"); } } Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -509,6 +509,7 @@ case CK_ZeroToOCLQueue: case CK_AddressSpaceConversion: case CK_IntToOCLSampler: + case CK_IntegralToFixedPoint: llvm_unreachable("invalid cast kind for complex value"); case CK_FloatingRealToComplex: Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ lib/CodeGen/CGExprConstant.cpp @@ -740,6 +740,8 @@ Expr *subExpr = E->getSubExpr(); switch (E->getCastKind()) { + case CK_IntegralToFixedPoint: + llvm_unreachable("VisitCastExpr CK_IntegralToFixedPoint"); // TODO case CK_ToUnion: { // GCC cast to union extension assert(E->getType()->isUnionType() && Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -23,6 +23,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/Optional.h" @@ -387,6 +388,9 @@ Value *VisitIntegerLiteral(const IntegerLiteral *E) { return Builder.getInt(E->getValue()); } + Value *VisitFixedPointLiteral(const FixedPointLiteral *E) { + return Builder.getInt(E->getValue()); + } Value *VisitFloatingLiteral(const FloatingLiteral *E) { return llvm::ConstantFP::get(VMContext, E->getValue()); } @@ -1772,6 +1776,15 @@ return Builder.CreateVectorSplat(NumElements, Elt, "splat"); } + case CK_IntegralToFixedPoint: { + assert(DestTy->isFixedPointType()); + assert(E->getType()->isIntegerType()); + unsigned scale = CGF.getContext().getFixedPointScale(DestTy); + return Builder.CreateShl( + EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()), + scale, "integral_to_fixed_point"); + } + case CK_IntegralCast: case CK_IntegralToFloating: case CK_FloatingToIntegral: Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -3761,6 +3761,11 @@ /*Default=*/false)) Args.AddLastArg(CmdArgs, options::OPT_ffixed_point); + if (Args.hasFlag(options::OPT_fsame_fbits, + options::OPT_fno_same_fbits, + /*Default=*/false)) + Args.AddLastArg(CmdArgs, options::OPT_fsame_fbits); + // Handle -{std, ansi, trigraphs} -- take the last of -{std, ansi} // (-ansi is equivalent to -std=c89 or -std=c++98). // Index: lib/Edit/RewriteObjCFoundationAPI.cpp =================================================================== --- lib/Edit/RewriteObjCFoundationAPI.cpp +++ lib/Edit/RewriteObjCFoundationAPI.cpp @@ -1002,6 +1002,9 @@ if (const ImplicitCastExpr *ICE = dyn_cast(Arg)) { switch (ICE->getCastKind()) { + case CK_IntegralToFixedPoint: + llvm_unreachable( + "rewriteToNumericBoxedExpression CK_IntegralToFixedPoint"); // TODO case CK_LValueToRValue: case CK_NoOp: case CK_UserDefinedConversion: Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2337,6 +2337,10 @@ Opts.FixedPoint = Args.hasFlag(OPT_ffixed_point, OPT_fno_fixed_point, /*Default=*/false) && !Opts.CPlusPlus; + Opts.SameFBits = + Args.hasFlag(OPT_fsame_fbits, OPT_fno_same_fbits, + /*Default=*/false) && + Opts.FixedPoint; // Handle exception personalities Arg *A = Args.getLastArg(options::OPT_fsjlj_exceptions, Index: lib/Index/USRGeneration.cpp =================================================================== --- lib/Index/USRGeneration.cpp +++ lib/Index/USRGeneration.cpp @@ -743,6 +743,7 @@ c = 'e'; break; } Out << c; + return; } Index: lib/Lex/LiteralSupport.cpp =================================================================== --- lib/Lex/LiteralSupport.cpp +++ lib/Lex/LiteralSupport.cpp @@ -27,6 +27,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ScaledNumber.h" #include #include #include @@ -538,6 +539,7 @@ saw_exponent = false; saw_period = false; saw_ud_suffix = false; + saw_fixed_point_suffix = false; isLong = false; isUnsigned = false; isLongLong = false; @@ -547,6 +549,8 @@ isFloat16 = false; isFloat128 = false; MicrosoftInteger = 0; + isFract = false; + isAccum = false; hadError = false; if (*s == '0') { // parse radix @@ -568,6 +572,14 @@ SuffixBegin = s; checkSeparator(TokLoc, s, CSK_AfterDigits); + // Initial scan to lookahead for fixed point suffix. + for (const char *c = s; c != ThisTokEnd; ++c) { + if (*c == 'r' || *c == 'k' || *c == 'R' || *c == 'K') { + saw_fixed_point_suffix = true; + break; + } + } + // Parse the suffix. At this point we can classify whether we have an FP or // integer constant. bool isFPConstant = isFloatingLiteral(); @@ -576,11 +588,21 @@ // we break out of the loop. for (; s != ThisTokEnd; ++s) { switch (*s) { + case 'R': + case 'r': + if (isFract || isAccum) break; + isFract = true; + continue; + case 'K': + case 'k': + if (isFract || isAccum) break; + isAccum = true; + continue; case 'h': // FP Suffix for "half". case 'H': // OpenCL Extension v1.2 s9.5 - h or H suffix for half type. - if (!PP.getLangOpts().Half) break; - if (!isFPConstant) break; // Error for integer constant. + if (!(PP.getLangOpts().Half || PP.getLangOpts().FixedPoint)) break; + if (isIntegerLiteral()) break; // Error for integer constant. if (isHalf || isFloat || isLong) break; // HH, FH, LH invalid. isHalf = true; continue; // Success. @@ -693,6 +715,9 @@ isHalf = false; isImaginary = false; MicrosoftInteger = 0; + saw_fixed_point_suffix = false; + isFract = false; + isAccum = false; } saw_ud_suffix = true; @@ -707,6 +732,11 @@ hadError = true; } } + + if (!hadError && saw_fixed_point_suffix) { + assert(isFract || isAccum); + assert(radix == 16 || radix == 10); + } } /// ParseDecimalOrOctalCommon - This method is called for decimal or octal @@ -1012,6 +1042,99 @@ return Result.convertFromString(Str, APFloat::rmNearestTiesToEven); } +bool NumericLiteralParser::GetFixedPointValue(uint64_t &Val, unsigned Scale) { + assert(radix == 16 || radix == 10); + + // Need as much storage space as possible + __int128 TmpVal = 0; + bool IntOverflowOccurred = false; + bool FractOverflowOccurred = false; + bool FoundExponent = false; + bool FoundDecimal = false; + + int64_t BaseShift = 0; + const char *Ptr = DigitsBegin; + for (; Ptr < SuffixBegin; ++Ptr) { + if (*Ptr == '.') { + FoundDecimal = true; + continue; + } + if (*Ptr == 'p' || *Ptr == 'P' || *Ptr == 'e' || *Ptr == 'E') { + FoundExponent = true; + ++Ptr; + break; + } + + // Normal reading of an integer + unsigned C = llvm::hexDigitValue(*Ptr); + assert(C < radix && "NumericLiteralParser ctor should have rejected this"); + + auto OldVal = TmpVal; + TmpVal *= radix; + if (!FoundDecimal) + IntOverflowOccurred |= (TmpVal / radix != OldVal); + else + FractOverflowOccurred |= (TmpVal / radix != OldVal); + + TmpVal += C; + if (!FoundDecimal) + IntOverflowOccurred |= (TmpVal < C); + else + FractOverflowOccurred |= (TmpVal < C); + + if (FractOverflowOccurred) { + TmpVal = OldVal; + break; + } else if (FoundDecimal) { + --BaseShift; + } + } + + // For a radix of 16, we will be multiplying by 2 instead of 16. + if (radix == 16) BaseShift *= 4; + + bool ExpOverflowOccurred = false; + if (FoundExponent) { + uint64_t Exponent = 0; + bool NegativeExponent = *Ptr == '-'; + if (NegativeExponent) ++Ptr; + for (; Ptr < SuffixBegin; ++Ptr) { + unsigned C = llvm::hexDigitValue(*Ptr); + assert(C < 10 && "NumericLiteralParser ctor should have rejected this"); + + uint64_t OldExponent = Exponent; + Exponent *= 10; + ExpOverflowOccurred |= (Exponent / 10 != OldExponent); + Exponent += C; + ExpOverflowOccurred |= (Exponent < C); + } + + if (NegativeExponent) + BaseShift -= Exponent; + else + BaseShift += Exponent; + } + + auto OldVal = TmpVal; + TmpVal <<= Scale; + IntOverflowOccurred |= (TmpVal >> Scale) != OldVal; + + uint64_t Base = (radix == 16) ? 2 : 10; + if (BaseShift > 0) { + for (int64_t i = 0; i < BaseShift && !IntOverflowOccurred; ++i) { + OldVal = TmpVal; + TmpVal *= Base; + IntOverflowOccurred |= (TmpVal / Base != OldVal); + } + } else if (BaseShift < 0) { + for (int64_t i = BaseShift; i < 0 && TmpVal; ++i) TmpVal /= Base; + } + + IntOverflowOccurred |= TmpVal > std::numeric_limits::max(); + Val = TmpVal; + return IntOverflowOccurred || ExpOverflowOccurred; +} + /// \verbatim /// user-defined-character-literal: [C++11 lex.ext] /// character-literal ud-suffix Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -532,6 +532,9 @@ case Type::STK_Floating: return CK_FloatingToBoolean; case Type::STK_IntegralComplex: return CK_IntegralComplexToBoolean; case Type::STK_FloatingComplex: return CK_FloatingComplexToBoolean; + case Type::STK_FixedPoint: + llvm_unreachable( + "Sema::ScalarTypeToBooleanCastKind for fixed point"); // TODO } llvm_unreachable("unknown scalar type kind"); } Index: lib/Sema/SemaExceptionSpec.cpp =================================================================== --- lib/Sema/SemaExceptionSpec.cpp +++ lib/Sema/SemaExceptionSpec.cpp @@ -1258,6 +1258,7 @@ case Expr::ImaginaryLiteralClass: case Expr::ImplicitValueInitExprClass: case Expr::IntegerLiteralClass: + case Expr::FixedPointLiteralClass: case Expr::ArrayInitIndexExprClass: case Expr::NoInitExprClass: case Expr::ObjCEncodeExprClass: Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -26,6 +26,7 @@ #include "clang/AST/ExprOpenMP.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/TypeLoc.h" +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" @@ -1218,6 +1219,46 @@ CK_IntegralRealToComplex); return ComplexType; } +/// \brief Handle arithmetic conversion from integer to fixed point. Helper +/// function of UsualArithmeticConversions() +static QualType handleIntToFixedPointConversion(Sema &S, + ExprResult &FixedPointExpr, + ExprResult &IntExpr, + QualType FixedPointTy, + QualType IntTy) { + assert(IntTy->isIntegerType()); + assert(FixedPointTy->isFixedPointType()); + + IntExpr = + S.ImpCastExprToType(IntExpr.get(), FixedPointTy, CK_IntegralToFixedPoint); + return FixedPointTy; +} + +/// \brief Handle arithmethic conversion with fixed point types. Helper +/// function of UsualArithmeticConversions(). +static QualType handleFixedPointConversion(Sema &S, ExprResult &LHS, + ExprResult &RHS, QualType LHSType, + QualType RHSType, + bool IsCompAssign) { + // At this point, the only other type we should be able to convert to is + // integer types since all prior types were handled beforehand in + // UsualArithmeticConventions(). + bool LHSFixed = LHSType->isFixedPointType(); + bool RHSFixed = RHSType->isFixedPointType(); + + if (LHSFixed && RHSFixed) { + // Cast up the smaller operand to the bigger + llvm_unreachable("Unhandled conversion between fixed point types"); // TODO + } else if (LHSFixed) { + assert(RHSType->isIntegerType()); + return handleIntToFixedPointConversion(S, LHS, RHS, LHSType, RHSType); + } else if (RHSFixed) { + assert(LHSType->isIntegerType()); + return handleIntToFixedPointConversion(S, RHS, LHS, RHSType, LHSType); + } else { + llvm_unreachable("Expected LHS and RHS to both be fixed point types."); + } +} /// UsualArithmeticConversions - Performs various conversions that are common to /// binary operators (C99 6.3.1.8). If both operands aren't arithmetic, this @@ -1291,6 +1332,11 @@ return handleComplexIntConversion(*this, LHS, RHS, LHSType, RHSType, IsCompAssign); + // Handle fixed point types + if (LHSType->isFixedPointType() || RHSType->isFixedPointType()) + return handleFixedPointConversion(*this, LHS, RHS, LHSType, RHSType, + IsCompAssign); + // Finally, we have two differing integer types. return handleIntegerConversion (*this, LHS, RHS, LHSType, RHSType, IsCompAssign); @@ -3144,33 +3190,40 @@ Context.IntTy, Loc); } -static Expr *BuildFloatingLiteral(Sema &S, NumericLiteralParser &Literal, - QualType Ty, SourceLocation Loc) { - const llvm::fltSemantics &Format = S.Context.getFloatTypeSemantics(Ty); - - using llvm::APFloat; - APFloat Val(Format); - - APFloat::opStatus result = Literal.GetFloatValue(Val); - +static void HandleFloatOverflow(Sema &S, QualType Ty, SourceLocation Loc, + const llvm::APFloat::opStatus &result, + const llvm::APFloat &Val, + const llvm::fltSemantics &Format) { // Overflow is always an error, but underflow is only an error if // we underflowed to zero (APFloat reports denormals as underflow). - if ((result & APFloat::opOverflow) || - ((result & APFloat::opUnderflow) && Val.isZero())) { + if ((result & llvm::APFloat::opOverflow) || + ((result & llvm::APFloat::opUnderflow) && Val.isZero())) { unsigned diagnostic; SmallString<20> buffer; - if (result & APFloat::opOverflow) { + if (result & llvm::APFloat::opOverflow) { diagnostic = diag::warn_float_overflow; - APFloat::getLargest(Format).toString(buffer); + llvm::APFloat::getLargest(Format).toString(buffer); } else { diagnostic = diag::warn_float_underflow; - APFloat::getSmallest(Format).toString(buffer); + llvm::APFloat::getSmallest(Format).toString(buffer); } S.Diag(Loc, diagnostic) << Ty << StringRef(buffer.data(), buffer.size()); } +} + +static Expr *BuildFloatingLiteral(Sema &S, NumericLiteralParser &Literal, + QualType Ty, SourceLocation Loc) { + const llvm::fltSemantics &Format = S.Context.getFloatTypeSemantics(Ty); + + using llvm::APFloat; + APFloat Val(Format); + + APFloat::opStatus result = Literal.GetFloatValue(Val); + + HandleFloatOverflow(S, Ty, Loc, result, Val, Format); bool isExact = (result == APFloat::opOK); return FloatingLiteral::Create(S.Context, Val, isExact, Ty, Loc); @@ -3326,7 +3379,65 @@ Expr *Res; - if (Literal.isFloatingLiteral()) { + if (Literal.isFixedPointLiteral()) { + QualType Ty; + + if (Literal.isAccum) { + if (Literal.isHalf) { + Ty = Context.ShortAccumTy; + } else if (Literal.isLong) { + Ty = Context.LongAccumTy; + } else { + Ty = Context.AccumTy; + } + } else if (Literal.isFract) { + if (Literal.isHalf) { + Ty = Context.ShortFractTy; + } else if (Literal.isLong) { + Ty = Context.LongFractTy; + } else { + Ty = Context.FractTy; + } + } + + if (Literal.isUnsigned) Ty = Context.getCorrespondingUnsignedType(Ty); + + bool isSigned = !Literal.isUnsigned; + unsigned scale = Context.getFixedPointScale(Ty); + unsigned ibits = Context.getFixedPointIBits(Ty); + unsigned bit_width = Context.getTypeInfo(Ty).Width; + + uint64_t Val; + bool Overflowed = Literal.GetFixedPointValue(Val, scale); + + // Throw an error if we receive a literal that cannot fit into the type. + if (Literal.isFract) { + uint64_t MaxVal = 1ULL << scale; + if (Val > MaxVal) { + Diag(Tok.getLocation(), diag::err_integral_part_on_fract); + } else if (Val == MaxVal) { + // Clause 6.4.4 - The value of a constant shall be in the range of + // representable values for its type, with exception for constants of a + // fract type with a value of exactly 1; such a constant shall denote + // the maximal value for the type. + --Val; + } + } else { + // Split the underlying integer into an integral and fractional part to + // prevent potential overflow when comparing against a single maximum + // value. + uint64_t MaxUnscaledVal = (1ULL << ibits) - 1; + uint64_t UnscaledVal = Val >> scale; + if (UnscaledVal > MaxUnscaledVal || Overflowed) { + Diag(Tok.getLocation(), diag::err_accum_too_large) + << (isSigned ? 0 : 1); + } + } + + llvm::APInt ResultVal(bit_width, Val, isSigned); + Res = FixedPointLiteral::CreateFromRawInt(Context, ResultVal, Ty, + Tok.getLocation(), scale); + } else if (Literal.isFloatingLiteral()) { QualType Ty; if (Literal.isHalf){ if (getOpenCLOptions().isEnabled("cl_khr_fp16")) @@ -5807,13 +5918,21 @@ case Type::STK_FloatingComplex: case Type::STK_IntegralComplex: case Type::STK_MemberPointer: + case Type::STK_FixedPoint: llvm_unreachable("illegal cast from pointer"); } llvm_unreachable("Should have returned before this"); + case Type::STK_FixedPoint: + llvm_unreachable( + "Sema::PrepareScalarCast from STK_FixedPoint to anything"); // TODO + case Type::STK_Bool: // casting from bool is like casting from an integer case Type::STK_Integral: switch (DestTy->getScalarTypeKind()) { + case Type::STK_FixedPoint: + llvm_unreachable( + "Sema::PrepareScalarCast from STK_Integral to STK_FixedPoint"); // TODO case Type::STK_CPointer: case Type::STK_ObjCObjectPointer: case Type::STK_BlockPointer: @@ -5844,6 +5963,9 @@ case Type::STK_Floating: switch (DestTy->getScalarTypeKind()) { + case Type::STK_FixedPoint: + llvm_unreachable( + "Sema::PrepareScalarCast from STK_Floating to STK_FixedPoint"); // TODO case Type::STK_Floating: return CK_FloatingCast; case Type::STK_Bool: @@ -5871,6 +5993,10 @@ case Type::STK_FloatingComplex: switch (DestTy->getScalarTypeKind()) { + case Type::STK_FixedPoint: + llvm_unreachable( + "Sema::PrepareScalarCast from STK_FloatingComplex to " + "STK_FixedPoint"); // TODO case Type::STK_FloatingComplex: return CK_FloatingComplexCast; case Type::STK_IntegralComplex: @@ -5900,6 +6026,10 @@ case Type::STK_IntegralComplex: switch (DestTy->getScalarTypeKind()) { + case Type::STK_FixedPoint: + llvm_unreachable( + "Sema::PrepareScalarCast from STK_IntegralComplex to " + "STK_FixedPoint"); // TODO case Type::STK_FloatingComplex: return CK_IntegralComplexToFloatingComplex; case Type::STK_IntegralComplex: @@ -13122,7 +13252,7 @@ CondExpr = CondICE.get(); CondIsTrue = condEval.getZExtValue(); - // If the condition is > zero, then the AST type is the same as the LSHExpr. + // If the condition is > zero, then the AST type is the same as the LHSExpr. Expr *ActiveExpr = CondIsTrue ? LHSExpr : RHSExpr; resType = ActiveExpr->getType(); Index: lib/Sema/TreeTransform.h =================================================================== --- lib/Sema/TreeTransform.h +++ lib/Sema/TreeTransform.h @@ -8888,6 +8888,12 @@ return E; } +template +ExprResult TreeTransform::TransformFixedPointLiteral( + FixedPointLiteral *E) { + return E; +} + template ExprResult TreeTransform::TransformFloatingLiteral(FloatingLiteral *E) { Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -533,6 +533,12 @@ E->setValue(Record.getContext(), Record.readAPInt()); } +void ASTStmtReader::VisitFixedPointLiteral(FixedPointLiteral *E) { + VisitExpr(E); + E->setLocation(ReadSourceLocation()); + E->setValue(Record.getContext(), Record.readAPInt()); +} + void ASTStmtReader::VisitFloatingLiteral(FloatingLiteral *E) { VisitExpr(E); E->setRawSemantics(static_cast(Record.readInt())); Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -444,6 +444,10 @@ Code = serialization::EXPR_INTEGER_LITERAL; } +void ASTStmtWriter::VisitFixedPointLiteral(FixedPointLiteral *E) { + llvm_unreachable("ASTStmtWriter::VisitFixedPointLiteral"); +} + void ASTStmtWriter::VisitFloatingLiteral(FloatingLiteral *E) { VisitExpr(E); Record.push_back(E->getRawSemantics()); Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1297,6 +1297,7 @@ case Stmt::AddrLabelExprClass: case Stmt::AttributedStmtClass: case Stmt::IntegerLiteralClass: + case Stmt::FixedPointLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::ImplicitValueInitExprClass: case Stmt::CXXScalarValueInitExprClass: Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -328,6 +328,9 @@ const LocationContext *LCtx = Pred->getLocationContext(); switch (CastE->getCastKind()) { + case CK_IntegralToFixedPoint: + llvm_unreachable( + "ExprEngine::VisitCast CK_IntegralToFixedPoint"); // TODO case CK_LValueToRValue: llvm_unreachable("LValueToRValue casts handled earlier."); case CK_ToVoid: Index: test/Frontend/fixed_point.c =================================================================== --- test/Frontend/fixed_point.c +++ test/Frontend/fixed_point.c @@ -236,4 +236,164 @@ //CHECK-NEXT: |-VarDecl {{.*}} SatlA_type 'SatlA_t':'_Sat long _Accum' //CHECK-NEXT: |-VarDecl {{.*}} SatsF_type 'SatsF_t':'_Sat short _Fract' //CHECK-NEXT: |-VarDecl {{.*}} SatF_type 'SatF_t':'_Sat _Fract' -//CHECK-NEXT: `-VarDecl {{.*}} SatlF_type 'SatlF_t':'_Sat long _Fract' +//CHECK-NEXT: |-VarDecl {{.*}} SatlF_type 'SatlF_t':'_Sat long _Fract' + +// Fixed point literal exponent syntax +_Accum decexp1 = 1.575e1k; +_Accum decexp2 = 1.575E1k; +_Accum decexp3 = 1575e-2k; +_Accum decexp4 = 1575E-2k; + +_Accum hexexp1 = 0x0.3p10k; +_Accum hexexp2 = 0x0.3P10k; +_Accum hexexp3 = 0x30000p-10k; +_Accum hexexp4 = 0x30000P-10k; + +_Accum zeroexp1 = 1e0k; +_Accum zeroexp2 = 1e-0k; + +//CHECK-NEXT: |-VarDecl {{.*}} decexp1 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 15.75 +//CHECK-NEXT: |-VarDecl {{.*}} decexp2 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 15.75 +//CHECK-NEXT: |-VarDecl {{.*}} decexp3 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 15.75 +//CHECK-NEXT: |-VarDecl {{.*}} decexp4 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 15.75 + +//CHECK-NEXT: |-VarDecl {{.*}} hexexp1 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 192.0 +//CHECK-NEXT: |-VarDecl {{.*}} hexexp2 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 192.0 +//CHECK-NEXT: |-VarDecl {{.*}} hexexp3 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 192.0 +//CHECK-NEXT: |-VarDecl {{.*}} hexexp4 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 192.0 + +//CHECK-NEXT: |-VarDecl {{.*}} zeroexp1 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 1.0 +//CHECK-NEXT: |-VarDecl {{.*}} zeroexp2 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 1.0 + +// Fixed point literal values +_Accum literal1 = 2.5k; // Precise decimal +_Accum literal2 = 0.0k; // Zero +_Accum literal3 = 1.1k; // Imprecise decimal +_Accum literal4 = 1.11k; +_Accum literal5 = 1.111k; +_Accum literal6 = 1.1111k; +_Accum literal7 = 1.11111k; // After some point after the radix, adding any more + // digits to the literal will not result in any + // further precision since the nth digit added may + // be less than the precision that can be + // represented by the fractional bits of the type. + // This results in the same value being stored for + // the type. + +//CHECK-NEXT: |-VarDecl {{.*}} literal1 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 2.5 +//CHECK-NEXT: |-VarDecl {{.*}} literal2 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 0.0 +//CHECK-NEXT: |-VarDecl {{.*}} literal3 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 1.0999755859375 +//CHECK-NEXT: |-VarDecl {{.*}} literal4 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 1.1099853515625 +//CHECK-NEXT: |-VarDecl {{.*}} literal5 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 1.110992431640625 +//CHECK-NEXT: |-VarDecl {{.*}} literal6 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 1.111083984375 +//CHECK-NEXT: |-VarDecl {{.*}} literal7 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 1.111083984375 + +long _Accum longaccumliteral = 0.99999999lk; +long _Accum longaccumliteral2 = 0.999999999lk; +long _Accum verylongaccumliteral = 0.99999999999999999999999999lk; +long _Fract longfractliteral = 0.99999999lr; +long _Fract longfractliteral2 = 0.999999999lr; +long _Fract verylongfractliteral = 0.99999999999999999999999999lr; + +//CHECK-NEXT: |-VarDecl {{.*}} longaccumliteral 'long _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Accum' 0.999999989755451679229736328125 +//CHECK-NEXT: |-VarDecl {{.*}} longaccumliteral2 'long _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Accum' 0.9999999986030161380767822265625 +//CHECK-NEXT: |-VarDecl {{.*}} verylongaccumliteral 'long _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Accum' 0.9999999995343387126922607421875 +//CHECK-NEXT: |-VarDecl {{.*}} longfractliteral 'long _Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Fract' 0.999999989755451679229736328125 +//CHECK-NEXT: |-VarDecl {{.*}} longfractliteral2 'long _Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Fract' 0.9999999986030161380767822265625 +//CHECK-NEXT: |-VarDecl {{.*}} verylongfractliteral 'long _Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Fract' 0.9999999995343387126922607421875 + +unsigned _Accum uliteral1 = 2.5uk; // Unsigned +_Accum literal8 = -2.5k; // Negative + +//CHECK-NEXT: |-VarDecl {{.*}} uliteral1 'unsigned _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'unsigned _Accum' 2.5 +//CHECK-NEXT: |-VarDecl {{.*}} literal8 '_Accum' cinit +//CHECK-NEXT: `-UnaryOperator {{.*}} '_Accum' prefix '-' +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 2.5 + +short _Accum literalexact1 = 0.9921875hk; // Exact value +_Accum literalexact2 = 0.999969482421875k; + +//CHECK-NEXT: |-VarDecl {{.*}} literalexact1 'short _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'short _Accum' 0.9921875 +//CHECK-NEXT: |-VarDecl {{.*}} literalexact2 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 0.999969482421875 + +// Unfortunately we do not have enough space to store the exact decimal value of +// 0.9999999995343387126922607421875 ((1 << 31) - 1), but we can still use a +// large number of 9s to get the max fractional value. +long _Accum long_accum_max = 0.999999999999999999999999999lk; + +//CHECK-NEXT: |-VarDecl {{.*}} long_accum_max 'long _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Accum' 0.9999999995343387126922607421875 + +// Epsilon +short _Accum short_accum_eps = 0.0078125hk; +short _Accum short_accum_eps2 = 0.0078124hk; // Less than epsilon floors to zero +_Accum accum_eps = 0.000030517578125k; +_Accum accum_eps2 = 0.000030517578124k; +long _Accum long_accum_eps = 0x1p-31lk; +long _Accum long_accum_eps2 = 0x0.99999999p-31lk; + +//CHECK-NEXT: |-VarDecl {{.*}} short_accum_eps 'short _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'short _Accum' 0.0078125 +//CHECK-NEXT: |-VarDecl {{.*}} short_accum_eps2 'short _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'short _Accum' 0.0 +//CHECK-NEXT: |-VarDecl {{.*}} accum_eps '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 0.000030517578125 +//CHECK-NEXT: |-VarDecl {{.*}} accum_eps2 '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 0.0 +//CHECK-NEXT: |-VarDecl {{.*}} long_accum_eps 'long _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Accum' 0.0000000004656612873077392578125 +//CHECK-NEXT: |-VarDecl {{.*}} long_accum_eps2 'long _Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Accum' 0.0 + +// Fract literals can be one but evaluate to the respective Fract max +short _Fract short_fract_one = 1.0hr; +_Fract fract_one = 1.0r; +long _Fract long_fract_one = 1.0lr; +unsigned short _Fract u_short_fract_one = 1.0uhr; +unsigned _Fract u_fract_one = 1.0ur; +unsigned long _Fract u_long_fract_one = 1.0ulr; + +//CHECK-NEXT: |-VarDecl {{.*}} short_fract_one 'short _Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'short _Fract' 0.9921875 +//CHECK-NEXT: |-VarDecl {{.*}} fract_one '_Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Fract' 0.999969482421875 +//CHECK-NEXT: |-VarDecl {{.*}} long_fract_one 'long _Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'long _Fract' 0.9999999995343387126922607421875 + +//CHECK-NEXT: |-VarDecl {{.*}} u_short_fract_one 'unsigned short _Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'unsigned short _Fract' 0.99609375 +//CHECK-NEXT: |-VarDecl {{.*}} u_fract_one 'unsigned _Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'unsigned _Fract' 0.9999847412109375 +//CHECK-NEXT: |-VarDecl {{.*}} u_long_fract_one 'unsigned long _Fract' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} 'unsigned long _Fract' 0.99999999976716935634613037109375 + +_Accum literallast = 1.0k; // One + +//CHECK-NEXT: `-VarDecl {{.*}} literallast '_Accum' cinit +//CHECK-NEXT: `-FixedPointLiteral {{.*}} '_Accum' 1.0 Index: test/Frontend/fixed_point_declarations.c =================================================================== --- /dev/null +++ test/Frontend/fixed_point_declarations.c @@ -0,0 +1,113 @@ +// RUN: %clang -ffixed-point -S -emit-llvm %s -o - | FileCheck %s +// RUN: %clang -ffixed-point -S -emit-llvm %s -o - --target=x86_64-scei-ps4-ubuntu-fast | FileCheck %s + +// Primary fixed point types +signed short _Accum s_short_accum; // CHECK-DAG: @s_short_accum = {{.*}}global i16 0, align 2 +signed _Accum s_accum; // CHECK-DAG: @s_accum = {{.*}}global i32 0, align 4 +signed long _Accum s_long_accum; // CHECK-DAG: @s_long_accum = {{.*}}global i64 0, align 8 +unsigned short _Accum u_short_accum; // CHECK-DAG: @u_short_accum = {{.*}}global i16 0, align 2 +unsigned _Accum u_accum; // CHECK-DAG: @u_accum = {{.*}}global i32 0, align 4 +unsigned long _Accum u_long_accum; // CHECK-DAG: @u_long_accum = {{.*}}global i64 0, align 8 +signed short _Fract s_short_fract; // CHECK-DAG: @s_short_fract = {{.*}}global i16 0, align 2 +signed _Fract s_fract; // CHECK-DAG: @s_fract = {{.*}}global i32 0, align 4 +signed long _Fract s_long_fract; // CHECK-DAG: @s_long_fract = {{.*}}global i64 0, align 8 +unsigned short _Fract u_short_fract; // CHECK-DAG: @u_short_fract = {{.*}}global i16 0, align 2 +unsigned _Fract u_fract; // CHECK-DAG: @u_fract = {{.*}}global i32 0, align 4 +unsigned long _Fract u_long_fract; // CHECK-DAG: @u_long_fract = {{.*}}global i64 0, align 8 + +// Aliased +short _Accum short_accum; // CHECK-DAG: @short_accum = {{.*}}global i16 0, align 2 +_Accum accum; // CHECK-DAG: @accum = {{.*}}global i32 0, align 4 +long _Accum long_accum; // CHECK-DAG: @long_accum = {{.*}}global i64 0, align 8 +short _Fract short_fract; // CHECK-DAG: @short_fract = {{.*}}global i16 0, align 2 +_Fract fract; // CHECK-DAG: @fract = {{.*}}global i32 0, align 4 +long _Fract long_fract; // CHECK-DAG: @long_fract = {{.*}}global i64 0, align 8 + +// Primary saturated +_Sat signed short _Accum sat_s_short_accum; // CHECK-DAG: @sat_s_short_accum = {{.*}}global i16 0, align 2 +_Sat signed _Accum sat_s_accum; // CHECK-DAG: @sat_s_accum = {{.*}}global i32 0, align 4 +_Sat signed long _Accum sat_s_long_accum; // CHECK-DAG: @sat_s_long_accum = {{.*}}global i64 0, align 8 +_Sat unsigned short _Accum sat_u_short_accum; // CHECK-DAG: @sat_u_short_accum = {{.*}}global i16 0, align 2 +_Sat unsigned _Accum sat_u_accum; // CHECK-DAG: @sat_u_accum = {{.*}}global i32 0, align 4 +_Sat unsigned long _Accum sat_u_long_accum; // CHECK-DAG: @sat_u_long_accum = {{.*}}global i64 0, align 8 +_Sat signed short _Fract sat_s_short_fract; // CHECK-DAG: @sat_s_short_fract = {{.*}}global i16 0, align 2 +_Sat signed _Fract sat_s_fract; // CHECK-DAG: @sat_s_fract = {{.*}}global i32 0, align 4 +_Sat signed long _Fract sat_s_long_fract; // CHECK-DAG: @sat_s_long_fract = {{.*}}global i64 0, align 8 +_Sat unsigned short _Fract sat_u_short_fract; // CHECK-DAG: @sat_u_short_fract = {{.*}}global i16 0, align 2 +_Sat unsigned _Fract sat_u_fract; // CHECK-DAG: @sat_u_fract = {{.*}}global i32 0, align 4 +_Sat unsigned long _Fract sat_u_long_fract; // CHECK-DAG: @sat_u_long_fract = {{.*}}global i64 0, align 8 + +// Aliased saturated +_Sat short _Accum sat_short_accum; // CHECK-DAG: @sat_short_accum = {{.*}}global i16 0, align 2 +_Sat _Accum sat_accum; // CHECK-DAG: @sat_accum = {{.*}}global i32 0, align 4 +_Sat long _Accum sat_long_accum; // CHECK-DAG: @sat_long_accum = {{.*}}global i64 0, align 8 +_Sat short _Fract sat_short_fract; // CHECK-DAG: @sat_short_fract = {{.*}}global i16 0, align 2 +_Sat _Fract sat_fract; // CHECK-DAG: @sat_fract = {{.*}}global i32 0, align 4 +_Sat long _Fract sat_long_fract; // CHECK-DAG: @sat_long_fract = {{.*}}global i64 0, align 8 + +/* Fixed point literals */ +short _Accum short_accum_literal = 2.5hk; // CHECK-DAG: @short_accum_literal = {{.*}}global i16 320, align 2 +_Accum accum_literal = 2.5k; // CHECK-DAG: @accum_literal = {{.*}}global i32 81920, align 4 +long _Accum long_accum_literal = 2.5lk; // CHECK-DAG: @long_accum_literal = {{.*}}global i64 5368709120, align 8 +short _Fract short_fract_literal = 0.5hr; // CHECK-DAG: @short_fract_literal = {{.*}}global i16 64, align 2 +_Fract fract_literal = 0.5r; // CHECK-DAG: @fract_literal = {{.*}}global i32 16384, align 4 +long _Fract long_fract_literal = 0.5lr; // CHECK-DAG: @long_fract_literal = {{.*}}global i64 1073741824, align 8 + +unsigned short _Accum u_short_accum_literal = 2.5uhk; // CHECK-DAG: @u_short_accum_literal = {{.*}}global i16 640, align 2 +unsigned _Accum u_accum_literal = 2.5uk; // CHECK-DAG: @u_accum_literal = {{.*}}global i32 163840, align 4 +unsigned long _Accum u_long_accum_literal = 2.5ulk; // CHECK-DAG: @u_long_accum_literal = {{.*}}global i64 10737418240, align 8 +unsigned short _Fract u_short_fract_literal = 0.5uhr; // CHECK-DAG: @u_short_fract_literal = {{.*}}global i16 128, align 2 +unsigned _Fract u_fract_literal = 0.5ur; // CHECK-DAG: @u_fract_literal = {{.*}}global i32 32768, align 4 +unsigned long _Fract u_long_fract_literal = 0.5ulr; // CHECK-DAG: @u_long_fract_literal = {{.*}}global i64 2147483648, align 8 + +// Max literal values +short _Accum short_accum_max = 255.9999999999999999hk; // CHECK-DAG: @short_accum_max = {{.*}}global i16 32767, align 2 +_Accum accum_max = 65535.9999999999999999k; // CHECK-DAG: @accum_max = {{.*}}global i32 2147483647, align 4 +long _Accum long_accum_max = 4294967295.9999999999999999lk; // CHECK-DAG: @long_accum_max = {{.*}}global i64 9223372036854775807, align 8 +unsigned short _Accum u_short_accum_max = 255.9999999999999999uhk; // CHECK-DAG: @u_short_accum_max = {{.*}}global i16 -1, align 2 +unsigned _Accum u_accum_max = 65535.9999999999999999uk; // CHECK-DAG: @u_accum_max = {{.*}}global i32 -1, align 4 +unsigned long _Accum u_long_accum_max = 4294967295.9999999999999999ulk; // CHECK-DAG: @u_long_accum_max = {{.*}}global i64 -1, align 8 + +short _Fract short_fract_max = 0.9999999999999999hr; // CHECK-DAG: @short_fract_max = {{.*}}global i16 127, align 2 +_Fract fract_max = 0.9999999999999999r; // CHECK-DAG: @fract_max = {{.*}}global i32 32767, align 4 +long _Fract long_fract_max = 0.9999999999999999lr; // CHECK-DAG: @long_fract_max = {{.*}}global i64 2147483647, align 8 +unsigned short _Fract u_short_fract_max = 0.9999999999999999uhr; // CHECK-DAG: @u_short_fract_max = {{.*}}global i16 255, align 2 +unsigned _Fract u_fract_max = 0.9999999999999999ur; // CHECK-DAG: @u_fract_max = {{.*}}global i32 65535, align 4 +unsigned long _Fract u_long_fract_max = 0.9999999999999999ulr; // CHECK-DAG: @u_long_fract_max = {{.*}}global i64 4294967295, align 8 + +// Fracts may be exactly one but evaluate to the Fract max +short _Fract short_fract_one = 1.0hr; // CHECK-DAG: @short_fract_one = {{.*}}global i16 127, align 2 +_Fract fract_one = 1.0r; // CHECK-DAG: @fract_one = {{.*}}global i32 32767, align 4 +long _Fract long_fract_one = 1.0lr; // CHECK-DAG: @long_fract_one = {{.*}}global i64 2147483647, align 8 +unsigned short _Fract u_short_fract_one = 1.0uhr; // CHECK-DAG: @u_short_fract_one = {{.*}}global i16 255, align 2 +unsigned _Fract u_fract_one = 1.0ur; // CHECK-DAG: @u_fract_one = {{.*}}global i32 65535, align 4 +unsigned long _Fract u_long_fract_one = 1.0ulr; // CHECK-DAG: @u_long_fract_one = {{.*}}global i64 4294967295, align 8 + +short _Fract short_fract_exp_one = 0.1e1hr; // CHECK-DAG: @short_fract_exp_one = {{.*}}global i16 127, align 2 +_Fract fract_exp_one = 0.1e1r; // CHECK-DAG: @fract_exp_one = {{.*}}global i32 32767, align 4 +long _Fract long_fract_exp_one = 0.1e1lr; // CHECK-DAG: @long_fract_exp_one = {{.*}}global i64 2147483647, align 8 +unsigned short _Fract u_short_fract_exp_one = 0.1e1uhr; // CHECK-DAG: @u_short_fract_exp_one = {{.*}}global i16 255, align 2 +unsigned _Fract u_fract_exp_one = 0.1e1ur; // CHECK-DAG: @u_fract_exp_one = {{.*}}global i32 65535, align 4 +unsigned long _Fract u_long_fract_exp_one = 0.1e1ulr; // CHECK-DAG: @u_long_fract_exp_one = {{.*}}global i64 4294967295, align 8 + +short _Fract short_fract_hex_exp_one = 0x0.8p1hr; // CHECK-DAG: @short_fract_hex_exp_one = {{.*}}global i16 127, align 2 +_Fract fract_hex_exp_one = 0x0.8p1r; // CHECK-DAG: @fract_hex_exp_one = {{.*}}global i32 32767, align 4 +long _Fract long_fract_hex_exp_one = 0x0.8p1lr; // CHECK-DAG: @long_fract_hex_exp_one = {{.*}}global i64 2147483647, align 8 +unsigned short _Fract u_short_fract_hex_exp_one = 0x0.8p1uhr; // CHECK-DAG: @u_short_fract_hex_exp_one = {{.*}}global i16 255, align 2 +unsigned _Fract u_fract_hex_exp_one = 0x0.8p1ur; // CHECK-DAG: @u_fract_hex_exp_one = {{.*}}global i32 65535, align 4 +unsigned long _Fract u_long_fract_hex_exp_one = 0x0.8p1ulr; // CHECK-DAG: @u_long_fract_hex_exp_one = {{.*}}global i64 4294967295, align 8 + +// Expsilon values +short _Accum short_accum_eps = 0x1p-7hk; // CHECK-DAG: @short_accum_eps = {{.*}}global i16 1, align 2 +_Accum accum_eps = 0x1p-15k; // CHECK-DAG: @accum_eps = {{.*}}global i32 1, align 4 +long _Accum long_accum_eps = 0x1p-31lk; // CHECK-DAG: @long_accum_eps = {{.*}}global i64 1, align 8 +unsigned short _Accum u_short_accum_eps = 0x1p-8uhk; // CHECK-DAG: @u_short_accum_eps = {{.*}}global i16 1, align 2 +unsigned _Accum u_accum_eps = 0x1p-16uk; // CHECK-DAG: @u_accum_eps = {{.*}}global i32 1, align 4 +unsigned long _Accum u_long_accum_eps = 0x1p-32ulk; // CHECK-DAG: @u_long_accum_eps = {{.*}}global i64 1, align 8 + +short _Fract short_fract_eps = 0x1p-7hr; // CHECK-DAG: @short_fract_eps = {{.*}}global i16 1, align 2 +_Fract fract_eps = 0x1p-15r; // CHECK-DAG: @fract_eps = {{.*}}global i32 1, align 4 +long _Fract long_fract_eps = 0x1p-31lr; // CHECK-DAG: @long_fract_eps = {{.*}}global i64 1, align 8 +unsigned short _Fract u_short_fract_eps = 0x1p-8uhr; // CHECK-DAG: @u_short_fract_eps = {{.*}}global i16 1, align 2 +unsigned _Fract u_fract_eps = 0x1p-16ur; // CHECK-DAG: @u_fract_eps = {{.*}}global i32 1, align 4 +unsigned long _Fract u_long_fract_eps = 0x1p-32ulr; // CHECK-DAG: @u_long_fract_eps = {{.*}}global i64 1, align 8 Index: test/Frontend/fixed_point_errors.c =================================================================== --- test/Frontend/fixed_point_errors.c +++ test/Frontend/fixed_point_errors.c @@ -13,7 +13,6 @@ _Sat long long _Fract sat_longlong_fract; // expected-error{{'long long _Fract' is invalid}} _Sat unsigned long long _Fract sat_u_longlong_fract; // expected-error{{'long long _Fract' is invalid}} - /* Although _Complex types work with floating point numbers, the extension * provides no info for complex fixed point types. */ @@ -78,6 +77,50 @@ _Sat int i; // expected-error{{'_Sat' specifier is only valid on '_Fract' or '_Accum', not 'int'}} _Sat _Sat _Fract fract; // expected-warning{{duplicate '_Sat' declaration specifier}} + +/* Literals that cannot fit into types */ +signed short _Accum s_short_accum = 256.0hk; // expected-error{{this value is too large for this signed _Accum type}} +unsigned short _Accum u_short_accum = 256.0uhk; // expected-error{{this value is too large for this unsigned _Accum type}} +signed _Accum s_accum = 65536.0k; // expected-error{{this value is too large for this signed _Accum type}} +unsigned _Accum u_accum = 65536.0uk; // expected-error{{this value is too large for this unsigned _Accum type}} +signed long _Accum s_long_accum = 4294967296.0lk; // expected-error{{this value is too large for this signed _Accum type}} +unsigned long _Accum u_long_accum = 4294967296.0ulk; // expected-error{{this value is too large for this unsigned _Accum type}} + +// Large values from decimal exponents +short _Accum short_accum_exp = 2.56e2hk; // expected-error{{this value is too large for this signed _Accum type}} +_Accum accum_exp = 6.5536e4k; // expected-error{{this value is too large for this signed _Accum type}} +long _Accum long_accum_exp = 4.294967296e9lk; // expected-error{{this value is too large for this signed _Accum type}} +unsigned short _Accum u_short_accum_exp = 2.56e2uhk; // expected-error{{this value is too large for this unsigned _Accum type}} +unsigned _Accum u_accum_exp = 6.5536e4uk; // expected-error{{this value is too large for this unsigned _Accum type}} +unsigned long _Accum u_long_accum_exp = 4.294967296e9ulk; // expected-error{{this value is too large for this unsigned _Accum type}} + +// Large value from hexidecimal exponents +short _Accum short_accum_hex_exp = 0x1e8hk; // expected-error{{this value is too large for this signed _Accum type}} +_Accum accum_hex_exp = 0x1e16k; // expected-error{{this value is too large for this signed _Accum type}} +long _Accum long_accum_hex_exp = 0x1e32lk; // expected-error{{this value is too large for this signed _Accum type}} +unsigned short _Accum u_short_accum_hex_exp = 0x1e8uhk; // expected-error{{this value is too large for this unsigned _Accum type}} +unsigned _Accum u_accum_hex_exp = 0x1e16uk; // expected-error{{this value is too large for this unsigned _Accum type}} +unsigned long _Accum u_long_accum_hex_exp = 0x1e32ulk; // expected-error{{this value is too large for this unsigned _Accum type}} + +// Very large exponent +_Accum x = 1e1000000000000000000000000000000000k; // expected-error{{this value is too large for this signed _Accum type}} + +/* Although _Fract's cannot equal 1, _Fract literals written as 1 are allowed + * and the underlying value represents the max value for that _Fract type. */ +short _Fract short_fract_above_1 = 1.1hr; // expected-error{{a _Fract type cannot have an integral part}} +_Fract fract_above_1 = 1.1r; // expected-error{{a _Fract type cannot have an integral part}} +long _Fract long_fract_above_1 = 1.1lr; // expected-error{{a _Fract type cannot have an integral part}} +unsigned short _Fract u_short_fract_above_1 = 1.1uhr; // expected-error{{a _Fract type cannot have an integral part}} +unsigned _Fract u_fract_above_1 = 1.1ur; // expected-error{{a _Fract type cannot have an integral part}} +unsigned long _Fract u_long_fract_above_1 = 1.1ulr; // expected-error{{a _Fract type cannot have an integral part}} + +short _Fract short_fract_hex_exp = 0x0.fp1hr; // expected-error{{a _Fract type cannot have an integral part}} +_Fract fract_hex_exp = 0x0.fp1r; // expected-error{{a _Fract type cannot have an integral part}} +long _Fract long_fract_hex_exp = 0x0.fp1lr; // expected-error{{a _Fract type cannot have an integral part}} +unsigned short _Fract u_short_fract_hex_exp = 0x0.fp1uhr; // expected-error{{a _Fract type cannot have an integral part}} +unsigned _Fract u_fract_hex_exp = 0x0.fp1ur; // expected-error{{a _Fract type cannot have an integral part}} +unsigned long _Fract u_long_fract_hex_exp = 0x0.fp1ulr; // expected-error{{a _Fract type cannot have an integral part}} + /* Do not allow typedef to be used with typedef'd types */ typedef short _Fract shortfract_t; typedef short _Accum shortaccum_t; @@ -91,3 +134,16 @@ _Sat accum_t td_sat_accum; // expected-error{{'_Sat' specifier is only valid on '_Fract' or '_Accum', not 'type-name'}} _Sat longfract_t td_sat_long_fract; // expected-error{{'_Sat' specifier is only valid on '_Fract' or '_Accum', not 'type-name'}} _Sat longaccum_t td_sat_long_accum; // expected-error{{'_Sat' specifier is only valid on '_Fract' or '_Accum', not 'type-name'}} + +/* Bad suffixes */ +_Accum fk = 1.0fk; // expected-error{{invalid suffix 'fk' on integer constant}} +_Accum kk = 1.0kk; // expected-error{{invalid suffix 'kk' on integer constant}} +_Accum rk = 1.0rk; // expected-error{{invalid suffix 'rk' on integer constant}} +_Accum rk = 1.0rr; // expected-error{{invalid suffix 'rr' on integer constant}} +_Accum qk = 1.0qr; // expected-error{{invalid suffix 'qr' on integer constant}} + +/* Using wrong exponent notation */ +_Accum dec_with_hex_exp1 = 0.1p10k; // expected-error{{invalid suffix 'p10k' on integer constant}} +_Accum dec_with_hex_exp2 = 0.1P10k; // expected-error{{invalid suffix 'P10k' on integer constant}} +_Accum hex_with_dex_exp1 = 0x0.1e10k; // expected-error{{hexadecimal floating constant requires an exponent}} +_Accum hex_with_dex_exp2 = 0x0.1E10k; // expected-error{{hexadecimal floating constant requires an exponent}} Index: test/Frontend/fixed_point_same_fbits.c =================================================================== --- /dev/null +++ test/Frontend/fixed_point_same_fbits.c @@ -0,0 +1,28 @@ +// RUN: %clang -ffixed-point -S -emit-llvm -o - %s | FileCheck %s -check-prefix=DEFAULT +// RUN: %clang -ffixed-point -fsame-fbits -S -emit-llvm -o - %s | FileCheck %s -check-prefix=SAME + +/* The scale for unsigned fixed point types should be the same as that of signed + * fixed point types when -fsame-fbits is enabled. */ + +void func() { + unsigned short _Accum u_short_accum = 0.5uhk; + unsigned _Accum u_accum = 0.5uk; + unsigned long _Accum u_long_accum = 0.5ulk; + unsigned short _Fract u_short_fract = 0.5uhr; + unsigned _Fract u_fract = 0.5ur; + unsigned long _Fract u_long_fract = 0.5ulr; + +// DEFAULT: store i16 128, i16* %u_short_accum, align 2 +// DEFAULT: store i32 32768, i32* %u_accum, align 4 +// DEFAULT: store i64 2147483648, i64* %u_long_accum, align 8 +// DEFAULT: store i16 128, i16* %u_short_fract, align 2 +// DEFAULT: store i32 32768, i32* %u_fract, align 4 +// DEFAULT: store i64 2147483648, i64* %u_long_fract, align 8 + +// SAME: store i16 64, i16* %u_short_accum, align 2 +// SAME: store i32 16384, i32* %u_accum, align 4 +// SAME: store i64 1073741824, i64* %u_long_accum, align 8 +// SAME: store i16 64, i16* %u_short_fract, align 2 +// SAME: store i32 16384, i32* %u_fract, align 4 +// SAME: store i64 1073741824, i64* %u_long_fract, align 8 +} Index: test/Frontend/fixed_point_validation.c =================================================================== --- /dev/null +++ test/Frontend/fixed_point_validation.c @@ -0,0 +1,19 @@ +// RUN: %clang -ffixed-point -S -emit-llvm -o - %s | lli -force-interpreter=true + +// Run simple validation tests + +#define assert(b) if (!(b)) { return 1; } + +int main(){ + short _Accum s_accum = 0.0hk; + short _Accum s_accum2 = 2.0hk; + short _Fract s_fract = 0.999hr; + short _Fract s_fract2 = -0.999hr; + + assert(s_accum == 0); + + s_accum = s_accum2; + + assert(s_accum == s_accum2); + assert(s_accum == 2); +} Index: tools/libclang/CXCursor.cpp =================================================================== --- tools/libclang/CXCursor.cpp +++ tools/libclang/CXCursor.cpp @@ -305,6 +305,10 @@ K = CXCursor_IntegerLiteral; break; + case Stmt::FixedPointLiteralClass: + llvm_unreachable("No cursor for FixedPointLiteralClass"); + break; + case Stmt::FloatingLiteralClass: K = CXCursor_FloatingLiteral; break;