summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/ASTContext.h6
-rw-r--r--clang/include/clang/AST/Type.h7
-rw-r--r--clang/include/clang/Basic/FixedPoint.h21
-rw-r--r--clang/lib/AST/ASTContext.cpp43
-rw-r--r--clang/lib/Basic/FixedPoint.cpp25
-rw-r--r--clang/lib/CodeGen/CGExprScalar.cpp82
-rw-r--r--clang/lib/Sema/SemaExpr.cpp91
-rw-r--r--clang/test/Frontend/fixed_point_add.c390
-rw-r--r--clang/test/Frontend/fixed_point_conversions.c22
9 files changed, 667 insertions, 20 deletions
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 13870116c7f..aadc3acb194 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2624,6 +2624,12 @@ public:
// 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
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index d4c97b1b5ef..4c48473d2e0 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2269,6 +2269,9 @@ public:
/// 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;
@@ -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 &&
diff --git a/clang/include/clang/Basic/FixedPoint.h b/clang/include/clang/Basic/FixedPoint.h
index 9a9b7cc9c1f..bfba55a0410 100644
--- a/clang/include/clang/Basic/FixedPoint.h
+++ b/clang/include/clang/Basic/FixedPoint.h
@@ -18,6 +18,7 @@
#define LLVM_CLANG_BASIC_FIXEDPOINT_H
#include "llvm/ADT/APSInt.h"
+#include "llvm/Support/raw_ostream.h"
namespace clang {
@@ -36,6 +37,8 @@ public:
: 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; }
@@ -46,6 +49,9 @@ public:
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;
@@ -53,6 +59,21 @@ public:
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;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 21b6f36e9aa..ed203a331d5 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -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,
@@ -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");
+ }
+}
diff --git a/clang/lib/Basic/FixedPoint.cpp b/clang/lib/Basic/FixedPoint.cpp
index bfff0fc212e..0aaa9af191d 100644
--- a/clang/lib/Basic/FixedPoint.cpp
+++ b/clang/lib/Basic/FixedPoint.cpp
@@ -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
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 1c14d4c99a2..6b3c78121bf 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -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) {
@@ -351,6 +358,9 @@ public:
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.
@@ -729,6 +739,9 @@ public:
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 &),
@@ -1423,10 +1436,6 @@ 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());
@@ -1434,6 +1443,16 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
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();
@@ -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");
@@ -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;
}
@@ -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()) {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d5416d4d057..533b3b7da9f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1250,6 +1250,93 @@ static QualType handleComplexIntConversion(Sema &S, ExprResult &LHS,
return ComplexType;
}
+/// Return the rank of a given fixed point or integer type. The value itself
+/// doesn't matter, but the values must be increasing with proper increasing
+/// rank as described in N1169 4.1.1.
+static unsigned GetFixedPointRank(QualType Ty) {
+ const auto *BTy = Ty->getAs<BuiltinType>();
+ assert(BTy && "Expected a builtin type.");
+
+ switch (BTy->getKind()) {
+ case BuiltinType::ShortFract:
+ case BuiltinType::UShortFract:
+ case BuiltinType::SatShortFract:
+ case BuiltinType::SatUShortFract:
+ return 1;
+ case BuiltinType::Fract:
+ case BuiltinType::UFract:
+ case BuiltinType::SatFract:
+ case BuiltinType::SatUFract:
+ return 2;
+ case BuiltinType::LongFract:
+ case BuiltinType::ULongFract:
+ case BuiltinType::SatLongFract:
+ case BuiltinType::SatULongFract:
+ return 3;
+ case BuiltinType::ShortAccum:
+ case BuiltinType::UShortAccum:
+ case BuiltinType::SatShortAccum:
+ case BuiltinType::SatUShortAccum:
+ return 4;
+ case BuiltinType::Accum:
+ case BuiltinType::UAccum:
+ case BuiltinType::SatAccum:
+ case BuiltinType::SatUAccum:
+ return 5;
+ case BuiltinType::LongAccum:
+ case BuiltinType::ULongAccum:
+ case BuiltinType::SatLongAccum:
+ case BuiltinType::SatULongAccum:
+ return 6;
+ default:
+ if (BTy->isInteger())
+ return 0;
+ llvm_unreachable("Unexpected fixed point or integer type");
+ }
+}
+
+/// handleFixedPointConversion - Fixed point operations between fixed
+/// point types and integers or other fixed point types do not fall under
+/// usual arithmetic conversion since these conversions could result in loss
+/// of precsision (N1169 4.1.4). These operations should be calculated with
+/// the full precision of their result type (N1169 4.1.6.2.1).
+static QualType handleFixedPointConversion(Sema &S, QualType LHSTy,
+ QualType RHSTy) {
+ assert((LHSTy->isFixedPointType() || RHSTy->isFixedPointType()) &&
+ "Expected at least one of the operands to be a fixed point type");
+ assert((LHSTy->isFixedPointOrIntegerType() ||
+ RHSTy->isFixedPointOrIntegerType()) &&
+ "Special fixed point arithmetic operation conversions are only "
+ "applied to ints or other fixed point types");
+
+ // If one operand has signed fixed-point type and the other operand has
+ // unsigned fixed-point type, then the unsigned fixed-point operand is
+ // converted to its corresponding signed fixed-point type and the resulting
+ // type is the type of the converted operand.
+ if (RHSTy->isSignedFixedPointType() && LHSTy->isUnsignedFixedPointType())
+ LHSTy = S.Context.getCorrespondingSignedFixedPointType(LHSTy);
+ else if (RHSTy->isUnsignedFixedPointType() && LHSTy->isSignedFixedPointType())
+ RHSTy = S.Context.getCorrespondingSignedFixedPointType(RHSTy);
+
+ // The result type is the type with the highest rank, whereby a fixed-point
+ // conversion rank is always greater than an integer conversion rank; if the
+ // type of either of the operands is a saturating fixedpoint type, the result
+ // type shall be the saturating fixed-point type corresponding to the type
+ // with the highest rank; the resulting value is converted (taking into
+ // account rounding and overflow) to the precision of the resulting type.
+ // Same ranks between signed and unsigned types are resolved earlier, so both
+ // types are either signed or both unsigned at this point.
+ unsigned LHSTyRank = GetFixedPointRank(LHSTy);
+ unsigned RHSTyRank = GetFixedPointRank(RHSTy);
+
+ QualType ResultTy = LHSTyRank > RHSTyRank ? LHSTy : RHSTy;
+
+ if (LHSTy->isSaturatedFixedPointType() || RHSTy->isSaturatedFixedPointType())
+ ResultTy = S.Context.getCorrespondingSaturatedType(ResultTy);
+
+ return ResultTy;
+}
+
/// UsualArithmeticConversions - Performs various conversions that are common to
/// binary operators (C99 6.3.1.8). If both operands aren't arithmetic, this
/// routine returns the first non-arithmetic type found. The client is
@@ -1322,12 +1409,14 @@ QualType Sema::UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS,
return handleComplexIntConversion(*this, LHS, RHS, LHSType, RHSType,
IsCompAssign);
+ if (LHSType->isFixedPointType() || RHSType->isFixedPointType())
+ return handleFixedPointConversion(*this, LHSType, RHSType);
+
// Finally, we have two differing integer types.
return handleIntegerConversion<doIntegralCast, doIntegralCast>
(*this, LHS, RHS, LHSType, RHSType, IsCompAssign);
}
-
//===----------------------------------------------------------------------===//
// Semantic Analysis for various Expression Types
//===----------------------------------------------------------------------===//
diff --git a/clang/test/Frontend/fixed_point_add.c b/clang/test/Frontend/fixed_point_add.c
new file mode 100644
index 00000000000..ff3cdb9d4ef
--- /dev/null
+++ b/clang/test/Frontend/fixed_point_add.c
@@ -0,0 +1,390 @@
+// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED
+// RUN: %clang_cc1 -ffixed-point -fpadding-on-unsigned-fixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,UNSIGNED
+
+void SignedAddition() {
+ // CHECK-LABEL: SignedAddition
+ short _Accum sa;
+ _Accum a, b, c, d;
+ long _Accum la;
+ unsigned short _Accum usa;
+ unsigned _Accum ua;
+ unsigned long _Accum ula;
+
+ short _Fract sf;
+ _Fract f;
+ long _Fract lf;
+ unsigned short _Fract usf;
+ unsigned _Fract uf;
+ unsigned long _Fract ulf;
+
+ // Same type
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[SA2:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[SA2]]
+ // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2
+ sa = sa + sa;
+
+ // To larger scale and larger width
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[A:%[0-9]+]] = load i32, i32* %a, align 4
+ // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i32
+ // CHECK-NEXT: [[SA:%[a-z0-9]+]] = shl i32 [[EXT_SA]], 8
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[SA]], [[A]]
+ // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4
+ a = sa + a;
+
+ // To same scale and smaller width
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[SF:%[0-9]+]] = load i8, i8* %sf, align 1
+ // CHECK-NEXT: [[EXT_SF:%[a-z0-9]+]] = sext i8 [[SF]] to i16
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[EXT_SF]]
+ // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2
+ sa = sa + sf;
+
+ // To smaller scale and same width.
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[F:%[0-9]+]] = load i16, i16* %f, align 2
+ // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i24
+ // CHECK-NEXT: [[SA:%[a-z0-9]+]] = shl i24 [[EXT_SA]], 8
+ // CHECK-NEXT: [[EXT_F:%[a-z0-9]+]] = sext i16 [[F]] to i24
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i24 [[SA]], [[EXT_F]]
+ // CHECK-NEXT: [[RES:%[a-z0-9]+]] = ashr i24 [[SUM]], 8
+ // CHECK-NEXT: [[TRUNC_RES:%[a-z0-9]+]] = trunc i24 [[RES]] to i16
+ // CHECK-NEXT: store i16 [[TRUNC_RES]], i16* %sa, align 2
+ sa = sa + f;
+
+ // To smaller scale and smaller width
+ // CHECK: [[A:%[0-9]+]] = load i32, i32* %a, align 4
+ // CHECK-NEXT: [[SF:%[0-9]+]] = load i8, i8* %sf, align 1
+ // CHECK-NEXT: [[EXT_SF:%[a-z0-9]+]] = sext i8 [[SF]] to i32
+ // CHECK-NEXT: [[SF:%[a-z0-9]+]] = shl i32 [[EXT_SF]], 8
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[A]], [[SF]]
+ // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4
+ a = a + sf;
+
+ // To larger scale and same width
+ // CHECK: [[A:%[0-9]+]] = load i32, i32* %a, align 4
+ // CHECK-NEXT: [[LF:%[0-9]+]] = load i32, i32* %lf, align 4
+ // CHECK-NEXT: [[EXT_A:%[a-z0-9]+]] = sext i32 [[A]] to i48
+ // CHECK-NEXT: [[A:%[a-z0-9]+]] = shl i48 [[EXT_A]], 16
+ // CHECK-NEXT: [[EXT_LF:%[a-z0-9]+]] = sext i32 [[LF]] to i48
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i48 [[A]], [[EXT_LF]]
+ // CHECK-NEXT: [[RES:%[a-z0-9]+]] = ashr i48 [[SUM]], 16
+ // CHECK-NEXT: [[TRUNC_RES:%[a-z0-9]+]] = trunc i48 [[RES]] to i32
+ // CHECK-NEXT: store i32 [[TRUNC_RES]], i32* %a, align 4
+ a = a + lf;
+
+ // With corresponding unsigned type
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+ // SIGNED-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i17
+ // SIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i17 [[SA_EXT]], 1
+ // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i17
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i17 [[SA]], [[USA_EXT]]
+ // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = ashr i17 [[SUM]], 1
+ // SIGNED-NEXT: [[SUM:%[a-z0-9]+]] = trunc i17 [[RESULT]] to i16
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[USA]]
+ // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2
+ sa = sa + usa;
+
+ // With unsigned of larger scale
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[USA:%[0-9]+]] = load i32, i32* %ua, align 4
+ // SIGNED-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i33
+ // SIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i33 [[SA_EXT]], 9
+ // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i32 [[USA]] to i33
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i33 [[SA]], [[USA_EXT]]
+ // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = ashr i33 [[SUM]], 1
+ // SIGNED-NEXT: [[SUM:%[a-z0-9]+]] = trunc i33 [[RESULT]] to i32
+ // UNSIGNED-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i32
+ // UNSIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i32 [[EXT_SA]], 8
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i32 [[SA]], [[USA]]
+ // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4
+ a = sa + ua;
+
+ // With unsigned of smaller width
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[USF:%[0-9]+]] = load i8, i8* %usf, align 1
+ // SIGNED-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i17
+ // SIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i17 [[SA_EXT]], 1
+ // SIGNED-NEXT: [[USF_EXT:%[a-z0-9]+]] = zext i8 [[USF]] to i17
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i17 [[SA]], [[USF_EXT]]
+ // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = ashr i17 [[SUM]], 1
+ // SIGNED-NEXT: [[SUM:%[a-z0-9]+]] = trunc i17 [[RESULT]] to i16
+ // UNSIGNED-NEXT: [[EXT_USF:%[a-z0-9]+]] = zext i8 [[USF]] to i16
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[EXT_USF]]
+ // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2
+ sa = sa + usf;
+
+ // With unsigned of larger width and smaller scale
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[ULF:%[0-9]+]] = load i32, i32* %ulf, align 4
+ // SIGNED-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i41
+ // SIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i41 [[SA_EXT]], 25
+ // SIGNED-NEXT: [[ULF_EXT:%[a-z0-9]+]] = zext i32 [[ULF]] to i41
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i41 [[SA]], [[ULF_EXT]]
+ // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = ashr i41 [[SUM]], 25
+ // SIGNED-NEXT: [[RES_TRUNC:%[a-z0-9]+]] = trunc i41 [[RESULT]] to i16
+ // UNSIGNED-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i40
+ // UNSIGNED-NEXT: [[SA:%[a-z0-9]+]] = shl i40 [[EXT_SA]], 24
+ // UNSIGNED-NEXT: [[EXT_ULF:%[a-z0-9]+]] = zext i32 [[ULF]] to i40
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[SA]], [[EXT_ULF]]
+ // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = ashr i40 [[SUM]], 24
+ // UNSIGNED-NEXT: [[RES_TRUNC:%[a-z0-9]+]] = trunc i40 [[RES]] to i16
+ // CHECK-NEXT: store i16 [[RES_TRUNC]], i16* %sa, align 2
+ sa = sa + ulf;
+
+ // Chained additions of the same signed type should result in the same
+ // semantics width.
+ // CHECK: [[A:%[0-9]+]] = load i32, i32* %a, align 4
+ // CHECK-NEXT: [[B:%[0-9]+]] = load i32, i32* %b, align 4
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[A]], [[B]]
+ // CHECK-NEXT: [[C:%[0-9]+]] = load i32, i32* %c, align 4
+ // CHECK-NEXT: [[SUM2:%[0-9]+]] = add i32 [[SUM]], [[C]]
+ // CHECK-NEXT: [[D:%[0-9]+]] = load i32, i32* %d, align 4
+ // CHECK-NEXT: [[SUM3:%[0-9]+]] = add i32 [[SUM2]], [[D]]
+ // CHECK-NEXT: store i32 [[SUM3]], i32* %a, align 4
+ a = a + b + c + d;
+}
+
+void UnsignedAddition() {
+ // CHECK-LABEL: UnsignedAddition
+ unsigned short _Accum usa;
+ unsigned _Accum ua;
+ unsigned long _Accum ula;
+
+ unsigned short _Fract usf;
+ unsigned _Fract uf;
+ unsigned long _Fract ulf;
+
+ // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+ // CHECK-NEXT: [[USA2:%[0-9]+]] = load i16, i16* %usa, align 2
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[USA]], [[USA2]]
+ // CHECK-NEXT: store i16 [[SUM]], i16* %usa, align 2
+ usa = usa + usa;
+
+ // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+ // CHECK-NEXT: [[UA:%[0-9]+]] = load i32, i32* %ua, align 4
+ // CHECK-NEXT: [[EXT_USA:%[a-z0-9]+]] = zext i16 [[USA]] to i32
+ // CHECK-NEXT: [[USA:%[a-z0-9]+]] = shl i32 [[EXT_USA]], 8
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[USA]], [[UA]]
+ // CHECK-NEXT: store i32 [[SUM]], i32* %ua, align 4
+ ua = usa + ua;
+
+ // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+ // CHECK-NEXT: [[USF:%[0-9]+]] = load i8, i8* %usf, align 1
+ // CHECK-NEXT: [[EXT_USF:%[a-z0-9]+]] = zext i8 [[USF]] to i16
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[USA]], [[EXT_USF]]
+ // CHECK-NEXT: store i16 [[SUM]], i16* %usa, align 2
+ usa = usa + usf;
+
+ // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+ // CHECK-NEXT: [[UF:%[0-9]+]] = load i16, i16* %uf, align 2
+ // CHECK-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i24
+ // CHECK-NEXT: [[USA:%[a-z0-9]+]] = shl i24 [[USA_EXT]], 8
+ // CHECK-NEXT: [[UF_EXT:%[a-z0-9]+]] = zext i16 [[UF]] to i24
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i24 [[USA]], [[UF_EXT]]
+ // CHECK-NEXT: [[RES:%[a-z0-9]+]] = lshr i24 [[SUM]], 8
+ // CHECK-NEXT: [[RES_TRUNC:%[a-z0-9]+]] = trunc i24 [[RES]] to i16
+ // CHECK-NEXT: store i16 [[RES_TRUNC]], i16* %usa, align 2
+ usa = usa + uf;
+}
+
+void IntAddition() {
+ // CHECK-LABEL: IntAddition
+ short _Accum sa;
+ _Accum a;
+ unsigned short _Accum usa;
+ _Sat short _Accum sa_sat;
+ int i;
+ unsigned int ui;
+ long _Fract lf;
+ _Bool b;
+
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4
+ // CHECK-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i39
+ // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39
+ // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_EXT]], [[I]]
+ // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16
+ // CHECK-NEXT: store i16 [[RES]], i16* %sa, align 2
+ sa = sa + i;
+
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[UI:%[0-9]+]] = load i32, i32* %ui, align 4
+ // CHECK-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i40
+ // CHECK-NEXT: [[UI_EXT:%[a-z0-9]+]] = zext i32 [[UI]] to i40
+ // CHECK-NEXT: [[UI:%[a-z0-9]+]] = shl i40 [[UI_EXT]], 7
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i40 [[SA_EXT]], [[UI]]
+ // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SUM]] to i16
+ // CHECK-NEXT: store i16 [[RES]], i16* %sa, align 2
+ sa = sa + ui;
+
+ // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+ // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4
+ // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i40
+ // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i40
+ // SIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]]
+ // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SUM]] to i16
+ // UNSIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i39
+ // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39
+ // UNSIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]]
+ // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16
+ // CHECK-NEXT: store i16 [[RES]], i16* %usa, align 2
+ usa = usa + i;
+
+ // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+ // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4
+ // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i40
+ // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i40
+ // SIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]]
+ // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SUM]] to i16
+ // UNSIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i39
+ // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i39
+ // UNSIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]]
+ // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16
+ // CHECK-NEXT: store i16 [[RES]], i16* %usa, align 2
+ usa = usa + ui;
+
+ // CHECK: [[LF:%[0-9]+]] = load i32, i32* %lf, align 4
+ // CHECK-NEXT: [[UI:%[0-9]+]] = load i32, i32* %ui, align 4
+ // CHECK-NEXT: [[LF_EXT:%[a-z0-9]+]] = sext i32 [[LF]] to i64
+ // CHECK-NEXT: [[UI_EXT:%[a-z0-9]+]] = zext i32 [[UI]] to i64
+ // CHECK-NEXT: [[UI:%[a-z0-9]+]] = shl i64 [[UI_EXT]], 31
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i64 [[LF_EXT]], [[UI]]
+ // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i64 [[SUM]] to i32
+ // CHECK-NEXT: store i32 [[RES]], i32* %lf, align 4
+ lf = lf + ui;
+
+ // CHECK: [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+ // CHECK-NEXT: [[BOOL:%[0-9]+]] = load i8, i8* %b, align 1
+ // CHECK-NEXT: [[AS_BOOL:%[a-z0-9]+]] = trunc i8 [[BOOL]] to i1
+ // CHECK-NEXT: [[BOOL_EXT:%[a-z0-9]+]] = zext i1 [[AS_BOOL]] to i32
+ // CHECK-NEXT: [[ACCUM_EXT:%[a-z0-9]+]] = sext i32 [[ACCUM]] to i47
+ // CHECK-NEXT: [[BOOL:%[a-z0-9]+]] = sext i32 [[BOOL_EXT]] to i47
+ // CHECK-NEXT: [[BOOL_EXT:%[a-z0-9]+]] = shl i47 [[BOOL]], 15
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = add i47 [[ACCUM_EXT]], [[BOOL_EXT]]
+ // CHECK-NEXT: [[RESULT:%[a-z0-9]+]] = trunc i47 [[SUM]] to i32
+ // CHECK-NEXT: store i32 [[RESULT]], i32* %a, align 4
+ a = a + b;
+}
+
+void SaturatedAddition() {
+ // CHECK-LABEL: SaturatedAddition
+ short _Accum sa;
+ _Accum a;
+ long _Accum la;
+ unsigned short _Accum usa;
+ unsigned _Accum ua;
+ unsigned long _Accum ula;
+
+ _Sat short _Accum sa_sat;
+ _Sat _Accum a_sat;
+ _Sat long _Accum la_sat;
+ _Sat unsigned short _Accum usa_sat;
+ _Sat unsigned _Accum ua_sat;
+ _Sat unsigned long _Accum ula_sat;
+ _Sat unsigned _Fract uf_sat;
+
+ int i;
+ unsigned int ui;
+
+ // CHECK: [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+ // CHECK-NEXT: [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.sadd.sat.i16(i16 [[SA]], i16
+ // [[SA_SAT]])
+ // CHECK-NEXT: store i16 [[SUM]], i16* %sa_sat, align 2
+ sa_sat = sa + sa_sat;
+
+ // CHECK: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+ // CHECK-NEXT: [[USA_SAT:%[0-9]+]] = load i16, i16* %usa_sat, align 2
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.uadd.sat.i16(i16 [[USA]], i16 [[USA_SAT]])
+ // SIGNED-NEXT: store i16 [[SUM]], i16* %usa_sat, align 2
+ // UNSIGNED-NEXT: [[USA_TRUNC:%[a-z0-9]+]] = trunc i16 [[USA]] to i15
+ // UNSIGNED-NEXT: [[USA_SAT_TRUNC:%[a-z0-9]+]] = trunc i16 [[USA_SAT]] to i15
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = call i15 @llvm.uadd.sat.i15(i15 [[USA_TRUNC]], i15 [[USA_SAT_TRUNC]])
+ // UNSIGNED-NEXT: [[SUM_EXT:%[a-z0-9]+]] = zext i15 [[SUM]] to i16
+ // UNSIGNED-NEXT: store i16 [[SUM_EXT]], i16* %usa_sat, align 2
+ usa_sat = usa + usa_sat;
+
+ // CHECK: [[UA:%[0-9]+]] = load i32, i32* %ua, align 4
+ // CHECK-NEXT: [[USA:%[0-9]+]] = load i16, i16* %usa_sat, align 2
+ // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i32
+ // SIGNED-NEXT: [[USA:%[a-z0-9]+]] = shl i32 [[USA_EXT]], 8
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = call i32 @llvm.uadd.sat.i32(i32 [[UA]], i32 [[USA]])
+ // SIGNED-NEXT: store i32 [[SUM]], i32* %ua_sat, align 4
+ // UNSIGNED-NEXT: [[UA_TRUNC:%[a-z0-9]+]] = trunc i32 [[UA]] to i31
+ // UNSIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i31
+ // UNSIGNED-NEXT: [[USA:%[a-z0-9]+]] = shl i31 [[USA_EXT]], 8
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = call i31 @llvm.uadd.sat.i31(i31 [[UA_TRUNC]], i31 [[USA]])
+ // UNSIGNED-NEXT: [[SUM_EXT:%[a-z0-9]+]] = zext i31 [[SUM]] to i32
+ // UNSIGNED-NEXT: store i32 [[SUM_EXT]], i32* %ua_sat, align 4
+ ua_sat = ua + usa_sat;
+
+ // CHECK: [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2
+ // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4
+ // CHECK-NEXT: [[SA_SAT_EXT:%[a-z0-9]+]] = sext i16 [[SA_SAT]] to i39
+ // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39
+ // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = call i39 @llvm.sadd.sat.i39(i39 [[SA_SAT_EXT]], i39 [[I]])
+ // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767
+ // CHECK-NEXT: [[RES:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[SUM]]
+ // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[RES]], -32768
+ // CHECK-NEXT: [[RES2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[RES]]
+ // CHECK-NEXT: [[RES3:%[a-z0-9]+]] = trunc i39 [[RES2]] to i16
+ // CHECK-NEXT: store i16 [[RES3]], i16* %sa_sat, align 2
+ sa_sat = sa_sat + i;
+
+ // CHECK: [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2
+ // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4
+ // CHECK-NEXT: [[SA_SAT_EXT:%[a-z0-9]+]] = sext i16 [[SA_SAT]] to i40
+ // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i40
+ // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i40 [[I_EXT]], 7
+ // CHECK-NEXT: [[SUM:%[0-9]+]] = call i40 @llvm.sadd.sat.i40(i40 [[SA_SAT_EXT]], i40 [[I]])
+ // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i40 [[SUM]], 32767
+ // CHECK-NEXT: [[RES:%[a-z0-9]+]] = select i1 [[USE_MAX]], i40 32767, i40 [[SUM]]
+ // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i40 [[RES]], -32768
+ // CHECK-NEXT: [[RES2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i40 -32768, i40 [[RES]]
+ // CHECK-NEXT: [[RES3:%[a-z0-9]+]] = trunc i40 [[RES2]] to i16
+ // CHECK-NEXT: store i16 [[RES3]], i16* %sa_sat, align 2
+ sa_sat = sa_sat + ui;
+
+ // CHECK: [[UF_SAT:%[0-9]+]] = load i16, i16* %uf_sat, align 2
+ // CHECK-NEXT: [[UF_SAT2:%[0-9]+]] = load i16, i16* %uf_sat, align 2
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.uadd.sat.i16(i16 [[UF_SAT]], i16 [[UF_SAT2]])
+ // SIGNED-NEXT: store i16 [[SUM]], i16* %uf_sat, align 2
+ // UNSIGNED-NEXT: [[UF_SAT_TRUNC:%[a-z0-9]+]] = trunc i16 [[UF_SAT]] to i15
+ // UNSIGNED-NEXT: [[UF_SAT_TRUNC2:%[a-z0-9]+]] = trunc i16 [[UF_SAT2]] to i15
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = call i15 @llvm.uadd.sat.i15(i15 [[UF_SAT_TRUNC]], i15 [[UF_SAT_TRUNC2]])
+ // UNSIGNED-NEXT: [[SUM_EXT:%[a-z0-9]+]] = zext i15 [[SUM]] to i16
+ // UNSIGNED-NEXT: store i16 [[SUM_EXT]], i16* %uf_sat, align 2
+ uf_sat = uf_sat + uf_sat;
+
+ // CHECK: [[USA_SAT:%[0-9]+]] = load i16, i16* %usa_sat, align 2
+ // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4
+ // SIGNED-NEXT: [[USA_SAT_RESIZE:%[a-z0-9]+]] = zext i16 [[USA_SAT]] to i40
+ // SIGNED-NEXT: [[I_RESIZE:%[a-z0-9]+]] = sext i32 [[I]] to i40
+ // SIGNED-NEXT: [[I_UPSCALE:%[a-z0-9]+]] = shl i40 [[I_RESIZE]], 8
+ // SIGNED-NEXT: [[SUM:%[0-9]+]] = call i40 @llvm.uadd.sat.i40(i40 [[USA_SAT_RESIZE]], i40 [[I_UPSCALE]])
+ // SIGNED-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i40 [[SUM]], 65535
+ // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = select i1 [[USE_MAX]], i40 65535, i40 [[SUM]]
+ // SIGNED-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i40 [[RESULT]], 0
+ // SIGNED-NEXT: [[RESULT2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i40 0, i40 [[RESULT]]
+ // SIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = trunc i40 [[RESULT2]] to i16
+ // UNSIGNED-NEXT: [[USA_SAT_RESIZE:%[a-z0-9]+]] = zext i16 [[USA_SAT]] to i39
+ // UNSIGNED-NEXT: [[I_RESIZE:%[a-z0-9]+]] = sext i32 [[I]] to i39
+ // UNSIGNED-NEXT: [[I_UPSCALE:%[a-z0-9]+]] = shl i39 [[I_RESIZE]], 7
+ // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = call i39 @llvm.uadd.sat.i39(i39 [[USA_SAT_RESIZE]], i39 [[I_UPSCALE]])
+ // UNSIGNED-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767
+ // UNSIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[SUM]]
+ // UNSIGNED-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[RESULT]], 0
+ // UNSIGNED-NEXT: [[RESULT2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 0, i39 [[RESULT]]
+ // UNSIGNED-NEXT: [[RESULT:%[a-z0-9]+]] = trunc i39 [[RESULT2]] to i16
+ // CHECK-NEXT: store i16 [[RESULT]], i16* %usa_sat, align 2
+ usa_sat = usa_sat + i;
+}
diff --git a/clang/test/Frontend/fixed_point_conversions.c b/clang/test/Frontend/fixed_point_conversions.c
index 7e98fb1e130..22cde0eb690 100644
--- a/clang/test/Frontend/fixed_point_conversions.c
+++ b/clang/test/Frontend/fixed_point_conversions.c
@@ -214,19 +214,17 @@ void TestFixedPointCastSaturation() {
// Only get overflow checking if signed fract to unsigned accum
sat_ua = sat_sf;
// DEFAULT: [[FRACT:%[0-9a-z]+]] = load i8, i8* %sat_sf, align 1
- // DEFAULT-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i17
- // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i17 [[FRACT_EXT]], 9
- // DEFAULT-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i17 [[ACCUM]], 0
- // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i17 0, i17 [[ACCUM]]
- // DEFAULT-NEXT: [[RESULT_EXT:%[0-9a-z]+]] = sext i17 [[RESULT]] to i32
- // DEFAULT-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4
+ // DEFAULT-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32
+ // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 9
+ // DEFAULT-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0
+ // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i32 0, i32 [[ACCUM]]
+ // DEFAULT-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4
// SAME: [[FRACT:%[0-9a-z]+]] = load i8, i8* %sat_sf, align 1
- // SAME-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i16
- // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i16 [[FRACT_EXT]], 8
- // SAME-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i16 [[ACCUM]], 0
- // SAME-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i16 0, i16 [[ACCUM]]
- // SAME-NEXT: [[RESULT_EXT:%[0-9a-z]+]] = sext i16 [[RESULT]] to i32
- // SAME-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4
+ // SAME-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32
+ // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 8
+ // SAME-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0
+ // SAME-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i32 0, i32 [[ACCUM]]
+ // SAME-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4
}
void TestFixedPointCastBetFractAccum() {
OpenPOWER on IntegriCloud