leonardchan updated this revision to Diff 173961.
leonardchan marked 11 inline comments as done.

Repository:
  rC Clang

https://reviews.llvm.org/D53738

Files:
  clang/include/clang/AST/ASTContext.h
  clang/include/clang/Basic/FixedPoint.h
  clang/lib/AST/ASTContext.cpp
  clang/lib/Basic/FixedPoint.cpp
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/test/Frontend/fixed_point_add.c
  clang/test/Frontend/fixed_point_add_ast.c
  clang/test/Frontend/fixed_point_conversions.c

Index: clang/test/Frontend/fixed_point_conversions.c
===================================================================
--- clang/test/Frontend/fixed_point_conversions.c
+++ clang/test/Frontend/fixed_point_conversions.c
@@ -214,19 +214,17 @@
   // Only get overflow checking if signed fract to unsigned accum
   sat_ua = sat_sf;
   // DEFAULT:      [[FRACT:%[0-9a-z]+]] = load i8, i8* %sat_sf, align 1
-  // DEFAULT-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i17
-  // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i17 [[FRACT_EXT]], 9
-  // DEFAULT-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i17 [[ACCUM]], 0
-  // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i17 0, i17 [[ACCUM]]
-  // DEFAULT-NEXT: [[RESULT_EXT:%[0-9a-z]+]] = sext i17 [[RESULT]] to i32
-  // DEFAULT-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4
+  // DEFAULT-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32
+  // DEFAULT-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 9
+  // DEFAULT-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0
+  // DEFAULT-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i32 0, i32 [[ACCUM]]
+  // DEFAULT-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4
   // SAME:      [[FRACT:%[0-9a-z]+]] = load i8, i8* %sat_sf, align 1
