authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-11-19 22:15:53+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-11-21 21:07:55+01:00
logec5220405b0218f892f6a1636ddd04d791017309
treed69aa5d8c4e876a61da6335d79149e8278194dde
parent460b3d39eae8d294efbe2e5762ea38c93c352d63
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Implement optionals and ensure correct alignment

Rather than writing the alignment in its natural form, wasm binaries encode the alignment of types as the exponent of a power of 2. So rather than performing this encoding during AIR->MIR, we do this while emitting MIR->binary encoding. This allows us to keep alignment logic to its natural form while doing calculations (Which is what we need during linking as well). We also implement optionals and pointers to an optional.

2 files changed, 61 insertions(+), 22 deletions(-)

src/arch/wasm/CodeGen.zig+57-21
...@@ -211,7 +211,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -211,7 +211,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
211 32 => switch (args.valtype1.?) {211 32 => switch (args.valtype1.?) {
212 .i64 => return .i64_store32,212 .i64 => return .i64_store32,
213 .i32 => return .i32_store,213 .i32 => return .i32_store,
214 .f32, .f64 => unreachable,214 .f32 => return .f32_store,
215 .f64 => unreachable,
215 },216 },
216 64 => switch (args.valtype1.?) {217 64 => switch (args.valtype1.?) {
217 .i64 => return .i64_store,218 .i64 => return .i64_store,
...@@ -680,6 +681,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {...@@ -680,6 +681,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
680 .ErrorSet,681 .ErrorSet,
681 .Struct,682 .Struct,
682 .ErrorUnion,683 .ErrorUnion,
684 .Optional,
683 => wasm.Valtype.i32,685 => wasm.Valtype.i32,
684 else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),686 else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),
685 };687 };
...@@ -878,7 +880,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu...@@ -878,7 +880,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
878880
879 const ret_ty = fn_ty.fnReturnType();881 const ret_ty = fn_ty.fnReturnType();
880 switch (ret_ty.zigTypeTag()) {882 switch (ret_ty.zigTypeTag()) {
881 .ErrorUnion => result.return_value = try self.allocLocal(Type.initTag(.i32)),883 .ErrorUnion, .Optional => result.return_value = try self.allocLocal(Type.initTag(.i32)),
882 .Int, .Float, .Bool, .Void, .NoReturn => {},884 .Int, .Float, .Bool, .Void, .NoReturn => {},
883 else => return self.fail("TODO: Implement function return type {}", .{ret_ty}),885 else => return self.fail("TODO: Implement function return type {}", .{ret_ty}),
884 }886 }
...@@ -1063,7 +1065,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1063,7 +1065,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
10631065
1064 const ret_ty = target.ty.fnReturnType();1066 const ret_ty = target.ty.fnReturnType();
1065 switch (ret_ty.zigTypeTag()) {1067 switch (ret_ty.zigTypeTag()) {
1066 .ErrorUnion => {1068 .ErrorUnion, .Optional => {
1067 const result_local = try self.allocLocal(ret_ty);1069 const result_local = try self.allocLocal(ret_ty);
1068 try self.addLabel(.local_set, result_local.local);1070 try self.addLabel(.local_set, result_local.local);
1069 return result_local;1071 return result_local;
...@@ -1111,14 +1113,15 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1111,14 +1113,15 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1111 .ErrorUnion, .Optional => {1113 .ErrorUnion, .Optional => {
1112 var buf: Type.Payload.ElemType = undefined;1114 var buf: Type.Payload.ElemType = undefined;
1113 const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);1115 const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);
1114 const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.i8);1116 const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8);
1115 const payload_offset = @intCast(u32, tag_ty.abiSize(self.target) / 8);1117 const payload_offset = @intCast(u32, tag_ty.abiSize(self.target));
1118
1116 if (rhs == .constant) {1119 if (rhs == .constant) {
1117 // constant will contain both tag and payload,1120 // constant will contain both tag and payload,
1118 // so save those in 2 temporary locals before storing them1121 // so save those in 2 temporary locals before storing them
1119 // in memory1122 // in memory
1120 try self.emitWValue(rhs);1123 try self.emitWValue(rhs);
1121 const tag_local = try self.allocLocal(Type.initTag(.i32));1124 const tag_local = try self.allocLocal(tag_ty);
1122 const payload_local = try self.allocLocal(payload_ty);1125 const payload_local = try self.allocLocal(payload_ty);
11231126
1124 try self.addLabel(.local_set, payload_local.local);1127 try self.addLabel(.local_set, payload_local.local);
...@@ -1147,8 +1150,14 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1147,8 +1150,14 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1147 });1150 });
11481151
1149 // store rhs value at stack pointer's location in memory1152 // store rhs value at stack pointer's location in memory
1150 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = offset, .alignment = 0 });1153 const mem_arg_index = try self.addExtra(Mir.MemArg{
1151 try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } });1154 .offset = offset,
1155 .alignment = ty.abiAlignment(self.target),
1156 });
1157 try self.addInst(.{
1158 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1159 .data = .{ .payload = mem_arg_index },
1160 });
1152}1161}
11531162
1154fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1163fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1157,8 +1166,11 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1157,8 +1166,11 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1157 const ty = self.air.getRefType(ty_op.ty);1166 const ty = self.air.getRefType(ty_op.ty);
11581167
1159 return switch (ty.zigTypeTag()) {1168 return switch (ty.zigTypeTag()) {
1160 .Struct, .ErrorUnion => operand,1169 .Struct, .ErrorUnion, .Optional => operand, // pass as pointer
1161 else => self.load(operand, ty, 0),1170 else => switch (operand) {
1171 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),
1172 else => try self.load(operand, ty, 0),
1173 },
1162 };1174 };
1163}1175}
11641176
...@@ -1174,7 +1186,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {...@@ -1174,7 +1186,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1174 .signedness = signedness,1186 .signedness = signedness,
1175 });1187 });
11761188
1177 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = offset, .alignment = 0 });1189 const mem_arg_index = try self.addExtra(Mir.MemArg{
1190 .offset = offset,
1191 .alignment = ty.abiAlignment(self.target),
1192 });
1178 try self.addInst(.{1193 try self.addInst(.{
1179 .tag = Mir.Inst.Tag.fromOpcode(opcode),1194 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1180 .data = .{ .payload = mem_arg_index },1195 .data = .{ .payload = mem_arg_index },
...@@ -1301,7 +1316,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1301,7 +1316,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
13011316
1302 // memory instruction followed by their memarg immediate1317 // memory instruction followed by their memarg immediate
1303 // memarg ::== x:u32, y:u32 => {align x, offset y}1318 // memarg ::== x:u32, y:u32 => {align x, offset y}
1304 const extra_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 });1319 const extra_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 4 });
1305 try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } });1320 try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } });
1306 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()});1321 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()});
1307 },1322 },
...@@ -1774,7 +1789,10 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W...@@ -1774,7 +1789,10 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
17741789
1775 // load the error tag value1790 // load the error tag value
1776 try self.emitWValue(operand);1791 try self.emitWValue(operand);
1777 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 });1792 const mem_arg_index = try self.addExtra(Mir.MemArg{
1793 .offset = 0,
1794 .alignment = err_ty.abiAlignment(self.target),
1795 });
1778 try self.addInst(.{1796 try self.addInst(.{
1779 .tag = .i32_load,1797 .tag = .i32_load,
1780 .data = .{ .payload = mem_arg_index },1798 .data = .{ .payload = mem_arg_index },
...@@ -1830,22 +1848,40 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!...@@ -1830,22 +1848,40 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!
1830 const un_op = self.air.instructions.items(.data)[inst].un_op;1848 const un_op = self.air.instructions.items(.data)[inst].un_op;
1831 const operand = self.resolveInst(un_op);1849 const operand = self.resolveInst(un_op);
18321850
1833 // load the null value which is positioned at local_with_offset's index1851 // load the null tag value
1834 try self.emitWValue(.{ .local = operand.local_with_offset.local });1852 try self.emitWValue(operand);
1853 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 1 });
1854 try self.addInst(.{
1855 .tag = .i32_load8_u,
1856 .data = .{ .payload = mem_arg_index },
1857 });
1858
1859 // Compare the error value with '0'
1835 try self.addImm32(0);1860 try self.addImm32(0);
1836 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));1861 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
18371862
1838 // we save the result in a new local1863 const is_null_tmp = try self.allocLocal(Type.initTag(.u8));
1839 const local = try self.allocLocal(Type.initTag(.i32));1864 try self.addLabel(.local_set, is_null_tmp.local);
1840 try self.addLabel(.local_set, local.local);1865 return is_null_tmp;
1841
1842 return local;
1843}1866}
18441867
1845fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1868fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1846 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1869 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1847 const operand = self.resolveInst(ty_op.operand);1870 const operand = self.resolveInst(ty_op.operand);
1848 return WValue{ .local = operand.local_with_offset.local + 1 };1871 const opt_ty = self.air.typeOf(ty_op.operand);
1872
1873 // For pointers we simply return its stack address, rather than
1874 // loading its value
1875 if (opt_ty.zigTypeTag() == .Pointer) {
1876 return WValue{ .local_with_offset = .{ .local = operand.local, .offset = 1 } };
1877 }
1878
1879 if (opt_ty.isPtrLikeOptional()) return operand;
1880
1881 var buf: Type.Payload.ElemType = undefined;
1882 const child_ty = opt_ty.optionalChild(&buf);
1883
1884 return self.load(operand, child_ty, @as(u32, 1)); // null tag is 1 byte
1849}1885}
18501886
1851fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1887fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
src/arch/wasm/Emit.zig+4-1
...@@ -251,7 +251,10 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {...@@ -251,7 +251,10 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
251 const extra_index = emit.mir.instructions.items(.data)[inst].payload;251 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
252 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;252 const mem_arg = emit.mir.extraData(Mir.MemArg, extra_index).data;
253 try emit.code.append(@enumToInt(tag));253 try emit.code.append(@enumToInt(tag));
254 try leb128.writeULEB128(emit.code.writer(), mem_arg.alignment);254
255 // wasm encodes alignment as power of 2, rather than natural alignment
256 const encoded_alignment = mem_arg.alignment >> 1;
257 try leb128.writeULEB128(emit.code.writer(), encoded_alignment);
255 try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);258 try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);
256}259}
257260