leonardchan created this revision.
leonardchan added reviewers: ebevhan, bjope, rjmccall.
leonardchan added a project: clang.

This patch covers addition between fixed point types and other fixed point 
types or integers, using the conversion rules  described in 4.1.4 of N1169.

Usual arithmetic rules do not apply to binary operations when one of the 
operands is a fixed point type, and the result of the operation must be 
calculated with the full precision of the operands, so we should not perform 
any casting to a common type.

This patch does not include constant expression evaluation for addition of 
fixed point types. That will be addressed in another patch since I think this 
one is already big enough.


Repository:
  rC Clang

https://reviews.llvm.org/D53738

Files:
  clang/include/clang/Sema/Sema.h
  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

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,280 @@
+// 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:%[0-9]+]] = sext i16 [[SA]] to i32
+  // CHECK-NEXT: [[SA:%[0-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:%[0-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:%[0-9]+]] = sext i8 [[SF]] to i32
+  // CHECK-NEXT: [[SF:%[0-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:%[0-9]+]] = sext i32 [[A]] to i48
+  // CHECK-NEXT: [[EXT_LF:%[0-9]+]] = sext i32 [[LF]] to i48
+  // CHECK-NEXT: [[A:%[0-9]+]] = shl i48 [[EXT_A]], 16
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i48 [[A]], [[EXT_LF]]
+  // CHECK-NEXT: [[RES:%[0-9]+]] = ashr i48 [[SUM]], 16
+  // CHECK-NEXT: [[TRUNC_RES:%[0-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:%[0-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:%[0-9]+]] = lshr i32 [[USA]], 1
+  // CHECK-NEXT: [[EXT_SA:%[0-9]+]] = sext i16 [[SA]] to i32
+  // CHECK-NEXT: [[SA:%[0-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:%[0-9]+]] = lshr i8 [[USF]], 1
+  // SIGNED-NEXT: [[EXT_USF:%[0-9]+]] = sext i8 [[USF_SHR]] to i16
+  // UNSIGNED-NEXT: [[EXT_USF:%[0-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:%[0-9]+]] = lshr i32 [[ULF]], 1
+  // CHECK-NEXT: [[EXT_SA:%[0-9]+]] = sext i16 [[SA]] to i40
+  // SIGNED-NEXT: [[EXT_ULF:%[0-9]+]] = sext i32 [[ULF_SHR]] to i40
+  // UNSIGNED-NEXT: [[EXT_ULF:%[0-9]+]] = sext i32 [[ULF]] to i40
+  // CHECK-NEXT: [[SA:%[0-9]+]] = shl i40 [[EXT_SA]], 24
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i40 [[SA]], [[EXT_ULF]]
+  // CHECK-NEXT: [[RES:%[0-9]+]] = ashr i40 [[SUM]], 24
+  // CHECK-NEXT: [[RES_TRUNC:%[0-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:%[0-9]+]] = zext i16 [[USA]] to i32
+  // CHECK-NEXT: [[USA:%[0-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:%[0-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:%[0-9]+]] = zext i16 [[USA]] to i24
+  // CHECK-NEXT: [[UF_EXT:%[0-9]+]] = zext i16 [[UF]] to i24
+  // CHECK-NEXT: [[USA:%[0-9]+]] = shl i24 [[USA_EXT]], 8
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i24 [[USA]], [[UF_EXT]]
+  // CHECK-NEXT: [[RES:%[0-9]+]] = lshr i24 [[SUM]], 8
+  // CHECK-NEXT: [[RES_TRUNC:%[0-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:%[0-9]+]] = sext i16 [[SA]] to i39
+  // CHECK-NEXT: [[I_EXT:%[0-9]+]] = sext i32 [[I]] to i39
+  // CHECK-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_EXT]], [[I]]
+  // CHECK-NEXT: [[RES:%[0-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:%[0-9]+]] = sext i16 [[SA]] to i39
+  // CHECK-NEXT: [[UI_EXT:%[0-9]+]] = zext i32 [[UI]] to i39
+  // CHECK-NEXT: [[UI:%[0-9]+]] = shl i39 [[UI_EXT]], 7
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_EXT]], [[UI]]
+  // CHECK-NEXT: [[RES:%[0-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:%[0-9]+]] = zext i16 [[USA]] to i40
+  // SIGNED-NEXT: [[I_EXT:%[0-9]+]] = sext i32 [[I]] to i40
+  // SIGNED-NEXT: [[I:%[0-9]+]] = shl i40 [[I_EXT]], 8
+  // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]]
+  // SIGNED-NEXT: [[RES:%[0-9]+]] = trunc i40 [[SUM]] to i16
+  // UNSIGNED-NEXT: [[USA_EXT:%[0-9]+]] = zext i16 [[USA]] to i39
+  // UNSIGNED-NEXT: [[I_EXT:%[0-9]+]] = sext i32 [[I]] to i39
+  // UNSIGNED-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7
+  // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]]
+  // UNSIGNED-NEXT: [[RES:%[0-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:%[0-9]+]] = zext i16 [[USA]] to i40
+  // SIGNED-NEXT: [[I_EXT:%[0-9]+]] = zext i32 [[I]] to i40
+  // SIGNED-NEXT: [[I:%[0-9]+]] = shl i40 [[I_EXT]], 8
+  // SIGNED-NEXT: [[SUM:%[0-9]+]] = add i40 [[USA_EXT]], [[I]]
+  // SIGNED-NEXT: [[RES:%[0-9]+]] = trunc i40 [[SUM]] to i16
+  // UNSIGNED-NEXT: [[USA_EXT:%[0-9]+]] = zext i16 [[USA]] to i39
+  // UNSIGNED-NEXT: [[I_EXT:%[0-9]+]] = zext i32 [[I]] to i39
+  // UNSIGNED-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7
+  // UNSIGNED-NEXT: [[SUM:%[0-9]+]] = add i39 [[USA_EXT]], [[I]]
+  // UNSIGNED-NEXT: [[RES:%[0-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:%[0-9]+]] = zext i16 [[USA]] to i32
+  // CHECK-NEXT: [[USA:%[0-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 %10, 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:%[0-9]+]] = sext i16 [[SA_SAT]] to i39
+  // CHECK-NEXT: [[I_EXT:%[0-9]+]] = sext i32 [[I]] to i39
+  // CHECK-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_SAT_EXT]], [[I]]
+  // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767
+  // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[SUM]], -32768
+  // CHECK-NEXT: [[RES:%[0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[SUM]]
+  // CHECK-NEXT: [[RES2:%[0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[RES]]
+  // CHECK-NEXT: [[RES3:%[0-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:%[0-9]+]] = sext i16 [[SA_SAT]] to i39
+  // CHECK-NEXT: [[I_EXT:%[0-9]+]] = zext i32 [[I]] to i39
+  // CHECK-NEXT: [[I:%[0-9]+]] = shl i39 [[I_EXT]], 7
+  // CHECK-NEXT: [[SUM:%[0-9]+]] = add i39 [[SA_SAT_EXT]], [[I]]
+  // CHECK-NEXT: [[USE_MAX:%[0-9]+]] = icmp sgt i39 [[SUM]], 32767
+  // CHECK-NEXT: [[USE_MIN:%[0-9]+]] = icmp slt i39 [[SUM]], -32768
+  // CHECK-NEXT: [[RES:%[0-9]+]] = select i1 [[USE_MIN]], i39 -32768, i39 [[SUM]]
+  // CHECK-NEXT: [[RES2:%[0-9]+]] = select i1 [[USE_MAX]], i39 32767, i39 [[RES]]
+  // CHECK-NEXT: [[RES3:%[0-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
@@ -1306,6 +1306,148 @@
            (*this, LHS, RHS, LHSType, RHSType, IsCompAssign);
 }
 
