| ... | ... | @@ -690,7 +690,7 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 690 | 690 | const info = ty.intInfo(self.target); |
| 691 | 691 | if (info.bits <= 32) break :blk wasm.Valtype.i32; |
| 692 | 692 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; |
| 693 | | return self.fail("TODO: Support integer bitsize: '{d}'", .{info.bits}); |
| 693 | break :blk wasm.Valtype.i32; // represented as pointer to stack |
| 694 | 694 | }, |
| 695 | 695 | .Enum => switch (ty.tag()) { |
| 696 | 696 | .enum_simple => wasm.Valtype.i32, |
| ... | ... | @@ -753,7 +753,7 @@ fn genFunctype(self: *Self, fn_ty: Type) !wasm.Type { |
| 753 | 753 | defer returns.deinit(); |
| 754 | 754 | const return_type = fn_ty.fnReturnType(); |
| 755 | 755 | |
| 756 | | const want_sret = isByRef(return_type); |
| 756 | const want_sret = self.isByRef(return_type); |
| 757 | 757 | |
| 758 | 758 | if (want_sret) { |
| 759 | 759 | try params.append(try self.typeToValtype(Type.usize)); |
| ... | ... | @@ -1084,7 +1084,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1084 | 1084 | const ret_ty = fn_ty.fnReturnType(); |
| 1085 | 1085 | // Check if we store the result as a pointer to the stack rather than |
| 1086 | 1086 | // by value |
| 1087 | | if (isByRef(ret_ty)) { |
| 1087 | if (self.isByRef(ret_ty)) { |
| 1088 | 1088 | // the sret arg will be passed as first argument, therefore we |
| 1089 | 1089 | // set the `return_value` before allocating locals for regular args. |
| 1090 | 1090 | result.return_value = .{ .local = self.local_index }; |
| ... | ... | @@ -1209,7 +1209,7 @@ fn ptrSize(self: *const Self) u16 { |
| 1209 | 1209 | |
| 1210 | 1210 | /// For a given `Type`, will return true when the type will be passed |
| 1211 | 1211 | /// by reference, rather than by value. |
| 1212 | | fn isByRef(ty: Type) bool { |
| 1212 | fn isByRef(self: Self, ty: Type) bool { |
| 1213 | 1213 | switch (ty.zigTypeTag()) { |
| 1214 | 1214 | .Type, |
| 1215 | 1215 | .ComptimeInt, |
| ... | ... | @@ -1224,7 +1224,6 @@ fn isByRef(ty: Type) bool { |
| 1224 | 1224 | .NoReturn, |
| 1225 | 1225 | .Void, |
| 1226 | 1226 | .Bool, |
| 1227 | | .Int, |
| 1228 | 1227 | .Float, |
| 1229 | 1228 | .ErrorSet, |
| 1230 | 1229 | .Fn, |
| ... | ... | @@ -1238,6 +1237,7 @@ fn isByRef(ty: Type) bool { |
| 1238 | 1237 | .Frame, |
| 1239 | 1238 | .Union, |
| 1240 | 1239 | => return ty.hasCodeGenBits(), |
| 1240 | .Int => return if (ty.intInfo(self.target).bits > 64) true else false, |
| 1241 | 1241 | .ErrorUnion => { |
| 1242 | 1242 | const has_tag = ty.errorUnionSet().hasCodeGenBits(); |
| 1243 | 1243 | const has_pl = ty.errorUnionPayload().hasCodeGenBits(); |
| ... | ... | @@ -1412,7 +1412,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1412 | 1412 | const child_type = self.air.typeOfIndex(inst).childType(); |
| 1413 | 1413 | if (child_type.abiSize(self.target) == 0) return WValue{ .none = {} }; |
| 1414 | 1414 | |
| 1415 | | if (isByRef(child_type)) { |
| 1415 | if (self.isByRef(child_type)) { |
| 1416 | 1416 | return self.return_value; |
| 1417 | 1417 | } |
| 1418 | 1418 | |
| ... | ... | @@ -1429,7 +1429,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1429 | 1429 | const ret_ty = self.air.typeOf(un_op).childType(); |
| 1430 | 1430 | if (!ret_ty.hasCodeGenBits()) return WValue.none; |
| 1431 | 1431 | |
| 1432 | | if (!isByRef(ret_ty)) { |
| 1432 | if (!self.isByRef(ret_ty)) { |
| 1433 | 1433 | const result = try self.load(operand, ret_ty, 0); |
| 1434 | 1434 | try self.emitWValue(result); |
| 1435 | 1435 | } |
| ... | ... | @@ -1451,7 +1451,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1451 | 1451 | else => unreachable, |
| 1452 | 1452 | }; |
| 1453 | 1453 | const ret_ty = fn_ty.fnReturnType(); |
| 1454 | | const first_param_sret = isByRef(ret_ty); |
| 1454 | const first_param_sret = self.isByRef(ret_ty); |
| 1455 | 1455 | |
| 1456 | 1456 | const target: ?*Decl = blk: { |
| 1457 | 1457 | const func_val = self.air.value(pl_op.operand) orelse break :blk null; |
| ... | ... | @@ -1479,7 +1479,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1479 | 1479 | |
| 1480 | 1480 | // If we need to pass by reference, but the argument is a constant, |
| 1481 | 1481 | // we must first lower it before passing it. |
| 1482 | | if (isByRef(arg_ty) and arg_val == .constant) { |
| 1482 | if (self.isByRef(arg_ty) and arg_val == .constant) { |
| 1483 | 1483 | const arg_local = try self.allocStack(arg_ty); |
| 1484 | 1484 | try self.store(arg_local, arg_val, arg_ty, 0); |
| 1485 | 1485 | try self.emitWValue(arg_local); |
| ... | ... | @@ -1591,7 +1591,12 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1591 | 1591 | if (payload_ty.hasCodeGenBits()) { |
| 1592 | 1592 | const payload_local = try self.allocLocal(payload_ty); |
| 1593 | 1593 | try self.addLabel(.local_set, payload_local.local); |
| 1594 | | 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 | } |
| 1595 | 1600 | } |
| 1596 | 1601 | try self.addLabel(.local_set, tag_local.local); |
| 1597 | 1602 | |
| ... | ... | @@ -1608,7 +1613,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1608 | 1613 | // Load values from `rhs` stack position and store in `lhs` instead |
| 1609 | 1614 | const tag_local = try self.load(rhs, tag_ty, 0); |
| 1610 | 1615 | if (payload_ty.hasCodeGenBits()) { |
| 1611 | | if (isByRef(payload_ty)) { |
| 1616 | if (self.isByRef(payload_ty)) { |
| 1612 | 1617 | const payload_ptr = try self.buildPointerOffset(rhs, payload_offset, .new); |
| 1613 | 1618 | const lhs_payload_ptr = try self.buildPointerOffset(lhs, payload_offset, .new); |
| 1614 | 1619 | try self.store(lhs_payload_ptr, payload_ptr, payload_ty, 0); |
| ... | ... | @@ -1734,7 +1739,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1734 | 1739 | |
| 1735 | 1740 | if (!ty.hasCodeGenBits()) return WValue{ .none = {} }; |
| 1736 | 1741 | |
| 1737 | | if (isByRef(ty)) { |
| 1742 | if (self.isByRef(ty)) { |
| 1738 | 1743 | const new_local = try self.allocStack(ty); |
| 1739 | 1744 | try self.store(new_local, operand, ty, 0); |
| 1740 | 1745 | return new_local; |
| ... | ... | @@ -1803,6 +1808,11 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 1803 | 1808 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1804 | 1809 | const lhs = self.resolveInst(bin_op.lhs); |
| 1805 | 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 | } |
| 1806 | 1816 | |
| 1807 | 1817 | try self.emitWValue(lhs); |
| 1808 | 1818 | try self.emitWValue(rhs); |
| ... | ... | @@ -1893,9 +1903,7 @@ fn emitConstant(self: *Self, val: Value, ty: Type) InnerError!void { |
| 1893 | 1903 | const result = try self.allocStack(ty); |
| 1894 | 1904 | var space: Value.BigIntSpace = undefined; |
| 1895 | 1905 | const bigint = val.toBigInt(&space); |
| 1896 | | if (bigint.limbs.len == 1) { |
| 1897 | | // value is '0'. As wasm's values are zeroed by default, |
| 1898 | | // just return and pop its stack pointer value. |
| 1906 | if (bigint.limbs.len == 1 and bigint.limbs[0] == 0) { |
| 1899 | 1907 | try self.addLabel(.local_get, result.local); |
| 1900 | 1908 | return; |
| 1901 | 1909 | } |
| ... | ... | @@ -2277,6 +2285,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 2277 | 2285 | // both lhs and rhs, as well as checking the payload are matching of lhs and rhs |
| 2278 | 2286 | return self.cmpOptionals(lhs, rhs, operand_ty, op); |
| 2279 | 2287 | } |
| 2288 | } else if (self.isByRef(operand_ty)) { |
| 2289 | return self.cmpBigInt(lhs, rhs, operand_ty, op); |
| 2280 | 2290 | } |
| 2281 | 2291 | |
| 2282 | 2292 | try self.emitWValue(lhs); |
| ... | ... | @@ -2425,7 +2435,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2425 | 2435 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); |
| 2426 | 2436 | }; |
| 2427 | 2437 | |
| 2428 | | if (isByRef(field_ty)) { |
| 2438 | if (self.isByRef(field_ty)) { |
| 2429 | 2439 | return WValue{ .local_with_offset = .{ .local = operand.local, .offset = offset } }; |
| 2430 | 2440 | } |
| 2431 | 2441 | |
| ... | ... | @@ -2608,13 +2618,16 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!W |
| 2608 | 2618 | } |
| 2609 | 2619 | |
| 2610 | 2620 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2611 | | if (self.liveness.isUnused(inst)) return WValue.none; |
| 2621 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2612 | 2622 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2613 | 2623 | const operand = self.resolveInst(ty_op.operand); |
| 2614 | 2624 | const err_ty = self.air.typeOf(ty_op.operand); |
| 2615 | 2625 | const payload_ty = err_ty.errorUnionPayload(); |
| 2616 | | if (!payload_ty.hasCodeGenBits()) return WValue.none; |
| 2626 | if (!payload_ty.hasCodeGenBits()) return WValue{ .none = {} }; |
| 2617 | 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 | } |
| 2618 | 2631 | return try self.load(operand, payload_ty, offset); |
| 2619 | 2632 | } |
| 2620 | 2633 | |
| ... | ... | @@ -2741,7 +2754,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2741 | 2754 | |
| 2742 | 2755 | const offset = opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target); |
| 2743 | 2756 | |
| 2744 | | if (isByRef(payload_ty)) { |
| 2757 | if (self.isByRef(payload_ty)) { |
| 2745 | 2758 | return self.buildPointerOffset(operand, offset, .new); |
| 2746 | 2759 | } |
| 2747 | 2760 | |
| ... | ... | @@ -2872,7 +2885,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2872 | 2885 | const result = try self.allocLocal(elem_ty); |
| 2873 | 2886 | try self.addLabel(.local_set, result.local); |
| 2874 | 2887 | |
| 2875 | | if (isByRef(elem_ty)) { |
| 2888 | if (self.isByRef(elem_ty)) { |
| 2876 | 2889 | return result; |
| 2877 | 2890 | } |
| 2878 | 2891 | return try self.load(result, elem_ty, 0); |
| ... | ... | @@ -3033,7 +3046,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3033 | 3046 | |
| 3034 | 3047 | const result = try self.allocLocal(elem_ty); |
| 3035 | 3048 | try self.addLabel(.local_set, result.local); |
| 3036 | | if (isByRef(elem_ty)) { |
| 3049 | if (self.isByRef(elem_ty)) { |
| 3037 | 3050 | return result; |
| 3038 | 3051 | } |
| 3039 | 3052 | return try self.load(result, elem_ty, 0); |
| ... | ... | @@ -3183,7 +3196,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3183 | 3196 | const result = try self.allocLocal(elem_ty); |
| 3184 | 3197 | try self.addLabel(.local_set, result.local); |
| 3185 | 3198 | |
| 3186 | | if (isByRef(elem_ty)) { |
| 3199 | if (self.isByRef(elem_ty)) { |
| 3187 | 3200 | return result; |
| 3188 | 3201 | } |
| 3189 | 3202 | return try self.load(result, elem_ty, 0); |
| ... | ... | @@ -3244,10 +3257,43 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std |
| 3244 | 3257 | try self.addLabel(.local_set, result.local); |
| 3245 | 3258 | try self.endBlock(); |
| 3246 | 3259 | |
| 3247 | | const is_equal = try self.allocLocal(Type.initTag(.i32)); |
| 3248 | 3260 | try self.emitWValue(result); |
| 3249 | 3261 | try self.addImm32(0); |
| 3250 | 3262 | try self.addTag(if (op == .eq) .i32_ne else .i32_eq); |
| 3251 | | try self.addLabel(.local_set, is_equal.local); |
| 3252 | | return is_equal; |
| 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; |
| 3253 | 3299 | } |