Author: adams381
Date: 2026-06-15T15:58:17-05:00
New Revision: 9ffa02dbaa065dee20ab4d604a3eed042f0b3cc4

URL: 
https://github.com/llvm/llvm-project/commit/9ffa02dbaa065dee20ab4d604a3eed042f0b3cc4
DIFF: 
https://github.com/llvm/llvm-project/commit/9ffa02dbaa065dee20ab4d604a3eed042f0b3cc4.diff

LOG: [CIR] Lower __builtin_bswapg (#203618)

C++23 `std::byteswap` lowers every value wider than a single byte
through the type-generic `__builtin_bswapg` builtin, which CIRGen had no
case for, so `std/numerics/bit/byteswap.pass.cpp` hit `errorBuiltinNYI`.

This handles `__builtin_bswapg` the way classic CodeGen does
(`CGBuiltin.cpp`): a bool or single-byte integer is returned unchanged,
and wider values go through `cir.byte_swap`. Unlike the unsigned-only
`__builtin_bswap16/32/64`, the generic builtin also accepts signed
operands, so a signed argument is reinterpreted as unsigned of the same
width before the swap and cast back afterward.

`cir.byte_swap` previously accepted only 16/32/64-bit operands, but
`std::byteswap` instantiates it for `__int128` and wide `_BitInt` too
(the libc++ test reaches `_BitInt(256)`). The operand constraint is
widened to any unsigned integer whose width is a multiple of 16 bits --
which is what `llvm.bswap` requires -- and the existing CIR-to-LLVM
lowering already handles any such width. With the fix,
`byteswap.pass.cpp` passes under `-fclangir`.

Added: 
    clang/test/CIR/IR/invalid-bit.cir

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
    clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
    clang/test/CIR/CodeGenBuiltins/builtin-bit.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 557e279d9bc71..9dae3534991e5 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -6305,15 +6305,15 @@ def CIR_BitReverseOp : CIR_BitOpBase<"bitreverse",
 }
 
 def CIR_ByteSwapOp : CIR_BitOpBase<"byte_swap",
