diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2019-02-20 22:40:41 -0500 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2019-02-20 22:40:41 -0500 |
| commit | 3ee9d06cbdb6bcaf561e7215c4c103c7ad65a72d (patch) | |
| tree | c9f612c22c8e2d2e1191a5aab8fc2bfaebf4cfcd /src/ir.cpp | |
| parent | 079728752eca4cffbb4f7e8dc06d5e23b81d7627 (diff) | |
| download | zig-3ee9d06cbdb6bcaf561e7215c4c103c7ad65a72d.tar.gz zig-3ee9d06cbdb6bcaf561e7215c4c103c7ad65a72d.zip | |
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
Diffstat (limited to 'src/ir.cpp')
| -rw-r--r-- | src/ir.cpp | 178 |
1 files changed, 164 insertions, 14 deletions
diff --git a/src/ir.cpp b/src/ir.cpp index 0eb1f2d533..177133788f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -198,10 +198,11 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; break; } - case ConstPtrSpecialBaseStruct: - result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ - const_val->data.x_ptr.data.base_struct.field_index]; + case ConstPtrSpecialBaseStruct: { + ConstExprValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; + result = &struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index]; break; + } case ConstPtrSpecialBaseErrorUnionCode: result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; break; @@ -230,20 +231,55 @@ static bool is_opt_err_set(ZigType *ty) { (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); } +// This function returns true when you can change the type of a ConstExprValue and the +// value remains meaningful. static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; - if (a->id == b->id) - return true; - if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; if (is_opt_err_set(a) && is_opt_err_set(b)) return true; - return false; + if (a->id != b->id) + return false; + + switch (a->id) { + case ZigTypeIdInvalid: + case ZigTypeIdUnreachable: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdPointer: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdNamespace: + case ZigTypeIdBoundFn: + case ZigTypeIdErrorSet: + case ZigTypeIdOpaque: + return true; + case ZigTypeIdFloat: + return a->data.floating.bit_count == b->data.floating.bit_count; + case ZigTypeIdInt: + return a->data.integral.is_signed == b->data.integral.is_signed; + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdArgTuple: + case ZigTypeIdPromise: + case ZigTypeIdVector: + return false; + } + zig_unreachable(); } static bool ir_should_inline(IrExecutable *exec, Scope *scope) { @@ -14421,12 +14457,11 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; - // We don't need to read the padding bytes, so we look at type_size_store bytes - size_t src_size = type_size_store(codegen, pointee->type); - size_t dst_size = type_size_store(codegen, out_val->type); + size_t src_size = type_size(codegen, pointee->type); + size_t dst_size = type_size(codegen, out_val->type); if (dst_size <= src_size) { - if (types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { + if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst); return ErrorNone; } @@ -20885,7 +20920,68 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ZigTypeIdVector: return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len); case ZigTypeIdStruct: - zig_panic("TODO buf_write_value_bytes struct type"); + switch (val->type->data.structure.layout) { + case ContainerLayoutAuto: + zig_unreachable(); + case ContainerLayoutExtern: + zig_panic("TODO buf_write_value_bytes extern struct"); + case ContainerLayoutPacked: { + size_t src_field_count = val->type->data.structure.src_field_count; + size_t gen_field_count = val->type->data.structure.gen_field_count; + size_t gen_i = 0; + size_t src_i = 0; + size_t offset = 0; + bool is_big_endian = codegen->is_big_endian; + uint8_t child_buf_prealloc[16]; + size_t child_buf_len = 16; + uint8_t *child_buf = child_buf_prealloc; + while (gen_i < gen_field_count) { + LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref, + (unsigned)gen_i); + size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type); + size_t big_int_byte_count = big_int_bit_count / 8; + if (big_int_byte_count > child_buf_len) { + child_buf = allocate_nonzero<uint8_t>(big_int_byte_count); + child_buf_len = big_int_byte_count; + } + BigInt big_int; + bigint_init_unsigned(&big_int, 0); + size_t used_bits = 0; + while (src_i < src_field_count) { + TypeStructField *field = &val->type->data.structure.fields[src_i]; + assert(field->gen_index != SIZE_MAX); + if (field->gen_index != gen_i) + break; + uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); + buf_write_value_bytes(codegen, child_buf, &val->data.x_struct.fields[src_i]); + BigInt child_val; + bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian, + false); + if (is_big_endian) { + BigInt shift_amt; + bigint_init_unsigned(&shift_amt, packed_bits_size); + BigInt shifted; + bigint_shl(&shifted, &big_int, &shift_amt); + bigint_or(&big_int, &shifted, &child_val); + } else { + BigInt shift_amt; + bigint_init_unsigned(&shift_amt, used_bits); + BigInt child_val_shifted; + bigint_shl(&child_val_shifted, &child_val, &shift_amt); + BigInt tmp; + bigint_or(&tmp, &big_int, &child_val_shifted); + big_int = tmp; + used_bits += packed_bits_size; + } + src_i += 1; + } + bigint_write_twos_complement(&big_int, buf + offset, big_int_bit_count, is_big_endian); + offset += big_int_byte_count; + gen_i += 1; + } + } + } + return; case ZigTypeIdOptional: zig_panic("TODO buf_write_value_bytes maybe type"); case ZigTypeIdErrorUnion: @@ -21012,8 +21108,62 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou } return ErrorNone; } - case ContainerLayoutPacked: - zig_panic("TODO buf_read_value_bytes packed struct"); + case ContainerLayoutPacked: { + size_t src_field_count = val->type->data.structure.src_field_count; + val->data.x_struct.fields = create_const_vals(src_field_count); + size_t gen_field_count = val->type->data.structure.gen_field_count; + size_t gen_i = 0; + size_t src_i = 0; + size_t offset = 0; + bool is_big_endian = codegen->is_big_endian; + uint8_t child_buf_prealloc[16]; + size_t child_buf_len = 16; + uint8_t *child_buf = child_buf_prealloc; + while (gen_i < gen_field_count) { + LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref, + (unsigned)gen_i); + size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type); + size_t big_int_byte_count = big_int_bit_count / 8; + if (big_int_byte_count > child_buf_len) { + child_buf = allocate_nonzero<uint8_t>(big_int_byte_count); + child_buf_len = big_int_byte_count; + } + BigInt big_int; + bigint_read_twos_complement(&big_int, buf + offset, big_int_bit_count, is_big_endian, false); + while (src_i < src_field_count) { + TypeStructField *field = &val->type->data.structure.fields[src_i]; + assert(field->gen_index != SIZE_MAX); + if (field->gen_index != gen_i) + break; + ConstExprValue *field_val = &val->data.x_struct.fields[src_i]; + field_val->special = ConstValSpecialStatic; + field_val->type = field->type_entry; + uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); + + BigInt child_val; + if (is_big_endian) { + zig_panic("TODO buf_read_value_bytes packed struct big endian"); + } else { + BigInt packed_bits_size_bi; + bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size); + bigint_truncate(&child_val, &big_int, packed_bits_size, false); + BigInt tmp; + bigint_shr(&tmp, &big_int, &packed_bits_size_bi); + big_int = tmp; + } + + bigint_write_twos_complement(&child_val, child_buf, big_int_bit_count, is_big_endian); + if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) { + return err; + } + + src_i += 1; + } + offset += big_int_byte_count; + gen_i += 1; + } + return ErrorNone; + } } zig_unreachable(); case ZigTypeIdOptional: |
