authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-06-28 00:04:18+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-07-02 15:02:59+02:00
log4b9d327f123df935adcde9dadcb8561f7a84ef95
tree1fc16b4c9acd8a36b76a30478606b00d0b4a3ea9
parente4447c54eaae4d416cb3027d9cefad11196f9f6d

stage2-wasm: sign extend strategy


1 files changed, 148 insertions(+), 355 deletions(-)

src/arch/wasm/CodeGen.zig+148-355
......@@ -2325,14 +2325,19 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
23252325 WValue{ .imm32 = @as(u32, @truncate(mask)) }
23262326 else
23272327 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)) };
23282332
23292333 try func.emitWValue(lhs);
23302334 const loaded = try func.load(lhs, int_elem_ty, 0);
23312335 const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and");
23322336 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");
23332338 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;
23362341 const result = try func.binOp(anded, shifted_value, int_elem_ty, .@"or");
23372342 // lhs is still on the stack
23382343 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
25152520 .valtype1 = typeToValtype(ty, mod),
25162521 .width = abi_size * 8,
25172522 .op = .load,
2518 .signedness = .unsigned,
2523 .signedness = if (ty.isSignedInt(mod)) .signed else .unsigned,
25192524 });
25202525
25212526 try func.addMemArg(
......@@ -2800,10 +2805,7 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28002805 switch (wasm_bits) {
28012806 32 => {
28022807 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
28072809 try func.addImm32(31);
28082810 try func.addTag(.i32_shr_s);
28092811
......@@ -2815,15 +2817,10 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28152817 try func.addTag(.i32_xor);
28162818 try func.emitWValue(tmp);
28172819 try func.addTag(.i32_sub);
2818
2819 _ = try func.wrapOperand(.stack, ty);
28202820 },
28212821 64 => {
28222822 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
28272824 try func.addImm64(63);
28282825 try func.addTag(.i64_shr_s);
28292826
......@@ -2835,8 +2832,6 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28352832 try func.addTag(.i64_xor);
28362833 try func.emitWValue(tmp);
28372834 try func.addTag(.i64_sub);
2838
2839 _ = try func.wrapOperand(.stack, ty);
28402835 },
28412836 128 => {
28422837 const mask = try func.allocStack(Type.u128);
......@@ -2844,10 +2839,6 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28442839 try func.emitWValue(mask);
28452840
28462841 _ = 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 }
28512842 try func.addImm64(63);
28522843 try func.addTag(.i64_shr_s);
28532844
......@@ -2860,9 +2851,8 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28602851
28612852 const a = try func.binOpBigInt(operand, mask, Type.u128, .xor);
28622853 const b = try func.binOpBigInt(a, mask, Type.u128, .sub);
2863 const result = try func.wrapOperand(b, ty);
28642854
2865 func.finishAir(inst, result, &.{ty_op.operand});
2855 func.finishAir(inst, b, &.{ty_op.operand});
28662856 return;
28672857 },
28682858 else => unreachable,
......@@ -3058,14 +3048,28 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
30583048 switch (wasm_bits) {
30593049 32 => {
30603050 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 }
30633060 return .stack;
30643061 },
30653062 64 => {
30663063 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 }
30693073 return .stack;
30703074 },
30713075 128 => {
......@@ -3078,8 +3082,15 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
30783082
30793083 try func.emitWValue(result);
30803084 _ = 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 }
30833094 try func.store(.stack, .stack, Type.u64, result.offset() + 8);
30843095
30853096 return result;
......@@ -3201,22 +3212,6 @@ fn lowerDeclRefValue(func: *CodeGen, decl_index: InternPool.DeclIndex, offset: u
32013212 } else return WValue{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
32023213}
32033214
3204/// Converts a signed integer to its 2's complement form and returns
3205/// an unsigned integer instead.
3206/// Asserts bitsize <= 64
3207fn 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
32203215/// Asserts that `isByRef` returns `false` for `ty`.
32213216fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
32223217 const mod = func.bin_file.base.comp.module.?;
......@@ -3268,18 +3263,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
32683263 const int_info = ty.intInfo(mod);
32693264 switch (int_info.signedness) {
32703265 .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)) },
32793268 else => unreachable,
32803269 },
32813270 .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)) },
32833272 33...64 => return WValue{ .imm64 = val.toUnsignedInt(mod) },
32843273 else => unreachable,
32853274 },
......@@ -3618,29 +3607,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
36183607 // incase of an actual integer, we emit the correct signedness
36193608 break :blk ty.intInfo(mod).signedness;
36203609 };
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;
36393610
36403611 // ensure that when we compare pointers, we emit
36413612 // 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);
36443615
36453616 const opcode: wasm.Opcode = buildOpcode(.{
36463617 .valtype1 = typeToValtype(ty, mod),
......@@ -3812,25 +3783,44 @@ fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
38123783}
38133784
38143785fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3786 const mod = func.bin_file.base.comp.module.?;
38153787 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
38163796 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);
38203797 if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) {
38213798 const bitcast_result = try func.bitcast(wanted_ty, given_ty, operand);
38223799 break :result try bitcast_result.toLocal(func, wanted_ty);
38233800 }
3824 const mod = func.bin_file.base.comp.module.?;
3801
38253802 if (isByRef(given_ty, mod) and !isByRef(wanted_ty, mod)) {
38263803 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 }
38283809 }
38293810 if (!isByRef(given_ty, mod) and isByRef(wanted_ty, mod)) {
38303811 const stack_memory = try func.allocStack(wanted_ty);
38313812 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 }
38333818 }
3819
3820 if (needs_wrapping) {
3821 break :result try (try func.wrapOperand(operand, wanted_ty)).toLocal(func, wanted_ty);
3822 }
3823
38343824 break :result func.reuseOperand(ty_op.operand, operand);
38353825 };
38363826 func.finishAir(inst, result, &.{ty_op.operand});
......@@ -4355,7 +4345,7 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43554345
43564346 const op_bits = toWasmBits(@as(u16, @intCast(operand_ty.bitSize(mod)))).?;
43574347 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)
43594349 func.reuseOperand(ty_op.operand, operand)
43604350 else
43614351 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
43774367 const op_bits = toWasmBits(given_bitsize).?;
43784368 const wanted_bits = toWasmBits(wanted_bitsize).?;
43794369 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 }
43904370 return operand;
43914371 }
43924372
4393 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {
4373 if (op_bits == 64 and wanted_bits == 32) {
43944374 try func.emitWValue(operand);
43954375 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);
44064379 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;
44114381 } else if (wanted_bits == 128) {
44124382 // for 128bit integers we store the integer in the virtual stack, rather than a local
44134383 const stack_ptr = try func.allocStack(wanted);
......@@ -4416,17 +4386,18 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
44164386 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it
44174387 // meaning less store operations are required.
44184388 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);
44204391 } else operand;
44214392
44224393 // 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());
44244395
44254396 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
44264397 if (wanted.isSignedInt(mod)) {
44274398 try func.emitWValue(stack_ptr);
44284399 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());
44304401 } else {
44314402 // Ensure memory of lsb is zero'd
44324403 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
......@@ -5823,25 +5794,34 @@ fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58235794 };
58245795
58255796 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 },
58265813 128 => {
58275814 _ = try func.load(operand, Type.u64, 0);
58285815 try func.addTag(.i64_popcnt);
58295816 _ = 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 }
58305820 try func.addTag(.i64_popcnt);
58315821 try func.addTag(.i64_add);
58325822 try func.addTag(.i32_wrap_i64);
58335823 },
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,
58455825 }
58465826
58475827 const result = try func.allocLocal(result_ty);
......@@ -5877,7 +5857,7 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58775857 const reversed = if (bits == 32)
58785858 intrin_ret
58795859 else
5880 try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, Type.u32, .shr);
5860 try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr);
58815861 const result = try reversed.toLocal(func, ty);
58825862 func.finishAir(inst, result, &.{ty_op.operand});
58835863 },
......@@ -5891,7 +5871,7 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
58915871 const reversed = if (bits == 64)
58925872 intrin_ret
58935873 else
5894 try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, Type.u64, .shr);
5874 try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr);
58955875 const result = try reversed.toLocal(func, ty);
58965876 func.finishAir(inst, result, &.{ty_op.operand});
58975877 },
......@@ -5928,7 +5908,11 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
59285908 defer tmp.free(func);
59295909 try func.addLabel(.local_tee, tmp.local.value);
59305910 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 }
59325916 try func.store(.stack, .stack, Type.u64, result.offset() + 8);
59335917 try func.addLabel(.local_get, tmp.local.value);
59345918 try func.emitWValue(.{ .imm64 = bits - 64 });
......@@ -5996,8 +5980,8 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
59965980 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
59975981 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
59985982
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);
60015985 const lhs_ty = func.typeOf(extra.lhs);
60025986 const mod = func.bin_file.base.comp.module.?;
60035987
......@@ -6012,7 +5996,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
60125996 };
60135997
60145998 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);
60166000 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
60176001 }
60186002
......@@ -6022,24 +6006,6 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
60226006 else => unreachable,
60236007 };
60246008
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
60436009 const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);
60446010 var result = if (wasm_bits != int_info.bits) blk: {
60456011 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
60536019 const lt = try func.cmp(bin_op, lhs, lhs_ty, .lt);
60546020 break :blk try func.binOp(cmp_zero, lt, Type.u32, .xor);
60556021 }
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);
60586023 } else if (wasm_bits == int_info.bits)
60596024 try func.cmp(bin_op, lhs, lhs_ty, cmp_op)
60606025 else
......@@ -6150,7 +6115,6 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61506115 }
61516116
61526117 const int_info = lhs_ty.intInfo(mod);
6153 const is_signed = int_info.signedness == .signed;
61546118 const wasm_bits = toWasmBits(int_info.bits) orelse {
61556119 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
61566120 };
......@@ -6170,13 +6134,7 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
61706134 } else shl;
61716135 defer result.free(func); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits)
61726136
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: {
61806138 try func.emitWValue(lhs);
61816139 const shr = try func.binOp(result, rhs_final, lhs_ty, .shr);
61826140 break :blk try func.cmp(.{ .stack = {} }, shr, lhs_ty, .neq);
......@@ -6245,10 +6203,8 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
62456203 break :blk down_cast;
62466204 }
62476205 } 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);
62526208 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);
62536209 try func.addLabel(.local_set, overflow_bit.local.value);
62546210 break :blk try func.wrapOperand(bin_op, lhs_ty);
......@@ -6697,6 +6653,9 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
66976653 return func.fail("TODO: @byteSwap for vectors", .{});
66986654 }
66996655 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 };
67006659
67016660 // bytes are no-op
67026661 if (int_info.bits == 8) {
......@@ -6704,73 +6663,34 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67046663 }
67056664
67066665 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) {
67376667 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);
67486678
6749 break :result try (try func.binOp(shl, shr, Type.u32, .@"or")).toLocal(func, Type.u32);
6679 break :result try swapped.toLocal(func, ty);
67506680 },
67516681 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);
67726692
6773 break :result try (try func.binOp(shl, shr, Type.u64, .@"or")).toLocal(func, Type.u64);
6693 break :result try swapped.toLocal(func, ty);
67746694 },
67756695 else => return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
67766696 }
......@@ -6779,32 +6699,24 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67796699}
67806700
67816701fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6782 const mod = func.bin_file.base.comp.module.?;
67836702 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67846703
67856704 const ty = func.typeOfIndex(inst);
67866705 const lhs = try func.resolveInst(bin_op.lhs);
67876706 const rhs = try func.resolveInst(bin_op.rhs);
67886707
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);
67936709 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
67946710}
67956711
67966712fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6797 const mod = func.bin_file.base.comp.module.?;
67986713 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67996714
68006715 const ty = func.typeOfIndex(inst);
68016716 const lhs = try func.resolveInst(bin_op.lhs);
68026717 const rhs = try func.resolveInst(bin_op.rhs);
68036718
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);
68086720
68096721 if (ty.isAnyFloat()) {
68106722 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 {
68346746 return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
68356747 }
68366748
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
68476749 const zero = switch (wasm_bits) {
68486750 32 => WValue{ .imm32 = 0 },
68496751 64 => WValue{ .imm64 = 0 },
......@@ -6852,7 +6754,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
68526754
68536755 // tee leaves the value on the stack and stores it in a local.
68546756 const quotient = try func.allocLocal(ty);
6855 _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .div);
6757 _ = try func.binOp(lhs, rhs, ty, .div);
68566758 try func.addLabel(.local_tee, quotient.local.value);
68576759
68586760 // 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 {
68646766 }
68656767
68666768 // 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);
68686770 _ = try func.cmp(.stack, zero, ty, .lt);
68696771
68706772 switch (wasm_bits) {
......@@ -6879,7 +6781,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
68796781 else => unreachable,
68806782 }
68816783
6882 _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .rem);
6784 _ = try func.binOp(lhs, rhs, ty, .rem);
68836785
68846786 if (wasm_bits == 64) {
68856787 try func.addTag(.i64_eqz);
......@@ -6929,68 +6831,14 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69296831 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
69306832}
69316833
6932fn 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
69636834fn airRem(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
69646835 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
69656836
6966 const mod = func.bin_file.base.comp.module.?;
69676837 const ty = func.typeOfIndex(inst);
69686838 const lhs = try func.resolveInst(bin_op.lhs);
69696839 const rhs = try func.resolveInst(bin_op.rhs);
69706840
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);
69946842
69956843 const return_local = try result.toLocal(func, ty);
69966844 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 {
70226870 return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
70236871 }
70246872
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);
70386876 } else {
70396877 return func.fail("TODO: implement `@mod` on floating point types for {}", .{func.target.cpu.arch});
70406878 }
......@@ -7044,42 +6882,6 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
70446882 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
70456883}
70466884
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.
7050fn 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
70836885fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
70846886 assert(op == .add or op == .sub);
70856887 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 {
71316933 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
71326934}
71336935
7134fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue {
6936fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
71356937 const mod = func.bin_file.base.comp.module.?;
71366938 const int_info = ty.intInfo(mod);
71376939 const wasm_bits = toWasmBits(int_info.bits).?;
71386940 const is_wasm_bits = wasm_bits == int_info.bits;
71396941 const ext_ty = if (!is_wasm_bits) try mod.intType(int_info.signedness, wasm_bits) else ty;
71406942
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
71486943 const max_val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits - 1))) - 1));
71496944 const min_val: i64 = (-@as(i64, @intCast(@as(u63, @intCast(max_val))))) - 1;
71506945 const max_wvalue = switch (wasm_bits) {
......@@ -7161,8 +6956,6 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,
71616956 var bin_result = try (try func.binOp(lhs, rhs, ext_ty, op)).toLocal(func, ext_ty);
71626957 if (!is_wasm_bits) {
71636958 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
71666959 try func.emitWValue(bin_result);
71676960 try func.emitWValue(max_wvalue);
71686961 _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt);