Commit 09a1162af5

raulgrell <raulgrell@gmail.com>
2018-09-07 23:43:01
builtin functions: @byteOffsetOf and @bitOffsetOf
1 parent b18af37
doc/langref.html.in
@@ -5838,8 +5838,14 @@ fn add(a: i32, b: i32) i32 {
       </p>
       {#see_also|@inlineCall#}
       {#header_close#}
-      {#header_open|@offsetOf#}
-      <pre><code class="zig">@offsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)</code></pre>
+      {#header_open|@byteOffsetOf#}
+      <pre><code class="zig">@byteOffsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)</code></pre>
+      <p>
+      This function returns the byte offset of a field relative to its containing struct.
+      </p>
+      {#header_close#}
+      {#header_open|@bitOffsetOf#}
+      <pre><code class="zig">@bitOffsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)</code></pre>
       <p>
       This function returns the byte offset of a field relative to its containing struct.
       </p>
src/all_types.hpp
@@ -1395,7 +1395,8 @@ enum BuiltinFnId {
     BuiltinFnIdTagName,
     BuiltinFnIdTagType,
     BuiltinFnIdFieldParentPtr,
-    BuiltinFnIdOffsetOf,
+    BuiltinFnIdByteOffsetOf,
+    BuiltinFnIdBitOffsetOf,
     BuiltinFnIdInlineCall,
     BuiltinFnIdNoInlineCall,
     BuiltinFnIdNewStackCall,
@@ -2119,7 +2120,8 @@ enum IrInstructionId {
     IrInstructionIdTagName,
     IrInstructionIdTagType,
     IrInstructionIdFieldParentPtr,
-    IrInstructionIdOffsetOf,
+    IrInstructionIdByteOffsetOf,
+    IrInstructionIdBitOffsetOf,
     IrInstructionIdTypeInfo,
     IrInstructionIdTypeId,
     IrInstructionIdSetEvalBranchQuota,
@@ -3022,7 +3024,14 @@ struct IrInstructionFieldParentPtr {
     TypeStructField *field;
 };
 
-struct IrInstructionOffsetOf {
+struct IrInstructionByteOffsetOf {
+    IrInstruction base;
+
+    IrInstruction *type_value;
+    IrInstruction *field_name;
+};
+
+struct IrInstructionBitOffsetOf {
     IrInstruction base;
 
     IrInstruction *type_value;
src/codegen.cpp
@@ -5194,7 +5194,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdTypeName:
         case IrInstructionIdDeclRef:
         case IrInstructionIdSwitchVar:
-        case IrInstructionIdOffsetOf:
+        case IrInstructionIdByteOffsetOf:
+        case IrInstructionIdBitOffsetOf:
         case IrInstructionIdTypeInfo:
         case IrInstructionIdTypeId:
         case IrInstructionIdSetEvalBranchQuota:
@@ -6766,7 +6767,8 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn(g, BuiltinFnIdTagName, "tagName", 1);
     create_builtin_fn(g, BuiltinFnIdTagType, "TagType", 1);
     create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3);
-    create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2);
+    create_builtin_fn(g, BuiltinFnIdByteOffsetOf, "byteOffsetOf", 2);
+    create_builtin_fn(g, BuiltinFnIdBitOffsetOf, "bitOffsetOf", 2);
     create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2);
     create_builtin_fn(g, BuiltinFnIdDivTrunc, "divTrunc", 2);
     create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);
src/ir.cpp
@@ -719,8 +719,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldParentPtr *
     return IrInstructionIdFieldParentPtr;
 }
 
-static constexpr IrInstructionId ir_instruction_id(IrInstructionOffsetOf *) {
-    return IrInstructionIdOffsetOf;
+static constexpr IrInstructionId ir_instruction_id(IrInstructionByteOffsetOf *) {
+    return IrInstructionIdByteOffsetOf;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionBitOffsetOf *) {
+    return IrInstructionIdBitOffsetOf;
 }
 
 static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) {
@@ -2645,10 +2649,23 @@ static IrInstruction *ir_build_field_parent_ptr(IrBuilder *irb, Scope *scope, As
     return &instruction->base;
 }
 
-static IrInstruction *ir_build_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node,
+static IrInstruction *ir_build_byte_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *type_value, IrInstruction *field_name)
+{
+    IrInstructionByteOffsetOf *instruction = ir_build_instruction<IrInstructionByteOffsetOf>(irb, scope, source_node);
+    instruction->type_value = type_value;
+    instruction->field_name = field_name;
+
+    ir_ref_instruction(type_value, irb->current_basic_block);
+    ir_ref_instruction(field_name, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_bit_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node,
         IrInstruction *type_value, IrInstruction *field_name)
 {
-    IrInstructionOffsetOf *instruction = ir_build_instruction<IrInstructionOffsetOf>(irb, scope, source_node);
+    IrInstructionBitOffsetOf *instruction = ir_build_instruction<IrInstructionBitOffsetOf>(irb, scope, source_node);
     instruction->type_value = type_value;
     instruction->field_name = field_name;
 
@@ -4695,7 +4712,22 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
                 IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);
                 return ir_lval_wrap(irb, scope, field_parent_ptr, lval);
             }
-        case BuiltinFnIdOffsetOf:
+        case BuiltinFnIdByteOffsetOf:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_instruction)
+                    return arg1_value;
+
+                IrInstruction *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value);
+                return ir_lval_wrap(irb, scope, offset_of, lval);
+            }
+        case BuiltinFnIdBitOffsetOf:
             {
                 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
                 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
@@ -4707,7 +4739,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
                 if (arg1_value == irb->codegen->invalid_instruction)
                     return arg1_value;
 
-                IrInstruction *offset_of = ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);
+                IrInstruction *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value);
                 return ir_lval_wrap(irb, scope, offset_of, lval);
             }
         case BuiltinFnIdInlineCall:
