Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -6557,10 +6557,18 @@ } // Return the number of fractional bits in a fixed point type. -unsigned getFixedPointFBits(const QualType& Ty); +unsigned getFixedPointFBits(const Type& Ty); + +inline unsigned getFixedPointFBits(const QualType& Ty) { + return getFixedPointFBits(*Ty); +} // Return the number of integral bits in a fixed point type. -unsigned getFixedPointIBits(const QualType& Ty); +unsigned getFixedPointIBits(const Type& Ty); + +inline unsigned getFixedPointIBits(const QualType& Ty) { + return getFixedPointIBits(*Ty); +} } // namespace clang Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -3994,10 +3994,10 @@ return getClass()->getAsCXXRecordDecl()->getMostRecentDecl(); } -unsigned clang::getFixedPointFBits(const QualType& Ty) { - assert(Ty->isFixedPointType()); +unsigned clang::getFixedPointFBits(const Type& Ty) { + assert(Ty.isFixedPointType()); - const auto *BT = Ty->getAs(); + const auto &BT = Ty.getAs(); switch (BT->getKind()) { default: llvm_unreachable("Not a fixed point type!"); case BuiltinType::ShortAccum: @@ -4050,10 +4050,10 @@ } } -unsigned clang::getFixedPointIBits(const QualType& Ty) { - assert(Ty->isFixedPointType()); +unsigned clang::getFixedPointIBits(const Type& Ty) { + assert(Ty.isFixedPointType()); - const auto *BT = Ty->getAs(); + const auto &BT = Ty.getAs(); switch (BT->getKind()) { default: llvm_unreachable("Not a fixed point type!"); case BuiltinType::ShortAccum: Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -308,6 +308,17 @@ Value *EmitScalarConversion(Value *Src, QualType SrcTy, QualType DstTy, SourceLocation Loc, bool TreatBooleanAsSigned); + /// Emit a conversion between fixed point types by moving the radix point. + /// This does not take into account resizing of the underlying llvm type + /// which should be handled either before or after calling this function. + /// + /// If the type is being scaled up, this method should be called after + /// performing an intcast. If the type is scaled down, this method should be + /// called before performing an intcast. This is necessary such that the + /// shift operations retain as much of the original data as possible before + /// truncation or after extension. + Value *EmitFixedPointRadixShift(Value *Src, QualType SrcTy, QualType DstTy); + /// Emit a conversion from the specified complex type to the specified /// destination type, where the destination type is an LLVM scalar type. Value *EmitComplexToScalarConversion(CodeGenFunction::ComplexPairTy Src, @@ -957,6 +968,50 @@ SanitizerHandler::FloatCastOverflow, StaticArgs, OrigSrc); } + +/// Emit a conversion between fixed point types by moving the radix point. +/// This does not take into account resizing of the underlying llvm type +/// which should be handled either before or after calling this function. +/// +/// If the type is being scaled up, this method should be called after +/// performing an intcast. If the type is scaled down, this method should be +/// called before performing an intcast. This is necessary such that the +/// shift operations retain as much of the original data as possible before +/// truncation or after extension. +Value *ScalarExprEmitter::EmitFixedPointRadixShift(Value *Src, QualType SrcTy, + QualType DstTy) { + assert(DstTy->isFixedPointType()); + assert(SrcTy->isFixedPointType()); + + Value* Res = Src; + + // Casting between fixed point types involves separating the integral and + // fractional bits, potentially shifting them, then joining back together. + unsigned dest_fbits = getFixedPointFBits(DstTy); + unsigned src_fbits = getFixedPointFBits(SrcTy); + unsigned dest_ibits = getFixedPointIBits(DstTy); + unsigned src_ibits = getFixedPointIBits(SrcTy); + + // If the number of integral bits is decreasing, trim off any extra bits while + // retaining the sign. + if (dest_ibits < src_ibits) { + Res = Builder.CreateShl(Res, src_ibits - dest_ibits); + Res = Builder.CreateAShr(Res, src_ibits - dest_ibits); + } + + // Move the radix. For irrational numbers, there will be loss of precision using + // this method when the number of fbits increases since we will be right padding + // zeros. Precision can still be retained if we temporarily convert to a float + // and perform some floating point arithmetic, though this may cost more. Enable + // that if #pragma FX_FULL_PRECISION is provided. + if (dest_fbits > src_fbits) { + Res = Builder.CreateShl(Res, dest_fbits - src_fbits); + } else if (dest_fbits < src_fbits) { + Res = Builder.CreateAShr(Res, src_fbits - dest_fbits); + } + return Res; +} + /// Emit a conversion from the specified type to the specified destination type, /// both of which are LLVM scalar types. Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType, @@ -1115,6 +1170,14 @@ DstTy = CGF.FloatTy; } + bool WorkingOnFixedPoints = DstType->isFixedPointType() && SrcType->isFixedPointType(); + int order = WorkingOnFixedPoints ? CGF.getContext().getFixedPointTypeOrder(DstType, SrcType) : 0; + + if (WorkingOnFixedPoints && order < 0) { + // Casting down, so we will need to shift early as to not lose data + Src = EmitFixedPointRadixShift(Src, SrcType, DstType); + } + if (isa(SrcTy)) { bool InputSigned = (SrcType->isSignedIntegerOrEnumerationType() || SrcType->isFixedPointType()); @@ -1142,6 +1205,11 @@ Res = Builder.CreateFPExt(Src, DstTy, "conv"); } + if (WorkingOnFixedPoints && order >= 0) { + // Casting up (or same type), so we can safely shift without losing data + Res = EmitFixedPointRadixShift(Res, SrcType, DstType); + } + if (DstTy != ResTy) { if (CGF.getContext().getTargetInfo().useFP16ConversionIntrinsics()) { assert(ResTy->isIntegerTy(16) && "Only half FP requires extra conversion"); @@ -1811,37 +1879,7 @@ } case CK_FixedPointCast: { - // Casting between fixed point types involves separating the integral and - // fractional bits, potentially shifting them, then joining back together. - assert(DestTy->isFixedPointType()); - assert(E->getType()->isFixedPointType()); - - unsigned dest_fbits = getFixedPointFBits(DestTy); - unsigned src_fbits = getFixedPointFBits(E->getType()); - unsigned dest_ibits = getFixedPointIBits(DestTy); - unsigned src_ibits = getFixedPointIBits(E->getType()); - - llvm::Value* result = EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()); - - // If the number of integral bits is decreasing, trim off any extra bits while - // retaining the sign. - if (dest_ibits < src_ibits) { - result = Builder.CreateShl(result, src_ibits - dest_ibits); - result = Builder.CreateAShr(result, src_ibits - dest_ibits); - } - - // Move the radix. For irrational numbers, there will be loss of precision using - // this method when the number of fbits increases since we will be right padding - // zeros. Precision can still be retained if we temporarily convert to a float - // and perform some floating point arithmetic, though this may cost more. Enable - // that if #pragma FX_FULL_PRECISION is provided. - if (dest_fbits > src_fbits) { - result = EmitScalarConversion(result, E->getType(), DestTy, CE->getExprLoc()); - result = Builder.CreateShl(result, dest_fbits - src_fbits); - } else if (dest_fbits < src_fbits) { - result = Builder.CreateAShr(result, src_fbits - dest_fbits); - } - return result; + return EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()); } case CK_IntegralToFixedPoint: { Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -6057,7 +6057,7 @@ default: llvm_unreachable("Unable to convert from fixed point type"); case Type::STK_Integral: llvm_unreachable("Unimplemented scalar cast from fixed point to int"); // TODO case Type::STK_Floating: return CK_FixedPointToFloating; - case Type::STK_FixedPoint: llvm_unreachable("Unimplemented scalar cast from fixed point to fixed point"); // TODO + case Type::STK_FixedPoint: return CK_FixedPointCast; } } Index: test/Frontend/fixed_point_validation.c =================================================================== --- test/Frontend/fixed_point_validation.c +++ test/Frontend/fixed_point_validation.c @@ -279,4 +279,35 @@ float laccum_diff = abs(base - 2.333lk); assert(accum_diff < saccum_diff); assert(laccum_diff < accum_diff); + + /**************** Auxillary assignments ***************/ + + s_accum = 7.5hk; + s_accum2 = 2.0hk; + s_accum += s_accum2; + assert(s_accum == 9.5hk); + s_accum += 2.5k; + assert(s_accum == 12); + + s_accum -= s_accum2; + assert(s_accum == 10); + s_accum -= 2.5lk; + assert(s_accum == 7.5k); + + s_accum2 = 3.0k; + s_accum *= s_accum2; + assert(s_accum == 22.5k); + s_accum *= 0.5r; + assert(s_accum == 11.25hk); + + s_accum /= s_accum2; + assert(s_accum == 3.75k); + s_accum /= 0.5hr; + assert(s_accum == 7.5k); + + s_accum <<= 3; + assert(s_accum == 60); + + s_accum >>= 3; + assert(s_accum == 7.5k); }