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

This patch contains logic and tests for different unary and comparison 
operations on fixed point types, and casting between integers and fixed point 
types.

The operations are `==`, `!=`, `>`, `<`, `>=`, `<=`, `!`, `+`, `-`, `++`, and 
`--`.

`~` is not a supported operation on fixed point types.

This is a parent of https://reviews.llvm.org/D46915


Repository:
  rC Clang

https://reviews.llvm.org/D46917

Files:
  lib/CodeGen/CGExprScalar.cpp
  lib/Sema/SemaExpr.cpp
  test/Frontend/fixed_point_declarations.c
  test/Frontend/fixed_point_validation.c

Index: test/Frontend/fixed_point_validation.c
===================================================================
--- test/Frontend/fixed_point_validation.c
+++ test/Frontend/fixed_point_validation.c
@@ -1,19 +1,170 @@
+// RUN: %clang -S -emit-llvm %s -o - | FileCheck %s
 // RUN: %clang_cc1 -S -emit-llvm -o - %s | lli
 
+// The first test checks the emitted llvm IR.
+// The second test checks the output.
+// Both these test require that the default bit widths for the fixed point types
+// are used since we check for bit shifted literals that were converted from
+// ints and floats.
+
+// Primary fixed point types
+signed short _Accum s_short_accum;    // CHECK-DAG: @s_short_accum =  common dso_local global i16 0, align 2
+signed _Accum s_accum;                // CHECK-DAG: @s_accum =        common dso_local global i32 0, align 4
+signed long _Accum s_long_accum;      // CHECK-DAG: @s_long_accum =   common dso_local global i64 0, align 8
+unsigned short _Accum u_short_accum;  // CHECK-DAG: @u_short_accum =  common dso_local global i16 0, align 2
+unsigned _Accum u_accum;              // CHECK-DAG: @u_accum =        common dso_local global i32 0, align 4
+unsigned long _Accum u_long_accum;    // CHECK-DAG: @u_long_accum =   common dso_local global i64 0, align 8
+signed short _Fract s_short_fract;    // CHECK-DAG: @s_short_fract =  common dso_local global i16 0, align 2
+signed _Fract s_fract;                // CHECK-DAG: @s_fract =        common dso_local global i32 0, align 4
+signed long _Fract s_long_fract;      // CHECK-DAG: @s_long_fract =   common dso_local global i64 0, align 8
+unsigned short _Fract u_short_fract;  // CHECK-DAG: @u_short_fract =  common dso_local global i16 0, align 2
+unsigned _Fract u_fract;              // CHECK-DAG: @u_fract =        common dso_local global i32 0, align 4
+unsigned long _Fract u_long_fract;    // CHECK-DAG: @u_long_fract =   common dso_local global i64 0, align 8
+
+// There are 7 bits allocated to the fractional part and 8
+// bits allocated to the integral part of a short _Accum by default.
+
+signed short _Accum s_short_accum2 = 2.5hk;  // CHECK-DAG: @s_short_accum2 = dso_local global i16 320, align 2
+short _Fract short_fract = 0.33333333333hr;  // CHECK-DAG: @short_fract = dso_local global i16 42, align 2
+
 // Run simple validation tests
 
 #define assert(b) if (!(b)) { return 1; }
 
 int main(){
-  short _Accum s_accum;
+  short _Accum s_accum = 0.0hk;
   short _Accum s_accum2 = 2.0hk;
   short _Fract s_fract = 0.999hr;
   short _Fract s_fract2 = -0.999hr;
+  const _Fract fract_zero = 0.0r;
+  // CHECK:      %s_accum = alloca i16, align 2
+  // CHECK:      %s_accum2 = alloca i16, align 2
+  // CHECK:      %s_fract = alloca i16, align 2
+  // CHECK:      %s_fract2 = alloca i16, align 2
+  // CHECK:      %fract_zero = alloca i32, align 4
+  // CHECK:      store i16 0, i16* %s_accum, align 2
+  // CHECK:      store i16 256, i16* %s_accum2, align 2
+  // CHECK:      store i16 127, i16* %s_fract, align 2
+  // CHECK:      store i16 -127, i16* %s_fract2, align 2
+  // CHECK:      store i32 0, i32* %fract_zero, align 4
+
+  /**************** Simple Comparisons ***************/
 
   assert(s_accum == 0);
+  // CHECK:      {{.*}} = load i16, i16* %s_accum, align 2
+  // CHECK-NEXT: {{.*}} = icmp eq i16 {{.*}}, 0
 
   s_accum = s_accum2;
+  // CHECK:      {{.*}} = load i16, i16* %s_accum2, align 2
+  // CHECK-NEXT: store i16 %1, i16* %s_accum, align 2
 
   assert(s_accum == s_accum2);
+  // CHECK:      {{.*}} = load i16, i16* %s_accum, align 2
+  // CHECK-NEXT: {{.*}} = load i16, i16* %s_accum2, align 2
+  // CHECK-NEXT: {{.*}} = icmp eq i16 {{.*}}, {{.*}}
+
+  assert(s_accum2 == s_accum);
+  // CHECK:      {{.*}} = load i16, i16* %s_accum2, align 2
+  // CHECK-NEXT: {{.*}} = load i16, i16* %s_accum, align 2
+  // CHECK-NEXT: {{.*}} = icmp eq i16 {{.*}}, {{.*}}
+
   assert(s_accum == 2);
+  // CHECK:      {{.*}} = icmp eq i16 {{.*}}, 256
+
+  assert(2 == s_accum);
+  // CHECK:      {{.*}} = icmp eq i16 256, {{.*}}
+
+  int x = 2;
+  assert(s_accum == x);
+  // CHECK:      {{.*}} = load i32, i32* %x, align 4
+  // CHECK-NEXT: {{.*}} = trunc i32 {{.*}} to i16
+  // CHECK-NEXT: {{.*}} = shl i16 {{.*}}, 7
+  // CHECK-NEXT: {{.*}} = icmp eq i16 {{.*}}, {{.*}}
+
+  assert(x == s_accum);
+
+  assert(s_accum != -2);
+  // CHECK:      {{.*}} = icmp ne i16 {{.*}}, -256
+
+  assert(-2 != s_accum);
+  // CHECK:      {{.*}} = icmp ne i16 -256, {{.*}}
+
+  assert(s_accum != -x);
+  assert(-x != s_accum);
+
+  assert(s_fract != 1);
+  // CHECK:      {{.*}} = load i16, i16* %s_fract, align 2
+  // CHECK_NEXT: {{.*}} = icmp ne i16 {{.*}}, 128
+
+  assert(s_fract2 != -1);
+  // CHECK:      {{.*}} = load i16, i16* %s_fract2, align 2
+  // CHECK_NEXT: {{.*}} = icmp ne i16 {{.*}}, -128
+
+  assert(!fract_zero);
+  assert(s_fract);
+  assert(s_fract == -s_fract2);
+
+  assert(s_fract > s_fract2);
+  // CHECK:      {{.*}} = load i16, i16* %s_fract, align 2
+  // CHECK:      {{.*}} = load i16, i16* %s_fract2, align 2
+  // CHECK:      {{.*}} = icmp sgt i16 {{.*}}, {{.*}}
+
+  assert(s_fract2 < s_fract);
+  // CHECK:      {{.*}} = load i16, i16* %s_fract2, align 2
+  // CHECK-NEXT: {{.*}} = load i16, i16* %s_fract, align 2
+  // CHECK-NEXT: {{.*}} = icmp slt i16 {{.*}}, {{.*}}
+
+  assert(s_fract >= s_fract);
+  // CHECK:      {{.*}} = icmp sge i16 {{.*}}, {{.*}}
+
+  assert(s_fract <= s_fract);
+  // CHECK:      {{.*}} = icmp sle i16 {{.*}}, {{.*}}
+
+  assert(s_fract >= s_fract2);
+  assert(s_fract2 <= s_fract);
+
+  // Depending on the number of fractional bits used, lack of precision
+  // could cause some values for fixed point numbers to be rounded to precise
+  // numbers.
+  assert(2 == 2.001hk);  // This is valid if SACCUM_FBITS == 7
+
+  /**************** Unary operations ***************/
+
+  s_accum = 0.0hk;
+  assert(!s_accum++);
+  // CHECK:      [[VAL:%.+]] = load i16, i16* %s_accum, align 2
+  // CHECK-NEXT: [[INC:%.+]] = add i16 [[VAL]], 128
+  // CHECK-NEXT: store i16 [[INC]], i16* %s_accum, align 2
+  // CHECK-NEXT: icmp ne i16 [[VAL]], 0
+
+  assert(s_accum == 1);
+  // CHECK:       load
+
+  assert(s_accum--);
+  // CHECK:      [[VAL:%.+]] = load i16, i16* %s_accum, align 2
+  // CHECK-NEXT: [[DEC:%.+]] = sub i16 [[VAL]], 128
+  // CHECK-NEXT: store i16 [[DEC]], i16* %s_accum, align 2
+  // CHECK-NEXT: icmp ne i16 [[VAL]], 0
+
+  assert(!s_accum);
+  // CHECK:      load
+
+  assert(++s_accum);
+  // CHECK:      [[VAL:%.+]] = load i16, i16* %s_accum, align 2
+  // CHECK-NEXT: [[INC:%.+]] = add i16 [[VAL]], 128
+  // CHECK-NEXT: store i16 [[INC]], i16* %s_accum, align 2
+  // CHECK-NEXT: icmp ne i16 [[INC]], 0
+
+  assert(s_accum == 1);
+  // CHECK:      load
+
+  assert(!(--s_accum));
+  // CHECK:      [[VAL:%.+]] = load i16, i16* %s_accum, align 2
+  // CHECK-NEXT: [[DEC:%.+]] = sub i16 [[VAL]], 128
+  // CHECK-NEXT: store i16 [[DEC]], i16* %s_accum, align 2
+  // CHECK-NEXT: icmp ne i16 [[DEC]], 0
+
+  assert(+s_fract == s_fract);
+  assert(+s_fract2 == s_fract2);  // s_fract2 is negative
+  assert(-s_fract == s_fract2);
 }