-  // SAME-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i16
-  // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i16 [[FRACT_EXT]], 8
-  // SAME-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i16 [[ACCUM]], 0
-  // SAME-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i16 0, i16 [[ACCUM]]
-  // SAME-NEXT: [[RESULT_EXT:%[0-9a-z]+]] = sext i16 [[RESULT]] to i32
-  // SAME-NEXT: store i32 [[RESULT_EXT]], i32* %sat_ua, align 4
+  // SAME-NEXT: [[FRACT_EXT:%[0-9a-z]+]] = sext i8 [[FRACT]] to i32
+  // SAME-NEXT: [[ACCUM:%[0-9a-z]+]] = shl i32 [[FRACT_EXT]], 8
+  // SAME-NEXT: [[IS_NEG:%[0-9a-z]+]] = icmp slt i32 [[ACCUM]], 0
+  // SAME-NEXT: [[RESULT:%[0-9a-z]+]] = select i1 [[IS_NEG]], i32 0, i32 [[ACCUM]]
+  // SAME-NEXT: store i32 [[RESULT]], i32* %sat_ua, align 4
 }
 
 void TestFixedPointCastBetFractAccum() {
Index: clang/test/Frontend/fixed_point_add_ast.c
===================================================================
--- /dev/null
+++ clang/test/Frontend/fixed_point_add_ast.c
@@ -0,0 +1,337 @@
+// RUN: %clang_cc1 -x c -ffixed-point -ast-dump %s | FileCheck %s --strict-whitespace
+
+void SignedAdditions() {
+  // CHECK-LABEL: SignedAdditions
+  short _Accum sa;
+  _Accum a;
+  long _Accum la;
+  unsigned short _Accum usa;
+  unsigned _Accum ua;
+  unsigned long _Accum ula;
+
+  short _Fract sf;
+  _Fract f;
+  long _Fract lf;
+  unsigned short _Fract usf;
+  unsigned _Fract uf;
+  unsigned long _Fract ulf;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  sa = sa + sa;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} '_Accum' {{.*}} 'a' '_Accum'
+  a = sa + a;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'long _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'long _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'long _Accum' {{.*}} 'la' 'long _Accum'
+  la = sa + la;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:       `-ImplicitCastExpr {{.*}} 'long _Accum' <FixedPointCast>
+  // CHECK-NEXT:    `-BinaryOperator {{.*}} '_Accum' '+'
+  // CHECK-NEXT:      |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:      | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:      `-ImplicitCastExpr {{.*}} '_Accum' <LValueToRValue>
+  // CHECK-NEXT:        `-DeclRefExpr {{.*}} '_Accum' {{.*}} 'a' '_Accum'
+  la = sa + a;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'short _Fract' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'short _Fract' {{.*}} 'sf' 'short _Fract'
+  sa = sa + sf;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Fract' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} '_Fract' {{.*}} 'f' '_Fract'
+  sa = sa + f;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'long _Fract' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'long _Fract' {{.*}} 'lf' 'long _Fract'
+  sa = sa + lf;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'short _Accum' <FixedPointCast>
+  // CHECK-NEXT:     `-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:       `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  sa = sa + usa;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Accum' <FixedPointCast>
+  // CHECK-NEXT:     `-ImplicitCastExpr {{.*}} 'unsigned _Accum' <LValueToRValue>
+  // CHECK-NEXT:       `-DeclRefExpr {{.*}} 'unsigned _Accum' {{.*}} 'ua' 'unsigned _Accum'
+  a = sa + ua;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'long _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'long _Accum' <FixedPointCast>
+  // CHECK-NEXT:     `-ImplicitCastExpr {{.*}} 'unsigned long _Accum' <LValueToRValue>
+  // CHECK-NEXT:       `-DeclRefExpr {{.*}} 'unsigned long _Accum' {{.*}} 'ula' 'unsigned long _Accum'
+  la = sa + ula;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'short _Fract' <FixedPointCast>
+  // CHECK-NEXT:     `-ImplicitCastExpr {{.*}} 'unsigned short _Fract' <LValueToRValue>
+  // CHECK-NEXT:       `-DeclRefExpr {{.*}} 'unsigned short _Fract' {{.*}} 'usf' 'unsigned short _Fract'
+  sa = sa + usf;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Fract' <FixedPointCast>
+  // CHECK-NEXT:     `-ImplicitCastExpr {{.*}} 'unsigned _Fract' <LValueToRValue>
+  // CHECK-NEXT:       `-DeclRefExpr {{.*}} 'unsigned _Fract' {{.*}} 'uf' 'unsigned _Fract'
+  sa = sa + uf;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'long _Fract' <FixedPointCast>
+  // CHECK-NEXT:     `-ImplicitCastExpr {{.*}} 'unsigned long _Fract' <LValueToRValue>
+  // CHECK-NEXT:       `-DeclRefExpr {{.*}} 'unsigned long _Fract' {{.*}} 'ulf' 'unsigned long _Fract'
+  sa = sa + ulf;
+}
+
+void UnsignedAdditions() {
+  // CHECK-LABEL: UnsignedAdditions
+  unsigned short _Accum usa;
+  unsigned _Accum ua;
+  unsigned long _Accum ula;
+
+  unsigned short _Fract usf;
+  unsigned _Fract uf;
+  unsigned long _Fract ulf;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'unsigned short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  usa = usa + usa;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'unsigned _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned _Accum' {{.*}} 'ua' 'unsigned _Accum'
+  ua = usa + ua;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'unsigned long _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned long _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned long _Accum' {{.*}} 'ula' 'unsigned long _Accum'
+  ula = usa + ula;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'unsigned short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned short _Fract' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned short _Fract' {{.*}} 'usf' 'unsigned short _Fract'
+  usa = usa + usf;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'unsigned short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned _Fract' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned _Fract' {{.*}} 'uf' 'unsigned _Fract'
+  usa = usa + uf;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'unsigned short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned long _Fract' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned long _Fract' {{.*}} 'ulf' 'unsigned long _Fract'
+  usa = usa + ulf;
+}
+
+void IntAdditions() {
+  // CHECK-LABEL: IntAdditions
+  short _Accum sa;
+  unsigned short _Accum usa;
+  int i;
+  unsigned int ui;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'int' {{.*}} 'i' 'int'
+  sa = sa + i;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned int' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned int' {{.*}} 'ui' 'unsigned int'
+  sa = sa + ui;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'unsigned short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'int' {{.*}} 'i' 'int'
+  usa = usa + i;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} 'unsigned short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned int' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned int' {{.*}} 'ui' 'unsigned int'
+  usa = usa + ui;
+}
+
+void SaturatedAdditions() {
+  // CHECK-LABEL: SaturatedAdditions
+  short _Accum sa;
+  _Accum a;
+  long _Accum la;
+  unsigned short _Accum usa;
+  unsigned _Accum ua;
+  unsigned long _Accum ula;
+
+  _Sat short _Accum sa_sat;
+  _Sat _Accum a_sat;
+  _Sat long _Accum la_sat;
+  _Sat unsigned short _Accum usa_sat;
+  _Sat unsigned _Accum ua_sat;
+  _Sat unsigned long _Accum ula_sat;
+
+  short _Fract sf;
+  _Fract f;
+  long _Fract lf;
+  unsigned short _Fract usf;
+  unsigned _Fract uf;
+  unsigned long _Fract ulf;
+
+  int i;
+  unsigned int ui;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Sat short _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum'
+  sa_sat = sa + sa_sat;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat long _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Sat long _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} '_Sat long _Accum' {{.*}} 'la_sat' '_Sat long _Accum'
+  la_sat = sa + la_sat;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat long _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} '_Sat short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'long _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'long _Accum' {{.*}} 'la' 'long _Accum'
+  la_sat = sa_sat + la;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'short _Accum' {{.*}} 'sa' 'short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Sat short _Accum' <FixedPointCast>
+  // CHECK-NEXT:     `-ImplicitCastExpr {{.*}} '_Sat unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:       `-DeclRefExpr {{.*}} '_Sat unsigned short _Accum' {{.*}} 'usa_sat' '_Sat unsigned short _Accum'
+  sa_sat = sa + usa_sat;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat long _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'short _Accum' <FixedPointCast>
+  // CHECK-NEXT:   | `-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   |   `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Sat long _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} '_Sat long _Accum' {{.*}} 'la_sat' '_Sat long _Accum'
+  la_sat = usa + la_sat;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat unsigned long _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} 'unsigned short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} 'unsigned short _Accum' {{.*}} 'usa' 'unsigned short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Sat unsigned long _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} '_Sat unsigned long _Accum' {{.*}} 'ula_sat' '_Sat unsigned long _Accum'
+  ula_sat = usa + ula_sat;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} '_Sat short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Sat short _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum'
+  sa_sat = sa_sat + sa_sat;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat long _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} '_Sat short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} '_Sat long _Accum' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} '_Sat long _Accum' {{.*}} 'la_sat' '_Sat long _Accum'
+  la_sat = sa_sat + la_sat;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} '_Sat short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'int' {{.*}} 'i' 'int'
+  sa_sat = sa_sat + i;
+
+  // CHECK-NOT:  FixedPointCast
+  // CHECK:      `-BinaryOperator {{.*}} '_Sat short _Accum' '+'
+  // CHECK-NEXT:   |-ImplicitCastExpr {{.*}} '_Sat short _Accum' <LValueToRValue>
+  // CHECK-NEXT:   | `-DeclRefExpr {{.*}} '_Sat short _Accum' {{.*}} 'sa_sat' '_Sat short _Accum'
+  // CHECK-NEXT:   `-ImplicitCastExpr {{.*}} 'unsigned int' <LValueToRValue>
+  // CHECK-NEXT:     `-DeclRefExpr {{.*}} 'unsigned int' {{.*}} 'ui' 'unsigned int'
+  sa_sat = sa_sat + ui;
+}
Index: clang/test/Frontend/fixed_point_add.c
===================================================================
--- /dev/null
+++ clang/test/Frontend/fixed_point_add.c
@@ -0,0 +1,279 @@
+// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED
+// RUN: %clang_cc1 -ffixed-point -fpadding-on-unsigned-fixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,UNSIGNED
+
+void SignedAddition() {
+  // CHECK-LABEL: SignedAddition
+  short _Accum sa;
+  _Accum a;
+  long _Accum la;
+  unsigned short _Accum usa;
+  unsigned _Accum ua;
+  unsigned long _Accum ula;
+
+  short _Fract sf;
+  _Fract f;
+  long _Fract lf;
+  unsigned short _Fract usf;
+  unsigned _Fract uf;
+  unsigned long _Fract ulf;
+
+  // Same type
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[SA2:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[SA2]]
+  // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2
+  sa = sa + sa;
+
+  // To larger scale and larger width
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[A:%[0-9]+]] = load i32, i32* %a, align 4
+  // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i32
+  // CHECK-NEXT: [[SA:%[a-z0-9]+]] = shl i32 [[EXT_SA]], 8
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[SA]], [[A]]
+  // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4
+  a = sa + a;
+
+  // To smaller scale and same width
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[SF:%[0-9]+]] = load i8, i8* %sf, align 1
+  // CHECK-NEXT: [[EXT_SF:%[a-z0-9]+]] = sext i8 [[SF]] to i16
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[EXT_SF]]
+  // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2
+  sa = sa + sf;
+
+  // To smaller scale and smaller width
+  // CHECK:      [[A:%[0-9]+]] = load i32, i32* %a, align 4
+  // CHECK-NEXT: [[SF:%[0-9]+]] = load i8, i8* %sf, align 1
+  // CHECK-NEXT: [[EXT_SF:%[a-z0-9]+]] = sext i8 [[SF]] to i32
+  // CHECK-NEXT: [[SF:%[a-z0-9]+]] = shl i32 [[EXT_SF]], 8
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[A]], [[SF]]
+  // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4
+  a = a + sf;
+
+  // To larger scale and same width
+  // CHECK:      [[A:%[0-9]+]] = load i32, i32* %a, align 4
+  // CHECK-NEXT: [[LF:%[0-9]+]] = load i32, i32* %lf, align 4
+  // CHECK-NEXT: [[EXT_A:%[a-z0-9]+]] = sext i32 [[A]] to i48
+  // CHECK-NEXT: [[A:%[a-z0-9]+]] = shl i48 [[EXT_A]], 16
+  // CHECK-NEXT: [[EXT_LF:%[a-z0-9]+]] = sext i32 [[LF]] to i48
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i48 [[A]], [[EXT_LF]]
+  // CHECK-NEXT: [[RES:%[a-z0-9]+]] = ashr i48 [[SUM]], 16
+  // CHECK-NEXT: [[TRUNC_RES:%[a-z0-9]+]] = trunc i48 [[RES]] to i32
+  // CHECK-NEXT: store i32 [[TRUNC_RES]], i32* %a, align 4
+  a = a + lf;
+
+  // With corresponding unsigned type
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+  // SIGNED-NEXT: [[USA_SHR:%[a-z0-9]+]] = lshr i16 [[USA]], 1
+  // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[USA_SHR]]
+  // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[USA]]
+  // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2
+  sa = sa + usa;
+
+  // With unsigned of larger scale
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[USA:%[0-9]+]] = load i32, i32* %ua, align 4
+  // SIGNED-NEXT: [[USA_SHR:%[a-z0-9]+]] = lshr i32 [[USA]], 1
+  // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i32
+  // CHECK-NEXT: [[SA:%[a-z0-9]+]] = shl i32 [[EXT_SA]], 8
+  // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i32 [[SA]], [[USA_SHR]]
+  // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i32 [[SA]], [[USA]]
+  // CHECK-NEXT: store i32 [[SUM]], i32* %a, align 4
+  a = sa + ua;
+
+  // With unsigned of smaller width
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[USF:%[0-9]+]] = load i8, i8* %usf, align 1
+  // SIGNED-NEXT: [[USF_SHR:%[a-z0-9]+]] = lshr i8 [[USF]], 1
+  // SIGNED-NEXT: [[EXT_USF:%[a-z0-9]+]] = sext i8 [[USF_SHR]] to i16
+  // UNSIGNED-NEXT: [[EXT_USF:%[a-z0-9]+]] = sext i8 [[USF]] to i16
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[SA]], [[EXT_USF]]
+  // CHECK-NEXT: store i16 [[SUM]], i16* %sa, align 2
+  sa = sa + usf;
+
+  // With unsigned of larger width and smaller scale
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[ULF:%[0-9]+]] = load i32, i32* %ulf, align 4
+  // SIGNED-NEXT: [[ULF_SHR:%[a-z0-9]+]] = lshr i32 [[ULF]], 1
+  // CHECK-NEXT: [[EXT_SA:%[a-z0-9]+]] = sext i16 [[SA]] to i40
+  // CHECK-NEXT: [[SA:%[a-z0-9]+]] = shl i40 [[EXT_SA]], 24
+  // SIGNED-NEXT: [[EXT_ULF:%[a-z0-9]+]] = sext i32 [[ULF_SHR]] to i40
+  // UNSIGNED-NEXT: [[EXT_ULF:%[a-z0-9]+]] = sext i32 [[ULF]] to i40
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i40 [[SA]], [[EXT_ULF]]
+  // CHECK-NEXT: [[RES:%[a-z0-9]+]] = ashr i40 [[SUM]], 24
+  // CHECK-NEXT: [[RES_TRUNC:%[a-z0-9]+]] = trunc i40 [[RES]] to i16
+  // CHECK-NEXT: store i16 [[RES_TRUNC]], i16* %sa, align 2
+  sa = sa + ulf;
+}
+
+void UnsignedAddition() {
+  // CHECK-LABEL: UnsignedAddition
+  unsigned short _Accum usa;
+  unsigned _Accum ua;
+  unsigned long _Accum ula;
+
+  unsigned short _Fract usf;
+  unsigned _Fract uf;
+  unsigned long _Fract ulf;
+
+  // CHECK:      [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+  // CHECK-NEXT: [[USA2:%[0-9]+]] = load i16, i16* %usa, align 2
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[USA]], [[USA2]]
+  // CHECK-NEXT: store i16 [[SUM]], i16* %usa, align 2
+  usa = usa + usa;
+
+  // CHECK:      [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+  // CHECK-NEXT: [[UA:%[0-9]+]] = load i32, i32* %ua, align 4
+  // CHECK-NEXT: [[EXT_USA:%[a-z0-9]+]] = zext i16 [[USA]] to i32
+  // CHECK-NEXT: [[USA:%[a-z0-9]+]] = shl i32 [[EXT_USA]], 8
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i32 [[USA]], [[UA]]
+  // CHECK-NEXT: store i32 [[SUM]], i32* %ua, align 4
+  ua = usa + ua;
+
+  // CHECK:      [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+  // CHECK-NEXT: [[USF:%[0-9]+]] = load i8, i8* %usf, align 1
+  // CHECK-NEXT: [[EXT_USF:%[a-z0-9]+]] = zext i8 [[USF]] to i16
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i16 [[USA]], [[EXT_USF]]
+  // CHECK-NEXT: store i16 [[SUM]], i16* %usa, align 2
+  usa = usa + usf;
+
+  // CHECK:      [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+  // CHECK-NEXT: [[UF:%[0-9]+]] = load i16, i16* %uf, align 2
+  // CHECK-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i24
+  // CHECK-NEXT: [[USA:%[a-z0-9]+]] = shl i24 [[USA_EXT]], 8
+  // CHECK-NEXT: [[UF_EXT:%[a-z0-9]+]] = zext i16 [[UF]] to i24
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i24 [[USA]], [[UF_EXT]]
+  // CHECK-NEXT: [[RES:%[a-z0-9]+]] = lshr i24 [[SUM]], 8
+  // CHECK-NEXT: [[RES_TRUNC:%[a-z0-9]+]] = trunc i24 [[RES]] to i16
+  // CHECK-NEXT: store i16 [[RES_TRUNC]], i16* %usa, align 2
+  usa = usa + uf;
+}
+
+void IntAddition() {
+  // CHECK-LABEL: IntAddition
+  short _Accum sa;
+  unsigned short _Accum usa;
+  _Sat short _Accum sa_sat;
+  int i;
+  unsigned int ui;
+
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4
+  // CHECK-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i39
+  // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39
+  // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_EXT]], [[I]]
+  // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16
+  // CHECK-NEXT: store i16 [[RES]], i16* %sa, align 2
+  sa = sa + i;
+
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[UI:%[0-9]+]] = load i32, i32* %ui, align 4
+  // CHECK-NEXT: [[SA_EXT:%[a-z0-9]+]] = sext i16 [[SA]] to i39
+  // CHECK-NEXT: [[UI_EXT:%[a-z0-9]+]] = zext i32 [[UI]] to i39
+  // CHECK-NEXT: [[UI:%[a-z0-9]+]] = shl i39 [[UI_EXT]], 7
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_EXT]], [[UI]]
+  // CHECK-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16
+  // CHECK-NEXT: store i16 [[RES]], i16* %sa, align 2
+  sa = sa + ui;
+
+  // CHECK:      [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+  // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4
+  // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i40
+  // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i40
+  // SIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8
+  // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]]
+  // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SUM]] to i16
+  // UNSIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i39
+  // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39
+  // UNSIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+  // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]]
+  // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16
+  // CHECK-NEXT: store i16 [[RES]], i16* %usa, align 2
+  usa = usa + i;
+
+  // CHECK:      [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+  // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4
+  // SIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i40
+  // SIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i40
+  // SIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i40 [[I_EXT]], 8
+  // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]]
+  // SIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i40 [[SUM]] to i16
+  // UNSIGNED-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i39
+  // UNSIGNED-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i39
+  // UNSIGNED-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+  // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]]
+  // UNSIGNED-NEXT: [[RES:%[a-z0-9]+]] = trunc i39 [[SUM]] to i16
+  // CHECK-NEXT: store i16 [[RES]], i16* %usa, align 2
+  usa = usa + ui;
+}
+
+void SaturatedAddition() {
+  // CHECK-LABEL: SaturatedAddition
+  short _Accum sa;
+  _Accum a;
+  long _Accum la;
+  unsigned short _Accum usa;
+  unsigned _Accum ua;
+  unsigned long _Accum ula;
+
+  _Sat short _Accum sa_sat;
+  _Sat _Accum a_sat;
+  _Sat long _Accum la_sat;
+  _Sat unsigned short _Accum usa_sat;
+  _Sat unsigned _Accum ua_sat;
+  _Sat unsigned long _Accum ula_sat;
+
+  int i;
+  unsigned int ui;
+
+  // CHECK:      [[SA:%[0-9]+]] = load i16, i16* %sa, align 2
+  // CHECK-NEXT: [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.sadd.sat.i16(i16 [[SA]], i16
+  // [[SA_SAT]])
+  // CHECK-NEXT: store i16 [[SUM]], i16* %sa_sat, align 2
+  sa_sat = sa + sa_sat;
+
+  // CHECK:      [[USA:%[0-9]+]] = load i16, i16* %usa, align 2
+  // CHECK-NEXT: [[USA_SAT:%[0-9]+]] = load i16, i16* %usa_sat, align 2
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = call i16 @llvm.uadd.sat.i16(i16 [[USA]], i16 [[USA_SAT]])
+  // CHECK-NEXT: store i16 [[SUM]], i16* %usa_sat, align 2
+  usa_sat = usa + usa_sat;
+
+  // CHECK:      [[UA:%[0-9]+]] = load i32, i32* %ua, align 4
+  // CHECK-NEXT: [[USA:%[0-9]+]] = load i16, i16* %usa_sat, align 2
+  // CHECK-NEXT: [[USA_EXT:%[a-z0-9]+]] = zext i16 [[USA]] to i32
+  // CHECK-NEXT: [[USA:%[a-z0-9]+]] = shl i32 [[USA_EXT]], 8
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = call i32 @llvm.uadd.sat.i32(i32 [[UA]], i32 [[USA]])
+  // CHECK-NEXT: store i32 [[SUM]], i32* %ua_sat, align 4
+  ua_sat = ua + usa_sat;
+
+  // CHECK:      [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2
+  // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %i, align 4
+  // CHECK-NEXT: [[SA_SAT_EXT:%[a-z0-9]+]] = sext i16 [[SA_SAT]] to i39
+  // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = sext i32 [[I]] to i39
+  // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = call i39 @llvm.sadd.sat.i39(i39 [[SA_SAT_EXT]], i39 [[I]])
+  // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767
+  // CHECK-NEXT: [[RES:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[SUM]]
+  // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[RES]], -32768
+  // CHECK-NEXT: [[RES2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[RES]]
+  // CHECK-NEXT: [[RES3:%[a-z0-9]+]] = trunc i39 [[RES2]] to i16
+  // CHECK-NEXT: store i16 [[RES3]], i16* %sa_sat, align 2
+  sa_sat = sa_sat + i;
+
+  // CHECK:      [[SA_SAT:%[0-9]+]] = load i16, i16* %sa_sat, align 2
+  // CHECK-NEXT: [[I:%[0-9]+]] = load i32, i32* %ui, align 4
+  // CHECK-NEXT: [[SA_SAT_EXT:%[a-z0-9]+]] = sext i16 [[SA_SAT]] to i39
+  // CHECK-NEXT: [[I_EXT:%[a-z0-9]+]] = zext i32 [[I]] to i39
+  // CHECK-NEXT: [[I:%[a-z0-9]+]] = shl i39 [[I_EXT]], 7
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = call i39 @llvm.sadd.sat.i39(i39 [[SA_SAT_EXT]], i39 [[I]])
+  // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767
+  // CHECK-NEXT: [[RES:%[a-z0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[SUM]]
+  // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[RES]], -32768
+  // CHECK-NEXT: [[RES2:%[a-z0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[RES]]
+  // CHECK-NEXT: [[RES3:%[a-z0-9]+]] = trunc i39 [[RES2]] to i16
+  // CHECK-NEXT: store i16 [[RES3]], i16* %sa_sat, align 2
+  sa_sat = sa_sat + ui;
+}
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -1229,6 +1229,102 @@
   return ComplexType;
 }
 
