authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-01 15:41:26-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-01 15:41:26-05:00
log98e84152bdf883303cb7be8bbc80e01688e698ab
tree0be18dd4d31de56144aab8214abe1159321b9a99
parenta41ad639a85218130f80956ce0c2e59ff322a1af
parent3de111d993712f01cf5dd3bf6e2704550c9745e4
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10481 from Luukdegram/wasm-behavior-tests

Stage2: wasm - Implement more behavior tests

4 files changed, 562 insertions(+), 157 deletions(-)

src/arch/wasm/CodeGen.zig+531-129
...@@ -644,6 +644,12 @@ fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void {...@@ -644,6 +644,12 @@ fn addFloat64(self: *Self, float: f64) error{OutOfMemory}!void {
644 try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });644 try self.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
645}645}
646646
647/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.
648fn addMemArg(self: *Self, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
649 const extra_index = try self.addExtra(mem_arg);
650 try self.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
651}
652
647/// Appends entries to `mir_extra` based on the type of `extra`.653/// Appends entries to `mir_extra` based on the type of `extra`.
648/// Returns the index into `mir_extra`654/// Returns the index into `mir_extra`
649fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 {655fn addExtra(self: *Self, extra: anytype) error{OutOfMemory}!u32 {
...@@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {...@@ -692,8 +698,9 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype {
692 .ErrorUnion,698 .ErrorUnion,
693 .Optional,699 .Optional,
694 .Fn,700 .Fn,
701 .Array,
695 => wasm.Valtype.i32,702 => wasm.Valtype.i32,
696 else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}),703 else => self.fail("TODO - Wasm typeToValtype for type '{}'", .{ty}),
697 };704 };
698}705}
699706
...@@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {...@@ -756,7 +763,6 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type {
756 switch (return_type.zigTypeTag()) {763 switch (return_type.zigTypeTag()) {
757 .Void, .NoReturn => {},764 .Void, .NoReturn => {},
758 .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),765 .Struct => return self.fail("TODO: Implement struct as return type for wasm", .{}),
759 .Optional => return self.fail("TODO: Implement optionals as return type for wasm", .{}),
760 else => try returns.append(try self.typeToValtype(return_type)),766 else => try returns.append(try self.typeToValtype(return_type)),
761 }767 }
762768
...@@ -967,6 +973,41 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {...@@ -967,6 +973,41 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result {
967 },973 },
968 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),974 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
969 },975 },
976 .ErrorUnion => {
977 const error_ty = ty.errorUnionSet();
978 const payload_ty = ty.errorUnionPayload();
979 const is_pl = val.errorUnionIsPayload();
980
981 const err_val = if (!is_pl) val else Value.initTag(.zero);
982 switch (try self.genTypedValue(error_ty, err_val)) {
983 .externally_managed => |data| try self.code.appendSlice(data),
984 .appended => {},
985 }
986
987 if (payload_ty.hasCodeGenBits()) {
988 const pl_val = if (val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef);
989 switch (try self.genTypedValue(payload_ty, pl_val)) {
990 .externally_managed => |data| try self.code.appendSlice(data),
991 .appended => {},
992 }
993 }
994
995 return Result.appended;
996 },
997 .ErrorSet => {
998 switch (val.tag()) {
999 .@"error" => {
1000 const name = val.castTag(.@"error").?.data.name;
1001 const value = self.global_error_set.get(name).?;
1002 try self.code.writer().writeIntLittle(u32, value);
1003 },
1004 else => {
1005 const abi_size = @intCast(usize, ty.abiSize(self.target));
1006 try self.code.appendNTimes(0, abi_size);
1007 },
1008 }
1009 return Result.appended;
1010 },
970 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),1011 else => |tag| return self.fail("TODO: Implement zig type codegen for type: '{s}'", .{tag}),
971 }1012 }
972}1013}
...@@ -1047,10 +1088,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu...@@ -1047,10 +1088,8 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
1047 }1088 }
10481089
1049 const ret_ty = fn_ty.fnReturnType();1090 const ret_ty = fn_ty.fnReturnType();
1050 switch (ret_ty.zigTypeTag()) {1091 if (isByRef(ret_ty)) {
1051 .ErrorUnion, .Optional, .Pointer => result.return_value = try self.allocLocal(Type.initTag(.i32)),1092 result.return_value = try self.allocLocal(Type.initTag(.i32));
1052 .Int, .Float, .Bool, .Void, .NoReturn => {},
1053 else => return self.fail("TODO: Implement function return type {}", .{ret_ty}),
1054 }1093 }
10551094
1056 // Check if we store the result as a pointer to the stack rather than1095 // Check if we store the result as a pointer to the stack rather than
...@@ -1116,6 +1155,26 @@ fn moveStack(self: *Self, offset: u32, local: u32) !void {...@@ -1116,6 +1155,26 @@ fn moveStack(self: *Self, offset: u32, local: u32) !void {
1116 try self.addLabel(.global_set, 0);1155 try self.addLabel(.global_set, 0);
1117}1156}
11181157
1158/// From a given type, will create space on the virtual stack to store the value of such type.
1159/// This returns a `WValue` with its active tag set to `local`, containing the index to the local
1160/// that points to the position on the virtual stack. This function should be used instead of
1161/// moveStack unless a local was already created to store the point.
1162///
1163/// Asserts Type has codegenbits
1164fn allocStack(self: *Self, ty: Type) !WValue {
1165 assert(ty.hasCodeGenBits());
1166
1167 // calculate needed stack space
1168 const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch {
1169 return self.fail("Given type '{}' too big to fit into stack frame", .{ty});
1170 };
1171
1172 // allocate a local using wasm's pointer size
1173 const local = try self.allocLocal(Type.@"usize");
1174 try self.moveStack(abi_size, local.local);
1175 return local;
1176}
1177
1119/// From given zig bitsize, returns the wasm bitsize1178/// From given zig bitsize, returns the wasm bitsize
1120fn toWasmIntBits(bits: u16) ?u16 {1179fn toWasmIntBits(bits: u16) ?u16 {
1121 return for ([_]u16{ 32, 64 }) |wasm_bits| {1180 return for ([_]u16{ 32, 64 }) |wasm_bits| {
...@@ -1123,6 +1182,80 @@ fn toWasmIntBits(bits: u16) ?u16 {...@@ -1123,6 +1182,80 @@ fn toWasmIntBits(bits: u16) ?u16 {
1123 } else null;1182 } else null;
1124}1183}
11251184
1185/// Performs a copy of bytes for a given type. Copying all bytes
1186/// from rhs to lhs.
1187/// Asserts `lhs` and `rhs` have their active tag set to `local`
1188///
1189/// TODO: Perform feature detection and when bulk_memory is available,
1190/// use wasm's mem.copy instruction.
1191fn memCopy(self: *Self, ty: Type, lhs: WValue, rhs: WValue) !void {
1192 const abi_size = ty.abiSize(self.target);
1193 var offset: u32 = 0;
1194 while (offset < abi_size) : (offset += 1) {
1195 // get lhs' address to store the result
1196 try self.addLabel(.local_get, lhs.local);
1197 // load byte from rhs' adress
1198 try self.addLabel(.local_get, rhs.local);
1199 try self.addMemArg(.i32_load8_u, .{ .offset = offset, .alignment = 1 });
1200 // store the result in lhs (we already have its address on the stack)
1201 try self.addMemArg(.i32_store8, .{ .offset = offset, .alignment = 1 });
1202 }
1203}
1204
1205fn ptrSize(self: *const Self) u16 {
1206 return @divExact(self.target.cpu.arch.ptrBitWidth(), 8);
1207}
1208
1209/// For a given `Type`, will return true when the type will be passed
1210/// by reference, rather than by value.
1211fn isByRef(ty: Type) bool {
1212 switch (ty.zigTypeTag()) {
1213 .Type,
1214 .ComptimeInt,
1215 .ComptimeFloat,
1216 .EnumLiteral,
1217 .Undefined,
1218 .Null,
1219 .BoundFn,
1220 .Opaque,
1221 => unreachable,
1222
1223 .NoReturn,
1224 .Void,
1225 .Bool,
1226 .Int,
1227 .Float,
1228 .ErrorSet,
1229 .Fn,
1230 .Enum,
1231 .Vector,
1232 .AnyFrame,
1233 => return false,
1234
1235 .Array,
1236 .Struct,
1237 .Frame,
1238 .Union,
1239 => return ty.hasCodeGenBits(),
1240 .ErrorUnion => {
1241 const has_tag = ty.errorUnionSet().hasCodeGenBits();
1242 const has_pl = ty.errorUnionPayload().hasCodeGenBits();
1243 if (!has_tag or !has_pl) return false;
1244 return ty.hasCodeGenBits();
1245 },
1246 .Optional => {
1247 if (ty.isPtrLikeOptional()) return false;
1248 var buf: Type.Payload.ElemType = undefined;
1249 return ty.optionalChild(&buf).hasCodeGenBits();
1250 },
1251 .Pointer => {
1252 // Slices act like struct and will be passed by reference
1253 if (ty.isSlice()) return ty.hasCodeGenBits();
1254 return false;
1255 },
1256 }
1257}
1258
1126fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {1259fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1127 const air_tags = self.air.instructions.items(.tag);1260 const air_tags = self.air.instructions.items(.tag);
1128 return switch (air_tags[inst]) {1261 return switch (air_tags[inst]) {
...@@ -1146,12 +1279,14 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1146,12 +1279,14 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1146 .cmp_lt => self.airCmp(inst, .lt),1279 .cmp_lt => self.airCmp(inst, .lt),
1147 .cmp_neq => self.airCmp(inst, .neq),1280 .cmp_neq => self.airCmp(inst, .neq),
11481281
1282 .array_to_slice => self.airArrayToSlice(inst),
1149 .alloc => self.airAlloc(inst),1283 .alloc => self.airAlloc(inst),
1150 .arg => self.airArg(inst),1284 .arg => self.airArg(inst),
1151 .bitcast => self.airBitcast(inst),1285 .bitcast => self.airBitcast(inst),
1152 .block => self.airBlock(inst),1286 .block => self.airBlock(inst),
1153 .breakpoint => self.airBreakpoint(inst),1287 .breakpoint => self.airBreakpoint(inst),
1154 .br => self.airBr(inst),1288 .br => self.airBr(inst),
1289 .bool_to_int => self.airBoolToInt(inst),
1155 .call => self.airCall(inst),1290 .call => self.airCall(inst),
1156 .cond_br => self.airCondBr(inst),1291 .cond_br => self.airCondBr(inst),
1157 .constant => unreachable,1292 .constant => unreachable,
...@@ -1172,11 +1307,17 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1172,11 +1307,17 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1172 .optional_payload => self.airOptionalPayload(inst),1307 .optional_payload => self.airOptionalPayload(inst),
1173 .optional_payload_ptr => self.airOptionalPayload(inst),1308 .optional_payload_ptr => self.airOptionalPayload(inst),
1174 .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst),1309 .optional_payload_ptr_set => self.airOptionalPayloadPtrSet(inst),
1310 .ptr_add => self.airPtrBinOp(inst, .add),
1311 .ptr_sub => self.airPtrBinOp(inst, .sub),
1312 .ptr_elem_ptr => self.airPtrElemPtr(inst),
1313 .ptr_elem_val => self.airPtrElemVal(inst),
1314 .ptrtoint => self.airPtrToInt(inst),
1175 .ret => self.airRet(inst),1315 .ret => self.airRet(inst),
1176 .ret_ptr => self.airRetPtr(inst),1316 .ret_ptr => self.airRetPtr(inst),
1177 .ret_load => self.airRetLoad(inst),1317 .ret_load => self.airRetLoad(inst),
1178 .slice_len => self.airSliceLen(inst),1318 .slice_len => self.airSliceLen(inst),
1179 .slice_elem_val => self.airSliceElemVal(inst),1319 .slice_elem_val => self.airSliceElemVal(inst),
1320 .slice_elem_ptr => self.airSliceElemPtr(inst),
1180 .slice_ptr => self.airSlicePtr(inst),1321 .slice_ptr => self.airSlicePtr(inst),
1181 .store => self.airStore(inst),1322 .store => self.airStore(inst),
1182 .struct_field_ptr => self.airStructFieldPtr(inst),1323 .struct_field_ptr => self.airStructFieldPtr(inst),
...@@ -1229,21 +1370,24 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1229,21 +1370,24 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1229 try self.initializeStack();1370 try self.initializeStack();
1230 }1371 }
12311372
1232 const abi_size = child_type.abiSize(self.target);1373 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
1233 if (abi_size == 0) return WValue{ .none = {} };
12341374
1235 // local, containing the offset to the stack position1375 return self.allocStack(child_type);
1236 const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32
1237 try self.moveStack(@intCast(u32, abi_size), local.local);
1238 return local;
1239}1376}
12401377
1241fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1378fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1242 const un_op = self.air.instructions.items(.data)[inst].un_op;1379 const un_op = self.air.instructions.items(.data)[inst].un_op;
1243 const operand = self.resolveInst(un_op);1380 const operand = self.resolveInst(un_op);
1381 const ret_ty = self.air.typeOf(un_op).childType();
1382 if (!ret_ty.hasCodeGenBits()) return WValue.none;
1383
1384 if (isByRef(ret_ty)) {
1385 try self.emitWValue(operand);
1386 } else {
1387 const result = try self.load(operand, ret_ty, 0);
1388 try self.emitWValue(result);
1389 }
12441390
1245 const result = try self.load(operand, self.air.typeOf(un_op).childType(), 0);
1246 try self.emitWValue(result);
1247 try self.restoreStackPointer();1391 try self.restoreStackPointer();
1248 try self.addTag(.@"return");1392 try self.addTag(.@"return");
1249 return .none;1393 return .none;
...@@ -1277,20 +1421,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1277,20 +1421,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1277 const arg_val = self.resolveInst(arg_ref);1421 const arg_val = self.resolveInst(arg_ref);
12781422
1279 const arg_ty = self.air.typeOf(arg_ref);1423 const arg_ty = self.air.typeOf(arg_ref);
1280 switch (arg_ty.zigTypeTag()) {1424 if (!arg_ty.hasCodeGenBits()) continue;
1281 .Struct, .Pointer, .Optional, .ErrorUnion => {1425
1282 // single pointer can be passed directly1426 // If we need to pass by reference, but the argument is a constant,
1283 if (arg_ty.isSinglePointer() or arg_val != .constant) {1427 // we must first lower it before passing it.
1284 try self.emitWValue(arg_val);1428 if (isByRef(arg_ty) and arg_val == .constant) {
1285 continue;1429 const arg_local = try self.allocStack(arg_ty);
1286 }1430 try self.store(arg_local, arg_val, arg_ty, 0);
1287 const abi_size = arg_ty.abiSize(self.target);1431 try self.emitWValue(arg_local);
1288 const arg_local = try self.allocLocal(Type.initTag(.i32));1432 } else if (arg_val == .none) {
1289 try self.moveStack(@intCast(u32, abi_size), arg_local.local);1433 // TODO: Remove this branch when zero-sized pointers do not generate
1290 try self.store(arg_local, arg_val, arg_ty, 0);1434 // an argument.
1291 try self.emitWValue(arg_local);1435 try self.addImm32(0);
1292 },1436 } else {
1293 else => try self.emitWValue(arg_val),1437 try self.emitWValue(arg_val);
1294 }1438 }
1295 }1439 }
12961440
...@@ -1301,12 +1445,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1301,12 +1445,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1301 // so load its value onto the stack1445 // so load its value onto the stack
1302 std.debug.assert(ty.zigTypeTag() == .Pointer);1446 std.debug.assert(ty.zigTypeTag() == .Pointer);
1303 const operand = self.resolveInst(pl_op.operand);1447 const operand = self.resolveInst(pl_op.operand);
1304 const offset = switch (operand) {1448 try self.emitWValue(operand);
1305 .local_with_offset => |with_offset| with_offset.offset,
1306 else => @as(u32, 0),
1307 };
1308 const result = try self.load(operand, fn_ty, offset);
1309 try self.addLabel(.local_get, result.local);
13101449
1311 var fn_type = try self.genFunctype(fn_ty);1450 var fn_type = try self.genFunctype(fn_ty);
1312 defer fn_type.deinit(self.gpa);1451 defer fn_type.deinit(self.gpa);
...@@ -1318,9 +1457,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1318,9 +1457,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1318 const ret_ty = fn_ty.fnReturnType();1457 const ret_ty = fn_ty.fnReturnType();
1319 if (!ret_ty.hasCodeGenBits()) return WValue.none;1458 if (!ret_ty.hasCodeGenBits()) return WValue.none;
13201459
1460 // TODO: Implement this for all aggregate types
1461 if (ret_ty.isSlice()) {
1462 // first load the values onto the regular stack, before we move the stack pointer
1463 // to prevent overwriting the return value.
1464 const tmp = try self.allocLocal(ret_ty);
1465 try self.addLabel(.local_set, tmp.local);
1466 const field_ty = Type.@"usize";
1467 const offset = @intCast(u32, field_ty.abiSize(self.target));
1468 const ptr_local = try self.load(tmp, field_ty, 0);
1469 const len_local = try self.load(tmp, field_ty, offset);
1470
1471 // As our values are now safe, we reserve space on the virtual stack and
1472 // store the values there.
1473 const result = try self.allocStack(ret_ty);
1474 try self.store(result, ptr_local, field_ty, 0);
1475 try self.store(result, len_local, field_ty, offset);
1476 return result;
1477 }
1478
1321 const result_local = try self.allocLocal(ret_ty);1479 const result_local = try self.allocLocal(ret_ty);
1322 try self.addLabel(.local_set, result_local.local);1480 try self.addLabel(.local_set, result_local.local);
1323 // if the result was allocated on the virtual stack, we must load
1324 return result_local;1481 return result_local;
1325}1482}
13261483
...@@ -1331,14 +1488,8 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1331,14 +1488,8 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1331 if (self.initial_stack_value == .none) {1488 if (self.initial_stack_value == .none) {
1332 try self.initializeStack();1489 try self.initializeStack();
1333 }1490 }
13341491 if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} };
1335 const abi_size = child_type.abiSize(self.target);1492 return self.allocStack(child_type);
1336 if (abi_size == 0) return WValue{ .none = {} };
1337
1338 // local, containing the offset to the stack position
1339 const local = try self.allocLocal(Type.initTag(.i32)); // always pointer therefore i32
1340 try self.moveStack(@intCast(u32, abi_size), local.local);
1341 return local;
1342}1493}
13431494
1344fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1495fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1370,6 +1521,14 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1370,6 +1521,14 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
13701521
1371 switch (rhs) {1522 switch (rhs) {
1372 .constant => {1523 .constant => {
1524 if (rhs.constant.val.castTag(.decl_ref)) |_| {
1525 // retrieve values from memory instead
1526 const mem_local = try self.allocLocal(Type.usize);
1527 try self.emitWValue(rhs);
1528 try self.addLabel(.local_set, mem_local.local);
1529 try self.store(lhs, mem_local, ty, 0);
1530 return;
1531 }
1373 // constant will contain both tag and payload,1532 // constant will contain both tag and payload,
1374 // so save those in 2 temporary locals before storing them1533 // so save those in 2 temporary locals before storing them
1375 // in memory1534 // in memory
...@@ -1396,6 +1555,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1396,6 +1555,11 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1396 return try self.store(lhs, tag_local, tag_ty, 0);1555 return try self.store(lhs, tag_local, tag_ty, 0);
1397 },1556 },
1398 .local_with_offset => |with_offset| {1557 .local_with_offset => |with_offset| {
1558 // check if we're storing the payload, or the error
1559 if (with_offset.offset == 0) {
1560 try self.store(lhs, .{ .local = with_offset.local }, tag_ty, 0);
1561 return;
1562 }
1399 const tag_local = try self.allocLocal(tag_ty);1563 const tag_local = try self.allocLocal(tag_ty);
1400 try self.addImm32(0);1564 try self.addImm32(0);
1401 try self.addLabel(.local_set, tag_local.local);1565 try self.addLabel(.local_set, tag_local.local);
...@@ -1412,30 +1576,43 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1412,30 +1576,43 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1412 }1576 }
1413 },1577 },
1414 .Struct => {1578 .Struct => {
1415 // we are copying a struct with its fields.1579 if (rhs == .constant) {
1416 // Replace this with a wasm memcpy instruction once we support that feature.1580 try self.emitWValue(rhs);
1417 const fields_len = ty.structFieldCount();1581 try self.addLabel(.local_set, lhs.local);
1418 var index: usize = 0;1582 return;
1419 while (index < fields_len) : (index += 1) {
1420 const field_ty = ty.structFieldType(index);
1421 if (!field_ty.hasCodeGenBits()) continue;
1422 const field_offset = std.math.cast(u32, ty.structFieldOffset(index, self.target)) catch {
1423 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
1424 };
1425 const field_local = try self.load(rhs, field_ty, field_offset);
1426 try self.store(lhs, field_local, field_ty, field_offset);
1427 }1583 }
1428 return;1584 return try self.memCopy(ty, lhs, rhs);
1429 },1585 },
1430 .Pointer => {1586 .Pointer => {
1431 if (ty.isSlice() and rhs == .constant) {1587 if (ty.isSlice() and rhs == .constant) {
1432 try self.emitWValue(rhs);1588 try self.emitWValue(rhs);
1589
1590 const val = rhs.constant.val;
1433 const len_local = try self.allocLocal(Type.usize);1591 const len_local = try self.allocLocal(Type.usize);
1434 const ptr_local = try self.allocLocal(Type.usize);1592 const ptr_local = try self.allocLocal(Type.usize);
1593 const len_offset = self.ptrSize();
1594 if (val.castTag(.decl_ref)) |decl| {
1595 // for decl references we also need to retrieve the length and the original decl's pointer
1596 try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = Type.@"usize".abiAlignment(self.target) });
1597 try self.addLabel(.memory_address, decl.data.link.wasm.sym_index);
1598 try self.addMemArg(
1599 .i32_load,
1600 .{ .offset = len_offset, .alignment = Type.@"usize".abiAlignment(self.target) },
1601 );
1602 }
1435 try self.addLabel(.local_set, len_local.local);1603 try self.addLabel(.local_set, len_local.local);
1436 try self.addLabel(.local_set, ptr_local.local);1604 try self.addLabel(.local_set, ptr_local.local);
1437 try self.store(lhs, ptr_local, Type.usize, 0);1605 try self.store(lhs, ptr_local, Type.usize, 0);
1438 try self.store(lhs, len_local, Type.usize, self.target.cpu.arch.ptrBitWidth() / 8);1606 try self.store(lhs, len_local, Type.usize, len_offset);
1607 return;
1608 } else if (ty.isSlice()) {
1609 // store pointer first
1610 const ptr_local = try self.load(rhs, Type.@"usize", 0);
1611 try self.store(lhs, ptr_local, Type.@"usize", 0);
1612
1613 // retrieve length from rhs, and store that alongside lhs as well
1614 const len_local = try self.load(rhs, Type.@"usize", 4);
1615 try self.store(lhs, len_local, Type.@"usize", 4);
1439 return;1616 return;
1440 }1617 }
1441 },1618 },
...@@ -1447,12 +1624,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1447,12 +1624,16 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1447 // check if we should pass by pointer or value based on ABI size1624 // check if we should pass by pointer or value based on ABI size
1448 // TODO: Implement a way to get ABI values from a given type,1625 // TODO: Implement a way to get ABI values from a given type,
1449 // that is portable across the backend, rather than copying logic.1626 // that is portable across the backend, rather than copying logic.
1450 const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8)1627 const abi_size = switch (ty.zigTypeTag()) {
1451 @intCast(u8, ty.abiSize(self.target))1628 .Int,
1452 else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum)1629 .Float,
1453 @intCast(u8, ty.abiSize(self.target))1630 .ErrorSet,
1454 else1631 .Enum,
1455 @as(u8, 4);1632 .Bool,
1633 .ErrorUnion,
1634 => @intCast(u8, ty.abiSize(self.target)),
1635 else => @as(u8, 4),
1636 };
1456 const opcode = buildOpcode(.{1637 const opcode = buildOpcode(.{
1457 .valtype1 = valtype,1638 .valtype1 = valtype,
1458 .width = abi_size * 8, // use bitsize instead of byte size1639 .width = abi_size * 8, // use bitsize instead of byte size
...@@ -1460,14 +1641,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1460,14 +1641,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1460 });1641 });
14611642
1462 // store rhs value at stack pointer's location in memory1643 // store rhs value at stack pointer's location in memory
1463 const mem_arg_index = try self.addExtra(Mir.MemArg{1644 try self.addMemArg(
1464 .offset = offset,1645 Mir.Inst.Tag.fromOpcode(opcode),
1465 .alignment = ty.abiAlignment(self.target),1646 .{ .offset = offset, .alignment = ty.abiAlignment(self.target) },
1466 });1647 );
1467 try self.addInst(.{
1468 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1469 .data = .{ .payload = mem_arg_index },
1470 });
1471}1648}
14721649
1473fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1650fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1477,12 +1654,15 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1477,12 +1654,15 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
14771654
1478 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };1655 if (!ty.hasCodeGenBits()) return WValue{ .none = {} };
14791656
1480 return switch (ty.zigTypeTag()) {1657 if (isByRef(ty)) {
1481 .Struct, .ErrorUnion, .Optional, .Pointer => operand, // pass as pointer1658 const new_local = try self.allocStack(ty);
1482 else => switch (operand) {1659 try self.store(new_local, operand, ty, 0);
1483 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),1660 return new_local;
1484 else => try self.load(operand, ty, 0),1661 }
1485 },1662
1663 return switch (operand) {
1664 .local_with_offset => |with_offset| try self.load(operand, ty, with_offset.offset),
1665 else => try self.load(operand, ty, 0),
1486 };1666 };
1487}1667}
14881668
...@@ -1494,15 +1674,18 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {...@@ -1494,15 +1674,18 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1494 .unsigned1674 .unsigned
1495 else1675 else
1496 .signed;1676 .signed;
1497 // check if we should pass by pointer or value based on ABI size
1498 // TODO: Implement a way to get ABI values from a given type,1677 // TODO: Implement a way to get ABI values from a given type,
1499 // that is portable across the backend, rather than copying logic.1678 // that is portable across the backend, rather than copying logic.
1500 const abi_size = if ((ty.isInt() or ty.isAnyFloat()) and ty.abiSize(self.target) <= 8)1679 const abi_size = switch (ty.zigTypeTag()) {
1501 @intCast(u8, ty.abiSize(self.target))1680 .Int,
1502 else if (ty.zigTypeTag() == .ErrorSet or ty.zigTypeTag() == .Enum)1681 .Float,
1503 @intCast(u8, ty.abiSize(self.target))1682 .ErrorSet,
1504 else1683 .Enum,
1505 @as(u8, 4);1684 .Bool,
1685 .ErrorUnion,
1686 => @intCast(u8, ty.abiSize(self.target)),
1687 else => @as(u8, 4),
1688 };
15061689
1507 const opcode = buildOpcode(.{1690 const opcode = buildOpcode(.{
1508 .valtype1 = try self.typeToValtype(ty),1691 .valtype1 = try self.typeToValtype(ty),
...@@ -1511,14 +1694,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {...@@ -1511,14 +1694,10 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue {
1511 .signedness = signedness,1694 .signedness = signedness,
1512 });1695 });
15131696
1514 const mem_arg_index = try self.addExtra(Mir.MemArg{1697 try self.addMemArg(
1515 .offset = offset,1698 Mir.Inst.Tag.fromOpcode(opcode),
1516 .alignment = ty.abiAlignment(self.target),1699 .{ .offset = offset, .alignment = ty.abiAlignment(self.target) },
1517 });1700 );
1518 try self.addInst(.{
1519 .tag = Mir.Inst.Tag.fromOpcode(opcode),
1520 .data = .{ .payload = mem_arg_index },
1521 });
15221701
1523 // store the result in a local1702 // store the result in a local
1524 const result = try self.allocLocal(ty);1703 const result = try self.allocLocal(ty);
...@@ -1647,6 +1826,12 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1647,6 +1826,12 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1647 } else {1826 } else {
1648 try self.addLabel(.memory_address, decl.link.wasm.sym_index);1827 try self.addLabel(.memory_address, decl.link.wasm.sym_index);
1649 }1828 }
1829 } else if (val.castTag(.int_u64)) |int_ptr| {
1830 try self.addImm32(@bitCast(i32, @intCast(u32, int_ptr.data)));
1831 } else if (val.tag() == .zero) {
1832 try self.addImm32(0);
1833 } else if (val.tag() == .one) {
1834 try self.addImm32(1);
1650 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()});1835 } else return self.fail("Wasm TODO: emitConstant for other const pointer tag {s}", .{val.tag()});
1651 },1836 },
1652 .Void => {},1837 .Void => {},
...@@ -1706,10 +1891,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1706,10 +1891,10 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
17061891
1707 // When constant has value 'null', set is_null local to '1'1892 // When constant has value 'null', set is_null local to '1'
1708 // and payload to '0'1893 // and payload to '0'
1709 if (val.castTag(.opt_payload)) |pl| {1894 if (val.castTag(.opt_payload)) |payload| {
1710 const payload_val = pl.data;
1711 try self.addImm32(0);1895 try self.addImm32(0);
1712 try self.emitConstant(payload_val, payload_type);1896 if (payload_type.hasCodeGenBits())
1897 try self.emitConstant(payload.data, payload_type);
1713 } else {1898 } else {
1714 // set null-tag1899 // set null-tag
1715 try self.addImm32(1);1900 try self.addImm32(1);
...@@ -1717,6 +1902,20 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {...@@ -1717,6 +1902,20 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void {
1717 try self.addImm32(0);1902 try self.addImm32(0);
1718 }1903 }
1719 },1904 },
1905 .Struct => {
1906 const struct_data = val.castTag(.@"struct").?;
1907 // in case of structs, we reserve stack space and store it there.
1908 const result = try self.allocStack(ty);
1909
1910 const fields = ty.structFields();
1911 for (fields.values()) |field, index| {
1912 const tmp = try self.allocLocal(field.ty);
1913 try self.emitConstant(struct_data.data[index], field.ty);
1914 try self.addLabel(.local_set, tmp.local);
1915 try self.store(result, tmp, field.ty, field.offset);
1916 }
1917 try self.addLabel(.local_get, result.local);
1918 },
1720 else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),1919 else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
1721 }1920 }
1722}1921}
...@@ -1733,6 +1932,14 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void {...@@ -1733,6 +1932,14 @@ fn emitUndefined(self: *Self, ty: Type) InnerError!void {
1733 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),1932 33...64 => try self.addFloat64(@bitCast(f64, @as(u64, 0xaaaaaaaaaaaaaaaa))),
1734 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),1933 else => |bits| return self.fail("Wasm TODO: emitUndefined for float bitsize: {d}", .{bits}),
1735 },1934 },
1935 // As arrays point to linear memory, we cannot use 0xaaaaaaaa as the wasm
1936 // validator will not accept it due to out-of-bounds memory access);
1937 .Array => try self.addImm32(@bitCast(i32, @as(u32, 0xaa))),
1938 .Struct => {
1939 // TODO: Write 0xaa to each field
1940 const result = try self.allocStack(ty);
1941 try self.addLabel(.local_get, result.local);
1942 },
1736 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),1943 else => return self.fail("Wasm TODO: emitUndefined for type: {}\n", .{ty}),
1737 }1944 }
1738}1945}
...@@ -1946,7 +2153,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1946,7 +2153,14 @@ fn airUnreachable(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
19462153
1947fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2154fn airBitcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1948 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2155 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1949 return self.resolveInst(ty_op.operand);2156 const operand = self.resolveInst(ty_op.operand);
2157 if (operand == .constant) {
2158 const result = try self.allocLocal(self.air.typeOfIndex(inst));
2159 try self.emitWValue(operand);
2160 try self.addLabel(.local_set, result.local);
2161 return result;
2162 }
2163 return operand;
1950}2164}
19512165
1952fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2166fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1966,16 +2180,26 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr...@@ -1966,16 +2180,26 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
1966 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2180 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1967 const struct_ptr = self.resolveInst(ty_op.operand);2181 const struct_ptr = self.resolveInst(ty_op.operand);
1968 const struct_ty = self.air.typeOf(ty_op.operand).childType();2182 const struct_ty = self.air.typeOf(ty_op.operand).childType();
2183 const field_ty = struct_ty.structFieldType(index);
1969 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {2184 const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {
1970 return self.fail("Field type '{}' too big to fit into stack frame", .{2185 return self.fail("Field type '{}' too big to fit into stack frame", .{
1971 struct_ty.structFieldType(index),2186 field_ty,
1972 });2187 });
1973 };2188 };
1974 return structFieldPtr(struct_ptr, offset);2189 return structFieldPtr(struct_ptr, offset);
1975}2190}
19762191
1977fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {2192fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue {
1978 return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } };2193 var final_offset = offset;
2194 const local = switch (struct_ptr) {
2195 .local => |local| local,
2196 .local_with_offset => |with_offset| blk: {
2197 final_offset += with_offset.offset;
2198 break :blk with_offset.local;
2199 },
2200 else => unreachable,
2201 };
2202 return WValue{ .local_with_offset = .{ .local = local, .offset = final_offset } };
1979}2203}
19802204
1981fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2205fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1991,7 +2215,15 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1991,7 +2215,15 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1991 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {2215 const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {
1992 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});2216 return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty});
1993 };2217 };
1994 return try self.load(operand, field_ty, offset);2218
2219 if (isByRef(field_ty)) {
2220 return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } };
2221 }
2222
2223 switch (operand) {
2224 .local_with_offset => |with_offset| return try self.load(operand, field_ty, offset + with_offset.offset),
2225 else => return try self.load(operand, field_ty, offset),
2226 }
1995}2227}
19962228
1997fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2229fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2145,29 +2377,29 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2145,29 +2377,29 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2145fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {2377fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue {
2146 const un_op = self.air.instructions.items(.data)[inst].un_op;2378 const un_op = self.air.instructions.items(.data)[inst].un_op;
2147 const operand = self.resolveInst(un_op);2379 const operand = self.resolveInst(un_op);
2148 const err_ty = self.air.typeOf(un_op).errorUnionSet();2380 const err_ty = self.air.typeOf(un_op);
2381 const pl_ty = err_ty.errorUnionPayload();
21492382
2150 // load the error tag value2383 // load the error tag value
2151 try self.emitWValue(operand);2384 try self.emitWValue(operand);
2152 const mem_arg_index = try self.addExtra(Mir.MemArg{2385 if (pl_ty.hasCodeGenBits()) {
2153 .offset = 0,2386 try self.addMemArg(.i32_load16_u, .{
2154 .alignment = err_ty.abiAlignment(self.target),2387 .offset = 0,
2155 });2388 .alignment = err_ty.errorUnionSet().abiAlignment(self.target),
2156 try self.addInst(.{2389 });
2157 .tag = .i32_load16_u,2390 }
2158 .data = .{ .payload = mem_arg_index },
2159 });
21602391
2161 // Compare the error value with '0'2392 // Compare the error value with '0'
2162 try self.addImm32(0);2393 try self.addImm32(0);
2163 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));2394 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
21642395
2165 const is_err_tmp = try self.allocLocal(err_ty);2396 const is_err_tmp = try self.allocLocal(Type.initTag(.i32)); // result is always an i32
2166 try self.addLabel(.local_set, is_err_tmp.local);2397 try self.addLabel(.local_set, is_err_tmp.local);
2167 return is_err_tmp;2398 return is_err_tmp;
2168}2399}
21692400
2170fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2401fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2402 if (self.liveness.isUnused(inst)) return WValue.none;
2171 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2403 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2172 const operand = self.resolveInst(ty_op.operand);2404 const operand = self.resolveInst(ty_op.operand);
2173 const err_ty = self.air.typeOf(ty_op.operand);2405 const err_ty = self.air.typeOf(ty_op.operand);
...@@ -2183,6 +2415,11 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2183,6 +2415,11 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2183 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2415 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2184 const operand = self.resolveInst(ty_op.operand);2416 const operand = self.resolveInst(ty_op.operand);
2185 const err_ty = self.air.typeOf(ty_op.operand);2417 const err_ty = self.air.typeOf(ty_op.operand);
2418 const payload_ty = err_ty.errorUnionPayload();
2419 if (!payload_ty.hasCodeGenBits()) {
2420 return operand;
2421 }
2422
2186 return try self.load(operand, err_ty.errorUnionSet(), 0);2423 return try self.load(operand, err_ty.errorUnionSet(), 0);
2187}2424}
21882425
...@@ -2192,20 +2429,43 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2192,20 +2429,43 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2192 const operand = self.resolveInst(ty_op.operand);2429 const operand = self.resolveInst(ty_op.operand);
21932430
2194 const op_ty = self.air.typeOf(ty_op.operand);2431 const op_ty = self.air.typeOf(ty_op.operand);
2195 if (!op_ty.hasCodeGenBits()) return WValue.none;2432 if (!op_ty.hasCodeGenBits()) return operand;
2196 const err_ty = self.air.getRefType(ty_op.ty);2433 const err_ty = self.air.getRefType(ty_op.ty);
2197 const offset = err_ty.errorUnionSet().abiSize(self.target);2434 const offset = err_ty.errorUnionSet().abiSize(self.target);
21982435
2199 return WValue{ .local_with_offset = .{2436 const err_union = try self.allocStack(err_ty);
2200 .local = operand.local,2437 const to_store = switch (op_ty.zigTypeTag()) {
2201 .offset = @intCast(u32, offset),2438 // for those types we must load the pointer and then store
2202 } };2439 // its value
2440 .Pointer, .Optional => blk: {
2441 if (!op_ty.isPtrLikeOptional()) {
2442 return self.fail("TODO: airWrapErrUnionPayload for optional type {}", .{op_ty});
2443 }
2444 break :blk try self.load(operand, op_ty, 0);
2445 },
2446 .Int => operand,
2447 else => return self.fail("TODO: airWrapErrUnionPayload for type {}", .{op_ty}),
2448 };
2449
2450 try self.store(err_union, to_store, op_ty, @intCast(u32, offset));
2451
2452 // ensure we also write '0' to the error part, so any present stack value gets overwritten by it.
2453 const tmp_local = try self.allocLocal(err_ty.errorUnionSet()); // locals are '0' by default.
2454 try self.store(err_union, tmp_local, err_ty.errorUnionSet(), 0);
2455
2456 return err_union;
2203}2457}
22042458
2205fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2459fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2206 if (self.liveness.isUnused(inst)) return WValue.none;2460 if (self.liveness.isUnused(inst)) return WValue.none;
2207 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2461 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2208 return self.resolveInst(ty_op.operand);2462 const operand = self.resolveInst(ty_op.operand);
2463 const err_ty = self.air.getRefType(ty_op.ty);
2464
2465 const err_union = try self.allocStack(err_ty);
2466 // TODO: Also write 'undefined' to the payload
2467 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);
2468 return err_union;
2209}2469}
22102470
2211fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2471fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2243,13 +2503,11 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!...@@ -2243,13 +2503,11 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!
2243 const un_op = self.air.instructions.items(.data)[inst].un_op;2503 const un_op = self.air.instructions.items(.data)[inst].un_op;
2244 const operand = self.resolveInst(un_op);2504 const operand = self.resolveInst(un_op);
22452505
2246 // load the null tag value2506 const op_ty = self.air.typeOf(un_op);
2247 try self.emitWValue(operand);2507 try self.emitWValue(operand);
2248 const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 1 });2508 if (!op_ty.isPtrLikeOptional()) {
2249 try self.addInst(.{2509 try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 });
2250 .tag = .i32_load8_u,2510 }
2251 .data = .{ .payload = mem_arg_index },
2252 });
22532511
2254 // Compare the error value with '0'2512 // Compare the error value with '0'
2255 try self.addImm32(0);2513 try self.addImm32(0);
...@@ -2306,9 +2564,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2306,9 +2564,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23062564
2307 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2565 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2308 const operand = self.resolveInst(ty_op.operand);2566 const operand = self.resolveInst(ty_op.operand);
2309 const pointer_width = self.target.cpu.arch.ptrBitWidth() / 8;
23102567
2311 return try self.load(operand, Type.usize, pointer_width);2568 return try self.load(operand, Type.usize, self.ptrSize());
2312}2569}
23132570
2314fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2571fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2333,12 +2590,36 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2333,12 +2590,36 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
23332590
2334 const result = try self.allocLocal(elem_ty);2591 const result = try self.allocLocal(elem_ty);
2335 try self.addLabel(.local_set, result.local);2592 try self.addLabel(.local_set, result.local);
2336 return switch (elem_ty.zigTypeTag()) {2593
2337 // pass as pointer2594 if (isByRef(elem_ty)) {
2338 .Pointer, .Struct, .Optional => result,2595 return result;
2339 // pass by value2596 }
2340 else => try self.load(result, elem_ty, 0),2597 return try self.load(result, elem_ty, 0);
2341 };2598}
2599
2600fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2601 if (self.liveness.isUnused(inst)) return WValue.none;
2602 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2603 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2604 const slice_ty = self.air.typeOf(bin_op.lhs);
2605 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
2606 const elem_size = elem_ty.abiSize(self.target);
2607
2608 const slice = self.resolveInst(bin_op.lhs);
2609 const index = self.resolveInst(bin_op.rhs);
2610
2611 const slice_ptr = try self.load(slice, slice_ty, 0);
2612 try self.addLabel(.local_get, slice_ptr.local);
2613
2614 // calculate index into slice
2615 try self.emitWValue(index);
2616 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
2617 try self.addTag(.i32_mul);
2618 try self.addTag(.i32_add);
2619
2620 const result = try self.allocLocal(Type.initTag(.i32));
2621 try self.addLabel(.local_set, result.local);
2622 return result;
2342}2623}
23432624
2344fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2625fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2407,3 +2688,124 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2407,3 +2688,124 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2407 try self.addLabel(.local_set, result.local);2688 try self.addLabel(.local_set, result.local);
2408 return result;2689 return result;
2409}2690}
2691
2692fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2693 const un_op = self.air.instructions.items(.data)[inst].un_op;
2694 return self.resolveInst(un_op);
2695}
2696
2697fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2698 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2699 const operand = self.resolveInst(ty_op.operand);
2700 const array_ty = self.air.typeOf(ty_op.operand).childType();
2701 const ty = Type.@"usize";
2702 const ptr_width = @intCast(u32, ty.abiSize(self.target));
2703 const slice_ty = self.air.getRefType(ty_op.ty);
2704
2705 // create a slice on the stack
2706 const slice_local = try self.allocStack(slice_ty);
2707
2708 // store the array ptr in the slice
2709 if (array_ty.hasCodeGenBits()) {
2710 try self.store(slice_local, operand, ty, 0);
2711 }
2712
2713 // store the length of the array in the slice
2714 const len = array_ty.arrayLen();
2715 try self.addImm32(@bitCast(i32, @intCast(u32, len)));
2716 const len_local = try self.allocLocal(ty);
2717 try self.addLabel(.local_set, len_local.local);
2718 try self.store(slice_local, len_local, ty, ptr_width);
2719
2720 return slice_local;
2721}
2722
2723fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2724 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2725 const un_op = self.air.instructions.items(.data)[inst].un_op;
2726 return self.resolveInst(un_op);
2727}
2728
2729fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2730 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2731
2732 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2733 const ptr_ty = self.air.typeOf(bin_op.lhs);
2734 const pointer = self.resolveInst(bin_op.lhs);
2735 const index = self.resolveInst(bin_op.rhs);
2736 const elem_ty = ptr_ty.childType();
2737 const elem_size = elem_ty.abiSize(self.target);
2738
2739 // load pointer onto the stack
2740 if (ptr_ty.isSlice()) {
2741 const ptr_local = try self.load(pointer, ptr_ty, 0);
2742 try self.addLabel(.local_get, ptr_local.local);
2743 } else {
2744 try self.emitWValue(pointer);
2745 }
2746
2747 // calculate index into slice
2748 try self.emitWValue(index);
2749 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
2750 try self.addTag(.i32_mul);
2751 try self.addTag(.i32_add);
2752
2753 const result = try self.allocLocal(elem_ty);
2754 try self.addLabel(.local_set, result.local);
2755 if (isByRef(elem_ty)) {
2756 return result;
2757 }
2758 return try self.load(result, elem_ty, 0);
2759}
2760
2761fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2762 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2763 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2764 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2765 const ptr_ty = self.air.typeOf(bin_op.lhs);
2766 const elem_ty = self.air.getRefType(ty_pl.ty).childType();
2767 const elem_size = elem_ty.abiSize(self.target);
2768
2769 const ptr = self.resolveInst(bin_op.lhs);
2770 const index = self.resolveInst(bin_op.rhs);
2771
2772 // load pointer onto the stack
2773 if (ptr_ty.isSlice()) {
2774 const ptr_local = try self.load(ptr, ptr_ty, 0);
2775 try self.addLabel(.local_get, ptr_local.local);
2776 } else {
2777 try self.emitWValue(ptr);
2778 }
2779
2780 // calculate index into ptr
2781 try self.emitWValue(index);
2782 try self.addImm32(@bitCast(i32, @intCast(u32, elem_size)));
2783 try self.addTag(.i32_mul);
2784 try self.addTag(.i32_add);
2785
2786 const result = try self.allocLocal(Type.initTag(.i32));
2787 try self.addLabel(.local_set, result.local);
2788 return result;
2789}
2790
2791fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
2792 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2793 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2794 const ptr = self.resolveInst(bin_op.lhs);
2795 const offset = self.resolveInst(bin_op.rhs);
2796 const pointee_ty = self.air.typeOf(bin_op.lhs).childType();
2797
2798 const valtype = try self.typeToValtype(Type.usize);
2799 const mul_opcode = buildOpcode(.{ .valtype1 = valtype, .op = .mul });
2800 const bin_opcode = buildOpcode(.{ .valtype1 = valtype, .op = op });
2801
2802 try self.emitWValue(ptr);
2803 try self.emitWValue(offset);
2804 try self.addImm32(@bitCast(i32, @intCast(u32, pointee_ty.abiSize(self.target))));
2805 try self.addTag(Mir.Inst.Tag.fromOpcode(mul_opcode));
2806 try self.addTag(Mir.Inst.Tag.fromOpcode(bin_opcode));
2807
2808 const result = try self.allocLocal(Type.usize);
2809 try self.addLabel(.local_set, result.local);
2810 return result;
2811}
src/arch/wasm/Emit.zig+4-2
...@@ -284,10 +284,12 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -284,10 +284,12 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
284}284}
285285
286fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {286fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void {
287 const label = emit.mir.instructions.items(.data)[inst].label;287 const type_index = emit.mir.instructions.items(.data)[inst].label;
288 try emit.code.append(std.wasm.opcode(.call_indirect));288 try emit.code.append(std.wasm.opcode(.call_indirect));
289 // NOTE: If we remove unused function types in the future for incremental
290 // linking, we must also emit a relocation for this `type_index`
291 try leb128.writeULEB128(emit.code.writer(), type_index);
289 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index292 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0)); // TODO: Emit relocation for table index
290 try leb128.writeULEB128(emit.code.writer(), label);
291}293}
292294
293fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {295fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
src/link/Wasm.zig+4-3
...@@ -257,6 +257,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {...@@ -257,6 +257,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
257 if (build_options.have_llvm) {257 if (build_options.have_llvm) {
258 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);258 if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
259 }259 }
260 if (!decl.ty.hasCodeGenBits()) return;
260 assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()261 assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
261262
262 decl.link.wasm.clear();263 decl.link.wasm.clear();
...@@ -645,7 +646,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -645,7 +646,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
645 .kind = .{646 .kind = .{
646 .table = .{647 .table = .{
647 .limits = .{648 .limits = .{
648 .min = @intCast(u32, self.imports.count()),649 .min = @intCast(u32, self.function_table.count()),
649 .max = null,650 .max = null,
650 },651 },
651 .reftype = .funcref,652 .reftype = .funcref,
...@@ -677,7 +678,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -677,7 +678,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
677 header_offset,678 header_offset,
678 .import,679 .import,
679 @intCast(u32, (try file.getPos()) - header_offset - header_size),680 @intCast(u32, (try file.getPos()) - header_offset - header_size),
680 @intCast(u32, self.imports.count() + @boolToInt(import_memory)),681 @intCast(u32, self.imports.count() + @boolToInt(import_memory) + @boolToInt(import_table)),
681 );682 );
682 }683 }
683684
...@@ -700,7 +701,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {...@@ -700,7 +701,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
700701
701 // Table section702 // Table section
702 const export_table = self.base.options.export_table;703 const export_table = self.base.options.export_table;
703 if (!import_table and (self.function_table.count() > 0 or export_table)) {704 if (!import_table) {
704 const header_offset = try reserveVecSectionHeader(file);705 const header_offset = try reserveVecSectionHeader(file);
705 const writer = file.writer();706 const writer = file.writer();
706707
test/behavior.zig+23-23
...@@ -4,60 +4,60 @@ test {...@@ -4,60 +4,60 @@ test {
4 // Tests that pass for stage1, stage2, the C -and wasm backend.4 // Tests that pass for stage1, stage2, the C -and wasm backend.
5 _ = @import("behavior/basic.zig");5 _ = @import("behavior/basic.zig");
6 _ = @import("behavior/bitcast.zig");6 _ = @import("behavior/bitcast.zig");
7 _ = @import("behavior/bool.zig");
7 _ = @import("behavior/bugs/624.zig");8 _ = @import("behavior/bugs/624.zig");
8 _ = @import("behavior/bugs/655.zig");9 _ = @import("behavior/bugs/655.zig");
9 _ = @import("behavior/bugs/679.zig");10 _ = @import("behavior/bugs/679.zig");
11 _ = @import("behavior/bugs/704.zig");
10 _ = @import("behavior/bugs/1111.zig");12 _ = @import("behavior/bugs/1111.zig");
11 _ = @import("behavior/bugs/1486.zig");13 _ = @import("behavior/bugs/1486.zig");
12 _ = @import("behavior/bugs/2346.zig");14 _ = @import("behavior/bugs/2346.zig");
15 _ = @import("behavior/bugs/2692.zig");
16 _ = @import("behavior/bugs/2889.zig");
17 _ = @import("behavior/bugs/3046.zig");
18 _ = @import("behavior/bugs/3586.zig");
19 _ = @import("behavior/bugs/4560.zig");
20 _ = @import("behavior/bugs/4769_a.zig");
21 _ = @import("behavior/bugs/4769_b.zig");
22 _ = @import("behavior/bugs/4954.zig");
13 _ = @import("behavior/bugs/6850.zig");23 _ = @import("behavior/bugs/6850.zig");
24 _ = @import("behavior/byval_arg_var.zig");
25 _ = @import("behavior/call.zig");
26 _ = @import("behavior/defer.zig");
14 _ = @import("behavior/enum.zig");27 _ = @import("behavior/enum.zig");
28 _ = @import("behavior/error.zig");
29 _ = @import("behavior/fn_in_struct_in_comptime.zig");
15 _ = @import("behavior/hasdecl.zig");30 _ = @import("behavior/hasdecl.zig");
16 _ = @import("behavior/hasfield.zig");31 _ = @import("behavior/hasfield.zig");
32 _ = @import("behavior/if.zig");
17 _ = @import("behavior/import.zig");33 _ = @import("behavior/import.zig");
34 _ = @import("behavior/incomplete_struct_param_tld.zig");
35 _ = @import("behavior/inttoptr.zig");
36 _ = @import("behavior/pointers.zig");
37 _ = @import("behavior/ptrcast.zig");
18 _ = @import("behavior/pub_enum.zig");38 _ = @import("behavior/pub_enum.zig");
39 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
19 _ = @import("behavior/slice_sentinel_comptime.zig");40 _ = @import("behavior/slice_sentinel_comptime.zig");
20 _ = @import("behavior/truncate.zig");41 _ = @import("behavior/truncate.zig");
21 _ = @import("behavior/type.zig");
22 _ = @import("behavior/type_info.zig");42 _ = @import("behavior/type_info.zig");
43 _ = @import("behavior/type.zig");
23 _ = @import("behavior/usingnamespace.zig");44 _ = @import("behavior/usingnamespace.zig");
45 _ = @import("behavior/underscore.zig");
2446
25 // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend47 // Tests that pass for stage1, stage2 and the C backend, but not for the wasm backend
26 if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {48 if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {
27 _ = @import("behavior/align.zig");49 _ = @import("behavior/align.zig");
28 _ = @import("behavior/array.zig");50 _ = @import("behavior/array.zig");
29 _ = @import("behavior/bool.zig");
30 _ = @import("behavior/bugs/704.zig");
31 _ = @import("behavior/bugs/2692.zig");
32 _ = @import("behavior/bugs/2889.zig");
33 _ = @import("behavior/bugs/3046.zig");
34 _ = @import("behavior/bugs/3586.zig");
35 _ = @import("behavior/bugs/4560.zig");
36 _ = @import("behavior/bugs/4769_a.zig");
37 _ = @import("behavior/bugs/4769_b.zig");
38 _ = @import("behavior/bugs/4954.zig");
39 _ = @import("behavior/byval_arg_var.zig");
40 _ = @import("behavior/call.zig");
41 _ = @import("behavior/cast.zig");51 _ = @import("behavior/cast.zig");
42 _ = @import("behavior/defer.zig");
43 _ = @import("behavior/error.zig");
44 _ = @import("behavior/fn_in_struct_in_comptime.zig");
45 _ = @import("behavior/for.zig");52 _ = @import("behavior/for.zig");
46 _ = @import("behavior/generics.zig");53 _ = @import("behavior/generics.zig");
47 _ = @import("behavior/if.zig");
48 _ = @import("behavior/incomplete_struct_param_tld.zig");
49 _ = @import("behavior/int128.zig");54 _ = @import("behavior/int128.zig");
50 _ = @import("behavior/inttoptr.zig");
51 _ = @import("behavior/member_func.zig");55 _ = @import("behavior/member_func.zig");
52 _ = @import("behavior/null.zig");56 _ = @import("behavior/null.zig");
53 _ = @import("behavior/optional.zig");57 _ = @import("behavior/optional.zig");
54 _ = @import("behavior/pointers.zig");
55 _ = @import("behavior/ptrcast.zig");
56 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
57 _ = @import("behavior/struct.zig");58 _ = @import("behavior/struct.zig");
58 _ = @import("behavior/this.zig");59 _ = @import("behavior/this.zig");
59 _ = @import("behavior/translate_c_macros.zig");60 _ = @import("behavior/translate_c_macros.zig");
60 _ = @import("behavior/underscore.zig");
61 _ = @import("behavior/while.zig");61 _ = @import("behavior/while.zig");
62 _ = @import("behavior/void.zig");62 _ = @import("behavior/void.zig");
6363