authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-11-20 21:28:39+01:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2021-11-21 21:07:55+01:00
logdeb8d0765b46f75546f4342ad9078dbe6ad7b9be
tree960363fd9114f4e4485480a961a5be8b09abf412
parentec5220405b0218f892f6a1636ddd04d791017309
signaturelock-open Commit is signed but in an unrecognized format.

wasm: Fix text cases and add pointer test cases

Ensure all previous test cases are still passing, as well as add some basic tests for now for testing pointers to the stack. This means we can start implementing wasm's C ABI found at: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md We also simplified the block logic by always using 'void' block types and instead writing the value to a local, which can then be referenced by continues instructions, as done currently by AIR. Besides this, we also no longer need to insert blocks at an offset, as we simply write the saved temporary after we create the block.

2 files changed, 153 insertions(+), 53 deletions(-)

src/arch/wasm/CodeGen.zig+118-53
...@@ -187,7 +187,13 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -187,7 +187,13 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
187 32 => switch (args.valtype1.?) {187 32 => switch (args.valtype1.?) {
188 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,188 .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u,
189 .i32 => return .i32_load,189 .i32 => return .i32_load,
190 .f32, .f64 => unreachable,190 .f32 => return .f32_load,
191 .f64 => unreachable,
192 },
193 64 => switch (args.valtype1.?) {
194 .i64 => return .i64_load,
195 .f64 => return .f64_load,
196 else => unreachable,
191 },197 },
192 else => unreachable,198 else => unreachable,
193 } else switch (args.valtype1.?) {199 } else switch (args.valtype1.?) {
...@@ -216,6 +222,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {...@@ -216,6 +222,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode {
216 },222 },
217 64 => switch (args.valtype1.?) {223 64 => switch (args.valtype1.?) {
218 .i64 => return .i64_store,224 .i64 => return .i64_store,
225 .f64 => return .f64_store,
219 else => unreachable,226 else => unreachable,
220 },227 },
221 else => unreachable,228 else => unreachable,
...@@ -505,7 +512,10 @@ gpa: *mem.Allocator,...@@ -505,7 +512,10 @@ gpa: *mem.Allocator,
505/// Table to save `WValue`'s generated by an `Air.Inst`512/// Table to save `WValue`'s generated by an `Air.Inst`
506values: ValueTable,513values: ValueTable,
507/// Mapping from Air.Inst.Index to block ids514/// Mapping from Air.Inst.Index to block ids
508blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{},515blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, struct {
516 label: u32,
517 value: WValue,
518}) = .{},
509/// `bytes` contains the wasm bytecode belonging to the 'code' section.519/// `bytes` contains the wasm bytecode belonging to the 'code' section.
510code: ArrayList(u8),520code: ArrayList(u8),
511/// Contains the generated function type bytecode for the current function521/// Contains the generated function type bytecode for the current function
...@@ -984,8 +994,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -984,8 +994,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
984 .dbg_stmt => WValue.none,994 .dbg_stmt => WValue.none,
985 .intcast => self.airIntcast(inst),995 .intcast => self.airIntcast(inst),
986996
987 .is_err => self.airIsErr(inst, .i32_eq),997 .is_err => self.airIsErr(inst, .i32_ne),
988 .is_non_err => self.airIsErr(inst, .i32_ne),998 .is_non_err => self.airIsErr(inst, .i32_eq),
989999
990 .is_null => self.airIsNull(inst, .i32_ne),1000 .is_null => self.airIsNull(inst, .i32_ne),
991 .is_non_null => self.airIsNull(inst, .i32_eq),1001 .is_non_null => self.airIsNull(inst, .i32_eq),
...@@ -1065,12 +1075,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1065,12 +1075,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
10651075
1066 const ret_ty = target.ty.fnReturnType();1076 const ret_ty = target.ty.fnReturnType();
1067 switch (ret_ty.zigTypeTag()) {1077 switch (ret_ty.zigTypeTag()) {
1068 .ErrorUnion, .Optional => {1078 .Void, .NoReturn => return WValue.none,
1079 else => {
1069 const result_local = try self.allocLocal(ret_ty);1080 const result_local = try self.allocLocal(ret_ty);
1070 try self.addLabel(.local_set, result_local.local);1081 try self.addLabel(.local_set, result_local.local);
1071 return result_local;1082 return result_local;
1072 },1083 },
1073 else => return WValue.none,
1074 }1084 }
1075}1085}
10761086
...@@ -1086,7 +1096,7 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1086,7 +1096,7 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1086 if (abi_size == 0) return WValue{ .none = {} };1096 if (abi_size == 0) return WValue{ .none = {} };
10871097
1088 // local, containing the offset to the stack position1098 // local, containing the offset to the stack position
1089 const local = try self.allocLocal(child_type);1099 const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32
1090 try self.moveStack(@intCast(u32, abi_size), local.local);1100 try self.moveStack(@intCast(u32, abi_size), local.local);
10911101
1092 return local;1102 return local;
...@@ -1114,29 +1124,64 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1114,29 +1124,64 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1114 var buf: Type.Payload.ElemType = undefined;1124 var buf: Type.Payload.ElemType = undefined;
1115 const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);1125 const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf);
1116 const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8);1126 const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.u8);
1117 const payload_offset = @intCast(u32, tag_ty.abiSize(self.target));1127 const payload_offset = if (ty.zigTypeTag() == .ErrorUnion)
11181128 @intCast(u32, tag_ty.abiSize(self.target))
1119 if (rhs == .constant) {1129 else
1120 // constant will contain both tag and payload,1130 @intCast(u32, ty.abiSize(self.target) - payload_ty.abiSize(self.target));
1121 // so save those in 2 temporary locals before storing them1131
1122 // in memory1132 switch (rhs) {
1123 try self.emitWValue(rhs);1133 .constant => {
1124 const tag_local = try self.allocLocal(tag_ty);1134 // constant will contain both tag and payload,
1125 const payload_local = try self.allocLocal(payload_ty);1135 // so save those in 2 temporary locals before storing them
11261136 // in memory
1127 try self.addLabel(.local_set, payload_local.local);1137 try self.emitWValue(rhs);
1128 try self.addLabel(.local_set, tag_local.local);1138 const tag_local = try self.allocLocal(tag_ty);
11291139 const payload_local = try self.allocLocal(payload_ty);
1130 try self.store(lhs, tag_local, tag_ty, 0);1140
1131 return try self.store(lhs, payload_local, payload_ty, payload_offset);1141 try self.addLabel(.local_set, payload_local.local);
1132 } else {1142 try self.addLabel(.local_set, tag_local.local);
1133 // Load values from `rhs` stack position and store in `lhs` instead1143
1134 const tag_local = try self.load(rhs, tag_ty, 0);1144 try self.store(lhs, tag_local, tag_ty, 0);
1135 const payload_local = try self.load(rhs, payload_ty, payload_offset);1145 return try self.store(lhs, payload_local, payload_ty, payload_offset);
1146 },
1147 .local => {
1148 // Load values from `rhs` stack position and store in `lhs` instead
1149 const tag_local = try self.load(rhs, tag_ty, 0);
1150 const payload_local = try self.load(rhs, payload_ty, payload_offset);
11361151
1137 try self.store(lhs, tag_local, tag_ty, 0);1152 try self.store(lhs, tag_local, tag_ty, 0);
1138 return try self.store(lhs, payload_local, payload_ty, payload_offset);1153 return try self.store(lhs, payload_local, payload_ty, payload_offset);
1154 },
1155 .local_with_offset => |with_offset| {
1156 const tag_local = try self.allocLocal(tag_ty);
1157 try self.addImm32(0);
1158 try self.store(lhs, tag_local, tag_ty, 0);
1159
1160 return try self.store(
1161 lhs,
1162 .{ .local = with_offset.local },
1163 payload_ty,
1164 with_offset.offset,
1165 );
1166 },
1167 else => unreachable,
1168 }
1169 },
1170 .Struct => {
1171 // we are copying a struct with its fields.
1172 // Replace this with a wasm memcpy instruction once we support that feature.
1173 const fields_len = ty.structFieldCount();
1174 var index: usize = 0;
1175 while (index < fields_len) : (index += 1) {
1176 const field_ty = ty.structFieldType(index);
1177 if (!field_ty.hasCodeGenBits()) continue;
1178 const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch {
1179 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
1180 };
1181 const field_local = try self.load(rhs, field_ty, field_offset);
1182 try self.store(lhs, field_local, field_ty, field_offset);
1139 }1183 }
1184 return;
1140 },1185 },
1141 else => {},1186 else => {},
1142 }1187 }
...@@ -1429,21 +1474,29 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1429,21 +1474,29 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1429 const extra = self.air.extraData(Air.Block, ty_pl.payload);1474 const extra = self.air.extraData(Air.Block, ty_pl.payload);
1430 const body = self.air.extra[extra.end..][0..extra.data.body_len];1475 const body = self.air.extra[extra.end..][0..extra.data.body_len];
14311476
1432 try self.startBlock(.block, block_ty, null);1477 // if block_ty is non-empty, we create a register to store the temporary value
1478 const block_result: WValue = if (block_ty != wasm.block_empty)
1479 try self.allocLocal(self.air.getRefType(ty_pl.ty))
1480 else
1481 WValue.none;
1482
1483 try self.startBlock(.block, wasm.block_empty);
1433 // Here we set the current block idx, so breaks know the depth to jump1484 // Here we set the current block idx, so breaks know the depth to jump
1434 // to when breaking out.1485 // to when breaking out.
1435 try self.blocks.putNoClobber(self.gpa, inst, self.block_depth);1486 try self.blocks.putNoClobber(self.gpa, inst, .{
1487 .label = self.block_depth,
1488 .value = block_result,
1489 });
1436 try self.genBody(body);1490 try self.genBody(body);
1437 try self.endBlock();1491 try self.endBlock();
14381492
1439 return .none;1493 return block_result;
1440}1494}
14411495
1442/// appends a new wasm block to the code section and increases the `block_depth` by 11496/// appends a new wasm block to the code section and increases the `block_depth` by 1
1443fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8, with_offset: ?usize) !void {1497fn startBlock(self: *Self, block_tag: wasm.Opcode, valtype: u8) !void {
1444 self.block_depth += 1;1498 self.block_depth += 1;
1445 const offset = with_offset orelse self.mir_instructions.len;1499 try self.addInst(.{
1446 try self.addInstAt(offset, .{
1447 .tag = Mir.Inst.Tag.fromOpcode(block_tag),1500 .tag = Mir.Inst.Tag.fromOpcode(block_tag),
1448 .data = .{ .block_type = valtype },1501 .data = .{ .block_type = valtype },
1449 });1502 });
...@@ -1462,7 +1515,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1462,7 +1515,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14621515
1463 // result type of loop is always 'noreturn', meaning we can always1516 // result type of loop is always 'noreturn', meaning we can always
1464 // emit the wasm type 'block_empty'.1517 // emit the wasm type 'block_empty'.
1465 try self.startBlock(.loop, wasm.block_empty, null);1518 try self.startBlock(.loop, wasm.block_empty);
1466 try self.genBody(body);1519 try self.genBody(body);
14671520
1468 // breaking to the index of a loop block will continue the loop instead1521 // breaking to the index of a loop block will continue the loop instead
...@@ -1480,13 +1533,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1480,13 +1533,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1480 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];1533 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1481 // TODO: Handle death instructions for then and else body1534 // TODO: Handle death instructions for then and else body
14821535
1483 // insert blocks at the position of `offset` so
1484 // the condition can jump to it
1485 const offset = self.mir_instructions.len;
1486 try self.emitWValue(condition);
1487
1488 // result type is always noreturn, so use `block_empty` as type.1536 // result type is always noreturn, so use `block_empty` as type.
1489 try self.startBlock(.block, wasm.block_empty, offset);1537 try self.startBlock(.block, wasm.block_empty);
1538 // emit the conditional value
1539 try self.emitWValue(condition);
14901540
1491 // we inserted the block in front of the condition1541 // we inserted the block in front of the condition
1492 // so now check if condition matches. If not, break outside this block1542 // so now check if condition matches. If not, break outside this block
...@@ -1539,15 +1589,20 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner...@@ -1539,15 +1589,20 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner
15391589
1540fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1590fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1541 const br = self.air.instructions.items(.data)[inst].br;1591 const br = self.air.instructions.items(.data)[inst].br;
1592 const block = self.blocks.get(br.block_inst).?;
15421593
1543 // if operand has codegen bits we should break with a value1594 // if operand has codegen bits we should break with a value
1544 if (self.air.typeOf(br.operand).hasCodeGenBits()) {1595 if (self.air.typeOf(br.operand).hasCodeGenBits()) {
1545 try self.emitWValue(self.resolveInst(br.operand));1596 try self.emitWValue(self.resolveInst(br.operand));
1597
1598 if (block.value != .none) {
1599 try self.addLabel(.local_set, block.value.local);
1600 }
1546 }1601 }
15471602
1548 // We map every block to its block index.1603 // We map every block to its block index.
1549 // We then determine how far we have to jump to it by subtracting it from current block depth1604 // We then determine how far we have to jump to it by subtracting it from current block depth
1550 const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?;1605 const idx: u32 = self.block_depth - block.label;
1551 try self.addLabel(.br, idx);1606 try self.addLabel(.br, idx);
15521607
1553 return .none;1608 return .none;
...@@ -1677,7 +1732,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1677,7 +1732,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1677 }1732 }
16781733
1679 case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });1734 case_list.appendAssumeCapacity(.{ .values = values, .body = case_body });
1680 try self.startBlock(.block, blocktype, null);1735 try self.startBlock(.block, blocktype);
1681 }1736 }
16821737
1683 // When the highest and lowest values are seperated by '50',1738 // When the highest and lowest values are seperated by '50',
...@@ -1690,7 +1745,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1690,7 +1745,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1690 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];1745 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
1691 const has_else_body = else_body.len != 0;1746 const has_else_body = else_body.len != 0;
1692 if (has_else_body) {1747 if (has_else_body) {
1693 try self.startBlock(.block, blocktype, null);1748 try self.startBlock(.block, blocktype);
1694 }1749 }
16951750
1696 if (!is_sparse) {1751 if (!is_sparse) {
...@@ -1698,7 +1753,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1698,7 +1753,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1698 // The value 'target' represents the index into the table.1753 // The value 'target' represents the index into the table.
1699 // Each index in the table represents a label to the branch1754 // Each index in the table represents a label to the branch
1700 // to jump to.1755 // to jump to.
1701 try self.startBlock(.block, blocktype, null);1756 try self.startBlock(.block, blocktype);
1702 try self.emitWValue(target);1757 try self.emitWValue(target);
1703 if (lowest < 0) {1758 if (lowest < 0) {
1704 // since br_table works using indexes, starting from '0', we must ensure all values1759 // since br_table works using indexes, starting from '0', we must ensure all values
...@@ -1754,7 +1809,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1754,7 +1809,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1754 try self.addLabel(.br_if, 0);1809 try self.addLabel(.br_if, 0);
1755 } else {1810 } else {
1756 // in multi-value prongs we must check if any prongs match the target value.1811 // in multi-value prongs we must check if any prongs match the target value.
1757 try self.startBlock(.block, blocktype, null);1812 try self.startBlock(.block, blocktype);
1758 for (case.values) |value| {1813 for (case.values) |value| {
1759 try self.emitWValue(target);1814 try self.emitWValue(target);
1760 try self.emitConstant(value.value, target_ty);1815 try self.emitConstant(value.value, target_ty);
...@@ -1794,7 +1849,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W...@@ -1794,7 +1849,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W
1794 .alignment = err_ty.abiAlignment(self.target),1849 .alignment = err_ty.abiAlignment(self.target),
1795 });1850 });
1796 try self.addInst(.{1851 try self.addInst(.{
1797 .tag = .i32_load,1852 .tag = .i32_load16_u,
1798 .data = .{ .payload = mem_arg_index },1853 .data = .{ .payload = mem_arg_index },
1799 });1854 });
18001855
...@@ -1811,14 +1866,14 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -1811,14 +1866,14 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue
1811 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1866 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1812 const operand = self.resolveInst(ty_op.operand);1867 const operand = self.resolveInst(ty_op.operand);
1813 const err_ty = self.air.typeOf(ty_op.operand);1868 const err_ty = self.air.typeOf(ty_op.operand);
1814 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target) / 8);1869 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
18151870 return self.load(operand, err_ty.errorUnionPayload(), offset);
1816 return self.load(operand, self.air.getRefType(ty_op.ty), offset);
1817}1871}
18181872
1819fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1873fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1820 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1874 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1821 return self.resolveInst(ty_op.operand);1875 _ = ty_op;
1876 return self.fail("TODO: wasm airWrapErrUnionPayload", .{});
1822}1877}
18231878
1824fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1879fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1880,8 +1935,9 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1880,8 +1935,9 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
18801935
1881 var buf: Type.Payload.ElemType = undefined;1936 var buf: Type.Payload.ElemType = undefined;
1882 const child_ty = opt_ty.optionalChild(&buf);1937 const child_ty = opt_ty.optionalChild(&buf);
1938 const offset = opt_ty.abiSize(self.target) - child_ty.abiSize(self.target);
18831939
1884 return self.load(operand, child_ty, @as(u32, 1)); // null tag is 1 byte1940 return self.load(operand, child_ty, @intCast(u32, offset));
1885}1941}
18861942
1887fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1943fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1893,5 +1949,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -1893,5 +1949,14 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
18931949
1894fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1950fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1895 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1951 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1896 return self.resolveInst(ty_op.operand);1952 const operand = self.resolveInst(ty_op.operand);
1953
1954 const op_ty = self.air.typeOf(ty_op.operand);
1955 const optional_ty = self.air.getRefType(ty_op.ty);
1956 const offset = optional_ty.abiSize(self.target) - op_ty.abiSize(self.target);
1957
1958 return WValue{ .local_with_offset = .{
1959 .local = operand.local,
1960 .offset = @intCast(u32, offset),
1961 } };
1897}1962}
test/stage2/wasm.zig+35
...@@ -740,4 +740,39 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -740,4 +740,39 @@ pub fn addCases(ctx: *TestContext) !void {
740 \\}740 \\}
741 , "0\n");741 , "0\n");
742 }742 }
743
744 {
745 var case = ctx.exe("wasm pointers", wasi);
746
747 case.addCompareOutput(
748 \\pub export fn _start() u32 {
749 \\ var x: u32 = 0;
750 \\
751 \\ foo(&x);
752 \\ return x;
753 \\}
754 \\
755 \\fn foo(x: *u32)void {
756 \\ x.* = 2;
757 \\}
758 , "2\n");
759
760 case.addCompareOutput(
761 \\pub export fn _start() u32 {
762 \\ var x: u32 = 0;
763 \\
764 \\ foo(&x);
765 \\ bar(&x);
766 \\ return x;
767 \\}
768 \\
769 \\fn foo(x: *u32)void {
770 \\ x.* = 2;
771 \\}
772 \\
773 \\fn bar(x: *u32) void {
774 \\ x.* += 2;
775 \\}
776 , "4\n");
777 }
743}778}