Skip to content

Commit 2044ac8

Browse files
committedJan 16, 2019
[Fixed Point Arithmetic] Fixed Point Addition
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
1 parent 07d8b32 commit 2044ac8

File tree

9 files changed

+667
-20
lines changed

9 files changed

+667
-20
lines changed
 

‎clang/include/clang/AST/ASTContext.h

+6
Original file line numberDiff line numberDiff line change
@@ -2624,6 +2624,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
26242624
// corresponding saturated type for a given fixed point type.
26252625
QualType getCorrespondingSaturatedType(QualType Ty) const;
26262626

2627+
// This method accepts fixed point types and returns the corresponding signed
2628+
// type. Unlike getCorrespondingUnsignedType(), this only accepts unsigned
2629+
// fixed point types because there are unsigned integer types like bool and
2630+
// char8_t that don't have signed equivalents.
2631+
QualType getCorrespondingSignedFixedPointType(QualType Ty) const;
2632+
26272633
//===--------------------------------------------------------------------===//
26282634
// Integer Values
26292635
//===--------------------------------------------------------------------===//

‎clang/include/clang/AST/Type.h

+7
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,9 @@ class Type : public ExtQualsTypeCommonBase {
22692269
/// ISO/IEC JTC1 SC22 WG14 N1169.
22702270
bool isFixedPointType() const;
22712271

2272+
/// Return true if this is a fixed point or integer type.
2273+
bool isFixedPointOrIntegerType() const;
2274+
22722275
/// Return true if this is a saturated fixed point type according to
22732276
/// ISO/IEC JTC1 SC22 WG14 N1169. This type can be signed or unsigned.
22742277
bool isSaturatedFixedPointType() const;
@@ -6596,6 +6599,10 @@ inline bool Type::isFixedPointType() const {
65966599
return false;
65976600
}
65986601

6602+
inline bool Type::isFixedPointOrIntegerType() const {
6603+
return isFixedPointType() || isIntegerType();
6604+
}
6605+
65996606
inline bool Type::isSaturatedFixedPointType() const {
66006607
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) {
66016608
return BT->getKind() >= BuiltinType::SatShortAccum &&

‎clang/include/clang/Basic/FixedPoint.h

+21
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define LLVM_CLANG_BASIC_FIXEDPOINT_H
1919

2020
#include "llvm/ADT/APSInt.h"
21+
#include "llvm/Support/raw_ostream.h"
2122

2223
namespace clang {
2324

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

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

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

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

62+
/// Return the FixedPointSemantics that allows for calculating the full
63+
/// precision semantic that can precisely represent the precision and ranges
64+
/// of both input values. This does not compute the resulting semantics for a
65+
/// given binary operation.
66+
FixedPointSemantics
67+
getCommonSemantics(const FixedPointSemantics &Other) const;
68+
69+
/// Return the FixedPointSemantics for an integer type.
70+
static FixedPointSemantics GetIntegerSemantics(unsigned Width,
71+
bool IsSigned) {
72+
return FixedPointSemantics(Width, /*Scale=*/0, IsSigned,
73+
/*IsSaturated=*/false,
74+
/*HasUnsignedPadding=*/false);
75+
}
76+
5677
private:
5778
unsigned Width;
5879
unsigned Scale;

‎clang/lib/AST/ASTContext.cpp

+42-1
Original file line numberDiff line numberDiff line change
@@ -10485,7 +10485,13 @@ unsigned char ASTContext::getFixedPointIBits(QualType Ty) const {
1048510485
}
1048610486

1048710487
FixedPointSemantics ASTContext::getFixedPointSemantics(QualType Ty) const {
10488-
assert(Ty->isFixedPointType());
10488+
assert(Ty->isFixedPointType() ||
10489+
Ty->isIntegerType() && "Can only get the fixed point semantics for a "
10490+
"fixed point or integer type.");
10491+
if (Ty->isIntegerType())
10492+
return FixedPointSemantics::GetIntegerSemantics(getIntWidth(Ty),
10493+
Ty->isSignedIntegerType());
10494+
1048910495
bool isSigned = Ty->isSignedFixedPointType();
1049010496
return FixedPointSemantics(
1049110497
static_cast<unsigned>(getTypeSize(Ty)), getFixedPointScale(Ty), isSigned,
@@ -10502,3 +10508,38 @@ APFixedPoint ASTContext::getFixedPointMin(QualType Ty) const {
1050210508
assert(Ty->isFixedPointType());
1050310509
return APFixedPoint::getMin(getFixedPointSemantics(Ty));
1050410510
}
10511+
10512+
QualType ASTContext::getCorrespondingSignedFixedPointType(QualType Ty) const {
10513+
assert(Ty->isUnsignedFixedPointType() &&
10514+
"Expected unsigned fixed point type");
10515+
const auto *BTy = Ty->getAs<BuiltinType>();
10516+
10517+
switch (BTy->getKind()) {
10518+
case BuiltinType::UShortAccum:
10519+
return ShortAccumTy;
10520+
case BuiltinType::UAccum:
10521+
return AccumTy;
10522+
case BuiltinType::ULongAccum:
10523+
return LongAccumTy;
10524+
case BuiltinType::SatUShortAccum:
10525+
return SatShortAccumTy;
10526+
case BuiltinType::SatUAccum:
10527+
return SatAccumTy;
10528+
case BuiltinType::SatULongAccum:
10529+
return SatLongAccumTy;
10530+
case BuiltinType::UShortFract:
10531+
return ShortFractTy;
10532+
case BuiltinType::UFract:
10533+
return FractTy;
10534+
case BuiltinType::ULongFract:
10535+
return LongFractTy;
10536+
case BuiltinType::SatUShortFract:
10537+
return SatShortFractTy;
10538+
case BuiltinType::SatUFract:
10539+
return SatFractTy;
10540+
case BuiltinType::SatULongFract:
10541+
return SatLongFractTy;
10542+
default:
10543+
llvm_unreachable("Unexpected unsigned fixed point type");
10544+
}
10545+
}

‎clang/lib/Basic/FixedPoint.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,29 @@ APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {
112112
return APFixedPoint(Val, Sema);
113113
}
114114

115+
FixedPointSemantics FixedPointSemantics::getCommonSemantics(
116+
const FixedPointSemantics &Other) const {
117+
unsigned CommonScale = std::max(getScale(), Other.getScale());
118+
unsigned CommonWidth =
119+
std::max(getIntegralBits(), Other.getIntegralBits()) + CommonScale;
120+
121+
bool ResultIsSigned = isSigned() || Other.isSigned();
122+
bool ResultIsSaturated = isSaturated() || Other.isSaturated();
123+
bool ResultHasUnsignedPadding = false;
124+
if (!ResultIsSigned) {
125+
// Both are unsigned.
126+
ResultHasUnsignedPadding = hasUnsignedPadding() &&
127+
Other.hasUnsignedPadding() && !ResultIsSaturated;
128+
}
129+
130+
// If the result is signed, add an extra bit for the sign. Otherwise, if it is
131+
// unsigned and has unsigned padding, we only need to add the extra padding
132+
// bit back if we are not saturating.
133+
if (ResultIsSigned || ResultHasUnsignedPadding)
134+
CommonWidth++;
135+
136+
return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned,
137+
ResultIsSaturated, ResultHasUnsignedPadding);
138+
}
139+
115140
} // namespace clang

‎clang/lib/CodeGen/CGExprScalar.cpp

+76-6
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ struct BinOpInfo {
125125
return CFP->isZero();
126126
return true;
127127
}
128+
129+
/// Check if either operand is a fixed point type, in which case, this
130+
/// operation did not follow usual arithmetic conversion and both operands may
131+
/// not be the same.
132+
bool isFixedPointBinOp() const {
133+
return isa<BinaryOperator>(E) && Ty->isFixedPointType();
134+
}
128135
};
129136

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

352359
Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy,
353360
SourceLocation Loc);
361+
Value *EmitFixedPointConversion(Value *Src, FixedPointSemantics &SrcFixedSema,
362+
FixedPointSemantics &DstFixedSema,
363+
SourceLocation Loc);
354364

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

742+
// Helper functions for fixed point binary operations.
743+
Value *EmitFixedPointAdd(const BinOpInfo &Ops);
744+
732745
BinOpInfo EmitBinOps(const BinaryOperator *E);
733746
LValue EmitCompoundAssignLValue(const CompoundAssignOperator *E,
734747
Value *(ScalarExprEmitter::*F)(const BinOpInfo &),
@@ -1423,17 +1436,23 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
14231436
Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
14241437
QualType DstTy,
14251438
SourceLocation Loc) {
1426-
using llvm::APInt;
1427-
using llvm::ConstantInt;
1428-
using llvm::Value;
1429-
14301439
assert(SrcTy->isFixedPointType());
14311440
assert(DstTy->isFixedPointType());
14321441

14331442
FixedPointSemantics SrcFPSema =
14341443
CGF.getContext().getFixedPointSemantics(SrcTy);
14351444
FixedPointSemantics DstFPSema =
14361445
CGF.getContext().getFixedPointSemantics(DstTy);
1446+
return EmitFixedPointConversion(Src, SrcFPSema, DstFPSema, Loc);
1447+
}
1448+
1449+
Value *ScalarExprEmitter::EmitFixedPointConversion(
1450+
Value *Src, FixedPointSemantics &SrcFPSema, FixedPointSemantics &DstFPSema,
1451+
SourceLocation Loc) {
1452+
using llvm::APInt;
1453+
using llvm::ConstantInt;
1454+
using llvm::Value;
1455+
14371456
unsigned SrcWidth = SrcFPSema.getWidth();
14381457
unsigned DstWidth = DstFPSema.getWidth();
14391458
unsigned SrcScale = SrcFPSema.getScale();
@@ -1462,7 +1481,8 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
14621481
} else {
14631482
// Adjust the number of fractional bits.
14641483
if (DstScale > SrcScale) {
1465-
ResultWidth = SrcWidth + DstScale - SrcScale;
1484+
// Compare to DstWidth to prevent resizing twice.
1485+
ResultWidth = std::max(SrcWidth + DstScale - SrcScale, DstWidth);
14661486
llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth);
14671487
Result = Builder.CreateIntCast(Result, UpscaledTy, SrcIsSigned, "resize");
14681488
Result = Builder.CreateShl(Result, DstScale - SrcScale, "upscale");
@@ -1493,7 +1513,8 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
14931513
}
14941514

14951515
// Resize the integer part to get the final destination size.
1496-
Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
1516+
if (ResultWidth != DstWidth)
1517+
Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
14971518
}
14981519
return Result;
14991520
}
@@ -3338,9 +3359,58 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) {
33383359
return propagateFMFlags(V, op);
33393360
}
33403361