+/// Return the rank of a given fixed point or integer type. The value itself
+/// doesn't matter, but the values must be increasing with proper increasing
+/// rank as described in N1169 4.1.1.
+static unsigned GetFixedPointRank(QualType Ty) {
+  if (Ty->isIntegerType())
+    return 0;
+
+  const auto *BTy = Ty->getAs<BuiltinType>();
+  switch (BTy->getKind()) {
+  case BuiltinType::ShortFract:
+  case BuiltinType::UShortFract:
+  case BuiltinType::SatShortFract:
+  case BuiltinType::SatUShortFract:
+    return 1;
+  case BuiltinType::Fract:
+  case BuiltinType::UFract:
+  case BuiltinType::SatFract:
+  case BuiltinType::SatUFract:
+    return 2;
+  case BuiltinType::LongFract:
+  case BuiltinType::ULongFract:
+  case BuiltinType::SatLongFract:
+  case BuiltinType::SatULongFract:
+    return 3;
+  case BuiltinType::ShortAccum:
+  case BuiltinType::UShortAccum:
+  case BuiltinType::SatShortAccum:
+  case BuiltinType::SatUShortAccum:
+    return 4;
+  case BuiltinType::Accum:
+  case BuiltinType::UAccum:
+  case BuiltinType::SatAccum:
+  case BuiltinType::SatUAccum:
+    return 5;
+  case BuiltinType::LongAccum:
+  case BuiltinType::ULongAccum:
+  case BuiltinType::SatLongAccum:
+  case BuiltinType::SatULongAccum:
+    return 6;
+  default:
+    llvm_unreachable("Unexpected fixed point or integer type");
+  }
+}
+
+/// handleFixedPointConversions - Fixed point operations between fixed
+/// point types and integers or other fixed point types do not fall under
+/// usual arithmetic conversion since these conversions could result in loss
+/// of precsision (N1169 4.1.4). These operations should be calculated with
+/// the full precision of their result type (N1169 4.1.6.2.1).
+static QualType handleFixedPointConversion(Sema &S, ExprResult &FixedExpr,
+                                           ExprResult &OtherExpr,
+                                           QualType FixedTy, QualType OtherTy) {
+  assert(FixedTy->isFixedPointType() &&
+         "Expected FixedTy to be a fixed point type");
+  assert((OtherTy->isFixedPointType() || OtherTy->isIntegerType()) &&
+         "Special fixed point arithmetic operation conversions are only "
+         "applied to ints or other fixed point types");
+
+  // If one operand has signed fixed-point type and the other operand has
+  // unsigned fixed-point type, then the unsigned fixed-point operand is
+  // converted to its corresponding signed fixed-point type and the resulting
+  // type is the type of the converted operand.
+  if (OtherTy->isSignedFixedPointType() &&
+      FixedTy->isUnsignedFixedPointType()) {
+    QualType ResultTy = S.Context.getCorrespondingSignedFixedPointType(FixedTy);
+    FixedExpr =
+        S.ImpCastExprToType(FixedExpr.get(), ResultTy, CK_FixedPointCast);
+    FixedTy = ResultTy;
+  } else if (OtherTy->isUnsignedFixedPointType() &&
+             FixedTy->isSignedFixedPointType()) {
+    QualType ResultTy = S.Context.getCorrespondingSignedFixedPointType(OtherTy);
+    OtherExpr =
+        S.ImpCastExprToType(OtherExpr.get(), ResultTy, CK_FixedPointCast);
+    OtherTy = ResultTy;
+  }
+
+  // The result type is the type with the highest rank, whereby a fixed-point
+  // conversion rank is always greater than an integer conversion rank; if the
+  // type of either of the operands is a saturating fixedpoint type, the result
+  // type shall be the saturating fixed-point type corresponding to the type
+  // with the highest rank; the resulting value is converted (taking into
+  // account rounding and overflow) to the precision of the resulting type.
+  // Same ranks between signed and unsigned types are resolved earlier, so both
+  // types are either signed or both unsigned at this point.
+  unsigned FixedTyRank = GetFixedPointRank(FixedTy);
+  unsigned OtherTyRank = GetFixedPointRank(OtherTy);
+
+  QualType ResultTy = FixedTyRank > OtherTyRank ? FixedTy : OtherTy;
+
+  if (FixedTy->isSaturatedFixedPointType() ||
+      OtherTy->isSaturatedFixedPointType())
+    ResultTy = S.Context.getCorrespondingSaturatedType(ResultTy);
+
+  return ResultTy;
+}
+
 /// UsualArithmeticConversions - Performs various conversions that are common to
 /// binary operators (C99 6.3.1.8). If both operands aren't arithmetic, this
 /// routine returns the first non-arithmetic type found. The client is