Index: test/Frontend/fixed_point_declarations.c
===================================================================
--- test/Frontend/fixed_point_declarations.c
+++ /dev/null
@@ -1,33 +0,0 @@
-// RUN: %clang -S -emit-llvm %s -o - | FileCheck %s
-
-// Primary fixed point types
-signed short _Accum s_short_accum;    // CHECK-DAG: @s_short_accum =  common dso_local global i16 0, align 2
-signed _Accum s_accum;                // CHECK-DAG: @s_accum =        common dso_local global i32 0, align 4
-signed long _Accum s_long_accum;      // CHECK-DAG: @s_long_accum =   common dso_local global i64 0, align 8
-unsigned short _Accum u_short_accum;  // CHECK-DAG: @u_short_accum =  common dso_local global i16 0, align 2
-unsigned _Accum u_accum;              // CHECK-DAG: @u_accum =        common dso_local global i32 0, align 4
-unsigned long _Accum u_long_accum;    // CHECK-DAG: @u_long_accum =   common dso_local global i64 0, align 8
-signed short _Fract s_short_fract;    // CHECK-DAG: @s_short_fract =  common dso_local global i16 0, align 2
-signed _Fract s_fract;                // CHECK-DAG: @s_fract =        common dso_local global i32 0, align 4
-signed long _Fract s_long_fract;      // CHECK-DAG: @s_long_fract =   common dso_local global i64 0, align 8
-unsigned short _Fract u_short_fract;  // CHECK-DAG: @u_short_fract =  common dso_local global i16 0, align 2
-unsigned _Fract u_fract;              // CHECK-DAG: @u_fract =        common dso_local global i32 0, align 4
-unsigned long _Fract u_long_fract;    // CHECK-DAG: @u_long_fract =   common dso_local global i64 0, align 8
-
-// There are 7 bits allocated to the fractional part and 8
-// bits allocated to the integral part of a short _Accum by default.
-
-signed short _Accum s_short_accum2 = 2.5hk;  // CHECK-DAG: @s_short_accum2 = dso_local global i16 320, align 2
-short _Fract short_fract = 0.33333333333hr;  // CHECK-DAG: @short_fract = dso_local global i16 42, align 2
-
-void func() {
-  s_short_accum = s_short_accum2;
-  // CHECK-DAG: %0 = load i16, i16* @s_short_accum2, align 2
-  // CHECK-DAG: store i16 %0, i16* @s_short_accum, align 2
-
-  s_short_accum == 0;
-  // CHECK-DAG: %1 = load i16, i16* @s_short_accum, align 2
-  // CHECK-DAG: %cmp = icmp eq i16 %1, 0
-
-  s_accum == 2;
-}
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -3489,7 +3489,7 @@
     // TODO: Check sizes to make sure we don't overflow or fit a value that
     // can't fit into the number of bits we have.
     double float_val = Val.convertToDouble();
