leonardchan created this revision.
leonardchan added reviewers: ebevhan, phosek, mcgrathr, jakehehrlich.
leonardchan added a project: clang.

This patch is a part of https://reviews.llvm.org/D48456 in an attempt to split 
them up. This contains the code for casting between fixed point types and other 
fixed point types.

The method for converting between fixed point types is based off the convert() 
method in APFixedPoint.


Repository:
  rC Clang

https://reviews.llvm.org/D50616

Files:
  include/clang/AST/OperationKinds.def
  include/clang/AST/Type.h
  lib/AST/Expr.cpp
  lib/AST/ExprConstant.cpp
  lib/AST/Type.cpp
  lib/CodeGen/CGExpr.cpp
  lib/CodeGen/CGExprAgg.cpp
  lib/CodeGen/CGExprComplex.cpp
  lib/CodeGen/CGExprConstant.cpp
  lib/CodeGen/CGExprScalar.cpp
  lib/Edit/RewriteObjCFoundationAPI.cpp
  lib/Sema/Sema.cpp
  lib/Sema/SemaExpr.cpp
  lib/StaticAnalyzer/Core/ExprEngineC.cpp
  test/Frontend/fixed_point_conversions.c

Index: test/Frontend/fixed_point_conversions.c
===================================================================
--- /dev/null
+++ test/Frontend/fixed_point_conversions.c
@@ -0,0 +1,326 @@
+// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - | FileCheck %s -check-prefix=DEFAULT
+// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s -check-prefix=SAME
+
+void TestFixedPointCastSameType() {
+  _Accum a = 2.5k;
+  _Accum a2 = a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a2, align 4
+
+  a2 = (_Accum)a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a2, align 4
+}
+
+void TestFixedPointCastDown() {
+  long _Accum la = 2.5lk;
+  _Accum a = la;
+  // DEFAULT:      [[LACCUM:%[0-9]+]] = load i64, i64* %la, align 8
+  // DEFAULT-NEXT: [[ACCUM_AS_I64:%[0-9]+]] = ashr i64 [[LACCUM]], 16
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i64 [[ACCUM_AS_I64]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+  a = (_Accum)la;
+  // DEFAULT:      [[LACCUM:%[0-9]+]] = load i64, i64* %la, align 8
+  // DEFAULT-NEXT: [[ACCUM_AS_I64:%[0-9]+]] = ashr i64 [[LACCUM]], 16
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i64 [[ACCUM_AS_I64]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+  short _Accum sa = a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[SACCUM_AS_I32:%[0-9]+]] = ashr i32 [[ACCUM]], 8
+  // DEFAULT-NEXT: [[SACCUM:%[0-9]+]] = trunc i32 [[SACCUM_AS_I32]] to i16
+  // DEFAULT-NEXT: store i16 [[SACCUM]], i16* %sa, align 2
+
+  sa = (short _Accum)a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[SACCUM_AS_I32:%[0-9]+]] = ashr i32 [[ACCUM]], 8
+  // DEFAULT-NEXT: [[SACCUM:%[0-9]+]] = trunc i32 [[SACCUM_AS_I32]] to i16
+  // DEFAULT-NEXT: store i16 [[SACCUM]], i16* %sa, align 2
+}
+
+void TestFixedPointCastUp() {
+  short _Accum sa = 2.5hk;
+  _Accum a = sa;
+  // DEFAULT:      [[SACCUM:%[0-9]+]] = load i16, i16* %sa, align 2
+  // DEFAULT-NEXT: [[SACCUM_BUFF:%[0-9]+]] = sext i16 [[SACCUM]] to i24
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i24 [[SACCUM_BUFF]], 8
+  // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i24 [[ACCUM]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM_EXT]], i32* %a, align 4
+
+  long _Accum la = a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[ACCUM_BUFF:%[0-9]+]] = sext i32 [[ACCUM]] to i48
+  // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i48 [[ACCUM_BUFF]], 16
+  // DEFAULT-NEXT: [[LACCUM_EXT:%[0-9]+]] = sext i48 [[LACCUM]] to i64
+  // DEFAULT-NEXT: store i64 [[LACCUM_EXT]], i64* %la, align 8
+
+  a = (_Accum)sa;
+  // DEFAULT:      [[SACCUM:%[0-9]+]] = load i16, i16* %sa, align 2
+  // DEFAULT-NEXT: [[SACCUM_BUFF:%[0-9]+]] = sext i16 [[SACCUM]] to i24
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i24 [[SACCUM_BUFF]], 8
+  // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i24 [[ACCUM]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM_EXT]], i32* %a, align 4
+
+  la = (long _Accum)a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[ACCUM_BUFF:%[0-9]+]] = sext i32 [[ACCUM]] to i48
+  // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i48 [[ACCUM_BUFF]], 16
+  // DEFAULT-NEXT: [[LACCUM_EXT:%[0-9]+]] = sext i48 [[LACCUM]] to i64
+  // DEFAULT-NEXT: store i64 [[LACCUM_EXT]], i64* %la, align 8
+}
+
+void TestFixedPointCastSignedness() {
+  _Accum a = 2.5k;
+  unsigned _Accum ua = a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i33
+  // DEFAULT-NEXT: [[UACCUM:%[0-9]+]] = shl i33 [[ACCUM_EXT]], 1
+  // DEFAULT-NEXT: [[ACCUM_TRUNC:%[0-9]+]] = trunc i33 [[UACCUM]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM_TRUNC]], i32* %ua, align 4
+  // SAME:      TestFixedPointCastSignedness
+  // SAME:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // SAME-NEXT: store i32 [[ACCUM]], i32* %ua, align 4
+
+  a = ua;
+  // DEFAULT:      [[UACCUM:%[0-9]+]] = load i32, i32* %ua, align 4
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = lshr i32 [[UACCUM]], 1
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+  // SAME:      [[ACCUM:%[0-9]+]] = load i32, i32* %ua, align 4
+  // SAME-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+  ua = (unsigned _Accum)a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i33
+  // DEFAULT-NEXT: [[UACCUM:%[0-9]+]] = shl i33 [[ACCUM_EXT]], 1
+  // DEFAULT-NEXT: [[ACCUM_TRUNC:%[0-9]+]] = trunc i33 [[UACCUM]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM_TRUNC]], i32* %ua, align 4
+
+  a = (_Accum)ua;
+  // DEFAULT:      [[UACCUM:%[0-9]+]] = load i32, i32* %ua, align 4
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = lshr i32 [[UACCUM]], 1
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+  _Accum a2;
+  unsigned long _Accum ula = a2;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a2, align 4
+  // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i49
+  // DEFAULT-NEXT: [[LACCUM:%[0-9]+]] = shl i49 [[ACCUM_EXT]], 17
+  // DEFAULT-NEXT: [[LACCUM_EXT:%[0-9]+]] = sext i49 [[LACCUM]] to i64
+  // DEFAULT-NEXT: store i64 [[LACCUM_EXT]], i64* %ula, align 8
+  // SAME:      [[ACCUM:%[0-9]+]] = load i32, i32* %a2, align 4
+  // SAME-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[ACCUM]] to i48
+  // SAME-NEXT: [[LACCUM:%[0-9]+]] = shl i48 [[ACCUM_EXT]], 16
+  // SAME-NEXT: [[LACCUM_EXT:%[0-9]+]] = sext i48 [[LACCUM]] to i64
+  // SAME-NEXT: store i64 [[LACCUM_EXT]], i64* %ula, align 8
+}
+
+void TestFixedPointCastSaturation() {
+  _Accum a;
+  _Sat short _Accum sat_sa;
+  _Sat _Accum sat_a;
+  _Sat long _Accum sat_la;
+  _Sat unsigned short _Accum sat_usa;
+  _Sat unsigned _Accum sat_ua;
+  _Sat unsigned long _Accum sat_ula;
+  _Sat short _Fract sat_sf;
+  _Sat _Fract sat_f;
+  _Sat long _Fract sat_lf;
+
+  // Casting down between types
+  sat_sa = sat_a;
+  // DEFAULT:      [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8
+  // DEFAULT-NEXT: [[MASK:%[0-9]+]] = and i32 [[ACCUM]], -32768
+  // DEFAULT-NEXT: [[MASK_CHANGED:%[0-9]+]] = icmp ne i32 -32768, [[MASK]]
+  // DEFAULT-NEXT: [[MASK_IS_NOT_ZERO:%[0-9]+]] = icmp ne i32 [[MASK]], 0
+  // DEFAULT-NEXT: [[DO_SAT:%[0-9]+]] = and i1 [[MASK_CHANGED]], [[MASK_IS_NOT_ZERO]]
+  // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i32 [[ACCUM]], 0
+  // DEFAULT-NEXT: [[SAT_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 -32768, i32 32767
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[DO_SAT]], i32 [[SAT_RESULT]], i32 [[ACCUM]]
+  // DEFAULT-NEXT: [[TRUNC_RESULT:%[0-9]+]] = trunc i32 [[RESULT]] to i16
+  // DEFAULT-NEXT: store i16 [[TRUNC_RESULT]], i16* %sat_sa, align 2
+
+  // Accum to Fract, decreasing scale
+  sat_sf = sat_a;
+  // DEFAULT:      [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+  // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8
+  // DEFAULT-NEXT: [[MASK:%[0-9]+]] = and i32 [[FRACT]], -128
+  // DEFAULT-NEXT: [[MASK_CHANGED:%[0-9]+]] = icmp ne i32 -128, [[MASK]]
+  // DEFAULT-NEXT: [[MASK_IS_NOT_ZERO:%[0-9]+]] = icmp ne i32 [[MASK]], 0
+  // DEFAULT-NEXT: [[DO_SAT:%[0-9]+]] = and i1 [[MASK_CHANGED]], [[MASK_IS_NOT_ZERO]]
+  // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i32 [[FRACT]], 0
+  // DEFAULT-NEXT: [[SAT_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 -128, i32 127
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[DO_SAT]], i32 [[SAT_RESULT]], i32 [[FRACT]]
+  // DEFAULT-NEXT: [[TRUNC_RESULT:%[0-9]+]] = trunc i32 [[RESULT]] to i8
+  // DEFAULT-NEXT: store i8 [[TRUNC_RESULT]], i8* %sat_sf, align 1
+
+  // Accum to Fract, same scale
+  sat_f = a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[MASK:%[0-9]+]] = and i32 [[ACCUM]], -32768
+  // DEFAULT-NEXT: [[MASK_CHANGED:%[0-9]+]] = icmp ne i32 -32768, [[MASK]]
+  // DEFAULT-NEXT: [[MASK_IS_NOT_ZERO:%[0-9]+]] = icmp ne i32 [[MASK]], 0
+  // DEFAULT-NEXT: [[DO_SAT:%[0-9]+]] = and i1 [[MASK_CHANGED]], [[MASK_IS_NOT_ZERO]]
+  // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i32 [[ACCUM]], 0
+  // DEFAULT-NEXT: [[SAT_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 -32768, i32 32767
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[DO_SAT]], i32 [[SAT_RESULT]], i32 [[ACCUM]]
+  // DEFAULT-NEXT: [[TRUNC_RESULT:%[0-9]+]] = trunc i32 [[RESULT]] to i16
+  // DEFAULT-NEXT: store i16 [[TRUNC_RESULT]], i16* %sat_f, align 2
+
+  // Accum to Fract, increasing scale
+  sat_lf = sat_a;
+  // DEFAULT:      [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+  // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[OLD_ACCUM]] to i48
+  // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = shl i48 [[ACCUM_EXT]], 16
+  // DEFAULT-NEXT: [[MASK:%[0-9]+]] = and i48 [[FRACT]], -2147483648
+  // DEFAULT-NEXT: [[MASK_CHANGED:%[0-9]+]] = icmp ne i48 -2147483648, [[MASK]]
+  // DEFAULT-NEXT: [[MASK_IS_NOT_ZERO:%[0-9]+]] = icmp ne i48 [[MASK]], 0
+  // DEFAULT-NEXT: [[DO_SAT:%[0-9]+]] = and i1 [[MASK_CHANGED]], [[MASK_IS_NOT_ZERO]]
+  // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i48 [[FRACT]], 0
+  // DEFAULT-NEXT: [[SAT_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i48 -2147483648, i48 2147483647
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[DO_SAT]], i48 [[SAT_RESULT]], i48 [[FRACT]]
+  // DEFAULT-NEXT: [[TRUNC_RESULT:%[0-9]+]] = trunc i48 [[RESULT]] to i32
+  // DEFAULT-NEXT: store i32 [[TRUNC_RESULT]], i32* %sat_lf, align 4
+
+  // Signed to unsigned, decreasing scale
+  _Sat _Accum sat_a2;
+  sat_usa = sat_a2;
+  // DEFAULT:      [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a2, align 4
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 7
+  // DEFAULT-NEXT: [[MASK:%[0-9]+]] = and i32 [[ACCUM]], -65536
+  // DEFAULT-NEXT: [[MASK_CHANGED:%[0-9]+]] = icmp ne i32 -65536, [[MASK]]
+  // DEFAULT-NEXT: [[MASK_IS_NOT_ZERO:%[0-9]+]] = icmp ne i32 [[MASK]], 0
+  // DEFAULT-NEXT: [[DO_SAT:%[0-9]+]] = and i1 [[MASK_CHANGED]], [[MASK_IS_NOT_ZERO]]
+  // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i32 [[ACCUM]], 0
+  // DEFAULT-NEXT: [[SAT_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 -65536, i32 65535
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[DO_SAT]], i32 [[SAT_RESULT]], i32 [[ACCUM]]
+  // DEFAULT-NEXT: [[NEW_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 0, i32 [[RESULT]]
+  // DEFAULT-NEXT: [[TRUNC_RESULT:%[0-9]+]] = trunc i32 [[NEW_RESULT]] to i16
+  // DEFAULT-NEXT: store i16 [[TRUNC_RESULT]], i16* %sat_usa, align 2
+  // SAME:      [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a2, align 4
+  // SAME-NEXT: [[ACCUM:%[0-9]+]] = ashr i32 [[OLD_ACCUM]], 8
+  // SAME-NEXT: [[MASK:%[0-9]+]] = and i32 [[ACCUM]], -32768
+  // SAME-NEXT: [[MASK_CHANGED:%[0-9]+]] = icmp ne i32 -32768, [[MASK]]
+  // SAME-NEXT: [[MASK_IS_NOT_ZERO:%[0-9]+]] = icmp ne i32 [[MASK]], 0
+  // SAME-NEXT: [[DO_SAT:%[0-9]+]] = and i1 [[MASK_CHANGED]], [[MASK_IS_NOT_ZERO]]
+  // SAME-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i32 [[ACCUM]], 0
+  // SAME-NEXT: [[SAT_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 -32768, i32 32767
+  // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[DO_SAT]], i32 [[SAT_RESULT]], i32 [[ACCUM]]
+  // SAME-NEXT: [[NEW_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 0, i32 [[RESULT]]
+  // SAME-NEXT: [[TRUNC_RESULT:%[0-9]+]] = trunc i32 [[NEW_RESULT]] to i16
+  // SAME-NEXT: store i16 [[TRUNC_RESULT]], i16* %sat_usa, align 2
+
+  // Signed to unsigned, increasing scale
+  sat_ua = sat_a;
+  // DEFAULT:      [[OLD_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+  // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i32 [[OLD_ACCUM]] to i33
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i33 [[ACCUM_EXT]], 1
+  // DEFAULT-NEXT: [[MASK:%[0-9]+]] = and i33 [[ACCUM]], -4294967296
+  // DEFAULT-NEXT: [[MASK_CHANGED:%[0-9]+]] = icmp ne i33 -4294967296, [[MASK]]
+  // DEFAULT-NEXT: [[MASK_IS_NOT_ZERO:%[0-9]+]] = icmp ne i33 [[MASK]], 0
+  // DEFAULT-NEXT: [[DO_SAT:%[0-9]+]] = and i1 [[MASK_CHANGED]], [[MASK_IS_NOT_ZERO]]
+  // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i33 [[ACCUM]], 0
+  // DEFAULT-NEXT: [[SAT_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i33 -4294967296, i33 4294967295
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[DO_SAT]], i33 [[SAT_RESULT]], i33 [[ACCUM]]
+  // DEFAULT-NEXT: [[NEW_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i33 0, i33 [[RESULT]]
+  // DEFAULT-NEXT: [[TRUNC_RESULT:%[0-9]+]] = trunc i33 [[NEW_RESULT]] to i32
+  // DEFAULT-NEXT: store i32 [[TRUNC_RESULT]], i32* %sat_ua, align 4
+  // SAME:      [[ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+  // SAME-NEXT: [[MASK:%[0-9]+]] = and i32 [[ACCUM]], -2147483648
+  // SAME-NEXT: [[MASK_CHANGED:%[0-9]+]] = icmp ne i32 -2147483648, [[MASK]]
+  // SAME-NEXT: [[MASK_IS_NOT_ZERO:%[0-9]+]] = icmp ne i32 [[MASK]], 0
+  // SAME-NEXT: [[DO_SAT:%[0-9]+]] = and i1 [[MASK_CHANGED]], [[MASK_IS_NOT_ZERO]]
+  // SAME-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i32 [[ACCUM]], 0
+  // SAME-NEXT: [[SAT_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 -2147483648, i32 2147483647
+  // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[DO_SAT]], i32 [[SAT_RESULT]], i32 [[ACCUM]]
+  // SAME-NEXT: [[NEW_RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i32 0, i32 [[RESULT]]
+  // SAME-NEXT: store i32 [[NEW_RESULT]], i32* %sat_ua, align 4
+
+  // Nothing when saturating to the same type and size
+  sat_a = a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %sat_a, align 4
+
+  // Nothing when assigning back
+  a = sat_a;
+  // DEFAULT:      [[SAT_ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+  // DEFAULT-NEXT: store i32 [[SAT_ACCUM]], i32* %a, align 4
+
+  // No overflow when casting from fract to signed accum
+  sat_a = sat_f;
+  // DEFAULT:      [[FRACT:%[0-9]+]] = load i16, i16* %sat_f, align 2
+  // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i16 [[FRACT]] to i32
+  // DEFAULT-NEXT: store i32 [[FRACT_EXT]], i32* %sat_a, align 4
+
+  // Only get overflow checking if signed fract to unsigned accum
+  sat_ua = sat_sf;
+  // DEFAULT:      [[FRACT:%[0-9]+]] = load i8, i8* %sat_sf, align 1
+  // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i17
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i17 [[FRACT_EXT]], 9
+  // DEFAULT-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i17 [[ACCUM]], 0
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i17 0, i17 [[ACCUM]]
+  // DEFAULT-NEXT: [[RESULT_EXT:%[0-9]+]] = sext i17 [[RESULT]] to i32
+  // DEFAULT-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4
+  // SAME:      [[FRACT:%[0-9]+]] = load i8, i8* %sat_sf, align 1
+  // SAME-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i16
+  // SAME-NEXT: [[ACCUM:%[0-9]+]] = shl i16 [[FRACT_EXT]], 8
+  // SAME-NEXT: [[IS_NEG:%[0-9]+]] = icmp slt i16 [[ACCUM]], 0
+  // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[IS_NEG]], i16 0, i16 [[ACCUM]]
+  // SAME-NEXT: [[RESULT_EXT:%[0-9]+]] = sext i16 [[RESULT]] to i32
+  // SAME-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4
+}
+
+void TestFixedPointCastBetFractAccum() {
+  short _Accum sa;
+  _Accum a;
+  long _Accum la;
+  short _Fract sf;
+  _Fract f;
+  long _Fract lf;
+  unsigned _Accum ua;
+  unsigned _Fract uf;
+
+  // To lower scale
+  sf = a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = ashr i32 [[ACCUM]], 8
+  // DEFAULT-NEXT: [[FRACT_TRUNC:%[0-9]+]] = trunc i32 [[FRACT]] to i8
+  // DEFAULT-NEXT: store i8 [[FRACT_TRUNC]], i8* %sf, align 1
+
+  // To higher scale
+  a = sf;
+  // DEFAULT:      [[FRACT:%[0-9]+]] = load i8, i8* %sf, align 1
+  // DEFAULT-NEXT: [[FRACT_EXT:%[0-9]+]] = sext i8 [[FRACT]] to i16
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = shl i16 [[FRACT_EXT]], 8
+  // DEFAULT-NEXT: [[ACCUM_EXT:%[0-9]+]] = sext i16 [[ACCUM]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM_EXT]], i32* %a, align 4
+
+  // To same scale
+  f = a;
+  // DEFAULT:      [[ACCUM:%[0-9]+]] = load i32, i32* %a, align 4
+  // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = trunc i32 [[ACCUM]] to i16
+  // DEFAULT-NEXT: store i16 [[FRACT]], i16* %f, align 2
+
+  a = f;
+  // DEFAULT:      [[FRACT:%[0-9]+]] = load i16, i16* %f, align 2
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = sext i16 [[FRACT]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %a, align 4
+
+  // To unsigned
+  ua = uf;
+  // DEFAULT:      [[FRACT:%[0-9]+]] = load i16, i16* %uf, align 2
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = zext i16 [[FRACT]] to i32
+  // DEFAULT-NEXT: store i32 [[ACCUM]], i32* %ua, align 4
+  // SAME:      [[FRACT:%[0-9]+]] = load i16, i16* %uf, align 2
+  // SAME-NEXT: [[ACCUM:%[0-9]+]] = zext i16 [[FRACT]] to i32
+  // SAME-NEXT: store i32 [[ACCUM]], i32* %ua, align 4
+
+  uf = ua;
+  // DEFAULT:      [[FRACT:%[0-9]+]] = load i32, i32* %ua, align 4
+  // DEFAULT-NEXT: [[ACCUM:%[0-9]+]] = trunc i32 [[FRACT]] to i16
+  // DEFAULT-NEXT: store i16 [[ACCUM]], i16* %uf, align 2
+  // SAME:      [[FRACT:%[0-9]+]] = load i32, i32* %ua, align 4
+  // SAME-NEXT: [[ACCUM:%[0-9]+]] = trunc i32 [[FRACT]] to i16
+  // SAME-NEXT: store i16 [[ACCUM]], i16* %uf, align 2
+}
Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -415,7 +415,8 @@
       case CK_ZeroToOCLEvent:
       case CK_ZeroToOCLQueue:
       case CK_IntToOCLSampler:
-      case CK_LValueBitCast: {
+      case CK_LValueBitCast:
+      case CK_FixedPointCast: {
         state =
             handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
         continue;
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -5868,10 +5868,28 @@
     case Type::STK_FloatingComplex:
     case Type::STK_IntegralComplex:
     case Type::STK_MemberPointer:
+    case Type::STK_FixedPoint:
       llvm_unreachable("illegal cast from pointer");
     }
     llvm_unreachable("Should have returned before this");
 
+  case Type::STK_FixedPoint:
+    switch (DestTy->getScalarTypeKind()) {
+    case Type::STK_FixedPoint:
+      return CK_FixedPointCast;
+    case Type::STK_CPointer:
+    case Type::STK_ObjCObjectPointer:
+    case Type::STK_BlockPointer:
+    case Type::STK_Bool:
+    case Type::STK_Integral:
+    case Type::STK_Floating:
+    case Type::STK_IntegralComplex:
+    case Type::STK_FloatingComplex:
+    case Type::STK_MemberPointer:
+      llvm_unreachable("Unimplemented conversion from FixedPoint to type");
+    }
+    llvm_unreachable("Should have returned before this");
+
   case Type::STK_Bool: // casting from bool is like casting from an integer
   case Type::STK_Integral:
     switch (DestTy->getScalarTypeKind()) {
@@ -5900,6 +5918,8 @@
       return CK_FloatingRealToComplex;
     case Type::STK_MemberPointer:
       llvm_unreachable("member pointer type in C");
+    case Type::STK_FixedPoint:
+      llvm_unreachable("Unimplemented conversion from Integral to FixedPoint");
     }
     llvm_unreachable("Should have returned before this");
 
@@ -5927,6 +5947,8 @@
       llvm_unreachable("valid float->pointer cast?");
     case Type::STK_MemberPointer:
       llvm_unreachable("member pointer type in C");
+    case Type::STK_FixedPoint:
+      llvm_unreachable("Unimplemented conversion from Floating to FixedPoint");
     }
     llvm_unreachable("Should have returned before this");
 
@@ -5956,6 +5978,9 @@
       llvm_unreachable("valid complex float->pointer cast?");
     case Type::STK_MemberPointer:
       llvm_unreachable("member pointer type in C");
+    case Type::STK_FixedPoint:
+      llvm_unreachable(
+          "Unimplemented conversion from FloatingComplex to FixedPoint");
     }
     llvm_unreachable("Should have returned before this");
 
