Skip to content

Commit

Permalink
[Fixed Point Arithmetic] Fixed Point Addition
Browse files Browse the repository at this point in the history
This patch covers addition between fixed point types and other fixed point
types or integers, using the conversion rules described in 4.1.4 of N1169.

Usual arithmetic rules do not apply to binary operations when one of the
operands is a fixed point type, and the result of the operation must be
calculated with the full precision of the operands, so we should not perform
any casting to a common type.

This patch does not include constant expression evaluation for addition of
fixed point types. That will be addressed in another patch since I think this
one is already big enough.

Differential Revision: https://reviews.llvm.org/D53738

llvm-svn: 351364
  • Loading branch information
PiJoules committed Jan 16, 2019
1 parent 07d8b32 commit 2044ac8
Show file tree
Hide file tree
Showing 9 changed files with 667 additions and 20 deletions.
6 changes: 6 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -2624,6 +2624,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
// corresponding saturated type for a given fixed point type.
QualType getCorrespondingSaturatedType(QualType Ty) const;

// This method accepts fixed point types and returns the corresponding signed
// type. Unlike getCorrespondingUnsignedType(), this only accepts unsigned
// fixed point types because there are unsigned integer types like bool and
// char8_t that don't have signed equivalents.
QualType getCorrespondingSignedFixedPointType(QualType Ty) const;