@@ -16909,47 +16941,76 @@ static ZigType *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
     return result_type;
 }
 
-static ZigType *ir_analyze_instruction_offset_of(IrAnalyze *ira,
-        IrInstructionOffsetOf *instruction)
+static TypeStructField *validate_byte_offset(IrAnalyze *ira,
+        IrInstruction *type_value,
+        IrInstruction *field_name_value,
+        size_t *byte_offset) 
 {
-    Error err;
-    IrInstruction *type_value = instruction->type_value->other;
     ZigType *container_type = ir_resolve_type(ira, type_value);
     if (type_is_invalid(container_type))
-        return ira->codegen->builtin_types.entry_invalid;
+        return nullptr;
 
+    Error err;
     if ((err = ensure_complete_type(ira->codegen, container_type)))
-        return ira->codegen->builtin_types.entry_invalid;
+        return nullptr;
 
-    IrInstruction *field_name_value = instruction->field_name->other;
     Buf *field_name = ir_resolve_str(ira, field_name_value);
     if (!field_name)
-        return ira->codegen->builtin_types.entry_invalid;
+        return nullptr;
 
     if (container_type->id != ZigTypeIdStruct) {
         ir_add_error(ira, type_value,
                 buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name)));
-        return ira->codegen->builtin_types.entry_invalid;
+        return nullptr;
     }
 
     TypeStructField *field = find_struct_type_field(container_type, field_name);
     if (field == nullptr) {
         ir_add_error(ira, field_name_value,
                 buf_sprintf("struct '%s' has no field '%s'",
-                    buf_ptr(&container_type->name), buf_ptr(field_name)));
-        return ira->codegen->builtin_types.entry_invalid;
+                        buf_ptr(&container_type->name), buf_ptr(field_name)));
+        return nullptr;
     }
 
     if (!type_has_bits(field->type_entry)) {
         ir_add_error(ira, field_name_value,
-                     buf_sprintf("zero-bit field '%s' in struct '%s' has no offset",
-                                 buf_ptr(field_name), buf_ptr(&container_type->name)));
-        return ira->codegen->builtin_types.entry_invalid;
+                buf_sprintf("zero-bit field '%s' in struct '%s' has no offset",
+                        buf_ptr(field_name), buf_ptr(&container_type->name)));
+        return nullptr;
     }
