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
