authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-20 22:40:41-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-20 22:40:41-05:00
log3ee9d06cbdb6bcaf561e7215c4c103c7ad65a72d
treec9f612c22c8e2d2e1191a5aab8fc2bfaebf4cfcd
parent079728752eca4cffbb4f7e8dc06d5e23b81d7627
signaturelock-open Commit is signed but in an unrecognized format.

packed structs support comptime bitcasting

* `type_size_store` is no longer a thing. loading and storing a pointer to a value may dereference up to `@sizeOf(T)` bytes, even for integers such as `u24`. * fix `types_have_same_zig_comptime_repr` to not think that the same `ZigTypeId` means the `ConstExprValue` neccesarily has the same representation. * implement `buf_write_value_bytes` and `buf_read_value_bytes` for `ContainerLayoutPacked` closes #1120

8 files changed, 226 insertions(+), 40 deletions(-)

src/analyze.cpp-22
......@@ -359,28 +359,6 @@ uint64_t type_size(CodeGen *g, ZigType *type_entry) {
359359 return LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref);
360360}
361361
362uint64_t type_size_store(CodeGen *g, ZigType *type_entry) {
363 assert(type_is_complete(type_entry));
364
365 if (!type_has_bits(type_entry))
366 return 0;
367
368 if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) {
369 uint64_t size_in_bits = type_size_bits(g, type_entry);
370 return (size_in_bits + 7) / 8;
371 } else if (type_entry->id == ZigTypeIdArray) {
372 ZigType *child_type = type_entry->data.array.child_type;
373 if (child_type->id == ZigTypeIdStruct &&
374 child_type->data.structure.layout == ContainerLayoutPacked)
375 {
376 uint64_t size_in_bits = type_size_bits(g, type_entry);
377 return (size_in_bits + 7) / 8;
378 }
379 }
380
381 return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
382}
383
384362uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) {
385363 assert(type_is_complete(type_entry));
386364
src/analyze.hpp-1
......@@ -19,7 +19,6 @@ ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
1919ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
2020 bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
2121uint64_t type_size(CodeGen *g, ZigType *type_entry);
22uint64_t type_size_store(CodeGen *g, ZigType *type_entry);
2322uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
2423ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
2524ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type);
src/codegen.cpp+4-1
......@@ -3070,7 +3070,10 @@ static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable,
30703070{
30713071 ZigType *wanted_type = instruction->base.value.type;
30723072 LLVMValueRef value = ir_llvm_value(g, instruction->value);
3073 return LLVMBuildBitCast(g->builder, value, wanted_type->type_ref, "");
3073 // We either bitcast the value directly or bitcast the pointer which does a pointer cast
3074 LLVMTypeRef wanted_type_ref = handle_is_ptr(wanted_type) ?
3075 LLVMPointerType(wanted_type->type_ref, 0) : wanted_type->type_ref;
3076 return LLVMBuildBitCast(g->builder, value, wanted_type_ref, "");
30743077}
30753078
30763079static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable,
src/ir.cpp+164-14
......@@ -198,10 +198,11 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c
198198 result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index];
199199 break;
200200 }
201 case ConstPtrSpecialBaseStruct:
202 result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[
203 const_val->data.x_ptr.data.base_struct.field_index];
201 case ConstPtrSpecialBaseStruct: {
202 ConstExprValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val;
203 result = &struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index];
204204 break;
205 }
205206 case ConstPtrSpecialBaseErrorUnionCode:
206207 result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set;
207208 break;
......@@ -230,20 +231,55 @@ static bool is_opt_err_set(ZigType *ty) {
230231 (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet);
231232}
232233
234// This function returns true when you can change the type of a ConstExprValue and the
235// value remains meaningful.
233236static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) {
234237 if (a == b)
235238 return true;
236239
237 if (a->id == b->id)
238 return true;
239
240240 if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr)
241241 return true;
242242
243243 if (is_opt_err_set(a) && is_opt_err_set(b))
244244 return true;
245245
246 return false;
246 if (a->id != b->id)
247 return false;
248
249 switch (a->id) {
250 case ZigTypeIdInvalid:
251 case ZigTypeIdUnreachable:
252 zig_unreachable();
253 case ZigTypeIdMetaType:
254 case ZigTypeIdVoid:
255 case ZigTypeIdBool:
256 case ZigTypeIdComptimeFloat:
257 case ZigTypeIdComptimeInt:
258 case ZigTypeIdPointer:
259 case ZigTypeIdUndefined:
260 case ZigTypeIdNull:
261 case ZigTypeIdNamespace:
262 case ZigTypeIdBoundFn:
263 case ZigTypeIdErrorSet:
264 case ZigTypeIdOpaque:
265 return true;
266 case ZigTypeIdFloat:
267 return a->data.floating.bit_count == b->data.floating.bit_count;
268 case ZigTypeIdInt:
269 return a->data.integral.is_signed == b->data.integral.is_signed;
270 case ZigTypeIdArray:
271 case ZigTypeIdStruct:
272 case ZigTypeIdOptional:
273 case ZigTypeIdErrorUnion:
274 case ZigTypeIdEnum:
275 case ZigTypeIdUnion:
276 case ZigTypeIdFn:
277 case ZigTypeIdArgTuple:
278 case ZigTypeIdPromise:
279 case ZigTypeIdVector:
280 return false;
281 }
282 zig_unreachable();
247283}
248284
249285static bool ir_should_inline(IrExecutable *exec, Scope *scope) {
......@@ -14421,12 +14457,11 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source
1442114457 if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown)))
1442214458 return ErrorSemanticAnalyzeFail;
1442314459
14424 // We don't need to read the padding bytes, so we look at type_size_store bytes
14425 size_t src_size = type_size_store(codegen, pointee->type);
14426 size_t dst_size = type_size_store(codegen, out_val->type);
14460 size_t src_size = type_size(codegen, pointee->type);
14461 size_t dst_size = type_size(codegen, out_val->type);
1442714462
1442814463 if (dst_size <= src_size) {
14429 if (types_have_same_zig_comptime_repr(pointee->type, out_val->type)) {
14464 if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) {
1443014465 copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
1443114466 return ErrorNone;
1443214467 }
......@@ -20885,7 +20920,68 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
2088520920 case ZigTypeIdVector:
2088620921 return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len);
2088720922 case ZigTypeIdStruct:
20888 zig_panic("TODO buf_write_value_bytes struct type");
20923 switch (val->type->data.structure.layout) {
20924 case ContainerLayoutAuto:
20925 zig_unreachable();
20926 case ContainerLayoutExtern:
20927 zig_panic("TODO buf_write_value_bytes extern struct");
20928 case ContainerLayoutPacked: {
20929 size_t src_field_count = val->type->data.structure.src_field_count;
20930 size_t gen_field_count = val->type->data.structure.gen_field_count;
20931 size_t gen_i = 0;
20932 size_t src_i = 0;
20933 size_t offset = 0;
20934 bool is_big_endian = codegen->is_big_endian;
20935 uint8_t child_buf_prealloc[16];
20936 size_t child_buf_len = 16;
20937 uint8_t *child_buf = child_buf_prealloc;
20938 while (gen_i < gen_field_count) {
20939 LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref,
20940 (unsigned)gen_i);
20941 size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type);
20942 size_t big_int_byte_count = big_int_bit_count / 8;
20943 if (big_int_byte_count > child_buf_len) {
20944 child_buf = allocate_nonzero<uint8_t>(big_int_byte_count);
20945 child_buf_len = big_int_byte_count;
20946 }
20947 BigInt big_int;
20948 bigint_init_unsigned(&big_int, 0);
20949 size_t used_bits = 0;
20950 while (src_i < src_field_count) {
20951 TypeStructField *field = &val->type->data.structure.fields[src_i];
20952 assert(field->gen_index != SIZE_MAX);
20953 if (field->gen_index != gen_i)
20954 break;
20955 uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry);
20956 buf_write_value_bytes(codegen, child_buf, &val->data.x_struct.fields[src_i]);
20957 BigInt child_val;
20958 bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian,
20959 false);
20960 if (is_big_endian) {
20961 BigInt shift_amt;
20962 bigint_init_unsigned(&shift_amt, packed_bits_size);
20963 BigInt shifted;
20964 bigint_shl(&shifted, &big_int, &shift_amt);
20965 bigint_or(&big_int, &shifted, &child_val);
20966 } else {
20967 BigInt shift_amt;
20968 bigint_init_unsigned(&shift_amt, used_bits);
20969 BigInt child_val_shifted;
20970 bigint_shl(&child_val_shifted, &child_val, &shift_amt);
20971 BigInt tmp;
20972 bigint_or(&tmp, &big_int, &child_val_shifted);
20973 big_int = tmp;
20974 used_bits += packed_bits_size;
20975 }
20976 src_i += 1;
20977 }
20978 bigint_write_twos_complement(&big_int, buf + offset, big_int_bit_count, is_big_endian);
20979 offset += big_int_byte_count;
20980 gen_i += 1;
20981 }
20982 }
20983 }
20984 return;
2088920985 case ZigTypeIdOptional:
2089020986 zig_panic("TODO buf_write_value_bytes maybe type");
2089120987 case ZigTypeIdErrorUnion:
......@@ -21012,8 +21108,62 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
2101221108 }
2101321109 return ErrorNone;
2101421110 }
21015 case ContainerLayoutPacked:
21016 zig_panic("TODO buf_read_value_bytes packed struct");
21111 case ContainerLayoutPacked: {
21112 size_t src_field_count = val->type->data.structure.src_field_count;
21113 val->data.x_struct.fields = create_const_vals(src_field_count);
21114 size_t gen_field_count = val->type->data.structure.gen_field_count;
21115 size_t gen_i = 0;
21116 size_t src_i = 0;
21117 size_t offset = 0;
21118 bool is_big_endian = codegen->is_big_endian;
21119 uint8_t child_buf_prealloc[16];
21120 size_t child_buf_len = 16;
21121 uint8_t *child_buf = child_buf_prealloc;
21122 while (gen_i < gen_field_count) {
21123 LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref,
21124 (unsigned)gen_i);
21125 size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type);
21126 size_t big_int_byte_count = big_int_bit_count / 8;
21127 if (big_int_byte_count > child_buf_len) {
21128 child_buf = allocate_nonzero<uint8_t>(big_int_byte_count);
21129 child_buf_len = big_int_byte_count;
21130 }
21131 BigInt big_int;
21132 bigint_read_twos_complement(&big_int, buf + offset, big_int_bit_count, is_big_endian, false);
21133 while (src_i < src_field_count) {
21134 TypeStructField *field = &val->type->data.structure.fields[src_i];
21135 assert(field->gen_index != SIZE_MAX);
21136 if (field->gen_index != gen_i)
21137 break;
21138 ConstExprValue *field_val = &val->data.x_struct.fields[src_i];
21139 field_val->special = ConstValSpecialStatic;
21140 field_val->type = field->type_entry;
21141 uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry);
21142
21143 BigInt child_val;
21144 if (is_big_endian) {
21145 zig_panic("TODO buf_read_value_bytes packed struct big endian");
21146 } else {
21147 BigInt packed_bits_size_bi;
21148 bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size);
21149 bigint_truncate(&child_val, &big_int, packed_bits_size, false);
21150 BigInt tmp;
21151 bigint_shr(&tmp, &big_int, &packed_bits_size_bi);
21152 big_int = tmp;
21153 }
21154
21155 bigint_write_twos_complement(&child_val, child_buf, big_int_bit_count, is_big_endian);
21156 if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) {
21157 return err;
21158 }
21159
21160 src_i += 1;
21161 }
21162 offset += big_int_byte_count;
21163 gen_i += 1;
21164 }
21165 return ErrorNone;
21166 }
2101721167 }
2101821168 zig_unreachable();
2101921169 case ZigTypeIdOptional:
test/compile_errors.zig+2-2
......@@ -318,12 +318,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
318318 "reading past end of pointer casted array",
319319 \\comptime {
320320 \\ const array = "aoeu";
321 \\ const slice = array[2..];
321 \\ const slice = array[1..];
322322 \\ const int_ptr = @ptrCast(*const u24, slice.ptr);
323323 \\ const deref = int_ptr.*;
324324 \\}
325325 ,
326 ".tmp_source.zig:5:26: error: attempt to read 3 bytes from [4]u8 at index 2 which is 2 bytes",
326 ".tmp_source.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes",
327327 );
328328
329329 cases.add(
test/stage1/behavior.zig+1
......@@ -11,6 +11,7 @@ comptime {
1111 _ = @import("behavior/bswap.zig");
1212 _ = @import("behavior/bugs/1076.zig");
1313 _ = @import("behavior/bugs/1111.zig");
14 _ = @import("behavior/bugs/1120.zig");
1415 _ = @import("behavior/bugs/1277.zig");
1516 _ = @import("behavior/bugs/1322.zig");
1617 _ = @import("behavior/bugs/1381.zig");
test/stage1/behavior/bitcast.zig+32
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const expect = std.testing.expect;
34const maxInt = std.math.maxInt;
45
......@@ -34,3 +35,34 @@ test "@bitCast extern enum to its integer type" {
3435 SOCK.testBitCastExternEnum();
3536 comptime SOCK.testBitCastExternEnum();
3637}
38
39test "@bitCast packed structs at runtime and comptime" {
40 const Full = packed struct {
41 number: u16,
42 };
43 const Divided = packed struct {
44 half1: u8,
45 quarter3: u4,
46 quarter4: u4,
47 };
48 const S = struct {
49 fn doTheTest() void {
50 var full = Full{ .number = 0x1234 };
51 var two_halves = @bitCast(Divided, full);
52 switch (builtin.endian) {
53 builtin.Endian.Big => {
54 expect(two_halves.half1 == 0x12);
55 expect(two_halves.quarter3 == 0x3);
56 expect(two_halves.quarter4 == 0x4);
57 },
58 builtin.Endian.Little => {
59 expect(two_halves.half1 == 0x34);
60 expect(two_halves.quarter3 == 0x2);
61 expect(two_halves.quarter4 == 0x1);
62 },
63 }
64 }
65 };
66 S.doTheTest();
67 comptime S.doTheTest();
68}
test/stage1/behavior/bugs/1120.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const A = packed struct {
5 a: u2,
6 b: u6,
7};
8const B = packed struct {
9 q: u8,
10 a: u2,
11 b: u6,
12};
13test "bug 1120" {
14 var a = A{ .a = 2, .b = 2 };
15 var b = B{ .q = 22, .a = 3, .b = 2 };
16 var t: usize = 0;
17 const ptr = switch (t) {
18 0 => &a.a,
19 1 => &b.a,
20 else => unreachable,
21 };
22 expect(ptr.* == 2);
23}