From 760b307e8a8fcbb31fc1f2abb170ef7399aa917e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 22 Dec 2017 18:27:33 -0500 Subject: [PATCH] fix endianness of sub-byte integer fields in packed structs closes #307 --- src/codegen.cpp | 40 +++++++++++++++++++++++++++++++--------- test/cases/struct.zig | 25 +++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 754641609..0aecacb0b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1238,11 +1238,13 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, TypeTableEntry return nullptr; } + bool big_endian = g->is_big_endian; + LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, ""); uint32_t bit_offset = ptr_type->data.pointer.bit_offset; uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); - uint32_t shift_amt = host_bit_count - bit_offset - unaligned_bit_count; + uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset; LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); LLVMValueRef mask_val = LLVMConstAllOnes(child_type->type_ref); @@ -2198,12 +2200,14 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI if (unaligned_bit_count == 0) return get_handle_value(g, ptr, child_type, ptr_type); + bool big_endian = g->is_big_endian; + assert(!handle_is_ptr(child_type)); LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, ""); uint32_t bit_offset = ptr_type->data.pointer.bit_offset; uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); - uint32_t shift_amt = host_bit_count - bit_offset - unaligned_bit_count; + uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset; LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); @@ -3796,17 +3800,26 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdStruct: { assert(type_entry->data.structure.layout == ContainerLayoutPacked); + bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); + size_t used_bits = 0; for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { TypeStructField *field = &type_entry->data.structure.fields[i]; if (field->gen_index == SIZE_MAX) { continue; } LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, &const_val->data.x_struct.fields[i]); - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, field->packed_bits_size, false); - val = LLVMConstShl(val, shift_amt); - val = LLVMConstOr(val, child_val); + if (is_big_endian) { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, field->packed_bits_size, false); + val = LLVMConstShl(val, shift_amt); + val = LLVMConstOr(val, child_val); + } else { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + used_bits += field->packed_bits_size; + } } return val; } @@ -3931,9 +3944,11 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { fields[type_struct_field->gen_index] = val; make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val); } else { + bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type LLVMTypeRef big_int_type_ref = LLVMStructGetTypeAtIndex(type_entry->type_ref, (unsigned)type_struct_field->gen_index); LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); + size_t used_bits = 0; for (size_t i = src_field_index; i < src_field_index_end; i += 1) { TypeStructField *it_field = &type_entry->data.structure.fields[i]; if (it_field->gen_index == SIZE_MAX) { @@ -3941,10 +3956,17 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, &const_val->data.x_struct.fields[i]); - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, - it_field->packed_bits_size, false); - val = LLVMConstShl(val, shift_amt); - val = LLVMConstOr(val, child_val); + if (is_big_endian) { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, + it_field->packed_bits_size, false); + val = LLVMConstShl(val, shift_amt); + val = LLVMConstOr(val, child_val); + } else { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + used_bits += it_field->packed_bits_size; + } } fields[type_struct_field->gen_index] = val; } diff --git a/test/cases/struct.zig b/test/cases/struct.zig index 28792e9a7..bdd2e5d04 100644 --- a/test/cases/struct.zig +++ b/test/cases/struct.zig @@ -379,3 +379,28 @@ const Nibbles = packed struct { x: u4, y: u4, }; + +const Bitfields = packed struct { + f1: u16, + f2: u16, + f3: u8, + f4: u8, + f5: u4, + f6: u4, + f7: u8, +}; + +test "native bit field understands endianness" { + var all: u64 = 0x7765443322221111; + var bytes: [8]u8 = undefined; + @memcpy(&bytes[0], @ptrCast(&u8, &all), 8); + var bitfields = *@ptrCast(&Bitfields, &bytes[0]); + + assert(bitfields.f1 == 0x1111); + assert(bitfields.f2 == 0x2222); + assert(bitfields.f3 == 0x33); + assert(bitfields.f4 == 0x44); + assert(bitfields.f5 == 0x5); + assert(bitfields.f6 == 0x6); + assert(bitfields.f7 == 0x77); +}