+/// For a given fixed point type, return it's signed equivalent.
+static QualType GetCorrespondingSignedFixedPointType(ASTContext &Ctx,
+                                                     QualType Ty) {
+  assert(Ty->isUnsignedFixedPointType() &&
+         "Expected unsigned fixed point type");
+  const auto *BTy = Ty->getAs<BuiltinType>();
+
+  switch (BTy->getKind()) {
+  case BuiltinType::UShortAccum:
+    return Ctx.ShortAccumTy;
+  case BuiltinType::UAccum:
+    return Ctx.AccumTy;
+  case BuiltinType::ULongAccum:
+    return Ctx.LongAccumTy;
+  case BuiltinType::SatUShortAccum:
+    return Ctx.SatShortAccumTy;
+  case BuiltinType::SatUAccum:
+    return Ctx.SatAccumTy;
+  case BuiltinType::SatULongAccum:
+    return Ctx.SatLongAccumTy;
+  case BuiltinType::UShortFract:
+    return Ctx.ShortFractTy;
+  case BuiltinType::UFract:
+    return Ctx.FractTy;
+  case BuiltinType::ULongFract:
+    return Ctx.LongFractTy;
+  case BuiltinType::SatUShortFract:
+    return Ctx.SatShortFractTy;
+  case BuiltinType::SatUFract:
+    return Ctx.SatFractTy;
+  case BuiltinType::SatULongFract:
+    return Ctx.SatLongFractTy;
+  default:
+    llvm_unreachable("Unexpected unsigned fixed point type");
+  }
+}
+
+/// Return the rank of a given fixed point 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) {
+  assert(Ty->isFixedPointType() && "Expected fixed point type");
+  const auto *BTy = Ty->getAs<BuiltinType>();
+
+  switch (BTy->getKind()) {
+  case BuiltinType::ShortFract:
+  case BuiltinType::UShortFract:
+  case BuiltinType::SatShortFract:
+  case BuiltinType::SatUShortFract:
+    return 0;
+  case BuiltinType::Fract:
+  case BuiltinType::UFract:
+  case BuiltinType::SatFract:
+  case BuiltinType::SatUFract:
+    return 1;
+  case BuiltinType::LongFract:
+  case BuiltinType::ULongFract:
+  case BuiltinType::SatLongFract:
+  case BuiltinType::SatULongFract:
+    return 2;
+  case BuiltinType::ShortAccum:
+  case BuiltinType::UShortAccum:
+  case BuiltinType::SatShortAccum:
+  case BuiltinType::SatUShortAccum:
+    return 3;
+  case BuiltinType::Accum:
+  case BuiltinType::UAccum:
+  case BuiltinType::SatAccum:
+  case BuiltinType::SatUAccum:
+    return 4;
+  case BuiltinType::LongAccum:
+  case BuiltinType::ULongAccum:
+  case BuiltinType::SatLongAccum:
+  case BuiltinType::SatULongAccum:
+    return 5;
+  default:
+    llvm_unreachable("Unexpected fixed point type");
+  }
+}
+
+/// If an operand in a binary operation is a fixed point type, do not perform
+/// implicit conversions on it (with the exception of unsigned to signed
+// conversion). Instead the resulting type is determined by the
+/// rules in N1169 4.1.4.
+QualType Sema::FixedPointConversions(ExprResult &FixedExpr,
+                                     ExprResult &OtherExpr, bool IsCompAssign) {
+  QualType FixedTy = FixedExpr.get()->getType();
+  QualType OtherTy = OtherExpr.get()->getType();
+  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");
+
+  // LValue to RValue conversions
+  if (!IsCompAssign) {
+    FixedExpr = UsualUnaryConversions(FixedExpr.get());
+    if (FixedExpr.isInvalid())
+      return QualType();
+  }
+  OtherExpr = UsualUnaryConversions(OtherExpr.get());
+  if (OtherExpr.isInvalid())
+    return QualType();
+
+  // 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 = GetCorrespondingSignedFixedPointType(Context, FixedTy);
+    FixedExpr = ImpCastExprToType(FixedExpr.get(), ResultTy, CK_FixedPointCast);
+    FixedTy = ResultTy;
+  } else if (OtherTy->isUnsignedFixedPointType() &&
+             FixedTy->isSignedFixedPointType()) {
+    QualType ResultTy = GetCorrespondingSignedFixedPointType(Context, OtherTy);
+    OtherExpr = 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.
+  if (OtherTy->isIntegerType())
+    return FixedTy;
+
+  // 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 = Context.getCorrespondingSaturatedType(ResultTy);
+
+  return ResultTy;
+}
 
 //===----------------------------------------------------------------------===//
 //  Semantic Analysis for various Expression Types
