[llvm-branch-commits] [clang] c4944a6 - [Fixed Point] Add codegen for conversion between fixed-point and floating point.

Bjorn Pettersson via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Jan 12 04:57:26 PST 2021


Author: Bevin Hansson
Date: 2021-01-12T13:53:01+01:00
New Revision: c4944a6f53f6d1876e76563599f5f149328e7f8f

URL: https://github.com/llvm/llvm-project/commit/c4944a6f53f6d1876e76563599f5f149328e7f8f
DIFF: https://github.com/llvm/llvm-project/commit/c4944a6f53f6d1876e76563599f5f149328e7f8f.diff

LOG: [Fixed Point] Add codegen for conversion between fixed-point and floating point.

The patch adds the required methods to FixedPointBuilder
for converting between fixed-point and floating point,
and uses them from Clang.

This depends on D54749.

Reviewed By: leonardchan

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

Added: 
    clang/test/Frontend/fixed_point_conversions_half.c

Modified: 
    clang/lib/CodeGen/CGExprScalar.cpp
    clang/test/Frontend/fixed_point_compound.c
    clang/test/Frontend/fixed_point_conversions.c
    llvm/include/llvm/IR/FixedPointBuilder.h

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index d6d5ec544c08..6f7e8263fa10 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -1212,13 +1212,14 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
       // 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);
 
@@ -1434,19 +1435,29 @@ Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
 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<CGBuilderTy> 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
index 897ba2e22636..5dcc7fba0da7 100644
--- a/clang/test/Frontend/fixed_point_compound.c
+++ b/clang/test/Frontend/fixed_point_compound.c
@@ -16,6 +16,8 @@ int i;
 unsigned int u;
 signed char c;
 
+float fl;
+
 
 // CHECK-LABEL: @add_shfa(
 // CHECK-NEXT:  entry:
@@ -358,6 +360,66 @@ void add_sshsuf() {
   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 @@ void sub_csa() {
   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 @@ void mul_csa() {
   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 @@ void div_csa() {
   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
index dfe727c708f4..96bc352d94dc 100644
--- a/clang/test/Frontend/fixed_point_conversions.c
+++ b/clang/test/Frontend/fixed_point_conversions.c
@@ -26,11 +26,15 @@ _Sat unsigned long _Accum sat_ula;
 _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_sat3() {
 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
index 000000000000..18261edf4474
--- /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
index dcccdb7add33..a99c761ad3e9 100644
--- a/llvm/include/llvm/IR/FixedPointBuilder.h
+++ b/llvm/include/llvm/IR/FixedPointBuilder.h
@@ -120,6 +120,16 @@ template <class IRBuilderTy> class FixedPointBuilder {
         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 @@ template <class IRBuilderTy> class FixedPointBuilder {
                    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


        


More information about the llvm-branch-commits mailing list