leonardchan updated this revision to Diff 160964.
leonardchan marked 7 inline comments as done.
leonardchan added a comment.

- Reworked logic for saturation


Repository:
  rC Clang

https://reviews.llvm.org/D50616

Files:
  include/clang/AST/OperationKinds.def
  include/clang/AST/Type.h
  include/clang/Basic/DiagnosticCommonKinds.td
  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
  test/Frontend/fixed_point_unknown_conversions.c

Index: test/Frontend/fixed_point_unknown_conversions.c
===================================================================
--- /dev/null
+++ test/Frontend/fixed_point_unknown_conversions.c
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -verify -ffixed-point %s
+
+void func() {
+  _Bool b;
+  char c;
+  int i;
+  float f;
+  double d;
+  double _Complex dc;
+  int _Complex ic;
+  struct S {
+    int i;
+  } s;
+  enum E {
+    A
+  } e;
+  int *ptr;
+  typedef int int_t;
+  int_t i2;
+
+  _Accum accum;
+  _Fract fract = accum; // ok
+  _Accum *accum_ptr;
+
+  accum = b;       // expected-error{{conversion between fixed point and '_Bool' is not yet supported}}
+  accum = i;       // expected-error{{conversion between fixed point and 'int' is not yet supported}}
+  accum = i;       // expected-error{{conversion between fixed point and 'int' is not yet supported}}
+  accum = f;       // expected-error{{conversion between fixed point and 'float' is not yet supported}}
+  accum = d;       // expected-error{{conversion between fixed point and 'double' is not yet supported}}
+  accum = dc;      // expected-error{{conversion between fixed point and '_Complex double' is not yet supported}}
+  accum = ic;      // expected-error{{conversion between fixed point and '_Complex int' is not yet supported}}
+  accum = s;       // expected-error{{assigning to '_Accum' from incompatible type 'struct S'}}
+  accum = e;       // expected-error{{conversion between fixed point and 'enum E' is not yet supported}}
+  accum = ptr;     // expected-error{{assigning to '_Accum' from incompatible type 'int *'}}
+  accum_ptr = ptr; // expected-warning{{incompatible pointer types assigning to '_Accum *' from 'int *'}}
+  accum = i2;      // expected-error{{conversion between fixed point and 'int_t' (aka 'int') is not yet supported}}
+
+  b = accum;       // expected-error{{conversion between fixed point and '_Bool' is not yet supported}}
+  c = accum;       // expected-error{{conversion between fixed point and 'char' is not yet supported}}
+  i = accum;       // expected-error{{conversion between fixed point and 'int' is not yet supported}}
+  f = accum;       // expected-error{{conversion between fixed point and 'float' is not yet supported}}
+  d = accum;       // expected-error{{conversion between fixed point and 'double' is not yet supported}}
+  dc = accum;      // expected-error{{conversion between fixed point and '_Complex double' is not yet supported}}
+  ic = accum;      // expected-error{{conversion between fixed point and '_Complex int' is not yet supported}}
+  s = accum;       // expected-error{{assigning to 'struct S' from incompatible type '_Accum'}}
+  e = accum;       // expected-error{{conversion between fixed point and 'enum E' is not yet supported}}
+  ptr = accum;     // expected-error{{assigning to 'int *' from incompatible type '_Accum'}}
+  ptr = accum_ptr; // expected-warning{{incompatible pointer types assigning to 'int *' from '_Accum *'}}
+  i2 = accum;      // expected-error{{conversion between fixed point and 'int' is not yet supported}}
+}
Index: test/Frontend/fixed_point_conversions.c
===================================================================
--- /dev/null
+++ test/Frontend/fixed_point_conversions.c
@@ -0,0 +1,294 @@
+// 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: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]]
+  // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -32768
+  // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]]
+  // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16
+  // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], 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: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[FRACT]], 127
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 127, i32 [[FRACT]]
+  // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -128
+  // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -128, i32 [[RESULT]]
+  // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i8
+  // DEFAULT-NEXT: store i8 [[RESULT_TRUNC]], 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: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]]
+  // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], -32768
+  // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 -32768, i32 [[RESULT]]
+  // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16
+  // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], 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:%[0-9]+]] = sext i32 [[OLD_ACCUM]] to i48
+  // DEFAULT-NEXT: [[FRACT:%[0-9]+]] = shl i48 [[ACCUM]], 16
+  // DEFAULT-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i48 [[FRACT]], 2147483647
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i48 2147483647, i48 [[FRACT]]
+  // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i48 [[RESULT]], -2147483648
+  // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i48 -2147483648, i48 [[RESULT]]
+  // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i48 [[RESULT2]] to i32
+  // DEFAULT-NEXT: store i32 [[RESULT_TRUNC]], 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: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 65535
+  // DEFAULT-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 65535, i32 [[ACCUM]]
+  // DEFAULT-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], 0
+  // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]]
+  // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16
+  // DEFAULT-NEXT: store i16 [[RESULT_TRUNC]], 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: [[USE_MAX:%[0-9]+]] = icmp sgt i32 [[ACCUM]], 32767
+  // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MAX]], i32 32767, i32 [[ACCUM]]
+  // SAME-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[RESULT]], 0
+  // SAME-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[RESULT]]
+  // SAME-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i32 [[RESULT2]] to i16
+  // SAME-NEXT: store i16 [[RESULT_TRUNC]], 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: [[USE_MIN:%[0-9]+]] = icmp slt i33 [[ACCUM]], 0
+  // DEFAULT-NEXT: [[RESULT2:%[0-9]+]] = select i1 [[USE_MIN]], i33 0, i33 [[ACCUM]]
+  // DEFAULT-NEXT: [[RESULT_TRUNC:%[0-9]+]] = trunc i33 [[RESULT2]] to i32
+  // DEFAULT-NEXT: store i32 [[RESULT_TRUNC]], i32* %sat_ua, align 4
+  // SAME:      [[ACCUM:%[0-9]+]] = load i32, i32* %sat_a, align 4
+  // SAME-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i32 [[ACCUM]], 0
+  // SAME-NEXT: [[RESULT:%[0-9]+]] = select i1 [[USE_MIN]], i32 0, i32 [[ACCUM]]
+  // SAME-NEXT: store i32 [[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,36 @@
     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_Bool:
+      Diag(Src.get()->getExprLoc(),
+           diag::err_unimplemented_conversion_with_fixed_point_type)
+          << DestTy;
+      return CK_IntegralToBoolean;
+    case Type::STK_Integral:
+    case Type::STK_Floating:
+    case Type::STK_IntegralComplex:
+    case Type::STK_FloatingComplex:
+      Diag(Src.get()->getExprLoc(),
+           diag::err_unimplemented_conversion_with_fixed_point_type)
+          << DestTy;
+      return CK_IntegralCast;
+    case Type::STK_CPointer:
+    case Type::STK_ObjCObjectPointer:
+    case Type::STK_BlockPointer:
+    case Type::STK_MemberPointer:
+      llvm_unreachable("illegal cast to pointer 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 +5926,11 @@
       return CK_FloatingRealToComplex;
     case Type::STK_MemberPointer:
       llvm_unreachable("member pointer type in C");
+    case Type::STK_FixedPoint:
+      Diag(Src.get()->getExprLoc(),
+           diag::err_unimplemented_conversion_with_fixed_point_type)
+          << SrcTy;
+      return CK_IntegralCast;
     }
     llvm_unreachable("Should have returned before this");
 
@@ -5927,6 +5958,11 @@
       llvm_unreachable("valid float->pointer cast?");
     case Type::STK_MemberPointer:
       llvm_unreachable("member pointer type in C");