-    size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, container_type->type_ref, field->gen_index);
-    ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
-    bigint_init_unsigned(&out_val->data.x_bigint, byte_offset);
-    return ira->codegen->builtin_types.entry_num_lit_int;
+
+    *byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, container_type->type_ref, field->gen_index);
+    return field;
+}
+
+static ZigType *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira,
+        IrInstructionByteOffsetOf *instruction)
+{
+    IrInstruction *type_value = instruction->type_value->other;
+    IrInstruction *field_name_value = instruction->field_name->other;
+    size_t byte_offset = 0;
+    if (validate_byte_offset(ira, type_value, field_name_value, &byte_offset)) {
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+        bigint_init_unsigned(&out_val->data.x_bigint, byte_offset);
+        return ira->codegen->builtin_types.entry_num_lit_int;
+    }
+    return ira->codegen->builtin_types.entry_invalid;
+}
+
+static ZigType *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira,
+        IrInstructionBitOffsetOf *instruction)
+{
+    IrInstruction *type_value = instruction->type_value->other;
+    IrInstruction *field_name_value = instruction->field_name->other;
+    size_t byte_offset = 0;
+    TypeStructField *field = nullptr;
+    if ((field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset))) {
+        size_t bit_offset = byte_offset * 8 + field->packed_bits_offset;
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+        bigint_init_unsigned(&out_val->data.x_bigint, bit_offset);
+        return ira->codegen->builtin_types.entry_num_lit_int;
+    }
+    return ira->codegen->builtin_types.entry_invalid;
 }
 
 static void ensure_field_index(ZigType *type, const char *field_name, size_t index)
@@ -21362,8 +21423,10 @@ static ZigType *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *ins
             return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionTagName *)instruction);
         case IrInstructionIdFieldParentPtr:
             return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction);
-        case IrInstructionIdOffsetOf:
-            return ir_analyze_instruction_offset_of(ira, (IrInstructionOffsetOf *)instruction);
+        case IrInstructionIdByteOffsetOf:
+            return ir_analyze_instruction_byte_offset_of(ira, (IrInstructionByteOffsetOf *)instruction);
+        case IrInstructionIdBitOffsetOf:
+            return ir_analyze_instruction_bit_offset_of(ira, (IrInstructionBitOffsetOf *)instruction);
         case IrInstructionIdTypeInfo:
             return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction);
         case IrInstructionIdTypeId:
@@ -21647,7 +21710,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdTypeName:
         case IrInstructionIdTagName:
         case IrInstructionIdFieldParentPtr:
-        case IrInstructionIdOffsetOf:
+        case IrInstructionIdByteOffsetOf:
+        case IrInstructionIdBitOffsetOf:
         case IrInstructionIdTypeInfo:
         case IrInstructionIdTypeId:
         case IrInstructionIdAlignCast:
src/ir_print.cpp
@@ -1041,8 +1041,16 @@ static void ir_print_field_parent_ptr(IrPrint *irp, IrInstructionFieldParentPtr
     fprintf(irp->f, ")");
 }
 
-static void ir_print_offset_of(IrPrint *irp, IrInstructionOffsetOf *instruction) {
-    fprintf(irp->f, "@offset_of(");
+static void ir_print_byte_offset_of(IrPrint *irp, IrInstructionByteOffsetOf *instruction) {
+    fprintf(irp->f, "@byte_offset_of(");
+    ir_print_other_instruction(irp, instruction->type_value);
+    fprintf(irp->f, ",");
+    ir_print_other_instruction(irp, instruction->field_name);
+    fprintf(irp->f, ")");
+}
+
+static void ir_print_bit_offset_of(IrPrint *irp, IrInstructionBitOffsetOf *instruction) {
+    fprintf(irp->f, "@bit_offset_of(");
     ir_print_other_instruction(irp, instruction->type_value);
     fprintf(irp->f, ",");
     ir_print_other_instruction(irp, instruction->field_name);
@@ -1649,8 +1657,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdFieldParentPtr:
             ir_print_field_parent_ptr(irp, (IrInstructionFieldParentPtr *)instruction);
             break;
-        case IrInstructionIdOffsetOf:
-            ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction);
+        case IrInstructionIdByteOffsetOf:
+            ir_print_byte_offset_of(irp, (IrInstructionByteOffsetOf *)instruction);
+            break;
+        case IrInstructionIdBitOffsetOf:
+            ir_print_bit_offset_of(irp, (IrInstructionBitOffsetOf *)instruction);
             break;
         case IrInstructionIdTypeInfo:
             ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction);