@@ -1301,12 +1397,16 @@
     return handleComplexIntConversion(*this, LHS, RHS, LHSType, RHSType,
                                       IsCompAssign);
 
+  if (LHSType->isFixedPointType())
+    return handleFixedPointConversion(*this, LHS, RHS, LHSType, RHSType);
+  else if (RHSType->isFixedPointType())
+    return handleFixedPointConversion(*this, RHS, LHS, RHSType, LHSType);
+
   // Finally, we have two differing integer types.
   return handleIntegerConversion<doIntegralCast, doIntegralCast>
            (*this, LHS, RHS, LHSType, RHSType, IsCompAssign);
 }
 
-
 //===----------------------------------------------------------------------===//
 //  Semantic Analysis for various Expression Types
 //===----------------------------------------------------------------------===//
@@ -16634,4 +16734,4 @@
 
   return new (Context)
       ObjCAvailabilityCheckExpr(Version, AtLoc, RParen, Context.BoolTy);
-}
\ No newline at end of file
+}
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -125,6 +125,13 @@
         return CFP->isZero();
     return true;
   }
+
+  /// Check if either operand is a fixed point type, in which case, this
+  /// operation did not follow usual arithmetic conversion and both operands may
+  /// not be the same.
+  bool isFixedPointBinOp() const {
+    return isa<BinaryOperator>(E) && Ty->isFixedPointType();
+  }
 };
 
 static bool MustVisitNullValue(const Expr *E) {
@@ -340,6 +347,9 @@
 
   Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy,
                                   SourceLocation Loc);