+    case Type::STK_FixedPoint:
+      Diag(Src.get()->getExprLoc(),
+           diag::err_unimplemented_conversion_with_fixed_point_type)
+          << SrcTy;
+      return CK_IntegralCast;
     }
     llvm_unreachable("Should have returned before this");
 
@@ -5956,6 +5992,11 @@
       llvm_unreachable("valid complex float->pointer cast?");
     case Type::STK_MemberPointer:
       llvm_unreachable("member pointer type in C");
+    case Type::STK_FixedPoint:
+      Diag(Src.get()->getExprLoc(),
+           diag::err_unimplemented_conversion_with_fixed_point_type)
+          << SrcTy;
+      return CK_IntegralCast;
     }
     llvm_unreachable("Should have returned before this");
 
@@ -5985,6 +6026,11 @@
       llvm_unreachable("valid complex int->pointer cast?");
     case Type::STK_MemberPointer:
       llvm_unreachable("member pointer type in C");
+    case Type::STK_FixedPoint:
+      Diag(Src.get()->getExprLoc(),
+           diag::err_unimplemented_conversion_with_fixed_point_type)
+          << SrcTy;
+      return CK_IntegralCast;
     }
     llvm_unreachable("Should have returned before this");
   }
@@ -12725,6 +12771,12 @@
       if (Context.getLangOpts().CPlusPlus) {
         // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9:
         // operand contextually converted to bool.
+        if (resultType->getScalarTypeKind() == Type::STK_FixedPoint) {
+          return ExprError(
+              Diag(Input.get()->getExprLoc(),
+                   diag::err_unimplemented_conversion_with_fixed_point_type)
+              << resultType);
+        }
         Input = ImpCastExprToType(Input.get(), Context.BoolTy,
                                   ScalarTypeToBooleanCastKind(resultType));
       } else if (Context.getLangOpts().OpenCL &&
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,82 @@
                 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 extend first before scaling up
+    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()) {
+    if (DstFPSema.getIntegralBits() < SrcFPSema.getIntegralBits()) {
+      auto Max = ConstantInt::get(
+          CGF.getLLVMContext(),
+          APFixedPoint::getMax(DstFPSema).getValue().extOrTrunc(ResultWidth));
+      Value *TooHigh = IsSigned ? Builder.CreateICmpSGT(Result, Max)
+                                : Builder.CreateICmpUGT(Result, Max);
+      Result = Builder.CreateSelect(TooHigh, Max, Result);
+
+      if (IsSigned) {
+        // Cannot overflow min to dest type is src is unsigned since all fixed
+        // point types can cover the unsigned min of 0.
+        auto Min = ConstantInt::get(
+            CGF.getLLVMContext(),
+            APFixedPoint::getMin(DstFPSema).getValue().extOrTrunc(ResultWidth));
+        Value *TooLow = Builder.CreateICmpSLT(Result, Min);
+        Result = Builder.CreateSelect(TooLow, Min, Result);
+      }
+    } else if (IsSigned && !DstFPSema.isSigned()) {
+      Value *Zero =
+          ConstantInt::get(CGF.getLLVMContext(), APInt(ResultWidth, 0));
+      Value *LTZero = Builder.CreateICmpSLT(Result, Zero);
+      Result = Builder.CreateSelect(LTZero, 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 +1954,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
@@ -4152,6 +4152,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/Basic/DiagnosticCommonKinds.td
===================================================================
--- include/clang/Basic/DiagnosticCommonKinds.td
+++ include/clang/Basic/DiagnosticCommonKinds.td
@@ -172,6 +172,8 @@
   "this value is too large for this fixed point type">;
 def err_fixed_point_not_enabled : Error<"compile with "
   "'-ffixed-point' to enable fixed point types">;
+def err_unimplemented_conversion_with_fixed_point_type : Error<
+  "conversion between fixed point and %0 is not yet supported">;
 
 // SEH
 def err_seh_expected_handler : Error<
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -1979,7 +1979,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