@@ -5985,6 +6010,9 @@
       llvm_unreachable("valid complex int->pointer cast?");
     case Type::STK_MemberPointer:
       llvm_unreachable("member pointer type in C");
+    case Type::STK_FixedPoint:
+      llvm_unreachable(
+          "Unimplemented conversion from IntegralComplex to FixedPoint");
     }
     llvm_unreachable("Should have returned before this");
   }
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -533,6 +533,8 @@
   case Type::STK_Floating: return CK_FloatingToBoolean;
   case Type::STK_IntegralComplex: return CK_IntegralComplexToBoolean;
   case Type::STK_FloatingComplex: return CK_FloatingComplexToBoolean;
+  case Type::STK_FixedPoint:
+    llvm_unreachable("Unknown cast from FixedPoint to boolean");
   }
   llvm_unreachable("unknown scalar type kind");
 }
Index: lib/Edit/RewriteObjCFoundationAPI.cpp
===================================================================
--- lib/Edit/RewriteObjCFoundationAPI.cpp
+++ lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -1085,6 +1085,9 @@
 
     case CK_BooleanToSignedIntegral:
       llvm_unreachable("OpenCL-specific cast in Objective-C?");
+
+    case CK_FixedPointCast:
+      llvm_unreachable("Fixed point types are disabled for Objective-C");
     }
   }
 
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -23,6 +23,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/FixedPoint.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Frontend/CodeGenOptions.h"
 #include "llvm/ADT/Optional.h"
