Commit fc5d47b9b9

Andrew Kelley <superjoe30@gmail.com>
2017-02-16 21:45:41
reading from a bit field partially works
See #261 Still need to do: * reading a field that has bit offset 0 but still needs to shift and truncate * writing a field
1 parent 4b5cc80
src/all_types.hpp
@@ -852,6 +852,7 @@ struct TypeTableEntryPointer {
     TypeTableEntry *child_type;
     bool is_const;
     bool is_volatile;
+    uint32_t bit_offset;
 };
 
 struct TypeTableEntryInt {
@@ -1202,7 +1203,7 @@ struct TypeId {
             TypeTableEntry *child_type;
             bool is_const;
             bool is_volatile;
-            uint8_t bit_offset;
+            uint32_t bit_offset;
         } pointer;
         struct {
             TypeTableEntry *child_type;
src/analyze.cpp
@@ -261,7 +261,7 @@ uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) {
 }
 
 // This has to do with packed structs
-static uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) {
+uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) {
     TypeTableEntry *canon_type = get_underlying_type(type_entry);
 
     if (!type_has_bits(type_entry))
@@ -287,7 +287,7 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
 }
 
 TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
-        uint8_t bit_offset, bool is_volatile)
+        uint32_t bit_offset, bool is_volatile)
 {
     assert(child_type->id != TypeTableEntryIdInvalid);
 
@@ -316,7 +316,12 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
     const char *const_str = is_const ? "const " : "";
     const char *volatile_str = is_volatile ? "volatile " : "";
     buf_resize(&entry->name, 0);
-    buf_appendf(&entry->name, "&%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name));
+    if (bit_offset == 0) {
+        buf_appendf(&entry->name, "&%s%s%s", const_str, volatile_str, buf_ptr(&child_type->name));
+    } else {
+        buf_appendf(&entry->name, "&:%" PRIu8 " %s%s%s", bit_offset, const_str,
+                volatile_str, buf_ptr(&child_type->name));
+    }
 
     TypeTableEntry *canon_child_type = get_underlying_type(child_type);
     assert(canon_child_type->id != TypeTableEntryIdInvalid);
@@ -338,6 +343,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
     entry->data.pointer.child_type = child_type;
     entry->data.pointer.is_const = is_const;
     entry->data.pointer.is_volatile = is_volatile;
+    entry->data.pointer.bit_offset = bit_offset;
 
     if (parent_pointer) {
         *parent_pointer = entry;
src/analyze.hpp
@@ -16,9 +16,10 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
 TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
 TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
 TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
-        uint8_t bit_offset, bool is_volatile);
+        uint32_t bit_offset, bool is_volatile);
 bool is_node_void_expr(AstNode *node);
 uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry);
+uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry);
 TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint8_t size_in_bits);
 TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint8_t size_in_bits);
 TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
src/codegen.cpp
@@ -1379,14 +1379,31 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
 
 static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrInstructionLoadPtr *instruction) {
     TypeTableEntry *child_type = instruction->base.value.type;
-    if (!type_has_bits(child_type)) {
+    if (!type_has_bits(child_type))
         return nullptr;
-    }
+
     LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
     TypeTableEntry *ptr_type = instruction->ptr->value.type;
     assert(ptr_type->id == TypeTableEntryIdPointer);
     bool is_volatile = ptr_type->data.pointer.is_volatile;
-    return get_handle_value(g, ptr, child_type, is_volatile);
+
+    uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
+    if (bit_offset == 0)
+        return get_handle_value(g, ptr, child_type, is_volatile);
+
+    assert(!handle_is_ptr(child_type));
+
+    LLVMValueRef containing_int = LLVMBuildLoad(g->builder, ptr, "");
+    LLVMSetVolatile(containing_int, is_volatile);
+
+    uint32_t child_bit_count = type_size_bits(g, child_type);
+    uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
+    uint32_t shift_amt = host_bit_count - bit_offset - child_bit_count;
+
+    LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
+    LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
+
+    return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
 }
 
 static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) {
src/ir.cpp
@@ -9067,7 +9067,8 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
                 }
             }
             ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
-            return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, 0, is_volatile);
+            return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const,
+                    field->packed_bits_offset, is_volatile);
         } else {
             return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
                 field_ptr_instruction, container_ptr, container_type);
test/cases/struct.zig
@@ -225,3 +225,34 @@ fn packedStruct() {
     const four = foo.x + foo.y;
     assert(four == 4);
 }
+
+
+const u2 = @intType(false, 2);
+const u3 = @intType(false, 3);
+
+const BitField1 = packed struct {
+    a: u3,
+    b: u3,
+    c: u2,
+};
+
+fn bitFieldAccess() {
+    @setFnTest(this);
+
+    const data = BitField1 {
+        .a = 1,
+        .b = 2,
+        .c = 3,
+    };
+    assert(getB(&data) == 2);
+    assert(getC(&data) == 3);
+    comptime assert(@sizeOf(BitField1) == 1);
+}
+
+fn getB(data: &const BitField1) -> u3 {
+    return data.b;
+}
+
+fn getC(data: &const BitField1) -> u2 {
+    return data.c;
+}