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