//===--------------------------------------------------------------------===//
// Integer Values
//===--------------------------------------------------------------------===//
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/AST/Type.h
Expand Up @@ -2269,6 +2269,9 @@ class Type : public ExtQualsTypeCommonBase {
/// ISO/IEC JTC1 SC22 WG14 N1169.
bool isFixedPointType() const;

/// Return true if this is a fixed point or integer type.
bool isFixedPointOrIntegerType() const;

/// Return true if this is a saturated fixed point type according to
/// ISO/IEC JTC1 SC22 WG14 N1169. This type can be signed or unsigned.
bool isSaturatedFixedPointType() const;
Expand Down Expand Up @@ -6596,6 +6599,10 @@ inline bool Type::isFixedPointType() const {
return false;
}

inline bool Type::isFixedPointOrIntegerType() const {
return isFixedPointType() || isIntegerType();
}

inline bool Type::isSaturatedFixedPointType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) {
return BT->getKind() >= BuiltinType::SatShortAccum &&
Expand Down
21 changes: 21 additions & 0 deletions clang/include/clang/Basic/FixedPoint.h
Expand Up @@ -18,6 +18,7 @@
#define LLVM_CLANG_BASIC_FIXEDPOINT_H

#include "llvm/ADT/APSInt.h"
#include "llvm/Support/raw_ostream.h"

namespace clang {

Expand All @@ -36,6 +37,8 @@ class FixedPointSemantics {
: Width(Width), Scale(Scale), IsSigned(IsSigned),
IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) {
assert(Width >= Scale && "Not enough room for the scale");
assert(!(IsSigned && HasUnsignedPadding) &&
"Cannot have unsigned padding on a signed type.");
}

unsigned getWidth() const { return Width; }
Expand All @@ -46,13 +49,31 @@ class FixedPointSemantics {

void setSaturated(bool Saturated) { IsSaturated = Saturated; }

/// Return the number of integral bits represented by these semantics. These
/// are separate from the fractional bits and do not include the sign or
/// padding bit.
unsigned getIntegralBits() const {
if (IsSigned || (!IsSigned && HasUnsignedPadding))
return Width - Scale - 1;
else
return Width - Scale;
}

/// Return the FixedPointSemantics that allows for calculating the full
/// precision semantic that can precisely represent the precision and ranges
/// of both input values. This does not compute the resulting semantics for a
/// given binary operation.
FixedPointSemantics
getCommonSemantics(const FixedPointSemantics &Other) const;

/// Return the FixedPointSemantics for an integer type.
static FixedPointSemantics GetIntegerSemantics(unsigned Width,
bool IsSigned) {
return FixedPointSemantics(Width, /*Scale=*/0, IsSigned,
/*IsSaturated=*/false,
/*HasUnsignedPadding=*/false);
}

private:
unsigned Width;
unsigned Scale;
Expand Down
43 changes: 42 additions & 1 deletion clang/lib/AST/ASTContext.cpp
Expand Up @@ -10485,7 +10485,13 @@ unsigned char ASTContext::getFixedPointIBits(QualType Ty) const {
}

FixedPointSemantics ASTContext::getFixedPointSemantics(QualType Ty) const {
assert(Ty->isFixedPointType());
assert(Ty->isFixedPointType() ||
Ty->isIntegerType() && "Can only get the fixed point semantics for a "
"fixed point or integer type.");
if (Ty->isIntegerType())
return FixedPointSemantics::GetIntegerSemantics(getIntWidth(Ty),
Ty->isSignedIntegerType());

bool isSigned = Ty->isSignedFixedPointType();
return FixedPointSemantics(
static_cast<unsigned>(getTypeSize(Ty)), getFixedPointScale(Ty), isSigned,
Expand All @@ -10502,3 +10508,38 @@ APFixedPoint ASTContext::getFixedPointMin(QualType Ty) const {
assert(Ty->isFixedPointType());
return APFixedPoint::getMin(getFixedPointSemantics(Ty));
}

QualType ASTContext::getCorrespondingSignedFixedPointType(QualType Ty) const {
assert(Ty->isUnsignedFixedPointType() &&
"Expected unsigned fixed point type");
const auto *BTy = Ty->getAs<BuiltinType>();

switch (BTy->getKind()) {
case BuiltinType::UShortAccum:
return ShortAccumTy;
case BuiltinType::UAccum:
return AccumTy;
case BuiltinType::ULongAccum:
return LongAccumTy;
case BuiltinType::SatUShortAccum:
return SatShortAccumTy;
case BuiltinType::SatUAccum:
return SatAccumTy;
case BuiltinType::SatULongAccum:
return SatLongAccumTy;
case BuiltinType::UShortFract:
return ShortFractTy;
case BuiltinType::UFract:
return FractTy;
case BuiltinType::ULongFract:
return LongFractTy;
case BuiltinType::SatUShortFract:
return SatShortFractTy;
case BuiltinType::SatUFract:
return SatFractTy;
case BuiltinType::SatULongFract:
return SatLongFractTy;
default:
llvm_unreachable("Unexpected unsigned fixed point type");
}
}
25 changes: 25 additions & 0 deletions clang/lib/Basic/FixedPoint.cpp
Expand Up @@ -112,4 +112,29 @@ APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {
return APFixedPoint(Val, Sema);
}

FixedPointSemantics FixedPointSemantics::getCommonSemantics(
const FixedPointSemantics &Other) const {
unsigned CommonScale = std::max(getScale(), Other.getScale());
unsigned CommonWidth =
std::max(getIntegralBits(), Other.getIntegralBits()) + CommonScale;

bool ResultIsSigned = isSigned() || Other.isSigned();
bool ResultIsSaturated = isSaturated() || Other.isSaturated();
bool ResultHasUnsignedPadding = false;
if (!ResultIsSigned) {
// Both are unsigned.
ResultHasUnsignedPadding = hasUnsignedPadding() &&
Other.hasUnsignedPadding() && !ResultIsSaturated;
}

// If the result is signed, add an extra bit for the sign. Otherwise, if it is
// unsigned and has unsigned padding, we only need to add the extra padding
// bit back if we are not saturating.
if (ResultIsSigned || ResultHasUnsignedPadding)
CommonWidth++;

return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned,
ResultIsSaturated, ResultHasUnsignedPadding);
}

} // namespace clang
82 changes: 76 additions & 6 deletions clang/lib/CodeGen/CGExprScalar.cpp
Expand Up @@ -125,6 +125,13 @@ struct BinOpInfo {
return CFP->isZero();
return true;
}

/// Check if either operand is a fixed point type, in which case, this
/// operation did not follow usual arithmetic conversion and both operands may
/// not be the same.
bool isFixedPointBinOp() const {
return isa<BinaryOperator>(E) && Ty->isFixedPointType();
}
};

static bool MustVisitNullValue(const Expr *E) {
Expand Down Expand Up @@ -351,6 +358,9 @@ class ScalarExprEmitter

Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy,
SourceLocation Loc);
Value *EmitFixedPointConversion(Value *Src, FixedPointSemantics &SrcFixedSema,
FixedPointSemantics &DstFixedSema,
SourceLocation Loc);

/// Emit a conversion from the specified complex type to the specified
/// destination type, where the destination type is an LLVM scalar type.
Expand Down Expand Up @@ -729,6 +739,9 @@ class ScalarExprEmitter
return Builder.CreateOr(Ops.LHS, Ops.RHS, "or");
}

// Helper functions for fixed point binary operations.
Value *EmitFixedPointAdd(const BinOpInfo &Ops);

BinOpInfo EmitBinOps(const BinaryOperator *E);
LValue EmitCompoundAssignLValue(const CompoundAssignOperator *E,
Value *(ScalarExprEmitter::*F)(const BinOpInfo &),
Expand Down Expand Up @@ -1423,17 +1436,23 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
QualType DstTy,
SourceLocation Loc) {
using llvm::APInt;
using llvm::ConstantInt;
using llvm::Value;

assert(SrcTy->isFixedPointType());
assert(DstTy->isFixedPointType());

FixedPointSemantics SrcFPSema =
CGF.getContext().getFixedPointSemantics(SrcTy);
FixedPointSemantics DstFPSema =
CGF.getContext().getFixedPointSemantics(DstTy);
return EmitFixedPointConversion(Src, SrcFPSema, DstFPSema, Loc);
}

Value *ScalarExprEmitter::EmitFixedPointConversion(
Value *Src, FixedPointSemantics &SrcFPSema, FixedPointSemantics &DstFPSema,
SourceLocation Loc) {
using llvm::APInt;
using llvm::ConstantInt;
using llvm::Value;

unsigned SrcWidth = SrcFPSema.getWidth();
unsigned DstWidth = DstFPSema.getWidth();
unsigned SrcScale = SrcFPSema.getScale();
Expand Down Expand Up @@ -1462,7 +1481,8 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
} else {
// Adjust the number of fractional bits.
if (DstScale > SrcScale) {
ResultWidth = SrcWidth + DstScale - SrcScale;
// Compare to DstWidth to prevent resizing twice.
ResultWidth = std::max(SrcWidth + DstScale - SrcScale, DstWidth);
llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth);
Result = Builder.CreateIntCast(Result, UpscaledTy, SrcIsSigned, "resize");
Result = Builder.CreateShl(Result, DstScale - SrcScale, "upscale");
Expand Down Expand Up @@ -1493,7 +1513,8 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
}

// Resize the integer part to get the final destination size.
Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
if (ResultWidth != DstWidth)
Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
}
return Result;
}
Expand Down Expand Up @@ -3338,9 +3359,58 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) {
return propagateFMFlags(V, op);
}