+  Value *EmitFixedPointConversion(Value *Src, FixedPointSemantics &SrcFixedSema,
+                                  FixedPointSemantics &DstFixedSema,
+                                  SourceLocation Loc);
 
   /// Emit a conversion from the specified complex type to the specified
   /// destination type, where the destination type is an LLVM scalar type.
@@ -715,6 +725,9 @@
     return Builder.CreateOr(Ops.LHS, Ops.RHS, "or");
   }
 
+  // Helper functions for fixed point binary operations.
+  Value *EmitFixedPointAdd(const BinOpInfo &Ops);
+
   BinOpInfo EmitBinOps(const BinaryOperator *E);
   LValue EmitCompoundAssignLValue(const CompoundAssignOperator *E,
                             Value *(ScalarExprEmitter::*F)(const BinOpInfo &),
@@ -1409,17 +1422,23 @@
 Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
                                                    QualType DstTy,
                                                    SourceLocation Loc) {
-  using llvm::APInt;
-  using llvm::ConstantInt;
-  using llvm::Value;
-
   assert(SrcTy->isFixedPointType());
   assert(DstTy->isFixedPointType());
 
   FixedPointSemantics SrcFPSema =
       CGF.getContext().getFixedPointSemantics(SrcTy);
   FixedPointSemantics DstFPSema =
       CGF.getContext().getFixedPointSemantics(DstTy);
+  return EmitFixedPointConversion(Src, SrcFPSema, DstFPSema, Loc);
+}
+
+Value *ScalarExprEmitter::EmitFixedPointConversion(
+    Value *Src, FixedPointSemantics &SrcFPSema, FixedPointSemantics &DstFPSema,
+    SourceLocation Loc) {
+  using llvm::APInt;
+  using llvm::ConstantInt;
+  using llvm::Value;
+
   unsigned SrcWidth = SrcFPSema.getWidth();
   unsigned DstWidth = DstFPSema.getWidth();
   unsigned SrcScale = SrcFPSema.getScale();
@@ -1448,7 +1467,8 @@
   } else {
     // Adjust the number of fractional bits.
     if (DstScale > SrcScale) {
-      ResultWidth = SrcWidth + DstScale - SrcScale;
+      // Compare to DstWidth to prevent resizing twice.
+      ResultWidth = std::max(SrcWidth + DstScale - SrcScale, DstWidth);
       llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth);
       Result = Builder.CreateIntCast(Result, UpscaledTy, SrcIsSigned, "resize");
       Result = Builder.CreateShl(Result, DstScale - SrcScale, "upscale");
@@ -1479,7 +1499,8 @@
     }
 
     // Resize the integer part to get the final destination size.
