diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -129,11 +129,10 @@ return true; } - /// Check if either operand is a fixed point type or integer type, with at - /// least one being a fixed point type. In any case, this - /// operation did not follow usual arithmetic conversion and both operands may - /// not be the same. - bool isFixedPointBinOp() const { + /// Check if at least one operand is a fixed point type. In such cases, this + /// operation did not follow usual arithmetic conversion and both operands + /// might not be of the same type. + bool isFixedPointOp() const { // We cannot simply check the result type since comparison operations return // an int. if (const auto *BinOp = dyn_cast(E)) { @@ -141,6 +140,8 @@ QualType RHSType = BinOp->getRHS()->getType(); return LHSType->isFixedPointType() || RHSType->isFixedPointType(); } + if (const auto *UnOp = dyn_cast(E)) + return UnOp->getSubExpr()->getType()->isFixedPointType(); return false; } }; @@ -746,7 +747,7 @@ Value *V = Builder.CreateFMul(Ops.LHS, Ops.RHS, "mul"); return propagateFMFlags(V, Ops); } - if (Ops.isFixedPointBinOp()) + if (Ops.isFixedPointOp()) return EmitFixedPointBinOp(Ops); return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); } @@ -2620,6 +2621,36 @@ } } + // Fixed-point types. + } else if (type->isFixedPointType()) { + // Fixed-point types are tricky. In some cases, it isn't possible to + // represent a 1 or a -1 in the type at all. Piggyback off of + // EmitFixedPointBinOp to avoid having to reimplement saturation. + BinOpInfo Info; + Info.E = E; + Info.Ty = E->getType(); + Info.Opcode = isInc ? BO_Add : BO_Sub; + Info.LHS = value; + Info.RHS = llvm::ConstantInt::get(value->getType(), 1, false); + // If the type is signed, it's better to represent this as +(-1) or -(-1), + // since -1 is guaranteed to be representable. + if (type->isSignedFixedPointType()) { + Info.Opcode = isInc ? BO_Sub : BO_Add; + Info.RHS = Builder.CreateNeg(Info.RHS); + } + // Now, convert from our invented integer literal to the type of the unary + // op. This will upscale and saturate if necessary. This value can become + // undef in some cases. + FixedPointSemantics SrcSema = + FixedPointSemantics::GetIntegerSemantics(value->getType() + ->getScalarSizeInBits(), + /*IsSigned=*/true); + FixedPointSemantics DstSema = + CGF.getContext().getFixedPointSemantics(Info.Ty); + Info.RHS = EmitFixedPointConversion(Info.RHS, SrcSema, DstSema, + E->getExprLoc()); + value = EmitFixedPointBinOp(Info); + // Objective-C pointer types. } else { const ObjCObjectPointerType *OPT = type->castAs(); @@ -3123,7 +3154,7 @@ } return Val; } - else if (Ops.isFixedPointBinOp()) + else if (Ops.isFixedPointOp()) return EmitFixedPointBinOp(Ops); else if (Ops.Ty->hasUnsignedIntegerRepresentation()) return Builder.CreateUDiv(Ops.LHS, Ops.RHS, "div"); @@ -3487,7 +3518,7 @@ return propagateFMFlags(V, op); } - if (op.isFixedPointBinOp()) + if (op.isFixedPointOp()) return EmitFixedPointBinOp(op); return Builder.CreateAdd(op.LHS, op.RHS, "add"); @@ -3499,14 +3530,19 @@ using llvm::APSInt; using llvm::ConstantInt; - const auto *BinOp = cast(op.E); - - // 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(). + // This is either a binary operation where at least one of the operands is + // a fixed-point type, or a unary operation where the operand is a fixed-point + // type. The result type of a binary operation is determined by + // Sema::handleFixedPointConversions(). QualType ResultTy = op.Ty; - QualType LHSTy = BinOp->getLHS()->getType(); - QualType RHSTy = BinOp->getRHS()->getType(); + QualType LHSTy, RHSTy; + if (const auto *BinOp = dyn_cast(op.E)) { + LHSTy = BinOp->getLHS()->getType(); + RHSTy = BinOp->getRHS()->getType(); + } else if (const auto *UnOp = dyn_cast(op.E)) { + LHSTy = UnOp->getSubExpr()->getType(); + RHSTy = UnOp->getSubExpr()->getType(); + } ASTContext &Ctx = CGF.getContext(); Value *LHS = op.LHS; Value *RHS = op.RHS; @@ -3518,13 +3554,13 @@ // Convert the operands to the full precision type. Value *FullLHS = EmitFixedPointConversion(LHS, LHSFixedSema, CommonFixedSema, - BinOp->getExprLoc()); + op.E->getExprLoc()); Value *FullRHS = EmitFixedPointConversion(RHS, RHSFixedSema, CommonFixedSema, - BinOp->getExprLoc()); + op.E->getExprLoc()); // Perform the actual addition. Value *Result; - switch (BinOp->getOpcode()) { + switch (op.Opcode) { case BO_Add: { if (ResultFixedSema.isSaturated()) { llvm::Intrinsic::ID IID = ResultFixedSema.isSigned() @@ -3621,7 +3657,7 @@ // Convert to the result type. return EmitFixedPointConversion(Result, CommonFixedSema, ResultFixedSema, - BinOp->getExprLoc()); + op.E->getExprLoc()); } Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { @@ -3655,7 +3691,7 @@ return propagateFMFlags(V, op); } - if (op.isFixedPointBinOp()) + if (op.isFixedPointOp()) return EmitFixedPointBinOp(op); return Builder.CreateSub(op.LHS, op.RHS, "sub"); @@ -3958,7 +3994,7 @@ E->getExprLoc()); } - if (BOInfo.isFixedPointBinOp()) { + if (BOInfo.isFixedPointOp()) { Result = EmitFixedPointBinOp(BOInfo); } else if (LHS->getType()->isFPOrFPVectorTy()) { if (!IsSignaling) diff --git a/clang/test/Frontend/fixed_point_unary.c b/clang/test/Frontend/fixed_point_unary.c new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/fixed_point_unary.c @@ -0,0 +1,264 @@ +// 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 + +_Accum a; +_Fract f; +long _Fract lf; +unsigned _Accum ua; +short unsigned _Accum usa; +unsigned _Fract uf; + +_Sat _Accum sa; +_Sat _Fract sf; +_Sat long _Fract slf; +_Sat unsigned _Accum sua; +_Sat short unsigned _Accum susa; +_Sat unsigned _Fract suf; + +// CHECK-LABEL: @Increment( +void Increment() { +// CHECK: [[TMP0:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[TMP0]], -32768 +// CHECK-NEXT: store i32 [[TMP1]], i32* @a, align 4 + a++; + +// CHECK: [[TMP2:%.*]] = load i16, i16* @f, align 2 +// CHECK-NEXT: [[TMP3:%.*]] = sub i16 [[TMP2]], -32768 +// CHECK-NEXT: store i16 [[TMP3]], i16* @f, align 2 + f++; + +// CHECK: [[TMP4:%.*]] = load i32, i32* @lf, align 4 +// CHECK-NEXT: [[TMP5:%.*]] = sub i32 [[TMP4]], -2147483648 +// CHECK-NEXT: store i32 [[TMP5]], i32* @lf, align 4 + lf++; + +// CHECK: [[TMP6:%.*]] = load i32, i32* @ua, align 4 +// SIGNED-NEXT: [[TMP7:%.*]] = add i32 [[TMP6]], 65536 +// UNSIGNED-NEXT: [[TMP7:%.*]] = add i32 [[TMP6]], 32768 +// CHECK-NEXT: store i32 [[TMP7]], i32* @ua, align 4 + ua++; + +// CHECK: [[TMP8:%.*]] = load i16, i16* @usa, align 2 +// SIGNED-NEXT: [[TMP9:%.*]] = add i16 [[TMP8]], 256 +// UNSIGNED-NEXT: [[TMP9:%.*]] = add i16 [[TMP8]], 128 +// CHECK-NEXT: store i16 [[TMP9]], i16* @usa, align 2 + usa++; + +// CHECK: [[TMP10:%.*]] = load i16, i16* @uf, align 2 +// SIGNED-NEXT: [[TMP11:%.*]] = add i16 [[TMP10]], undef +// UNSIGNED-NEXT: [[TMP11:%.*]] = add i16 [[TMP10]], -32768 +// CHECK-NEXT: store i16 [[TMP11]], i16* @uf, align 2 + uf++; + +// CHECK: [[TMP12:%.*]] = load i32, i32* @sa, align 4 +// CHECK-NEXT: [[TMP13:%.*]] = call i32 @llvm.ssub.sat.i32(i32 [[TMP12]], i32 -32768) +// CHECK-NEXT: store i32 [[TMP13]], i32* @sa, align 4 + sa++; + +// CHECK: [[TMP14:%.*]] = load i16, i16* @sf, align 2 +// CHECK-NEXT: [[TMP15:%.*]] = call i16 @llvm.ssub.sat.i16(i16 [[TMP14]], i16 -32768) +// CHECK-NEXT: store i16 [[TMP15]], i16* @sf, align 2 + sf++; + +// CHECK: [[TMP16:%.*]] = load i32, i32* @slf, align 4 +// CHECK-NEXT: [[TMP17:%.*]] = call i32 @llvm.ssub.sat.i32(i32 [[TMP16]], i32 -2147483648) +// CHECK-NEXT: store i32 [[TMP17]], i32* @slf, align 4 + slf++; + +// CHECK: [[TMP18:%.*]] = load i32, i32* @sua, align 4 +// SIGNED-NEXT: [[TMP19:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[TMP18]], i32 65536) +// SIGNED-NEXT: store i32 [[TMP19]], i32* @sua, align 4 +// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP18]] to i31 +// UNSIGNED-NEXT: [[TMP19:%.*]] = call i31 @llvm.uadd.sat.i31(i31 [[RESIZE]], i31 32768) +// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i31 [[TMP19]] to i32 +// UNSIGNED-NEXT: store i32 [[RESIZE1]], i32* @sua, align 4 + sua++; + +// CHECK: [[TMP20:%.*]] = load i16, i16* @susa, align 2 +// SIGNED-NEXT: [[TMP21:%.*]] = call i16 @llvm.uadd.sat.i16(i16 [[TMP20]], i16 256) +// SIGNED-NEXT: store i16 [[TMP21]], i16* @susa, align 2 +// UNSIGNED-NEXT: [[RESIZE2:%.*]] = trunc i16 [[TMP20]] to i15 +// UNSIGNED-NEXT: [[TMP21:%.*]] = call i15 @llvm.uadd.sat.i15(i15 [[RESIZE2]], i15 128) +// UNSIGNED-NEXT: [[RESIZE3:%.*]] = zext i15 [[TMP21]] to i16 +// UNSIGNED-NEXT: store i16 [[RESIZE3]], i16* @susa, align 2 + susa++; + +// CHECK: [[TMP22:%.*]] = load i16, i16* @suf, align 2 +// SIGNED-NEXT: [[TMP23:%.*]] = call i16 @llvm.uadd.sat.i16(i16 [[TMP22]], i16 -1) +// SIGNED-NEXT: store i16 [[TMP23]], i16* @suf, align 2 +// UNSIGNED-NEXT: [[RESIZE4:%.*]] = trunc i16 [[TMP22]] to i15 +// UNSIGNED-NEXT: [[TMP23:%.*]] = call i15 @llvm.uadd.sat.i15(i15 [[RESIZE4]], i15 -1) +// UNSIGNED-NEXT: [[RESIZE5:%.*]] = zext i15 [[TMP23]] to i16 +// UNSIGNED-NEXT: store i16 [[RESIZE5]], i16* @suf, align 2 + suf++; +} + +// CHECK-LABEL: @Decrement( +void Decrement() { +// CHECK: [[TMP0:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = add i32 [[TMP0]], -32768 +// CHECK-NEXT: store i32 [[TMP1]], i32* @a, align 4 + a--; + +// CHECK: [[TMP2:%.*]] = load i16, i16* @f, align 2 +// CHECK-NEXT: [[TMP3:%.*]] = add i16 [[TMP2]], -32768 +// CHECK-NEXT: store i16 [[TMP3]], i16* @f, align 2 + f--; + +// CHECK: [[TMP4:%.*]] = load i32, i32* @lf, align 4 +// CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], -2147483648 +// CHECK-NEXT: store i32 [[TMP5]], i32* @lf, align 4 + lf--; + +// CHECK: [[TMP6:%.*]] = load i32, i32* @ua, align 4 +// SIGNED-NEXT: [[TMP7:%.*]] = sub i32 [[TMP6]], 65536 +// UNSIGNED-NEXT: [[TMP7:%.*]] = sub i32 [[TMP6]], 32768 +// CHECK-NEXT: store i32 [[TMP7]], i32* @ua, align 4 + ua--; + +// CHECK: [[TMP8:%.*]] = load i16, i16* @usa, align 2 +// SIGNED-NEXT: [[TMP9:%.*]] = sub i16 [[TMP8]], 256 +// UNSIGNED-NEXT: [[TMP9:%.*]] = sub i16 [[TMP8]], 128 +// CHECK-NEXT: store i16 [[TMP9]], i16* @usa, align 2 + usa--; + +// CHECK: [[TMP10:%.*]] = load i16, i16* @uf, align 2 +// SIGNED-NEXT: [[TMP11:%.*]] = sub i16 [[TMP10]], undef +// UNSIGNED-NEXT: [[TMP11:%.*]] = sub i16 [[TMP10]], -32768 +// CHECK-NEXT: store i16 [[TMP11]], i16* @uf, align 2 + uf--; + +// CHECK: [[TMP12:%.*]] = load i32, i32* @sa, align 4 +// CHECK-NEXT: [[TMP13:%.*]] = call i32 @llvm.sadd.sat.i32(i32 [[TMP12]], i32 -32768) +// CHECK-NEXT: store i32 [[TMP13]], i32* @sa, align 4 + sa--; + +// CHECK: [[TMP14:%.*]] = load i16, i16* @sf, align 2 +// CHECK-NEXT: [[TMP15:%.*]] = call i16 @llvm.sadd.sat.i16(i16 [[TMP14]], i16 -32768) +// CHECK-NEXT: store i16 [[TMP15]], i16* @sf, align 2 + sf--; + +// CHECK: [[TMP16:%.*]] = load i32, i32* @slf, align 4 +// CHECK-NEXT: [[TMP17:%.*]] = call i32 @llvm.sadd.sat.i32(i32 [[TMP16]], i32 -2147483648) +// CHECK-NEXT: store i32 [[TMP17]], i32* @slf, align 4 + slf--; + +// CHECK: [[TMP18:%.*]] = load i32, i32* @sua, align 4 +// SIGNED-NEXT: [[TMP19:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[TMP18]], i32 65536) +// SIGNED-NEXT: store i32 [[TMP19]], i32* @sua, align 4 +// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i32 [[TMP18]] to i31 +// UNSIGNED-NEXT: [[TMP19:%.*]] = call i31 @llvm.usub.sat.i31(i31 [[RESIZE]], i31 32768) +// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i31 [[TMP19]] to i32 +// UNSIGNED-NEXT: store i32 [[RESIZE1]], i32* @sua, align 4 + sua--; + +// CHECK: [[TMP20:%.*]] = load i16, i16* @susa, align 2 +// SIGNED-NEXT: [[TMP21:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[TMP20]], i16 256) +// SIGNED-NEXT: store i16 [[TMP21]], i16* @susa, align 2 +// UNSIGNED-NEXT: [[RESIZE2:%.*]] = trunc i16 [[TMP20]] to i15 +// UNSIGNED-NEXT: [[TMP21:%.*]] = call i15 @llvm.usub.sat.i15(i15 [[RESIZE2]], i15 128) +// UNSIGNED-NEXT: [[RESIZE3:%.*]] = zext i15 [[TMP21]] to i16 +// UNSIGNED-NEXT: store i16 [[RESIZE3]], i16* @susa, align 2 + susa--; + +// CHECK: [[TMP22:%.*]] = load i16, i16* @suf, align 2 +// SIGNED-NEXT: [[TMP23:%.*]] = call i16 @llvm.usub.sat.i16(i16 [[TMP22]], i16 -1) +// SIGNED-NEXT: store i16 [[TMP23]], i16* @suf, align 2 +// UNSIGNED-NEXT: [[RESIZE4:%.*]] = trunc i16 [[TMP22]] to i15 +// UNSIGNED-NEXT: [[TMP23:%.*]] = call i15 @llvm.usub.sat.i15(i15 [[RESIZE4]], i15 -1) +// UNSIGNED-NEXT: [[RESIZE5:%.*]] = zext i15 [[TMP23]] to i16 +// UNSIGNED-NEXT: store i16 [[RESIZE5]], i16* @suf, align 2 + suf--; +} + +// CHECK-LABEL: @Minus( +void Minus() { +// CHECK: [[TMP0:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sub i32 0, [[TMP0]] +// CHECK-NEXT: store i32 [[TMP1]], i32* @a, align 4 + a = -a; + +// CHECK: [[TMP2:%.*]] = load i16, i16* @f, align 2 +// CHECK-NEXT: [[TMP3:%.*]] = sub i16 0, [[TMP2]] +// CHECK-NEXT: store i16 [[TMP3]], i16* @f, align 2 + f = -f; + +// CHECK: [[TMP4:%.*]] = load i16, i16* @usa, align 2 +// CHECK-NEXT: [[TMP5:%.*]] = sub i16 0, [[TMP4]] +// CHECK-NEXT: store i16 [[TMP5]], i16* @usa, align 2 + usa = -usa; + +// CHECK: [[TMP6:%.*]] = load i16, i16* @uf, align 2 +// CHECK-NEXT: [[TMP7:%.*]] = sub i16 0, [[TMP6]] +// CHECK-NEXT: store i16 [[TMP7]], i16* @uf, align 2 + uf = -uf; + +// CHECK: [[TMP8:%.*]] = load i32, i32* @sa, align 4 +// CHECK-NEXT: [[TMP9:%.*]] = call i32 @llvm.ssub.sat.i32(i32 0, i32 [[TMP8]]) +// CHECK-NEXT: store i32 [[TMP9]], i32* @sa, align 4 + sa = -sa; + +// CHECK: [[TMP10:%.*]] = load i16, i16* @sf, align 2 +// CHECK-NEXT: [[TMP11:%.*]] = call i16 @llvm.ssub.sat.i16(i16 0, i16 [[TMP10]]) +// CHECK-NEXT: store i16 [[TMP11]], i16* @sf, align 2 + sf = -sf; + +// CHECK: [[TMP12:%.*]] = load i16, i16* @susa, align 2 +// SIGNED-NEXT: [[TMP13:%.*]] = call i16 @llvm.usub.sat.i16(i16 0, i16 [[TMP12]]) +// SIGNED-NEXT: store i16 [[TMP13]], i16* @susa, align 2 +// UNSIGNED-NEXT: [[RESIZE:%.*]] = trunc i16 [[TMP12]] to i15 +// UNSIGNED-NEXT: [[TMP13:%.*]] = call i15 @llvm.usub.sat.i15(i15 0, i15 [[RESIZE]]) +// UNSIGNED-NEXT: [[RESIZE1:%.*]] = zext i15 [[TMP13]] to i16 +// UNSIGNED-NEXT: store i16 [[RESIZE1]], i16* @susa, align 2 + susa = -susa; + +// CHECK: [[TMP14:%.*]] = load i16, i16* @suf, align 2 +// SIGNED-NEXT: [[TMP15:%.*]] = call i16 @llvm.usub.sat.i16(i16 0, i16 [[TMP14]]) +// SIGNED-NEXT: store i16 [[TMP15]], i16* @suf, align 2 +// UNSIGNED-NEXT: [[RESIZE2:%.*]] = trunc i16 [[TMP14]] to i15 +// UNSIGNED-NEXT: [[TMP15:%.*]] = call i15 @llvm.usub.sat.i15(i15 0, i15 [[RESIZE2]]) +// UNSIGNED-NEXT: [[RESIZE3:%.*]] = zext i15 [[TMP15]] to i16 +// UNSIGNED-NEXT: store i16 [[RESIZE3]], i16* @suf, align 2 + suf = -suf; +} + +// CHECK-LABEL: @Plus( +void Plus() { +// CHECK: [[TMP0:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: store i32 [[TMP0]], i32* @a, align 4 + a = +a; + +// CHECK: [[TMP1:%.*]] = load i16, i16* @uf, align 2 +// CHECK-NEXT: store i16 [[TMP1]], i16* @uf, align 2 + uf = +uf; + +// CHECK: [[TMP2:%.*]] = load i32, i32* @sa, align 4 +// CHECK-NEXT: store i32 [[TMP2]], i32* @sa, align 4 + sa = +sa; +} + +// CHECK-LABEL: @Not( +void Not() { + int i; + +// CHECK: [[TMP0:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i32 [[TMP0]], 0 +// CHECK-NEXT: [[LNOT:%.*]] = xor i1 [[TOBOOL]], true +// CHECK-NEXT: [[LNOT_EXT:%.*]] = zext i1 [[LNOT]] to i32 +// CHECK-NEXT: store i32 [[LNOT_EXT]], i32* %i, align 4 + i = !a; + +// CHECK: [[TMP1:%.*]] = load i16, i16* @uf, align 2 +// CHECK-NEXT: [[TOBOOL1:%.*]] = icmp ne i16 [[TMP1]], 0 +// CHECK-NEXT: [[LNOT2:%.*]] = xor i1 [[TOBOOL1]], true +// CHECK-NEXT: [[LNOT_EXT3:%.*]] = zext i1 [[LNOT2]] to i32 +// CHECK-NEXT: store i32 [[LNOT_EXT3]], i32* %i, align 4 + i = !uf; + +// CHECK: [[TMP2:%.*]] = load i16, i16* @susa, align 2 +// CHECK-NEXT: [[TOBOOL4:%.*]] = icmp ne i16 [[TMP2]], 0 +// CHECK-NEXT: [[LNOT5:%.*]] = xor i1 [[TOBOOL4]], true +// CHECK-NEXT: [[LNOT_EXT6:%.*]] = zext i1 [[LNOT5]] to i32 +// CHECK-NEXT: store i32 [[LNOT_EXT6]], i32* %i, align 4 + i = !susa; +}