authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-09-20 23:53:06-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-09-23 13:04:56-04:00
log2fddd767ba20374e7677003c101e60f470c3804c
tree91ffbed5086771488201cebd82be6e1554ac14b5
parentce919ccf45951856a762ffdb8ef850301cd8c588

sema: add support for unions in readFromMemory and writeToMemory


10 files changed, 160 insertions(+), 14 deletions(-)

src/Module.zig+28
......@@ -6607,6 +6607,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in
66076607
66086608pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 {
66096609 const ip = &mod.intern_pool;
6610 if (enum_tag.toIntern() == .undef) return null;
66106611 assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty);
66116612 const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type;
66126613 return enum_type.tagValueIndex(ip, enum_tag.toIntern());
......@@ -6672,3 +6673,30 @@ pub fn structPackedFieldBitOffset(
66726673 }
66736674 unreachable; // index out of bounds
66746675}
6676
6677pub fn unionLargestField(mod: *Module, u: InternPool.UnionType) struct {
6678 ty: Type,
6679 index: u32,
6680 size: u64,
6681} {
6682 const fields = u.field_types.get(&mod.intern_pool);
6683 assert(fields.len != 0);
6684 var largest_field_ty: Type = undefined;
6685 var largest_field_size: u64 = 0;
6686 var largest_field_index: u32 = 0;
6687 for (fields, 0..) |union_field, i| {
6688 const field_ty = union_field.toType();
6689 const size: u32 = @intCast(field_ty.abiSize(mod));
6690 if (size > largest_field_size) {
6691 largest_field_ty = field_ty;
6692 largest_field_size = size;
6693 largest_field_index = @intCast(i);
6694 }
6695 }
6696
6697 return .{
6698 .ty = largest_field_ty,
6699 .index = largest_field_index,
6700 .size = largest_field_size,
6701 };
6702}
src/Sema.zig+13-3
......@@ -29740,10 +29740,15 @@ fn storePtrVal(
2974029740 error.OutOfMemory => return error.OutOfMemory,
2974129741 error.ReinterpretDeclRef => unreachable,
2974229742 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
29743 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
29743 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}),
2974429744 };
2974529745
29746 reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue();
29746 const val = Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena) catch |err| switch (err) {
29747 error.OutOfMemory => return error.OutOfMemory,
29748 error.IllDefinedMemoryLayout => unreachable,
29749 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
29750 };
29751 reinterpret.val_ptr.* = (try val.intern(mut_kit.ty, mod)).toValue();
2974729752 },
2974829753 .bad_decl_ty, .bad_ptr_ty => {
2974929754 // TODO show the decl declaration site in a note and explain whether the decl
......@@ -30655,7 +30660,12 @@ fn bitCastVal(
3065530660 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
3065630661 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
3065730662 };
30658 return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena);
30663
30664 return Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena) catch |err| switch (err) {
30665 error.OutOfMemory => return error.OutOfMemory,
30666 error.IllDefinedMemoryLayout => unreachable,
30667 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{new_ty.fmt(mod)}),
30668 };
3065930669}
3066030670
3066130671fn coerceArrayPtrToSlice(
src/arch/wasm/CodeGen.zig+4-1
......@@ -3259,7 +3259,10 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
32593259 .un => |un| {
32603260 // in this case we have a packed union which will not be passed by reference.
32613261 const union_obj = mod.typeToUnion(ty).?;
3262 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
3262 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: {
3263 assert(union_obj.getLayout(ip) == .Extern);
3264 break :f mod.unionLargestField(union_obj).index;
3265 };
32633266 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
32643267 return func.lowerConstant(un.val.toValue(), field_ty);
32653268 },
src/codegen.zig+5-1
......@@ -583,7 +583,11 @@ pub fn generateSymbol(
583583 }
584584
585585 const union_obj = mod.typeToUnion(typed_value.ty).?;
586 const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
586 const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod) orelse f: {
587 assert(union_obj.getLayout(ip) == .Extern);
588 break :f mod.unionLargestField(union_obj).index;
589 };
590
587591 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
588592 if (!field_ty.hasRuntimeBits(mod)) {
589593 try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
src/codegen/c.zig+4-1
......@@ -1439,7 +1439,10 @@ pub const DeclGen = struct {
14391439 }
14401440
14411441 const union_obj = mod.typeToUnion(ty).?;
1442 const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
1442 const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: {
1443 assert(union_obj.getLayout(ip) == .Extern);
1444 break :f mod.unionLargestField(union_obj).index;
1445 };
14431446 const field_ty = union_obj.field_types.get(ip)[field_i].toType();
14441447 const field_name = union_obj.field_names.get(ip)[field_i];
14451448 if (union_obj.getLayout(ip) == .Packed) {
src/codegen/llvm.zig+4-1
......@@ -4108,7 +4108,10 @@ pub const Object = struct {
41084108 if (layout.payload_size == 0) return o.lowerValue(un.tag);
41094109
41104110 const union_obj = mod.typeToUnion(ty).?;
4111 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
4111 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: {
4112 assert(union_obj.getLayout(ip) == .Extern);
4113 break :f mod.unionLargestField(union_obj).index;
4114 };
41124115
41134116 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
41144117 if (union_obj.getLayout(ip) == .Packed) {
src/codegen/spirv.zig+4-1
......@@ -838,7 +838,10 @@ pub const DeclGen = struct {
838838 return dg.todo("packed union constants", .{});
839839 }
840840
841 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module).?;
841 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module) orelse f: {
842 assert(union_obj.getLayout(ip) == .Extern);
843 break :f mod.unionLargestField(union_obj).index;
844 };
842845 const active_field_ty = union_obj.field_types.get(ip)[active_field].toType();
843846
844847 const has_tag = layout.tag_size != 0;
src/type.zig+6-2
......@@ -1929,8 +1929,12 @@ pub const Type = struct {
19291929 pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type {
19301930 const ip = &mod.intern_pool;
19311931 const union_obj = mod.typeToUnion(ty).?;
1932 const index = mod.unionTagFieldIndex(union_obj, enum_tag).?;
1933 return union_obj.field_types.get(ip)[index].toType();
1932 const union_fields = union_obj.field_types.get(ip);
1933 if (mod.unionTagFieldIndex(union_obj, enum_tag)) |index| {
1934 return union_fields[index].toType();
1935 } else {
1936 return mod.unionLargestField(union_obj).ty;
1937 }
19341938 }
19351939
19361940 pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 {
src/value.zig+69-4
......@@ -704,7 +704,22 @@ pub const Value = struct {
704704 },
705705 .Union => switch (ty.containerLayout(mod)) {
706706 .Auto => return error.IllDefinedMemoryLayout,
707 .Extern => return error.Unimplemented,
707 .Extern => {
708 const union_obj = mod.typeToUnion(ty).?;
709 const union_tag = val.unionTag(mod);
710
711 const field_type, const field_index = if (mod.unionTagFieldIndex(union_obj, union_tag)) |field_index| .{
712 union_obj.field_types.get(&mod.intern_pool)[field_index].toType(),
713 field_index,
714 } else f: {
715 const largest_field = mod.unionLargestField(union_obj);
716 break :f .{ largest_field.ty, largest_field.index };
717 };
718
719 const field_val = try val.fieldValue(mod, field_index);
720 const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
721 return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
722 },
708723 .Packed => {
709724 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
710725 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
......@@ -856,7 +871,11 @@ pub const Value = struct {
856871 mod: *Module,
857872 buffer: []const u8,
858873 arena: Allocator,
859 ) Allocator.Error!Value {
874 ) error{
875 IllDefinedMemoryLayout,
876 Unimplemented,
877 OutOfMemory,
878 }!Value {
860879 const ip = &mod.intern_pool;
861880 const target = mod.getTarget();
862881 const endian = target.cpu.arch.endian();
......@@ -966,6 +985,26 @@ pub const Value = struct {
966985 .name = name,
967986 } })).toValue();
968987 },
988 .Union => switch (ty.containerLayout(mod)) {
989 .Auto => return error.IllDefinedMemoryLayout,
990 .Extern => {
991 const union_obj = mod.typeToUnion(ty).?;
992 const largest_field = mod.unionLargestField(union_obj);
993 const field_size: usize = @intCast(largest_field.size);
994 const val = try (try readFromMemory(largest_field.ty, mod, buffer[0..field_size], arena)).intern(largest_field.ty, mod);
995 return (try mod.intern(.{
996 .un = .{
997 .ty = ty.toIntern(),
998 .tag = .undef,
999 .val = val,
1000 },
1001 })).toValue();
1002 },
1003 .Packed => {
1004 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1005 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1006 },
1007 },
9691008 .Pointer => {
9701009 assert(!ty.isSlice(mod)); // No well defined layout.
9711010 const int_val = try readFromMemory(Type.usize, mod, buffer, arena);
......@@ -987,7 +1026,7 @@ pub const Value = struct {
9871026 },
9881027 } })).toValue();
9891028 },
990 else => @panic("TODO implement readFromMemory for more types"),
1029 else => return error.Unimplemented,
9911030 }
9921031 }
9931032
......@@ -1001,7 +1040,10 @@ pub const Value = struct {
10011040 buffer: []const u8,
10021041 bit_offset: usize,
10031042 arena: Allocator,
1004 ) Allocator.Error!Value {
1043 ) error{
1044 IllDefinedMemoryLayout,
1045 OutOfMemory,
1046 }!Value {
10051047 const ip = &mod.intern_pool;
10061048 const target = mod.getTarget();
10071049 const endian = target.cpu.arch.endian();
......@@ -1098,6 +1140,21 @@ pub const Value = struct {
10981140 .storage = .{ .elems = field_vals },
10991141 } })).toValue();
11001142 },
1143 .Union => switch (ty.containerLayout(mod)) {
1144 .Auto => return error.IllDefinedMemoryLayout,
1145 .Extern => unreachable, // Handled by non-packed readFromMemory
1146 .Packed => {
1147 const union_obj = mod.typeToUnion(ty).?;
1148 const largest_field = mod.unionLargestField(union_obj);
1149 const un_tag_val = try mod.enumValueFieldIndex(union_obj.enum_tag_ty.toType(), largest_field.index);
1150 const un_val = try (try readFromPackedMemory(largest_field.ty, mod, buffer, bit_offset, arena)).intern(largest_field.ty, mod);
1151 return (try mod.intern(.{ .un = .{
1152 .ty = ty.toIntern(),
1153 .tag = un_tag_val.ip_index,
1154 .val = un_val,
1155 } })).toValue();
1156 },
1157 },
11011158 .Pointer => {
11021159 assert(!ty.isSlice(mod)); // No well defined layout.
11031160 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
......@@ -1713,6 +1770,14 @@ pub const Value = struct {
17131770 };
17141771 }
17151772
1773 pub fn unionValue(val: Value, mod: *Module) Value {
1774 if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;
1775 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1776 .un => |un| un.val.toValue(),
1777 else => unreachable,
1778 };
1779 }
1780
17161781 /// Returns a pointer to the element value at the index.
17171782 pub fn elemPtr(
17181783 val: Value,
test/behavior/comptime_memory.zig+23
......@@ -1,3 +1,4 @@
1const std = @import("std");
12const builtin = @import("builtin");
23const endian = builtin.cpu.arch.endian();
34const testing = @import("std").testing;
......@@ -452,3 +453,25 @@ test "type pun null pointer-like optional" {
452453 // note that expectEqual hides the bug
453454 try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null);
454455}
456
457test "reinterpret extern union" {
458 const U = extern union {
459 a: u32,
460 b: u64,
461 };
462
463 comptime var u: U = undefined;
464 comptime @memset(std.mem.asBytes(&u), 42);
465 try testing.expectEqual(@as(u64, 0x2a2a2a2a_2a2a2a2a), u.b);
466}
467
468test "reinterpret packed union" {
469 const U = packed union {
470 a: u32,
471 b: u64,
472 };
473
474 comptime var u: U = undefined;
475 comptime @memset(std.mem.asBytes(&u), 42);
476 try testing.expectEqual(@as(u64, 0x2a2a2a2a_2a2a2a2a), u.b);
477}