-    assert(float_val > 0);
+    assert(float_val >= 0);
     double int_part;
     double fract_part = modf(float_val, &int_part);
     uint64_t int_part_as_int = static_cast<uint64_t>(int_part);
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -781,7 +781,8 @@
   if (const MemberPointerType *MPT = dyn_cast<MemberPointerType>(SrcType))
     return CGF.CGM.getCXXABI().EmitMemberPointerIsNotNull(CGF, Src, MPT);
 
-  assert((SrcType->isIntegerType() || isa<llvm::PointerType>(Src->getType())) &&
+  assert((SrcType->isIntegerType() || isa<llvm::PointerType>(Src->getType()) ||
+          SrcType->isFixedPointType()) &&
          "Unknown scalar type to convert");
 
   if (isa<llvm::IntegerType>(Src->getType()))
@@ -2102,6 +2103,34 @@
       }
     }
 
+  // Fixed point type
+  } else if (type->isFixedPointType()) {
+    // TODO: Account for overflows and the sort for saturation
+    unsigned fbits;
+    const auto *BT = type->getAs<BuiltinType>();
+    switch (BT->getKind()) {
+      default: llvm_unreachable("Not a fixed point type!");
+      case BuiltinType::ShortAccum:   fbits = BUILTIN_SACCUM_FBIT; break;
+      case BuiltinType::Accum:        fbits = BUILTIN_ACCUM_FBIT; break;
+      case BuiltinType::LongAccum:    fbits = BUILTIN_LACCUM_FBIT; break;
+      case BuiltinType::UShortAccum:  fbits = BUILTIN_USACCUM_FBIT; break;
+      case BuiltinType::UAccum:       fbits = BUILTIN_UACCUM_FBIT; break;
+      case BuiltinType::ULongAccum:   fbits = BUILTIN_ULACCUM_FBIT; break;
+      case BuiltinType::ShortFract:   fbits = BUILTIN_SFRACT_FBIT; break;
+      case BuiltinType::Fract:        fbits = BUILTIN_FRACT_FBIT; break;
+      case BuiltinType::LongFract:    fbits = BUILTIN_LFRACT_FBIT; break;
+      case BuiltinType::UShortFract:  fbits = BUILTIN_USFRACT_FBIT; break;
+      case BuiltinType::UFract:       fbits = BUILTIN_UFRACT_FBIT; break;
+      case BuiltinType::ULongFract:   fbits = BUILTIN_ULFRACT_FBIT; break;
+    }
+    llvm::Value *amt = llvm::ConstantInt::get(value->getType(), 1 << fbits,
+                                              /*isSigned=*/type->isSignedFixedPointType());
+    if (isInc) {
+      value = Builder.CreateAdd(value, amt, "fixed_point_post_inc");
+    } else {
+      value = Builder.CreateSub(value, amt, "fixed_point_post_dec");
+    }
+
   // Objective-C pointer types.
   } else {
     const ObjCObjectPointerType *OPT = type->castAs<ObjCObjectPointerType>();
@@ -3264,7 +3293,9 @@
 
     if (LHS->getType()->isFPOrFPVectorTy()) {
       Result = Builder.CreateFCmp(FCmpOpc, LHS, RHS, "cmp");
-    } else if (LHSTy->hasSignedIntegerRepresentation()) {
+    } else if (LHSTy->hasSignedIntegerRepresentation() ||
+               LHSTy->isSignedFixedPointType() ||
+               RHSTy->isSignedFixedPointType()) {
       Result = Builder.CreateICmp(SICmpOpc, LHS, RHS, "cmp");
     } else {
       // Unsigned integers and pointers.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to