@@ -325,6 +326,9 @@
                        SourceLocation Loc,
                        ScalarConversionOpts Opts = ScalarConversionOpts());
 
+  Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy,
+                                  SourceLocation Loc);
+
   /// Emit a conversion from the specified complex type to the specified
   /// destination type, where the destination type is an LLVM scalar type.
   Value *EmitComplexToScalarConversion(CodeGenFunction::ComplexPairTy Src,
@@ -985,6 +989,92 @@
                 SanitizerHandler::ImplicitConversion, StaticArgs, {Src, Dst});
 }
 
+// This code is effectively the same as APFixedPoint::convert().
+Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
+                                                   QualType DstTy,
+                                                   SourceLocation Loc) {
+  using llvm::APInt;
+  using llvm::ConstantInt;
+
+  assert(SrcTy->isFixedPointType());
+  assert(DstTy->isFixedPointType());
+
+  FixedPointSemantics SrcFPSema =
+      CGF.getContext().getFixedPointSemantics(SrcTy);
+  FixedPointSemantics DstFPSema =
+      CGF.getContext().getFixedPointSemantics(DstTy);
+  unsigned SrcWidth = SrcFPSema.getWidth();
+  unsigned DstWidth = DstFPSema.getWidth();
+  unsigned SrcScale = SrcFPSema.getScale();
+  unsigned DstScale = DstFPSema.getScale();
+  bool IsSigned = SrcFPSema.isSigned();
+
+  Value *Result = Src;
+  unsigned ResultWidth = SrcWidth;
+
+  if (DstScale > SrcScale) {
+    // Need to allocate space before shifting left
+    ResultWidth = SrcWidth + DstScale - SrcScale;
+    llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth);
+
+    if (IsSigned)
+      Result = Builder.CreateSExt(Result, UpscaledTy);
+    else
+      Result = Builder.CreateZExt(Result, UpscaledTy);
+
+    Result = Builder.CreateShl(Result, DstScale - SrcScale);
+  } else if (DstScale < SrcScale) {
+    if (IsSigned)
+      Result = Builder.CreateAShr(Result, SrcScale - DstScale);
+    else
+      Result = Builder.CreateLShr(Result, SrcScale - DstScale);
+  }
+
+  if (DstFPSema.isSaturated() &&
+      (CGF.getContext().getCorrespondingSaturatedType(SrcTy) != DstTy)) {
+    auto Mask = APInt::getBitsSetFrom(
+        ResultWidth,
+        std::min(DstScale + DstFPSema.getIntegralBits(), ResultWidth));
+    Value *Zero = ConstantInt::get(CGF.getLLVMContext(), APInt(ResultWidth, 0));
+
+    Value *IsNegative = nullptr;
+    if (Mask != 0) {
+      Value *ConstMask = ConstantInt::get(CGF.getLLVMContext(), Mask);
+      Value *Masked = Builder.CreateAnd(Result, ConstMask);
+
+      // Change in bits above the sign
+      Value *MasksDiffer = Builder.CreateICmpNE(ConstMask, Masked);
+      Value *MaskedIsNonZero = Builder.CreateIsNotNull(Masked);
+      Value *DoSaturate = Builder.CreateAnd(MasksDiffer, MaskedIsNonZero);
+
+      IsNegative = IsSigned ? Builder.CreateICmpSLT(Result, Zero)
+                            : ConstantInt::getFalse(CGF.getLLVMContext());
+
+      Value *OnesMask = ConstantInt::get(CGF.getLLVMContext(),
+                                         APInt::getAllOnesValue(ResultWidth));
+      Result = Builder.CreateSelect(
+          DoSaturate,
+          Builder.CreateSelect(IsNegative, ConstMask,
+                               Builder.CreateXor(ConstMask, OnesMask)),
+          Result);
+    }
+
+    if (!DstFPSema.isSigned() && IsSigned) {
+      if (IsNegative == nullptr)
+        IsNegative = Builder.CreateICmpSLT(Result, Zero);
+      Result = Builder.CreateSelect(IsNegative, Zero, Result);
+    }
+  }
+
+  // Final resizing to dst width
+  llvm::Type *DstIntTy = Builder.getIntNTy(DstWidth);
+  if (IsSigned)
+    Result = Builder.CreateSExtOrTrunc(Result, DstIntTy);
+  else
+    Result = Builder.CreateZExtOrTrunc(Result, DstIntTy);
+  return Result;
+}
+
 /// Emit a conversion from the specified type to the specified destination type,
 /// both of which are LLVM scalar types.
 Value *ScalarExprEmitter::EmitScalarConversion(Value *Src, QualType SrcType,
@@ -1874,6 +1964,10 @@
     return Builder.CreateVectorSplat(NumElements, Elt, "splat");
   }
 
