authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-11-29 10:52:04+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-11-29 10:52:04+01:00
log7a7df392d146b8f77c74b0578fc48f6c927efc93
tree248d909fcbb7866124aafdf9e01b7d2cb6646da9
parent2ca5a859e9f5148a09928803cc2d109440a8067c
parentadf059f272dfd3c1652bce774c0b6c204d5d6b8b
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10240 from Luukdegram/stage2-wasm-behaviour

Stage2: wasm - Implement 'zig test'

9 files changed, 623 insertions(+), 244 deletions(-)

lib/std/start.zig+18
......@@ -30,6 +30,8 @@ comptime {
3030 }
3131 } else if (builtin.os.tag == .windows) {
3232 @export(wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
33 } else if (builtin.os.tag == .wasi and @hasDecl(root, "main")) {
34 @export(wasmMain2, .{ .name = "_start" });
3335 } else {
3436 if (!@hasDecl(root, "_start")) {
3537 @export(_start2, .{ .name = "_start" });
......@@ -98,6 +100,22 @@ fn callMain2() noreturn {
98100 exit2(0);
99101}
100102
103fn wasmMain2() u8 {
104 switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) {
105 .Void => {
106 root.main();
107 return 0;
108 },
109 .Int => |info| {
110 if (info.bits != 8 or info.signedness == .signed) {
111 @compileError(bad_main_ret);
112 }
113 return root.main();
114 },
115 else => @compileError("Bad return type main"),
116 }
117}
118
101119fn wWinMainCRTStartup2() callconv(.C) noreturn {
102120 root.main();
103121 exit2(0);
src/arch/wasm/CodeGen.zig+315-40
......@@ -692,6 +692,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
692692 .Struct,
693693 .ErrorUnion,
694694 .Optional,
695 .Fn,
695696 => wasm.Valtype.i32,
696697 else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),
697698 };
......@@ -809,23 +810,52 @@ pub fn genFunc(self: *Self) InnerError!Result {
809810}
810811
811812/// Generates the wasm bytecode for the declaration belonging to `Context`
812pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result {
813pub fn genDecl(self: *Self, ty: Type, val: Value) InnerError!Result {
814 if (val.isUndef()) {
815 try self.code.appendNTimes(0xaa, @intCast(usize, ty.abiSize(self.target)));
816 return Result.appended;
817 }
813818 switch (ty.zigTypeTag()) {
814819 .Fn => {
815 if (val.tag() == .extern_fn) {
816 var func_type = try self.genFunctype(self.decl.ty);
817 defer func_type.deinit(self.gpa);
818 self.decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
819 return Result.appended; // don't need code body for extern functions
820 const fn_decl = switch (val.tag()) {
821 .extern_fn => val.castTag(.extern_fn).?.data,
822 .function => val.castTag(.function).?.data.owner_decl,
823 else => unreachable,
824 };
825 return try self.lowerDeclRef(fn_decl);
826 },
827 .Optional => {
828 var opt_buf: Type.Payload.ElemType = undefined;
829 const payload_type = ty.optionalChild(&opt_buf);
830 if (ty.isPtrLikeOptional()) {
831 if (val.castTag(.opt_payload)) |payload| {
832 return try self.genDecl(payload_type, payload.data);
833 } else if (!val.isNull()) {
834 return try self.genDecl(payload_type, val);
835 } else {
836 try self.code.appendNTimes(0, @intCast(usize, ty.abiSize(self.target)));
837 return Result.appended;
838 }
820839 }
821 return self.fail("TODO implement wasm codegen for function pointers", .{});
840 // `null-tag` byte
841 try self.code.appendNTimes(@boolToInt(!val.isNull()), 4);
842 const pl_result = try self.genDecl(
843 payload_type,
844 if (val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
845 );
846 switch (pl_result) {
847 .appended => {},
848 .externally_managed => |payload| try self.code.appendSlice(payload),
849 }
850 return Result.appended;
822851 },
823 .Array => {
824 if (val.castTag(.bytes)) |payload| {
852 .Array => switch (val.tag()) {
853 .bytes => {
854 const payload = val.castTag(.bytes).?;
825855 if (ty.sentinel()) |sentinel| {
826856 try self.code.appendSlice(payload.data);
827857
828 switch (try self.gen(ty.childType(), sentinel)) {
858 switch (try self.genDecl(ty.childType(), sentinel)) {
829859 .appended => return Result.appended,
830860 .externally_managed => |data| {
831861 try self.code.appendSlice(data);
......@@ -834,16 +864,33 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result {
834864 }
835865 }
836866 return Result{ .externally_managed = payload.data };
837 } else return self.fail("TODO implement gen for more kinds of arrays", .{});
867 },
868 .array => {
869 const elem_vals = val.castTag(.array).?.data;
870 const elem_ty = ty.elemType();
871 for (elem_vals) |elem_val| {
872 switch (try self.genDecl(elem_ty, elem_val)) {
873 .appended => {},
874 .externally_managed => |data| {
875 try self.code.appendSlice(data);
876 },
877 }
878 }
879 return Result.appended;
880 },
881 else => return self.fail("TODO implement genDecl for array type value: {s}", .{@tagName(val.tag())}),
838882 },
839883 .Int => {
840884 const info = ty.intInfo(self.target);
841 if (info.bits == 8 and info.signedness == .unsigned) {
842 const int_byte = val.toUnsignedInt();
843 try self.code.append(@intCast(u8, int_byte));
844 return Result.appended;
845 }
846 return self.fail("TODO: Implement codegen for int type: '{}'", .{ty});
885 const abi_size = @intCast(usize, ty.abiSize(self.target));
886 // todo: Implement integer sizes larger than 64bits
887 if (info.bits > 64) return self.fail("TODO: Implement genDecl for integer bit size: {d}", .{info.bits});
888 var buf: [8]u8 = undefined;
889 if (info.signedness == .unsigned) {
890 std.mem.writeIntLittle(u64, &buf, val.toUnsignedInt());
891 } else std.mem.writeIntLittle(i64, &buf, val.toSignedInt());
892 try self.code.appendSlice(buf[0..abi_size]);
893 return Result.appended;
847894 },
848895 .Enum => {
849896 try self.emitConstant(val, ty);
......@@ -855,15 +902,83 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result {
855902 return Result.appended;
856903 },
857904 .Struct => {
858 // TODO write the fields for real
859 const abi_size = try std.math.cast(usize, ty.abiSize(self.target));
860 try self.code.writer().writeByteNTimes(0xaa, abi_size);
861 return Result{ .appended = {} };
905 const field_vals = val.castTag(.@"struct").?.data;
906 for (field_vals) |field_val, index| {
907 const field_ty = ty.structFieldType(index);
908 if (!field_ty.hasCodeGenBits()) continue;
909
910 switch (try self.genDecl(field_ty, field_val)) {
911 .appended => {},
912 .externally_managed => |payload| try self.code.appendSlice(payload),
913 }
914 }
915 return Result.appended;
916 },
917 .Union => {
918 // TODO: Implement Union declarations
919 const abi_size = @intCast(usize, ty.abiSize(self.target));
920 try self.code.appendNTimes(0xaa, abi_size);
921 return Result.appended;
922 },
923 .Pointer => switch (val.tag()) {
924 .variable => {
925 const decl = val.castTag(.variable).?.data.owner_decl;
926 return try self.lowerDeclRef(decl);
927 },
928 .decl_ref => {
929 const decl = val.castTag(.decl_ref).?.data;
930 return try self.lowerDeclRef(decl);
931 },
932 .slice => {
933 const slice = val.castTag(.slice).?.data;
934 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
935 const ptr_ty = ty.slicePtrFieldType(&buf);
936 switch (try self.genDecl(ptr_ty, slice.ptr)) {
937 .externally_managed => |data| try self.code.appendSlice(data),
938 .appended => {},
939 }
940 switch (try self.genDecl(Type.usize, slice.len)) {
941 .externally_managed => |data| try self.code.appendSlice(data),
942 .appended => {},
943 }
944 return Result.appended;
945 },
946 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
862947 },
863948 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
864949 }
865950}
866951
952fn lowerDeclRef(self: *Self, decl: *Module.Decl) InnerError!Result {
953 decl.alive = true;
954
955 const offset = @intCast(u32, self.code.items.len);
956 const atom = &self.decl.link.wasm;
957 const target_sym_index = decl.link.wasm.sym_index;
958
959 if (decl.ty.zigTypeTag() == .Fn) {
960 // We found a function pointer, so add it to our table,
961 // as function pointers are not allowed to be stored inside the data section,
962 // but rather in a function table which are called by index
963 try self.bin_file.addTableFunction(target_sym_index);
964 try atom.relocs.append(self.gpa, .{
965 .index = target_sym_index,
966 .offset = offset,
967 .relocation_type = .R_WASM_TABLE_INDEX_I32,
968 });
969 } else {
970 try atom.relocs.append(self.gpa, .{
971 .index = target_sym_index,
972 .offset = offset,
973 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
974 });
975 }
976 const ptr_width = @intCast(usize, self.target.cpu.arch.ptrBitWidth() / 8);
977 try self.code.appendNTimes(0xaa, ptr_width);
978
979 return Result.appended;
980}
981
867982const CallWValues = struct {
868983 args: []WValue,
869984 return_value: WValue,
......@@ -1015,6 +1130,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
10151130 .loop => self.airLoop(inst),
10161131 .not => self.airNot(inst),
10171132 .ret => self.airRet(inst),
1133 .ret_ptr => self.airRetPtr(inst),
1134 .ret_load => self.airRetLoad(inst),
1135 .slice_len => self.airSliceLen(inst),
1136 .slice_elem_val => self.airSliceElemVal(inst),
10181137 .store => self.airStore(inst),
10191138 .struct_field_ptr => self.airStructFieldPtr(inst),
10201139 .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0),
......@@ -1027,6 +1146,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
10271146 .wrap_optional => self.airWrapOptional(inst),
10281147
10291148 .unwrap_errunion_payload => self.airUnwrapErrUnionPayload(inst),
1149 .unwrap_errunion_err => self.airUnwrapErrUnionError(inst),
10301150 .wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
10311151
10321152 .optional_payload => self.airOptionalPayload(inst),
......@@ -1059,13 +1179,48 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
10591179 return .none;
10601180}
10611181
1182fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1183 const child_type = self.air.typeOfIndex(inst).childType();
1184
1185 // Initialize the stack
1186 if (self.initial_stack_value == .none) {
1187 try self.initializeStack();
1188 }
1189
1190 const abi_size = child_type.abiSize(self.target);
1191 if (abi_size == 0) return WValue{ .none = {} };
1192
1193 // local, containing the offset to the stack position
1194 const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32
1195 try self.moveStack(@intCast(u32, abi_size), local.local);
1196
1197 return local;
1198}
1199
1200fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1201 const un_op = self.air.instructions.items(.data)[inst].un_op;
1202 const operand = self.resolveInst(un_op);
1203 const result = try self.load(operand, self.air.typeOf(un_op), 0);
1204 try self.addLabel(.local_get, result.local);
1205 try self.restoreStackPointer();
1206 try self.addTag(.@"return");
1207 return .none;
1208}
1209
10621210fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
10631211 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
10641212 const extra = self.air.extraData(Air.Call, pl_op.payload);
10651213 const args = self.air.extra[extra.end..][0..extra.data.args_len];
1214 const ty = self.air.typeOf(pl_op.operand);
10661215
1067 const target: *Decl = blk: {
1068 const func_val = self.air.value(pl_op.operand).?;
1216 const fn_ty = switch (ty.zigTypeTag()) {
1217 .Fn => ty,
1218 .Pointer => ty.childType(),
1219 else => unreachable,
1220 };
1221
1222 const target: ?*Decl = blk: {
1223 const func_val = self.air.value(pl_op.operand) orelse break :blk null;
10691224
10701225 if (func_val.castTag(.function)) |func| {
10711226 break :blk func.data.owner_decl;
......@@ -1080,9 +1235,25 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
10801235 try self.emitWValue(arg_val);
10811236 }
10821237
1083 try self.addLabel(.call, target.link.wasm.sym_index);
1238 if (target) |direct| {
1239 try self.addLabel(.call, direct.link.wasm.sym_index);
1240 } else {
1241 // in this case we call a function pointer
1242 // so load its value onto the stack
1243 std.debug.assert(ty.zigTypeTag() == .Pointer);
1244 const operand = self.resolveInst(pl_op.operand);
1245 try self.emitWValue(operand);
1246 const result = try self.load(operand, fn_ty, operand.local_with_offset.offset);
1247 try self.addLabel(.local_get, result.local);
1248
1249 var fn_type = try self.genFunctype(fn_ty);
1250 defer fn_type.deinit(self.gpa);
10841251
1085 const ret_ty = target.ty.fnReturnType();
1252 const fn_type_index = try self.bin_file.putOrGetFuncType(fn_type);
1253 try self.addLabel(.call_indirect, fn_type_index);
1254 }
1255
1256 const ret_ty = fn_ty.fnReturnType();
10861257 switch (ret_ty.zigTypeTag()) {
10871258 .Void, .NoReturn => return WValue.none,
10881259 else => {
......@@ -1145,13 +1316,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
11451316 // in memory
11461317 try self.emitWValue(rhs);
11471318 const tag_local = try self.allocLocal(tag_ty);
1148 const payload_local = try self.allocLocal(payload_ty);
11491319
1150 try self.addLabel(.local_set, payload_local.local);
1320 if (payload_ty.hasCodeGenBits()) {
1321 const payload_local = try self.allocLocal(payload_ty);
1322 try self.addLabel(.local_set, payload_local.local);
1323 try self.store(lhs, payload_local, payload_ty, payload_offset);
1324 }
11511325 try self.addLabel(.local_set, tag_local.local);
11521326
11531327 try self.store(lhs, tag_local, tag_ty, 0);
1154 return try self.store(lhs, payload_local, payload_ty, payload_offset);
1328 return;
11551329 },
11561330 .local => {
11571331 // Load values from `rhs` stack position and store in `lhs` instead
......@@ -1197,9 +1371,18 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
11971371 try self.emitWValue(lhs);
11981372 try self.emitWValue(rhs);
11991373 const valtype = try self.typeToValtype(ty);
1374 // check if we should pass by pointer or value based on ABI size
1375 // TODO: Implement a way to get ABI values from a given type,
1376 // that is portable across the backend, rather than copying logic.
1377 const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8)
1378 @intCast(u8, ty.abiSize(self.target))
1379 else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum)
1380 @intCast(u8, ty.abiSize(self.target))
1381 else
1382 @as(u8, 4);
12001383 const opcode = buildOpcode(.{
12011384 .valtype1 = valtype,
1202 .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size
1385 .width = abi_size * 8, // use bitsize instead of byte size
12031386 .op = .store,
12041387 });
12051388
......@@ -1220,7 +1403,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
12201403 const ty = self.air.getRefType(ty_op.ty);
12211404
12221405 return switch (ty.zigTypeTag()) {
1223 .Struct, .ErrorUnion, .Optional => operand, // pass as pointer
1406 .Struct, .ErrorUnion, .Optional, .Pointer => operand, // pass as pointer
12241407 else => switch (operand) {
12251408 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),
12261409 else => try self.load(operand, ty, 0),
......@@ -1233,9 +1416,19 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
12331416 try self.emitWValue(operand);
12341417 // Build the opcode with the right bitsize
12351418 const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed;
1419 // check if we should pass by pointer or value based on ABI size
1420 // TODO: Implement a way to get ABI values from a given type,
1421 // that is portable across the backend, rather than copying logic.
1422 const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8)
1423 @intCast(u8, ty.abiSize(self.target))
1424 else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum)
1425 @intCast(u8, ty.abiSize(self.target))
1426 else
1427 @as(u8, 4);
1428
12361429 const opcode = buildOpcode(.{
12371430 .valtype1 = try self.typeToValtype(ty),
1238 .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size
1431 .width = abi_size * 8, // use bitsize instead of byte size
12391432 .op = .load,
12401433 .signedness = signedness,
12411434 });
......@@ -1399,14 +1592,19 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
13991592 const payload_val = pl.data;
14001593 // no error, so write a '0' const
14011594 try self.addImm32(0);
1402 // after the error code, we emit the payload
1403 try self.emitConstant(payload_val, payload_type);
1595
1596 if (payload_type.hasCodeGenBits()) {
1597 // after the error code, we emit the payload
1598 try self.emitConstant(payload_val, payload_type);
1599 }
14041600 } else {
14051601 // write the error val
14061602 try self.emitConstant(val, error_type);
14071603
1408 // no payload, so write a '0' const
1409 try self.addImm32(0);
1604 if (payload_type.hasCodeGenBits()) {
1605 // no payload, so write a '0' const
1606 try self.addImm32(0);
1607 }
14101608 }
14111609 },
14121610 .Optional => {
......@@ -1867,8 +2065,19 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue
18672065 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
18682066 const operand = self.resolveInst(ty_op.operand);
18692067 const err_ty = self.air.typeOf(ty_op.operand);
2068 const payload_ty = err_ty.errorUnionPayload();
2069 if (!payload_ty.hasCodeGenBits()) return WValue.none;
18702070 const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target));
1871 return self.load(operand, err_ty.errorUnionPayload(), offset);
2071 return try self.load(operand, payload_ty, offset);
2072}
2073
2074fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2075 if (self.liveness.isUnused(inst)) return WValue.none;
2076
2077 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2078 const operand = self.resolveInst(ty_op.operand);
2079 const err_ty = self.air.typeOf(ty_op.operand);
2080 return try self.load(operand, err_ty.errorUnionSet(), 0);
18722081}
18732082
18742083fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -1886,18 +2095,20 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
18862095 const op_bits = ref_info.bits;
18872096 const wanted_bits = ty.intInfo(self.target).bits;
18882097
1889 try self.emitWValue(operand);
18902098 if (op_bits > 32 and wanted_bits <= 32) {
2099 try self.emitWValue(operand);
18912100 try self.addTag(.i32_wrap_i64);
18922101 } else if (op_bits <= 32 and wanted_bits > 32) {
2102 try self.emitWValue(operand);
18932103 try self.addTag(switch (ref_info.signedness) {
18942104 .signed => .i64_extend_i32_s,
18952105 .unsigned => .i64_extend_i32_u,
18962106 });
1897 }
2107 } else return operand;
18982108
1899 // other cases are no-op
1900 return .none;
2109 const result = try self.allocLocal(ty);
2110 try self.addLabel(.local_set, result.local);
2111 return result;
19012112}
19022113
19032114fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
......@@ -1961,3 +2172,67 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
19612172 .offset = @intCast(u32, offset),
19622173 } };
19632174}
2175
2176fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2177 if (self.liveness.isUnused(inst)) return WValue.none;
2178
2179 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2180 const operand = self.resolveInst(ty_op.operand);
2181 const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8;
2182
2183 // Get pointer to slice
2184 try self.emitWValue(operand);
2185 // length of slice is stored after the pointer of the slice
2186 const extra_index = try self.addExtra(Mir.MemArg{
2187 .offset = pointer_width,
2188 .alignment = pointer_width,
2189 });
2190 try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = extra_index } });
2191
2192 const result = try self.allocLocal(Type.initTag(.i32)); // pointer is always i32
2193 // store slice length in local
2194 try self.addLabel(.local_set, result.local);
2195 return result;
2196}
2197
2198fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2199 if (self.liveness.isUnused(inst)) return WValue.none;
2200
2201 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2202 const slice_ty = self.air.typeOf(bin_op.lhs);
2203 const slice = self.resolveInst(bin_op.lhs);
2204 const index = self.resolveInst(bin_op.rhs);
2205 const elem_ty = slice_ty.childType();
2206 const elem_size = elem_ty.abiSize(self.target);
2207
2208 // load pointer onto stack
2209 try self.emitWValue(slice);
2210
2211 // calculate index into slice
2212 try self.emitWValue(index);
2213 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
2214 try self.addTag(.i32_mul);
2215 try self.addTag(.i32_add);
2216
2217 const abi_size = if (elem_size < 8)
2218 @intCast(u8, elem_size)
2219 else
2220 @as(u8, 4); // elements larger than 8 bytes will be passed by pointer
2221
2222 const extra_index = try self.addExtra(Mir.MemArg{
2223 .offset = 0,
2224 .alignment = elem_ty.abiAlignment(self.target),
2225 });
2226 const signedness: std.builtin.Signedness = if (elem_ty.isUnsignedInt()) .unsigned else .signed;
2227 const opcode = buildOpcode(.{
2228 .valtype1 = try self.typeToValtype(elem_ty),
2229 .width = abi_size * 8,
2230 .op = .load,
2231 .signedness = signedness,
2232 });
2233 try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = extra_index } });
2234
2235 const result = try self.allocLocal(elem_ty);
2236 try self.addLabel(.local_set, result.local);
2237 return result;
2238}
src/arch/wasm/Emit.zig+9-1
......@@ -47,6 +47,7 @@ pub fn emitMir(emit: *Emit) InnerError!void {
4747
4848 // relocatables
4949 .call => try emit.emitCall(inst),
50 .call_indirect => try emit.emitCallIndirect(inst),
5051 .global_get => try emit.emitGlobal(tag, inst),
5152 .global_set => try emit.emitGlobal(tag, inst),
5253 .memory_address => try emit.emitMemAddress(inst),
......@@ -256,7 +257,7 @@ fn emitMemArg(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
256257 try emit.code.append(@enumToInt(tag));
257258
258259 // wasm encodes alignment as power of 2, rather than natural alignment
259 const encoded_alignment = mem_arg.alignment >> 1;
260 const encoded_alignment = @ctz(u32, mem_arg.alignment);
260261 try leb128.writeULEB128(emit.code.writer(), encoded_alignment);
261262 try leb128.writeULEB128(emit.code.writer(), mem_arg.offset);
262263}
......@@ -276,6 +277,13 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
276277 });
277278}
278279
280fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
281 const label = emit.mir.instructions.items(.data)[inst].label;
282 try emit.code.append(std.wasm.opcode(.call_indirect));
283 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
284 try leb128.writeULEB128(emit.code.writer(), label);
285}
286
279287fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
280288 const symbol_index = emit.mir.instructions.items(.data)[inst].label;
281289 try emit.code.append(std.wasm.opcode(.i32_const));
src/arch/wasm/Mir.zig+5
......@@ -69,6 +69,11 @@ pub const Inst = struct {
6969 ///
7070 /// Uses `label`
7171 call = 0x10,
72 /// Calls a function pointer by its function signature
73 /// and index into the function table.
74 ///
75 /// Uses `label`
76 call_indirect = 0x11,
7277 /// Loads a local at given index onto the stack.
7378 ///
7479 /// Uses `label`
src/link/Wasm.zig+65-15
......@@ -79,7 +79,9 @@ memories: wasm.Memory = .{ .limits = .{ .min = 0, .max = null } },
7979/// Indirect function table, used to call function pointers
8080/// When this is non-zero, we must emit a table entry,
8181/// as well as an 'elements' section.
82function_table: std.ArrayListUnmanaged(Symbol) = .{},
82///
83/// Note: Key is symbol index, value represents the index into the table
84function_table: std.AutoHashMapUnmanaged(u32, u32) = .{},
8385
8486pub const Segment = struct {
8587 alignment: u32,
......@@ -276,7 +278,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
276278 defer codegen.deinit();
277279
278280 // generate the 'code' section for the function declaration
279 const result = codegen.gen(decl.ty, decl.val) catch |err| switch (err) {
281 const result = codegen.genDecl(decl.ty, decl.val) catch |err| switch (err) {
280282 error.CodegenFail => {
281283 decl.analysis = .codegen_failure;
282284 try module.failed_decls.put(module.gpa, decl, codegen.err_msg);
......@@ -334,6 +336,25 @@ pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
334336 else => unreachable,
335337 }
336338 }
339
340 // maybe remove from function table if needed
341 if (decl.ty.zigTypeTag() == .Fn) {
342 _ = self.function_table.remove(atom.sym_index);
343 }
344}
345
346/// Appends a new entry to the indirect function table
347pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void {
348 const index = @intCast(u32, self.function_table.count());
349 try self.function_table.put(self.base.allocator, symbol_index, index);
350}
351
352fn mapFunctionTable(self: *Wasm) void {
353 var it = self.function_table.valueIterator();
354 var index: u32 = 0;
355 while (it.next()) |value_ptr| : (index += 1) {
356 value_ptr.* = index;
357 }
337358}
338359
339360fn addOrUpdateImport(self: *Wasm, decl: *Module.Decl) !void {
......@@ -583,6 +604,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
583604
584605 try self.setupMemory();
585606 try self.allocateAtoms();
607 self.mapFunctionTable();
586608
587609 const file = self.base.file.?;
588610 const header_size = 5 + 1;
......@@ -662,6 +684,22 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
662684 );
663685 }
664686
687 if (self.function_table.count() > 0) {
688 const header_offset = try reserveVecSectionHeader(file);
689 const writer = file.writer();
690
691 try leb.writeULEB128(writer, wasm.reftype(.funcref));
692 try emitLimits(writer, .{ .min = 1, .max = null });
693
694 try writeVecSectionHeader(
695 file,
696 header_offset,
697 .table,
698 @intCast(u32, (try file.getPos()) - header_offset - header_size),
699 @as(u32, 1),
700 );
701 }
702
665703 // Memory section
666704 if (!self.base.options.import_memory) {
667705 const header_offset = try reserveVecSectionHeader(file);
......@@ -743,6 +781,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
743781 );
744782 }
745783
784 // element section (function table)
785 if (self.function_table.count() > 0) {
786 const header_offset = try reserveVecSectionHeader(file);
787 const writer = file.writer();
788
789 var flags: u32 = 0x2; // Yes we have a table
790 try leb.writeULEB128(writer, flags);
791 try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols
792 try emitInit(writer, .{ .i32_const = 0 });
793 try leb.writeULEB128(writer, @as(u8, 0));
794 try leb.writeULEB128(writer, @intCast(u32, self.function_table.count()));
795 var symbol_it = self.function_table.keyIterator();
796 while (symbol_it.next()) |symbol_index_ptr| {
797 try leb.writeULEB128(writer, self.symbols.items[symbol_index_ptr.*].index);
798 }
799
800 try writeVecSectionHeader(
801 file,
802 header_offset,
803 .element,
804 @intCast(u32, (try file.getPos()) - header_offset - header_size),
805 @as(u32, 1),
806 );
807 }
808
746809 // Code section
747810 if (self.code_section_index) |code_index| {
748811 const header_offset = try reserveVecSectionHeader(file);
......@@ -1233,16 +1296,3 @@ pub fn putOrGetFuncType(self: *Wasm, func_type: wasm.Type) !u32 {
12331296 });
12341297 return index;
12351298}
1236
1237/// From a given index and an `ExternalKind`, finds the corresponding Import.
1238/// This is due to indexes for imports being unique per type, rather than across all imports.
1239fn findImport(self: Wasm, index: u32, external_type: wasm.ExternalKind) ?*wasm.Import {
1240 var current_index: u32 = 0;
1241 for (self.imports.items) |*import| {
1242 if (import.kind == external_type) {
1243 if (current_index == index) return import;
1244 current_index += 1;
1245 }
1246 }
1247 return null;
1248}
src/link/Wasm/Atom.zig+3-5
......@@ -83,7 +83,7 @@ pub fn resolveRelocs(self: *Atom, wasm_bin: *const Wasm) !void {
8383
8484 for (self.relocs.items) |reloc| {
8585 const value = try relocationValue(reloc, wasm_bin);
86 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}\n", .{
86 log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
8787 wasm_bin.symbols.items[reloc.index].name,
8888 symbol.name,
8989 reloc.offset,
......@@ -129,7 +129,7 @@ fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
129129 .R_WASM_TABLE_INDEX_I64,
130130 .R_WASM_TABLE_INDEX_SLEB,
131131 .R_WASM_TABLE_INDEX_SLEB64,
132 => return error.TodoImplementTableIndex, // find table index from a function symbol
132 => return wasm_bin.function_table.get(relocation.index) orelse 0,
133133 .R_WASM_TYPE_INDEX_LEB => wasm_bin.functions.items[symbol.index].type_index,
134134 .R_WASM_GLOBAL_INDEX_I32,
135135 .R_WASM_GLOBAL_INDEX_LEB,
......@@ -152,9 +152,7 @@ fn relocationValue(relocation: types.Relocation, wasm_bin: *const Wasm) !u64 {
152152 target_atom = target_atom.next orelse break;
153153 }
154154 const segment = wasm_bin.segments.items[atom_index];
155 const base = wasm_bin.base.options.global_base orelse 1024;
156 const offset = target_atom.offset + segment.offset;
157 break :blk offset + base + (relocation.addend orelse 0);
155 break :blk target_atom.offset + segment.offset + (relocation.addend orelse 0);
158156 },
159157 .R_WASM_EVENT_INDEX_LEB => symbol.index,
160158 .R_WASM_SECTION_OFFSET_I32,
test/cases.zig+1-1
......@@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void {
2626 var case = ctx.exe("hello world with updates", linux_x64);
2727
2828 case.addError("", &[_][]const u8{
29 ":97:9: error: struct 'tmp.tmp' has no member named 'main'",
29 ":99:9: error: struct 'tmp.tmp' has no member named 'main'",
3030 });
3131
3232 // Incorrect return type
test/stage2/darwin.zig+1-1
......@@ -14,7 +14,7 @@ pub fn addCases(ctx: *TestContext) !void {
1414 {
1515 var case = ctx.exe("darwin hello world with updates", target);
1616 case.addError("", &[_][]const u8{
17 ":97:9: error: struct 'tmp.tmp' has no member named 'main'",
17 ":99:9: error: struct 'tmp.tmp' has no member named 'main'",
1818 });
1919
2020 // Incorrect return type
test/stage2/wasm.zig+206-181
......@@ -11,7 +11,7 @@ pub fn addCases(ctx: *TestContext) !void {
1111 var case = ctx.exe("wasm function calls", wasi);
1212
1313 case.addCompareOutput(
14 \\pub export fn _start() u32 {
14 \\pub fn main() u8 {
1515 \\ foo();
1616 \\ bar();
1717 \\ return 42;
......@@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void {
2626 );
2727
2828 case.addCompareOutput(
29 \\pub export fn _start() i64 {
29 \\pub fn main() u8 {
3030 \\ bar();
3131 \\ foo();
3232 \\ foo();
......@@ -44,10 +44,10 @@ pub fn addCases(ctx: *TestContext) !void {
4444 );
4545
4646 case.addCompareOutput(
47 \\pub export fn _start() f32 {
47 \\pub fn main() void {
4848 \\ bar();
4949 \\ foo();
50 \\ return 42.0;
50 \\ return;
5151 \\}
5252 \\fn foo() void {
5353 \\ bar();
......@@ -56,15 +56,15 @@ pub fn addCases(ctx: *TestContext) !void {
5656 \\}
5757 \\fn bar() void {}
5858 ,
59 "42\n",
59 "0\n",
6060 );
6161
6262 case.addCompareOutput(
63 \\pub export fn _start() u32 {
63 \\pub fn main() u8 {
6464 \\ foo(10, 20);
6565 \\ return 5;
6666 \\}
67 \\fn foo(x: u32, y: u32) void { _ = x; _ = y; }
67 \\fn foo(x: u8, y: u8) void { _ = x; _ = y; }
6868 , "5\n");
6969 }
7070
......@@ -72,10 +72,10 @@ pub fn addCases(ctx: *TestContext) !void {
7272 var case = ctx.exe("wasm locals", wasi);
7373
7474 case.addCompareOutput(
75 \\pub export fn _start() u32 {
76 \\ var i: u32 = 5;
75 \\pub fn main() u8 {
76 \\ var i: u8 = 5;
7777 \\ var y: f32 = 42.0;
78 \\ var x: u32 = 10;
78 \\ var x: u8 = 10;
7979 \\ if (false) {
8080 \\ y;
8181 \\ x;
......@@ -85,18 +85,18 @@ pub fn addCases(ctx: *TestContext) !void {
8585 , "5\n");
8686
8787 case.addCompareOutput(
88 \\pub export fn _start() u32 {
89 \\ var i: u32 = 5;
88 \\pub fn main() u8 {
89 \\ var i: u8 = 5;
9090 \\ var y: f32 = 42.0;
9191 \\ _ = y;
92 \\ var x: u32 = 10;
92 \\ var x: u8 = 10;
9393 \\ foo(i, x);
9494 \\ i = x;
9595 \\ return i;
9696 \\}
97 \\fn foo(x: u32, y: u32) void {
97 \\fn foo(x: u8, y: u8) void {
9898 \\ _ = y;
99 \\ var i: u32 = 10;
99 \\ var i: u8 = 10;
100100 \\ i = x;
101101 \\}
102102 , "10\n");
......@@ -106,228 +106,246 @@ pub fn addCases(ctx: *TestContext) !void {
106106 var case = ctx.exe("wasm binary operands", wasi);
107107
108108 case.addCompareOutput(
109 \\pub export fn _start() u32 {
110 \\ var i: u32 = 5;
109 \\pub fn main() u8 {
110 \\ var i: u8 = 5;
111111 \\ i += 20;
112112 \\ return i;
113113 \\}
114114 , "25\n");
115115
116116 case.addCompareOutput(
117 \\pub export fn _start() i32 {
117 \\pub fn main() void {
118118 \\ var i: i32 = 2147483647;
119 \\ return i +% 1;
119 \\ if (i +% 1 != -2147483648) unreachable;
120 \\ return;
120121 \\}
121 , "-2147483648\n");
122 , "0\n");
122123
123124 case.addCompareOutput(
124 \\pub export fn _start() i32 {
125 \\pub fn main() void {
125126 \\ var i: i4 = 7;
126 \\ return i +% 1;
127 \\ if (i +% 1 != 0) unreachable;
128 \\ return;
127129 \\}
128130 , "0\n");
129131
130132 case.addCompareOutput(
131 \\pub export fn _start() u32 {
133 \\pub fn main() u8 {
132134 \\ var i: u8 = 255;
133135 \\ return i +% 1;
134136 \\}
135137 , "0\n");
136138
137139 case.addCompareOutput(
138 \\pub export fn _start() u32 {
139 \\ var i: u32 = 5;
140 \\pub fn main() u8 {
141 \\ var i: u8 = 5;
140142 \\ i += 20;
141 \\ var result: u32 = foo(i, 10);
143 \\ var result: u8 = foo(i, 10);
142144 \\ return result;
143145 \\}
144 \\fn foo(x: u32, y: u32) u32 {
146 \\fn foo(x: u8, y: u8) u8 {
145147 \\ return x + y;
146148 \\}
147149 , "35\n");
148150
149151 case.addCompareOutput(
150 \\pub export fn _start() u32 {
151 \\ var i: u32 = 20;
152 \\pub fn main() u8 {
153 \\ var i: u8 = 20;
152154 \\ i -= 5;
153155 \\ return i;
154156 \\}
155157 , "15\n");
156158
157159 case.addCompareOutput(
158 \\pub export fn _start() i32 {
160 \\pub fn main() void {
159161 \\ var i: i32 = -2147483648;
160 \\ return i -% 1;
162 \\ if (i -% 1 != 2147483647) unreachable;
163 \\ return;
161164 \\}
162 , "2147483647\n");
165 , "0\n");
163166
164167 case.addCompareOutput(
165 \\pub export fn _start() i32 {
168 \\pub fn main() void {
166169 \\ var i: i7 = -64;
167 \\ return i -% 1;
170 \\ if (i -% 1 != 63) unreachable;
171 \\ return;
168172 \\}
169 , "63\n");
173 , "0\n");
170174
171175 case.addCompareOutput(
172 \\pub export fn _start() u32 {
176 \\pub fn main() u8 {
173177 \\ var i: u4 = 0;
174178 \\ return i -% 1;
175179 \\}
176180 , "15\n");
177181
178182 case.addCompareOutput(
179 \\pub export fn _start() u32 {
180 \\ var i: u32 = 5;
183 \\pub fn main() u8 {
184 \\ var i: u8 = 5;
181185 \\ i -= 3;
182 \\ var result: u32 = foo(i, 10);
186 \\ var result: u8 = foo(i, 10);
183187 \\ return result;
184188 \\}
185 \\fn foo(x: u32, y: u32) u32 {
189 \\fn foo(x: u8, y: u8) u8 {
186190 \\ return y - x;
187191 \\}
188192 , "8\n");
189193
190194 case.addCompareOutput(
191 \\pub export fn _start() u32 {
195 \\pub fn main() void {
192196 \\ var i: u32 = 5;
193197 \\ i *= 7;
194198 \\ var result: u32 = foo(i, 10);
195 \\ return result;
199 \\ if (result != 350) unreachable;
200 \\ return;
196201 \\}
197202 \\fn foo(x: u32, y: u32) u32 {
198203 \\ return x * y;
199204 \\}
200 , "350\n");
205 , "0\n");
201206
202207 case.addCompareOutput(
203 \\pub export fn _start() i32 {
208 \\pub fn main() void {
204209 \\ var i: i32 = 2147483647;
205 \\ return i *% 2;
210 \\ const result = i *% 2;
211 \\ if (result != -2) unreachable;
212 \\ return;
206213 \\}
207 , "-2\n");
214 , "0\n");
208215
209216 case.addCompareOutput(
210 \\pub export fn _start() u32 {
217 \\pub fn main() void {
211218 \\ var i: u3 = 3;
212 \\ return i *% 3;
219 \\ if (i *% 3 != 1) unreachable;
220 \\ return;
213221 \\}
214 , "1\n");
222 , "0\n");
215223
216224 case.addCompareOutput(
217 \\pub export fn _start() i32 {
225 \\pub fn main() void {
218226 \\ var i: i4 = 3;
219 \\ return i *% 3;
227 \\ if (i *% 3 != 1) unreachable;
228 \\ return;
220229 \\}
221 , "1\n");
230 , "0\n");
222231
223232 case.addCompareOutput(
224 \\pub export fn _start() u32 {
233 \\pub fn main() void {
225234 \\ var i: u32 = 352;
226235 \\ i /= 7; // i = 50
227236 \\ var result: u32 = foo(i, 7);
228 \\ return result;
237 \\ if (result != 7) unreachable;
238 \\ return;
229239 \\}
230240 \\fn foo(x: u32, y: u32) u32 {
231241 \\ return x / y;
232242 \\}
233 , "7\n");
243 , "0\n");
234244
235245 case.addCompareOutput(
236 \\pub export fn _start() u32 {
237 \\ var i: u32 = 5;
246 \\pub fn main() u8 {
247 \\ var i: u8 = 5;
238248 \\ i &= 6;
239249 \\ return i;
240250 \\}
241251 , "4\n");
242252
243253 case.addCompareOutput(
244 \\pub export fn _start() u32 {
245 \\ var i: u32 = 5;
254 \\pub fn main() u8 {
255 \\ var i: u8 = 5;
246256 \\ i |= 6;
247257 \\ return i;
248258 \\}
249259 , "7\n");
250260
251261 case.addCompareOutput(
252 \\pub export fn _start() u32 {
253 \\ var i: u32 = 5;
262 \\pub fn main() u8 {
263 \\ var i: u8 = 5;
254264 \\ i ^= 6;
255265 \\ return i;
256266 \\}
257267 , "3\n");
258268
259269 case.addCompareOutput(
260 \\pub export fn _start() bool {
270 \\pub fn main() void {
261271 \\ var b: bool = false;
262272 \\ b = b or false;
263 \\ return b;
273 \\ if (b) unreachable;
274 \\ return;
264275 \\}
265276 , "0\n");
266277
267278 case.addCompareOutput(
268 \\pub export fn _start() bool {
279 \\pub fn main() void {
269280 \\ var b: bool = true;
270281 \\ b = b or false;
271 \\ return b;
282 \\ if (!b) unreachable;
283 \\ return;
272284 \\}
273 , "1\n");
285 , "0\n");
274286
275287 case.addCompareOutput(
276 \\pub export fn _start() bool {
288 \\pub fn main() void {
277289 \\ var b: bool = false;
278290 \\ b = b or true;
279 \\ return b;
291 \\ if (!b) unreachable;
292 \\ return;
280293 \\}
281 , "1\n");
294 , "0\n");
282295
283296 case.addCompareOutput(
284 \\pub export fn _start() bool {
297 \\pub fn main() void {
285298 \\ var b: bool = true;
286299 \\ b = b or true;
287 \\ return b;
300 \\ if (!b) unreachable;
301 \\ return;
288302 \\}
289 , "1\n");
303 , "0\n");
290304
291305 case.addCompareOutput(
292 \\pub export fn _start() bool {
306 \\pub fn main() void {
293307 \\ var b: bool = false;
294308 \\ b = b and false;
295 \\ return b;
309 \\ if (b) unreachable;
310 \\ return;
296311 \\}
297312 , "0\n");
298313
299314 case.addCompareOutput(
300 \\pub export fn _start() bool {
315 \\pub fn main() void {
301316 \\ var b: bool = true;
302317 \\ b = b and false;
303 \\ return b;
318 \\ if (b) unreachable;
319 \\ return;
304320 \\}
305321 , "0\n");
306322
307323 case.addCompareOutput(
308 \\pub export fn _start() bool {
324 \\pub fn main() void {
309325 \\ var b: bool = false;
310326 \\ b = b and true;
311 \\ return b;
327 \\ if (b) unreachable;
328 \\ return;
312329 \\}
313330 , "0\n");
314331
315332 case.addCompareOutput(
316 \\pub export fn _start() bool {
333 \\pub fn main() void {
317334 \\ var b: bool = true;
318335 \\ b = b and true;
319 \\ return b;
336 \\ if (!b) unreachable;
337 \\ return;
320338 \\}
321 , "1\n");
339 , "0\n");
322340 }
323341
324342 {
325343 var case = ctx.exe("wasm conditions", wasi);
326344
327345 case.addCompareOutput(
328 \\pub export fn _start() u32 {
329 \\ var i: u32 = 5;
330 \\ if (i > @as(u32, 4)) {
346 \\pub fn main() u8 {
347 \\ var i: u8 = 5;
348 \\ if (i > @as(u8, 4)) {
331349 \\ i += 10;
332350 \\ }
333351 \\ return i;
......@@ -335,9 +353,9 @@ pub fn addCases(ctx: *TestContext) !void {
335353 , "15\n");
336354
337355 case.addCompareOutput(
338 \\pub export fn _start() u32 {
339 \\ var i: u32 = 5;
340 \\ if (i < @as(u32, 4)) {
356 \\pub fn main() u8 {
357 \\ var i: u8 = 5;
358 \\ if (i < @as(u8, 4)) {
341359 \\ i += 10;
342360 \\ } else {
343361 \\ i = 2;
......@@ -347,11 +365,11 @@ pub fn addCases(ctx: *TestContext) !void {
347365 , "2\n");
348366
349367 case.addCompareOutput(
350 \\pub export fn _start() u32 {
351 \\ var i: u32 = 5;
352 \\ if (i < @as(u32, 4)) {
368 \\pub fn main() u8 {
369 \\ var i: u8 = 5;
370 \\ if (i < @as(u8, 4)) {
353371 \\ i += 10;
354 \\ } else if(i == @as(u32, 5)) {
372 \\ } else if(i == @as(u8, 5)) {
355373 \\ i = 20;
356374 \\ }
357375 \\ return i;
......@@ -359,12 +377,12 @@ pub fn addCases(ctx: *TestContext) !void {
359377 , "20\n");
360378
361379 case.addCompareOutput(
362 \\pub export fn _start() u32 {
363 \\ var i: u32 = 11;
364 \\ if (i < @as(u32, 4)) {
380 \\pub fn main() u8 {
381 \\ var i: u8 = 11;
382 \\ if (i < @as(u8, 4)) {
365383 \\ i += 10;
366384 \\ } else {
367 \\ if (i > @as(u32, 10)) {
385 \\ if (i > @as(u8, 10)) {
368386 \\ i += 20;
369387 \\ } else {
370388 \\ i = 20;
......@@ -375,7 +393,7 @@ pub fn addCases(ctx: *TestContext) !void {
375393 , "31\n");
376394
377395 case.addCompareOutput(
378 \\pub export fn _start() void {
396 \\pub fn main() void {
379397 \\ assert(foo(true) != @as(i32, 30));
380398 \\}
381399 \\
......@@ -387,10 +405,10 @@ pub fn addCases(ctx: *TestContext) !void {
387405 \\ const x = if(ok) @as(i32, 20) else @as(i32, 10);
388406 \\ return x;
389407 \\}
390 , "");
408 , "0\n");
391409
392410 case.addCompareOutput(
393 \\pub export fn _start() void {
411 \\pub fn main() void {
394412 \\ assert(foo(false) == @as(i32, 20));
395413 \\ assert(foo(true) == @as(i32, 30));
396414 \\}
......@@ -407,16 +425,16 @@ pub fn addCases(ctx: *TestContext) !void {
407425 \\ };
408426 \\ return val + 10;
409427 \\}
410 , "");
428 , "0\n");
411429 }
412430
413431 {
414432 var case = ctx.exe("wasm while loops", wasi);
415433
416434 case.addCompareOutput(
417 \\pub export fn _start() u32 {
418 \\ var i: u32 = 0;
419 \\ while(i < @as(u32, 5)){
435 \\pub fn main() u8 {
436 \\ var i: u8 = 0;
437 \\ while(i < @as(u8, 5)){
420438 \\ i += 1;
421439 \\ }
422440 \\
......@@ -425,10 +443,10 @@ pub fn addCases(ctx: *TestContext) !void {
425443 , "5\n");
426444
427445 case.addCompareOutput(
428 \\pub export fn _start() u32 {
429 \\ var i: u32 = 0;
430 \\ while(i < @as(u32, 10)){
431 \\ var x: u32 = 1;
446 \\pub fn main() u8 {
447 \\ var i: u8 = 0;
448 \\ while(i < @as(u8, 10)){
449 \\ var x: u8 = 1;
432450 \\ i += x;
433451 \\ }
434452 \\ return i;
......@@ -436,12 +454,12 @@ pub fn addCases(ctx: *TestContext) !void {
436454 , "10\n");
437455
438456 case.addCompareOutput(
439 \\pub export fn _start() u32 {
440 \\ var i: u32 = 0;
441 \\ while(i < @as(u32, 10)){
442 \\ var x: u32 = 1;
457 \\pub fn main() u8 {
458 \\ var i: u8 = 0;
459 \\ while(i < @as(u8, 10)){
460 \\ var x: u8 = 1;
443461 \\ i += x;
444 \\ if (i == @as(u32, 5)) break;
462 \\ if (i == @as(u8, 5)) break;
445463 \\ }
446464 \\ return i;
447465 \\}
......@@ -454,7 +472,7 @@ pub fn addCases(ctx: *TestContext) !void {
454472 case.addCompareOutput(
455473 \\const Number = enum { One, Two, Three };
456474 \\
457 \\pub export fn _start() i32 {
475 \\pub fn main() void {
458476 \\ var number1 = Number.One;
459477 \\ var number2: Number = .Two;
460478 \\ if (false) {
......@@ -462,47 +480,52 @@ pub fn addCases(ctx: *TestContext) !void {
462480 \\ number2;
463481 \\ }
464482 \\ const number3 = @intToEnum(Number, 2);
465 \\
466 \\ return @enumToInt(number3);
483 \\ if (@enumToInt(number3) != 2) {
484 \\ unreachable;
485 \\ }
486 \\ return;
467487 \\}
468 , "2\n");
488 , "0\n");
469489
470490 case.addCompareOutput(
471491 \\const Number = enum { One, Two, Three };
472492 \\
473 \\pub export fn _start() i32 {
493 \\pub fn main() void {
474494 \\ var number1 = Number.One;
475495 \\ var number2: Number = .Two;
476496 \\ const number3 = @intToEnum(Number, 2);
477 \\ if (number1 == number2) return 1;
478 \\ if (number2 == number3) return 1;
479 \\ if (@enumToInt(number1) != 0) return 1;
480 \\ if (@enumToInt(number2) != 1) return 1;
481 \\ if (@enumToInt(number3) != 2) return 1;
497 \\ assert(number1 != number2);
498 \\ assert(number2 != number3);
499 \\ assert(@enumToInt(number1) == 0);
500 \\ assert(@enumToInt(number2) == 1);
501 \\ assert(@enumToInt(number3) == 2);
482502 \\ var x: Number = .Two;
483 \\ if (number2 != x) return 1;
503 \\ assert(number2 == x);
484504 \\
485 \\ return @enumToInt(number3);
505 \\ return;
486506 \\}
487 , "2\n");
507 \\fn assert(val: bool) void {
508 \\ if(!val) unreachable;
509 \\}
510 , "0\n");
488511 }
489512
490513 {
491514 var case = ctx.exe("wasm structs", wasi);
492515
493516 case.addCompareOutput(
494 \\const Example = struct { x: u32 };
517 \\const Example = struct { x: u8 };
495518 \\
496 \\pub export fn _start() u32 {
519 \\pub fn main() u8 {
497520 \\ var example: Example = .{ .x = 5 };
498521 \\ return example.x;
499522 \\}
500523 , "5\n");
501524
502525 case.addCompareOutput(
503 \\const Example = struct { x: u32 };
526 \\const Example = struct { x: u8 };
504527 \\
505 \\pub export fn _start() u32 {
528 \\pub fn main() u8 {
506529 \\ var example: Example = .{ .x = 5 };
507530 \\ example.x = 10;
508531 \\ return example.x;
......@@ -510,18 +533,18 @@ pub fn addCases(ctx: *TestContext) !void {
510533 , "10\n");
511534
512535 case.addCompareOutput(
513 \\const Example = struct { x: u32, y: u32 };
536 \\const Example = struct { x: u8, y: u8 };
514537 \\
515 \\pub export fn _start() u32 {
538 \\pub fn main() u8 {
516539 \\ var example: Example = .{ .x = 5, .y = 10 };
517540 \\ return example.y + example.x;
518541 \\}
519542 , "15\n");
520543
521544 case.addCompareOutput(
522 \\const Example = struct { x: u32, y: u32 };
545 \\const Example = struct { x: u8, y: u8 };
523546 \\
524 \\pub export fn _start() u32 {
547 \\pub fn main() u8 {
525548 \\ var example: Example = .{ .x = 5, .y = 10 };
526549 \\ var example2: Example = .{ .x = 10, .y = 20 };
527550 \\
......@@ -531,9 +554,9 @@ pub fn addCases(ctx: *TestContext) !void {
531554 , "30\n");
532555
533556 case.addCompareOutput(
534 \\const Example = struct { x: u32, y: u32 };
557 \\const Example = struct { x: u8, y: u8 };
535558 \\
536 \\pub export fn _start() u32 {
559 \\pub fn main() u8 {
537560 \\ var example: Example = .{ .x = 5, .y = 10 };
538561 \\
539562 \\ example = .{ .x = 10, .y = 20 };
......@@ -546,9 +569,9 @@ pub fn addCases(ctx: *TestContext) !void {
546569 var case = ctx.exe("wasm switch", wasi);
547570
548571 case.addCompareOutput(
549 \\pub export fn _start() u32 {
550 \\ var val: u32 = 1;
551 \\ var a: u32 = switch (val) {
572 \\pub fn main() u8 {
573 \\ var val: u8 = 1;
574 \\ var a: u8 = switch (val) {
552575 \\ 0, 1 => 2,
553576 \\ 2 => 3,
554577 \\ 3 => 4,
......@@ -560,9 +583,9 @@ pub fn addCases(ctx: *TestContext) !void {
560583 , "2\n");
561584
562585 case.addCompareOutput(
563 \\pub export fn _start() u32 {
564 \\ var val: u32 = 2;
565 \\ var a: u32 = switch (val) {
586 \\pub fn main() u8 {
587 \\ var val: u8 = 2;
588 \\ var a: u8 = switch (val) {
566589 \\ 0, 1 => 2,
567590 \\ 2 => 3,
568591 \\ 3 => 4,
......@@ -574,9 +597,9 @@ pub fn addCases(ctx: *TestContext) !void {
574597 , "3\n");
575598
576599 case.addCompareOutput(
577 \\pub export fn _start() u32 {
578 \\ var val: u32 = 10;
579 \\ var a: u32 = switch (val) {
600 \\pub fn main() u8 {
601 \\ var val: u8 = 10;
602 \\ var a: u8 = switch (val) {
580603 \\ 0, 1 => 2,
581604 \\ 2 => 3,
582605 \\ 3 => 4,
......@@ -590,9 +613,9 @@ pub fn addCases(ctx: *TestContext) !void {
590613 case.addCompareOutput(
591614 \\const MyEnum = enum { One, Two, Three };
592615 \\
593 \\pub export fn _start() u32 {
616 \\pub fn main() u8 {
594617 \\ var val: MyEnum = .Two;
595 \\ var a: u32 = switch (val) {
618 \\ var a: u8 = switch (val) {
596619 \\ .One => 1,
597620 \\ .Two => 2,
598621 \\ .Three => 3,
......@@ -607,7 +630,7 @@ pub fn addCases(ctx: *TestContext) !void {
607630 var case = ctx.exe("wasm error unions", wasi);
608631
609632 case.addCompareOutput(
610 \\pub export fn _start() void {
633 \\pub fn main() void {
611634 \\ var e1 = error.Foo;
612635 \\ var e2 = error.Bar;
613636 \\ assert(e1 != e2);
......@@ -618,32 +641,32 @@ pub fn addCases(ctx: *TestContext) !void {
618641 \\fn assert(b: bool) void {
619642 \\ if (!b) unreachable;
620643 \\}
621 , "");
644 , "0\n");
622645
623646 case.addCompareOutput(
624 \\pub export fn _start() u32 {
625 \\ var e: anyerror!u32 = 5;
647 \\pub fn main() u8 {
648 \\ var e: anyerror!u8 = 5;
626649 \\ const i = e catch 10;
627650 \\ return i;
628651 \\}
629652 , "5\n");
630653
631654 case.addCompareOutput(
632 \\pub export fn _start() u32 {
633 \\ var e: anyerror!u32 = error.Foo;
655 \\pub fn main() u8 {
656 \\ var e: anyerror!u8 = error.Foo;
634657 \\ const i = e catch 10;
635658 \\ return i;
636659 \\}
637660 , "10\n");
638661
639662 case.addCompareOutput(
640 \\pub export fn _start() u32 {
663 \\pub fn main() u8 {
641664 \\ var e = foo();
642665 \\ const i = e catch 69;
643666 \\ return i;
644667 \\}
645668 \\
646 \\fn foo() anyerror!u32 {
669 \\fn foo() anyerror!u8 {
647670 \\ return 5;
648671 \\}
649672 , "5\n");
......@@ -653,24 +676,24 @@ pub fn addCases(ctx: *TestContext) !void {
653676 var case = ctx.exe("wasm error union part 2", wasi);
654677
655678 case.addCompareOutput(
656 \\pub export fn _start() u32 {
679 \\pub fn main() u8 {
657680 \\ var e = foo();
658681 \\ const i = e catch 69;
659682 \\ return i;
660683 \\}
661684 \\
662 \\fn foo() anyerror!u32 {
685 \\fn foo() anyerror!u8 {
663686 \\ return error.Bruh;
664687 \\}
665688 , "69\n");
666689 case.addCompareOutput(
667 \\pub export fn _start() u32 {
690 \\pub fn main() u8 {
668691 \\ var e = foo();
669692 \\ const i = e catch 42;
670693 \\ return i;
671694 \\}
672695 \\
673 \\fn foo() anyerror!u32 {
696 \\fn foo() anyerror!u8 {
674697 \\ return error.Dab;
675698 \\}
676699 , "42\n");
......@@ -680,20 +703,22 @@ pub fn addCases(ctx: *TestContext) !void {
680703 var case = ctx.exe("wasm integer widening", wasi);
681704
682705 case.addCompareOutput(
683 \\pub export fn _start() u64 {
684 \\ var x: u32 = 5;
685 \\ return x;
706 \\pub fn main() void{
707 \\ var x: u8 = 5;
708 \\ var y: u64 = x;
709 \\ _ = y;
710 \\ return;
686711 \\}
687 , "5\n");
712 , "0\n");
688713 }
689714
690715 {
691716 var case = ctx.exe("wasm optionals", wasi);
692717
693718 case.addCompareOutput(
694 \\pub export fn _start() u32 {
695 \\ var x: ?u32 = 5;
696 \\ var y: u32 = 0;
719 \\pub fn main() u8 {
720 \\ var x: ?u8 = 5;
721 \\ var y: u8 = 0;
697722 \\ if (x) |val| {
698723 \\ y = val;
699724 \\ }
......@@ -702,9 +727,9 @@ pub fn addCases(ctx: *TestContext) !void {
702727 , "5\n");
703728
704729 case.addCompareOutput(
705 \\pub export fn _start() u32 {
706 \\ var x: ?u32 = null;
707 \\ var y: u32 = 0;
730 \\pub fn main() u8 {
731 \\ var x: ?u8 = null;
732 \\ var y: u8 = 0;
708733 \\ if (x) |val| {
709734 \\ y = val;
710735 \\ }
......@@ -713,23 +738,23 @@ pub fn addCases(ctx: *TestContext) !void {
713738 , "0\n");
714739
715740 case.addCompareOutput(
716 \\pub export fn _start() u32 {
717 \\ var x: ?u32 = 5;
741 \\pub fn main() u8 {
742 \\ var x: ?u8 = 5;
718743 \\ return x.?;
719744 \\}
720745 , "5\n");
721746
722747 case.addCompareOutput(
723 \\pub export fn _start() u32 {
724 \\ var x: u32 = 5;
725 \\ var y: ?u32 = x;
748 \\pub fn main() u8 {
749 \\ var x: u8 = 5;
750 \\ var y: ?u8 = x;
726751 \\ return y.?;
727752 \\}
728753 , "5\n");
729754
730755 case.addCompareOutput(
731 \\pub export fn _start() u32 {
732 \\ var val: ?u32 = 5;
756 \\pub fn main() u8 {
757 \\ var val: ?u8 = 5;
733758 \\ while (val) |*v| {
734759 \\ v.* -= 1;
735760 \\ if (v.* == 2) {
......@@ -745,32 +770,32 @@ pub fn addCases(ctx: *TestContext) !void {
745770 var case = ctx.exe("wasm pointers", wasi);
746771
747772 case.addCompareOutput(
748 \\pub export fn _start() u32 {
749 \\ var x: u32 = 0;
773 \\pub fn main() u8 {
774 \\ var x: u8 = 0;
750775 \\
751776 \\ foo(&x);
752777 \\ return x;
753778 \\}
754779 \\
755 \\fn foo(x: *u32)void {
780 \\fn foo(x: *u8)void {
756781 \\ x.* = 2;
757782 \\}
758783 , "2\n");
759784
760785 case.addCompareOutput(
761 \\pub export fn _start() u32 {
762 \\ var x: u32 = 0;
786 \\pub fn main() u8 {
787 \\ var x: u8 = 0;
763788 \\
764789 \\ foo(&x);
765790 \\ bar(&x);
766791 \\ return x;
767792 \\}
768793 \\
769 \\fn foo(x: *u32)void {
794 \\fn foo(x: *u8)void {
770795 \\ x.* = 2;
771796 \\}
772797 \\
773 \\fn bar(x: *u32) void {
798 \\fn bar(x: *u8) void {
774799 \\ x.* += 2;
775800 \\}
776801 , "4\n");