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 {
644644 try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
645645}
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
647653/// Appends entries to `mir_extra` based on the type of `extra`.
648654/// Returns the index into `mir_extra`
649655fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
......@@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
692698 .ErrorUnion,
693699 .Optional,
694700 .Fn,
701 .Array,
695702 => wasm.Valtype.i32,
696 else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),
703 else => self.fail("TODO - Wasm typeToValtype for type '{}'", .{ty}),
697704 };
698705}
699706
......@@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
756763 switch (return_type.zigTypeTag()) {
757764 .Void, .NoReturn => {},
758765 .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", .{}),
760766 else => try returns.append(try self.typeToValtype(return_type)),
761767 }
762768
......@@ -1146,6 +1152,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
11461152 .cmp_lt => self.airCmp(inst, .lt),
11471153 .cmp_neq => self.airCmp(inst, .neq),
11481154
1155 .array_to_slice => self.airArrayToSlice(inst),
11491156 .alloc => self.airAlloc(inst),
11501157 .arg => self.airArg(inst),
11511158 .bitcast => self.airBitcast(inst),
......@@ -1178,6 +1185,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
11781185 .ret_load => self.airRetLoad(inst),
11791186 .slice_len => self.airSliceLen(inst),
11801187 .slice_elem_val => self.airSliceElemVal(inst),
1188 .slice_elem_ptr => self.airSliceElemPtr(inst),
11811189 .slice_ptr => self.airSlicePtr(inst),
11821190 .store => self.airStore(inst),
11831191 .struct_field_ptr => self.airStructFieldPtr(inst),
......@@ -1283,6 +1291,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
12831291 .Struct, .Pointer, .Optional, .ErrorUnion => {
12841292 // single pointer can be passed directly
12851293 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 }
12861302 try self.emitWValue(arg_val);
12871303 continue;
12881304 }
......@@ -1467,14 +1483,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
14671483 });
14681484
14691485 // store rhs value at stack pointer's location in memory
1470 const mem_arg_index = try self.addExtra(Mir.MemArg{
1471 .offset = offset,
1472 .alignment = ty.abiAlignment(self.target),
1473 });
1474 try self.addInst(.{
1475 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1476 .data = .{ .payload = mem_arg_index },
1477 });
1486 try self.addMemArg(
1487 Mir.Inst.Tag.fromOpcode(opcode),
1488 .{ .offset = offset, .alignment = ty.abiAlignment(self.target) },
1489 );
14781490}
14791491
14801492fn 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 {
15181530 .signedness = signedness,
15191531 });
15201532
1521 const mem_arg_index = try self.addExtra(Mir.MemArg{
1522 .offset = offset,
1523 .alignment = ty.abiAlignment(self.target),
1524 });
1525 try self.addInst(.{
1526 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1527 .data = .{ .payload = mem_arg_index },
1528 });
1533 try self.addMemArg(
1534 Mir.Inst.Tag.fromOpcode(opcode),
1535 .{ .offset = offset, .alignment = ty.abiAlignment(self.target) },
1536 );
15291537
15301538 // store the result in a local
15311539 const result = try self.allocLocal(ty);
......@@ -1713,10 +1721,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
17131721
17141722 // When constant has value 'null', set is_null local to '1'
17151723 // and payload to '0'
1716 if (val.castTag(.opt_payload)) |pl| {
1717 const payload_val = pl.data;
1724 if (val.castTag(.opt_payload)) |payload| {
17181725 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);
17201728 } else {
17211729 // set null-tag
17221730 try self.addImm32(1);
......@@ -1740,6 +1748,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void {
17401748 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),
17411749 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),
17421750 },
1751 .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaaaaaaaa))),
17431752 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),
17441753 }
17451754}
......@@ -1953,7 +1962,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
19531962
19541963fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
19551964 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;
19571973}
19581974
19591975fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -1973,12 +1989,21 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
19731989 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
19741990 const struct_ptr = self.resolveInst(ty_op.operand);
19751991 const struct_ty = self.air.typeOf(ty_op.operand).childType();
1992 const field_ty = struct_ty.structFieldType(index);
19761993 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {
19771994 return self.fail("Field type '{}' too big to fit into stack frame", .{
1978 struct_ty.structFieldType(index),
1995 field_ty,
19791996 });
19801997 };
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 }
19822007}
19832008
19842009fn 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
21562181
21572182 // load the error tag value
21582183 try self.emitWValue(operand);
2159 const mem_arg_index = try self.addExtra(Mir.MemArg{
2160 .offset = 0,
2161 .alignment = err_ty.abiAlignment(self.target),
2162 });
2163 try self.addInst(.{
2164 .tag = .i32_load16_u,
2165 .data = .{ .payload = mem_arg_index },
2166 });
2184 try self.addMemArg(
2185 .i32_load16_u,
2186 .{ .offset = 0, .alignment = err_ty.abiAlignment(self.target) },
2187 );
21672188
21682189 // Compare the error value with '0'
21692190 try self.addImm32(0);
......@@ -2257,11 +2278,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!
22572278
22582279 // load the null tag value
22592280 try self.emitWValue(operand);
2260 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 1 });
2261 try self.addInst(.{
2262 .tag = .i32_load8_u,
2263 .data = .{ .payload = mem_arg_index },
2264 });
2281 try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 });
22652282
22662283 // Compare the error value with '0'
22672284 try self.addImm32(0);
......@@ -2353,6 +2370,31 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23532370 };
23542371}
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
23562398fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23572399 if (self.liveness.isUnused(inst)) return WValue.none;
23582400 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 {
24242466 const un_op = self.air.instructions.items(.data)[inst].un_op;
24252467 return self.resolveInst(un_op);
24262468}
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 {
88 _ = @import("behavior/bugs/624.zig");
99 _ = @import("behavior/bugs/655.zig");
1010 _ = @import("behavior/bugs/679.zig");
11 _ = @import("behavior/bugs/704.zig");
1112 _ = @import("behavior/bugs/1111.zig");
1213 _ = @import("behavior/bugs/1486.zig");
1314 _ = @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");
1423 _ = @import("behavior/bugs/6850.zig");
1524 _ = @import("behavior/enum.zig");
1625 _ = @import("behavior/hasdecl.zig");