+  case CK_FixedPointCast:
+    return EmitFixedPointConversion(Visit(E), E->getType(), DestTy,
+                                    CE->getExprLoc());
+
   case CK_IntegralCast: {
     ScalarConversionOpts Opts;
     if (CGF.SanOpts.has(SanitizerKind::ImplicitIntegerTruncation)) {
Index: lib/CodeGen/CGExprConstant.cpp
===================================================================
--- lib/CodeGen/CGExprConstant.cpp
+++ lib/CodeGen/CGExprConstant.cpp
@@ -871,6 +871,7 @@
     case CK_FloatingCast:
     case CK_ZeroToOCLEvent:
     case CK_ZeroToOCLQueue:
+    case CK_FixedPointCast:
       return nullptr;
     }
     llvm_unreachable("Invalid CastKind");
Index: lib/CodeGen/CGExprComplex.cpp
===================================================================
--- lib/CodeGen/CGExprComplex.cpp
+++ lib/CodeGen/CGExprComplex.cpp
@@ -509,6 +509,7 @@
   case CK_ZeroToOCLQueue:
   case CK_AddressSpaceConversion:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     llvm_unreachable("invalid cast kind for complex value");
 
   case CK_FloatingRealToComplex:
Index: lib/CodeGen/CGExprAgg.cpp
===================================================================
--- lib/CodeGen/CGExprAgg.cpp
+++ lib/CodeGen/CGExprAgg.cpp
@@ -851,6 +851,7 @@
   case CK_ZeroToOCLQueue:
   case CK_AddressSpaceConversion:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     llvm_unreachable("cast kind invalid for aggregate types");
   }
 }
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -4156,6 +4156,7 @@
   case CK_CopyAndAutoreleaseBlockObject:
   case CK_AddressSpaceConversion:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     return EmitUnsupportedLValue(E, "unexpected cast lvalue");
 
   case CK_Dependent:
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -1977,6 +1977,7 @@
     if (BT->getKind() == BuiltinType::NullPtr) return STK_CPointer;
     if (BT->isInteger()) return STK_Integral;
     if (BT->isFloatingPoint()) return STK_Floating;