test/cases/sizeof_and_typeof.zig
@@ -7,28 +7,44 @@ test "sizeofAndTypeOf" {
 const x: u16 = 13;
 const z: @typeOf(x) = 19;
 
-const A = struct {
+const S = struct {
     a: u8,
     b: u32,
     c: u8,
+    d: u3,
+    e: u5,
+    f: u16,
+    g: u16,
 };
 
 const P = packed struct {
     a: u8,
     b: u32,
     c: u8,
+    d: u3,
+    e: u5,
+    f: u16,
+    g: u16,
 };
 
-test "offsetOf" {
-    // Packed structs have fixed memory layout
+test "byteOffsetOf" {
     const p: P = undefined;
-    assert(@offsetOf(P, "a") == 0);
-    assert(@offsetOf(@typeOf(p), "b") == 1);
-    assert(@offsetOf(@typeOf(p), "c") == 5);
-
-    // Non-packed struct fields can be moved/padded
-    const a: A = undefined;
-    assert(@ptrToInt(&a.a) - @ptrToInt(&a) == @offsetOf(A, "a"));
-    assert(@ptrToInt(&a.b) - @ptrToInt(&a) == @offsetOf(@typeOf(a), "b"));
-    assert(@ptrToInt(&a.c) - @ptrToInt(&a) == @offsetOf(@typeOf(a), "c"));
+    std.debug.assert(@byteOffsetOf(P, "a") == 0 and @byteOffsetOf(S, "a") == 0);
+    std.debug.assert(@byteOffsetOf(P, "b") == 1 and @byteOffsetOf(S, "b") == 4);
+    std.debug.assert(@byteOffsetOf(P, "c") == 5 and @byteOffsetOf(S, "c") == 8);
+    std.debug.assert(@byteOffsetOf(P, "d") == 6 and @byteOffsetOf(S, "d") == 9);
+    std.debug.assert(@byteOffsetOf(P, "e") == 6 and @byteOffsetOf(S, "e") == 10);
+    std.debug.assert(@byteOffsetOf(P, "f") == 7 and @byteOffsetOf(S, "f") == 12);
+    std.debug.assert(@byteOffsetOf(P, "g") == 9 and @byteOffsetOf(S, "g") == 14);
 }
+
+test "bitOffsetOf" {
+    const p: P = undefined;
+    std.debug.assert(@bitOffsetOf(P, "a") == 0 and @bitOffsetOf(S, "a") == 0);
+    std.debug.assert(@bitOffsetOf(P, "b") == 8 and @bitOffsetOf(S, "b") == 32);
+    std.debug.assert(@bitOffsetOf(P, "c") == 40 and @bitOffsetOf(S, "c") == 64);
+    std.debug.assert(@bitOffsetOf(P, "d") == 48 and @bitOffsetOf(S, "d") == 72);
+    std.debug.assert(@bitOffsetOf(P, "e") == 51 and @bitOffsetOf(S, "e") == 80);
+    std.debug.assert(@bitOffsetOf(P, "f") == 56 and @bitOffsetOf(S, "f") == 96);
+    std.debug.assert(@bitOffsetOf(P, "g") == 72 and @bitOffsetOf(S, "g") == 112);
+}
\ No newline at end of file
test/compile_errors.zig
@@ -3706,22 +3706,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
     );
 
     cases.add(
-        "@offsetOf - non struct",
+        "@byteOffsetOf - non struct",
         \\const Foo = i32;
         \\export fn foo() usize {
-        \\    return @offsetOf(Foo, "a",);
+        \\    return @byteOffsetOf(Foo, "a",);
         \\}
     ,
         ".tmp_source.zig:3:22: error: expected struct type, found 'i32'",
     );
 
     cases.add(
-        "@offsetOf - bad field name",
+        "@byteOffsetOf - bad field name",
         \\const Foo = struct {
         \\    derp: i32,
         \\};
         \\export fn foo() usize {
-        \\    return @offsetOf(Foo, "a",);
+        \\    return @byteOffsetOf(Foo, "a",);
         \\}
     ,
         ".tmp_source.zig:5:27: error: struct 'Foo' has no field 'a'",
@@ -5029,12 +5029,24 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
     );
 
     cases.add(
-        "taking offset of void field in struct",
+        "taking byte offset of void field in struct",
         \\const Empty = struct {
         \\    val: void,
         \\};
         \\export fn foo() void {
-        \\    const fieldOffset = @offsetOf(Empty, "val",);
+        \\    const fieldOffset = @byteOffsetOf(Empty, "val",);
+        \\}
+    ,
+        ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset",
+    );
+
+    cases.add(
+        "taking bit offset of void field in struct",
+        \\const Empty = struct {
+        \\    val: void,
+        \\};
+        \\export fn foo() void {
+        \\    const fieldOffset = @bitOffsetOf(Empty, "val",);
         \\}
     ,
         ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset",