From 09a1162af57858a9579ef112b75472f82581e1d3 Mon Sep 17 00:00:00 2001
From: raulgrell
@offsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)
+ {#header_open|@byteOffsetOf#}
+ @byteOffsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)
+ + This function returns the byte offset of a field relative to its containing struct. +
+ {#header_close#} + {#header_open|@bitOffsetOf#} +@bitOffsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)
This function returns the byte offset of a field relative to its containing struct.
diff --git a/src/all_types.hpp b/src/all_types.hpp index b80fe17053..eab13b596b 100644 --- a/src/all_types.hpp +++ b/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; diff --git a/src/codegen.cpp b/src/codegen.cpp index 8acc7e9702..73c9a19499 100644 --- a/src/codegen.cpp +++ b/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); diff --git a/src/ir.cpp b/src/ir.cpp index 30f40b3a1e..98f5e0650b 100644 --- a/src/ir.cpp +++ b/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) { - IrInstructionOffsetOf *instruction = ir_build_instruction@bitOffsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)
- This function returns the byte offset of a field relative to its containing struct. + This function returns the bit offset of a field relative to its containing struct.
{#header_close#} {#header_open|@OpaqueType#} From 7c5e3e1f8e4671b383865df0150b549e2445d170 Mon Sep 17 00:00:00 2001 From: Andrew Kelley{#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout.
+TODO bit fields
+TODO alignment
+TODO endianness
+TODO @bitOffsetOf and @byteOffsetOf
+TODO mention how volatile loads and stores of bit packed fields could be more efficient when + done by hand instead of with packed struct
+ {#header_close#} {#header_open|struct Naming#}Since all structs are anonymous, Zig infers the type name based on a few rules.
{#syntax#}@bitOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}
+ + Returns the bit offset of a field relative to its containing struct. +
++ For non {#link|packed structs|packed struct#}, this will always be divisible by {#syntax#}8{#endsyntax#}. + For packed structs, non-byte-aligned fields will share a byte offset, but they will have different + bit offsets. +
+ {#see_also|@byteOffsetOf#} + {#header_close#} {#header_open|@breakpoint#}{#syntax#}@breakpoint(){#endsyntax#}
@@ -5145,6 +5166,13 @@ fn seq(c: u8) void { This function is only valid within function scope.
+ {#header_close#} + {#header_open|@byteOffsetOf#} +{#syntax#}@byteOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}
+ + Returns the byte offset of a field relative to its containing struct. +
+ {#see_also|@bitOffsetOf#} {#header_close#} {#header_open|@alignCast#}{#syntax#}@alignCast(comptime alignment: u29, ptr: var) var{#endsyntax#}
@@ -5868,18 +5896,6 @@ fn add(a: i32, b: i32) i32 {
{#see_also|@inlineCall#}
{#header_close#}
- {#header_open|@offsetOf#}
- {#syntax#}@byteOffsetOf(comptime T: type, comptime field_name: [] const u8) comptime_int{#endsyntax#}
- - This function returns the byte offset of a field relative to its containing struct. -
- {#header_close#} - {#header_open|@bitOffsetOf#} -@bitOffsetOf(comptime T: type, comptime field_name: [] const u8) (number literal)
- - This function returns the bit offset of a field relative to its containing struct. -
- {#header_close#} {#header_open|@OpaqueType#}{#syntax#}@OpaqueType() type{#endsyntax#}
diff --git a/src/ir.cpp b/src/ir.cpp index 7daff2ca0e..5e0f79ec13 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -17088,30 +17088,36 @@ static ZigType *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira, IrInstructionByteOffsetOf *instruction) { IrInstruction *type_value = instruction->type_value->other; + if (type_is_invalid(type_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + 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; + if (!validate_byte_offset(ira, type_value, field_name_value, &byte_offset)) + return ira->codegen->builtin_types.entry_invalid; + + + 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; } static ZigType *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira, IrInstructionBitOffsetOf *instruction) { IrInstruction *type_value = instruction->type_value->other; + if (type_is_invalid(type_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; 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; + if (!(field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset))) + return ira->codegen->builtin_types.entry_invalid; + + 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; } static void ensure_field_index(ZigType *type, const char *field_name, size_t index) diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 2e238e40eb..86c5e4828e 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -158,12 +158,12 @@ const std = @import("../index.zig"); const assert = std.debug.assert; comptime { - assert(@offsetOf(Kevent, "ident") == 0); - assert(@offsetOf(Kevent, "filter") == 8); - assert(@offsetOf(Kevent, "flags") == 10); - assert(@offsetOf(Kevent, "fflags") == 12); - assert(@offsetOf(Kevent, "data") == 16); - assert(@offsetOf(Kevent, "udata") == 24); + assert(@byteOffsetOf(Kevent, "ident") == 0); + assert(@byteOffsetOf(Kevent, "filter") == 8); + assert(@byteOffsetOf(Kevent, "flags") == 10); + assert(@byteOffsetOf(Kevent, "fflags") == 12); + assert(@byteOffsetOf(Kevent, "data") == 16); + assert(@byteOffsetOf(Kevent, "udata") == 24); } pub const kevent64_s = extern struct { @@ -180,11 +180,11 @@ pub const kevent64_s = extern struct { // to make sure the struct is laid out the same. These values were // produced from C code using the offsetof macro. comptime { - assert(@offsetOf(kevent64_s, "ident") == 0); - assert(@offsetOf(kevent64_s, "filter") == 8); - assert(@offsetOf(kevent64_s, "flags") == 10); - assert(@offsetOf(kevent64_s, "fflags") == 12); - assert(@offsetOf(kevent64_s, "data") == 16); - assert(@offsetOf(kevent64_s, "udata") == 24); - assert(@offsetOf(kevent64_s, "ext") == 32); + assert(@byteOffsetOf(kevent64_s, "ident") == 0); + assert(@byteOffsetOf(kevent64_s, "filter") == 8); + assert(@byteOffsetOf(kevent64_s, "flags") == 10); + assert(@byteOffsetOf(kevent64_s, "fflags") == 12); + assert(@byteOffsetOf(kevent64_s, "data") == 16); + assert(@byteOffsetOf(kevent64_s, "udata") == 24); + assert(@byteOffsetOf(kevent64_s, "ext") == 32); } diff --git a/test/cases/sizeof_and_typeof.zig b/test/cases/sizeof_and_typeof.zig index 2d1ffa9434..11c6b2f6ba 100644 --- a/test/cases/sizeof_and_typeof.zig +++ b/test/cases/sizeof_and_typeof.zig @@ -1,13 +1,14 @@ +const builtin = @import("builtin"); const assert = @import("std").debug.assert; -test "sizeofAndTypeOf" { +test "@sizeOf and @typeOf" { const y: @typeOf(x) = 120; assert(@sizeOf(@typeOf(y)) == 2); } const x: u16 = 13; const z: @typeOf(x) = 19; -const S = struct { +const A = struct { a: u8, b: u32, c: u8, @@ -27,24 +28,42 @@ const P = packed struct { g: u16, }; -test "byteOffsetOf" { - const p: P = undefined; - 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 "@byteOffsetOf" { + // Packed structs have fixed memory layout + assert(@byteOffsetOf(P, "a") == 0); + assert(@byteOffsetOf(P, "b") == 1); + assert(@byteOffsetOf(P, "c") == 5); + assert(@byteOffsetOf(P, "d") == 6); + assert(@byteOffsetOf(P, "e") == 6); + assert(@byteOffsetOf(P, "f") == 7); + assert(@byteOffsetOf(P, "g") == 9); + + // Normal struct fields can be moved/padded + var a: A = undefined; + assert(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); + assert(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); + assert(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); + assert(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); + assert(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); + assert(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); + assert(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); } -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 "@bitOffsetOf" { + // Packed structs have fixed memory layout + assert(@bitOffsetOf(P, "a") == 0); + assert(@bitOffsetOf(P, "b") == 8); + assert(@bitOffsetOf(P, "c") == 40); + assert(@bitOffsetOf(P, "d") == 48); + assert(@bitOffsetOf(P, "e") == 51); + assert(@bitOffsetOf(P, "f") == 56); + assert(@bitOffsetOf(P, "g") == 72); + + assert(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); + assert(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); + assert(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); + assert(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); + assert(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); + assert(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); + assert(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 7b659a8dc1..fae022f3fb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3769,7 +3769,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @byteOffsetOf(Foo, "a",); \\} , - ".tmp_source.zig:3:22: error: expected struct type, found 'i32'", + ".tmp_source.zig:3:26: error: expected struct type, found 'i32'", ); cases.add( @@ -3781,7 +3781,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @byteOffsetOf(Foo, "a",); \\} , - ".tmp_source.zig:5:27: error: struct 'Foo' has no field 'a'", + ".tmp_source.zig:5:31: error: struct 'Foo' has no field 'a'", ); cases.addExe( @@ -5092,7 +5092,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const fieldOffset = @byteOffsetOf(Empty, "val",); \\} , - ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset", + ".tmp_source.zig:5:46: error: zero-bit field 'val' in struct 'Empty' has no offset", ); cases.add( @@ -5104,7 +5104,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const fieldOffset = @bitOffsetOf(Empty, "val",); \\} , - ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset", + ".tmp_source.zig:5:45: error: zero-bit field 'val' in struct 'Empty' has no offset", ); cases.add(