[clang] [CIR] Upstream missing support for fixed point literal (PR #193445)

Haocong Lu via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 22 02:10:27 PDT 2026


https://github.com/Luhaocong created https://github.com/llvm/llvm-project/pull/193445

- Upstream CIR CodeGen for fixed point builtin types `_Fract`, `_Accum` and `_Sat`.
- Upstream CIR CodeGen for fixed point literal
- Part of task https://github.com/llvm/llvm-project/issues/192316

>From ed9f566b3f4fed95f7f0f127f8e2e5b1c1d39aac Mon Sep 17 00:00:00 2001
From: Haocong Lu <haocong.lu at witintech.com>
Date: Wed, 22 Apr 2026 17:13:40 +0800
Subject: [PATCH] [CIR] Upstream missing support for fixed point literal

- Upstream CIR CodeGen for fixed point builtin types `_Fract`,
  `_Accum` and `_Sat`.
- Upstream CIR CodeGen for fixed point literal
---
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp   |   6 +-
 clang/lib/CIR/CodeGen/CIRGenTypes.cpp        |  26 +++
 clang/test/CIR/CodeGen/fixed-point-literal.c | 208 +++++++++++++++++++
 3 files changed, 237 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/fixed-point-literal.c

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 298baaba8d3e2..d79a66e87116e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -231,9 +231,9 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
   }
 
   mlir::Value VisitFixedPointLiteral(const FixedPointLiteral *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(),
-                     "ScalarExprEmitter: fixed point literal");
-    return {};
+    mlir::Type type = cgf.convertType(e->getType());
+    return cir::ConstantOp::create(builder, cgf.getLoc(e->getExprLoc()),
+                                   cir::IntAttr::get(type, e->getValue()));
   }
 
   mlir::Value VisitFloatingLiteral(const FloatingLiteral *e) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 883b791d5909c..4fd7be6f8cc10 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -317,6 +317,19 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
     case BuiltinType::SChar:
     case BuiltinType::Short:
     case BuiltinType::WChar_S:
+    case BuiltinType::Accum:
+    case BuiltinType::Fract:
+    case BuiltinType::LongAccum:
+    case BuiltinType::LongFract:
+    case BuiltinType::ShortAccum:
+    case BuiltinType::ShortFract:
+    // Saturated signed types.
+    case BuiltinType::SatAccum:
+    case BuiltinType::SatFract:
+    case BuiltinType::SatLongAccum:
+    case BuiltinType::SatLongFract:
+    case BuiltinType::SatShortAccum:
+    case BuiltinType::SatShortFract:
       resultType =
           cir::IntType::get(&getMLIRContext(), astContext.getTypeSize(ty),
                             /*isSigned=*/true);
@@ -388,6 +401,19 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
     case BuiltinType::ULongLong:
     case BuiltinType::UShort:
     case BuiltinType::WChar_U:
+    case BuiltinType::UAccum:
+    case BuiltinType::UFract:
+    case BuiltinType::ULongAccum:
+    case BuiltinType::ULongFract:
+    case BuiltinType::UShortAccum:
+    case BuiltinType::UShortFract:
+    // Saturated unsigned types.
+    case BuiltinType::SatUAccum:
+    case BuiltinType::SatUFract:
+    case BuiltinType::SatULongAccum:
+    case BuiltinType::SatULongFract:
+    case BuiltinType::SatUShortAccum:
+    case BuiltinType::SatUShortFract:
       resultType =
           cir::IntType::get(&getMLIRContext(), astContext.getTypeSize(ty),
                             /*isSigned=*/false);