@@ -9070,7 +9212,13 @@
     return compType;
   }
 
-  QualType compType = UsualArithmeticConversions(LHS, RHS, CompLHSTy);
+  QualType compType;
+  if (LHS.get()->getType()->isFixedPointType())
+    compType = FixedPointConversions(LHS, RHS, CompLHSTy);
+  else if (RHS.get()->getType()->isFixedPointType())
+    compType = FixedPointConversions(RHS, LHS, CompLHSTy);
+  else
+    compType = UsualArithmeticConversions(LHS, RHS, CompLHSTy);
   if (LHS.isInvalid() || RHS.isInvalid())
     return QualType();
 
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) {
@@ -714,6 +721,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 &),
@@ -3135,9 +3145,128 @@
     return propagateFMFlags(V, op);
   }
 
+  if (op.isFixedPointBinOp())
+    return EmitFixedPointAdd(op);
+
   return Builder.CreateAdd(op.LHS, op.RHS, "add");
 }
 
+/// Extract the width, scale, and sign from a fixed point or integer type. If
+/// the type is an integer, the scale is zero.
+static void GetFixedPointAttributes(ASTContext &Ctx, QualType Ty,
+                                    unsigned &Width, unsigned &Scale,
+                                    bool &Sign) {
+  if (Ty->isFixedPointType()) {
+    auto FixedSema = Ctx.getFixedPointSemantics(Ty);
+    Width = FixedSema.getWidth();
+    Scale = FixedSema.getScale();
+    Sign = Ty->isSignedFixedPointType();
+  } else if (Ty->isIntegerType()) {
+    Width = Ctx.getIntWidth(Ty);
+    Scale = 0;
+    Sign = Ty->isSignedIntegerType();
+  } else {
+    llvm_unreachable("Expected the type to be a fixed point or integer type");
+  }
+}
+
+/// 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;
+
+  unsigned LHSWidth, LHSScale, RHSWidth, RHSScale, ResultWidth, ResultScale;
+  bool LHSSign, RHSSign, ResultSign;
+  GetFixedPointAttributes(Ctx, LHSTy, LHSWidth, LHSScale, LHSSign);
+  GetFixedPointAttributes(Ctx, RHSTy, RHSWidth, RHSScale, RHSSign);
+  GetFixedPointAttributes(Ctx, ResultTy, ResultWidth, ResultScale, ResultSign);
+
+  // Cast both to a common type than can hold the full precision of the
+  // resulting value.
+  unsigned LHSIntegralBits = LHSWidth - LHSScale;
+  unsigned RHSIntegralBits = RHSWidth - RHSScale;
+  unsigned CommonWidth =
+      std::max(LHSIntegralBits, RHSIntegralBits) + std::max(LHSScale, RHSScale);
+  llvm::Type *CommonTy = Builder.getIntNTy(CommonWidth);
+
+  if (LHSWidth < CommonWidth)
+    LHS = LHSSign ? Builder.CreateSExt(LHS, CommonTy)
+                  : Builder.CreateZExt(LHS, CommonTy);
+  if (RHSWidth < CommonWidth)
+    RHS = RHSSign ? Builder.CreateSExt(RHS, CommonTy)
+                  : Builder.CreateZExt(RHS, CommonTy);
+
+  // Align scales
+  unsigned CommonScale = std::max({LHSScale, RHSScale, ResultScale});
+  if (CommonScale > LHSScale)
+    LHS = Builder.CreateShl(LHS, CommonScale - LHSScale);
+  if (CommonScale > RHSScale)
+    RHS = Builder.CreateShl(RHS, CommonScale - RHSScale);
+
+  Value *Result;
+  if (ResultTy->isSaturatedFixedPointType()) {
+    if (ResultWidth < CommonWidth) {
+      // In the event we extended the sign of both operands, the intrinsic will
+      // not saturate to the initial bit width of the result type. In this case,
+      // we can default to use min/max clamping. This can arise from adding an
+      // operand of an int type whose width is larger than the width of the
+      // other fixed point operand.
+      // If we end up implementing the intrinsics for saturating ints to
+      // specified witdths, this section could be replaced to a call to those
+      // intrinsics.
+      APSInt MaxVal =
+          Ctx.getFixedPointMax(ResultTy).getValue().extend(CommonWidth);
+      APSInt MinVal =
+          Ctx.getFixedPointMin(ResultTy).getValue().extend(CommonWidth);
+      auto MaxValConst = ConstantInt::get(CommonTy, MaxVal);
+      auto MinValConst = ConstantInt::get(CommonTy, MinVal);
+
+      Result = Builder.CreateAdd(LHS, RHS);
+      Value *UseMax = ResultSign ? Builder.CreateICmpSGT(Result, MaxValConst)
+                                 : Builder.CreateICmpUGT(Result, MaxValConst);
+      Value *UseMin = ResultSign ? Builder.CreateICmpSLT(Result, MinValConst)
+                                 : Builder.CreateICmpULT(Result, MinValConst);
+
+      Result = Builder.CreateSelect(
+          UseMax, MaxValConst,
+          Builder.CreateSelect(UseMin, MinValConst, Result));
+    } else {
+      unsigned IID =
+          ResultSign ? llvm::Intrinsic::sadd_sat : llvm::Intrinsic::uadd_sat;
+      llvm::Function *intrinsic = CGF.CGM.getIntrinsic(IID, CommonTy);
+      Result = Builder.CreateCall(intrinsic, {LHS, RHS});
+    }
+  } else {
+    Result = Builder.CreateAdd(LHS, RHS);
+  }
+
+  // Align to result scale
+  if (ResultScale < CommonScale) {
+    Result = ResultSign ? Builder.CreateAShr(Result, CommonScale - ResultScale)
+                        : Builder.CreateLShr(Result, CommonScale - ResultScale);
+  }
+
+  if (ResultWidth < CommonWidth)
+    Result = Builder.CreateTrunc(Result, Builder.getIntNTy(ResultWidth));
+
+  return Result;
+}
+
 Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
   // The LHS is always a pointer if either side is.
   if (!op.LHS->getType()->isPointerTy()) {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -9460,6 +9460,14 @@
   QualType UsualArithmeticConversions(ExprResult &LHS, ExprResult &RHS,
                                       bool IsCompAssign = false);
 
+  /// FixedPointConversions - 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).
+  QualType FixedPointConversions(ExprResult &FixedExpr, ExprResult &OtherExpr,
+                                 bool IsCompAssign = false);
+
   /// AssignConvertType - All of the 'assignment' semantic checks return this
   /// enum to indicate whether the assignment was allowed.  These checks are
   /// done for simple assignments, as well as initialization, return from
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to