https://github.com/pedroclobo updated https://github.com/llvm/llvm-project/pull/230827
>From cb11298e860fe5550fc6fa5f0623cbe56c92bb31 Mon Sep 17 00:00:00 2001 From: Pedro Lobo <[email protected]> Date: Sat, 10 Oct 2026 17:47:27 +0100 Subject: [PATCH] [llubi] Add support for `bitinsert` and `bitextract` --- .../tools/llubi/bitinsert_bitextract_be.ll | 78 +++++++++++++++++++ .../tools/llubi/bitinsert_bitextract_le.ll | 78 +++++++++++++++++++ llvm/tools/llubi/lib/Context.cpp | 19 +++++ llvm/tools/llubi/lib/Context.h | 7 ++ llvm/tools/llubi/lib/Interpreter.cpp | 29 +++++++ 5 files changed, 211 insertions(+) create mode 100644 llvm/test/tools/llubi/bitinsert_bitextract_be.ll create mode 100644 llvm/test/tools/llubi/bitinsert_bitextract_le.ll diff --git a/llvm/test/tools/llubi/bitinsert_bitextract_be.ll b/llvm/test/tools/llubi/bitinsert_bitextract_be.ll new file mode 100644 index 0000000000000..8b867e2c026a8 --- /dev/null +++ b/llvm/test/tools/llubi/bitinsert_bitextract_be.ll @@ -0,0 +1,78 @@ +; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6 +; RUN: llubi --verbose < %s 2>&1 | FileCheck %s +target datalayout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64" + +define void @main() { +entry: + %bitextract_aligned = bitextract i8, b32 u0x12345678, i32 8 + %bitextract_unaligned = bitextract i8, b32 u0x12345678, i32 4 + %bitextract_i3 = bitextract i3, b32 u0x12345678, i32 6 + %bitextract_float = bitextract float, b64 u0x3F80000000000000, i32 32 + %bitextract_b12 = bitextract b12, b32 u0x12345678, i32 10 + %bitextract_oob = bitextract i8, b32 u0x12345678, i32 25 + %bitextract_oob_wrap = bitextract i8, b32 u0x12345678, i32 -1 + %bitextract_poison_offset = bitextract i8, b32 u0x12345678, i32 poison + %bitextract_poison_src = bitextract i8, b32 poison, i32 0 + + %bitinsert_aligned = bitinsert b32 u0x12345678, i8 0, i32 8 + %bitinsert_unaligned = bitinsert b32 u0x12345678, i8 -1, i32 4 + %bitinsert_float = bitinsert b64 0, float 1.0, i32 32 + %bitinsert_b8 = bitinsert b32 0, b8 -1, i32 12 + %bitinsert_b12 = bitinsert b12 0, i4 -1, i32 8 + %bitinsert_oob = bitinsert b32 u0x12345678, i8 0, i32 25 + %bitinsert_poison_offset = bitinsert b32 u0x12345678, i8 0, i32 poison + %bitinsert_poison = bitinsert b32 0, i16 poison, i32 4 + + %bitextract_poison_overlap = bitextract i8, b32 %bitinsert_poison, i32 16 + %bitextract_poison_disjoint = bitextract i8, b32 %bitinsert_poison, i32 20 + %bitextract_poison_overlap_b8 = bitextract b8, b32 %bitinsert_poison, i32 16 + + %ptr = alloca i32 + %bitinsert_ptr = bitinsert b128 0, ptr %ptr, i32 32 + %bitextract_ptr = bitextract ptr, b128 %bitinsert_ptr, i32 32 + %bitextract_ptr_shifted = bitextract ptr, b128 %bitinsert_ptr, i32 31 + %bitextract_ptr_addr = bitextract i64, b128 %bitinsert_ptr, i32 32 + %bitinsert_ptr_clobber = bitinsert b128 %bitinsert_ptr, i8 0, i32 40 + %bitextract_ptr_clobbered = bitextract ptr, b128 %bitinsert_ptr_clobber, i32 32 + %bitinsert_ptr_unaligned = bitinsert b128 0, ptr %ptr, i32 3 + %bitextract_ptr_unaligned = bitextract ptr, b128 %bitinsert_ptr_unaligned, i32 3 + %bitextract_ptr_bytes = bitextract b64, b128 %bitinsert_ptr, i32 32 + %bitinsert_ptr_bytes = bitinsert b128 0, b64 %bitextract_ptr_bytes, i32 5 + %bitextract_ptr_via_bytes = bitextract ptr, b128 %bitinsert_ptr_bytes, i32 5 + ret void +} +; CHECK: Entering function: main +; CHECK-NEXT: %bitextract_aligned = bitextract i8, b32 305419896, i32 8 => i8 86 +; CHECK-NEXT: %bitextract_unaligned = bitextract i8, b32 305419896, i32 4 => i8 103 +; CHECK-NEXT: %bitextract_i3 = bitextract i3, b32 305419896, i32 6 => i3 1 +; CHECK-NEXT: %bitextract_float = bitextract float, b64 4575657221408423936, i32 32 => float 1.000000e+00 +; CHECK-NEXT: %bitextract_b12 = bitextract b12, b32 305419896, i32 10 => b12 1101 0x15 +; CHECK-NEXT: %bitextract_oob = bitextract i8, b32 305419896, i32 25 => poison +; CHECK-NEXT: %bitextract_oob_wrap = bitextract i8, b32 305419896, i32 -1 => poison +; CHECK-NEXT: %bitextract_poison_offset = bitextract i8, b32 305419896, i32 poison => poison +; CHECK-NEXT: %bitextract_poison_src = bitextract i8, b32 poison, i32 0 => poison +; CHECK-NEXT: %bitinsert_aligned = bitinsert b32 305419896, i8 0, i32 8 => b32 0x12 0x34 0x00 0x78 +; CHECK-NEXT: %bitinsert_unaligned = bitinsert b32 305419896, i8 -1, i32 4 => b32 0x12 0x34 0x5F 0xF8 +; CHECK-NEXT: %bitinsert_float = bitinsert b64 0, float 1.000000e+00, i32 32 => b64 0x3F 0x80 0x00 0x00 0x00 0x00 0x00 0x00 +; CHECK-NEXT: %bitinsert_b8 = bitinsert b32 0, b8 -1, i32 12 => b32 0x00 0x0F 0xF0 0x00 +; CHECK-NEXT: %bitinsert_b12 = bitinsert b12 0, i4 -1, i32 8 => b12 1111 0x00 +; CHECK-NEXT: %bitinsert_oob = bitinsert b32 305419896, i8 0, i32 25 => b32 0x!! 0x!! 0x!! 0x!! +; CHECK-NEXT: %bitinsert_poison_offset = bitinsert b32 305419896, i8 0, i32 poison => b32 0x!! 0x!! 0x!! 0x!! +; CHECK-NEXT: %bitinsert_poison = bitinsert b32 0, i16 poison, i32 4 => b32 0x00 0000!!!! 0x!! !!!!0000 +; CHECK-NEXT: %bitextract_poison_overlap = bitextract i8, b32 %bitinsert_poison, i32 16 => poison +; CHECK-NEXT: %bitextract_poison_disjoint = bitextract i8, b32 %bitinsert_poison, i32 20 => i8 0 +; CHECK-NEXT: %bitextract_poison_overlap_b8 = bitextract b8, b32 %bitinsert_poison, i32 16 => b8 0000!!!! +; CHECK-NEXT: %ptr = alloca i32, align 4 => ptr 0x8 [ptr] +; CHECK-NEXT: %bitinsert_ptr = bitinsert b128 0, ptr %ptr, i32 32 => b128 0x00 0x00 0x00 0x00 00000000(00101000) 00000000(11101000) 00000000(00110111) 00000000(11000101) 00000000(11001011) 00000000(01000001) 00000000(11011100) 00001000(00111110) 0x00 0x00 0x00 0x00 +; CHECK-NEXT: %bitextract_ptr = bitextract ptr, b128 %bitinsert_ptr, i32 32 => ptr 0x8 [ptr] +; CHECK-NEXT: %bitextract_ptr_shifted = bitextract ptr, b128 %bitinsert_ptr, i32 31 => ptr 0x10 [nullary] +; CHECK-NEXT: %bitextract_ptr_addr = bitextract i64, b128 %bitinsert_ptr, i32 32 => i64 8 +; CHECK-NEXT: %bitinsert_ptr_clobber = bitinsert b128 %bitinsert_ptr, i8 0, i32 40 => b128 0x00 0x00 0x00 0x00 00000000(00101000) 00000000(11101000) 00000000(00110111) 00000000(11000101) 00000000(11001011) 00000000(01000001) 0x00 00001000(00111110) 0x00 0x00 0x00 0x00 +; CHECK-NEXT: %bitextract_ptr_clobbered = bitextract ptr, b128 %bitinsert_ptr_clobber, i32 32 => ptr 0x8 [nullary] +; CHECK-NEXT: %bitinsert_ptr_unaligned = bitinsert b128 0, ptr %ptr, i32 3 => b128 0x00 0x00 0x00 0x00 0x00 0x00 0x00 00000000(!!!!!001) 00000000(01000111) 00000000(01000001) 00000000(10111110) 00000000(00101110) 00000000(01011010) 00000000(00001110) 00000000(11100001) 01000000(11110!!!) +; CHECK-NEXT: %bitextract_ptr_unaligned = bitextract ptr, b128 %bitinsert_ptr_unaligned, i32 3 => ptr 0x8 [ptr] +; CHECK-NEXT: %bitextract_ptr_bytes = bitextract b64, b128 %bitinsert_ptr, i32 32 => b64 ptr 0x8 [ptr] +; CHECK-NEXT: %bitinsert_ptr_bytes = bitinsert b128 0, b64 %bitextract_ptr_bytes, i32 5 => b128 0x00 0x00 0x00 0x00 0x00 0x00 0x00 00000000(!!!00101) 00000000(00011101) 00000000(00000110) 00000000(11111000) 00000000(10111001) 00000000(01101000) 00000000(00111011) 00000001(10000111) 00000000(110!!!!!) +; CHECK-NEXT: %bitextract_ptr_via_bytes = bitextract ptr, b128 %bitinsert_ptr_bytes, i32 5 => ptr 0x8 [ptr] +; CHECK-NEXT: ret void +; CHECK-NEXT: Exiting function: main diff --git a/llvm/test/tools/llubi/bitinsert_bitextract_le.ll b/llvm/test/tools/llubi/bitinsert_bitextract_le.ll new file mode 100644 index 0000000000000..4561aebb931df --- /dev/null +++ b/llvm/test/tools/llubi/bitinsert_bitextract_le.ll @@ -0,0 +1,78 @@ +; NOTE: Assertions have been autogenerated by utils/update_llubi_test_checks.py UTC_ARGS: --version 6 +; RUN: llubi --verbose < %s 2>&1 | FileCheck %s +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64" + +define void @main() { +entry: + %bitextract_aligned = bitextract i8, b32 u0x12345678, i32 8 + %bitextract_unaligned = bitextract i8, b32 u0x12345678, i32 4 + %bitextract_i3 = bitextract i3, b32 u0x12345678, i32 6 + %bitextract_float = bitextract float, b64 u0x3F80000000000000, i32 32 + %bitextract_b12 = bitextract b12, b32 u0x12345678, i32 10 + %bitextract_oob = bitextract i8, b32 u0x12345678, i32 25 + %bitextract_oob_wrap = bitextract i8, b32 u0x12345678, i32 -1 + %bitextract_poison_offset = bitextract i8, b32 u0x12345678, i32 poison + %bitextract_poison_src = bitextract i8, b32 poison, i32 0 + + %bitinsert_aligned = bitinsert b32 u0x12345678, i8 0, i32 8 + %bitinsert_unaligned = bitinsert b32 u0x12345678, i8 -1, i32 4 + %bitinsert_float = bitinsert b64 0, float 1.0, i32 32 + %bitinsert_b8 = bitinsert b32 0, b8 -1, i32 12 + %bitinsert_b12 = bitinsert b12 0, i4 -1, i32 8 + %bitinsert_oob = bitinsert b32 u0x12345678, i8 0, i32 25 + %bitinsert_poison_offset = bitinsert b32 u0x12345678, i8 0, i32 poison + %bitinsert_poison = bitinsert b32 0, i16 poison, i32 4 + + %bitextract_poison_overlap = bitextract i8, b32 %bitinsert_poison, i32 16 + %bitextract_poison_disjoint = bitextract i8, b32 %bitinsert_poison, i32 20 + %bitextract_poison_overlap_b8 = bitextract b8, b32 %bitinsert_poison, i32 16 + + %ptr = alloca i32 + %bitinsert_ptr = bitinsert b128 0, ptr %ptr, i32 32 + %bitextract_ptr = bitextract ptr, b128 %bitinsert_ptr, i32 32 + %bitextract_ptr_shifted = bitextract ptr, b128 %bitinsert_ptr, i32 31 + %bitextract_ptr_addr = bitextract i64, b128 %bitinsert_ptr, i32 32 + %bitinsert_ptr_clobber = bitinsert b128 %bitinsert_ptr, i8 0, i32 40 + %bitextract_ptr_clobbered = bitextract ptr, b128 %bitinsert_ptr_clobber, i32 32 + %bitinsert_ptr_unaligned = bitinsert b128 0, ptr %ptr, i32 3 + %bitextract_ptr_unaligned = bitextract ptr, b128 %bitinsert_ptr_unaligned, i32 3 + %bitextract_ptr_bytes = bitextract b64, b128 %bitinsert_ptr, i32 32 + %bitinsert_ptr_bytes = bitinsert b128 0, b64 %bitextract_ptr_bytes, i32 5 + %bitextract_ptr_via_bytes = bitextract ptr, b128 %bitinsert_ptr_bytes, i32 5 + ret void +} +; CHECK: Entering function: main +; CHECK-NEXT: %bitextract_aligned = bitextract i8, b32 305419896, i32 8 => i8 86 +; CHECK-NEXT: %bitextract_unaligned = bitextract i8, b32 305419896, i32 4 => i8 103 +; CHECK-NEXT: %bitextract_i3 = bitextract i3, b32 305419896, i32 6 => i3 1 +; CHECK-NEXT: %bitextract_float = bitextract float, b64 4575657221408423936, i32 32 => float 1.000000e+00 +; CHECK-NEXT: %bitextract_b12 = bitextract b12, b32 305419896, i32 10 => b12 0x15 1101 +; CHECK-NEXT: %bitextract_oob = bitextract i8, b32 305419896, i32 25 => poison +; CHECK-NEXT: %bitextract_oob_wrap = bitextract i8, b32 305419896, i32 -1 => poison +; CHECK-NEXT: %bitextract_poison_offset = bitextract i8, b32 305419896, i32 poison => poison +; CHECK-NEXT: %bitextract_poison_src = bitextract i8, b32 poison, i32 0 => poison +; CHECK-NEXT: %bitinsert_aligned = bitinsert b32 305419896, i8 0, i32 8 => b32 0x78 0x00 0x34 0x12 +; CHECK-NEXT: %bitinsert_unaligned = bitinsert b32 305419896, i8 -1, i32 4 => b32 0xF8 0x5F 0x34 0x12 +; CHECK-NEXT: %bitinsert_float = bitinsert b64 0, float 1.000000e+00, i32 32 => b64 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x3F +; CHECK-NEXT: %bitinsert_b8 = bitinsert b32 0, b8 -1, i32 12 => b32 0x00 0xF0 0x0F 0x00 +; CHECK-NEXT: %bitinsert_b12 = bitinsert b12 0, i4 -1, i32 8 => b12 0x00 1111 +; CHECK-NEXT: %bitinsert_oob = bitinsert b32 305419896, i8 0, i32 25 => b32 0x!! 0x!! 0x!! 0x!! +; CHECK-NEXT: %bitinsert_poison_offset = bitinsert b32 305419896, i8 0, i32 poison => b32 0x!! 0x!! 0x!! 0x!! +; CHECK-NEXT: %bitinsert_poison = bitinsert b32 0, i16 poison, i32 4 => b32 !!!!0000 0x!! 0000!!!! 0x00 +; CHECK-NEXT: %bitextract_poison_overlap = bitextract i8, b32 %bitinsert_poison, i32 16 => poison +; CHECK-NEXT: %bitextract_poison_disjoint = bitextract i8, b32 %bitinsert_poison, i32 20 => i8 0 +; CHECK-NEXT: %bitextract_poison_overlap_b8 = bitextract b8, b32 %bitinsert_poison, i32 16 => b8 0000!!!! +; CHECK-NEXT: %ptr = alloca i32, align 4 => ptr 0x8 [ptr] +; CHECK-NEXT: %bitinsert_ptr = bitinsert b128 0, ptr %ptr, i32 32 => b128 0x00 0x00 0x00 0x00 00001000(00111110) 00000000(11011100) 00000000(01000001) 00000000(11001011) 00000000(11000101) 00000000(00110111) 00000000(11101000) 00000000(00101000) 0x00 0x00 0x00 0x00 +; CHECK-NEXT: %bitextract_ptr = bitextract ptr, b128 %bitinsert_ptr, i32 32 => ptr 0x8 [ptr] +; CHECK-NEXT: %bitextract_ptr_shifted = bitextract ptr, b128 %bitinsert_ptr, i32 31 => ptr 0x10 [nullary] +; CHECK-NEXT: %bitextract_ptr_addr = bitextract i64, b128 %bitinsert_ptr, i32 32 => i64 8 +; CHECK-NEXT: %bitinsert_ptr_clobber = bitinsert b128 %bitinsert_ptr, i8 0, i32 40 => b128 0x00 0x00 0x00 0x00 00001000(00111110) 0x00 00000000(01000001) 00000000(11001011) 00000000(11000101) 00000000(00110111) 00000000(11101000) 00000000(00101000) 0x00 0x00 0x00 0x00 +; CHECK-NEXT: %bitextract_ptr_clobbered = bitextract ptr, b128 %bitinsert_ptr_clobber, i32 32 => ptr 0x8 [nullary] +; CHECK-NEXT: %bitinsert_ptr_unaligned = bitinsert b128 0, ptr %ptr, i32 3 => b128 01000000(11110!!!) 00000000(11100001) 00000000(00001110) 00000000(01011010) 00000000(00101110) 00000000(10111110) 00000000(01000001) 00000000(01000111) 00000000(!!!!!001) 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +; CHECK-NEXT: %bitextract_ptr_unaligned = bitextract ptr, b128 %bitinsert_ptr_unaligned, i32 3 => ptr 0x8 [ptr] +; CHECK-NEXT: %bitextract_ptr_bytes = bitextract b64, b128 %bitinsert_ptr, i32 32 => b64 ptr 0x8 [ptr] +; CHECK-NEXT: %bitinsert_ptr_bytes = bitinsert b128 0, b64 %bitextract_ptr_bytes, i32 5 => b128 00000000(110!!!!!) 00000001(10000111) 00000000(00111011) 00000000(01101000) 00000000(10111001) 00000000(11111000) 00000000(00000110) 00000000(00011101) 00000000(!!!00101) 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +; CHECK-NEXT: %bitextract_ptr_via_bytes = bitextract ptr, b128 %bitinsert_ptr_bytes, i32 5 => ptr 0x8 [ptr] +; CHECK-NEXT: ret void +; CHECK-NEXT: Exiting function: main diff --git a/llvm/tools/llubi/lib/Context.cpp b/llvm/tools/llubi/lib/Context.cpp index 1650d09fa1456..db7bb1a424b87 100644 --- a/llvm/tools/llubi/lib/Context.cpp +++ b/llvm/tools/llubi/lib/Context.cpp @@ -829,6 +829,25 @@ void Context::toBytes(const AnyValue &Val, Type *Ty, llvm_unreachable("Unsupported first class type."); } +AnyValue Context::extractBits(const ByteValue &Src, uint32_t OffsetInBits, + Type *Ty) { + assert(OffsetInBits + DL.getTypeSizeInBits(Ty).getFixedValue() <= + Src.getBitWidth() && + "Bit range out of bounds"); + return fromBytes(ConstBytesView(Src.bytes(), DL), Ty, OffsetInBits, + /*CheckPaddingBits=*/false, + /*ContainsUndefinedBits=*/nullptr); +} + +void Context::insertBits(ByteValue &Base, uint32_t OffsetInBits, + const AnyValue &Val, Type *Ty) { + assert(OffsetInBits + DL.getTypeSizeInBits(Ty).getFixedValue() <= + Base.getBitWidth() && + "Bit range out of bounds"); + toBytes(Val, Ty, OffsetInBits, MutableBytesView(Base.mutableBytes(), DL), + /*PaddingBits=*/false); +} + AnyValue Context::load(MemoryObject &MO, uint64_t Offset, Type *ValTy, bool *ContainsUndefinedBits) { return fromBytes( diff --git a/llvm/tools/llubi/lib/Context.h b/llvm/tools/llubi/lib/Context.h index 571be140cc83f..5d1a41e66bb3e 100644 --- a/llvm/tools/llubi/lib/Context.h +++ b/llvm/tools/llubi/lib/Context.h @@ -420,6 +420,13 @@ class Context { bool *ContainsUndefinedBits = nullptr); /// Convert a value to byte sequence. Padding bits are set to zero. void toBytes(const AnyValue &Val, Type *Ty, MutableArrayRef<Byte> Bytes); + /// Read the bits [OffsetInBits, OffsetInBits + bitwidth(Ty)) of \p Src as a + /// value of type \p Ty. The bit range must be within \p Src. + AnyValue extractBits(const ByteValue &Src, uint32_t OffsetInBits, Type *Ty); + /// Overwrite the bits [OffsetInBits, OffsetInBits + bitwidth(Ty)) of \p Base + /// with \p Val. The bit range must be within \p Base. + void insertBits(ByteValue &Base, uint32_t OffsetInBits, const AnyValue &Val, + Type *Ty); /// Direct memory load without checks. AnyValue load(MemoryObject &MO, uint64_t Offset, Type *ValTy, bool *ContainsUndefinedBits = nullptr); diff --git a/llvm/tools/llubi/lib/Interpreter.cpp b/llvm/tools/llubi/lib/Interpreter.cpp index 1154804b10d23..e7ef61ce86036 100644 --- a/llvm/tools/llubi/lib/Interpreter.cpp +++ b/llvm/tools/llubi/lib/Interpreter.cpp @@ -2899,6 +2899,35 @@ class InstExecutor : public InstVisitor<InstExecutor, void>, setResult(BCI, Ctx.fromBytes(Bytes, BCI.getType())); } + void visitBitInsertInst(BitInsertInst &BII) { + ByteValue Res = getValue(BII.getOperand(0)).asByte(); + auto &Val = getValue(BII.getOperand(1)); + auto &Offset = getValue(BII.getOperand(2)); + Type *ValTy = BII.getOperand(1)->getType(); + uint64_t NumBits = DL.getTypeSizeInBits(ValTy).getFixedValue(); + uint64_t OffsetInBits = + Offset.isPoison() ? 0 : Offset.asInteger().getZExtValue(); + if (Offset.isPoison() || OffsetInBits + NumBits > Res.getBitWidth()) { + setResult(BII, AnyValue::getPoisonValue(Ctx, BII.getType())); + return; + } + Ctx.insertBits(Res, OffsetInBits, Val, ValTy); + setResult(BII, std::move(Res)); + } + + void visitBitExtractInst(BitExtractInst &BEI) { + auto &Src = getValue(BEI.getOperand(0)).asByte(); + auto &Offset = getValue(BEI.getOperand(1)); + uint64_t NumBits = DL.getTypeSizeInBits(BEI.getType()).getFixedValue(); + uint64_t OffsetInBits = + Offset.isPoison() ? 0 : Offset.asInteger().getZExtValue(); + if (Offset.isPoison() || OffsetInBits + NumBits > Src.getBitWidth()) { + setResult(BEI, AnyValue::getPoisonValue(Ctx, BEI.getType())); + return; + } + setResult(BEI, Ctx.extractBits(Src, OffsetInBits, BEI.getType())); + } + void visitFreezeInst(FreezeInst &FI) { AnyValue Val = getValue(FI.getOperand(0)); Ctx.freeze(Val, FI.getType()); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
