| ... | ... | @@ -644,6 +644,12 @@ fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void { |
| 644 | 644 | try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); |
| 645 | 645 | } |
| 646 | 646 | |
| 647 | /// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`. |
| 648 | fn 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 | 653 | /// Appends entries to `mir_extra` based on the type of `extra`. |
| 648 | 654 | /// Returns the index into `mir_extra` |
| 649 | 655 | fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 { |
| ... | ... | @@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 692 | 698 | .ErrorUnion, |
| 693 | 699 | .Optional, |
| 694 | 700 | .Fn, |
| 701 | .Array, |
| 695 | 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 | } |
| 699 | 706 | |
| ... | ... | @@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { |
| 756 | 763 | switch (return_type.zigTypeTag()) { |
| 757 | 764 | .Void, .NoReturn => {}, |
| 758 | 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 | 766 | else => try returns.append(try self.typeToValtype(return_type)), |
| 761 | 767 | } |
| 762 | 768 | |
| ... | ... | @@ -1146,6 +1152,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1146 | 1152 | .cmp_lt => self.airCmp(inst, .lt), |
| 1147 | 1153 | .cmp_neq => self.airCmp(inst, .neq), |
| 1148 | 1154 | |
| 1155 | .array_to_slice => self.airArrayToSlice(inst), |
| 1149 | 1156 | .alloc => self.airAlloc(inst), |
| 1150 | 1157 | .arg => self.airArg(inst), |
| 1151 | 1158 | .bitcast => self.airBitcast(inst), |
| ... | ... | @@ -1178,6 +1185,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1178 | 1185 | .ret_load => self.airRetLoad(inst), |
| 1179 | 1186 | .slice_len => self.airSliceLen(inst), |
| 1180 | 1187 | .slice_elem_val => self.airSliceElemVal(inst), |
| 1188 | .slice_elem_ptr => self.airSliceElemPtr(inst), |
| 1181 | 1189 | .slice_ptr => self.airSlicePtr(inst), |
| 1182 | 1190 | .store => self.airStore(inst), |
| 1183 | 1191 | .struct_field_ptr => self.airStructFieldPtr(inst), |
| ... | ... | @@ -1283,6 +1291,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1283 | 1291 | .Struct, .Pointer, .Optional, .ErrorUnion => { |
| 1284 | 1292 | // single pointer can be passed directly |
| 1285 | 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 | 1302 | try self.emitWValue(arg_val); |
| 1287 | 1303 | continue; |
| 1288 | 1304 | } |
| ... | ... | @@ -1467,14 +1483,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1467 | 1483 | }); |
| 1468 | 1484 | |
| 1469 | 1485 | // 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 | ); |
| 1478 | 1490 | } |
| 1479 | 1491 | |
| 1480 | 1492 | fn 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 | 1530 | .signedness = signedness, |
| 1519 | 1531 | }); |
| 1520 | 1532 | |
| 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 | ); |
| 1529 | 1537 | |
| 1530 | 1538 | // store the result in a local |
| 1531 | 1539 | const result = try self.allocLocal(ty); |
| ... | ... | @@ -1713,10 +1721,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1713 | 1721 | |
| 1714 | 1722 | // When constant has value 'null', set is_null local to '1' |
| 1715 | 1723 | // and payload to '0' |
| 1716 | | if (val.castTag(.opt_payload)) |pl| { |
| 1717 | | const payload_val = pl.data; |
| 1724 | if (val.castTag(.opt_payload)) |payload| { |
| 1718 | 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 | 1728 | } else { |
| 1721 | 1729 | // set null-tag |
| 1722 | 1730 | try self.addImm32(1); |
| ... | ... | @@ -1740,6 +1748,7 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void { |
| 1740 | 1748 | 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))), |
| 1741 | 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 | 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 | 1962 | |
| 1954 | 1963 | fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1955 | 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 | } |
| 1958 | 1974 | |
| 1959 | 1975 | fn 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 | 1989 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1974 | 1990 | const struct_ptr = self.resolveInst(ty_op.operand); |
| 1975 | 1991 | const struct_ty = self.air.typeOf(ty_op.operand).childType(); |
| 1992 | const field_ty = struct_ty.structFieldType(index); |
| 1976 | 1993 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { |
| 1977 | 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 | } |
| 1983 | 2008 | |
| 1984 | 2009 | fn 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 | 2181 | |
| 2157 | 2182 | // load the error tag value |
| 2158 | 2183 | 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 | ); |
| 2167 | 2188 | |
| 2168 | 2189 | // Compare the error value with '0' |
| 2169 | 2190 | try self.addImm32(0); |
| ... | ... | @@ -2257,11 +2278,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! |
| 2257 | 2278 | |
| 2258 | 2279 | // load the null tag value |
| 2259 | 2280 | 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 }); |
| 2265 | 2282 | |
| 2266 | 2283 | // Compare the error value with '0' |
| 2267 | 2284 | try self.addImm32(0); |
| ... | ... | @@ -2353,6 +2370,31 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2353 | 2370 | }; |
| 2354 | 2371 | } |
| 2355 | 2372 | |
| 2373 | fn 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 | |
| 2356 | 2398 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2357 | 2399 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 2358 | 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 | 2466 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2425 | 2467 | return self.resolveInst(un_op); |
| 2426 | 2468 | } |
| 2469 | |
| 2470 | fn 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 | } |