| ... | ... | @@ -51,6 +51,22 @@ const WValue = union(enum) { |
| 51 | 51 | /// In wasm function pointers are indexes into a function table, |
| 52 | 52 | /// rather than an address in the data section. |
| 53 | 53 | function_index: u32, |
| 54 | /// Offset from the bottom of the stack, with the offset |
| 55 | /// pointing to where the value lives. |
| 56 | stack_offset: u32, |
| 57 | |
| 58 | /// Returns the offset from the bottom of the stack. This is useful when |
| 59 | /// we use the load or store instruction to ensure we retrieve the value |
| 60 | /// from the correct position, rather than the value that lives at the |
| 61 | /// bottom of the stack. For instances where `WValue` is not `stack_value` |
| 62 | /// this will return 0, which allows us to simply call this function for all |
| 63 | /// loads and stores without requiring checks everywhere. |
| 64 | fn offset(self: WValue) u32 { |
| 65 | switch (self) { |
| 66 | .stack_offset => |offset| return offset, |
| 67 | else => return 0, |
| 68 | } |
| 69 | } |
| 54 | 70 | }; |
| 55 | 71 | |
| 56 | 72 | /// Wasm ops, but without input/output/signedness information |
| ... | ... | @@ -778,6 +794,7 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { |
| 778 | 794 | try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); |
| 779 | 795 | }, |
| 780 | 796 | .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation |
| 797 | .stack_offset => try self.addLabel(.local_get, self.bottom_stack_value.local), // caller must ensure to address the offset |
| 781 | 798 | } |
| 782 | 799 | } |
| 783 | 800 | |
| ... | ... | @@ -1372,18 +1389,6 @@ fn restoreStackPointer(self: *Self) !void { |
| 1372 | 1389 | try self.addLabel(.global_set, 0); |
| 1373 | 1390 | } |
| 1374 | 1391 | |
| 1375 | | /// Saves the current stack size's stack pointer position into a given local |
| 1376 | | /// It does this by retrieving the bottom stack pointer, adding `self.stack_size` and storing |
| 1377 | | /// the result back into the local. |
| 1378 | | fn saveStack(self: *Self) !WValue { |
| 1379 | | const local = try self.allocLocal(Type.usize); |
| 1380 | | try self.addLabel(.local_get, self.bottom_stack_value.local); |
| 1381 | | try self.addImm32(@intCast(i32, self.stack_size)); |
| 1382 | | try self.addTag(.i32_add); |
| 1383 | | try self.addLabel(.local_set, local.local); |
| 1384 | | return local; |
| 1385 | | } |
| 1386 | | |
| 1387 | 1392 | /// From a given type, will create space on the virtual stack to store the value of such type. |
| 1388 | 1393 | /// This returns a `WValue` with its active tag set to `local`, containing the index to the local |
| 1389 | 1394 | /// that points to the position on the virtual stack. This function should be used instead of |
| ... | ... | @@ -1408,8 +1413,7 @@ fn allocStack(self: *Self, ty: Type) !WValue { |
| 1408 | 1413 | const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_align); |
| 1409 | 1414 | defer self.stack_size = offset + abi_size; |
| 1410 | 1415 | |
| 1411 | | // store the stack pointer and return a local to it |
| 1412 | | return self.saveStack(); |
| 1416 | return WValue{ .stack_offset = offset }; |
| 1413 | 1417 | } |
| 1414 | 1418 | |
| 1415 | 1419 | /// From a given AIR instruction generates a pointer to the stack where |
| ... | ... | @@ -1439,8 +1443,7 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1439 | 1443 | const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_alignment); |
| 1440 | 1444 | defer self.stack_size = offset + abi_size; |
| 1441 | 1445 | |
| 1442 | | // store the stack pointer and return a local to it |
| 1443 | | return self.saveStack(); |
| 1446 | return WValue{ .stack_offset = offset }; |
| 1444 | 1447 | } |
| 1445 | 1448 | |
| 1446 | 1449 | /// From given zig bitsize, returns the wasm bitsize |
| ... | ... | @@ -1458,14 +1461,16 @@ fn toWasmIntBits(bits: u16) ?u16 { |
| 1458 | 1461 | fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void { |
| 1459 | 1462 | const abi_size = ty.abiSize(self.target); |
| 1460 | 1463 | var offset: u32 = 0; |
| 1464 | const lhs_base = lhs.offset(); |
| 1465 | const rhs_base = rhs.offset(); |
| 1461 | 1466 | while (offset < abi_size) : (offset += 1) { |
| 1462 | 1467 | // get lhs' address to store the result |
| 1463 | 1468 | try self.emitWValue(lhs); |
| 1464 | 1469 | // load byte from rhs' adress |
| 1465 | 1470 | try self.emitWValue(rhs); |
| 1466 | | try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 }); |
| 1471 | try self.addMemArg(.i32_load8_u, .{ .offset = rhs_base + offset, .alignment = 1 }); |
| 1467 | 1472 | // store the result in lhs (we already have its address on the stack) |
| 1468 | | try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 }); |
| 1473 | try self.addMemArg(.i32_store8, .{ .offset = lhs_base + offset, .alignment = 1 }); |
| 1469 | 1474 | } |
| 1470 | 1475 | } |
| 1471 | 1476 | |
| ... | ... | @@ -1533,19 +1538,19 @@ fn isByRef(ty: Type, target: std.Target) bool { |
| 1533 | 1538 | /// local value to store the pointer. This allows for local re-use and improves binary size. |
| 1534 | 1539 | fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum { modify, new }) InnerError!WValue { |
| 1535 | 1540 | // do not perform arithmetic when offset is 0. |
| 1536 | | if (offset == 0) return ptr_value; |
| 1541 | if (offset == 0 and ptr_value.offset() == 0) return ptr_value; |
| 1537 | 1542 | const result_ptr: WValue = switch (action) { |
| 1538 | 1543 | .new => try self.allocLocal(Type.usize), |
| 1539 | 1544 | .modify => ptr_value, |
| 1540 | 1545 | }; |
| 1541 | 1546 | try self.emitWValue(ptr_value); |
| 1542 | | switch (self.target.cpu.arch.ptrBitWidth()) { |
| 1543 | | 32 => { |
| 1544 | | try self.addImm32(@bitCast(i32, @intCast(u32, offset))); |
| 1547 | switch (self.arch()) { |
| 1548 | .wasm32 => { |
| 1549 | try self.addImm32(@bitCast(i32, @intCast(u32, offset + ptr_value.offset()))); |
| 1545 | 1550 | try self.addTag(.i32_add); |
| 1546 | 1551 | }, |
| 1547 | | 64 => { |
| 1548 | | try self.addImm64(offset); |
| 1552 | .wasm64 => { |
| 1553 | try self.addImm64(offset + ptr_value.offset()); |
| 1549 | 1554 | try self.addTag(.i64_add); |
| 1550 | 1555 | }, |
| 1551 | 1556 | else => unreachable, |
| ... | ... | @@ -1554,16 +1559,6 @@ fn buildPointerOffset(self: *Self, ptr_value: WValue, offset: u64, action: enum |
| 1554 | 1559 | return result_ptr; |
| 1555 | 1560 | } |
| 1556 | 1561 | |
| 1557 | | /// Creates a new local and sets its value to the given `value` local. |
| 1558 | | /// User must ensure `ty` matches that of given `value`. |
| 1559 | | /// Asserts `value` is a `local`. |
| 1560 | | fn copyLocal(self: *Self, value: WValue, ty: Type) InnerError!WValue { |
| 1561 | | const copy = try self.allocLocal(ty); |
| 1562 | | try self.addLabel(.local_get, value.local); |
| 1563 | | try self.addLabel(.local_set, copy.local); |
| 1564 | | return copy; |
| 1565 | | } |
| 1566 | | |
| 1567 | 1562 | fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1568 | 1563 | const air_tags = self.air.instructions.items(.tag); |
| 1569 | 1564 | return switch (air_tags[inst]) { |
| ... | ... | @@ -1789,7 +1784,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1789 | 1784 | |
| 1790 | 1785 | const sret = if (first_param_sret) blk: { |
| 1791 | 1786 | const sret_local = try self.allocStack(ret_ty); |
| 1792 | | try self.emitWValue(sret_local); |
| 1787 | const ptr_offset = try self.buildPointerOffset(sret_local, 0, .new); |
| 1788 | try self.emitWValue(ptr_offset); |
| 1793 | 1789 | break :blk sret_local; |
| 1794 | 1790 | } else WValue{ .none = {} }; |
| 1795 | 1791 | |
| ... | ... | @@ -1799,7 +1795,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1799 | 1795 | |
| 1800 | 1796 | const arg_ty = self.air.typeOf(arg_ref); |
| 1801 | 1797 | if (!arg_ty.hasRuntimeBits()) continue; |
| 1802 | | try self.emitWValue(arg_val); |
| 1798 | |
| 1799 | switch (arg_val) { |
| 1800 | .stack_offset => try self.emitWValue(try self.buildPointerOffset(arg_val, 0, .new)), |
| 1801 | else => try self.emitWValue(arg_val), |
| 1802 | } |
| 1803 | 1803 | } |
| 1804 | 1804 | |
| 1805 | 1805 | if (target) |direct| { |
| ... | ... | @@ -1865,7 +1865,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1865 | 1865 | var buf: Type.Payload.ElemType = undefined; |
| 1866 | 1866 | const pl_ty = ty.optionalChild(&buf); |
| 1867 | 1867 | if (!pl_ty.hasRuntimeBits()) { |
| 1868 | | return self.store(lhs, rhs, Type.initTag(.u8), 0); |
| 1868 | return self.store(lhs, rhs, Type.u8, 0); |
| 1869 | 1869 | } |
| 1870 | 1870 | |
| 1871 | 1871 | return self.memCopy(ty, lhs, rhs); |
| ... | ... | @@ -1891,7 +1891,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1891 | 1891 | else => {}, |
| 1892 | 1892 | } |
| 1893 | 1893 | try self.emitWValue(lhs); |
| 1894 | | try self.emitWValue(rhs); |
| 1894 | // In this case we're actually interested in storing the stack position |
| 1895 | // into lhs, so we calculate that and emit that instead |
| 1896 | if (rhs == .stack_offset) { |
| 1897 | try self.emitWValue(try self.buildPointerOffset(rhs, 0, .new)); |
| 1898 | } else { |
| 1899 | try self.emitWValue(rhs); |
| 1900 | } |
| 1895 | 1901 | const valtype = typeToValtype(ty, self.target); |
| 1896 | 1902 | const abi_size = @intCast(u8, ty.abiSize(self.target)); |
| 1897 | 1903 | |
| ... | ... | @@ -1904,7 +1910,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1904 | 1910 | // store rhs value at stack pointer's location in memory |
| 1905 | 1911 | try self.addMemArg( |
| 1906 | 1912 | Mir.Inst.Tag.fromOpcode(opcode), |
| 1907 | | .{ .offset = offset, .alignment = ty.abiAlignment(self.target) }, |
| 1913 | .{ .offset = offset + lhs.offset(), .alignment = ty.abiAlignment(self.target) }, |
| 1908 | 1914 | ); |
| 1909 | 1915 | } |
| 1910 | 1916 | |
| ... | ... | @@ -1947,7 +1953,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1947 | 1953 | |
| 1948 | 1954 | try self.addMemArg( |
| 1949 | 1955 | Mir.Inst.Tag.fromOpcode(opcode), |
| 1950 | | .{ .offset = offset, .alignment = ty.abiAlignment(self.target) }, |
| 1956 | .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(self.target) }, |
| 1951 | 1957 | ); |
| 1952 | 1958 | |
| 1953 | 1959 | // store the result in a local |
| ... | ... | @@ -2358,7 +2364,12 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2358 | 2364 | |
| 2359 | 2365 | // if operand has codegen bits we should break with a value |
| 2360 | 2366 | if (self.air.typeOf(br.operand).hasRuntimeBits()) { |
| 2361 | | try self.emitWValue(try self.resolveInst(br.operand)); |
| 2367 | const operand = try self.resolveInst(br.operand); |
| 2368 | const op = switch (operand) { |
| 2369 | .stack_offset => try self.buildPointerOffset(operand, 0, .new), |
| 2370 | else => operand, |
| 2371 | }; |
| 2372 | try self.emitWValue(op); |
| 2362 | 2373 | |
| 2363 | 2374 | if (block.value != .none) { |
| 2364 | 2375 | try self.addLabel(.local_set, block.value.local); |
| ... | ... | @@ -2381,8 +2392,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2381 | 2392 | |
| 2382 | 2393 | // wasm does not have booleans nor the `not` instruction, therefore compare with 0 |
| 2383 | 2394 | // to create the same logic |
| 2384 | | try self.addImm32(0); |
| 2385 | | try self.addTag(.i32_eq); |
| 2395 | try self.addTag(.i32_eqz); |
| 2386 | 2396 | |
| 2387 | 2397 | // save the result in the local |
| 2388 | 2398 | const not_tmp = try self.allocLocal(Type.initTag(.i32)); |
| ... | ... | @@ -2406,8 +2416,7 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2406 | 2416 | |
| 2407 | 2417 | fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2408 | 2418 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2409 | | const operand = try self.resolveInst(ty_op.operand); |
| 2410 | | return operand; |
| 2419 | return self.resolveInst(ty_op.operand); |
| 2411 | 2420 | } |
| 2412 | 2421 | |
| 2413 | 2422 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -2437,7 +2446,12 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr |
| 2437 | 2446 | } |
| 2438 | 2447 | |
| 2439 | 2448 | fn structFieldPtr(self: *Self, struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 2440 | | return self.buildPointerOffset(struct_ptr, offset, .new); |
| 2449 | switch (struct_ptr) { |
| 2450 | .stack_offset => |stack_offset| { |
| 2451 | return WValue{ .stack_offset = stack_offset + offset }; |
| 2452 | }, |
| 2453 | else => return self.buildPointerOffset(struct_ptr, offset, .new), |
| 2454 | } |
| 2441 | 2455 | } |
| 2442 | 2456 | |
| 2443 | 2457 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -2455,7 +2469,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2455 | 2469 | }; |
| 2456 | 2470 | |
| 2457 | 2471 | if (isByRef(field_ty, self.target)) { |
| 2458 | | return self.buildPointerOffset(operand, offset, .new); |
| 2472 | switch (operand) { |
| 2473 | .stack_offset => |stack_offset| { |
| 2474 | return WValue{ .stack_offset = stack_offset + offset }; |
| 2475 | }, |
| 2476 | else => return self.buildPointerOffset(operand, offset, .new), |
| 2477 | } |
| 2459 | 2478 | } |
| 2460 | 2479 | |
| 2461 | 2480 | return self.load(operand, field_ty, offset); |
| ... | ... | @@ -2621,7 +2640,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W |
| 2621 | 2640 | try self.emitWValue(operand); |
| 2622 | 2641 | if (pl_ty.hasRuntimeBits()) { |
| 2623 | 2642 | try self.addMemArg(.i32_load16_u, .{ |
| 2624 | | .offset = 0, |
| 2643 | .offset = operand.offset(), |
| 2625 | 2644 | .alignment = err_ty.errorUnionSet().abiAlignment(self.target), |
| 2626 | 2645 | }); |
| 2627 | 2646 | } |
| ... | ... | @@ -2679,9 +2698,9 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2679 | 2698 | try self.store(payload_ptr, operand, op_ty, 0); |
| 2680 | 2699 | |
| 2681 | 2700 | // ensure we also write '0' to the error part, so any present stack value gets overwritten by it. |
| 2682 | | try self.addLabel(.local_get, err_union.local); |
| 2701 | try self.emitWValue(err_union); |
| 2683 | 2702 | try self.addImm32(0); |
| 2684 | | try self.addMemArg(.i32_store16, .{ .offset = 0, .alignment = 2 }); |
| 2703 | try self.addMemArg(.i32_store16, .{ .offset = err_union.offset(), .alignment = 2 }); |
| 2685 | 2704 | |
| 2686 | 2705 | return err_union; |
| 2687 | 2706 | } |
| ... | ... | @@ -2752,7 +2771,7 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) |
| 2752 | 2771 | // When payload is zero-bits, we can treat operand as a value, rather than |
| 2753 | 2772 | // a pointer to the stack value |
| 2754 | 2773 | if (payload_ty.hasRuntimeBits()) { |
| 2755 | | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); |
| 2774 | try self.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 }); |
| 2756 | 2775 | } |
| 2757 | 2776 | } |
| 2758 | 2777 | |
| ... | ... | @@ -2820,7 +2839,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue |
| 2820 | 2839 | |
| 2821 | 2840 | try self.emitWValue(operand); |
| 2822 | 2841 | try self.addImm32(1); |
| 2823 | | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| 2842 | try self.addMemArg(.i32_store8, .{ .offset = operand.offset(), .alignment = 1 }); |
| 2824 | 2843 | |
| 2825 | 2844 | return self.buildPointerOffset(operand, offset, .new); |
| 2826 | 2845 | } |
| ... | ... | @@ -2832,9 +2851,9 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2832 | 2851 | const payload_ty = self.air.typeOf(ty_op.operand); |
| 2833 | 2852 | if (!payload_ty.hasRuntimeBits()) { |
| 2834 | 2853 | const non_null_bit = try self.allocStack(Type.initTag(.u1)); |
| 2835 | | try self.addLabel(.local_get, non_null_bit.local); |
| 2854 | try self.emitWValue(non_null_bit); |
| 2836 | 2855 | try self.addImm32(1); |
| 2837 | | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| 2856 | try self.addMemArg(.i32_store8, .{ .offset = non_null_bit.offset(), .alignment = 1 }); |
| 2838 | 2857 | return non_null_bit; |
| 2839 | 2858 | } |
| 2840 | 2859 | |
| ... | ... | @@ -2849,9 +2868,9 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2849 | 2868 | |
| 2850 | 2869 | // Create optional type, set the non-null bit, and store the operand inside the optional type |
| 2851 | 2870 | const result = try self.allocStack(op_ty); |
| 2852 | | try self.addLabel(.local_get, result.local); |
| 2871 | try self.emitWValue(result); |
| 2853 | 2872 | try self.addImm32(1); |
| 2854 | | try self.addMemArg(.i32_store8, .{ .offset = 0, .alignment = 1 }); |
| 2873 | try self.addMemArg(.i32_store8, .{ .offset = result.offset(), .alignment = 1 }); |
| 2855 | 2874 | |
| 2856 | 2875 | const payload_ptr = try self.buildPointerOffset(result, offset, .new); |
| 2857 | 2876 | try self.store(payload_ptr, operand, payload_ty, 0); |
| ... | ... | @@ -3013,8 +3032,6 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3013 | 3032 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3014 | 3033 | const operand = try self.resolveInst(ty_op.operand); |
| 3015 | 3034 | const array_ty = self.air.typeOf(ty_op.operand).childType(); |
| 3016 | | const ty = Type.@"usize"; |
| 3017 | | const ptr_width = @intCast(u32, ty.abiSize(self.target)); |
| 3018 | 3035 | const slice_ty = self.air.getRefType(ty_op.ty); |
| 3019 | 3036 | |
| 3020 | 3037 | // create a slice on the stack |
| ... | ... | @@ -3022,15 +3039,12 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3022 | 3039 | |
| 3023 | 3040 | // store the array ptr in the slice |
| 3024 | 3041 | if (array_ty.hasRuntimeBits()) { |
| 3025 | | try self.store(slice_local, operand, ty, 0); |
| 3042 | try self.store(slice_local, operand, Type.usize, 0); |
| 3026 | 3043 | } |
| 3027 | 3044 | |
| 3028 | 3045 | // store the length of the array in the slice |
| 3029 | | const len = array_ty.arrayLen(); |
| 3030 | | try self.addImm32(@bitCast(i32, @intCast(u32, len))); |
| 3031 | | const len_local = try self.allocLocal(ty); |
| 3032 | | try self.addLabel(.local_set, len_local.local); |
| 3033 | | try self.store(slice_local, len_local, ty, ptr_width); |
| 3046 | const len = WValue{ .imm32 = @intCast(u32, array_ty.arrayLen()) }; |
| 3047 | try self.store(slice_local, len, Type.usize, self.ptrSize()); |
| 3034 | 3048 | |
| 3035 | 3049 | return slice_local; |
| 3036 | 3050 | } |
| ... | ... | @@ -3038,7 +3052,13 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3038 | 3052 | fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3039 | 3053 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 3040 | 3054 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 3041 | | return self.resolveInst(un_op); |
| 3055 | const operand = try self.resolveInst(un_op); |
| 3056 | |
| 3057 | switch (operand) { |
| 3058 | // for stack offset, return a pointer to this offset. |
| 3059 | .stack_offset => return self.buildPointerOffset(operand, 0, .new), |
| 3060 | else => return operand, |
| 3061 | } |
| 3042 | 3062 | } |
| 3043 | 3063 | |
| 3044 | 3064 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3046,16 +3066,20 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3046 | 3066 | |
| 3047 | 3067 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3048 | 3068 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 3049 | | const pointer = try self.resolveInst(bin_op.lhs); |
| 3069 | const ptr = try self.resolveInst(bin_op.lhs); |
| 3050 | 3070 | const index = try self.resolveInst(bin_op.rhs); |
| 3051 | 3071 | const elem_ty = ptr_ty.childType(); |
| 3052 | 3072 | const elem_size = elem_ty.abiSize(self.target); |
| 3053 | 3073 | |
| 3054 | 3074 | // load pointer onto the stack |
| 3055 | 3075 | if (ptr_ty.isSlice()) { |
| 3056 | | const ptr_local = try self.load(pointer, Type.usize, 0); |
| 3076 | const ptr_local = try self.load(ptr, Type.usize, 0); |
| 3057 | 3077 | try self.addLabel(.local_get, ptr_local.local); |
| 3058 | 3078 | } else { |
| 3079 | const pointer = switch (ptr) { |
| 3080 | .stack_offset => try self.buildPointerOffset(ptr, 0, .new), |
| 3081 | else => ptr, |
| 3082 | }; |
| 3059 | 3083 | try self.emitWValue(pointer); |
| 3060 | 3084 | } |
| 3061 | 3085 | |
| ... | ... | @@ -3089,7 +3113,11 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3089 | 3113 | const ptr_local = try self.load(ptr, Type.usize, 0); |
| 3090 | 3114 | try self.addLabel(.local_get, ptr_local.local); |
| 3091 | 3115 | } else { |
| 3092 | | try self.emitWValue(ptr); |
| 3116 | const pointer = switch (ptr) { |
| 3117 | .stack_offset => try self.buildPointerOffset(ptr, 0, .new), |
| 3118 | else => ptr, |
| 3119 | }; |
| 3120 | try self.emitWValue(pointer); |
| 3093 | 3121 | } |
| 3094 | 3122 | |
| 3095 | 3123 | // calculate index into ptr |
| ... | ... | @@ -3118,7 +3146,11 @@ fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 3118 | 3146 | const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul }); |
| 3119 | 3147 | const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op }); |
| 3120 | 3148 | |
| 3121 | | try self.emitWValue(ptr); |
| 3149 | const pointer = switch (ptr) { |
| 3150 | .stack_offset => try self.buildPointerOffset(ptr, 0, .new), |
| 3151 | else => ptr, |
| 3152 | }; |
| 3153 | try self.emitWValue(pointer); |
| 3122 | 3154 | try self.emitWValue(offset); |
| 3123 | 3155 | try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target)))); |
| 3124 | 3156 | try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode)); |
| ... | ... | @@ -3138,7 +3170,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3138 | 3170 | const len = try self.resolveInst(bin_op.rhs); |
| 3139 | 3171 | try self.memSet(ptr, len, value); |
| 3140 | 3172 | |
| 3141 | | return WValue.none; |
| 3173 | return WValue{ .none = {} }; |
| 3142 | 3174 | } |
| 3143 | 3175 | |
| 3144 | 3176 | /// Sets a region of memory at `ptr` to the value of `value` |
| ... | ... | @@ -3149,7 +3181,10 @@ fn memSet(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void |
| 3149 | 3181 | // When bulk_memory is enabled, we lower it to wasm's memset instruction. |
| 3150 | 3182 | // If not, we lower it ourselves |
| 3151 | 3183 | if (std.Target.wasm.featureSetHas(self.target.cpu.features, .bulk_memory)) { |
| 3152 | | try self.emitWValue(ptr); |
| 3184 | switch (ptr) { |
| 3185 | .stack_offset => try self.emitWValue(try self.buildPointerOffset(ptr, 0, .new)), |
| 3186 | else => try self.emitWValue(ptr), |
| 3187 | } |
| 3153 | 3188 | try self.emitWValue(value); |
| 3154 | 3189 | try self.emitWValue(len); |
| 3155 | 3190 | try self.addExtended(.memory_fill); |
| ... | ... | @@ -3172,18 +3207,18 @@ fn memSet(self: *Self, ptr: WValue, len: WValue, value: WValue) InnerError!void |
| 3172 | 3207 | try self.addLabel(.br_if, 1); // jump out of loop into outer block (finished) |
| 3173 | 3208 | try self.emitWValue(ptr); |
| 3174 | 3209 | try self.emitWValue(offset); |
| 3175 | | switch (self.ptrSize()) { |
| 3176 | | 4 => try self.addTag(.i32_add), |
| 3177 | | 8 => try self.addTag(.i64_add), |
| 3210 | switch (self.arch()) { |
| 3211 | .wasm32 => try self.addTag(.i32_add), |
| 3212 | .wasm64 => try self.addTag(.i64_add), |
| 3178 | 3213 | else => unreachable, |
| 3179 | 3214 | } |
| 3180 | 3215 | try self.emitWValue(value); |
| 3181 | | const mem_store_op: Mir.Inst.Tag = switch (self.ptrSize()) { |
| 3182 | | 4 => .i32_store8, |
| 3183 | | 8 => .i64_store8, |
| 3216 | const mem_store_op: Mir.Inst.Tag = switch (self.arch()) { |
| 3217 | .wasm32 => .i32_store8, |
| 3218 | .wasm64 => .i64_store8, |
| 3184 | 3219 | else => unreachable, |
| 3185 | 3220 | }; |
| 3186 | | try self.addMemArg(mem_store_op, .{ .offset = 0, .alignment = 1 }); |
| 3221 | try self.addMemArg(mem_store_op, .{ .offset = ptr.offset(), .alignment = 1 }); |
| 3187 | 3222 | try self.emitWValue(offset); |
| 3188 | 3223 | try self.addImm32(1); |
| 3189 | 3224 | switch (self.ptrSize()) { |
| ... | ... | @@ -3207,14 +3242,18 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3207 | 3242 | const elem_ty = array_ty.childType(); |
| 3208 | 3243 | const elem_size = elem_ty.abiSize(self.target); |
| 3209 | 3244 | |
| 3210 | | // calculate index into slice |
| 3211 | | try self.emitWValue(array); |
| 3245 | const array_ptr = switch (array) { |
| 3246 | .stack_offset => try self.buildPointerOffset(array, 0, .new), |
| 3247 | else => array, |
| 3248 | }; |
| 3249 | |
| 3250 | try self.emitWValue(array_ptr); |
| 3212 | 3251 | try self.emitWValue(index); |
| 3213 | 3252 | try self.addImm32(@bitCast(i32, @intCast(u32, elem_size))); |
| 3214 | 3253 | try self.addTag(.i32_mul); |
| 3215 | 3254 | try self.addTag(.i32_add); |
| 3216 | 3255 | |
| 3217 | | const result = try self.allocLocal(elem_ty); |
| 3256 | const result = try self.allocLocal(Type.usize); |
| 3218 | 3257 | try self.addLabel(.local_set, result.local); |
| 3219 | 3258 | |
| 3220 | 3259 | if (isByRef(elem_ty, self.target)) { |
| ... | ... | @@ -3277,9 +3316,7 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3277 | 3316 | if (isByRef(elem_ty, self.target)) { |
| 3278 | 3317 | // copy stack pointer into a temporary local, which is |
| 3279 | 3318 | // moved for each element to store each value in the right position. |
| 3280 | | const offset = try self.allocLocal(Type.usize); |
| 3281 | | try self.emitWValue(result); |
| 3282 | | try self.addLabel(.local_set, offset.local); |
| 3319 | const offset = try self.buildPointerOffset(result, 0, .new); |
| 3283 | 3320 | for (elements) |elem, elem_index| { |
| 3284 | 3321 | const elem_val = try self.resolveInst(elem); |
| 3285 | 3322 | try self.store(offset, elem_val, elem_ty, 0); |
| ... | ... | @@ -3301,9 +3338,7 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3301 | 3338 | .Struct => { |
| 3302 | 3339 | const tuple = vector_ty.castTag(.tuple).?.data; |
| 3303 | 3340 | const result = try self.allocStack(vector_ty); |
| 3304 | | const offset = try self.allocLocal(Type.usize); // pointer to offset |
| 3305 | | try self.emitWValue(result); |
| 3306 | | try self.addLabel(.local_set, offset.local); |
| 3341 | const offset = try self.buildPointerOffset(result, 0, .new); // pointer to offset |
| 3307 | 3342 | for (elements) |elem, elem_index| { |
| 3308 | 3343 | if (tuple.values[elem_index].tag() != .unreachable_value) continue; |
| 3309 | 3344 | |
| ... | ... | @@ -3379,10 +3414,10 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma |
| 3379 | 3414 | const result = try self.allocLocal(Type.initTag(.i32)); |
| 3380 | 3415 | { |
| 3381 | 3416 | try self.startBlock(.block, wasm.block_empty); |
| 3382 | | const lhs_high_bit = try self.load(lhs, Type.initTag(.u64), 0); |
| 3383 | | const lhs_low_bit = try self.load(lhs, Type.initTag(.u64), 8); |
| 3384 | | const rhs_high_bit = try self.load(rhs, Type.initTag(.u64), 0); |
| 3385 | | const rhs_low_bit = try self.load(rhs, Type.initTag(.u64), 8); |
| 3417 | const lhs_high_bit = try self.load(lhs, Type.u64, 0); |
| 3418 | const lhs_low_bit = try self.load(lhs, Type.u64, 8); |
| 3419 | const rhs_high_bit = try self.load(rhs, Type.u64, 0); |
| 3420 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 3386 | 3421 | try self.emitWValue(lhs_high_bit); |
| 3387 | 3422 | try self.emitWValue(rhs_high_bit); |
| 3388 | 3423 | try self.addTag(.i64_ne); |