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 @@ -1219,13 +1219,14 @@ // padding is enabled because overflow into this bit is undefined // behavior. return Builder.CreateIsNotNull(Src, "tobool"); - if (DstType->isFixedPointType() || DstType->isIntegerType()) + if (DstType->isFixedPointType() || DstType->isIntegerType() || + DstType->isRealFloatingType()) return EmitFixedPointConversion(Src, SrcType, DstType, Loc); llvm_unreachable( "Unhandled scalar conversion from a fixed point type to another type."); } else if (DstType->isFixedPointType()) { - if (SrcType->isIntegerType()) + if (SrcType->isIntegerType() || SrcType->isRealFloatingType()) // This also includes converting booleans and enums to fixed point types. return EmitFixedPointConversion(Src, SrcType, DstType, Loc); @@ -1441,19 +1442,29 @@ Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy, SourceLocation Loc) { - auto SrcFPSema = CGF.getContext().getFixedPointSemantics(SrcTy); - auto DstFPSema = CGF.getContext().getFixedPointSemantics(DstTy); llvm::FixedPointBuilder FPBuilder(Builder); llvm::Value *Result; - if (DstTy->isIntegerType()) - Result = FPBuilder.CreateFixedToInteger(Src, SrcFPSema, - DstFPSema.getWidth(), - DstFPSema.isSigned()); - else if (SrcTy->isIntegerType()) - Result = FPBuilder.CreateIntegerToFixed(Src, SrcFPSema.isSigned(), - DstFPSema); - else - Result = FPBuilder.CreateFixedToFixed(Src, SrcFPSema, DstFPSema); + if (SrcTy->isRealFloatingType()) + Result = FPBuilder.CreateFloatingToFixed(Src, + CGF.getContext().getFixedPointSemantics(DstTy)); + else if (DstTy->isRealFloatingType()) + Result = FPBuilder.CreateFixedToFloating(Src, + CGF.getContext().getFixedPointSemantics(SrcTy), + ConvertType(DstTy)); + else { + auto SrcFPSema = CGF.getContext().getFixedPointSemantics(SrcTy); + auto DstFPSema = CGF.getContext().getFixedPointSemantics(DstTy); + + if (DstTy->isIntegerType()) + Result = FPBuilder.CreateFixedToInteger(Src, SrcFPSema, + DstFPSema.getWidth(), + DstFPSema.isSigned()); + else if (SrcTy->isIntegerType()) + Result = FPBuilder.CreateIntegerToFixed(Src, SrcFPSema.isSigned(), + DstFPSema); + else + Result = FPBuilder.CreateFixedToFixed(Src, SrcFPSema, DstFPSema); + } return Result; } diff --git a/clang/test/Frontend/fixed_point_compound.c b/clang/test/Frontend/fixed_point_compound.c --- a/clang/test/Frontend/fixed_point_compound.c +++ b/clang/test/Frontend/fixed_point_compound.c @@ -16,6 +16,8 @@ unsigned int u; signed char c; +float fl; + // CHECK-LABEL: @add_shfa( // CHECK-NEXT: entry: @@ -358,6 +360,66 @@ sshf += suf; } +// CHECK-LABEL: @add_afl( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP2:%.*]] = sitofp i32 [[TMP1]] to float +// CHECK-NEXT: [[TMP3:%.*]] = fmul float [[TMP2]], 0x3F00000000000000 +// CHECK-NEXT: [[ADD:%.*]] = fadd float [[TMP3]], [[TMP0]] +// CHECK-NEXT: [[TMP4:%.*]] = fmul float [[ADD]], 3.276800e+04 +// CHECK-NEXT: [[TMP5:%.*]] = fptosi float [[TMP4]] to i32 +// CHECK-NEXT: store i32 [[TMP5]], i32* @a, align 4 +// CHECK-NEXT: ret void +// +void add_afl() { + a += fl; +} + +// CHECK-LABEL: @add_fla( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i32 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3F00000000000000 +// CHECK-NEXT: [[TMP3:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[ADD:%.*]] = fadd float [[TMP3]], [[TMP2]] +// CHECK-NEXT: store float [[ADD]], float* @fl, align 4 +// CHECK-NEXT: ret void +// +void add_fla() { + fl += a; +} + +// CHECK-LABEL: @add_safl( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @sa, align 4 +// CHECK-NEXT: [[TMP2:%.*]] = sitofp i32 [[TMP1]] to float +// CHECK-NEXT: [[TMP3:%.*]] = fmul float [[TMP2]], 0x3F00000000000000 +// CHECK-NEXT: [[ADD:%.*]] = fadd float [[TMP3]], [[TMP0]] +// CHECK-NEXT: [[TMP4:%.*]] = fmul float [[ADD]], 3.276800e+04 +// CHECK-NEXT: [[TMP5:%.*]] = call i32 @llvm.fptosi.sat.i32.f32(float [[TMP4]]) +// CHECK-NEXT: store i32 [[TMP5]], i32* @sa, align 4 +// CHECK-NEXT: ret void +// +void add_safl() { + sa += fl; +} + +// CHECK-LABEL: @add_flsa( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @sa, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i32 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3F00000000000000 +// CHECK-NEXT: [[TMP3:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[ADD:%.*]] = fadd float [[TMP3]], [[TMP2]] +// CHECK-NEXT: store float [[ADD]], float* @fl, align 4 +// CHECK-NEXT: ret void +// +void add_flsa() { + fl += sa; +} + // Subtraction, multiplication and division should work about the same, so // just make sure we can do them. @@ -429,6 +491,22 @@ c -= sa; } +// CHECK-LABEL: @sub_afl( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP2:%.*]] = sitofp i32 [[TMP1]] to float +// CHECK-NEXT: [[TMP3:%.*]] = fmul float [[TMP2]], 0x3F00000000000000 +// CHECK-NEXT: [[SUB:%.*]] = fsub float [[TMP3]], [[TMP0]] +// CHECK-NEXT: [[TMP4:%.*]] = fmul float [[SUB]], 3.276800e+04 +// CHECK-NEXT: [[TMP5:%.*]] = fptosi float [[TMP4]] to i32 +// CHECK-NEXT: store i32 [[TMP5]], i32* @a, align 4 +// CHECK-NEXT: ret void +// +void sub_afl() { + a -= fl; +} + // SIGNED-LABEL: @mul_auf( // SIGNED-NEXT: entry: @@ -498,6 +576,22 @@ c *= sa; } +// CHECK-LABEL: @mul_afl( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP2:%.*]] = sitofp i32 [[TMP1]] to float +// CHECK-NEXT: [[TMP3:%.*]] = fmul float [[TMP2]], 0x3F00000000000000 +// CHECK-NEXT: [[MUL:%.*]] = fmul float [[TMP3]], [[TMP0]] +// CHECK-NEXT: [[TMP4:%.*]] = fmul float [[MUL]], 3.276800e+04 +// CHECK-NEXT: [[TMP5:%.*]] = fptosi float [[TMP4]] to i32 +// CHECK-NEXT: store i32 [[TMP5]], i32* @a, align 4 +// CHECK-NEXT: ret void +// +void mul_afl() { + a *= fl; +} + // SIGNED-LABEL: @div_auf( // SIGNED-NEXT: entry: @@ -567,6 +661,22 @@ c /= sa; } +// CHECK-LABEL: @div_afl( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP2:%.*]] = sitofp i32 [[TMP1]] to float +// CHECK-NEXT: [[TMP3:%.*]] = fmul float [[TMP2]], 0x3F00000000000000 +// CHECK-NEXT: [[DIV:%.*]] = fdiv float [[TMP3]], [[TMP0]] +// CHECK-NEXT: [[TMP4:%.*]] = fmul float [[DIV]], 3.276800e+04 +// CHECK-NEXT: [[TMP5:%.*]] = fptosi float [[TMP4]] to i32 +// CHECK-NEXT: store i32 [[TMP5]], i32* @a, align 4 +// CHECK-NEXT: ret void +// +void div_afl() { + a /= fl; +} + // CHECK-LABEL: @shft_ai( // CHECK-NEXT: entry: diff --git a/clang/test/Frontend/fixed_point_conversions.c b/clang/test/Frontend/fixed_point_conversions.c --- a/clang/test/Frontend/fixed_point_conversions.c +++ b/clang/test/Frontend/fixed_point_conversions.c @@ -26,11 +26,15 @@ _Sat short _Fract sat_sf; _Sat _Fract sat_f; _Sat long _Fract sat_lf; +_Sat unsigned _Fract sat_uf; short s; int i; unsigned int ui; +float fl; +double d; + // CHECK-LABEL: @fix_same1( // CHECK-NEXT: entry: // CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @a, align 4 @@ -695,3 +699,298 @@ void int_sat4() { sat_usa = ui; } + + +// CHECK-LABEL: @float_fix1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 1.280000e+02 +// CHECK-NEXT: [[TMP2:%.*]] = fptosi float [[TMP1]] to i16 +// CHECK-NEXT: store i16 [[TMP2]], i16* @sa, align 2 +// CHECK-NEXT: ret void +// +void float_fix1() { + sa = fl; +} + +// CHECK-LABEL: @float_fix2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 3.276800e+04 +// CHECK-NEXT: [[TMP2:%.*]] = fptosi float [[TMP1]] to i32 +// CHECK-NEXT: store i32 [[TMP2]], i32* @a, align 4 +// CHECK-NEXT: ret void +// +void float_fix2() { + a = fl; +} + +// CHECK-LABEL: @float_fix3( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 0x41E0000000000000 +// CHECK-NEXT: [[TMP2:%.*]] = fptosi float [[TMP1]] to i64 +// CHECK-NEXT: store i64 [[TMP2]], i64* @la, align 8 +// CHECK-NEXT: ret void +// +void float_fix3() { + la = fl; +} + +// CHECK-LABEL: @float_fix4( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 1.280000e+02 +// CHECK-NEXT: [[TMP2:%.*]] = fptosi float [[TMP1]] to i8 +// CHECK-NEXT: store i8 [[TMP2]], i8* @sf, align 1 +// CHECK-NEXT: ret void +// +void float_fix4() { + sf = fl; +} + +// CHECK-LABEL: @float_fix5( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 0x41E0000000000000 +// CHECK-NEXT: [[TMP2:%.*]] = fptosi float [[TMP1]] to i32 +// CHECK-NEXT: store i32 [[TMP2]], i32* @lf, align 4 +// CHECK-NEXT: ret void +// +void float_fix5() { + lf = fl; +} + +// SIGNED-LABEL: @float_fix6( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// SIGNED-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 6.553600e+04 +// SIGNED-NEXT: [[TMP2:%.*]] = fptoui float [[TMP1]] to i32 +// SIGNED-NEXT: store i32 [[TMP2]], i32* @ua, align 4 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @float_fix6( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// UNSIGNED-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 3.276800e+04 +// UNSIGNED-NEXT: [[TMP2:%.*]] = fptosi float [[TMP1]] to i32 +// UNSIGNED-NEXT: store i32 [[TMP2]], i32* @ua, align 4 +// UNSIGNED-NEXT: ret void +// +void float_fix6() { + ua = fl; +} + +// SIGNED-LABEL: @float_fix7( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// SIGNED-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 6.553600e+04 +// SIGNED-NEXT: [[TMP2:%.*]] = fptoui float [[TMP1]] to i16 +// SIGNED-NEXT: store i16 [[TMP2]], i16* @uf, align 2 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @float_fix7( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// UNSIGNED-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 3.276800e+04 +// UNSIGNED-NEXT: [[TMP2:%.*]] = fptosi float [[TMP1]] to i16 +// UNSIGNED-NEXT: store i16 [[TMP2]], i16* @uf, align 2 +// UNSIGNED-NEXT: ret void +// +void float_fix7() { + uf = fl; +} + + +// CHECK-LABEL: @fix_float1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i16, i16* @sa, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i16 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 7.812500e-03 +// CHECK-NEXT: store float [[TMP2]], float* @fl, align 4 +// CHECK-NEXT: ret void +// +void fix_float1() { + fl = sa; +} + +// CHECK-LABEL: @fix_float2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @a, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i32 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3F00000000000000 +// CHECK-NEXT: store float [[TMP2]], float* @fl, align 4 +// CHECK-NEXT: ret void +// +void fix_float2() { + fl = a; +} + +// CHECK-LABEL: @fix_float3( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i64, i64* @la, align 8 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i64 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3E00000000000000 +// CHECK-NEXT: store float [[TMP2]], float* @fl, align 4 +// CHECK-NEXT: ret void +// +void fix_float3() { + fl = la; +} + +// CHECK-LABEL: @fix_float4( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* @sf, align 1 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i8 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 7.812500e-03 +// CHECK-NEXT: store float [[TMP2]], float* @fl, align 4 +// CHECK-NEXT: ret void +// +void fix_float4() { + fl = sf; +} + +// CHECK-LABEL: @fix_float5( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @lf, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i32 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3E00000000000000 +// CHECK-NEXT: store float [[TMP2]], float* @fl, align 4 +// CHECK-NEXT: ret void +// +void fix_float5() { + fl = lf; +} + +// SIGNED-LABEL: @fix_float6( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load i32, i32* @ua, align 4 +// SIGNED-NEXT: [[TMP1:%.*]] = uitofp i32 [[TMP0]] to float +// SIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3EF0000000000000 +// SIGNED-NEXT: store float [[TMP2]], float* @fl, align 4 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @fix_float6( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load i32, i32* @ua, align 4 +// UNSIGNED-NEXT: [[TMP1:%.*]] = uitofp i32 [[TMP0]] to float +// UNSIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3F00000000000000 +// UNSIGNED-NEXT: store float [[TMP2]], float* @fl, align 4 +// UNSIGNED-NEXT: ret void +// +void fix_float6() { + fl = ua; +} + +// SIGNED-LABEL: @fix_float7( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @uf, align 2 +// SIGNED-NEXT: [[TMP1:%.*]] = uitofp i16 [[TMP0]] to float +// SIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3EF0000000000000 +// SIGNED-NEXT: store float [[TMP2]], float* @fl, align 4 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @fix_float7( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @uf, align 2 +// UNSIGNED-NEXT: [[TMP1:%.*]] = uitofp i16 [[TMP0]] to float +// UNSIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3F00000000000000 +// UNSIGNED-NEXT: store float [[TMP2]], float* @fl, align 4 +// UNSIGNED-NEXT: ret void +// +void fix_float7() { + fl = uf; +} + + +// CHECK-LABEL: @float_sat1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 1.280000e+02 +// CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.fptosi.sat.i16.f32(float [[TMP1]]) +// CHECK-NEXT: store i16 [[TMP2]], i16* @sat_sa, align 2 +// CHECK-NEXT: ret void +// +void float_sat1() { + sat_sa = fl; +} + +// CHECK-LABEL: @float_sat2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 3.276800e+04 +// CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.fptosi.sat.i32.f32(float [[TMP1]]) +// CHECK-NEXT: store i32 [[TMP2]], i32* @sat_a, align 4 +// CHECK-NEXT: ret void +// +void float_sat2() { + sat_a = fl; +} + +// CHECK-LABEL: @float_sat3( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 0x41E0000000000000 +// CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.fptosi.sat.i64.f32(float [[TMP1]]) +// CHECK-NEXT: store i64 [[TMP2]], i64* @sat_la, align 8 +// CHECK-NEXT: ret void +// +void float_sat3() { + sat_la = fl; +} + +// CHECK-LABEL: @float_sat4( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 1.280000e+02 +// CHECK-NEXT: [[TMP2:%.*]] = call i8 @llvm.fptosi.sat.i8.f32(float [[TMP1]]) +// CHECK-NEXT: store i8 [[TMP2]], i8* @sat_sf, align 1 +// CHECK-NEXT: ret void +// +void float_sat4() { + sat_sf = fl; +} + +// SIGNED-LABEL: @float_sat5( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// SIGNED-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 6.553600e+04 +// SIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.fptoui.sat.i32.f32(float [[TMP1]]) +// SIGNED-NEXT: store i32 [[TMP2]], i32* @sat_ua, align 4 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @float_sat5( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// UNSIGNED-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 3.276800e+04 +// UNSIGNED-NEXT: [[TMP2:%.*]] = call i32 @llvm.fptosi.sat.i32.f32(float [[TMP1]]) +// UNSIGNED-NEXT: [[TMP3:%.*]] = icmp slt i32 [[TMP2]], 0 +// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP3]], i32 0, i32 [[TMP2]] +// UNSIGNED-NEXT: store i32 [[SATMIN]], i32* @sat_ua, align 4 +// UNSIGNED-NEXT: ret void +// +void float_sat5() { + sat_ua = fl; +} + +// SIGNED-LABEL: @float_sat6( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// SIGNED-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 6.553600e+04 +// SIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.fptoui.sat.i16.f32(float [[TMP1]]) +// SIGNED-NEXT: store i16 [[TMP2]], i16* @sat_uf, align 2 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @float_sat6( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load float, float* @fl, align 4 +// UNSIGNED-NEXT: [[TMP1:%.*]] = fmul float [[TMP0]], 3.276800e+04 +// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.fptosi.sat.i16.f32(float [[TMP1]]) +// UNSIGNED-NEXT: [[TMP3:%.*]] = icmp slt i16 [[TMP2]], 0 +// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP3]], i16 0, i16 [[TMP2]] +// UNSIGNED-NEXT: store i16 [[SATMIN]], i16* @sat_uf, align 2 +// UNSIGNED-NEXT: ret void +// +void float_sat6() { + sat_uf = fl; +} diff --git a/clang/test/Frontend/fixed_point_conversions_half.c b/clang/test/Frontend/fixed_point_conversions_half.c new file mode 100644 --- /dev/null +++ b/clang/test/Frontend/fixed_point_conversions_half.c @@ -0,0 +1,309 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -ffixed-point -triple arm64-unknown-linux-gnu -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED +// RUN: %clang_cc1 -ffixed-point -triple arm64-unknown-linux-gnu -S -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s --check-prefixes=CHECK,UNSIGNED + +short _Fract sf; +long _Fract lf; + +short _Accum sa; +long _Accum la; + +unsigned short _Accum usa; +unsigned long _Accum ula; + +_Sat short _Fract sf_sat; +_Sat long _Fract lf_sat; + +_Sat short _Accum sa_sat; +_Sat long _Accum la_sat; + +_Sat unsigned short _Accum usa_sat; +_Sat unsigned long _Accum ula_sat; + +_Float16 h; + + +// CHECK-LABEL: @half_fix1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = fmul half [[TMP0]], 0xH5800 +// CHECK-NEXT: [[TMP2:%.*]] = fptosi half [[TMP1]] to i8 +// CHECK-NEXT: store i8 [[TMP2]], i8* @sf, align 1 +// CHECK-NEXT: ret void +// +void half_fix1() { + sf = h; +} + +// CHECK-LABEL: @half_fix2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x41E0000000000000 +// CHECK-NEXT: [[TMP3:%.*]] = fptosi float [[TMP2]] to i32 +// CHECK-NEXT: store i32 [[TMP3]], i32* @lf, align 4 +// CHECK-NEXT: ret void +// +void half_fix2() { + lf = h; +} + +// CHECK-LABEL: @half_fix3( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = fmul half [[TMP0]], 0xH5800 +// CHECK-NEXT: [[TMP2:%.*]] = fptosi half [[TMP1]] to i16 +// CHECK-NEXT: store i16 [[TMP2]], i16* @sa, align 2 +// CHECK-NEXT: ret void +// +void half_fix3() { + sa = h; +} + +// CHECK-LABEL: @half_fix4( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x41E0000000000000 +// CHECK-NEXT: [[TMP3:%.*]] = fptosi float [[TMP2]] to i64 +// CHECK-NEXT: store i64 [[TMP3]], i64* @la, align 8 +// CHECK-NEXT: ret void +// +void half_fix4() { + la = h; +} + +// SIGNED-LABEL: @half_fix5( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// SIGNED-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// SIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 2.560000e+02 +// SIGNED-NEXT: [[TMP3:%.*]] = fptoui float [[TMP2]] to i16 +// SIGNED-NEXT: store i16 [[TMP3]], i16* @usa, align 2 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @half_fix5( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// UNSIGNED-NEXT: [[TMP1:%.*]] = fmul half [[TMP0]], 0xH5800 +// UNSIGNED-NEXT: [[TMP2:%.*]] = fptosi half [[TMP1]] to i16 +// UNSIGNED-NEXT: store i16 [[TMP2]], i16* @usa, align 2 +// UNSIGNED-NEXT: ret void +// +void half_fix5() { + usa = h; +} + +// SIGNED-LABEL: @half_fix6( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// SIGNED-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// SIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x41F0000000000000 +// SIGNED-NEXT: [[TMP3:%.*]] = fptoui float [[TMP2]] to i64 +// SIGNED-NEXT: store i64 [[TMP3]], i64* @ula, align 8 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @half_fix6( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// UNSIGNED-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// UNSIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x41E0000000000000 +// UNSIGNED-NEXT: [[TMP3:%.*]] = fptosi float [[TMP2]] to i64 +// UNSIGNED-NEXT: store i64 [[TMP3]], i64* @ula, align 8 +// UNSIGNED-NEXT: ret void +// +void half_fix6() { + ula = h; +} + + +// CHECK-LABEL: @half_sat1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = fmul half [[TMP0]], 0xH5800 +// CHECK-NEXT: [[TMP2:%.*]] = call i8 @llvm.fptosi.sat.i8.f16(half [[TMP1]]) +// CHECK-NEXT: store i8 [[TMP2]], i8* @sf_sat, align 1 +// CHECK-NEXT: ret void +// +void half_sat1() { + sf_sat = h; +} + +// CHECK-LABEL: @half_sat2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x41E0000000000000 +// CHECK-NEXT: [[TMP3:%.*]] = call i32 @llvm.fptosi.sat.i32.f32(float [[TMP2]]) +// CHECK-NEXT: store i32 [[TMP3]], i32* @lf_sat, align 4 +// CHECK-NEXT: ret void +// +void half_sat2() { + lf_sat = h; +} + +// CHECK-LABEL: @half_sat3( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = fmul half [[TMP0]], 0xH5800 +// CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.fptosi.sat.i16.f16(half [[TMP1]]) +// CHECK-NEXT: store i16 [[TMP2]], i16* @sa_sat, align 2 +// CHECK-NEXT: ret void +// +void half_sat3() { + sa_sat = h; +} + +// CHECK-LABEL: @half_sat4( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x41E0000000000000 +// CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.fptosi.sat.i64.f32(float [[TMP2]]) +// CHECK-NEXT: store i64 [[TMP3]], i64* @la_sat, align 8 +// CHECK-NEXT: ret void +// +void half_sat4() { + la_sat = h; +} + +// SIGNED-LABEL: @half_sat5( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// SIGNED-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// SIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 2.560000e+02 +// SIGNED-NEXT: [[TMP3:%.*]] = call i16 @llvm.fptoui.sat.i16.f32(float [[TMP2]]) +// SIGNED-NEXT: store i16 [[TMP3]], i16* @usa_sat, align 2 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @half_sat5( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// UNSIGNED-NEXT: [[TMP1:%.*]] = fmul half [[TMP0]], 0xH5800 +// UNSIGNED-NEXT: [[TMP2:%.*]] = call i16 @llvm.fptosi.sat.i16.f16(half [[TMP1]]) +// UNSIGNED-NEXT: [[TMP3:%.*]] = icmp slt i16 [[TMP2]], 0 +// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP3]], i16 0, i16 [[TMP2]] +// UNSIGNED-NEXT: store i16 [[SATMIN]], i16* @usa_sat, align 2 +// UNSIGNED-NEXT: ret void +// +void half_sat5() { + usa_sat = h; +} + +// SIGNED-LABEL: @half_sat6( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// SIGNED-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// SIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x41F0000000000000 +// SIGNED-NEXT: [[TMP3:%.*]] = call i64 @llvm.fptoui.sat.i64.f32(float [[TMP2]]) +// SIGNED-NEXT: store i64 [[TMP3]], i64* @ula_sat, align 8 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @half_sat6( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load half, half* @h, align 2 +// UNSIGNED-NEXT: [[TMP1:%.*]] = fpext half [[TMP0]] to float +// UNSIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x41E0000000000000 +// UNSIGNED-NEXT: [[TMP3:%.*]] = call i64 @llvm.fptosi.sat.i64.f32(float [[TMP2]]) +// UNSIGNED-NEXT: [[TMP4:%.*]] = icmp slt i64 [[TMP3]], 0 +// UNSIGNED-NEXT: [[SATMIN:%.*]] = select i1 [[TMP4]], i64 0, i64 [[TMP3]] +// UNSIGNED-NEXT: store i64 [[SATMIN]], i64* @ula_sat, align 8 +// UNSIGNED-NEXT: ret void +// +void half_sat6() { + ula_sat = h; +} + + +// CHECK-LABEL: @fix_half1( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i8, i8* @sf, align 1 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i8 [[TMP0]] to half +// CHECK-NEXT: [[TMP2:%.*]] = fmul half [[TMP1]], 0xH2000 +// CHECK-NEXT: store half [[TMP2]], half* @h, align 2 +// CHECK-NEXT: ret void +// +void fix_half1() { + h = sf; +} + +// CHECK-LABEL: @fix_half2( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* @lf, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i32 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3E00000000000000 +// CHECK-NEXT: [[TMP3:%.*]] = fptrunc float [[TMP2]] to half +// CHECK-NEXT: store half [[TMP3]], half* @h, align 2 +// CHECK-NEXT: ret void +// +void fix_half2() { + h = lf; +} + +// CHECK-LABEL: @fix_half3( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i16, i16* @sa, align 2 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i16 [[TMP0]] to half +// CHECK-NEXT: [[TMP2:%.*]] = fmul half [[TMP1]], 0xH2000 +// CHECK-NEXT: store half [[TMP2]], half* @h, align 2 +// CHECK-NEXT: ret void +// +void fix_half3() { + h = sa; +} + +// CHECK-LABEL: @fix_half4( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = load i64, i64* @la, align 8 +// CHECK-NEXT: [[TMP1:%.*]] = sitofp i64 [[TMP0]] to float +// CHECK-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3E00000000000000 +// CHECK-NEXT: [[TMP3:%.*]] = fptrunc float [[TMP2]] to half +// CHECK-NEXT: store half [[TMP3]], half* @h, align 2 +// CHECK-NEXT: ret void +// +void fix_half4() { + h = la; +} + +// SIGNED-LABEL: @fix_half5( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2 +// SIGNED-NEXT: [[TMP1:%.*]] = uitofp i16 [[TMP0]] to float +// SIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 3.906250e-03 +// SIGNED-NEXT: [[TMP3:%.*]] = fptrunc float [[TMP2]] to half +// SIGNED-NEXT: store half [[TMP3]], half* @h, align 2 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @fix_half5( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load i16, i16* @usa, align 2 +// UNSIGNED-NEXT: [[TMP1:%.*]] = uitofp i16 [[TMP0]] to half +// UNSIGNED-NEXT: [[TMP2:%.*]] = fmul half [[TMP1]], 0xH2000 +// UNSIGNED-NEXT: store half [[TMP2]], half* @h, align 2 +// UNSIGNED-NEXT: ret void +// +void fix_half5() { + h = usa; +} + +// SIGNED-LABEL: @fix_half6( +// SIGNED-NEXT: entry: +// SIGNED-NEXT: [[TMP0:%.*]] = load i64, i64* @ula, align 8 +// SIGNED-NEXT: [[TMP1:%.*]] = uitofp i64 [[TMP0]] to float +// SIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3DF0000000000000 +// SIGNED-NEXT: [[TMP3:%.*]] = fptrunc float [[TMP2]] to half +// SIGNED-NEXT: store half [[TMP3]], half* @h, align 2 +// SIGNED-NEXT: ret void +// +// UNSIGNED-LABEL: @fix_half6( +// UNSIGNED-NEXT: entry: +// UNSIGNED-NEXT: [[TMP0:%.*]] = load i64, i64* @ula, align 8 +// UNSIGNED-NEXT: [[TMP1:%.*]] = uitofp i64 [[TMP0]] to float +// UNSIGNED-NEXT: [[TMP2:%.*]] = fmul float [[TMP1]], 0x3E00000000000000 +// UNSIGNED-NEXT: [[TMP3:%.*]] = fptrunc float [[TMP2]] to half +// UNSIGNED-NEXT: store half [[TMP3]], half* @h, align 2 +// UNSIGNED-NEXT: ret void +// +void fix_half6() { + h = ula; +} diff --git a/llvm/include/llvm/IR/FixedPointBuilder.h b/llvm/include/llvm/IR/FixedPointBuilder.h --- a/llvm/include/llvm/IR/FixedPointBuilder.h +++ b/llvm/include/llvm/IR/FixedPointBuilder.h @@ -120,6 +120,16 @@ C.isSigned(), C.isSaturated(), BothPadded); } + /// Given a floating point type and a fixed-point semantic, return a floating + /// point type which can accommodate the fixed-point semantic. This is either + /// \p Ty, or a floating point type with a larger exponent than Ty. + Type *getAccommodatingFloatType(Type *Ty, const FixedPointSemantics &Sema) { + const fltSemantics *FloatSema = &Ty->getFltSemantics(); + while (!Sema.fitsInFloatSemantics(*FloatSema)) + FloatSema = APFixedPoint::promoteFloatSemantics(FloatSema); + return Type::getFloatingPointTy(Ty->getContext(), *FloatSema); + } + public: FixedPointBuilder(IRBuilderTy &Builder) : B(Builder) {} @@ -159,6 +169,55 @@ DstSema, false); } + Value *CreateFixedToFloating(Value *Src, const FixedPointSemantics &SrcSema, + Type *DstTy) { + Value *Result; + Type *OpTy = getAccommodatingFloatType(DstTy, SrcSema); + // Convert the raw fixed-point value directly to floating point. If the + // value is too large to fit, it will be rounded, not truncated. + Result = SrcSema.isSigned() ? B.CreateSIToFP(Src, OpTy) + : B.CreateUIToFP(Src, OpTy); + // Rescale the integral-in-floating point by the scaling factor. This is + // lossless, except for overflow to infinity which is unlikely. + Result = B.CreateFMul(Result, + ConstantFP::get(OpTy, std::pow(2, -(int)SrcSema.getScale()))); + if (OpTy != DstTy) + Result = B.CreateFPTrunc(Result, DstTy); + return Result; + } + + Value *CreateFloatingToFixed(Value *Src, const FixedPointSemantics &DstSema) { + bool UseSigned = DstSema.isSigned() || DstSema.hasUnsignedPadding(); + Value *Result = Src; + Type *OpTy = getAccommodatingFloatType(Src->getType(), DstSema); + if (OpTy != Src->getType()) + Result = B.CreateFPExt(Result, OpTy); + // Rescale the floating point value so that its significant bits (for the + // purposes of the conversion) are in the integral range. + Result = B.CreateFMul(Result, + ConstantFP::get(OpTy, std::pow(2, DstSema.getScale()))); + + Type *ResultTy = B.getIntNTy(DstSema.getWidth()); + if (DstSema.isSaturated()) { + Intrinsic::ID IID = + UseSigned ? Intrinsic::fptosi_sat : Intrinsic::fptoui_sat; + Result = B.CreateIntrinsic(IID, {ResultTy, OpTy}, {Result}); + } else { + Result = UseSigned ? B.CreateFPToSI(Result, ResultTy) + : B.CreateFPToUI(Result, ResultTy); + } + + // When saturating unsigned-with-padding using signed operations, we may + // get negative values. Emit an extra clamp to zero. + if (DstSema.isSaturated() && DstSema.hasUnsignedPadding()) { + Constant *Zero = Constant::getNullValue(Result->getType()); + Result = + B.CreateSelect(B.CreateICmpSLT(Result, Zero), Zero, Result, "satmin"); + } + + return Result; + } + /// Add two fixed-point values and return the result in their common semantic. /// \p LHS - The left hand side /// \p LHSSema - The semantic of the left hand side