+    if (BT->isFixedPointType()) return STK_FixedPoint;
     llvm_unreachable("unknown scalar builtin type");
   } else if (isa<PointerType>(T)) {
     return STK_CPointer;
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -9549,6 +9549,7 @@
   case CK_NonAtomicToAtomic:
   case CK_AddressSpaceConversion:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     llvm_unreachable("invalid cast kind for integral value");
 
   case CK_BitCast:
@@ -10083,6 +10084,7 @@
   case CK_NonAtomicToAtomic:
   case CK_AddressSpaceConversion:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     llvm_unreachable("invalid cast kind for complex value");
 
   case CK_LValueToRValue:
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -1644,6 +1644,7 @@
   case CK_ZeroToOCLEvent:
   case CK_ZeroToOCLQueue:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     assert(!getType()->isBooleanType() && "unheralded conversion to bool");
     goto CheckNoBasePath;
 
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -1916,7 +1916,8 @@
     STK_Integral,
     STK_Floating,
     STK_IntegralComplex,
-    STK_FloatingComplex
+    STK_FloatingComplex,
+    STK_FixedPoint
   };
 
   /// Given that this is a scalar type, classify it.
Index: include/clang/AST/OperationKinds.def
===================================================================
--- include/clang/AST/OperationKinds.def
+++ include/clang/AST/OperationKinds.def
@@ -197,6 +197,10 @@
 ///    float f = i;
 CAST_OPERATION(IntegralToFloating)
 
+/// CK_FixedPointCast - Fixed point to fixed point.
+///    (_Accum) 0.5r
+CAST_OPERATION(FixedPointCast)
+
 /// CK_FloatingToIntegral - Floating point to integral.  Rounds
 /// towards zero, discarding any fractional component.
 ///    (int) f
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to