diff --git a/clang/test/CIR/CodeGen/fixed-point-literal.c b/clang/test/CIR/CodeGen/fixed-point-literal.c
new file mode 100644
index 0000000000000..4c2fa18a04592
--- /dev/null
+++ b/clang/test/CIR/CodeGen/fixed-point-literal.c
@@ -0,0 +1,208 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffixed-point -fclangir -Wno-unused-value -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffixed-point -fclangir -Wno-unused-value -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -ffixed-point -Wno-unused-value -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+// Test basic fixed-point literals
+void test_short_fract() {
+  // CIR:  cir.func{{.*}} @test_short_fract
+  // LLVM: void @test_short_fract
+  // OGCG: void @test_short_fract
+  short _Fract sf = 0.5hr;
+  // CIR:  %{{.*}} = cir.const #cir.int<64> : !s8i
+  // LLVM: store i8 64, ptr %{{.*}}, align 1
+  // OGCG: store i8 64, ptr %{{.*}}, align 1
+  unsigned short _Fract usf = 0.5uhr;
+  // CIR:  %{{.*}} = cir.const #cir.int<128> : !u8i
+  // LLVM: store i8 -128, ptr %{{.*}}, align 1
+  // OGCG: store i8 -128, ptr %{{.*}}, align 1
+}
+
+void test_fract() {
+  // CIR:  cir.func{{.*}} @test_fract
+  // LLVM: void @test_fract
+  // OGCG: void @test_fract
+  _Fract f = 0.5r;
+  // CIR:  %{{.*}} = cir.const #cir.int<16384> : !s16i
+  // LLVM: store i16 16384, ptr %{{.*}}, align 2
+  // OGCG: store i16 16384, ptr %{{.*}}, align 2
+  unsigned _Fract uf = 0.5ur;
+  // CIR:  %{{.*}} = cir.const #cir.int<32768> : !u16i
+  // LLVM: store i16 -32768, ptr %{{.*}}, align 2
+  // OGCG: store i16 -32768, ptr %{{.*}}, align 2
+}
+
+void test_long_fract() {
+  // CIR:  cir.func{{.*}} @test_long_fract
+  // LLVM: void @test_long_fract
+  // OGCG: void @test_long_fract
+  long _Fract lf = 0.5lr;
+  // CIR:  %{{.*}} = cir.const #cir.int<1073741824> : !s32i
+  // LLVM: store i32 1073741824, ptr %{{.*}}, align 4
+  // OGCG: store i32 1073741824, ptr %{{.*}}, align 4
+  unsigned long _Fract ulf = 0.5ulr;
+  // CIR:  %{{.*}} = cir.const #cir.int<2147483648> : !u32i
+  // LLVM: store i32 -2147483648, ptr %{{.*}}, align 4
+  // OGCG: store i32 -2147483648, ptr %{{.*}}, align 4
+}
+
+void test_short_accum() {
+  // CIR:  cir.func{{.*}} @test_short_accum
+  // LLVM: void @test_short_accum
+  // OGCG: void @test_short_accum
+  short _Accum sa = 0.5hk;
+  // CIR:  %{{.*}} = cir.const #cir.int<64> : !s16i
+  // LLVM: store i16 64, ptr %{{.*}}, align 2
+  // OGCG: store i16 64, ptr %{{.*}}, align 2
+  unsigned short _Accum usa = 0.5uhk;
+  // CIR:  %{{.*}} = cir.const #cir.int<128> : !u16i
+  // LLVM: store i16 128, ptr %{{.*}}, align 2
+  // OGCG: store i16 128, ptr %{{.*}}, align 2
+}
+
+void test_accum() {
+  // CIR:  cir.func{{.*}} @test_accum
+  // LLVM: void @test_accum
+  // OGCG: void @test_accum
+  _Accum a = 0.5k;
+  // CIR:  %{{.*}} = cir.const #cir.int<16384> : !s32i
+  // LLVM: store i32 16384, ptr %{{.*}}, align 4
+  // OGCG: store i32 16384, ptr %{{.*}}, align 4
+  unsigned _Accum ua = 0.5uk;
+  // CIR:  %{{.*}} = cir.const #cir.int<32768> : !u32i
+  // LLVM: store i32 32768, ptr %{{.*}}, align 4
+  // OGCG: store i32 32768, ptr %{{.*}}, align 4
+}
+
+void test_long_accum() {
+  // CIR:  cir.func{{.*}} @test_long_accum
+  // LLVM: void @test_long_accum
+  // OGCG: void @test_long_accum
+  long _Accum la = 0.5lk;
+  // CIR:  %{{.*}} = cir.const #cir.int<1073741824> : !s64i
+  // LLVM: store i64 1073741824, ptr %{{.*}}, align 8
+  // OGCG: store i64 1073741824, ptr %{{.*}}, align 8
+  unsigned long _Accum ula = 0.5ulk;
+  // CIR:  %{{.*}} = cir.const #cir.int<2147483648> : !u64i
+  // LLVM: store i64 2147483648, ptr %{{.*}}, align 8
+  // OGCG: store i64 2147483648, ptr %{{.*}}, align 8
+}
+
+void test_negative() {
+  // CIR:  cir.func{{.*}} @test_negative
+  // LLVM: void @test_negative
+  // OGCG: void @test_negative
+  short _Fract sf = -0.5hr;
+  // CIR:  %{{.*}} = cir.const #cir.int<-64> : !s8i
+  // LLVM: store i8 -64, ptr %{{.*}}, align 1
+  // OGCG: store i8 -64, ptr %{{.*}}, align 1
+  _Fract f = -0.5r;
+  // CIR:  %{{.*}} = cir.const #cir.int<-16384> : !s16i
+  // LLVM: store i16 -16384, ptr %{{.*}}, align 2
+  // OGCG: store i16 -16384, ptr %{{.*}}, align 2
+  long _Fract lf = -0.5lr;
+  // CIR:  %{{.*}} = cir.const #cir.int<-1073741824> : !s32i
+  // LLVM: store i32 -1073741824, ptr %{{.*}}, align 4
+  // OGCG: store i32 -1073741824, ptr %{{.*}}, align 4
+  short _Accum sa = -0.5hk;
+  // CIR:  %{{.*}} = cir.const #cir.int<-64> : !s16i
+  // LLVM: store i16 -64, ptr %{{.*}}, align 2
+  // OGCG: store i16 -64, ptr %{{.*}}, align 2
+  _Accum a = -0.5k;
+  // CIR:  %{{.*}} = cir.const #cir.int<-16384> : !s32i
+  // LLVM: store i32 -16384, ptr %{{.*}}, align 4
+  // OGCG: store i32 -16384, ptr %{{.*}}, align 4
+  long _Accum la = -0.5lk;
+  // CIR:  %{{.*}} = cir.const #cir.int<-1073741824> : !s64i
+  // LLVM: store i64 -1073741824, ptr %{{.*}}, align 8
+  // OGCG: store i64 -1073741824, ptr %{{.*}}, align 8
+}
+
+// FIXME: `FixedPointCast` in CIR is not supported.
+//        Only check valid for `_Sat` fixed point types,
+
+void test_sat_short_accum() {
+  // CIR:  cir.func{{.*}} @test_sat_short_accum
+  // LLVM: void @test_sat_short_accum
+  // OGCG: void @test_sat_short_accum
+  _Sat short _Accum ssa;
+  // CIR:  cir.alloca !s16i, !cir.ptr<!s16i>, ["ssa"]
+  // LLVM: alloca i16, i64 1, align 2
+  // OGCG: alloca i16, align 2
+  _Sat unsigned short _Accum susa;
+  // CIR:  cir.alloca !u16i, !cir.ptr<!u16i>, ["susa"]
+  // LLVM: alloca i16, i64 1, align 2
+  // OGCG: alloca i16, align 2
+}
+
+void test_sat_accum() {
+  // CIR:  cir.func{{.*}} @test_sat_accum
+  // LLVM: void @test_sat_accum
+  // OGCG: void @test_sat_accum
+  _Sat _Accum sa;
+  // CIR:  cir.alloca !s32i, !cir.ptr<!s32i>, ["sa"]
+  // LLVM: alloca i32, i64 1, align 4
+  // OGCG: alloca i32, align 4
+  _Sat unsigned _Accum sua;
+  // CIR:  cir.alloca !u32i, !cir.ptr<!u32i>, ["sua"]
+  // LLVM: alloca i32, i64 1, align 4
+  // OGCG: alloca i32, align 4
+}
+
+void test_sat_long_accum() {
+  // CIR:  cir.func{{.*}} @test_sat_long_accum
+  // LLVM: void @test_sat_long_accum
+  // OGCG: void @test_sat_long_accum
+  _Sat long _Accum sla;
+  // CIR:  cir.alloca !s64i, !cir.ptr<!s64i>, ["sla"]
+  // LLVM: alloca i64, i64 1, align 8
+  // OGCG: alloca i64, align 8
+  _Sat unsigned long _Accum sula;
+  // CIR:  cir.alloca !u64i, !cir.ptr<!u64i>, ["sula"]
+  // LLVM: alloca i64, i64 1, align 8
+  // OGCG: alloca i64, align 8
+}
+
+void test_sat_short_fract() {
+  // CIR:  cir.func{{.*}} @test_sat_short_fract
+  // LLVM: void @test_sat_short_fract
+  // OGCG: void @test_sat_short_fract
+  _Sat short _Fract ssf;
+  // CIR:  cir.alloca !s8i, !cir.ptr<!s8i>, ["ssf"]
+  // LLVM: alloca i8, i64 1, align 1
+  // OGCG: alloca i8, align 1
+  _Sat unsigned short _Fract susf;
+  // CIR:  cir.alloca !u8i, !cir.ptr<!u8i>, ["susf"]
+  // LLVM: alloca i8, i64 1, align 1
+  // OGCG: alloca i8, align 1
+}
+
+void test_sat_fract() {
+  // CIR:  cir.func{{.*}} @test_sat_fract
+  // LLVM: void @test_sat_fract
+  // OGCG: void @test_sat_fract
+  _Sat _Fract sf;
+  // CIR:  cir.alloca !s16i, !cir.ptr<!s16i>, ["sf"]
+  // LLVM: alloca i16, i64 1, align 2
+  // OGCG: alloca i16, align 2
+  _Sat unsigned _Fract suf;
+  // CIR:  cir.alloca !u16i, !cir.ptr<!u16i>, ["suf"]
+  // LLVM: alloca i16, i64 1, align 2
+  // OGCG: alloca i16, align 2
+}
+
+void test_sat_long_fract() {
+  // CIR:  cir.func{{.*}} @test_sat_long_fract
+  // LLVM: void @test_sat_long_fract
+  // OGCG: void @test_sat_long_fract
+  _Sat long _Fract slf;
+  // CIR:  cir.alloca !s32i, !cir.ptr<!s32i>, ["slf"]
+  // LLVM: alloca i32, i64 1, align 4
+  // OGCG: alloca i32, align 4
+  _Sat unsigned long _Fract sulf;
+  // CIR:  cir.alloca !u32i, !cir.ptr<!u32i>, ["sulf"]
+  // LLVM: alloca i32, i64 1, align 4
+  // OGCG: alloca i32, align 4
+}



More information about the cfe-commits mailing list