llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: adams381

<details>
<summary>Changes</summary>

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`.


---
Full diff: https://github.com/llvm/llvm-project/pull/203618.diff


5 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+3-3) 
- (modified) clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td (+4) 
- (modified) clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp (+15) 
- (modified) clang/test/CIR/CodeGenBuiltins/builtin-bit.cpp (+139) 
- (added) clang/test/CIR/IR/invalid-bit.cir (+12) 


``````````diff
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..049a7d5f255a0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1277,6 +1277,21 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl 
&gd, unsigned builtinID,
     return RValue::get(cir::ByteSwapOp::create(builder, loc, arg));
   }
 
+  case Builtin::BI__builtin_bswapg: {
+    mlir::Value arg = emitScalarExpr(e->getArg(0));
+    // A bool or any single-byte integer byte-swaps to itself; cir.byte_swap
+    // only accepts an unsigned integer whose width is a multiple of 16 bits.
+    auto argTy = mlir::dyn_cast<cir::IntType>(arg.getType());
+    if (!argTy || argTy.getWidth() <= 8)
+      return RValue::get(arg);
+    // 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_bitreverse8:
   case Builtin::BI__builtin_bitreverse16:
   case Builtin::BI__builtin_bitreverse32:
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
+  }
+}

``````````

</details>


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

Reply via email to