[clang] [CIR] Accept _BitInt up to 128 bits in x86_64 callconv lowering (PR #212668)

Adam Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 30 11:47:52 PDT 2026


================
@@ -0,0 +1,230 @@
+// RUN: cir-opt %s -cir-call-conv-lowering=target=x86_64 | FileCheck %s
+// RUN: cir-opt %s -cir-call-conv-lowering=target=x86_64 -cir-to-llvm -o - 2>/dev/null \
+// RUN:   | mlir-translate -mlir-to-llvmir --allow-unregistered-dialect \
+// RUN:   | FileCheck %s --check-prefix=LLVM
+
+!b17i = !cir.int<s, 17, bitint>
+!ub17i = !cir.int<u, 17, bitint>
+!b32i = !cir.int<s, 32, bitint>
+!b33i = !cir.int<s, 33, bitint>
+!ub33i = !cir.int<u, 33, bitint>
+!b40i = !cir.int<s, 40, bitint>
+!b63i = !cir.int<s, 63, bitint>
+!b64i = !cir.int<s, 64, bitint>
+!b65i = !cir.int<s, 65, bitint>
+!ub65i = !cir.int<u, 65, bitint>
+!b127i = !cir.int<s, 127, bitint>
+!b128i = !cir.int<s, 128, bitint>
+!s8i = !cir.int<s, 8>
+!rec_S = !cir.struct<"S" {!b17i}>
+!rec_P = !cir.struct<"P" {!b65i}>
+!rec_W = !cir.struct<"W" {!b128i}>
+!rec_O = !cir.struct<"O" {!b128i, !s8i}>
+!rec_Arr = !cir.struct<"Arr" {!cir.array<!b65i x 2>}>
+
+module attributes {
+  dlti.dl_spec = #dlti.dl_spec<
+    #dlti.dl_entry<i8, dense<8>: vector<2xi64>>,
+    #dlti.dl_entry<i16, dense<16>: vector<2xi64>>,
+    #dlti.dl_entry<i32, dense<32>: vector<2xi64>>,
+    #dlti.dl_entry<i64, dense<64>: vector<2xi64>>,
+    #dlti.dl_entry<i128, dense<128>: vector<2xi64>>>
+} {
+
+  // Sub-register signed _BitInt: Extend, one argument with signext.
+  cir.func @ret_b17(%arg0: !b17i) -> !b17i {
+    cir.return %arg0 : !b17i
+  }
+
+  // CHECK: cir.func{{.*}} @ret_b17(%arg0: !cir.int<s, 17, bitint> {llvm.signext}) -> (!cir.int<s, 17, bitint> {llvm.signext})
+
+  // Unsigned sub-register _BitInt: zeroext instead.
+  cir.func @ret_ub17(%arg0: !ub17i) -> !ub17i {
+    cir.return %arg0 : !ub17i
+  }
+
+  // CHECK: cir.func{{.*}} @ret_ub17(%arg0: !cir.int<u, 17, bitint> {llvm.zeroext}) -> (!cir.int<u, 17, bitint> {llvm.zeroext})
+
+  // Too wide to extend, and already the width the classifier names: unchanged.
+  cir.func @ret_b32(%arg0: !b32i) -> !b32i {
+    cir.return %arg0 : !b32i
+  }
+
+  // CHECK: cir.func{{.*}} @ret_b32(%arg0: !s32i_bitint) -> !s32i_bitint
+  // CHECK-NEXT: cir.return %arg0 : !s32i_bitint
+
+  // One bit wider is still one eightbyte, but the classifier names i64, so the
+  // value travels in that width and the narrower one is recovered from memory.
+  cir.func @ret_b33(%arg0: !b33i) -> !b33i {
+    cir.return %arg0 : !b33i
+  }
+
+  // CHECK: cir.func{{.*}} @ret_b33(%arg0: !u64i) -> !u64i
+  // CHECK: cir.cast bitcast %{{.*}} : !cir.ptr<!u64i> -> !cir.ptr<!cir.int<s, 33, bitint>>
----------------
adams381 wrote:

It was worse than that.  I left the operand wildcarded on that bitcast check, so it matched the argument slot and not the return slot, which means I wasn't pinning the extension either.  Neither the trunc nor the sext showed up in any check line, and everything I had on the LLVM side stopped at the `define`.

So I rewrote the file.  Every function now gets a `CHECK-LABEL` block for the CIR and an `LLVM-LABEL` block that walks the body, and wherever the value actually makes the round trip through memory both conversions are named:

```llvm
// LLVM-NEXT:    %[[WIDE:[0-9]+]] = load i64, ptr %[[ARGSLOT]]
// LLVM-NEXT:    %[[NARROW:[0-9]+]] = trunc i64 %[[WIDE]] to i33
// LLVM-NEXT:    %[[EXT:[0-9]+]] = sext i33 %[[NARROW]] to i64
```

https://github.com/llvm/llvm-project/pull/212668


More information about the cfe-commits mailing list