if (op.isFixedPointBinOp())
return EmitFixedPointAdd(op);

return Builder.CreateAdd(op.LHS, op.RHS, "add");
}

/// The resulting value must be calculated with exact precision, so the operands
/// may not be the same type.
Value *ScalarExprEmitter::EmitFixedPointAdd(const BinOpInfo &op) {
using llvm::APSInt;
using llvm::ConstantInt;

const auto *BinOp = cast<BinaryOperator>(op.E);
assert(BinOp->getOpcode() == BO_Add && "Expected operation to be addition");

// The result is a fixed point type and at least one of the operands is fixed
// point while the other is either fixed point or an int. This resulting type
// should be determined by Sema::handleFixedPointConversions().
QualType ResultTy = op.Ty;
QualType LHSTy = BinOp->getLHS()->getType();
QualType RHSTy = BinOp->getRHS()->getType();
ASTContext &Ctx = CGF.getContext();
Value *LHS = op.LHS;
Value *RHS = op.RHS;

auto LHSFixedSema = Ctx.getFixedPointSemantics(LHSTy);
auto RHSFixedSema = Ctx.getFixedPointSemantics(RHSTy);
auto ResultFixedSema = Ctx.getFixedPointSemantics(ResultTy);
auto CommonFixedSema = LHSFixedSema.getCommonSemantics(RHSFixedSema);

// Convert the operands to the full precision type.
Value *FullLHS = EmitFixedPointConversion(LHS, LHSFixedSema, CommonFixedSema,
BinOp->getExprLoc());
Value *FullRHS = EmitFixedPointConversion(RHS, RHSFixedSema, CommonFixedSema,
BinOp->getExprLoc());

// Perform the actual addition.
Value *Result;
if (ResultFixedSema.isSaturated()) {
llvm::Intrinsic::ID IID = ResultFixedSema.isSigned()
? llvm::Intrinsic::sadd_sat
: llvm::Intrinsic::uadd_sat;
Result = Builder.CreateBinaryIntrinsic(IID, FullLHS, FullRHS);
} else {
Result = Builder.CreateAdd(FullLHS, FullRHS);
}

// Convert to the result type.
return EmitFixedPointConversion(Result, CommonFixedSema, ResultFixedSema,
BinOp->getExprLoc());
}

Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
// The LHS is always a pointer if either side is.
if (!op.LHS->getType()->isPointerTy()) {
Expand Down

0 comments on commit 2044ac8

Please sign in to comment.