authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-12-27 22:40:58+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-01-01 12:58:59+01:00
log29164a31cc1dece981588b5dd34482804524df13
tree9dbb1073675602965aba01b6abf671da0264049e
parent726ce85c109a3a17bb6e82caee49e1199570ad5c
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Pass 'bugs' behavior tests


2 files changed, 93 insertions(+), 37 deletions(-)

src/arch/wasm/CodeGen.zig+84-37
...@@ -644,6 +644,12 @@ fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void {...@@ -644,6 +644,12 @@ fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void {
644 try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });644 try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
645}645}
646646
647/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.
648fn addMemArg(self: *Self, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
649 const extra_index = try self.addExtra(mem_arg);
650 try self.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
651}
652
647/// Appends entries to `mir_extra` based on the type of `extra`.653/// Appends entries to `mir_extra` based on the type of `extra`.
648/// Returns the index into `mir_extra`654/// Returns the index into `mir_extra`
649fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 {655fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
...@@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {...@@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
692 .ErrorUnion,698 .ErrorUnion,
693 .Optional,699 .Optional,
694 .Fn,700 .Fn,
701 .Array,
695 => wasm.Valtype.i32,702 => wasm.Valtype.i32,
696 else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),703 else => self.fail("TODO - Wasm typeToValtype for type '{}'", .{ty}),
697 };704 };
698}705}
699706
...@@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {...@@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
756 switch (return_type.zigTypeTag()) {763 switch (return_type.zigTypeTag()) {
757 .Void, .NoReturn => {},764 .Void, .NoReturn => {},
758 .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),765 .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),
759 .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),
760 else => try returns.append(try self.typeToValtype(return_type)),766 else => try returns.append(try self.typeToValtype(return_type)),
761 }767 }
762768
...@@ -1146,6 +1152,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1146,6 +1152,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1146 .cmp_lt => self.airCmp(inst, .lt),1152 .cmp_lt => self.airCmp(inst, .lt),
1147 .cmp_neq => self.airCmp(inst, .neq),1153 .cmp_neq => self.airCmp(inst, .neq),
11481154
1155 .array_to_slice => self.airArrayToSlice(inst),
1149 .alloc => self.airAlloc(inst),1156 .alloc => self.airAlloc(inst),
1150 .arg => self.airArg(inst),1157 .arg => self.airArg(inst),
1151 .bitcast => self.airBitcast(inst),1158 .bitcast => self.airBitcast(inst),
...@@ -1178,6 +1185,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1178,6 +1185,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1178 .ret_load => self.airRetLoad(inst),1185 .ret_load => self.airRetLoad(inst),
1179 .slice_len => self.airSliceLen(inst),1186 .slice_len => self.airSliceLen(inst),
1180 .slice_elem_val => self.airSliceElemVal(inst),1187 .slice_elem_val => self.airSliceElemVal(inst),
1188 .slice_elem_ptr => self.airSliceElemPtr(inst),
1181 .slice_ptr => self.airSlicePtr(inst),1189 .slice_ptr => self.airSlicePtr(inst),
1182 .store => self.airStore(inst),1190 .store => self.airStore(inst),
1183 .struct_field_ptr => self.airStructFieldPtr(inst),1191 .struct_field_ptr => self.airStructFieldPtr(inst),
...@@ -1283,6 +1291,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1283,6 +1291,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1283 .Struct, .Pointer, .Optional, .ErrorUnion => {1291 .Struct, .Pointer, .Optional, .ErrorUnion => {
1284 // single pointer can be passed directly1292 // single pointer can be passed directly
1285 if (arg_ty.isSinglePointer() or arg_val != .constant) {1293 if (arg_ty.isSinglePointer() or arg_val != .constant) {
1294 if (arg_val == .none) {
1295 // when the argument is a 0-sized value, but the function
1296 // expects a non-zero typed value (such as a slice), we must emit an argument
1297 // as function calls are verified with the function signature in wasm.
1298 // In those cases we will emit a '0xaa' as address, meaning invalid memory.
1299 try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa)));
1300 continue;
1301 }
1286 try self.emitWValue(arg_val);1302 try self.emitWValue(arg_val);
1287 continue;1303 continue;
1288 }1304 }
...@@ -1467,14 +1483,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1467,14 +1483,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1467 });1483 });
14681484
1469 // store rhs value at stack pointer's location in memory1485 // store rhs value at stack pointer's location in memory
1470 const mem_arg_index = try self.addExtra(Mir.MemArg{1486 try self.addMemArg(
1471 .offset = offset,1487 Mir.Inst.Tag.fromOpcode(opcode),
1472 .alignment = ty.abiAlignment(self.target),1488 .{ .offset = offset, .alignment = ty.abiAlignment(self.target) },
1473 });1489 );
1474 try self.addInst(.{
1475 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1476 .data = .{ .payload = mem_arg_index },
1477 });
1478}1490}
14791491
1480fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1492fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1518,14 +1530,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {...@@ -1518,14 +1530,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1518 .signedness = signedness,1530 .signedness = signedness,
1519 });1531 });
15201532
1521 const mem_arg_index = try self.addExtra(Mir.MemArg{1533 try self.addMemArg(
1522 .offset = offset,1534 Mir.Inst.Tag.fromOpcode(opcode),
1523 .alignment = ty.abiAlignment(self.target),1535 .{ .offset = offset, .alignment = ty.abiAlignment(self.target) },
1524 });1536 );
1525 try self.addInst(.{
1526 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1527 .data = .{ .payload = mem_arg_index },
1528 });
15291537
1530 // store the result in a local1538 // store the result in a local
1531 const result = try self.allocLocal(ty);1539 const result = try self.allocLocal(ty);
...@@ -1713,10 +1721,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1713,10 +1721,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
17131721
1714 // When constant has value 'null', set is_null local to '1'1722 // When constant has value 'null', set is_null local to '1'
1715 // and payload to '0'1723 // and payload to '0'
1716 if (val.castTag(.opt_payload)) |pl| {1724 if (val.castTag(.opt_payload)) |payload| {
1717 const payload_val = pl.data;
1718 try self.addImm32(0);1725 try self.addImm32(0);
1719 try self.emitConstant(payload_val, payload_type);1726 if (payload_type.hasCodeGenBits())
1727 try self.emitConstant(payload.data, payload_type);
1720 } else {1728 } else {
1721 // set null-tag1729 // set null-tag
1722 try self.addImm32(1);1730 try self.addImm32(1);
...@@ -1740,6 +1748,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void {...@@ -1740,6 +1748,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void {
1740 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),1748 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),
1741 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),1749 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),
1742 },1750 },
1751 .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),
1743 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),1752 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),
1744 }1753 }
1745}1754}
...@@ -1953,7 +1962,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1953,7 +1962,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
19531962
1954fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1963fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1955 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1964 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1956 return self.resolveInst(ty_op.operand);1965 const operand = self.resolveInst(ty_op.operand);
1966 if (operand == .constant) {
1967 const result = try self.allocLocal(self.air.typeOfIndex(inst));
1968 try self.emitWValue(operand);
1969 try self.addLabel(.local_set, result.local);
1970 return result;
1971 }
1972 return operand;
1957}1973}
19581974
1959fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1975fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1973,12 +1989,21 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr...@@ -1973,12 +1989,21 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
1973 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1989 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1974 const struct_ptr = self.resolveInst(ty_op.operand);1990 const struct_ptr = self.resolveInst(ty_op.operand);
1975 const struct_ty = self.air.typeOf(ty_op.operand).childType();1991 const struct_ty = self.air.typeOf(ty_op.operand).childType();
1992 const field_ty = struct_ty.structFieldType(index);
1976 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {1993 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {
1977 return self.fail("Field type '{}' too big to fit into stack frame", .{1994 return self.fail("Field type '{}' too big to fit into stack frame", .{
1978 struct_ty.structFieldType(index),1995 field_ty,
1979 });1996 });
1980 };1997 };
1981 return structFieldPtr(struct_ptr, offset);1998 // field points to another struct, so retrieve that struct first
1999 switch (struct_ptr) {
2000 .local => return structFieldPtr(struct_ptr, offset),
2001 .local_with_offset => |with_offset| {
2002 const result = try self.load(struct_ptr, field_ty, with_offset.offset);
2003 return structFieldPtr(result, offset);
2004 },
2005 else => unreachable,
2006 }
1982}2007}
19832008
1984fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {2009fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {
...@@ -2156,14 +2181,10 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W...@@ -2156,14 +2181,10 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
21562181
2157 // load the error tag value2182 // load the error tag value
2158 try self.emitWValue(operand);2183 try self.emitWValue(operand);
2159 const mem_arg_index = try self.addExtra(Mir.MemArg{2184 try self.addMemArg(
2160 .offset = 0,2185 .i32_load16_u,
2161 .alignment = err_ty.abiAlignment(self.target),2186 .{ .offset = 0, .alignment = err_ty.abiAlignment(self.target) },
2162 });2187 );
2163 try self.addInst(.{
2164 .tag = .i32_load16_u,
2165 .data = .{ .payload = mem_arg_index },
2166 });
21672188
2168 // Compare the error value with '0'2189 // Compare the error value with '0'
2169 try self.addImm32(0);2190 try self.addImm32(0);
...@@ -2257,11 +2278,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!...@@ -2257,11 +2278,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!
22572278
2258 // load the null tag value2279 // load the null tag value
2259 try self.emitWValue(operand);2280 try self.emitWValue(operand);
2260 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 1 });2281 try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 });
2261 try self.addInst(.{
2262 .tag = .i32_load8_u,
2263 .data = .{ .payload = mem_arg_index },
2264 });
22652282
2266 // Compare the error value with '0'2283 // Compare the error value with '0'
2267 try self.addImm32(0);2284 try self.addImm32(0);
...@@ -2353,6 +2370,31 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2353,6 +2370,31 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2353 };2370 };
2354}2371}
23552372
2373fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2374 if (self.liveness.isUnused(inst)) return WValue.none;
2375 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2376 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2377 const slice_ty = self.air.typeOf(bin_op.lhs);
2378 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
2379 const elem_size = elem_ty.abiSize(self.target);
2380
2381 const slice = self.resolveInst(bin_op.lhs);
2382 const index = self.resolveInst(bin_op.rhs);
2383
2384 const slice_ptr = try self.load(slice, slice_ty, 0);
2385 try self.addLabel(.local_get, slice_ptr.local);
2386
2387 // calculate index into slice
2388 try self.emitWValue(index);
2389 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
2390 try self.addTag(.i32_mul);
2391 try self.addTag(.i32_add);
2392
2393 const result = try self.allocLocal(Type.initTag(.i32));
2394 try self.addLabel(.local_set, result.local);
2395 return result;
2396}
2397
2356fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2398fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2357 if (self.liveness.isUnused(inst)) return WValue.none;2399 if (self.liveness.isUnused(inst)) return WValue.none;
2358 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2400 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
...@@ -2424,3 +2466,8 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2424,3 +2466,8 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2424 const un_op = self.air.instructions.items(.data)[inst].un_op;2466 const un_op = self.air.instructions.items(.data)[inst].un_op;
2425 return self.resolveInst(un_op);2467 return self.resolveInst(un_op);
2426}2468}
2469
2470fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2471 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2472 return self.resolveInst(ty_op.operand);
2473}
test/behavior.zig+9
...@@ -8,9 +8,18 @@ test {...@@ -8,9 +8,18 @@ test {
8 _ = @import("behavior/bugs/624.zig");8 _ = @import("behavior/bugs/624.zig");
9 _ = @import("behavior/bugs/655.zig");9 _ = @import("behavior/bugs/655.zig");
10 _ = @import("behavior/bugs/679.zig");10 _ = @import("behavior/bugs/679.zig");
11 _ = @import("behavior/bugs/704.zig");
11 _ = @import("behavior/bugs/1111.zig");12 _ = @import("behavior/bugs/1111.zig");
12 _ = @import("behavior/bugs/1486.zig");13 _ = @import("behavior/bugs/1486.zig");
13 _ = @import("behavior/bugs/2346.zig");14 _ = @import("behavior/bugs/2346.zig");
15 _ = @import("behavior/bugs/2692.zig");
16 _ = @import("behavior/bugs/2889.zig");
17 _ = @import("behavior/bugs/3046.zig");
18 _ = @import("behavior/bugs/3586.zig");
19 _ = @import("behavior/bugs/4560.zig");
20 _ = @import("behavior/bugs/4769_a.zig");
21 _ = @import("behavior/bugs/4769_b.zig");
22 _ = @import("behavior/bugs/4954.zig");
14 _ = @import("behavior/bugs/6850.zig");23 _ = @import("behavior/bugs/6850.zig");
15 _ = @import("behavior/enum.zig");24 _ = @import("behavior/enum.zig");
16 _ = @import("behavior/hasdecl.zig");25 _ = @import("behavior/hasdecl.zig");