| ... | ... | @@ -1789,8 +1789,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1789 | 1789 | |
| 1790 | 1790 | const fn_info = self.decl.ty.fnInfo(); |
| 1791 | 1791 | if (!firstParamSRet(fn_info.cc, fn_info.return_type, self.target)) { |
| 1792 | | const result = try self.load(operand, ret_ty, 0); |
| 1793 | | try self.emitWValue(result); |
| 1792 | // leave on the stack |
| 1793 | _ = try self.load(operand, ret_ty, 0); |
| 1794 | 1794 | } |
| 1795 | 1795 | |
| 1796 | 1796 | try self.restoreStackPointer(); |
| ... | ... | @@ -1943,20 +1943,26 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1943 | 1943 | .Pointer => { |
| 1944 | 1944 | if (ty.isSlice()) { |
| 1945 | 1945 | // store pointer first |
| 1946 | // lower it to the stack so we do not have to store rhs into a local first |
| 1947 | try self.emitWValue(lhs); |
| 1946 | 1948 | const ptr_local = try self.load(rhs, Type.usize, 0); |
| 1947 | | try self.store(lhs, ptr_local, Type.usize, 0); |
| 1949 | try self.store(.{ .stack = {} }, ptr_local, Type.usize, 0 + lhs.offset()); |
| 1948 | 1950 | |
| 1949 | 1951 | // retrieve length from rhs, and store that alongside lhs as well |
| 1952 | try self.emitWValue(lhs); |
| 1950 | 1953 | const len_local = try self.load(rhs, Type.usize, self.ptrSize()); |
| 1951 | | try self.store(lhs, len_local, Type.usize, self.ptrSize()); |
| 1954 | try self.store(.{ .stack = {} }, len_local, Type.usize, self.ptrSize() + lhs.offset()); |
| 1952 | 1955 | return; |
| 1953 | 1956 | } |
| 1954 | 1957 | }, |
| 1955 | 1958 | .Int => if (ty.intInfo(self.target).bits > 64) { |
| 1959 | try self.emitWValue(lhs); |
| 1956 | 1960 | const lsb = try self.load(rhs, Type.u64, 0); |
| 1961 | try self.store(.{ .stack = {} }, lsb, Type.u64, 0 + lhs.offset()); |
| 1962 | |
| 1963 | try self.emitWValue(lhs); |
| 1957 | 1964 | const msb = try self.load(rhs, Type.u64, 8); |
| 1958 | | try self.store(lhs, lsb, Type.u64, 0); |
| 1959 | | try self.store(lhs, msb, Type.u64, 8); |
| 1965 | try self.store(.{ .stack = {} }, msb, Type.u64, 8 + lhs.offset()); |
| 1960 | 1966 | return; |
| 1961 | 1967 | }, |
| 1962 | 1968 | else => {}, |
| ... | ... | @@ -1995,9 +2001,12 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1995 | 2001 | return new_local; |
| 1996 | 2002 | } |
| 1997 | 2003 | |
| 1998 | | return self.load(operand, ty, 0); |
| 2004 | const stack_loaded = try self.load(operand, ty, 0); |
| 2005 | return stack_loaded.toLocal(self, ty); |
| 1999 | 2006 | } |
| 2000 | 2007 | |
| 2008 | /// Loads an operand from the linear memory section. |
| 2009 | /// NOTE: Leaves the value on the stack. |
| 2001 | 2010 | fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 2002 | 2011 | // load local's value from memory by its stack position |
| 2003 | 2012 | try self.emitWValue(operand); |
| ... | ... | @@ -2015,10 +2024,7 @@ fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 2015 | 2024 | .{ .offset = offset + operand.offset(), .alignment = ty.abiAlignment(self.target) }, |
| 2016 | 2025 | ); |
| 2017 | 2026 | |
| 2018 | | // store the result in a local |
| 2019 | | const result = try self.allocLocal(ty); |
| 2020 | | try self.addLabel(.local_set, result.local); |
| 2021 | | return result; |
| 2027 | return WValue{ .stack = {} }; |
| 2022 | 2028 | } |
| 2023 | 2029 | |
| 2024 | 2030 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -2124,18 +2130,15 @@ fn binOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WVa |
| 2124 | 2130 | return WValue{ .stack = {} }; |
| 2125 | 2131 | } |
| 2126 | 2132 | |
| 2133 | /// Performs a binary operation for 16-bit floats. |
| 2134 | /// NOTE: Leaves the result value on the stack |
| 2127 | 2135 | fn binOpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: Op) InnerError!WValue { |
| 2128 | | const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32); |
| 2129 | | const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32); |
| 2130 | | |
| 2131 | 2136 | const opcode: wasm.Opcode = buildOpcode(.{ .op = op, .valtype1 = .f32, .signedness = .unsigned }); |
| 2132 | | try self.emitWValue(ext_lhs); |
| 2133 | | try self.emitWValue(ext_rhs); |
| 2137 | _ = try self.fpext(lhs, Type.f16, Type.f32); |
| 2138 | _ = try self.fpext(rhs, Type.f16, Type.f32); |
| 2134 | 2139 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2135 | 2140 | |
| 2136 | | // re-use temporary local |
| 2137 | | try self.addLabel(.local_set, ext_lhs.local); |
| 2138 | | return self.fptrunc(ext_lhs, Type.f32, Type.f16); |
| 2141 | return self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); |
| 2139 | 2142 | } |
| 2140 | 2143 | |
| 2141 | 2144 | fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| ... | ... | @@ -2148,12 +2151,12 @@ fn binOpBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerErr |
| 2148 | 2151 | } |
| 2149 | 2152 | |
| 2150 | 2153 | const result = try self.allocStack(ty); |
| 2151 | | const lhs_high_bit = try self.load(lhs, Type.u64, 0); |
| 2154 | const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 2155 | const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 2156 | const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); |
| 2157 | |
| 2152 | 2158 | const lhs_low_bit = try self.load(lhs, Type.u64, 8); |
| 2153 | | const rhs_high_bit = try self.load(rhs, Type.u64, 0); |
| 2154 | 2159 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 2155 | | |
| 2156 | | const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); |
| 2157 | 2160 | const low_op_res = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op); |
| 2158 | 2161 | |
| 2159 | 2162 | const lt = if (op == .add) blk: { |
| ... | ... | @@ -2204,14 +2207,14 @@ fn wrapOperand(self: *Self, operand: WValue, ty: Type) InnerError!WValue { |
| 2204 | 2207 | |
| 2205 | 2208 | if (wasm_bits == 128) { |
| 2206 | 2209 | assert(operand != .stack); |
| 2207 | | const msb = try self.load(operand, Type.u64, 0); |
| 2208 | 2210 | const lsb = try self.load(operand, Type.u64, 8); |
| 2209 | 2211 | |
| 2210 | 2212 | const result_ptr = try self.allocStack(ty); |
| 2211 | | try self.store(result_ptr, lsb, Type.u64, 8); |
| 2213 | try self.emitWValue(result_ptr); |
| 2214 | try self.store(.{ .stack = {} }, lsb, Type.u64, 8 + result_ptr.offset()); |
| 2212 | 2215 | const result = (@as(u64, 1) << @intCast(u6, 64 - (wasm_bits - bitsize))) - 1; |
| 2213 | 2216 | try self.emitWValue(result_ptr); |
| 2214 | | try self.emitWValue(msb); |
| 2217 | _ = try self.load(operand, Type.u64, 0); |
| 2215 | 2218 | try self.addImm64(result); |
| 2216 | 2219 | try self.addTag(.i64_and); |
| 2217 | 2220 | try self.addMemArg(.i64_store, .{ .offset = result_ptr.offset(), .alignment = 8 }); |
| ... | ... | @@ -2692,10 +2695,9 @@ fn cmp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOper |
| 2692 | 2695 | return WValue{ .stack = {} }; |
| 2693 | 2696 | } |
| 2694 | 2697 | |
| 2698 | /// Compares 16-bit floats |
| 2699 | /// NOTE: The result value remains on top of the stack. |
| 2695 | 2700 | fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperator) InnerError!WValue { |
| 2696 | | const ext_lhs = try self.fpext(lhs, Type.f16, Type.f32); |
| 2697 | | const ext_rhs = try self.fpext(rhs, Type.f16, Type.f32); |
| 2698 | | |
| 2699 | 2701 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 2700 | 2702 | .op = switch (op) { |
| 2701 | 2703 | .lt => .lt, |
| ... | ... | @@ -2708,8 +2710,8 @@ fn cmpFloat16(self: *Self, lhs: WValue, rhs: WValue, op: std.math.CompareOperato |
| 2708 | 2710 | .valtype1 = .f32, |
| 2709 | 2711 | .signedness = .unsigned, |
| 2710 | 2712 | }); |
| 2711 | | try self.emitWValue(ext_lhs); |
| 2712 | | try self.emitWValue(ext_rhs); |
| 2713 | _ = try self.fpext(lhs, Type.f16, Type.f32); |
| 2714 | _ = try self.fpext(rhs, Type.f16, Type.f32); |
| 2713 | 2715 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 2714 | 2716 | |
| 2715 | 2717 | return WValue{ .stack = {} }; |
| ... | ... | @@ -2781,13 +2783,15 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2781 | 2783 | }, |
| 2782 | 2784 | 128 => { |
| 2783 | 2785 | const result_ptr = try self.allocStack(operand_ty); |
| 2786 | try self.emitWValue(result_ptr); |
| 2784 | 2787 | const msb = try self.load(operand, Type.u64, 0); |
| 2785 | | const lsb = try self.load(operand, Type.u64, 8); |
| 2788 | const msb_xor = try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); |
| 2789 | try self.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset()); |
| 2786 | 2790 | |
| 2787 | | const msb_xor = try (try self.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); |
| 2788 | | const lsb_xor = try (try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor)).toLocal(self, operand_ty); |
| 2789 | | try self.store(result_ptr, msb_xor, Type.u64, 0); |
| 2790 | | try self.store(result_ptr, lsb_xor, Type.u64, 8); |
| 2791 | try self.emitWValue(result_ptr); |
| 2792 | const lsb = try self.load(operand, Type.u64, 8); |
| 2793 | const lsb_xor = try self.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor); |
| 2794 | try self.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset()); |
| 2791 | 2795 | return result_ptr; |
| 2792 | 2796 | }, |
| 2793 | 2797 | else => unreachable, |
| ... | ... | @@ -2875,7 +2879,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2875 | 2879 | } |
| 2876 | 2880 | } |
| 2877 | 2881 | |
| 2878 | | return self.load(operand, field_ty, offset); |
| 2882 | const field = try self.load(operand, field_ty, offset); |
| 2883 | return field.toLocal(self, field_ty); |
| 2879 | 2884 | } |
| 2880 | 2885 | |
| 2881 | 2886 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3085,7 +3090,9 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) |
| 3085 | 3090 | if (op_is_ptr or isByRef(payload_ty, self.target)) { |
| 3086 | 3091 | return self.buildPointerOffset(operand, pl_offset, .new); |
| 3087 | 3092 | } |
| 3088 | | return self.load(operand, payload_ty, pl_offset); |
| 3093 | |
| 3094 | const payload = try self.load(operand, payload_ty, pl_offset); |
| 3095 | return payload.toLocal(self, payload_ty); |
| 3089 | 3096 | } |
| 3090 | 3097 | |
| 3091 | 3098 | fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!WValue { |
| ... | ... | @@ -3105,7 +3112,8 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index, op_is_ptr: bool) In |
| 3105 | 3112 | return operand; |
| 3106 | 3113 | } |
| 3107 | 3114 | |
| 3108 | | return self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target))); |
| 3115 | const error_val = try self.load(operand, Type.anyerror, @intCast(u32, errUnionErrorOffset(payload_ty, self.target))); |
| 3116 | return error_val.toLocal(self, Type.anyerror); |
| 3109 | 3117 | } |
| 3110 | 3118 | |
| 3111 | 3119 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3235,9 +3243,12 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: en |
| 3235 | 3243 | |
| 3236 | 3244 | const op_ty = self.air.typeOf(un_op); |
| 3237 | 3245 | const optional_ty = if (op_kind == .ptr) op_ty.childType() else op_ty; |
| 3238 | | return self.isNull(operand, optional_ty, opcode); |
| 3246 | const is_null = try self.isNull(operand, optional_ty, opcode); |
| 3247 | return is_null.toLocal(self, optional_ty); |
| 3239 | 3248 | } |
| 3240 | 3249 | |
| 3250 | /// For a given type and operand, checks if it's considered `null`. |
| 3251 | /// NOTE: Leaves the result on the stack |
| 3241 | 3252 | fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { |
| 3242 | 3253 | try self.emitWValue(operand); |
| 3243 | 3254 | if (!optional_ty.optionalReprIsPayload()) { |
| ... | ... | @@ -3254,9 +3265,7 @@ fn isNull(self: *Self, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) |
| 3254 | 3265 | try self.addImm32(0); |
| 3255 | 3266 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 3256 | 3267 | |
| 3257 | | const is_null_tmp = try self.allocLocal(Type.initTag(.i32)); |
| 3258 | | try self.addLabel(.local_set, is_null_tmp.local); |
| 3259 | | return is_null_tmp; |
| 3268 | return WValue{ .stack = {} }; |
| 3260 | 3269 | } |
| 3261 | 3270 | |
| 3262 | 3271 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3274,7 +3283,8 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3274 | 3283 | return self.buildPointerOffset(operand, offset, .new); |
| 3275 | 3284 | } |
| 3276 | 3285 | |
| 3277 | | return self.load(operand, payload_ty, @intCast(u32, offset)); |
| 3286 | const payload = try self.load(operand, payload_ty, @intCast(u32, offset)); |
| 3287 | return payload.toLocal(self, payload_ty); |
| 3278 | 3288 | } |
| 3279 | 3289 | |
| 3280 | 3290 | fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3377,7 +3387,8 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3377 | 3387 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3378 | 3388 | const operand = try self.resolveInst(ty_op.operand); |
| 3379 | 3389 | |
| 3380 | | return self.load(operand, Type.usize, self.ptrSize()); |
| 3390 | const len = try self.load(operand, Type.usize, self.ptrSize()); |
| 3391 | return len.toLocal(self, Type.usize); |
| 3381 | 3392 | } |
| 3382 | 3393 | |
| 3383 | 3394 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3391,8 +3402,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3391 | 3402 | const elem_size = elem_ty.abiSize(self.target); |
| 3392 | 3403 | |
| 3393 | 3404 | // load pointer onto stack |
| 3394 | | const slice_ptr = try self.load(slice, Type.usize, 0); |
| 3395 | | try self.addLabel(.local_get, slice_ptr.local); |
| 3405 | _ = try self.load(slice, Type.usize, 0); |
| 3396 | 3406 | |
| 3397 | 3407 | // calculate index into slice |
| 3398 | 3408 | try self.emitWValue(index); |
| ... | ... | @@ -3406,7 +3416,9 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3406 | 3416 | if (isByRef(elem_ty, self.target)) { |
| 3407 | 3417 | return result; |
| 3408 | 3418 | } |
| 3409 | | return self.load(result, elem_ty, 0); |
| 3419 | |
| 3420 | const elem_val = try self.load(result, elem_ty, 0); |
| 3421 | return elem_val.toLocal(self, elem_ty); |
| 3410 | 3422 | } |
| 3411 | 3423 | |
| 3412 | 3424 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3419,8 +3431,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3419 | 3431 | const slice = try self.resolveInst(bin_op.lhs); |
| 3420 | 3432 | const index = try self.resolveInst(bin_op.rhs); |
| 3421 | 3433 | |
| 3422 | | const slice_ptr = try self.load(slice, Type.usize, 0); |
| 3423 | | try self.addLabel(.local_get, slice_ptr.local); |
| 3434 | _ = try self.load(slice, Type.usize, 0); |
| 3424 | 3435 | |
| 3425 | 3436 | // calculate index into slice |
| 3426 | 3437 | try self.emitWValue(index); |
| ... | ... | @@ -3428,7 +3439,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3428 | 3439 | try self.addTag(.i32_mul); |
| 3429 | 3440 | try self.addTag(.i32_add); |
| 3430 | 3441 | |
| 3431 | | const result = try self.allocLocal(Type.initTag(.i32)); |
| 3442 | const result = try self.allocLocal(Type.i32); |
| 3432 | 3443 | try self.addLabel(.local_set, result.local); |
| 3433 | 3444 | return result; |
| 3434 | 3445 | } |
| ... | ... | @@ -3437,7 +3448,8 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3437 | 3448 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 3438 | 3449 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3439 | 3450 | const operand = try self.resolveInst(ty_op.operand); |
| 3440 | | return self.load(operand, Type.usize, 0); |
| 3451 | const ptr = try self.load(operand, Type.usize, 0); |
| 3452 | return ptr.toLocal(self, Type.usize); |
| 3441 | 3453 | } |
| 3442 | 3454 | |
| 3443 | 3455 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3511,8 +3523,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3511 | 3523 | |
| 3512 | 3524 | // load pointer onto the stack |
| 3513 | 3525 | if (ptr_ty.isSlice()) { |
| 3514 | | const ptr_local = try self.load(ptr, Type.usize, 0); |
| 3515 | | try self.addLabel(.local_get, ptr_local.local); |
| 3526 | _ = try self.load(ptr, Type.usize, 0); |
| 3516 | 3527 | } else { |
| 3517 | 3528 | try self.lowerToStack(ptr); |
| 3518 | 3529 | } |
| ... | ... | @@ -3528,7 +3539,9 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3528 | 3539 | if (isByRef(elem_ty, self.target)) { |
| 3529 | 3540 | return result; |
| 3530 | 3541 | } |
| 3531 | | return self.load(result, elem_ty, 0); |
| 3542 | |
| 3543 | const elem_val = try self.load(result, elem_ty, 0); |
| 3544 | return elem_val.toLocal(self, elem_ty); |
| 3532 | 3545 | } |
| 3533 | 3546 | |
| 3534 | 3547 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3544,8 +3557,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3544 | 3557 | |
| 3545 | 3558 | // load pointer onto the stack |
| 3546 | 3559 | if (ptr_ty.isSlice()) { |
| 3547 | | const ptr_local = try self.load(ptr, Type.usize, 0); |
| 3548 | | try self.addLabel(.local_get, ptr_local.local); |
| 3560 | _ = try self.load(ptr, Type.usize, 0); |
| 3549 | 3561 | } else { |
| 3550 | 3562 | try self.lowerToStack(ptr); |
| 3551 | 3563 | } |
| ... | ... | @@ -3556,7 +3568,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3556 | 3568 | try self.addTag(.i32_mul); |
| 3557 | 3569 | try self.addTag(.i32_add); |
| 3558 | 3570 | |
| 3559 | | const result = try self.allocLocal(Type.initTag(.i32)); |
| 3571 | const result = try self.allocLocal(Type.i32); |
| 3560 | 3572 | try self.addLabel(.local_set, result.local); |
| 3561 | 3573 | return result; |
| 3562 | 3574 | } |
| ... | ... | @@ -3707,7 +3719,8 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3707 | 3719 | if (isByRef(elem_ty, self.target)) { |
| 3708 | 3720 | return result; |
| 3709 | 3721 | } |
| 3710 | | return self.load(result, elem_ty, 0); |
| 3722 | const elem_val = try self.load(result, elem_ty, 0); |
| 3723 | return elem_val.toLocal(self, elem_ty); |
| 3711 | 3724 | } |
| 3712 | 3725 | |
| 3713 | 3726 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -3929,24 +3942,18 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std |
| 3929 | 3942 | const payload_ty = operand_ty.optionalChild(&buf); |
| 3930 | 3943 | const offset = @intCast(u32, operand_ty.abiSize(self.target) - payload_ty.abiSize(self.target)); |
| 3931 | 3944 | |
| 3932 | | const lhs_is_null = try self.isNull(lhs, operand_ty, .i32_eq); |
| 3933 | | const rhs_is_null = try self.isNull(rhs, operand_ty, .i32_eq); |
| 3934 | | |
| 3935 | 3945 | // We store the final result in here that will be validated |
| 3936 | 3946 | // if the optional is truly equal. |
| 3937 | 3947 | const result = try self.allocLocal(Type.initTag(.i32)); |
| 3938 | 3948 | |
| 3939 | 3949 | try self.startBlock(.block, wasm.block_empty); |
| 3940 | | try self.emitWValue(lhs_is_null); |
| 3941 | | try self.emitWValue(rhs_is_null); |
| 3950 | _ = try self.isNull(lhs, operand_ty, .i32_eq); |
| 3951 | _ = try self.isNull(rhs, operand_ty, .i32_eq); |
| 3942 | 3952 | try self.addTag(.i32_ne); // inverse so we can exit early |
| 3943 | 3953 | try self.addLabel(.br_if, 0); |
| 3944 | 3954 | |
| 3945 | | const lhs_pl = try self.load(lhs, payload_ty, offset); |
| 3946 | | const rhs_pl = try self.load(rhs, payload_ty, offset); |
| 3947 | | |
| 3948 | | try self.emitWValue(lhs_pl); |
| 3949 | | try self.emitWValue(rhs_pl); |
| 3955 | _ = try self.load(lhs, payload_ty, offset); |
| 3956 | _ = try self.load(rhs, payload_ty, offset); |
| 3950 | 3957 | const opcode = buildOpcode(.{ .op = .ne, .valtype1 = typeToValtype(payload_ty, self.target) }); |
| 3951 | 3958 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 3952 | 3959 | try self.addLabel(.br_if, 0); |
| ... | ... | @@ -3973,14 +3980,14 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma |
| 3973 | 3980 | return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits}); |
| 3974 | 3981 | } |
| 3975 | 3982 | |
| 3976 | | const lhs_high_bit = try self.load(lhs, Type.u64, 0); |
| 3977 | | const lhs_low_bit = try self.load(lhs, Type.u64, 8); |
| 3978 | | const rhs_high_bit = try self.load(rhs, Type.u64, 0); |
| 3979 | | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 3983 | const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 3984 | const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 3980 | 3985 | |
| 3981 | 3986 | switch (op) { |
| 3982 | 3987 | .eq, .neq => { |
| 3983 | 3988 | const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor); |
| 3989 | const lhs_low_bit = try self.load(lhs, Type.u64, 8); |
| 3990 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 3984 | 3991 | const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor); |
| 3985 | 3992 | const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or"); |
| 3986 | 3993 | |
| ... | ... | @@ -3993,6 +4000,8 @@ fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.ma |
| 3993 | 4000 | else => { |
| 3994 | 4001 | const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64; |
| 3995 | 4002 | // leave those value on top of the stack for '.select' |
| 4003 | const lhs_low_bit = try self.load(lhs, Type.u64, 8); |
| 4004 | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 3996 | 4005 | _ = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op); |
| 3997 | 4006 | _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op); |
| 3998 | 4007 | _ = try self.cmp(lhs_high_bit, rhs_high_bit, ty, .eq); |
| ... | ... | @@ -4040,7 +4049,8 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4040 | 4049 | const offset = if (layout.tag_align < layout.payload_align) blk: { |
| 4041 | 4050 | break :blk @intCast(u32, layout.payload_size); |
| 4042 | 4051 | } else @as(u32, 0); |
| 4043 | | return self.load(operand, tag_ty, offset); |
| 4052 | const tag = try self.load(operand, tag_ty, offset); |
| 4053 | return tag.toLocal(self, tag_ty); |
| 4044 | 4054 | } |
| 4045 | 4055 | |
| 4046 | 4056 | fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -4050,19 +4060,20 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4050 | 4060 | const dest_ty = self.air.typeOfIndex(inst); |
| 4051 | 4061 | const operand = try self.resolveInst(ty_op.operand); |
| 4052 | 4062 | |
| 4053 | | return self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty); |
| 4063 | const extended = try self.fpext(operand, self.air.typeOf(ty_op.operand), dest_ty); |
| 4064 | return extended.toLocal(self, dest_ty); |
| 4054 | 4065 | } |
| 4055 | 4066 | |
| 4067 | /// Extends a float from a given `Type` to a larger wanted `Type` |
| 4068 | /// NOTE: Leaves the result on the stack |
| 4056 | 4069 | fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 4057 | 4070 | const given_bits = given.floatBits(self.target); |
| 4058 | 4071 | const wanted_bits = wanted.floatBits(self.target); |
| 4059 | 4072 | |
| 4060 | 4073 | if (wanted_bits == 64 and given_bits == 32) { |
| 4061 | | const result = try self.allocLocal(wanted); |
| 4062 | 4074 | try self.emitWValue(operand); |
| 4063 | 4075 | try self.addTag(.f64_promote_f32); |
| 4064 | | try self.addLabel(.local_set, result.local); |
| 4065 | | return result; |
| 4076 | return WValue{ .stack = {} }; |
| 4066 | 4077 | } else if (given_bits == 16) { |
| 4067 | 4078 | // call __extendhfsf2(f16) f32 |
| 4068 | 4079 | const f32_result = try self.callIntrinsic( |
| ... | ... | @@ -4076,11 +4087,9 @@ fn fpext(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WVa |
| 4076 | 4087 | return f32_result; |
| 4077 | 4088 | } |
| 4078 | 4089 | if (wanted_bits == 64) { |
| 4079 | | const result = try self.allocLocal(wanted); |
| 4080 | 4090 | try self.emitWValue(f32_result); |
| 4081 | 4091 | try self.addTag(.f64_promote_f32); |
| 4082 | | try self.addLabel(.local_set, result.local); |
| 4083 | | return result; |
| 4092 | return WValue{ .stack = {} }; |
| 4084 | 4093 | } |
| 4085 | 4094 | return self.fail("TODO: Implement 'fpext' for floats with bitsize: {d}", .{wanted_bits}); |
| 4086 | 4095 | } else { |
| ... | ... | @@ -4095,26 +4104,25 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4095 | 4104 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 4096 | 4105 | const dest_ty = self.air.typeOfIndex(inst); |
| 4097 | 4106 | const operand = try self.resolveInst(ty_op.operand); |
| 4098 | | return self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty); |
| 4107 | const trunc = try self.fptrunc(operand, self.air.typeOf(ty_op.operand), dest_ty); |
| 4108 | return trunc.toLocal(self, dest_ty); |
| 4099 | 4109 | } |
| 4100 | 4110 | |
| 4111 | /// Truncates a float from a given `Type` to its wanted `Type` |
| 4112 | /// NOTE: The result value remains on the stack |
| 4101 | 4113 | fn fptrunc(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!WValue { |
| 4102 | 4114 | const given_bits = given.floatBits(self.target); |
| 4103 | 4115 | const wanted_bits = wanted.floatBits(self.target); |
| 4104 | 4116 | |
| 4105 | 4117 | if (wanted_bits == 32 and given_bits == 64) { |
| 4106 | | const result = try self.allocLocal(wanted); |
| 4107 | 4118 | try self.emitWValue(operand); |
| 4108 | 4119 | try self.addTag(.f32_demote_f64); |
| 4109 | | try self.addLabel(.local_set, result.local); |
| 4110 | | return result; |
| 4120 | return WValue{ .stack = {} }; |
| 4111 | 4121 | } else if (wanted_bits == 16) { |
| 4112 | 4122 | const op: WValue = if (given_bits == 64) blk: { |
| 4113 | | const tmp = try self.allocLocal(Type.f32); |
| 4114 | 4123 | try self.emitWValue(operand); |
| 4115 | 4124 | try self.addTag(.f32_demote_f64); |
| 4116 | | try self.addLabel(.local_set, tmp.local); |
| 4117 | | break :blk tmp; |
| 4125 | break :blk WValue{ .stack = {} }; |
| 4118 | 4126 | } else operand; |
| 4119 | 4127 | |
| 4120 | 4128 | // call __truncsfhf2(f32) f16 |
| ... | ... | @@ -4199,12 +4207,9 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4199 | 4207 | |
| 4200 | 4208 | switch (wasm_bits) { |
| 4201 | 4209 | 128 => { |
| 4202 | | const msb = try self.load(operand, Type.u64, 0); |
| 4203 | | const lsb = try self.load(operand, Type.u64, 8); |
| 4204 | | |
| 4205 | | try self.emitWValue(msb); |
| 4210 | _ = try self.load(operand, Type.u64, 0); |
| 4206 | 4211 | try self.addTag(.i64_popcnt); |
| 4207 | | try self.emitWValue(lsb); |
| 4212 | _ = try self.load(operand, Type.u64, 8); |
| 4208 | 4213 | try self.addTag(.i64_popcnt); |
| 4209 | 4214 | try self.addTag(.i64_add); |
| 4210 | 4215 | try self.addTag(.i32_wrap_i64); |
| ... | ... | @@ -4351,10 +4356,10 @@ fn airAddSubWithOverflowBigInt(self: *Self, lhs: WValue, rhs: WValue, ty: Type, |
| 4351 | 4356 | return self.fail("TODO: Implement @{{add/sub}}WithOverflow for integer bitsize '{d}'", .{int_info.bits}); |
| 4352 | 4357 | } |
| 4353 | 4358 | |
| 4354 | | const lhs_high_bit = try self.load(lhs, Type.u64, 0); |
| 4355 | | const lhs_low_bit = try self.load(lhs, Type.u64, 8); |
| 4356 | | const rhs_high_bit = try self.load(rhs, Type.u64, 0); |
| 4357 | | const rhs_low_bit = try self.load(rhs, Type.u64, 8); |
| 4359 | const lhs_high_bit = try (try self.load(lhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 4360 | const lhs_low_bit = try (try self.load(lhs, Type.u64, 8)).toLocal(self, Type.u64); |
| 4361 | const rhs_high_bit = try (try self.load(rhs, Type.u64, 0)).toLocal(self, Type.u64); |
| 4362 | const rhs_low_bit = try (try self.load(rhs, Type.u64, 8)).toLocal(self, Type.u64); |
| 4358 | 4363 | |
| 4359 | 4364 | const low_op_res = try (try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, op)).toLocal(self, Type.u64); |
| 4360 | 4365 | const high_op_res = try (try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, op)).toLocal(self, Type.u64); |
| ... | ... | @@ -4563,9 +4568,9 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4563 | 4568 | const rhs = try self.resolveInst(bin_op.rhs); |
| 4564 | 4569 | |
| 4565 | 4570 | if (ty.floatBits(self.target) == 16) { |
| 4566 | | const addend_ext = try self.fpext(addend, ty, Type.f32); |
| 4567 | | const lhs_ext = try self.fpext(lhs, ty, Type.f32); |
| 4568 | 4571 | const rhs_ext = try self.fpext(rhs, ty, Type.f32); |
| 4572 | const lhs_ext = try self.fpext(lhs, ty, Type.f32); |
| 4573 | const addend_ext = try self.fpext(addend, ty, Type.f32); |
| 4569 | 4574 | // call to compiler-rt `fn fmaf(f32, f32, f32) f32` |
| 4570 | 4575 | const result = try self.callIntrinsic( |
| 4571 | 4576 | "fmaf", |
| ... | ... | @@ -4573,7 +4578,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4573 | 4578 | Type.f32, |
| 4574 | 4579 | &.{ rhs_ext, lhs_ext, addend_ext }, |
| 4575 | 4580 | ); |
| 4576 | | return try self.fptrunc(result, Type.f32, ty); |
| 4581 | return try (try self.fptrunc(result, Type.f32, ty)).toLocal(self, ty); |
| 4577 | 4582 | } |
| 4578 | 4583 | |
| 4579 | 4584 | const mul_result = try self.binOp(lhs, rhs, ty, .mul); |
| ... | ... | @@ -4606,12 +4611,11 @@ fn airClz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4606 | 4611 | try self.addTag(.i32_wrap_i64); |
| 4607 | 4612 | }, |
| 4608 | 4613 | 128 => { |
| 4609 | | const msb = try self.load(operand, Type.u64, 0); |
| 4610 | | const lsb = try self.load(operand, Type.u64, 8); |
| 4614 | const lsb = try (try self.load(operand, Type.u64, 8)).toLocal(self, Type.u64); |
| 4611 | 4615 | |
| 4612 | 4616 | try self.emitWValue(lsb); |
| 4613 | 4617 | try self.addTag(.i64_clz); |
| 4614 | | try self.emitWValue(msb); |
| 4618 | _ = try self.load(operand, Type.u64, 0); |
| 4615 | 4619 | try self.addTag(.i64_clz); |
| 4616 | 4620 | try self.emitWValue(.{ .imm64 = 64 }); |
| 4617 | 4621 | try self.addTag(.i64_add); |
| ... | ... | @@ -4667,12 +4671,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4667 | 4671 | try self.addTag(.i32_wrap_i64); |
| 4668 | 4672 | }, |
| 4669 | 4673 | 128 => { |
| 4670 | | const msb = try self.load(operand, Type.u64, 0); |
| 4671 | | const lsb = try self.load(operand, Type.u64, 8); |
| 4674 | const msb = try (try self.load(operand, Type.u64, 0)).toLocal(self, Type.u64); |
| 4672 | 4675 | |
| 4673 | 4676 | try self.emitWValue(msb); |
| 4674 | 4677 | try self.addTag(.i64_ctz); |
| 4675 | | try self.emitWValue(lsb); |
| 4678 | _ = try self.load(operand, Type.u64, 8); |
| 4676 | 4679 | if (wasm_bits != int_info.bits) { |
| 4677 | 4680 | try self.addImm64(@as(u64, 1) << @intCast(u6, int_info.bits - 64)); |
| 4678 | 4681 | try self.addTag(.i64_or); |
| ... | ... | @@ -4810,7 +4813,8 @@ fn lowerTry( |
| 4810 | 4813 | if (isByRef(pl_ty, self.target)) { |
| 4811 | 4814 | return buildPointerOffset(self, err_union, pl_offset, .new); |
| 4812 | 4815 | } |
| 4813 | | return self.load(err_union, pl_ty, pl_offset); |
| 4816 | const payload = try self.load(err_union, pl_ty, pl_offset); |
| 4817 | return payload.toLocal(self, pl_ty); |
| 4814 | 4818 | } |
| 4815 | 4819 | |
| 4816 | 4820 | fn airByteSwap(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -4972,9 +4976,7 @@ fn airDivFloor(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 4972 | 4976 | } |
| 4973 | 4977 | |
| 4974 | 4978 | if (is_f16) { |
| 4975 | | // we can re-use temporary local |
| 4976 | | try self.addLabel(.local_set, lhs_operand.local); |
| 4977 | | return self.fptrunc(lhs_operand, Type.f32, Type.f16); |
| 4979 | _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); |
| 4978 | 4980 | } |
| 4979 | 4981 | } |
| 4980 | 4982 | |
| ... | ... | @@ -5066,9 +5068,7 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu |
| 5066 | 5068 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 5067 | 5069 | |
| 5068 | 5070 | if (is_f16) { |
| 5069 | | // re-use temporary to save locals |
| 5070 | | try self.addLabel(.local_set, op_to_lower.local); |
| 5071 | | return self.fptrunc(op_to_lower, Type.f32, Type.f16); |
| 5071 | _ = try self.fptrunc(.{ .stack = {} }, Type.f32, Type.f16); |
| 5072 | 5072 | } |
| 5073 | 5073 | |
| 5074 | 5074 | const result = try self.allocLocal(ty); |
| ... | ... | @@ -5285,6 +5285,8 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 5285 | 5285 | /// Calls a compiler-rt intrinsic by creating an undefined symbol, |
| 5286 | 5286 | /// then lowering the arguments and calling the symbol as a function call. |
| 5287 | 5287 | /// This function call assumes the C-ABI. |
| 5288 | /// Asserts arguments are not stack values when the return value is |
| 5289 | /// passed as the first parameter. |
| 5288 | 5290 | fn callIntrinsic( |
| 5289 | 5291 | self: *Self, |
| 5290 | 5292 | name: []const u8, |
| ... | ... | @@ -5314,6 +5316,7 @@ fn callIntrinsic( |
| 5314 | 5316 | |
| 5315 | 5317 | // Lower all arguments to the stack before we call our function |
| 5316 | 5318 | for (args) |arg, arg_i| { |
| 5319 | assert(!(want_sret_param and arg == .stack)); |
| 5317 | 5320 | assert(param_types[arg_i].hasRuntimeBitsIgnoreComptime()); |
| 5318 | 5321 | try self.lowerArg(.C, param_types[arg_i], arg); |
| 5319 | 5322 | } |