| ... | @@ -597,7 +597,8 @@ fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue { | ... | @@ -597,7 +597,8 @@ fn resolveInst(self: Self, ref: Air.Inst.Ref) WValue { |
| 597 | }; | 597 | }; |
| 598 | | 598 | |
| 599 | const inst_type = self.air.typeOfIndex(inst_index); | 599 | const inst_type = self.air.typeOfIndex(inst_index); |
| 600 | if (!inst_type.hasCodeGenBits()) return .none; | 600 | // It's allowed to have 0-bit integers |
| | 601 | if (!inst_type.hasCodeGenBits() and !inst_type.isInt()) return WValue{ .none = {} }; |
| 601 | | 602 | |
| 602 | if (self.air.instructions.items(.tag)[inst_index] == .constant) { | 603 | if (self.air.instructions.items(.tag)[inst_index] == .constant) { |
| 603 | const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; | 604 | const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; |
| ... | @@ -689,7 +690,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { | ... | @@ -689,7 +690,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 689 | const info = ty.intInfo(self.target); | 690 | const info = ty.intInfo(self.target); |
| 690 | if (info.bits <= 32) break :blk wasm.Valtype.i32; | 691 | if (info.bits <= 32) break :blk wasm.Valtype.i32; |
| 691 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; | 692 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; |
| 692 | return self.fail("Integer bit size not supported by wasm: '{d}'", .{info.bits}); | 693 | break :blk wasm.Valtype.i32; // represented as pointer to stack |
| 693 | }, | 694 | }, |
| 694 | .Enum => switch (ty.tag()) { | 695 | .Enum => switch (ty.tag()) { |
| 695 | .enum_simple => wasm.Valtype.i32, | 696 | .enum_simple => wasm.Valtype.i32, |
| ... | @@ -752,7 +753,7 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { | ... | @@ -752,7 +753,7 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { |
| 752 | defer returns.deinit(); | 753 | defer returns.deinit(); |
| 753 | const return_type = fn_ty.fnReturnType(); | 754 | const return_type = fn_ty.fnReturnType(); |
| 754 | | 755 | |
| 755 | const want_sret = isByRef(return_type); | 756 | const want_sret = self.isByRef(return_type); |
| 756 | | 757 | |
| 757 | if (want_sret) { | 758 | if (want_sret) { |
| 758 | try params.append(try self.typeToValtype(Type.usize)); | 759 | try params.append(try self.typeToValtype(Type.usize)); |
| ... | @@ -900,17 +901,6 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -900,17 +901,6 @@ fn genTypedValue(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 900 | .Array => switch (val.tag()) { | 901 | .Array => switch (val.tag()) { |
| 901 | .bytes => { | 902 | .bytes => { |
| 902 | const payload = val.castTag(.bytes).?; | 903 | const payload = val.castTag(.bytes).?; |
| 903 | if (ty.sentinel()) |sentinel| { | | |
| 904 | try self.code.appendSlice(payload.data); | | |
| 905 | | | |
| 906 | switch (try self.genTypedValue(ty.childType(), sentinel)) { | | |
| 907 | .appended => return Result.appended, | | |
| 908 | .externally_managed => |data| { | | |
| 909 | try self.code.appendSlice(data); | | |
| 910 | return Result.appended; | | |
| 911 | }, | | |
| 912 | } | | |
| 913 | } | | |
| 914 | return Result{ .externally_managed = payload.data }; | 904 | return Result{ .externally_managed = payload.data }; |
| 915 | }, | 905 | }, |
| 916 | .array => { | 906 | .array => { |
| ... | @@ -1094,7 +1084,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1094,7 +1084,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1094 | const ret_ty = fn_ty.fnReturnType(); | 1084 | const ret_ty = fn_ty.fnReturnType(); |
| 1095 | // Check if we store the result as a pointer to the stack rather than | 1085 | // Check if we store the result as a pointer to the stack rather than |
| 1096 | // by value | 1086 | // by value |
| 1097 | if (isByRef(ret_ty)) { | 1087 | if (self.isByRef(ret_ty)) { |
| 1098 | // the sret arg will be passed as first argument, therefore we | 1088 | // the sret arg will be passed as first argument, therefore we |
| 1099 | // set the `return_value` before allocating locals for regular args. | 1089 | // set the `return_value` before allocating locals for regular args. |
| 1100 | result.return_value = .{ .local = self.local_index }; | 1090 | result.return_value = .{ .local = self.local_index }; |
| ... | @@ -1219,7 +1209,7 @@ fn ptrSize(self: *const Self) u16 { | ... | @@ -1219,7 +1209,7 @@ fn ptrSize(self: *const Self) u16 { |
| 1219 | | 1209 | |
| 1220 | /// For a given `Type`, will return true when the type will be passed | 1210 | /// For a given `Type`, will return true when the type will be passed |
| 1221 | /// by reference, rather than by value. | 1211 | /// by reference, rather than by value. |
| 1222 | fn isByRef(ty: Type) bool { | 1212 | fn isByRef(self: Self, ty: Type) bool { |
| 1223 | switch (ty.zigTypeTag()) { | 1213 | switch (ty.zigTypeTag()) { |
| 1224 | .Type, | 1214 | .Type, |
| 1225 | .ComptimeInt, | 1215 | .ComptimeInt, |
| ... | @@ -1234,7 +1224,6 @@ fn isByRef(ty: Type) bool { | ... | @@ -1234,7 +1224,6 @@ fn isByRef(ty: Type) bool { |
| 1234 | .NoReturn, | 1224 | .NoReturn, |
| 1235 | .Void, | 1225 | .Void, |
| 1236 | .Bool, | 1226 | .Bool, |
| 1237 | .Int, | | |
| 1238 | .Float, | 1227 | .Float, |
| 1239 | .ErrorSet, | 1228 | .ErrorSet, |
| 1240 | .Fn, | 1229 | .Fn, |
| ... | @@ -1248,6 +1237,7 @@ fn isByRef(ty: Type) bool { | ... | @@ -1248,6 +1237,7 @@ fn isByRef(ty: Type) bool { |
| 1248 | .Frame, | 1237 | .Frame, |
| 1249 | .Union, | 1238 | .Union, |
| 1250 | => return ty.hasCodeGenBits(), | 1239 | => return ty.hasCodeGenBits(), |
| | 1240 | .Int => return if (ty.intInfo(self.target).bits > 64) true else false, |
| 1251 | .ErrorUnion => { | 1241 | .ErrorUnion => { |
| 1252 | const has_tag = ty.errorUnionSet().hasCodeGenBits(); | 1242 | const has_tag = ty.errorUnionSet().hasCodeGenBits(); |
| 1253 | const has_pl = ty.errorUnionPayload().hasCodeGenBits(); | 1243 | const has_pl = ty.errorUnionPayload().hasCodeGenBits(); |
| ... | @@ -1318,6 +1308,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1318,6 +1308,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1318 | .bit_or => self.airBinOp(inst, .@"or"), | 1308 | .bit_or => self.airBinOp(inst, .@"or"), |
| 1319 | .bool_and => self.airBinOp(inst, .@"and"), | 1309 | .bool_and => self.airBinOp(inst, .@"and"), |
| 1320 | .bool_or => self.airBinOp(inst, .@"or"), | 1310 | .bool_or => self.airBinOp(inst, .@"or"), |
| | 1311 | .shl => self.airBinOp(inst, .shl), |
| | 1312 | .shr => self.airBinOp(inst, .shr), |
| 1321 | .xor => self.airBinOp(inst, .xor), | 1313 | .xor => self.airBinOp(inst, .xor), |
| 1322 | | 1314 | |
| 1323 | .cmp_eq => self.airCmp(inst, .eq), | 1315 | .cmp_eq => self.airCmp(inst, .eq), |
| ... | @@ -1341,6 +1333,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1341,6 +1333,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1341 | .constant => unreachable, | 1333 | .constant => unreachable, |
| 1342 | .dbg_stmt => WValue.none, | 1334 | .dbg_stmt => WValue.none, |
| 1343 | .intcast => self.airIntcast(inst), | 1335 | .intcast => self.airIntcast(inst), |
| | 1336 | .float_to_int => self.airFloatToInt(inst), |
| 1344 | | 1337 | |
| 1345 | .is_err => self.airIsErr(inst, .i32_ne), | 1338 | .is_err => self.airIsErr(inst, .i32_ne), |
| 1346 | .is_non_err => self.airIsErr(inst, .i32_eq), | 1339 | .is_non_err => self.airIsErr(inst, .i32_eq), |
| ... | @@ -1417,17 +1410,16 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1417,17 +1410,16 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1417 | | 1410 | |
| 1418 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1411 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1419 | const child_type = self.air.typeOfIndex(inst).childType(); | 1412 | const child_type = self.air.typeOfIndex(inst).childType(); |
| | 1413 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| | 1414 | |
| | 1415 | if (self.isByRef(child_type)) { |
| | 1416 | return self.return_value; |
| | 1417 | } |
| 1420 | | 1418 | |
| 1421 | // Initialize the stack | 1419 | // Initialize the stack |
| 1422 | if (self.initial_stack_value == .none) { | 1420 | if (self.initial_stack_value == .none) { |
| 1423 | try self.initializeStack(); | 1421 | try self.initializeStack(); |
| 1424 | } | 1422 | } |
| 1425 | | | |
| 1426 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; | | |
| 1427 | | | |
| 1428 | if (isByRef(child_type)) { | | |
| 1429 | return self.return_value; | | |
| 1430 | } | | |
| 1431 | return self.allocStack(child_type); | 1423 | return self.allocStack(child_type); |
| 1432 | } | 1424 | } |
| 1433 | | 1425 | |
| ... | @@ -1437,7 +1429,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1437,7 +1429,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1437 | const ret_ty = self.air.typeOf(un_op).childType(); | 1429 | const ret_ty = self.air.typeOf(un_op).childType(); |
| 1438 | if (!ret_ty.hasCodeGenBits()) return WValue.none; | 1430 | if (!ret_ty.hasCodeGenBits()) return WValue.none; |
| 1439 | | 1431 | |
| 1440 | if (!isByRef(ret_ty)) { | 1432 | if (!self.isByRef(ret_ty)) { |
| 1441 | const result = try self.load(operand, ret_ty, 0); | 1433 | const result = try self.load(operand, ret_ty, 0); |
| 1442 | try self.emitWValue(result); | 1434 | try self.emitWValue(result); |
| 1443 | } | 1435 | } |
| ... | @@ -1459,7 +1451,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1459,7 +1451,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1459 | else => unreachable, | 1451 | else => unreachable, |
| 1460 | }; | 1452 | }; |
| 1461 | const ret_ty = fn_ty.fnReturnType(); | 1453 | const ret_ty = fn_ty.fnReturnType(); |
| 1462 | const first_param_sret = isByRef(ret_ty); | 1454 | const first_param_sret = self.isByRef(ret_ty); |
| 1463 | | 1455 | |
| 1464 | const target: ?*Decl = blk: { | 1456 | const target: ?*Decl = blk: { |
| 1465 | const func_val = self.air.value(pl_op.operand) orelse break :blk null; | 1457 | const func_val = self.air.value(pl_op.operand) orelse break :blk null; |
| ... | @@ -1487,7 +1479,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1487,7 +1479,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1487 | | 1479 | |
| 1488 | // If we need to pass by reference, but the argument is a constant, | 1480 | // If we need to pass by reference, but the argument is a constant, |
| 1489 | // we must first lower it before passing it. | 1481 | // we must first lower it before passing it. |
| 1490 | if (isByRef(arg_ty) and arg_val == .constant) { | 1482 | if (self.isByRef(arg_ty) and arg_val == .constant) { |
| 1491 | const arg_local = try self.allocStack(arg_ty); | 1483 | const arg_local = try self.allocStack(arg_ty); |
| 1492 | try self.store(arg_local, arg_val, arg_ty, 0); | 1484 | try self.store(arg_local, arg_val, arg_ty, 0); |
| 1493 | try self.emitWValue(arg_local); | 1485 | try self.emitWValue(arg_local); |
| ... | @@ -1599,7 +1591,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1599,7 +1591,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1599 | if (payload_ty.hasCodeGenBits()) { | 1591 | if (payload_ty.hasCodeGenBits()) { |
| 1600 | const payload_local = try self.allocLocal(payload_ty); | 1592 | const payload_local = try self.allocLocal(payload_ty); |
| 1601 | try self.addLabel(.local_set, payload_local.local); | 1593 | try self.addLabel(.local_set, payload_local.local); |
| 1602 | try self.store(lhs, payload_local, payload_ty, payload_offset); | 1594 | if (self.isByRef(payload_ty)) { |
| | 1595 | const ptr = try self.buildPointerOffset(lhs, payload_offset, .new); |
| | 1596 | try self.store(ptr, payload_local, payload_ty, 0); |
| | 1597 | } else { |
| | 1598 | try self.store(lhs, payload_local, payload_ty, payload_offset); |
| | 1599 | } |
| 1603 | } | 1600 | } |
| 1604 | try self.addLabel(.local_set, tag_local.local); | 1601 | try self.addLabel(.local_set, tag_local.local); |
| 1605 | | 1602 | |
| ... | @@ -1616,7 +1613,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1616,7 +1613,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1616 | // Load values from `rhs` stack position and store in `lhs` instead | 1613 | // Load values from `rhs` stack position and store in `lhs` instead |
| 1617 | const tag_local = try self.load(rhs, tag_ty, 0); | 1614 | const tag_local = try self.load(rhs, tag_ty, 0); |
| 1618 | if (payload_ty.hasCodeGenBits()) { | 1615 | if (payload_ty.hasCodeGenBits()) { |
| 1619 | if (isByRef(payload_ty)) { | 1616 | if (self.isByRef(payload_ty)) { |
| 1620 | const payload_ptr = try self.buildPointerOffset(rhs, payload_offset, .new); | 1617 | const payload_ptr = try self.buildPointerOffset(rhs, payload_offset, .new); |
| 1621 | const lhs_payload_ptr = try self.buildPointerOffset(lhs, payload_offset, .new); | 1618 | const lhs_payload_ptr = try self.buildPointerOffset(lhs, payload_offset, .new); |
| 1622 | try self.store(lhs_payload_ptr, payload_ptr, payload_ty, 0); | 1619 | try self.store(lhs_payload_ptr, payload_ptr, payload_ty, 0); |
| ... | @@ -1649,12 +1646,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1649,12 +1646,13 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1649 | } | 1646 | } |
| 1650 | }, | 1647 | }, |
| 1651 | .Struct, .Array => { | 1648 | .Struct, .Array => { |
| 1652 | if (rhs == .constant) { | 1649 | const final_rhs = if (rhs == .constant) blk: { |
| | 1650 | const tmp = try self.allocLocal(Type.usize); |
| 1653 | try self.emitWValue(rhs); | 1651 | try self.emitWValue(rhs); |
| 1654 | try self.addLabel(.local_set, lhs.local); | 1652 | try self.addLabel(.local_set, tmp.local); |
| 1655 | return; | 1653 | break :blk tmp; |
| 1656 | } | 1654 | } else rhs; |
| 1657 | return try self.memCopy(ty, lhs, rhs); | 1655 | return try self.memCopy(ty, lhs, final_rhs); |
| 1658 | }, | 1656 | }, |
| 1659 | .Pointer => { | 1657 | .Pointer => { |
| 1660 | if (ty.isSlice() and rhs == .constant) { | 1658 | if (ty.isSlice() and rhs == .constant) { |
| ... | @@ -1665,10 +1663,20 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1665,10 +1663,20 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1665 | const ptr_local = try self.allocLocal(Type.usize); | 1663 | const ptr_local = try self.allocLocal(Type.usize); |
| 1666 | const len_offset = self.ptrSize(); | 1664 | const len_offset = self.ptrSize(); |
| 1667 | if (val.castTag(.decl_ref)) |decl| { | 1665 | if (val.castTag(.decl_ref)) |decl| { |
| 1668 | // for decl references we also need to retrieve the length and the original decl's pointer | 1666 | const decl_ty: Type = decl.data.ty; |
| 1669 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = self.ptrSize() }); | 1667 | if (decl_ty.isSlice()) { |
| 1670 | try self.addLabel(.memory_address, decl.data.link.wasm.sym_index); | 1668 | // for decl references we also need to retrieve the length and the original decl's pointer |
| 1671 | try self.addMemArg(.i32_load, .{ .offset = len_offset, .alignment = self.ptrSize() }); | 1669 | try self.addMemArg(.i32_load, .{ .offset = 0, .alignment = self.ptrSize() }); |
| | 1670 | try self.addLabel(.memory_address, decl.data.link.wasm.sym_index); |
| | 1671 | try self.addMemArg(.i32_load, .{ .offset = len_offset, .alignment = self.ptrSize() }); |
| | 1672 | } else if (decl_ty.zigTypeTag() == .Array) { |
| | 1673 | const len = decl_ty.arrayLen(); |
| | 1674 | switch (self.ptrSize()) { |
| | 1675 | 4 => try self.addImm32(@bitCast(i32, @intCast(u32, len))), |
| | 1676 | 8 => try self.addImm64(len), |
| | 1677 | else => unreachable, |
| | 1678 | } |
| | 1679 | } else return self.fail("Wasm todo: Implement storing slices for decl_ref with type: {}", .{decl_ty}); |
| 1672 | } | 1680 | } |
| 1673 | try self.addLabel(.local_set, len_local.local); | 1681 | try self.addLabel(.local_set, len_local.local); |
| 1674 | try self.addLabel(.local_set, ptr_local.local); | 1682 | try self.addLabel(.local_set, ptr_local.local); |
| ... | @@ -1677,15 +1685,23 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1677,15 +1685,23 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1677 | return; | 1685 | return; |
| 1678 | } else if (ty.isSlice()) { | 1686 | } else if (ty.isSlice()) { |
| 1679 | // store pointer first | 1687 | // store pointer first |
| 1680 | const ptr_local = try self.load(rhs, Type.@"usize", 0); | 1688 | const ptr_local = try self.load(rhs, Type.usize, 0); |
| 1681 | try self.store(lhs, ptr_local, Type.@"usize", 0); | 1689 | try self.store(lhs, ptr_local, Type.usize, 0); |
| 1682 | | 1690 | |
| 1683 | // retrieve length from rhs, and store that alongside lhs as well | 1691 | // retrieve length from rhs, and store that alongside lhs as well |
| 1684 | const len_local = try self.load(rhs, Type.@"usize", 4); | 1692 | const len_local = try self.load(rhs, Type.usize, self.ptrSize()); |
| 1685 | try self.store(lhs, len_local, Type.@"usize", 4); | 1693 | try self.store(lhs, len_local, Type.usize, self.ptrSize()); |
| 1686 | return; | 1694 | return; |
| 1687 | } | 1695 | } |
| 1688 | }, | 1696 | }, |
| | 1697 | .Int => if (ty.intInfo(self.target).bits > 64) { |
| | 1698 | if (rhs == .constant) { |
| | 1699 | try self.emitWValue(rhs); |
| | 1700 | try self.addLabel(.local_set, lhs.local); |
| | 1701 | return; |
| | 1702 | } |
| | 1703 | return try self.memCopy(ty, lhs, rhs); |
| | 1704 | }, |
| 1689 | else => {}, | 1705 | else => {}, |
| 1690 | } | 1706 | } |
| 1691 | try self.emitWValue(lhs); | 1707 | try self.emitWValue(lhs); |
| ... | @@ -1723,7 +1739,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1723,7 +1739,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1723 | | 1739 | |
| 1724 | if (!ty.hasCodeGenBits()) return WValue{ .none = {} }; | 1740 | if (!ty.hasCodeGenBits()) return WValue{ .none = {} }; |
| 1725 | | 1741 | |
| 1726 | if (isByRef(ty)) { | 1742 | if (self.isByRef(ty)) { |
| 1727 | const new_local = try self.allocStack(ty); | 1743 | const new_local = try self.allocStack(ty); |
| 1728 | try self.store(new_local, operand, ty, 0); | 1744 | try self.store(new_local, operand, ty, 0); |
| 1729 | return new_local; | 1745 | return new_local; |
| ... | @@ -1787,9 +1803,16 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1787,9 +1803,16 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1787 | } | 1803 | } |
| 1788 | | 1804 | |
| 1789 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | 1805 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| | 1806 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 1807 | |
| 1790 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1808 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1791 | const lhs = self.resolveInst(bin_op.lhs); | 1809 | const lhs = self.resolveInst(bin_op.lhs); |
| 1792 | const rhs = self.resolveInst(bin_op.rhs); | 1810 | const rhs = self.resolveInst(bin_op.rhs); |
| | 1811 | const operand_ty = self.air.typeOfIndex(inst); |
| | 1812 | |
| | 1813 | if (self.isByRef(operand_ty)) { |
| | 1814 | return self.fail("TODO: Implement binary operation for type: {}", .{operand_ty}); |
| | 1815 | } |
| 1793 | | 1816 | |
| 1794 | try self.emitWValue(lhs); | 1817 | try self.emitWValue(lhs); |
| 1795 | try self.emitWValue(rhs); | 1818 | try self.emitWValue(rhs); |
| ... | @@ -1865,16 +1888,36 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { | ... | @@ -1865,16 +1888,36 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1865 | // write constant | 1888 | // write constant |
| 1866 | switch (int_info.signedness) { | 1889 | switch (int_info.signedness) { |
| 1867 | .signed => switch (int_info.bits) { | 1890 | .signed => switch (int_info.bits) { |
| 1868 | 0...32 => try self.addImm32(@intCast(i32, val.toSignedInt())), | 1891 | 0...32 => return try self.addImm32(@intCast(i32, val.toSignedInt())), |
| 1869 | 33...64 => try self.addImm64(@bitCast(u64, val.toSignedInt())), | 1892 | 33...64 => return try self.addImm64(@bitCast(u64, val.toSignedInt())), |
| | 1893 | 65...128 => {}, |
| 1870 | else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}), | 1894 | else => |bits| return self.fail("Wasm todo: emitConstant for integer with {d} bits", .{bits}), |
| 1871 | }, | 1895 | }, |
| 1872 | .unsigned => switch (int_info.bits) { | 1896 | .unsigned => switch (int_info.bits) { |
| 1873 | 0...32 => try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))), | 1897 | 0...32 => return try self.addImm32(@bitCast(i32, @intCast(u32, val.toUnsignedInt()))), |
| 1874 | 33...64 => try self.addImm64(val.toUnsignedInt()), | 1898 | 33...64 => return try self.addImm64(val.toUnsignedInt()), |
| | 1899 | 65...128 => {}, |
| 1875 | else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}), | 1900 | else => |bits| return self.fail("Wasm TODO: emitConstant for integer with {d} bits", .{bits}), |
| 1876 | }, | 1901 | }, |
| 1877 | } | 1902 | } |
| | 1903 | const result = try self.allocStack(ty); |
| | 1904 | var space: Value.BigIntSpace = undefined; |
| | 1905 | const bigint = val.toBigInt(&space); |
| | 1906 | if (bigint.limbs.len == 1 and bigint.limbs[0] == 0) { |
| | 1907 | try self.addLabel(.local_get, result.local); |
| | 1908 | return; |
| | 1909 | } |
| | 1910 | if (@sizeOf(usize) != @sizeOf(u64)) { |
| | 1911 | return self.fail("Wasm todo: Implement big integers for 32bit compiler", .{}); |
| | 1912 | } |
| | 1913 | |
| | 1914 | for (bigint.limbs) |_, index| { |
| | 1915 | const limb = bigint.limbs[bigint.limbs.len - index - 1]; |
| | 1916 | try self.addLabel(.local_get, result.local); |
| | 1917 | try self.addImm64(limb); |
| | 1918 | try self.addMemArg(.i64_store, .{ .offset = @intCast(u32, index * 8), .alignment = 8 }); |
| | 1919 | } |
| | 1920 | try self.addLabel(.local_get, result.local); |
| 1878 | }, | 1921 | }, |
| 1879 | .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())), | 1922 | .Bool => try self.addImm32(@intCast(i32, val.toSignedInt())), |
| 1880 | .Float => { | 1923 | .Float => { |
| ... | @@ -2233,9 +2276,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner | ... | @@ -2233,9 +2276,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 2233 | const rhs = self.resolveInst(bin_op.rhs); | 2276 | const rhs = self.resolveInst(bin_op.rhs); |
| 2234 | const operand_ty = self.air.typeOf(bin_op.lhs); | 2277 | const operand_ty = self.air.typeOf(bin_op.lhs); |
| 2235 | | 2278 | |
| 2236 | try self.emitWValue(lhs); | | |
| 2237 | try self.emitWValue(rhs); | | |
| 2238 | | | |
| 2239 | if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) { | 2279 | if (operand_ty.zigTypeTag() == .Optional and !operand_ty.isPtrLikeOptional()) { |
| 2240 | var buf: Type.Payload.ElemType = undefined; | 2280 | var buf: Type.Payload.ElemType = undefined; |
| 2241 | const payload_ty = operand_ty.optionalChild(&buf); | 2281 | const payload_ty = operand_ty.optionalChild(&buf); |
| ... | @@ -2243,10 +2283,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner | ... | @@ -2243,10 +2283,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 2243 | // When we hit this case, we must check the value of optionals | 2283 | // When we hit this case, we must check the value of optionals |
| 2244 | // that are not pointers. This means first checking against non-null for | 2284 | // that are not pointers. This means first checking against non-null for |
| 2245 | // both lhs and rhs, as well as checking the payload are matching of lhs and rhs | 2285 | // both lhs and rhs, as well as checking the payload are matching of lhs and rhs |
| 2246 | return self.fail("TODO: Implement airCmp for comparing optionals", .{}); | 2286 | return self.cmpOptionals(lhs, rhs, operand_ty, op); |
| 2247 | } | 2287 | } |
| | 2288 | } else if (self.isByRef(operand_ty)) { |
| | 2289 | return self.cmpBigInt(lhs, rhs, operand_ty, op); |
| 2248 | } | 2290 | } |
| 2249 | | 2291 | |
| | 2292 | try self.emitWValue(lhs); |
| | 2293 | try self.emitWValue(rhs); |
| | 2294 | |
| 2250 | const signedness: std.builtin.Signedness = blk: { | 2295 | const signedness: std.builtin.Signedness = blk: { |
| 2251 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 2296 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 2252 | if (operand_ty.zigTypeTag() != .Int) break :blk .unsigned; | 2297 | if (operand_ty.zigTypeTag() != .Int) break :blk .unsigned; |
| ... | @@ -2390,7 +2435,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2390,7 +2435,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2390 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); | 2435 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); |
| 2391 | }; | 2436 | }; |
| 2392 | | 2437 | |
| 2393 | if (isByRef(field_ty)) { | 2438 | if (self.isByRef(field_ty)) { |
| 2394 | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } }; | 2439 | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } }; |
| 2395 | } | 2440 | } |
| 2396 | | 2441 | |
| ... | @@ -2573,13 +2618,16 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W | ... | @@ -2573,13 +2618,16 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W |
| 2573 | } | 2618 | } |
| 2574 | | 2619 | |
| 2575 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2620 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2576 | if (self.liveness.isUnused(inst)) return WValue.none; | 2621 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2577 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2622 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2578 | const operand = self.resolveInst(ty_op.operand); | 2623 | const operand = self.resolveInst(ty_op.operand); |
| 2579 | const err_ty = self.air.typeOf(ty_op.operand); | 2624 | const err_ty = self.air.typeOf(ty_op.operand); |
| 2580 | const payload_ty = err_ty.errorUnionPayload(); | 2625 | const payload_ty = err_ty.errorUnionPayload(); |
| 2581 | if (!payload_ty.hasCodeGenBits()) return WValue.none; | 2626 | if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} }; |
| 2582 | const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target)); | 2627 | const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target)); |
| | 2628 | if (self.isByRef(payload_ty)) { |
| | 2629 | return self.buildPointerOffset(operand, offset, .new); |
| | 2630 | } |
| 2583 | return try self.load(operand, payload_ty, offset); | 2631 | return try self.load(operand, payload_ty, offset); |
| 2584 | } | 2632 | } |
| 2585 | | 2633 | |
| ... | @@ -2632,6 +2680,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2632,6 +2680,8 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2632 | } | 2680 | } |
| 2633 | | 2681 | |
| 2634 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2682 | fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 2683 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2684 | |
| 2635 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2685 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2636 | const ty = self.air.getRefType(ty_op.ty); | 2686 | const ty = self.air.getRefType(ty_op.ty); |
| 2637 | const operand = self.resolveInst(ty_op.operand); | 2687 | const operand = self.resolveInst(ty_op.operand); |
| ... | @@ -2656,7 +2706,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2656,7 +2706,8 @@ fn airIntcast(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2656 | .signed => .i64_extend_i32_s, | 2706 | .signed => .i64_extend_i32_s, |
| 2657 | .unsigned => .i64_extend_i32_u, | 2707 | .unsigned => .i64_extend_i32_u, |
| 2658 | }); | 2708 | }); |
| 2659 | } | 2709 | } else unreachable; |
| | 2710 | |
| 2660 | const result = try self.allocLocal(ty); | 2711 | const result = try self.allocLocal(ty); |
| 2661 | try self.addLabel(.local_set, result.local); | 2712 | try self.addLabel(.local_set, result.local); |
| 2662 | return result; | 2713 | return result; |
| ... | @@ -2668,12 +2719,16 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en | ... | @@ -2668,12 +2719,16 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en |
| 2668 | | 2719 | |
| 2669 | const op_ty = self.air.typeOf(un_op); | 2720 | const op_ty = self.air.typeOf(un_op); |
| 2670 | const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty; | 2721 | const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty; |
| | 2722 | return self.isNull(operand, optional_ty, opcode); |
| | 2723 | } |
| | 2724 | |
| | 2725 | fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { |
| 2671 | try self.emitWValue(operand); | 2726 | try self.emitWValue(operand); |
| 2672 | if (!optional_ty.isPtrLikeOptional()) { | 2727 | if (!optional_ty.isPtrLikeOptional()) { |
| 2673 | var buf: Type.Payload.ElemType = undefined; | 2728 | var buf: Type.Payload.ElemType = undefined; |
| 2674 | const payload_ty = optional_ty.optionalChild(&buf); | 2729 | const payload_ty = optional_ty.optionalChild(&buf); |
| 2675 | // When payload is zero-bits, we can treat operand as a value, rather than a | 2730 | // When payload is zero-bits, we can treat operand as a value, rather than |
| 2676 | // stack value | 2731 | // a pointer to the stack value |
| 2677 | if (payload_ty.hasCodeGenBits()) { | 2732 | if (payload_ty.hasCodeGenBits()) { |
| 2678 | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); | 2733 | try self.addMemArg(.i32_load8_u, .{ .offset = 0, .alignment = 1 }); |
| 2679 | } | 2734 | } |
| ... | @@ -2699,7 +2754,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2699,7 +2754,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2699 | | 2754 | |
| 2700 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); | 2755 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); |
| 2701 | | 2756 | |
| 2702 | if (isByRef(payload_ty)) { | 2757 | if (self.isByRef(payload_ty)) { |
| 2703 | return self.buildPointerOffset(operand, offset, .new); | 2758 | return self.buildPointerOffset(operand, offset, .new); |
| 2704 | } | 2759 | } |
| 2705 | | 2760 | |
| ... | @@ -2830,7 +2885,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2830,7 +2885,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2830 | const result = try self.allocLocal(elem_ty); | 2885 | const result = try self.allocLocal(elem_ty); |
| 2831 | try self.addLabel(.local_set, result.local); | 2886 | try self.addLabel(.local_set, result.local); |
| 2832 | | 2887 | |
| 2833 | if (isByRef(elem_ty)) { | 2888 | if (self.isByRef(elem_ty)) { |
| 2834 | return result; | 2889 | return result; |
| 2835 | } | 2890 | } |
| 2836 | return try self.load(result, elem_ty, 0); | 2891 | return try self.load(result, elem_ty, 0); |
| ... | @@ -2991,7 +3046,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2991,7 +3046,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2991 | | 3046 | |
| 2992 | const result = try self.allocLocal(elem_ty); | 3047 | const result = try self.allocLocal(elem_ty); |
| 2993 | try self.addLabel(.local_set, result.local); | 3048 | try self.addLabel(.local_set, result.local); |
| 2994 | if (isByRef(elem_ty)) { | 3049 | if (self.isByRef(elem_ty)) { |
| 2995 | return result; | 3050 | return result; |
| 2996 | } | 3051 | } |
| 2997 | return try self.load(result, elem_ty, 0); | 3052 | return try self.load(result, elem_ty, 0); |
| ... | @@ -3141,8 +3196,104 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3141,8 +3196,104 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3141 | const result = try self.allocLocal(elem_ty); | 3196 | const result = try self.allocLocal(elem_ty); |
| 3142 | try self.addLabel(.local_set, result.local); | 3197 | try self.addLabel(.local_set, result.local); |
| 3143 | | 3198 | |
| 3144 | if (isByRef(elem_ty)) { | 3199 | if (self.isByRef(elem_ty)) { |
| 3145 | return result; | 3200 | return result; |
| 3146 | } | 3201 | } |
| 3147 | return try self.load(result, elem_ty, 0); | 3202 | return try self.load(result, elem_ty, 0); |
| 3148 | } | 3203 | } |
| | 3204 | |
| | 3205 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| | 3206 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 3207 | |
| | 3208 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 3209 | const operand = self.resolveInst(ty_op.operand); |
| | 3210 | const dest_ty = self.air.typeOfIndex(inst); |
| | 3211 | const op_ty = self.air.typeOf(ty_op.operand); |
| | 3212 | |
| | 3213 | try self.emitWValue(operand); |
| | 3214 | const op = buildOpcode(.{ |
| | 3215 | .op = .trunc, |
| | 3216 | .valtype1 = try self.typeToValtype(dest_ty), |
| | 3217 | .valtype2 = try self.typeToValtype(op_ty), |
| | 3218 | .signedness = if (dest_ty.isSignedInt()) .signed else .unsigned, |
| | 3219 | }); |
| | 3220 | try self.addTag(Mir.Inst.Tag.fromOpcode(op)); |
| | 3221 | |
| | 3222 | const result = try self.allocLocal(dest_ty); |
| | 3223 | try self.addLabel(.local_set, result.local); |
| | 3224 | return result; |
| | 3225 | } |
| | 3226 | |
| | 3227 | fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { |
| | 3228 | assert(operand_ty.hasCodeGenBits()); |
| | 3229 | assert(op == .eq or op == .neq); |
| | 3230 | var buf: Type.Payload.ElemType = undefined; |
| | 3231 | const payload_ty = operand_ty.optionalChild(&buf); |
| | 3232 | const offset = @intCast(u32, operand_ty.abiSize(self.target) - payload_ty.abiSize(self.target)); |
| | 3233 | |
| | 3234 | const lhs_is_null = try self.isNull(lhs, operand_ty, .i32_eq); |
| | 3235 | const rhs_is_null = try self.isNull(rhs, operand_ty, .i32_eq); |
| | 3236 | |
| | 3237 | // We store the final result in here that will be validated |
| | 3238 | // if the optional is truly equal. |
| | 3239 | const result = try self.allocLocal(Type.initTag(.i32)); |
| | 3240 | |
| | 3241 | try self.startBlock(.block, wasm.block_empty); |
| | 3242 | try self.emitWValue(lhs_is_null); |
| | 3243 | try self.emitWValue(rhs_is_null); |
| | 3244 | try self.addTag(.i32_ne); // inverse so we can exit early |
| | 3245 | try self.addLabel(.br_if, 0); |
| | 3246 | |
| | 3247 | const lhs_pl = try self.load(lhs, payload_ty, offset); |
| | 3248 | const rhs_pl = try self.load(rhs, payload_ty, offset); |
| | 3249 | |
| | 3250 | try self.emitWValue(lhs_pl); |
| | 3251 | try self.emitWValue(rhs_pl); |
| | 3252 | const opcode = buildOpcode(.{ .op = .ne, .valtype1 = try self.typeToValtype(payload_ty) }); |
| | 3253 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| | 3254 | try self.addLabel(.br_if, 0); |
| | 3255 | |
| | 3256 | try self.addImm32(1); |
| | 3257 | try self.addLabel(.local_set, result.local); |
| | 3258 | try self.endBlock(); |
| | 3259 | |
| | 3260 | try self.emitWValue(result); |
| | 3261 | try self.addImm32(0); |
| | 3262 | try self.addTag(if (op == .eq) .i32_ne else .i32_eq); |
| | 3263 | try self.addLabel(.local_set, result.local); |
| | 3264 | return result; |
| | 3265 | } |
| | 3266 | |
| | 3267 | /// Compares big integers by checking both its high bits and low bits. |
| | 3268 | /// TODO: Lower this to compiler_rt call |
| | 3269 | fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { |
| | 3270 | if (operand_ty.intInfo(self.target).bits > 128) { |
| | 3271 | return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); |
| | 3272 | } |
| | 3273 | |
| | 3274 | const result = try self.allocLocal(Type.initTag(.i32)); |
| | 3275 | { |
| | 3276 | try self.startBlock(.block, wasm.block_empty); |
| | 3277 | const lhs_high_bit = try self.load(lhs, Type.initTag(.u64), 0); |
| | 3278 | const lhs_low_bit = try self.load(lhs, Type.initTag(.u64), 8); |
| | 3279 | const rhs_high_bit = try self.load(rhs, Type.initTag(.u64), 0); |
| | 3280 | const rhs_low_bit = try self.load(rhs, Type.initTag(.u64), 8); |
| | 3281 | try self.emitWValue(lhs_high_bit); |
| | 3282 | try self.emitWValue(rhs_high_bit); |
| | 3283 | try self.addTag(.i64_ne); |
| | 3284 | try self.addLabel(.br_if, 0); |
| | 3285 | try self.emitWValue(lhs_low_bit); |
| | 3286 | try self.emitWValue(rhs_low_bit); |
| | 3287 | try self.addTag(.i64_ne); |
| | 3288 | try self.addLabel(.br_if, 0); |
| | 3289 | try self.addImm32(1); |
| | 3290 | try self.addLabel(.local_set, result.local); |
| | 3291 | try self.endBlock(); |
| | 3292 | } |
| | 3293 | |
| | 3294 | try self.emitWValue(result); |
| | 3295 | try self.addImm32(0); |
| | 3296 | try self.addTag(if (op == .eq) .i32_ne else .i32_eq); |
| | 3297 | try self.addLabel(.local_set, result.local); |
| | 3298 | return result; |
| | 3299 | } |