| ... | ... | @@ -2325,14 +2325,19 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void |
| 2325 | 2325 | WValue{ .imm32 = @as(u32, @truncate(mask)) } |
| 2326 | 2326 | else |
| 2327 | 2327 | WValue{ .imm64 = mask }; |
| 2328 | const wrap_mask_val = if (ptr_info.packed_offset.host_size <= 4) |
| 2329 | WValue{ .imm32 = @truncate(~@as(u64, 0) >> @intCast(64 - ty.bitSize(mod))) } |
| 2330 | else |
| 2331 | WValue{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(mod)) }; |
| 2328 | 2332 | |
| 2329 | 2333 | try func.emitWValue(lhs); |
| 2330 | 2334 | const loaded = try func.load(lhs, int_elem_ty, 0); |
| 2331 | 2335 | const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and"); |
| 2332 | 2336 | const extended_value = try func.intcast(rhs, ty, int_elem_ty); |
| 2337 | const masked_value = try func.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and"); |
| 2333 | 2338 | const shifted_value = if (ptr_info.packed_offset.bit_offset > 0) shifted: { |
| 2334 | | break :shifted try func.binOp(extended_value, shift_val, int_elem_ty, .shl); |
| 2335 | | } else extended_value; |
| 2339 | break :shifted try func.binOp(masked_value, shift_val, int_elem_ty, .shl); |
| 2340 | } else masked_value; |
| 2336 | 2341 | const result = try func.binOp(anded, shifted_value, int_elem_ty, .@"or"); |
| 2337 | 2342 | // lhs is still on the stack |
| 2338 | 2343 | try func.store(.stack, result, int_elem_ty, lhs.offset()); |
| ... | ... | @@ -2515,7 +2520,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu |
| 2515 | 2520 | .valtype1 = typeToValtype(ty, mod), |
| 2516 | 2521 | .width = abi_size * 8, |
| 2517 | 2522 | .op = .load, |
| 2518 | | .signedness = .unsigned, |
| 2523 | .signedness = if (ty.isSignedInt(mod)) .signed else .unsigned, |
| 2519 | 2524 | }); |
| 2520 | 2525 | |
| 2521 | 2526 | try func.addMemArg( |
| ... | ... | @@ -2800,10 +2805,7 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2800 | 2805 | switch (wasm_bits) { |
| 2801 | 2806 | 32 => { |
| 2802 | 2807 | try func.emitWValue(operand); |
| 2803 | | if (wasm_bits != int_bits) { |
| 2804 | | try func.addImm32(wasm_bits - int_bits); |
| 2805 | | try func.addTag(.i32_shl); |
| 2806 | | } |
| 2808 | |
| 2807 | 2809 | try func.addImm32(31); |
| 2808 | 2810 | try func.addTag(.i32_shr_s); |
| 2809 | 2811 | |
| ... | ... | @@ -2815,15 +2817,10 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2815 | 2817 | try func.addTag(.i32_xor); |
| 2816 | 2818 | try func.emitWValue(tmp); |
| 2817 | 2819 | try func.addTag(.i32_sub); |
| 2818 | | |
| 2819 | | _ = try func.wrapOperand(.stack, ty); |
| 2820 | 2820 | }, |
| 2821 | 2821 | 64 => { |
| 2822 | 2822 | try func.emitWValue(operand); |
| 2823 | | if (wasm_bits != int_bits) { |
| 2824 | | try func.addImm64(wasm_bits - int_bits); |
| 2825 | | try func.addTag(.i64_shl); |
| 2826 | | } |
| 2823 | |
| 2827 | 2824 | try func.addImm64(63); |
| 2828 | 2825 | try func.addTag(.i64_shr_s); |
| 2829 | 2826 | |
| ... | ... | @@ -2835,8 +2832,6 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2835 | 2832 | try func.addTag(.i64_xor); |
| 2836 | 2833 | try func.emitWValue(tmp); |
| 2837 | 2834 | try func.addTag(.i64_sub); |
| 2838 | | |
| 2839 | | _ = try func.wrapOperand(.stack, ty); |
| 2840 | 2835 | }, |
| 2841 | 2836 | 128 => { |
| 2842 | 2837 | const mask = try func.allocStack(Type.u128); |
| ... | ... | @@ -2844,10 +2839,6 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2844 | 2839 | try func.emitWValue(mask); |
| 2845 | 2840 | |
| 2846 | 2841 | _ = try func.load(operand, Type.u64, 8); |
| 2847 | | if (int_bits != 128) { |
| 2848 | | try func.addImm64(128 - int_bits); |
| 2849 | | try func.addTag(.i64_shl); |
| 2850 | | } |
| 2851 | 2842 | try func.addImm64(63); |
| 2852 | 2843 | try func.addTag(.i64_shr_s); |
| 2853 | 2844 | |
| ... | ... | @@ -2860,9 +2851,8 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2860 | 2851 | |
| 2861 | 2852 | const a = try func.binOpBigInt(operand, mask, Type.u128, .xor); |
| 2862 | 2853 | const b = try func.binOpBigInt(a, mask, Type.u128, .sub); |
| 2863 | | const result = try func.wrapOperand(b, ty); |
| 2864 | 2854 | |
| 2865 | | func.finishAir(inst, result, &.{ty_op.operand}); |
| 2855 | func.finishAir(inst, b, &.{ty_op.operand}); |
| 2866 | 2856 | return; |
| 2867 | 2857 | }, |
| 2868 | 2858 | else => unreachable, |
| ... | ... | @@ -3058,14 +3048,28 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 3058 | 3048 | switch (wasm_bits) { |
| 3059 | 3049 | 32 => { |
| 3060 | 3050 | try func.emitWValue(operand); |
| 3061 | | try func.addImm32((@as(u32, 1) << @intCast(int_bits)) - 1); |
| 3062 | | try func.addTag(.i32_and); |
| 3051 | if (ty.isSignedInt(mod)) { |
| 3052 | try func.addImm32(32 - int_bits); |
| 3053 | try func.addTag(.i32_shl); |
| 3054 | try func.addImm32(32 - int_bits); |
| 3055 | try func.addTag(.i32_shr_s); |
| 3056 | } else { |
| 3057 | try func.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits)); |
| 3058 | try func.addTag(.i32_and); |
| 3059 | } |
| 3063 | 3060 | return .stack; |
| 3064 | 3061 | }, |
| 3065 | 3062 | 64 => { |
| 3066 | 3063 | try func.emitWValue(operand); |
| 3067 | | try func.addImm64((@as(u64, 1) << @intCast(int_bits)) - 1); |
| 3068 | | try func.addTag(.i64_and); |
| 3064 | if (ty.isSignedInt(mod)) { |
| 3065 | try func.addImm64(64 - int_bits); |
| 3066 | try func.addTag(.i64_shl); |
| 3067 | try func.addImm64(64 - int_bits); |
| 3068 | try func.addTag(.i64_shr_s); |
| 3069 | } else { |
| 3070 | try func.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits)); |
| 3071 | try func.addTag(.i64_and); |
| 3072 | } |
| 3069 | 3073 | return .stack; |
| 3070 | 3074 | }, |
| 3071 | 3075 | 128 => { |
| ... | ... | @@ -3078,8 +3082,15 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 3078 | 3082 | |
| 3079 | 3083 | try func.emitWValue(result); |
| 3080 | 3084 | _ = try func.load(operand, Type.u64, 8); |
| 3081 | | try func.addImm64((@as(u64, 1) << @intCast(int_bits - 64)) - 1); |
| 3082 | | try func.addTag(.i64_and); |
| 3085 | if (ty.isSignedInt(mod)) { |
| 3086 | try func.addImm64(128 - int_bits); |
| 3087 | try func.addTag(.i64_shl); |
| 3088 | try func.addImm64(128 - int_bits); |
| 3089 | try func.addTag(.i64_shr_s); |
| 3090 | } else { |
| 3091 | try func.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits)); |
| 3092 | try func.addTag(.i64_and); |
| 3093 | } |
| 3083 | 3094 | try func.store(.stack, .stack, Type.u64, result.offset() + 8); |
| 3084 | 3095 | |
| 3085 | 3096 | return result; |
| ... | ... | @@ -3201,22 +3212,6 @@ fn lowerDeclRefValue(func: *CodeGen, decl_index: InternPool.DeclIndex, offset: u |
| 3201 | 3212 | } else return WValue{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } }; |
| 3202 | 3213 | } |
| 3203 | 3214 | |
| 3204 | | /// Converts a signed integer to its 2's complement form and returns |
| 3205 | | /// an unsigned integer instead. |
| 3206 | | /// Asserts bitsize <= 64 |
| 3207 | | fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { |
| 3208 | | const T = @TypeOf(value); |
| 3209 | | comptime assert(@typeInfo(T) == .Int); |
| 3210 | | comptime assert(@typeInfo(T).Int.signedness == .signed); |
| 3211 | | assert(bits <= 64); |
| 3212 | | const WantedT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); |
| 3213 | | if (value >= 0) return @as(WantedT, @bitCast(value)); |
| 3214 | | const max_value = @as(u64, @intCast((@as(u65, 1) << bits) - 1)); |
| 3215 | | const flipped = @as(T, @intCast((~-@as(i65, value)) + 1)); |
| 3216 | | const result = @as(WantedT, @bitCast(flipped)) & max_value; |
| 3217 | | return @as(WantedT, @intCast(result)); |
| 3218 | | } |
| 3219 | | |
| 3220 | 3215 | /// Asserts that `isByRef` returns `false` for `ty`. |
| 3221 | 3216 | fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3222 | 3217 | const mod = func.bin_file.base.comp.module.?; |
| ... | ... | @@ -3268,18 +3263,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { |
| 3268 | 3263 | const int_info = ty.intInfo(mod); |
| 3269 | 3264 | switch (int_info.signedness) { |
| 3270 | 3265 | .signed => switch (int_info.bits) { |
| 3271 | | 0...32 => return WValue{ .imm32 = @as(u32, @intCast(toTwosComplement( |
| 3272 | | val.toSignedInt(mod), |
| 3273 | | @as(u6, @intCast(int_info.bits)), |
| 3274 | | ))) }, |
| 3275 | | 33...64 => return WValue{ .imm64 = toTwosComplement( |
| 3276 | | val.toSignedInt(mod), |
| 3277 | | @as(u7, @intCast(int_info.bits)), |
| 3278 | | ) }, |
| 3266 | 0...32 => return WValue{ .imm32 = @bitCast(@as(i32, @intCast(val.toSignedInt(mod)))) }, |
| 3267 | 33...64 => return WValue{ .imm64 = @bitCast(val.toSignedInt(mod)) }, |
| 3279 | 3268 | else => unreachable, |
| 3280 | 3269 | }, |
| 3281 | 3270 | .unsigned => switch (int_info.bits) { |
| 3282 | | 0...32 => return WValue{ .imm32 = @as(u32, @intCast(val.toUnsignedInt(mod))) }, |
| 3271 | 0...32 => return WValue{ .imm32 = @intCast(val.toUnsignedInt(mod)) }, |
| 3283 | 3272 | 33...64 => return WValue{ .imm64 = val.toUnsignedInt(mod) }, |
| 3284 | 3273 | else => unreachable, |
| 3285 | 3274 | }, |
| ... | ... | @@ -3618,29 +3607,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO |
| 3618 | 3607 | // incase of an actual integer, we emit the correct signedness |
| 3619 | 3608 | break :blk ty.intInfo(mod).signedness; |
| 3620 | 3609 | }; |
| 3621 | | const extend_sign = blk: { |
| 3622 | | // do we need to extend the sign bit? |
| 3623 | | if (signedness != .signed) break :blk false; |
| 3624 | | if (op == .eq or op == .neq) break :blk false; |
| 3625 | | const int_bits = ty.intInfo(mod).bits; |
| 3626 | | const wasm_bits = toWasmBits(int_bits) orelse unreachable; |
| 3627 | | break :blk (wasm_bits != int_bits); |
| 3628 | | }; |
| 3629 | | |
| 3630 | | const lhs_wasm = if (extend_sign) |
| 3631 | | try func.signExtendInt(lhs, ty) |
| 3632 | | else |
| 3633 | | lhs; |
| 3634 | | |
| 3635 | | const rhs_wasm = if (extend_sign) |
| 3636 | | try func.signExtendInt(rhs, ty) |
| 3637 | | else |
| 3638 | | rhs; |
| 3639 | 3610 | |
| 3640 | 3611 | // ensure that when we compare pointers, we emit |
| 3641 | 3612 | // the true pointer of a stack value, rather than the stack pointer. |
| 3642 | | try func.lowerToStack(lhs_wasm); |
| 3643 | | try func.lowerToStack(rhs_wasm); |
| 3613 | try func.lowerToStack(lhs); |
| 3614 | try func.lowerToStack(rhs); |
| 3644 | 3615 | |
| 3645 | 3616 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 3646 | 3617 | .valtype1 = typeToValtype(ty, mod), |
| ... | ... | @@ -3812,25 +3783,44 @@ fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3812 | 3783 | } |
| 3813 | 3784 | |
| 3814 | 3785 | fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3786 | const mod = func.bin_file.base.comp.module.?; |
| 3815 | 3787 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3788 | const operand = try func.resolveInst(ty_op.operand); |
| 3789 | const wanted_ty = func.typeOfIndex(inst); |
| 3790 | const given_ty = func.typeOf(ty_op.operand); |
| 3791 | |
| 3792 | const bit_size = given_ty.bitSize(mod); |
| 3793 | const needs_wrapping = (given_ty.isSignedInt(mod) != wanted_ty.isSignedInt(mod)) and |
| 3794 | bit_size != 32 and bit_size != 64 and bit_size != 128; |
| 3795 | |
| 3816 | 3796 | const result = result: { |
| 3817 | | const operand = try func.resolveInst(ty_op.operand); |
| 3818 | | const wanted_ty = func.typeOfIndex(inst); |
| 3819 | | const given_ty = func.typeOf(ty_op.operand); |
| 3820 | 3797 | if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) { |
| 3821 | 3798 | const bitcast_result = try func.bitcast(wanted_ty, given_ty, operand); |
| 3822 | 3799 | break :result try bitcast_result.toLocal(func, wanted_ty); |
| 3823 | 3800 | } |
| 3824 | | const mod = func.bin_file.base.comp.module.?; |
| 3801 | |
| 3825 | 3802 | if (isByRef(given_ty, mod) and !isByRef(wanted_ty, mod)) { |
| 3826 | 3803 | const loaded_memory = try func.load(operand, wanted_ty, 0); |
| 3827 | | break :result try loaded_memory.toLocal(func, wanted_ty); |
| 3804 | if (needs_wrapping) { |
| 3805 | break :result try (try func.wrapOperand(loaded_memory, wanted_ty)).toLocal(func, wanted_ty); |
| 3806 | } else { |
| 3807 | break :result try loaded_memory.toLocal(func, wanted_ty); |
| 3808 | } |
| 3828 | 3809 | } |
| 3829 | 3810 | if (!isByRef(given_ty, mod) and isByRef(wanted_ty, mod)) { |
| 3830 | 3811 | const stack_memory = try func.allocStack(wanted_ty); |
| 3831 | 3812 | try func.store(stack_memory, operand, given_ty, 0); |
| 3832 | | break :result stack_memory; |
| 3813 | if (needs_wrapping) { |
| 3814 | break :result try (try func.wrapOperand(stack_memory, wanted_ty)).toLocal(func, wanted_ty); |
| 3815 | } else { |
| 3816 | break :result stack_memory; |
| 3817 | } |
| 3833 | 3818 | } |
| 3819 | |
| 3820 | if (needs_wrapping) { |
| 3821 | break :result try (try func.wrapOperand(operand, wanted_ty)).toLocal(func, wanted_ty); |
| 3822 | } |
| 3823 | |
| 3834 | 3824 | break :result func.reuseOperand(ty_op.operand, operand); |
| 3835 | 3825 | }; |
| 3836 | 3826 | func.finishAir(inst, result, &.{ty_op.operand}); |
| ... | ... | @@ -4355,7 +4345,7 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4355 | 4345 | |
| 4356 | 4346 | const op_bits = toWasmBits(@as(u16, @intCast(operand_ty.bitSize(mod)))).?; |
| 4357 | 4347 | const wanted_bits = toWasmBits(@as(u16, @intCast(ty.bitSize(mod)))).?; |
| 4358 | | const result = if (op_bits == wanted_bits and !ty.isSignedInt(mod)) |
| 4348 | const result = if (op_bits == wanted_bits) |
| 4359 | 4349 | func.reuseOperand(ty_op.operand, operand) |
| 4360 | 4350 | else |
| 4361 | 4351 | try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty); |
| ... | ... | @@ -4377,37 +4367,17 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4377 | 4367 | const op_bits = toWasmBits(given_bitsize).?; |
| 4378 | 4368 | const wanted_bits = toWasmBits(wanted_bitsize).?; |
| 4379 | 4369 | if (op_bits == wanted_bits) { |
| 4380 | | if (given.isSignedInt(mod)) { |
| 4381 | | if (given_bitsize < wanted_bitsize) { |
| 4382 | | // signed integers are stored as two's complement, |
| 4383 | | // when we upcast from a smaller integer to larger |
| 4384 | | // integers, we must get its absolute value similar to |
| 4385 | | // i64_extend_i32_s instruction. |
| 4386 | | return func.signExtendInt(operand, given); |
| 4387 | | } |
| 4388 | | return func.wrapOperand(operand, wanted); |
| 4389 | | } |
| 4390 | 4370 | return operand; |
| 4391 | 4371 | } |
| 4392 | 4372 | |
| 4393 | | if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) { |
| 4373 | if (op_bits == 64 and wanted_bits == 32) { |
| 4394 | 4374 | try func.emitWValue(operand); |
| 4395 | 4375 | try func.addTag(.i32_wrap_i64); |
| 4396 | | if (given.isSignedInt(mod) and wanted_bitsize < 32) |
| 4397 | | return func.wrapOperand(.{ .stack = {} }, wanted) |
| 4398 | | else |
| 4399 | | return WValue{ .stack = {} }; |
| 4400 | | } else if (op_bits == 32 and wanted_bits > 32 and wanted_bits <= 64) { |
| 4401 | | const operand32 = if (given_bitsize < 32 and wanted.isSignedInt(mod)) |
| 4402 | | try func.signExtendInt(operand, given) |
| 4403 | | else |
| 4404 | | operand; |
| 4405 | | try func.emitWValue(operand32); |
| 4376 | return .stack; |
| 4377 | } else if (op_bits == 32 and wanted_bits == 64) { |
| 4378 | try func.emitWValue(operand); |
| 4406 | 4379 | try func.addTag(if (wanted.isSignedInt(mod)) .i64_extend_i32_s else .i64_extend_i32_u); |
| 4407 | | if (given.isSignedInt(mod) and wanted_bitsize < 64) |
| 4408 | | return func.wrapOperand(.{ .stack = {} }, wanted) |
| 4409 | | else |
| 4410 | | return WValue{ .stack = {} }; |
| 4380 | return .stack; |
| 4411 | 4381 | } else if (wanted_bits == 128) { |
| 4412 | 4382 | // for 128bit integers we store the integer in the virtual stack, rather than a local |
| 4413 | 4383 | const stack_ptr = try func.allocStack(wanted); |
| ... | ... | @@ -4416,17 +4386,18 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro |
| 4416 | 4386 | // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it |
| 4417 | 4387 | // meaning less store operations are required. |
| 4418 | 4388 | const lhs = if (op_bits == 32) blk: { |
| 4419 | | break :blk try func.intcast(operand, given, if (wanted.isSignedInt(mod)) Type.i64 else Type.u64); |
| 4389 | const sign_ty = if (wanted.isSignedInt(mod)) Type.i64 else Type.u64; |
| 4390 | break :blk try (try func.intcast(operand, given, sign_ty)).toLocal(func, sign_ty); |
| 4420 | 4391 | } else operand; |
| 4421 | 4392 | |
| 4422 | 4393 | // store msb first |
| 4423 | | try func.store(.{ .stack = {} }, lhs, Type.u64, 0 + stack_ptr.offset()); |
| 4394 | try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset()); |
| 4424 | 4395 | |
| 4425 | 4396 | // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value |
| 4426 | 4397 | if (wanted.isSignedInt(mod)) { |
| 4427 | 4398 | try func.emitWValue(stack_ptr); |
| 4428 | 4399 | const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr); |
| 4429 | | try func.store(.{ .stack = {} }, shr, Type.u64, 8 + stack_ptr.offset()); |
| 4400 | try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset()); |
| 4430 | 4401 | } else { |
| 4431 | 4402 | // Ensure memory of lsb is zero'd |
| 4432 | 4403 | try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8); |
| ... | ... | @@ -5823,25 +5794,34 @@ fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5823 | 5794 | }; |
| 5824 | 5795 | |
| 5825 | 5796 | switch (wasm_bits) { |
| 5797 | 32 => { |
| 5798 | try func.emitWValue(operand); |
| 5799 | if (op_ty.isSignedInt(mod) and bits != wasm_bits) { |
| 5800 | _ = try func.wrapOperand(.stack, try mod.intType(.unsigned, bits)); |
| 5801 | } |
| 5802 | try func.addTag(.i32_popcnt); |
| 5803 | }, |
| 5804 | 64 => { |
| 5805 | try func.emitWValue(operand); |
| 5806 | if (op_ty.isSignedInt(mod) and bits != wasm_bits) { |
| 5807 | _ = try func.wrapOperand(.stack, try mod.intType(.unsigned, bits)); |
| 5808 | } |
| 5809 | try func.addTag(.i64_popcnt); |
| 5810 | try func.addTag(.i32_wrap_i64); |
| 5811 | try func.emitWValue(operand); |
| 5812 | }, |
| 5826 | 5813 | 128 => { |
| 5827 | 5814 | _ = try func.load(operand, Type.u64, 0); |
| 5828 | 5815 | try func.addTag(.i64_popcnt); |
| 5829 | 5816 | _ = try func.load(operand, Type.u64, 8); |
| 5817 | if (op_ty.isSignedInt(mod) and bits != wasm_bits) { |
| 5818 | _ = try func.wrapOperand(.stack, try mod.intType(.unsigned, bits - 64)); |
| 5819 | } |
| 5830 | 5820 | try func.addTag(.i64_popcnt); |
| 5831 | 5821 | try func.addTag(.i64_add); |
| 5832 | 5822 | try func.addTag(.i32_wrap_i64); |
| 5833 | 5823 | }, |
| 5834 | | else => { |
| 5835 | | try func.emitWValue(operand); |
| 5836 | | switch (wasm_bits) { |
| 5837 | | 32 => try func.addTag(.i32_popcnt), |
| 5838 | | 64 => { |
| 5839 | | try func.addTag(.i64_popcnt); |
| 5840 | | try func.addTag(.i32_wrap_i64); |
| 5841 | | }, |
| 5842 | | else => unreachable, |
| 5843 | | } |
| 5844 | | }, |
| 5824 | else => unreachable, |
| 5845 | 5825 | } |
| 5846 | 5826 | |
| 5847 | 5827 | const result = try func.allocLocal(result_ty); |
| ... | ... | @@ -5877,7 +5857,7 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5877 | 5857 | const reversed = if (bits == 32) |
| 5878 | 5858 | intrin_ret |
| 5879 | 5859 | else |
| 5880 | | try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, Type.u32, .shr); |
| 5860 | try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr); |
| 5881 | 5861 | const result = try reversed.toLocal(func, ty); |
| 5882 | 5862 | func.finishAir(inst, result, &.{ty_op.operand}); |
| 5883 | 5863 | }, |
| ... | ... | @@ -5891,7 +5871,7 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5891 | 5871 | const reversed = if (bits == 64) |
| 5892 | 5872 | intrin_ret |
| 5893 | 5873 | else |
| 5894 | | try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, Type.u64, .shr); |
| 5874 | try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr); |
| 5895 | 5875 | const result = try reversed.toLocal(func, ty); |
| 5896 | 5876 | func.finishAir(inst, result, &.{ty_op.operand}); |
| 5897 | 5877 | }, |
| ... | ... | @@ -5928,7 +5908,11 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5928 | 5908 | defer tmp.free(func); |
| 5929 | 5909 | try func.addLabel(.local_tee, tmp.local.value); |
| 5930 | 5910 | try func.emitWValue(.{ .imm64 = 128 - bits }); |
| 5931 | | try func.addTag(.i64_shr_u); |
| 5911 | if (ty.isSignedInt(mod)) { |
| 5912 | try func.addTag(.i64_shr_s); |
| 5913 | } else { |
| 5914 | try func.addTag(.i64_shr_u); |
| 5915 | } |
| 5932 | 5916 | try func.store(.stack, .stack, Type.u64, result.offset() + 8); |
| 5933 | 5917 | try func.addLabel(.local_get, tmp.local.value); |
| 5934 | 5918 | try func.emitWValue(.{ .imm64 = bits - 64 }); |
| ... | ... | @@ -5996,8 +5980,8 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro |
| 5996 | 5980 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5997 | 5981 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; |
| 5998 | 5982 | |
| 5999 | | const lhs_op = try func.resolveInst(extra.lhs); |
| 6000 | | const rhs_op = try func.resolveInst(extra.rhs); |
| 5983 | const lhs = try func.resolveInst(extra.lhs); |
| 5984 | const rhs = try func.resolveInst(extra.rhs); |
| 6001 | 5985 | const lhs_ty = func.typeOf(extra.lhs); |
| 6002 | 5986 | const mod = func.bin_file.base.comp.module.?; |
| 6003 | 5987 | |
| ... | ... | @@ -6012,7 +5996,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro |
| 6012 | 5996 | }; |
| 6013 | 5997 | |
| 6014 | 5998 | if (wasm_bits == 128) { |
| 6015 | | const result = try func.addSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, func.typeOfIndex(inst), op); |
| 5999 | const result = try func.addSubWithOverflowBigInt(lhs, rhs, lhs_ty, func.typeOfIndex(inst), op); |
| 6016 | 6000 | return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs }); |
| 6017 | 6001 | } |
| 6018 | 6002 | |
| ... | ... | @@ -6022,24 +6006,6 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro |
| 6022 | 6006 | else => unreachable, |
| 6023 | 6007 | }; |
| 6024 | 6008 | |
| 6025 | | // for signed integers, we first apply signed shifts by the difference in bits |
| 6026 | | // to get the signed value, as we store it internally as 2's complement. |
| 6027 | | var lhs = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 6028 | | break :blk try (try func.signExtendInt(lhs_op, lhs_ty)).toLocal(func, lhs_ty); |
| 6029 | | } else lhs_op; |
| 6030 | | var rhs = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 6031 | | break :blk try (try func.signExtendInt(rhs_op, lhs_ty)).toLocal(func, lhs_ty); |
| 6032 | | } else rhs_op; |
| 6033 | | |
| 6034 | | // in this case, we performed a signExtendInt which created a temporary local |
| 6035 | | // so let's free this so it can be re-used instead. |
| 6036 | | // In the other case we do not want to free it, because that would free the |
| 6037 | | // resolved instructions which may be referenced by other instructions. |
| 6038 | | defer if (wasm_bits != int_info.bits and is_signed) { |
| 6039 | | lhs.free(func); |
| 6040 | | rhs.free(func); |
| 6041 | | }; |
| 6042 | | |
| 6043 | 6009 | const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty); |
| 6044 | 6010 | var result = if (wasm_bits != int_info.bits) blk: { |
| 6045 | 6011 | break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty); |
| ... | ... | @@ -6053,8 +6019,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro |
| 6053 | 6019 | const lt = try func.cmp(bin_op, lhs, lhs_ty, .lt); |
| 6054 | 6020 | break :blk try func.binOp(cmp_zero, lt, Type.u32, .xor); |
| 6055 | 6021 | } |
| 6056 | | const abs = try func.signExtendInt(bin_op, lhs_ty); |
| 6057 | | break :blk try func.cmp(abs, bin_op, lhs_ty, .neq); |
| 6022 | break :blk try func.cmp(bin_op, bin_op, lhs_ty, .neq); |
| 6058 | 6023 | } else if (wasm_bits == int_info.bits) |
| 6059 | 6024 | try func.cmp(bin_op, lhs, lhs_ty, cmp_op) |
| 6060 | 6025 | else |
| ... | ... | @@ -6150,7 +6115,6 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6150 | 6115 | } |
| 6151 | 6116 | |
| 6152 | 6117 | const int_info = lhs_ty.intInfo(mod); |
| 6153 | | const is_signed = int_info.signedness == .signed; |
| 6154 | 6118 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6155 | 6119 | return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits}); |
| 6156 | 6120 | }; |
| ... | ... | @@ -6170,13 +6134,7 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6170 | 6134 | } else shl; |
| 6171 | 6135 | defer result.free(func); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits) |
| 6172 | 6136 | |
| 6173 | | const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: { |
| 6174 | | // emit lhs to stack to we can keep 'wrapped' on the stack also |
| 6175 | | try func.emitWValue(lhs); |
| 6176 | | const abs = try func.signExtendInt(shl, lhs_ty); |
| 6177 | | const wrapped = try func.wrapBinOp(abs, rhs_final, lhs_ty, .shr); |
| 6178 | | break :blk try func.cmp(.{ .stack = {} }, wrapped, lhs_ty, .neq); |
| 6179 | | } else blk: { |
| 6137 | const overflow_bit = blk: { |
| 6180 | 6138 | try func.emitWValue(lhs); |
| 6181 | 6139 | const shr = try func.binOp(result, rhs_final, lhs_ty, .shr); |
| 6182 | 6140 | break :blk try func.cmp(.{ .stack = {} }, shr, lhs_ty, .neq); |
| ... | ... | @@ -6245,10 +6203,8 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6245 | 6203 | break :blk down_cast; |
| 6246 | 6204 | } |
| 6247 | 6205 | } else if (int_info.signedness == .signed and wasm_bits == 32) blk: { |
| 6248 | | const lhs_abs = try func.signExtendInt(lhs, lhs_ty); |
| 6249 | | const rhs_abs = try func.signExtendInt(rhs, lhs_ty); |
| 6250 | | const bin_op = try (try func.binOp(lhs_abs, rhs_abs, lhs_ty, .mul)).toLocal(func, lhs_ty); |
| 6251 | | const mul_abs = try func.signExtendInt(bin_op, lhs_ty); |
| 6206 | const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty); |
| 6207 | const mul_abs = try func.wrapOperand(bin_op, lhs_ty); |
| 6252 | 6208 | _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq); |
| 6253 | 6209 | try func.addLabel(.local_set, overflow_bit.local.value); |
| 6254 | 6210 | break :blk try func.wrapOperand(bin_op, lhs_ty); |
| ... | ... | @@ -6697,6 +6653,9 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6697 | 6653 | return func.fail("TODO: @byteSwap for vectors", .{}); |
| 6698 | 6654 | } |
| 6699 | 6655 | const int_info = ty.intInfo(mod); |
| 6656 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6657 | return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}); |
| 6658 | }; |
| 6700 | 6659 | |
| 6701 | 6660 | // bytes are no-op |
| 6702 | 6661 | if (int_info.bits == 8) { |
| ... | ... | @@ -6704,73 +6663,34 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6704 | 6663 | } |
| 6705 | 6664 | |
| 6706 | 6665 | const result = result: { |
| 6707 | | switch (int_info.bits) { |
| 6708 | | 16 => { |
| 6709 | | const shl_res = try func.binOp(operand, .{ .imm32 = 8 }, ty, .shl); |
| 6710 | | const lhs = try func.binOp(shl_res, .{ .imm32 = 0xFF00 }, ty, .@"and"); |
| 6711 | | const shr_res = try func.binOp(operand, .{ .imm32 = 8 }, ty, .shr); |
| 6712 | | const res = if (int_info.signedness == .signed) blk: { |
| 6713 | | break :blk try func.wrapOperand(shr_res, Type.u8); |
| 6714 | | } else shr_res; |
| 6715 | | break :result try (try func.binOp(lhs, res, ty, .@"or")).toLocal(func, ty); |
| 6716 | | }, |
| 6717 | | 24 => { |
| 6718 | | var msb = try (try func.wrapOperand(operand, Type.u16)).toLocal(func, Type.u16); |
| 6719 | | defer msb.free(func); |
| 6720 | | |
| 6721 | | const shl_res = try func.binOp(msb, .{ .imm32 = 8 }, Type.u16, .shl); |
| 6722 | | const lhs = try func.binOp(shl_res, .{ .imm32 = 0xFF0000 }, Type.u16, .@"and"); |
| 6723 | | const shr_res = try func.binOp(msb, .{ .imm32 = 8 }, ty, .shr); |
| 6724 | | |
| 6725 | | const res = if (int_info.signedness == .signed) blk: { |
| 6726 | | break :blk try func.wrapOperand(shr_res, Type.u8); |
| 6727 | | } else shr_res; |
| 6728 | | const lhs_tmp = try func.binOp(lhs, res, ty, .@"or"); |
| 6729 | | const lhs_result = try func.binOp(lhs_tmp, .{ .imm32 = 8 }, ty, .shr); |
| 6730 | | const rhs_wrap = try func.wrapOperand(msb, Type.u8); |
| 6731 | | const rhs_result = try func.binOp(rhs_wrap, .{ .imm32 = 16 }, ty, .shl); |
| 6732 | | |
| 6733 | | const lsb = try func.wrapBinOp(operand, .{ .imm32 = 16 }, Type.u8, .shr); |
| 6734 | | const tmp = try func.binOp(lhs_result, rhs_result, ty, .@"or"); |
| 6735 | | break :result try (try func.binOp(tmp, lsb, ty, .@"or")).toLocal(func, ty); |
| 6736 | | }, |
| 6666 | switch (wasm_bits) { |
| 6737 | 6667 | 32 => { |
| 6738 | | const shl_tmp = try func.binOp(operand, .{ .imm32 = 8 }, Type.u32, .shl); |
| 6739 | | const lhs = try func.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, Type.u32, .@"and"); |
| 6740 | | const shr_tmp = try func.binOp(operand, .{ .imm32 = 8 }, Type.u32, .shr); |
| 6741 | | const rhs = try func.binOp(shr_tmp, .{ .imm32 = 0x00FF00FF }, Type.u32, .@"and"); |
| 6742 | | var tmp_or = try (try func.binOp(lhs, rhs, Type.u32, .@"or")).toLocal(func, Type.u32); |
| 6743 | | |
| 6744 | | const shl = try func.binOp(tmp_or, .{ .imm32 = 16 }, Type.u32, .shl); |
| 6745 | | const shr = try func.binOp(tmp_or, .{ .imm32 = 16 }, Type.u32, .shr); |
| 6746 | | |
| 6747 | | tmp_or.free(func); |
| 6668 | const intrin_ret = try func.callIntrinsic( |
| 6669 | "__bswapsi2", |
| 6670 | &.{.u32_type}, |
| 6671 | Type.u32, |
| 6672 | &.{operand}, |
| 6673 | ); |
| 6674 | const swapped = if (int_info.bits == 32) |
| 6675 | intrin_ret |
| 6676 | else |
| 6677 | try func.binOp(intrin_ret, .{ .imm32 = 32 - int_info.bits }, ty, .shr); |
| 6748 | 6678 | |
| 6749 | | break :result try (try func.binOp(shl, shr, Type.u32, .@"or")).toLocal(func, Type.u32); |
| 6679 | break :result try swapped.toLocal(func, ty); |
| 6750 | 6680 | }, |
| 6751 | 6681 | 64 => { |
| 6752 | | const shl_tmp_1 = try func.binOp(operand, .{ .imm64 = 8 }, Type.u64, .shl); |
| 6753 | | const lhs_1 = try func.binOp(shl_tmp_1, .{ .imm64 = 0xFF00FF00FF00FF00 }, Type.u64, .@"and"); |
| 6754 | | |
| 6755 | | const shr_tmp_1 = try func.binOp(operand, .{ .imm64 = 8 }, Type.u64, .shr); |
| 6756 | | const rhs_1 = try func.binOp(shr_tmp_1, .{ .imm64 = 0x00FF00FF00FF00FF }, Type.u64, .@"and"); |
| 6757 | | |
| 6758 | | var tmp_or_1 = try (try func.binOp(lhs_1, rhs_1, Type.u64, .@"or")).toLocal(func, Type.u64); |
| 6759 | | |
| 6760 | | const shl_tmp_2 = try func.binOp(tmp_or_1, .{ .imm64 = 16 }, Type.u64, .shl); |
| 6761 | | const lhs_2 = try func.binOp(shl_tmp_2, .{ .imm64 = 0xFFFF0000FFFF0000 }, Type.u64, .@"and"); |
| 6762 | | |
| 6763 | | const shr_tmp_2 = try func.binOp(tmp_or_1, .{ .imm64 = 16 }, Type.u64, .shr); |
| 6764 | | tmp_or_1.free(func); |
| 6765 | | const rhs_2 = try func.binOp(shr_tmp_2, .{ .imm64 = 0x0000FFFF0000FFFF }, Type.u64, .@"and"); |
| 6766 | | |
| 6767 | | var tmp_or_2 = try (try func.binOp(lhs_2, rhs_2, Type.u64, .@"or")).toLocal(func, Type.u64); |
| 6768 | | |
| 6769 | | const shl = try func.binOp(tmp_or_2, .{ .imm64 = 32 }, Type.u64, .shl); |
| 6770 | | const shr = try func.binOp(tmp_or_2, .{ .imm64 = 32 }, Type.u64, .shr); |
| 6771 | | tmp_or_2.free(func); |
| 6682 | const intrin_ret = try func.callIntrinsic( |
| 6683 | "__bswapdi2", |
| 6684 | &.{.u64_type}, |
| 6685 | Type.u64, |
| 6686 | &.{operand}, |
| 6687 | ); |
| 6688 | const swapped = if (int_info.bits == 64) |
| 6689 | intrin_ret |
| 6690 | else |
| 6691 | try func.binOp(intrin_ret, .{ .imm64 = 64 - int_info.bits }, ty, .shr); |
| 6772 | 6692 | |
| 6773 | | break :result try (try func.binOp(shl, shr, Type.u64, .@"or")).toLocal(func, Type.u64); |
| 6693 | break :result try swapped.toLocal(func, ty); |
| 6774 | 6694 | }, |
| 6775 | 6695 | else => return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}), |
| 6776 | 6696 | } |
| ... | ... | @@ -6779,32 +6699,24 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6779 | 6699 | } |
| 6780 | 6700 | |
| 6781 | 6701 | fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6782 | | const mod = func.bin_file.base.comp.module.?; |
| 6783 | 6702 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6784 | 6703 | |
| 6785 | 6704 | const ty = func.typeOfIndex(inst); |
| 6786 | 6705 | const lhs = try func.resolveInst(bin_op.lhs); |
| 6787 | 6706 | const rhs = try func.resolveInst(bin_op.rhs); |
| 6788 | 6707 | |
| 6789 | | const result = if (ty.isSignedInt(mod)) |
| 6790 | | try func.divSigned(lhs, rhs, ty) |
| 6791 | | else |
| 6792 | | try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty); |
| 6708 | const result = try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty); |
| 6793 | 6709 | func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 6794 | 6710 | } |
| 6795 | 6711 | |
| 6796 | 6712 | fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6797 | | const mod = func.bin_file.base.comp.module.?; |
| 6798 | 6713 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6799 | 6714 | |
| 6800 | 6715 | const ty = func.typeOfIndex(inst); |
| 6801 | 6716 | const lhs = try func.resolveInst(bin_op.lhs); |
| 6802 | 6717 | const rhs = try func.resolveInst(bin_op.rhs); |
| 6803 | 6718 | |
| 6804 | | const div_result = if (ty.isSignedInt(mod)) |
| 6805 | | try func.divSigned(lhs, rhs, ty) |
| 6806 | | else |
| 6807 | | try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty); |
| 6719 | const div_result = try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty); |
| 6808 | 6720 | |
| 6809 | 6721 | if (ty.isAnyFloat()) { |
| 6810 | 6722 | const trunc_result = try (try func.floatOp(.trunc, ty, &.{div_result})).toLocal(func, ty); |
| ... | ... | @@ -6834,16 +6746,6 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6834 | 6746 | return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); |
| 6835 | 6747 | } |
| 6836 | 6748 | |
| 6837 | | const lhs_wasm = if (wasm_bits != int_bits) |
| 6838 | | try (try func.signExtendInt(lhs, ty)).toLocal(func, ty) |
| 6839 | | else |
| 6840 | | lhs; |
| 6841 | | |
| 6842 | | const rhs_wasm = if (wasm_bits != int_bits) |
| 6843 | | try (try func.signExtendInt(rhs, ty)).toLocal(func, ty) |
| 6844 | | else |
| 6845 | | rhs; |
| 6846 | | |
| 6847 | 6749 | const zero = switch (wasm_bits) { |
| 6848 | 6750 | 32 => WValue{ .imm32 = 0 }, |
| 6849 | 6751 | 64 => WValue{ .imm64 = 0 }, |
| ... | ... | @@ -6852,7 +6754,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6852 | 6754 | |
| 6853 | 6755 | // tee leaves the value on the stack and stores it in a local. |
| 6854 | 6756 | const quotient = try func.allocLocal(ty); |
| 6855 | | _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .div); |
| 6757 | _ = try func.binOp(lhs, rhs, ty, .div); |
| 6856 | 6758 | try func.addLabel(.local_tee, quotient.local.value); |
| 6857 | 6759 | |
| 6858 | 6760 | // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow |
| ... | ... | @@ -6864,7 +6766,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6864 | 6766 | } |
| 6865 | 6767 | |
| 6866 | 6768 | // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise. |
| 6867 | | _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .xor); |
| 6769 | _ = try func.binOp(lhs, rhs, ty, .xor); |
| 6868 | 6770 | _ = try func.cmp(.stack, zero, ty, .lt); |
| 6869 | 6771 | |
| 6870 | 6772 | switch (wasm_bits) { |
| ... | ... | @@ -6879,7 +6781,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6879 | 6781 | else => unreachable, |
| 6880 | 6782 | } |
| 6881 | 6783 | |
| 6882 | | _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .rem); |
| 6784 | _ = try func.binOp(lhs, rhs, ty, .rem); |
| 6883 | 6785 | |
| 6884 | 6786 | if (wasm_bits == 64) { |
| 6885 | 6787 | try func.addTag(.i64_eqz); |
| ... | ... | @@ -6929,68 +6831,14 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6929 | 6831 | func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 6930 | 6832 | } |
| 6931 | 6833 | |
| 6932 | | fn divSigned(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue { |
| 6933 | | const mod = func.bin_file.base.comp.module.?; |
| 6934 | | const int_bits = ty.intInfo(mod).bits; |
| 6935 | | const wasm_bits = toWasmBits(int_bits) orelse { |
| 6936 | | return func.fail("TODO: Implement signed division for integers with bitsize '{d}'", .{int_bits}); |
| 6937 | | }; |
| 6938 | | |
| 6939 | | if (wasm_bits == 128) { |
| 6940 | | return func.fail("TODO: Implement signed division for 128-bit integerrs", .{}); |
| 6941 | | } |
| 6942 | | |
| 6943 | | if (wasm_bits != int_bits) { |
| 6944 | | // Leave both values on the stack |
| 6945 | | _ = try func.signExtendInt(lhs, ty); |
| 6946 | | _ = try func.signExtendInt(rhs, ty); |
| 6947 | | } else { |
| 6948 | | try func.emitWValue(lhs); |
| 6949 | | try func.emitWValue(rhs); |
| 6950 | | } |
| 6951 | | switch (wasm_bits) { |
| 6952 | | 32 => try func.addTag(.i32_div_s), |
| 6953 | | 64 => try func.addTag(.i64_div_s), |
| 6954 | | else => unreachable, |
| 6955 | | } |
| 6956 | | _ = try func.wrapOperand(.stack, ty); |
| 6957 | | |
| 6958 | | const result = try func.allocLocal(ty); |
| 6959 | | try func.addLabel(.local_set, result.local.value); |
| 6960 | | return result; |
| 6961 | | } |
| 6962 | | |
| 6963 | 6834 | fn airRem(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6964 | 6835 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6965 | 6836 | |
| 6966 | | const mod = func.bin_file.base.comp.module.?; |
| 6967 | 6837 | const ty = func.typeOfIndex(inst); |
| 6968 | 6838 | const lhs = try func.resolveInst(bin_op.lhs); |
| 6969 | 6839 | const rhs = try func.resolveInst(bin_op.rhs); |
| 6970 | 6840 | |
| 6971 | | const result = if (ty.isSignedInt(mod)) result: { |
| 6972 | | const int_bits = ty.intInfo(mod).bits; |
| 6973 | | const wasm_bits = toWasmBits(int_bits) orelse { |
| 6974 | | return func.fail("TODO: `@rem` for signed integers larger than 128 bits ({d} bits requested)", .{int_bits}); |
| 6975 | | }; |
| 6976 | | |
| 6977 | | if (wasm_bits > 64) { |
| 6978 | | return func.fail("TODO: `@rem` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); |
| 6979 | | } |
| 6980 | | |
| 6981 | | const lhs_wasm = if (wasm_bits != int_bits) |
| 6982 | | try (try func.signExtendInt(lhs, ty)).toLocal(func, ty) |
| 6983 | | else |
| 6984 | | lhs; |
| 6985 | | |
| 6986 | | const rhs_wasm = if (wasm_bits != int_bits) |
| 6987 | | try (try func.signExtendInt(rhs, ty)).toLocal(func, ty) |
| 6988 | | else |
| 6989 | | rhs; |
| 6990 | | |
| 6991 | | _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .rem); |
| 6992 | | break :result try func.wrapOperand(.stack, ty); |
| 6993 | | } else try func.binOp(lhs, rhs, ty, .rem); |
| 6841 | const result = try func.binOp(lhs, rhs, ty, .rem); |
| 6994 | 6842 | |
| 6995 | 6843 | const return_local = try result.toLocal(func, ty); |
| 6996 | 6844 | func.finishAir(inst, return_local, &.{ bin_op.lhs, bin_op.rhs }); |
| ... | ... | @@ -7022,19 +6870,9 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7022 | 6870 | return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits}); |
| 7023 | 6871 | } |
| 7024 | 6872 | |
| 7025 | | const lhs_wasm = if (wasm_bits != int_bits) |
| 7026 | | try (try func.signExtendInt(lhs, ty)).toLocal(func, ty) |
| 7027 | | else |
| 7028 | | lhs; |
| 7029 | | |
| 7030 | | const rhs_wasm = if (wasm_bits != int_bits) |
| 7031 | | try (try func.signExtendInt(rhs, ty)).toLocal(func, ty) |
| 7032 | | else |
| 7033 | | rhs; |
| 7034 | | |
| 7035 | | _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .rem); |
| 7036 | | _ = try func.binOp(.stack, rhs_wasm, ty, .add); |
| 7037 | | _ = try func.binOp(.stack, rhs_wasm, ty, .rem); |
| 6873 | _ = try func.binOp(lhs, rhs, ty, .rem); |
| 6874 | _ = try func.binOp(.stack, rhs, ty, .add); |
| 6875 | _ = try func.binOp(.stack, rhs, ty, .rem); |
| 7038 | 6876 | } else { |
| 7039 | 6877 | return func.fail("TODO: implement `@mod` on floating point types for {}", .{func.target.cpu.arch}); |
| 7040 | 6878 | } |
| ... | ... | @@ -7044,42 +6882,6 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7044 | 6882 | func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 7045 | 6883 | } |
| 7046 | 6884 | |
| 7047 | | /// Sign extends an N bit signed integer and pushes the result to the stack. |
| 7048 | | /// The result will be sign extended to 32 bits if N <= 32 or 64 bits if N <= 64. |
| 7049 | | /// Support for integers wider than 64 bits has not yet been implemented. |
| 7050 | | fn signExtendInt(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { |
| 7051 | | const mod = func.bin_file.base.comp.module.?; |
| 7052 | | const int_bits = ty.intInfo(mod).bits; |
| 7053 | | const wasm_bits = toWasmBits(int_bits) orelse { |
| 7054 | | return func.fail("TODO: signExtendInt for signed integers larger than '{d}' bits", .{int_bits}); |
| 7055 | | }; |
| 7056 | | |
| 7057 | | const shift_val = switch (wasm_bits) { |
| 7058 | | 32 => WValue{ .imm32 = wasm_bits - int_bits }, |
| 7059 | | 64 => WValue{ .imm64 = wasm_bits - int_bits }, |
| 7060 | | else => return func.fail("TODO: signExtendInt for i128", .{}), |
| 7061 | | }; |
| 7062 | | |
| 7063 | | try func.emitWValue(operand); |
| 7064 | | switch (wasm_bits) { |
| 7065 | | 32 => { |
| 7066 | | try func.emitWValue(shift_val); |
| 7067 | | try func.addTag(.i32_shl); |
| 7068 | | try func.emitWValue(shift_val); |
| 7069 | | try func.addTag(.i32_shr_s); |
| 7070 | | }, |
| 7071 | | 64 => { |
| 7072 | | try func.emitWValue(shift_val); |
| 7073 | | try func.addTag(.i64_shl); |
| 7074 | | try func.emitWValue(shift_val); |
| 7075 | | try func.addTag(.i64_shr_s); |
| 7076 | | }, |
| 7077 | | else => unreachable, |
| 7078 | | } |
| 7079 | | |
| 7080 | | return WValue{ .stack = {} }; |
| 7081 | | } |
| 7082 | | |
| 7083 | 6885 | fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 7084 | 6886 | assert(op == .add or op == .sub); |
| 7085 | 6887 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| ... | ... | @@ -7131,20 +6933,13 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { |
| 7131 | 6933 | return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs }); |
| 7132 | 6934 | } |
| 7133 | 6935 | |
| 7134 | | fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue { |
| 6936 | fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { |
| 7135 | 6937 | const mod = func.bin_file.base.comp.module.?; |
| 7136 | 6938 | const int_info = ty.intInfo(mod); |
| 7137 | 6939 | const wasm_bits = toWasmBits(int_info.bits).?; |
| 7138 | 6940 | const is_wasm_bits = wasm_bits == int_info.bits; |
| 7139 | 6941 | const ext_ty = if (!is_wasm_bits) try mod.intType(int_info.signedness, wasm_bits) else ty; |
| 7140 | 6942 | |
| 7141 | | var lhs = if (!is_wasm_bits) lhs: { |
| 7142 | | break :lhs try (try func.signExtendInt(lhs_operand, ty)).toLocal(func, ext_ty); |
| 7143 | | } else lhs_operand; |
| 7144 | | var rhs = if (!is_wasm_bits) rhs: { |
| 7145 | | break :rhs try (try func.signExtendInt(rhs_operand, ty)).toLocal(func, ext_ty); |
| 7146 | | } else rhs_operand; |
| 7147 | | |
| 7148 | 6943 | const max_val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits - 1))) - 1)); |
| 7149 | 6944 | const min_val: i64 = (-@as(i64, @intCast(@as(u63, @intCast(max_val))))) - 1; |
| 7150 | 6945 | const max_wvalue = switch (wasm_bits) { |
| ... | ... | @@ -7161,8 +6956,6 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type, |
| 7161 | 6956 | var bin_result = try (try func.binOp(lhs, rhs, ext_ty, op)).toLocal(func, ext_ty); |
| 7162 | 6957 | if (!is_wasm_bits) { |
| 7163 | 6958 | defer bin_result.free(func); // not returned in this branch |
| 7164 | | defer lhs.free(func); // uses temporary local for absvalue |
| 7165 | | defer rhs.free(func); // uses temporary local for absvalue |
| 7166 | 6959 | try func.emitWValue(bin_result); |
| 7167 | 6960 | try func.emitWValue(max_wvalue); |
| 7168 | 6961 | _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt); |