authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-19 19:17:34+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-19 22:29:36+01:00
logb9fe6a93ff51ecb5ce770c78f463c38a0620de49
treea155cb583568eb2136dfb8fd42ff40b9c41245f1
parent1fe1e4d29207de8c972fcc1b61266fbaddb07d63
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Re-use genTypedValue for constants

When a constant will be passed by reference, such as a struct, we will call into genTypedValue to lower the constant to bytes and store them into the `rodata` section. We will then return the address of this constant as a `WValue`. This change means we will have all constants lowered during compilation time, and no longer have to sacrifice runtime to lower them onto the stack.

2 files changed, 122 insertions(+), 244 deletions(-)

src/arch/wasm/CodeGen.zig+121-244
...@@ -599,7 +599,41 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {...@@ -599,7 +599,41 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
599 const val = self.air.value(ref).?;599 const val = self.air.value(ref).?;
600 const ty = self.air.typeOf(ref);600 const ty = self.air.typeOf(ref);
601 if (!ty.hasCodeGenBits() and !ty.isInt()) return WValue{ .none = {} };601 if (!ty.hasCodeGenBits() and !ty.isInt()) return WValue{ .none = {} };
602 const result = try self.lowerConstant(val, ty);602
603 // When we need to pass the value by reference (such as a struct), we will
604 // leverage `genTypedValue` to lower the constant to bytes and emit it
605 // to the 'rodata' section. We then return the index into the section as `WValue`.
606 //
607 // In the other cases, we will simply lower the constant to a value that fits
608 // into a single local (such as a pointer, integer, bool, etc).
609 const result = if (isByRef(ty, self.target)) blk: {
610 var value_bytes = std.ArrayList(u8).init(self.gpa);
611 defer value_bytes.deinit();
612
613 var decl_gen: DeclGen = .{
614 .bin_file = self.bin_file,
615 .decl = self.decl,
616 .err_msg = undefined,
617 .gpa = self.gpa,
618 .module = self.module,
619 .code = &value_bytes,
620 .symbol_index = try self.bin_file.createLocalSymbol(self.decl, ty),
621 };
622 const result = decl_gen.genTypedValue(ty, val, value_bytes.writer()) catch |err| {
623 // When a codegen error occured, take ownership of the error message
624 if (err == error.CodegenFail) {
625 self.err_msg = decl_gen.err_msg;
626 }
627 return err;
628 };
629 const code = switch (result) {
630 .appended => value_bytes.items,
631 .externally_managed => |data| data,
632 };
633 try self.bin_file.updateLocalSymbolCode(self.decl, decl_gen.symbol_index, code);
634 break :blk WValue{ .memory = decl_gen.symbol_index };
635 } else try self.lowerConstant(val, ty);
636
603 gop.value_ptr.* = result;637 gop.value_ptr.* = result;
604 return result;638 return result;
605}639}
...@@ -867,15 +901,15 @@ pub const DeclGen = struct {...@@ -867,15 +901,15 @@ pub const DeclGen = struct {
867 var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target());901 var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target());
868 func_type.deinit(self.gpa);902 func_type.deinit(self.gpa);
869 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);903 ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
870 return Result.appended;904 return Result{ .appended = {} };
871 } else {905 } else {
872 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {906 const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
873 break :init_val payload.data.init;907 break :init_val payload.data.init;
874 } else decl.val;908 } else decl.val;
875 if (init_val.tag() != .unreachable_value) {909 if (init_val.tag() != .unreachable_value) {
876 return try self.genTypedValue(decl.ty, init_val, self.code.writer());910 return self.genTypedValue(decl.ty, init_val, self.code.writer());
877 }911 }
878 return Result.appended;912 return Result{ .appended = {} };
879 }913 }
880 }914 }
881915
...@@ -883,7 +917,7 @@ pub const DeclGen = struct {...@@ -883,7 +917,7 @@ pub const DeclGen = struct {
883 fn genTypedValue(self: *DeclGen, ty: Type, val: Value, writer: anytype) InnerError!Result {917 fn genTypedValue(self: *DeclGen, ty: Type, val: Value, writer: anytype) InnerError!Result {
884 if (val.isUndef()) {918 if (val.isUndef()) {
885 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));919 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
886 return Result.appended;920 return Result{ .appended = {} };
887 }921 }
888 switch (ty.zigTypeTag()) {922 switch (ty.zigTypeTag()) {
889 .Fn => {923 .Fn => {
...@@ -898,35 +932,36 @@ pub const DeclGen = struct {...@@ -898,35 +932,36 @@ pub const DeclGen = struct {
898 var opt_buf: Type.Payload.ElemType = undefined;932 var opt_buf: Type.Payload.ElemType = undefined;
899 const payload_type = ty.optionalChild(&opt_buf);933 const payload_type = ty.optionalChild(&opt_buf);
900 const is_pl = !val.isNull();934 const is_pl = !val.isNull();
935 const abi_size = @intCast(usize, ty.abiSize(self.target()));
936 const offset = abi_size - @intCast(usize, payload_type.abiSize(self.target()));
901937
902 if (!payload_type.hasCodeGenBits()) {938 if (!payload_type.hasCodeGenBits()) {
903 try writer.writeByteNTimes(@boolToInt(is_pl), 4);939 try writer.writeByteNTimes(@boolToInt(is_pl), abi_size);
904 return Result.appended;940 return Result{ .appended = {} };
905 }941 }
906942
907 if (ty.isPtrLikeOptional()) {943 if (ty.isPtrLikeOptional()) {
908 if (val.castTag(.opt_payload)) |payload| {944 if (val.castTag(.opt_payload)) |payload| {
909 return try self.genTypedValue(payload_type, payload.data, writer);945 return self.genTypedValue(payload_type, payload.data, writer);
910 } else if (!val.isNull()) {946 } else if (!val.isNull()) {
911 return try self.genTypedValue(payload_type, val, writer);947 return self.genTypedValue(payload_type, val, writer);
912 } else {948 } else {
913 try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target())));949 try writer.writeByteNTimes(0, abi_size);
914 return Result.appended;950 return Result{ .appended = {} };
915 }951 }
916 }952 }
917953
918 // `null-tag` bytes954 // `null-tag` bytes
919 try writer.writeByteNTimes(@boolToInt(is_pl), 4);955 try writer.writeByteNTimes(@boolToInt(is_pl), offset);
920 const pl_result = try self.genTypedValue(956 switch (try self.genTypedValue(
921 payload_type,957 payload_type,
922 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),958 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
923 writer,959 writer,
924 );960 )) {
925 switch (pl_result) {
926 .appended => {},961 .appended => {},
927 .externally_managed => |payload| try writer.writeAll(payload),962 .externally_managed => |payload| try writer.writeAll(payload),
928 }963 }
929 return Result.appended;964 return Result{ .appended = {} };
930 },965 },
931 .Array => switch (val.tag()) {966 .Array => switch (val.tag()) {
932 .bytes => {967 .bytes => {
...@@ -942,29 +977,68 @@ pub const DeclGen = struct {...@@ -942,29 +977,68 @@ pub const DeclGen = struct {
942 .externally_managed => |data| try writer.writeAll(data),977 .externally_managed => |data| try writer.writeAll(data),
943 }978 }
944 }979 }
945 return Result.appended;980 return Result{ .appended = {} };
981 },
982 .repeated => {
983 const array = val.castTag(.repeated).?.data;
984 const elem_ty = ty.childType();
985 const sentinel = ty.sentinel();
986 const len = ty.arrayLen();
987
988 var index: u32 = 0;
989 while (index < len) : (index += 1) {
990 switch (try self.genTypedValue(elem_ty, array, writer)) {
991 .externally_managed => |data| try writer.writeAll(data),
992 .appended => {},
993 }
994 }
995 if (sentinel) |sentinel_value| {
996 return self.genTypedValue(elem_ty, sentinel_value, writer);
997 }
998 return Result{ .appended = {} };
999 },
1000 .empty_array_sentinel => {
1001 const elem_ty = ty.childType();
1002 const sent_val = ty.sentinel().?;
1003 return self.genTypedValue(elem_ty, sent_val, writer);
946 },1004 },
947 else => return self.fail("TODO implement genTypedValue for array type value: {s}", .{@tagName(val.tag())}),1005 else => unreachable,
948 },1006 },
949 .Int => {1007 .Int => {
950 const info = ty.intInfo(self.target());1008 const info = ty.intInfo(self.target());
951 const abi_size = @intCast(usize, ty.abiSize(self.target()));1009 const abi_size = @intCast(usize, ty.abiSize(self.target()));
952 // todo: Implement integer sizes larger than 64bits1010 if (info.bits <= 64) {
953 if (info.bits > 64) return self.fail("TODO: Implement genTypedValue for integer bit size: {d}", .{info.bits});1011 var buf: [8]u8 = undefined;
954 var buf: [8]u8 = undefined;1012 if (info.signedness == .unsigned) {
955 if (info.signedness == .unsigned) {1013 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
956 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());1014 } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());
957 } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());1015 try writer.writeAll(buf[0..abi_size]);
958 try writer.writeAll(buf[0..abi_size]);1016 return Result{ .appended = {} };
959 return Result.appended;1017 }
1018 var space: Value.BigIntSpace = undefined;
1019 const bigint = val.toBigInt(&space);
1020 const iterations = @divExact(abi_size, @sizeOf(usize));
1021 for (bigint.limbs) |_, index| {
1022 const limb = bigint.limbs[bigint.limbs.len - index - 1];
1023 try writer.writeIntLittle(usize, limb);
1024 } else if (bigint.limbs.len < iterations) {
1025 // When the value is saved in less limbs than the required
1026 // abi size, we fill the remaining parts with 0's.
1027 var it_left = iterations - bigint.limbs.len;
1028 while (it_left > 0) {
1029 it_left -= 1;
1030 try writer.writeIntLittle(usize, 0);
1031 }
1032 }
1033 return Result{ .appended = {} };
960 },1034 },
961 .Enum => {1035 .Enum => {
962 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));1036 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
963 return Result.appended;1037 return Result{ .appended = {} };
964 },1038 },
965 .Bool => {1039 .Bool => {
966 try writer.writeByte(@boolToInt(val.toBool()));1040 try writer.writeByte(@boolToInt(val.toBool()));
967 return Result.appended;1041 return Result{ .appended = {} };
968 },1042 },
969 .Struct => {1043 .Struct => {
970 const field_vals = val.castTag(.@"struct").?.data;1044 const field_vals = val.castTag(.@"struct").?.data;
...@@ -976,21 +1050,21 @@ pub const DeclGen = struct {...@@ -976,21 +1050,21 @@ pub const DeclGen = struct {
976 .externally_managed => |payload| try writer.writeAll(payload),1050 .externally_managed => |payload| try writer.writeAll(payload),
977 }1051 }
978 }1052 }
979 return Result.appended;1053 return Result{ .appended = {} };
980 },1054 },
981 .Union => {1055 .Union => {
982 // TODO: Implement Union declarations1056 // TODO: Implement Union declarations
983 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));1057 try writer.writeByteNTimes(0xaa, @intCast(usize, ty.abiSize(self.target())));
984 return Result.appended;1058 return Result{ .appended = {} };
985 },1059 },
986 .Pointer => switch (val.tag()) {1060 .Pointer => switch (val.tag()) {
987 .variable => {1061 .variable => {
988 const decl = val.castTag(.variable).?.data.owner_decl;1062 const decl = val.castTag(.variable).?.data.owner_decl;
989 return try self.lowerDeclRef(ty, val, decl, writer);1063 return self.lowerDeclRef(ty, val, decl, writer);
990 },1064 },
991 .decl_ref => {1065 .decl_ref => {
992 const decl = val.castTag(.decl_ref).?.data;1066 const decl = val.castTag(.decl_ref).?.data;
993 return try self.lowerDeclRef(ty, val, decl, writer);1067 return self.lowerDeclRef(ty, val, decl, writer);
994 },1068 },
995 .slice => {1069 .slice => {
996 const slice = val.castTag(.slice).?.data;1070 const slice = val.castTag(.slice).?.data;
...@@ -1004,7 +1078,7 @@ pub const DeclGen = struct {...@@ -1004,7 +1078,7 @@ pub const DeclGen = struct {
1004 .externally_managed => |data| try writer.writeAll(data),1078 .externally_managed => |data| try writer.writeAll(data),
1005 .appended => {},1079 .appended => {},
1006 }1080 }
1007 return Result.appended;1081 return Result{ .appended = {} };
1008 },1082 },
1009 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),1083 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
1010 },1084 },
...@@ -1027,7 +1101,7 @@ pub const DeclGen = struct {...@@ -1027,7 +1101,7 @@ pub const DeclGen = struct {
1027 }1101 }
1028 }1102 }
10291103
1030 return Result.appended;1104 return Result{ .appended = {} };
1031 },1105 },
1032 .ErrorSet => {1106 .ErrorSet => {
1033 switch (val.tag()) {1107 switch (val.tag()) {
...@@ -1040,7 +1114,7 @@ pub const DeclGen = struct {...@@ -1040,7 +1114,7 @@ pub const DeclGen = struct {
1040 try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target())));1114 try writer.writeByteNTimes(0, @intCast(usize, ty.abiSize(self.target())));
1041 },1115 },
1042 }1116 }
1043 return Result.appended;1117 return Result{ .appended = {} };
1044 },1118 },
1045 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),1119 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
1046 }1120 }
...@@ -1058,7 +1132,7 @@ pub const DeclGen = struct {...@@ -1058,7 +1132,7 @@ pub const DeclGen = struct {
1058 .base = .{ .tag = .int_u64 },1132 .base = .{ .tag = .int_u64 },
1059 .data = val.sliceLen(),1133 .data = val.sliceLen(),
1060 };1134 };
1061 return try self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base), writer);1135 return self.genTypedValue(Type.usize, Value.initPayload(&slice_len.base), writer);
1062 }1136 }
10631137
1064 decl.markAlive();1138 decl.markAlive();
...@@ -1069,7 +1143,7 @@ pub const DeclGen = struct {...@@ -1069,7 +1143,7 @@ pub const DeclGen = struct {
1069 decl.link.wasm.sym_index, // target symbol index1143 decl.link.wasm.sym_index, // target symbol index
1070 @intCast(u32, self.code.items.len), // offset1144 @intCast(u32, self.code.items.len), // offset
1071 ));1145 ));
1072 return Result.appended;1146 return Result{ .appended = {} };
1073 }1147 }
1074};1148};
10751149
...@@ -1172,16 +1246,10 @@ fn allocStack(self: *Self, ty: Type) !WValue {...@@ -1172,16 +1246,10 @@ fn allocStack(self: *Self, ty: Type) !WValue {
1172 assert(ty.hasCodeGenBits());1246 assert(ty.hasCodeGenBits());
11731247
1174 // calculate needed stack space1248 // calculate needed stack space
1175 var abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch {1249 const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch {
1176 return self.fail("Given type '{}' too big to fit into stack frame", .{ty});1250 return self.fail("Given type '{}' too big to fit into stack frame", .{ty});
1177 };1251 };
11781252
1179 // We store slices as a struct with a pointer field and a length field
1180 // both being 'usize' size.
1181 if (ty.isSlice()) {
1182 abi_size = self.ptrSize() * 2;
1183 }
1184
1185 // allocate a local using wasm's pointer size1253 // allocate a local using wasm's pointer size
1186 const local = try self.allocLocal(Type.@"usize");1254 const local = try self.allocLocal(Type.@"usize");
1187 try self.moveStack(abi_size, local.local);1255 try self.moveStack(abi_size, local.local);
...@@ -1624,7 +1692,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1624,7 +1692,6 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1624 var buf: Type.Payload.ElemType = undefined;1692 var buf: Type.Payload.ElemType = undefined;
1625 const pl_ty = ty.optionalChild(&buf);1693 const pl_ty = ty.optionalChild(&buf);
1626 if (!pl_ty.hasCodeGenBits()) {1694 if (!pl_ty.hasCodeGenBits()) {
1627 // const null_val = try self.load(rhs, Type.initTag(.u8), 0);
1628 return self.store(lhs, rhs, Type.initTag(.u8), 0);1695 return self.store(lhs, rhs, Type.initTag(.u8), 0);
1629 }1696 }
16301697
...@@ -1833,61 +1900,22 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {...@@ -1833,61 +1900,22 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
1833 .signed => switch (int_info.bits) {1900 .signed => switch (int_info.bits) {
1834 0...32 => return WValue{ .imm32 = @bitCast(u32, @intCast(i32, val.toSignedInt())) },1901 0...32 => return WValue{ .imm32 = @bitCast(u32, @intCast(i32, val.toSignedInt())) },
1835 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) },1902 33...64 => return WValue{ .imm64 = @bitCast(u64, val.toSignedInt()) },
1836 65...128 => {},1903 else => unreachable,
1837 else => |bits| return self.fail("Wasm todo: lowerConstant for integer with {d} bits", .{bits}),
1838 },1904 },
1839 .unsigned => switch (int_info.bits) {1905 .unsigned => switch (int_info.bits) {
1840 0...32 => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },1906 0...32 => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1841 33...64 => return WValue{ .imm64 = val.toUnsignedInt() },1907 33...64 => return WValue{ .imm64 = val.toUnsignedInt() },
1842 65...128 => {},1908 else => unreachable,
1843 else => |bits| return self.fail("Wasm TODO: lowerConstant for integer with {d} bits", .{bits}),
1844 },1909 },
1845 }1910 }
1846 const result = try self.allocStack(ty);
1847 var space: Value.BigIntSpace = undefined;
1848 const bigint = val.toBigInt(&space);
1849 if (bigint.limbs.len == 1 and bigint.limbs[0] == 0) {
1850 return result;
1851 }
1852 if (@sizeOf(usize) != @sizeOf(u64)) {
1853 return self.fail("Wasm todo: Implement big integers for 32bit compiler", .{});
1854 }
1855
1856 for (bigint.limbs) |_, index| {
1857 const limb = bigint.limbs[bigint.limbs.len - index - 1];
1858 try self.addLabel(.local_get, result.local);
1859 try self.addImm64(limb);
1860 try self.addMemArg(.i64_store, .{ .offset = @intCast(u32, index * 8), .alignment = 8 });
1861 }
1862 return result;
1863 },1911 },
1864 .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },1912 .Bool => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1865 .Float => switch (ty.floatBits(self.target)) {1913 .Float => switch (ty.floatBits(self.target)) {
1866 0...32 => return WValue{ .float32 = val.toFloat(f32) },1914 0...32 => return WValue{ .float32 = val.toFloat(f32) },
1867 33...64 => return WValue{ .float64 = val.toFloat(f64) },1915 33...64 => return WValue{ .float64 = val.toFloat(f64) },
1868 else => |bits| return self.fail("Wasm TODO: lowerConstant for floats with {d} bits", .{bits}),1916 else => unreachable,
1869 },1917 },
1870 .Pointer => switch (val.tag()) {1918 .Pointer => switch (val.tag()) {
1871 .slice => {
1872 const result = try self.allocStack(ty);
1873 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1874 const ptr = try self.lowerConstant(val.slicePtr(), ty.slicePtrFieldType(&buf));
1875 const len = val.sliceLen();
1876 try self.store(result, ptr, Type.usize, 0);
1877 try self.addLabel(.local_get, result.local);
1878 switch (self.arch()) {
1879 .wasm32 => {
1880 try self.addImm32(@bitCast(i32, @intCast(u32, len)));
1881 try self.addMemArg(.i32_store, .{ .offset = self.ptrSize(), .alignment = self.ptrSize() });
1882 },
1883 .wasm64 => {
1884 try self.addImm64(len);
1885 try self.addMemArg(.i64_store, .{ .offset = self.ptrSize(), .alignment = self.ptrSize() });
1886 },
1887 else => unreachable,
1888 }
1889 return result;
1890 },
1891 .decl_ref => {1919 .decl_ref => {
1892 const decl = val.castTag(.decl_ref).?.data;1920 const decl = val.castTag(.decl_ref).?.data;
1893 decl.markAlive();1921 decl.markAlive();
...@@ -1941,120 +1969,16 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {...@@ -1941,120 +1969,16 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
1941 },1969 },
1942 .ErrorUnion => {1970 .ErrorUnion => {
1943 const error_type = ty.errorUnionSet();1971 const error_type = ty.errorUnionSet();
1944 const payload_type = ty.errorUnionPayload();
1945 const is_pl = val.errorUnionIsPayload();1972 const is_pl = val.errorUnionIsPayload();
1946 const err_val = if (!is_pl) val else Value.initTag(.zero);1973 const err_val = if (!is_pl) val else Value.initTag(.zero);
1947 const error_value = try self.lowerConstant(err_val, error_type);1974 return self.lowerConstant(err_val, error_type);
1948 if (!payload_type.hasCodeGenBits()) {
1949 return error_value;
1950 }
1951
1952 const result = try self.allocStack(ty);
1953 try self.store(result, error_value, error_type, 0);
1954 const payload = try self.lowerConstant(
1955 if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
1956 payload_type,
1957 );
1958 const pl_ptr = if (isByRef(payload_type, self.target))
1959 try self.buildPointerOffset(result, error_type.abiSize(self.target), .new)
1960 else
1961 result;
1962 try self.store(pl_ptr, payload, payload_type, @intCast(u32, error_type.abiSize(self.target)));
1963 return result;
1964 },1975 },
1965 .Optional => if (ty.isPtrLikeOptional()) {1976 .Optional => if (ty.isPtrLikeOptional()) {
1966 var buf: Type.Payload.ElemType = undefined;1977 var buf: Type.Payload.ElemType = undefined;
1967 return self.lowerConstant(val, ty.optionalChild(&buf));1978 return self.lowerConstant(val, ty.optionalChild(&buf));
1968 } else {1979 } else {
1969 var buf: Type.Payload.ElemType = undefined;
1970 const payload_type = ty.optionalChild(&buf);
1971 const is_pl = val.tag() == .opt_payload;1980 const is_pl = val.tag() == .opt_payload;
1972 const null_value = WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };1981 return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
1973 if (!payload_type.hasCodeGenBits()) {
1974 return null_value;
1975 }
1976
1977 const result = try self.allocStack(ty);
1978 try self.store(result, null_value, Type.initTag(.u8), 0);
1979 const payload = try self.lowerConstant(
1980 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
1981 payload_type,
1982 );
1983
1984 const offset = @intCast(u32, ty.abiSize(self.target) - payload_type.abiSize(self.target));
1985 const pl_ptr = if (self.isByRef(payload_type)) blk: {
1986 break :blk try self.buildPointerOffset(result, offset, .new);
1987 } else result;
1988 try self.store(pl_ptr, payload, payload_type, offset);
1989 return result;
1990 },
1991 .Struct => {
1992 const struct_data = val.castTag(.@"struct").?;
1993 // in case of structs, we reserve stack space and store it there.
1994 const result = try self.allocStack(ty);
1995
1996 const fields = ty.structFields();
1997 const offset = try self.copyLocal(result, ty);
1998 for (fields.values()) |field, index| {
1999 const field_value = try self.lowerConstant(struct_data.data[index], field.ty);
2000 try self.store(offset, field_value, field.ty, 0);
2001
2002 // this prevents us from emitting useless instructions when we reached the end of the loop
2003 if (index != (fields.count() - 1)) {
2004 _ = try self.buildPointerOffset(offset, field.ty.abiSize(self.target), .modify);
2005 }
2006 }
2007 return result;
2008 },
2009 .Array => {
2010 const result = try self.allocStack(ty);
2011 if (val.castTag(.bytes)) |bytes| {
2012 for (bytes.data) |byte, index| {
2013 try self.addLabel(.local_get, result.local);
2014 try self.addImm32(@intCast(i32, byte));
2015 try self.addMemArg(.i32_store8, .{ .offset = @intCast(u32, index), .alignment = 1 });
2016 }
2017 } else if (val.castTag(.array)) |array| {
2018 const elem_ty = ty.childType();
2019 const elem_size = elem_ty.abiSize(self.target);
2020 const offset = try self.copyLocal(result, ty);
2021 for (array.data) |value, index| {
2022 const elem_val = try self.lowerConstant(value, elem_ty);
2023 try self.store(offset, elem_val, elem_ty, 0);
2024
2025 if (index != (array.data.len - 1)) {
2026 _ = try self.buildPointerOffset(offset, elem_size, .modify);
2027 }
2028 }
2029 } else if (val.castTag(.repeated)) |repeated| {
2030 const value = repeated.data;
2031 const elem_ty = ty.childType();
2032 const elem_size = elem_ty.abiSize(self.target);
2033 const sentinel = ty.sentinel();
2034 const len = ty.arrayLen();
2035 const len_with_sent = len + @boolToInt(sentinel != null);
2036 const offset = try self.copyLocal(result, ty);
2037
2038 var index: u32 = 0;
2039 while (index < len_with_sent) : (index += 1) {
2040 const elem_val = if (sentinel != null and index == len)
2041 try self.lowerConstant(sentinel.?, elem_ty)
2042 else
2043 try self.lowerConstant(value, elem_ty);
2044
2045 try self.store(offset, elem_val, elem_ty, 0);
2046
2047 if (index != (len_with_sent - 1)) {
2048 _ = try self.buildPointerOffset(offset, elem_size, .modify);
2049 }
2050 }
2051 } else if (val.tag() == .empty_array_sentinel) {
2052 const elem_ty = ty.childType();
2053 const sent_val = ty.sentinel().?;
2054 const sentinel = try self.lowerConstant(sent_val, elem_ty);
2055 try self.store(result, sentinel, elem_ty, 0);
2056 } else unreachable;
2057 return result;
2058 },1982 },
2059 else => |zig_type| return self.fail("Wasm TODO: LowerConstant for zigTypeTag {s}", .{zig_type}),1983 else => |zig_type| return self.fail("Wasm TODO: LowerConstant for zigTypeTag {s}", .{zig_type}),
2060 }1984 }
...@@ -2066,27 +1990,12 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {...@@ -2066,27 +1990,12 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
2066 .Int => switch (ty.intInfo(self.target).bits) {1990 .Int => switch (ty.intInfo(self.target).bits) {
2067 0...32 => return WValue{ .imm32 = 0xaaaaaaaa },1991 0...32 => return WValue{ .imm32 = 0xaaaaaaaa },
2068 33...64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },1992 33...64 => return WValue{ .imm64 = 0xaaaaaaaaaaaaaaaa },
2069 else => |bits| return self.fail("Wasm TODO: emitUndefined for integer bitsize: {d}", .{bits}),1993 else => unreachable,
2070 },1994 },
2071 .Float => switch (ty.floatBits(self.target)) {1995 .Float => switch (ty.floatBits(self.target)) {
2072 0...32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) },1996 0...32 => return WValue{ .float32 = @bitCast(f32, @as(u32, 0xaaaaaaaa)) },
2073 33...64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) },1997 33...64 => return WValue{ .float64 = @bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa)) },
2074 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),1998 else => unreachable,
2075 },
2076 .Array, .Struct => {
2077 const result = try self.allocStack(ty);
2078 const abi_size = ty.abiSize(self.target);
2079 var offset: u32 = 0;
2080 while (offset < abi_size) : (offset += 1) {
2081 try self.emitWValue(result);
2082 try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa)));
2083 switch (self.arch()) {
2084 .wasm32 => try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }),
2085 .wasm64 => try self.addMemArg(.i64_store8, .{ .offset = offset, .alignment = 1 }),
2086 else => unreachable,
2087 }
2088 }
2089 return result;
2090 },1999 },
2091 .Pointer => switch (self.arch()) {2000 .Pointer => switch (self.arch()) {
2092 .wasm32 => return WValue{ .imm32 = 0xaaaaaaaa },2001 .wasm32 => return WValue{ .imm32 = 0xaaaaaaaa },
...@@ -2099,42 +2008,10 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {...@@ -2099,42 +2008,10 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!WValue {
2099 if (ty.isPtrLikeOptional()) {2008 if (ty.isPtrLikeOptional()) {
2100 return self.emitUndefined(pl_ty);2009 return self.emitUndefined(pl_ty);
2101 }2010 }
2102 if (!pl_ty.hasCodeGenBits()) {2011 return WValue{ .imm32 = 0xaaaaaaaa };
2103 return self.emitUndefined(Type.initTag(.u8));
2104 }
2105 const result = try self.allocStack(ty);
2106 const abi_size = ty.abiSize(self.target);
2107 var offset: u32 = 0;
2108 while (offset < abi_size) : (offset += 1) {
2109 try self.emitWValue(result);
2110 try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa)));
2111 switch (self.arch()) {
2112 .wasm32 => try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }),
2113 .wasm64 => try self.addMemArg(.i64_store8, .{ .offset = offset, .alignment = 1 }),
2114 else => unreachable,
2115 }
2116 }
2117 return result;
2118 },2012 },
2119 .ErrorUnion => {2013 .ErrorUnion => {
2120 // const error_set = ty.errorUnionSet();2014 return WValue{ .imm32 = 0xaaaaaaaa };
2121 const pl_ty = ty.errorUnionPayload();
2122 if (!pl_ty.hasCodeGenBits()) {
2123 return WValue{ .imm32 = 0xaaaaaaaa };
2124 }
2125 const result = try self.allocStack(ty);
2126 const abi_size = ty.abiSize(self.target);
2127 var offset: u32 = 0;
2128 while (offset < abi_size) : (offset += 1) {
2129 try self.emitWValue(result);
2130 try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa)));
2131 switch (self.arch()) {
2132 .wasm32 => try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }),
2133 .wasm64 => try self.addMemArg(.i64_store8, .{ .offset = offset, .alignment = 1 }),
2134 else => unreachable,
2135 }
2136 }
2137 return result;
2138 },2015 },
2139 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}),2016 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty.zigTypeTag()}),
2140 }2017 }
src/link/Wasm.zig+1
...@@ -323,6 +323,7 @@ pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {...@@ -323,6 +323,7 @@ pub fn createLocalSymbol(self: *Wasm, decl: *Module.Decl, ty: Type) !u32 {
323323
324 var atom = Atom.empty;324 var atom = Atom.empty;
325 atom.alignment = ty.abiAlignment(self.base.options.target);325 atom.alignment = ty.abiAlignment(self.base.options.target);
326 try self.symbols.ensureUnusedCapacity(self.base.allocator, 1);
326327
327 if (self.symbols_free_list.popOrNull()) |index| {328 if (self.symbols_free_list.popOrNull()) |index| {
328 atom.sym_index = index;329 atom.sym_index = index;