3362+
if (op.isFixedPointBinOp())
3363+
return EmitFixedPointAdd(op);
3364+
33413365
return Builder.CreateAdd(op.LHS, op.RHS, "add");
33423366
}
33433367

3368+
/// The resulting value must be calculated with exact precision, so the operands
3369+
/// may not be the same type.
3370+
Value *ScalarExprEmitter::EmitFixedPointAdd(const BinOpInfo &op) {
3371+
using llvm::APSInt;
3372+
using llvm::ConstantInt;
3373+
3374+
const auto *BinOp = cast<BinaryOperator>(op.E);
3375+
assert(BinOp->getOpcode() == BO_Add && "Expected operation to be addition");
3376+
3377+
// The result is a fixed point type and at least one of the operands is fixed
3378+
// point while the other is either fixed point or an int. This resulting type
3379+
// should be determined by Sema::handleFixedPointConversions().
3380+
QualType ResultTy = op.Ty;
3381+
QualType LHSTy = BinOp->getLHS()->getType();
3382+
QualType RHSTy = BinOp->getRHS()->getType();
3383+
ASTContext &Ctx = CGF.getContext();
3384+
Value *LHS = op.LHS;
3385+
Value *RHS = op.RHS;
3386+
3387+
auto LHSFixedSema = Ctx.getFixedPointSemantics(LHSTy);
3388+
auto RHSFixedSema = Ctx.getFixedPointSemantics(RHSTy);
3389+
auto ResultFixedSema = Ctx.getFixedPointSemantics(ResultTy);
3390+
auto CommonFixedSema = LHSFixedSema.getCommonSemantics(RHSFixedSema);
3391+
3392+
// Convert the operands to the full precision type.
3393+
Value *FullLHS = EmitFixedPointConversion(LHS, LHSFixedSema, CommonFixedSema,
3394+
BinOp->getExprLoc());
3395+
Value *FullRHS = EmitFixedPointConversion(RHS, RHSFixedSema, CommonFixedSema,
3396+
BinOp->getExprLoc());
3397+
3398+
// Perform the actual addition.
3399+
Value *Result;
3400+
if (ResultFixedSema.isSaturated()) {
3401+
llvm::Intrinsic::ID IID = ResultFixedSema.isSigned()
3402+
? llvm::Intrinsic::sadd_sat
3403+
: llvm::Intrinsic::uadd_sat;
3404+
Result = Builder.CreateBinaryIntrinsic(IID, FullLHS, FullRHS);
3405+
} else {
3406+
Result = Builder.CreateAdd(FullLHS, FullRHS);
3407+
}
3408+
3409+
// Convert to the result type.
3410+
return EmitFixedPointConversion(Result, CommonFixedSema, ResultFixedSema,
3411+
BinOp->getExprLoc());
3412+
}
3413+
33443414
Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
33453415
// The LHS is always a pointer if either side is.
33463416
if (!op.LHS->getType()->isPointerTy()) {

0 commit comments

Comments
 (0)
Please sign in to comment.