-    Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
+    if (ResultWidth != DstWidth)
+      Result = Builder.CreateIntCast(Result, DstIntTy, SrcIsSigned, "resize");
   }
   return Result;
 }
@@ -3325,9 +3346,66 @@
     return propagateFMFlags(V, op);
   }
 
+  if (op.isFixedPointBinOp())
+    return EmitFixedPointAdd(op);
+
   return Builder.CreateAdd(op.LHS, op.RHS, "add");
 }
 
+/// The resulting value must be calculated with exact precision, so the operands
+/// may not be the same type.
+Value *ScalarExprEmitter::EmitFixedPointAdd(const BinOpInfo &op) {
+  using llvm::APSInt;
+  using llvm::ConstantInt;
+
+  const auto *BinOp = dyn_cast<BinaryOperator>(op.E);
+  assert(BinOp && "Expected the operator to be a binary operator");
+  assert(BinOp->getOpcode() == BO_Add && "Expected operation to be addition");
+
+  // The result is a fixed point type and at least one of the operands is fixed
+  // point while the other is either fixed point or an int. This resulting type
+  // should be determined by Sema::FixedPointConversions().
+  QualType ResultTy = op.Ty;
+  QualType LHSTy = BinOp->getLHS()->getType();
+  QualType RHSTy = BinOp->getRHS()->getType();
+  ASTContext &Ctx = CGF.getContext();
+  Value *LHS = op.LHS;
+  Value *RHS = op.RHS;
+
+  auto LHSFixedSema = Ctx.getFixedPointSemantics(LHSTy);
+  auto RHSFixedSema = Ctx.getFixedPointSemantics(RHSTy);
+  auto ResultFixedSema = Ctx.getFixedPointSemantics(ResultTy);
+  auto CommonFixedSema = LHSFixedSema.getCommonSemantics(RHSFixedSema);
+  bool IsCommonSaturated = CommonFixedSema.isSaturated();
+
+  // When finding this common type, we do not care about saturating the operands
+  // when converting since they should be resized to enough bits to hold the
+  // result with full precision.
+  if (IsCommonSaturated)
+    CommonFixedSema.setSaturated(false);
+
+  // Convert and align the operands.
+  Value *LHSAligned = EmitFixedPointConversion(
+      LHS, LHSFixedSema, CommonFixedSema, BinOp->getExprLoc());
+  Value *RHSAligned = EmitFixedPointConversion(
+      RHS, RHSFixedSema, CommonFixedSema, BinOp->getExprLoc());
+
+  // Perform the actual addition.
+  Value *Result;
+  if (IsCommonSaturated) {
+    llvm::Intrinsic::ID IID = CommonFixedSema.isSigned()
+                                  ? llvm::Intrinsic::sadd_sat
+                                  : llvm::Intrinsic::uadd_sat;
+    Result = Builder.CreateBinaryIntrinsic(IID, LHSAligned, RHSAligned);
+  } else {
+    Result = Builder.CreateAdd(LHSAligned, RHSAligned);
+  }
+
+  // Convert to the result type.
+  return EmitFixedPointConversion(Result, CommonFixedSema, ResultFixedSema,
+                                  BinOp->getExprLoc());
+}
+
 Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
   // The LHS is always a pointer if either side is.
   if (!op.LHS->getType()->isPointerTy()) {
Index: clang/lib/Basic/FixedPoint.cpp
===================================================================
--- clang/lib/Basic/FixedPoint.cpp
+++ clang/lib/Basic/FixedPoint.cpp
@@ -112,4 +112,31 @@
   return APFixedPoint(Val, Sema);
 }
 