-  CIR_UIntOfWidths<[16, 32, 64]>
+  CIR_UIntMultipleOf16
 > {
   let summary = "Reverse the bytes in the object representation of the 
operand";
   let description = [{
     The `cir.byte_swap` operation takes an integer as operand, reverse the 
bytes
     in the object representation of the operand integer, and returns the 
result.
 
-    The operand integer must be an unsigned integer. Its widths must be either
-    16, 32, or 64.
+    The operand integer must be an unsigned integer whose width is a multiple 
of
+    16 bits (e.g. 16, 32, 64, 128, or a wider `_BitInt`).
 
     Example:
 

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index 322eec853d821..a8643ec21af80 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -82,6 +82,10 @@ class CIR_UIntOfWidths<list<int> widths>
     : CIR_ConfinedType<CIR_AnyUIntType, [CIR_IntOfWidthsPred<widths>],
         "unsigned integer type of widths " # !interleave(widths, "/")>;
 
+def CIR_UIntMultipleOf16
+    : CIR_ConfinedType<CIR_AnyUIntType, [CPred<"$_self.getWidth() % 16 == 0">],
+        "unsigned integer type with a width that is a multiple of 16 bits">;
+
 class CIR_UInt<int width>
     : CIR_ConfinedType<CIR_AnyUIntType, [CIR_HasWidthPred<width>],
         width # "-bit unsigned integer">,

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index e6de3954a8c69..a483eb635f0e2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1267,6 +1267,27 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
     return RValue::get(result);
   }
 
+  case Builtin::BI__builtin_bswapg: {
+    mlir::Value arg = emitScalarExpr(e->getArg(0));
+    // CIR models bool as cir.bool rather than an integer, so peel it off
+    // before the cast below.  Like classic codegen's i1 case, it byte-swaps
+    // to itself.
+    if (mlir::isa<cir::BoolType>(arg.getType()))
+      return RValue::get(arg);
+    auto argTy = mlir::cast<cir::IntType>(arg.getType());
+    // A single bit or a single byte byte-swaps to itself.
+    if (argTy.getWidth() == 1 || argTy.getWidth() == 8)
+      return RValue::get(arg);
+    assert(argTy.getWidth() % 16 == 0 &&
+           "__builtin_bswapg requires a single byte or a multiple of 16 bits");
+    // cir.byte_swap requires an unsigned operand.  Reinterpret a signed
+    // argument as unsigned of the same width; createBuiltinBitOp casts the
+    // swapped result back to the builtin's (possibly signed) return type.
+    if (argTy.isSigned())
+      arg = builder.createIntCast(arg, builder.getUIntNTy(argTy.getWidth()));
+    return RValue::get(createBuiltinBitOp<cir::ByteSwapOp>(*this, e, arg));
+  }
+
   case Builtin::BI__builtin_bswap16:
   case Builtin::BI__builtin_bswap32:
   case Builtin::BI__builtin_bswap64:

diff  --git a/clang/test/CIR/CodeGenBuiltins/builtin-bit.cpp 
b/clang/test/CIR/CodeGenBuiltins/builtin-bit.cpp
index 23e9c77e11130..2d3900caeca3a 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-bit.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-bit.cpp
@@ -550,6 +550,145 @@ unsigned long long test_builtin_bswap64(unsigned long 
long x) {
 // OGCG-LABEL: @_Z20test_builtin_bswap64y
 // OGCG:         %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
 
+unsigned short test_bswapg_u16(unsigned short x) {
+  return __builtin_bswapg(x);
+}
+
+// CIR-LABEL: @_Z15test_bswapg_u16t
+// CIR:         %{{.+}} = cir.byte_swap %{{.+}} : !u16i
+
+// LLVM-LABEL: @_Z15test_bswapg_u16t
+// LLVM:         %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
+
+// OGCG-LABEL: @_Z15test_bswapg_u16t
+// OGCG:         %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
+
+short test_bswapg_s16(short x) {
+  return __builtin_bswapg(x);
+}
+
+// A signed operand is reinterpreted as unsigned of the same width, byte
+// swapped, then cast back to the signed return type.
+// CIR-LABEL: @_Z15test_bswapg_s16s
+// CIR:         %[[ARG:.+]] = cir.cast integral %{{.+}} : !s16i -> !u16i
+// CIR:         %[[RES:.+]] = cir.byte_swap %[[ARG]] : !u16i
+// CIR:         %{{.+}} = cir.cast integral %[[RES]] : !u16i -> !s16i
+
+// LLVM-LABEL: @_Z15test_bswapg_s16s
+// LLVM:         %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
+
+// OGCG-LABEL: @_Z15test_bswapg_s16s
+// OGCG:         %{{.+}} = call i16 @llvm.bswap.i16(i16 %{{.+}})
+
+unsigned test_bswapg_u32(unsigned x) {
+  return __builtin_bswapg(x);
+}
+
+// CIR-LABEL: @_Z15test_bswapg_u32j
+// CIR:         %{{.+}} = cir.byte_swap %{{.+}} : !u32i
+
+// LLVM-LABEL: @_Z15test_bswapg_u32j
+// LLVM:         %{{.+}} = call i32 @llvm.bswap.i32(i32 %{{.+}})
+
+// OGCG-LABEL: @_Z15test_bswapg_u32j
+// OGCG:         %{{.+}} = call i32 @llvm.bswap.i32(i32 %{{.+}})
+
+unsigned long long test_bswapg_u64(unsigned long long x) {
+  return __builtin_bswapg(x);
+}
+
+// CIR-LABEL: @_Z15test_bswapg_u64y
+// CIR:         %{{.+}} = cir.byte_swap %{{.+}} : !u64i
+
+// LLVM-LABEL: @_Z15test_bswapg_u64y
+// LLVM:         %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
+
+// OGCG-LABEL: @_Z15test_bswapg_u64y
+// OGCG:         %{{.+}} = call i64 @llvm.bswap.i64(i64 %{{.+}})
+
+unsigned __int128 test_bswapg_u128(unsigned __int128 x) {
+  return __builtin_bswapg(x);
+}
+
+// CIR-LABEL: @_Z16test_bswapg_u128o
+// CIR:         %{{.+}} = cir.byte_swap %{{.+}} : !u128i
+
+// LLVM-LABEL: @_Z16test_bswapg_u128o
+// LLVM:         %{{.+}} = call i128 @llvm.bswap.i128(i128 %{{.+}})
+
+// OGCG-LABEL: @_Z16test_bswapg_u128o
+// OGCG:         %{{.+}} = call i128 @llvm.bswap.i128(i128 %{{.+}})
+
+unsigned _BitInt(256) test_bswapg_u256(unsigned _BitInt(256) x) {
+  return __builtin_bswapg(x);
+}
+
+// A wide _BitInt exercises the multiple-of-16 width constraint on the op.
+// CIR-LABEL: @_Z16test_bswapg_u256DU256_
+// CIR:         %{{.+}} = cir.byte_swap %{{.+}} : !u256i_bitint
+
+// LLVM-LABEL: @_Z16test_bswapg_u256DU256_
+// LLVM:         %{{.+}} = call i256 @llvm.bswap.i256(i256 %{{.+}})
+
+// OGCG-LABEL: @_Z16test_bswapg_u256DU256_
+// OGCG:         %{{.+}} = call i256 @llvm.bswap.i256(i256 %{{.+}})
+
+_BitInt(256) test_bswapg_s256(_BitInt(256) x) {
+  return __builtin_bswapg(x);
+}
+
+// CIR-LABEL: @_Z16test_bswapg_s256DB256_
+// CIR:         %[[ARG:.+]] = cir.cast integral %{{.+}} : !s256i_bitint -> 
!u256i
+// CIR:         %[[RES:.+]] = cir.byte_swap %[[ARG]] : !u256i
+// CIR:         %{{.+}} = cir.cast integral %[[RES]] : !u256i -> !s256i_bitint
+
+// LLVM-LABEL: @_Z16test_bswapg_s256DB256_
+// LLVM:         %{{.+}} = call i256 @llvm.bswap.i256(i256 %{{.+}})
+
+// OGCG-LABEL: @_Z16test_bswapg_s256DB256_
+// OGCG:         %{{.+}} = call i256 @llvm.bswap.i256(i256 %{{.+}})
+
+unsigned char test_bswapg_u8(unsigned char x) {
+  return __builtin_bswapg(x);
+}
+
+// A single-byte swap is the identity, so no cir.byte_swap / llvm.bswap.
+// CIR-LABEL: @_Z14test_bswapg_u8h
+// CIR-NOT:     cir.byte_swap
+
+// LLVM-LABEL: @_Z14test_bswapg_u8h
+// LLVM-NOT:    @llvm.bswap
+
+// OGCG-LABEL: @_Z14test_bswapg_u8h
+// OGCG-NOT:    @llvm.bswap
+
+bool test_bswapg_bool(bool x) {
+  return __builtin_bswapg(x);
+}
+
+// A bool is a single-byte value, so it is returned unchanged.
+// CIR-LABEL: @_Z16test_bswapg_boolb
+// CIR-NOT:     cir.byte_swap
+
+// LLVM-LABEL: @_Z16test_bswapg_boolb
+// LLVM-NOT:    @llvm.bswap
+
+// OGCG-LABEL: @_Z16test_bswapg_boolb
+// OGCG-NOT:    @llvm.bswap
+
+signed char test_bswapg_s8(signed char x) {
+  return __builtin_bswapg(x);
+}
+
+// CIR-LABEL: @_Z14test_bswapg_s8a
+// CIR-NOT:     cir.byte_swap
+
+// LLVM-LABEL: @_Z14test_bswapg_s8a
+// LLVM-NOT:    @llvm.bswap
+
+// OGCG-LABEL: @_Z14test_bswapg_s8a
+// OGCG-NOT:    @llvm.bswap
+
 unsigned char test_builtin_rotateleft8(unsigned char x, unsigned char y) {
   return __builtin_rotateleft8(x, y);
 }

diff  --git a/clang/test/CIR/IR/invalid-bit.cir 
b/clang/test/CIR/IR/invalid-bit.cir
new file mode 100644
index 0000000000000..5b918c95996ce
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-bit.cir
@@ -0,0 +1,12 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+// cir.byte_swap requires an unsigned integer whose width is a multiple of 16
+// bits.
+
+module {
+  cir.func @bad_byte_swap_width(%x : !cir.int<u, 24>) {
+    // expected-error@+1 {{'cir.byte_swap' op operand #0 must be unsigned 
integer type with a width that is a multiple of 16 bits, but got '!cir.int<u, 
24>'}}
+    %0 = cir.byte_swap %x : !cir.int<u, 24>
+    cir.return
+  }
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to