Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -6572,10 +6572,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!"); @@ -4051,10 +4051,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!"); 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, @@ -961,6 +972,49 @@ 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, @@ -1119,6 +1173,17 @@ 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()); @@ -1146,6 +1211,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"); @@ -1815,39 +1885,8 @@ } 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 @@ -6076,8 +6076,7 @@ case Type::STK_Floating: return CK_FixedPointToFloating; case Type::STK_FixedPoint: - llvm_unreachable( - "Unimplemented scalar cast from fixed point to fixed point"); // TODO + return CK_FixedPointCast; } } Index: test/Frontend/fixed_point_validation.c =================================================================== --- test/Frontend/fixed_point_validation.c +++ test/Frontend/fixed_point_validation.c @@ -1,5 +1,5 @@ -// RUN: %clang -S -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -S -emit-llvm -o - %s | lli +// RUN: %clang -S -emit-llvm %s -o - | FileCheck %s // The first test checks the emitted llvm IR. // The second test checks the output. @@ -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); }