+FixedPointSemantics FixedPointSemantics::GetIntegerSemantics(unsigned Width,
+                                                             bool IsSigned) {
+  return FixedPointSemantics(Width, /*Scale=*/0, IsSigned,
+                             /*IsSaturated=*/false,
+                             /*HasUnsignedPadding=*/false);
+}
+
+FixedPointSemantics FixedPointSemantics::getCommonSemantics(
+    const FixedPointSemantics &Other) const {
+  unsigned CommonScale = std::max(getScale(), Other.getScale());
+  unsigned NonFractBits = getWidth() - getScale();
+  unsigned OtherNonFractBits = Other.getWidth() - Other.getScale();
+  unsigned CommonWidth =
+      std::max(NonFractBits, OtherNonFractBits) + CommonScale;
+
+  bool ResultIsSigned = isSigned() || Other.isSigned();
+  bool ResultHasUnsignedPadding = false;
+  if (!ResultIsSigned)
+    // Both are unsigned.
+    ResultHasUnsignedPadding =
+        hasUnsignedPadding() || Other.hasUnsignedPadding();
+
+  return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned,
+                             isSaturated() || Other.isSaturated(),
+                             ResultHasUnsignedPadding);
+}
+
 }  // namespace clang
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -10481,7 +10481,13 @@
 }
 
 FixedPointSemantics ASTContext::getFixedPointSemantics(QualType Ty) const {
-  assert(Ty->isFixedPointType());
+  assert(Ty->isFixedPointType() ||
+         Ty->isIntegerType() && "Can only get the fixed point semantics for a "
+                                "fixed point or integer type.");
+  if (Ty->isIntegerType())
+    return FixedPointSemantics::GetIntegerSemantics(getIntWidth(Ty),
+                                                    Ty->isSignedIntegerType());
+
   bool isSigned = Ty->isSignedFixedPointType();
   return FixedPointSemantics(
       static_cast<unsigned>(getTypeSize(Ty)), getFixedPointScale(Ty), isSigned,
@@ -10498,3 +10504,38 @@
   assert(Ty->isFixedPointType());
   return APFixedPoint::getMin(getFixedPointSemantics(Ty));
 }
+
+QualType ASTContext::getCorrespondingSignedFixedPointType(QualType Ty) const {
+  assert(Ty->isUnsignedFixedPointType() &&
+         "Expected unsigned fixed point type");
+  const auto *BTy = Ty->getAs<BuiltinType>();
+
+  switch (BTy->getKind()) {
+  case BuiltinType::UShortAccum:
+    return ShortAccumTy;
+  case BuiltinType::UAccum:
+    return AccumTy;
+  case BuiltinType::ULongAccum:
+    return LongAccumTy;
+  case BuiltinType::SatUShortAccum:
+    return SatShortAccumTy;
+  case BuiltinType::SatUAccum:
+    return SatAccumTy;
+  case BuiltinType::SatULongAccum:
+    return SatLongAccumTy;
+  case BuiltinType::UShortFract:
+    return ShortFractTy;
+  case BuiltinType::UFract:
+    return FractTy;
+  case BuiltinType::ULongFract:
+    return LongFractTy;
+  case BuiltinType::SatUShortFract:
+    return SatShortFractTy;
+  case BuiltinType::SatUFract:
+    return SatFractTy;
+  case BuiltinType::SatULongFract:
+    return SatLongFractTy;
+  default:
+    llvm_unreachable("Unexpected unsigned fixed point type");
+  }
+}
Index: clang/include/clang/Basic/FixedPoint.h
===================================================================
--- clang/include/clang/Basic/FixedPoint.h
+++ clang/include/clang/Basic/FixedPoint.h
@@ -36,6 +36,9 @@
       : Width(Width), Scale(Scale), IsSigned(IsSigned),
         IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) {
     assert(Width >= Scale && "Not enough room for the scale");
+    if (IsSigned)
+      assert(!HasUnsignedPadding &&
+             "Cannot have unsigned padding on a signed type.");
   }
 
   unsigned getWidth() const { return Width; }
@@ -53,6 +56,14 @@
       return Width - Scale;
   }
 
+  /// Return the FixedPointSemantics that allows for calculating the full
+  /// precision semantic between this one and another one.
+  FixedPointSemantics
+  getCommonSemantics(const FixedPointSemantics &Other) const;
+
+  /// Return the FixedPointSemantics for an integer type.
+  static FixedPointSemantics GetIntegerSemantics(unsigned Width, bool IsSigned);
+
 private:
   unsigned Width;
   unsigned Scale;
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ clang/include/clang/AST/ASTContext.h
@@ -2633,6 +2633,10 @@
   // corresponding saturated type for a given fixed point type.
   QualType getCorrespondingSaturatedType(QualType Ty) const;
 
+  // This method accepts fixed point types and returns the corresponding signed
+  // type.
+  QualType getCorrespondingSignedFixedPointType(QualType Ty) const;
+
   //===--------------------------------------------------------------------===//
   //                    Integer Values
   //===--------------------------------------------------------------------===//
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to