authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2024-07-02 17:58:01+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-02 17:58:01+02:00
log1905137d0b692d19c511dab6c0f7e4dfad1808c1
tree2216fd471d2e3820da0ba814bee2b1ffb3a4db4d
parent02b3d5b58adc58f85f213b580e60f4a07ae9b140
parent1a951b49af90d04832ec7e928eb2f87630499d59
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20438 from pavelverigo/stage2-wasm-default-signextend

stage2-wasm: store signed integers with extended sign bit + minor changes

4 files changed, 252 insertions(+), 412 deletions(-)

src/arch/wasm/CodeGen.zig+198-390
...@@ -11,10 +11,8 @@ const log = std.log.scoped(.codegen);...@@ -11,10 +11,8 @@ const log = std.log.scoped(.codegen);
1111
12const codegen = @import("../../codegen.zig");12const codegen = @import("../../codegen.zig");
13const Zcu = @import("../../Zcu.zig");13const Zcu = @import("../../Zcu.zig");
14/// Deprecated.
15const Module = Zcu;
16const InternPool = @import("../../InternPool.zig");14const InternPool = @import("../../InternPool.zig");
17const Decl = Module.Decl;15const Decl = Zcu.Decl;
18const Type = @import("../../type.zig").Type;16const Type = @import("../../type.zig").Type;
19const Value = @import("../../Value.zig");17const Value = @import("../../Value.zig");
20const Compilation = @import("../../Compilation.zig");18const Compilation = @import("../../Compilation.zig");
...@@ -674,7 +672,7 @@ local_index: u32 = 0,...@@ -674,7 +672,7 @@ local_index: u32 = 0,
674/// Used to track which argument is being referenced in `airArg`.672/// Used to track which argument is being referenced in `airArg`.
675arg_index: u32 = 0,673arg_index: u32 = 0,
676/// If codegen fails, an error messages will be allocated and saved in `err_msg`674/// If codegen fails, an error messages will be allocated and saved in `err_msg`
677err_msg: *Module.ErrorMsg,675err_msg: *Zcu.ErrorMsg,
678/// List of all locals' types generated throughout this declaration676/// List of all locals' types generated throughout this declaration
679/// used to emit locals count at start of 'code' section.677/// used to emit locals count at start of 'code' section.
680locals: std.ArrayListUnmanaged(u8),678locals: std.ArrayListUnmanaged(u8),
...@@ -768,7 +766,7 @@ pub fn deinit(func: *CodeGen) void {...@@ -768,7 +766,7 @@ pub fn deinit(func: *CodeGen) void {
768fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {766fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {
769 const mod = func.bin_file.base.comp.module.?;767 const mod = func.bin_file.base.comp.module.?;
770 const src_loc = func.decl.navSrcLoc(mod).upgrade(mod);768 const src_loc = func.decl.navSrcLoc(mod).upgrade(mod);
771 func.err_msg = try Module.ErrorMsg.create(func.gpa, src_loc, fmt, args);769 func.err_msg = try Zcu.ErrorMsg.create(func.gpa, src_loc, fmt, args);
772 return error.CodegenFail;770 return error.CodegenFail;
773}771}
774772
...@@ -992,7 +990,7 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32...@@ -992,7 +990,7 @@ fn addExtraAssumeCapacity(func: *CodeGen, extra: anytype) error{OutOfMemory}!u32
992}990}
993991
994/// Using a given `Type`, returns the corresponding type992/// Using a given `Type`, returns the corresponding type
995fn typeToValtype(ty: Type, mod: *Module) wasm.Valtype {993fn typeToValtype(ty: Type, mod: *Zcu) wasm.Valtype {
996 const target = mod.getTarget();994 const target = mod.getTarget();
997 const ip = &mod.intern_pool;995 const ip = &mod.intern_pool;
998 return switch (ty.zigTypeTag(mod)) {996 return switch (ty.zigTypeTag(mod)) {
...@@ -1032,14 +1030,14 @@ fn typeToValtype(ty: Type, mod: *Module) wasm.Valtype {...@@ -1032,14 +1030,14 @@ fn typeToValtype(ty: Type, mod: *Module) wasm.Valtype {
1032}1030}
10331031
1034/// Using a given `Type`, returns the byte representation of its wasm value type1032/// Using a given `Type`, returns the byte representation of its wasm value type
1035fn genValtype(ty: Type, mod: *Module) u8 {1033fn genValtype(ty: Type, mod: *Zcu) u8 {
1036 return wasm.valtype(typeToValtype(ty, mod));1034 return wasm.valtype(typeToValtype(ty, mod));
1037}1035}
10381036
1039/// Using a given `Type`, returns the corresponding wasm value type1037/// Using a given `Type`, returns the corresponding wasm value type
1040/// Differently from `genValtype` this also allows `void` to create a block1038/// Differently from `genValtype` this also allows `void` to create a block
1041/// with no return type1039/// with no return type
1042fn genBlockType(ty: Type, mod: *Module) u8 {1040fn genBlockType(ty: Type, mod: *Zcu) u8 {
1043 return switch (ty.ip_index) {1041 return switch (ty.ip_index) {
1044 .void_type, .noreturn_type => wasm.block_empty,1042 .void_type, .noreturn_type => wasm.block_empty,
1045 else => genValtype(ty, mod),1043 else => genValtype(ty, mod),
...@@ -1149,7 +1147,7 @@ fn genFunctype(...@@ -1149,7 +1147,7 @@ fn genFunctype(
1149 cc: std.builtin.CallingConvention,1147 cc: std.builtin.CallingConvention,
1150 params: []const InternPool.Index,1148 params: []const InternPool.Index,
1151 return_type: Type,1149 return_type: Type,
1152 mod: *Module,1150 mod: *Zcu,
1153) !wasm.Type {1151) !wasm.Type {
1154 var temp_params = std.ArrayList(wasm.Valtype).init(gpa);1152 var temp_params = std.ArrayList(wasm.Valtype).init(gpa);
1155 defer temp_params.deinit();1153 defer temp_params.deinit();
...@@ -1204,7 +1202,7 @@ fn genFunctype(...@@ -1204,7 +1202,7 @@ fn genFunctype(
12041202
1205pub fn generate(1203pub fn generate(
1206 bin_file: *link.File,1204 bin_file: *link.File,
1207 src_loc: Module.SrcLoc,1205 src_loc: Zcu.SrcLoc,
1208 func_index: InternPool.Index,1206 func_index: InternPool.Index,
1209 air: Air,1207 air: Air,
1210 liveness: Liveness,1208 liveness: Liveness,
...@@ -1405,7 +1403,7 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV...@@ -1405,7 +1403,7 @@ fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWV
1405 return result;1403 return result;
1406}1404}
14071405
1408fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, mod: *Module) bool {1406fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, mod: *Zcu) bool {
1409 switch (cc) {1407 switch (cc) {
1410 .Unspecified, .Inline => return isByRef(return_type, mod),1408 .Unspecified, .Inline => return isByRef(return_type, mod),
1411 .C => {1409 .C => {
...@@ -1713,7 +1711,7 @@ fn arch(func: *const CodeGen) std.Target.Cpu.Arch {...@@ -1713,7 +1711,7 @@ fn arch(func: *const CodeGen) std.Target.Cpu.Arch {
17131711
1714/// For a given `Type`, will return true when the type will be passed1712/// For a given `Type`, will return true when the type will be passed
1715/// by reference, rather than by value1713/// by reference, rather than by value
1716fn isByRef(ty: Type, mod: *Module) bool {1714fn isByRef(ty: Type, mod: *Zcu) bool {
1717 const ip = &mod.intern_pool;1715 const ip = &mod.intern_pool;
1718 const target = mod.getTarget();1716 const target = mod.getTarget();
1719 switch (ty.zigTypeTag(mod)) {1717 switch (ty.zigTypeTag(mod)) {
...@@ -1785,7 +1783,7 @@ const SimdStoreStrategy = enum {...@@ -1785,7 +1783,7 @@ const SimdStoreStrategy = enum {
1785/// This means when a given type is 128 bits and either the simd128 or relaxed-simd1783/// This means when a given type is 128 bits and either the simd128 or relaxed-simd
1786/// features are enabled, the function will return `.direct`. This would allow to store1784/// features are enabled, the function will return `.direct`. This would allow to store
1787/// it using a instruction, rather than an unrolled version.1785/// it using a instruction, rather than an unrolled version.
1788fn determineSimdStoreStrategy(ty: Type, mod: *Module) SimdStoreStrategy {1786fn determineSimdStoreStrategy(ty: Type, mod: *Zcu) SimdStoreStrategy {
1789 std.debug.assert(ty.zigTypeTag(mod) == .Vector);1787 std.debug.assert(ty.zigTypeTag(mod) == .Vector);
1790 if (ty.bitSize(mod) != 128) return .unrolled;1788 if (ty.bitSize(mod) != 128) return .unrolled;
1791 const hasFeature = std.Target.wasm.featureSetHas;1789 const hasFeature = std.Target.wasm.featureSetHas;
...@@ -2325,14 +2323,19 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void...@@ -2325,14 +2323,19 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void
2325 WValue{ .imm32 = @as(u32, @truncate(mask)) }2323 WValue{ .imm32 = @as(u32, @truncate(mask)) }
2326 else2324 else
2327 WValue{ .imm64 = mask };2325 WValue{ .imm64 = mask };
2326 const wrap_mask_val = if (ptr_info.packed_offset.host_size <= 4)
2327 WValue{ .imm32 = @truncate(~@as(u64, 0) >> @intCast(64 - ty.bitSize(mod))) }
2328 else
2329 WValue{ .imm64 = ~@as(u64, 0) >> @intCast(64 - ty.bitSize(mod)) };
23282330
2329 try func.emitWValue(lhs);2331 try func.emitWValue(lhs);
2330 const loaded = try func.load(lhs, int_elem_ty, 0);2332 const loaded = try func.load(lhs, int_elem_ty, 0);
2331 const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and");2333 const anded = try func.binOp(loaded, mask_val, int_elem_ty, .@"and");
2332 const extended_value = try func.intcast(rhs, ty, int_elem_ty);2334 const extended_value = try func.intcast(rhs, ty, int_elem_ty);
2335 const masked_value = try func.binOp(extended_value, wrap_mask_val, int_elem_ty, .@"and");
2333 const shifted_value = if (ptr_info.packed_offset.bit_offset > 0) shifted: {2336 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);2337 break :shifted try func.binOp(masked_value, shift_val, int_elem_ty, .shl);
2335 } else extended_value;2338 } else masked_value;
2336 const result = try func.binOp(anded, shifted_value, int_elem_ty, .@"or");2339 const result = try func.binOp(anded, shifted_value, int_elem_ty, .@"or");
2337 // lhs is still on the stack2340 // lhs is still on the stack
2338 try func.store(.stack, result, int_elem_ty, lhs.offset());2341 try func.store(.stack, result, int_elem_ty, lhs.offset());
...@@ -2515,7 +2518,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu...@@ -2515,7 +2518,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu
2515 .valtype1 = typeToValtype(ty, mod),2518 .valtype1 = typeToValtype(ty, mod),
2516 .width = abi_size * 8,2519 .width = abi_size * 8,
2517 .op = .load,2520 .op = .load,
2518 .signedness = .unsigned,2521 .signedness = if (ty.isSignedInt(mod)) .signed else .unsigned,
2519 });2522 });
25202523
2521 try func.addMemArg(2524 try func.addMemArg(
...@@ -2800,10 +2803,7 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2800,10 +2803,7 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2800 switch (wasm_bits) {2803 switch (wasm_bits) {
2801 32 => {2804 32 => {
2802 try func.emitWValue(operand);2805 try func.emitWValue(operand);
2803 if (wasm_bits != int_bits) {2806
2804 try func.addImm32(wasm_bits - int_bits);
2805 try func.addTag(.i32_shl);
2806 }
2807 try func.addImm32(31);2807 try func.addImm32(31);
2808 try func.addTag(.i32_shr_s);2808 try func.addTag(.i32_shr_s);
28092809
...@@ -2815,15 +2815,10 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2815,15 +2815,10 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2815 try func.addTag(.i32_xor);2815 try func.addTag(.i32_xor);
2816 try func.emitWValue(tmp);2816 try func.emitWValue(tmp);
2817 try func.addTag(.i32_sub);2817 try func.addTag(.i32_sub);
2818
2819 _ = try func.wrapOperand(.stack, ty);
2820 },2818 },
2821 64 => {2819 64 => {
2822 try func.emitWValue(operand);2820 try func.emitWValue(operand);
2823 if (wasm_bits != int_bits) {2821
2824 try func.addImm64(wasm_bits - int_bits);
2825 try func.addTag(.i64_shl);
2826 }
2827 try func.addImm64(63);2822 try func.addImm64(63);
2828 try func.addTag(.i64_shr_s);2823 try func.addTag(.i64_shr_s);
28292824
...@@ -2835,8 +2830,6 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2835,8 +2830,6 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2835 try func.addTag(.i64_xor);2830 try func.addTag(.i64_xor);
2836 try func.emitWValue(tmp);2831 try func.emitWValue(tmp);
2837 try func.addTag(.i64_sub);2832 try func.addTag(.i64_sub);
2838
2839 _ = try func.wrapOperand(.stack, ty);
2840 },2833 },
2841 128 => {2834 128 => {
2842 const mask = try func.allocStack(Type.u128);2835 const mask = try func.allocStack(Type.u128);
...@@ -2844,10 +2837,6 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2844,10 +2837,6 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2844 try func.emitWValue(mask);2837 try func.emitWValue(mask);
28452838
2846 _ = try func.load(operand, Type.u64, 8);2839 _ = 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 try func.addImm64(63);2840 try func.addImm64(63);
2852 try func.addTag(.i64_shr_s);2841 try func.addTag(.i64_shr_s);
28532842
...@@ -2860,9 +2849,8 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2860,9 +2849,8 @@ fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
28602849
2861 const a = try func.binOpBigInt(operand, mask, Type.u128, .xor);2850 const a = try func.binOpBigInt(operand, mask, Type.u128, .xor);
2862 const b = try func.binOpBigInt(a, mask, Type.u128, .sub);2851 const b = try func.binOpBigInt(a, mask, Type.u128, .sub);
2863 const result = try func.wrapOperand(b, ty);
28642852
2865 func.finishAir(inst, result, &.{ty_op.operand});2853 func.finishAir(inst, b, &.{ty_op.operand});
2866 return;2854 return;
2867 },2855 },
2868 else => unreachable,2856 else => unreachable,
...@@ -3058,14 +3046,28 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {...@@ -3058,14 +3046,28 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
3058 switch (wasm_bits) {3046 switch (wasm_bits) {
3059 32 => {3047 32 => {
3060 try func.emitWValue(operand);3048 try func.emitWValue(operand);
3061 try func.addImm32((@as(u32, 1) << @intCast(int_bits)) - 1);3049 if (ty.isSignedInt(mod)) {
3062 try func.addTag(.i32_and);3050 try func.addImm32(32 - int_bits);
3051 try func.addTag(.i32_shl);
3052 try func.addImm32(32 - int_bits);
3053 try func.addTag(.i32_shr_s);
3054 } else {
3055 try func.addImm32(~@as(u32, 0) >> @intCast(32 - int_bits));
3056 try func.addTag(.i32_and);
3057 }
3063 return .stack;3058 return .stack;
3064 },3059 },
3065 64 => {3060 64 => {
3066 try func.emitWValue(operand);3061 try func.emitWValue(operand);
3067 try func.addImm64((@as(u64, 1) << @intCast(int_bits)) - 1);3062 if (ty.isSignedInt(mod)) {
3068 try func.addTag(.i64_and);3063 try func.addImm64(64 - int_bits);
3064 try func.addTag(.i64_shl);
3065 try func.addImm64(64 - int_bits);
3066 try func.addTag(.i64_shr_s);
3067 } else {
3068 try func.addImm64(~@as(u64, 0) >> @intCast(64 - int_bits));
3069 try func.addTag(.i64_and);
3070 }
3069 return .stack;3071 return .stack;
3070 },3072 },
3071 128 => {3073 128 => {
...@@ -3078,8 +3080,15 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {...@@ -3078,8 +3080,15 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue {
30783080
3079 try func.emitWValue(result);3081 try func.emitWValue(result);
3080 _ = try func.load(operand, Type.u64, 8);3082 _ = try func.load(operand, Type.u64, 8);
3081 try func.addImm64((@as(u64, 1) << @intCast(int_bits - 64)) - 1);3083 if (ty.isSignedInt(mod)) {
3082 try func.addTag(.i64_and);3084 try func.addImm64(128 - int_bits);
3085 try func.addTag(.i64_shl);
3086 try func.addImm64(128 - int_bits);
3087 try func.addTag(.i64_shr_s);
3088 } else {
3089 try func.addImm64(~@as(u64, 0) >> @intCast(128 - int_bits));
3090 try func.addTag(.i64_and);
3091 }
3083 try func.store(.stack, .stack, Type.u64, result.offset() + 8);3092 try func.store(.stack, .stack, Type.u64, result.offset() + 8);
30843093
3085 return result;3094 return result;
...@@ -3201,22 +3210,6 @@ fn lowerDeclRefValue(func: *CodeGen, decl_index: InternPool.DeclIndex, offset: u...@@ -3201,22 +3210,6 @@ fn lowerDeclRefValue(func: *CodeGen, decl_index: InternPool.DeclIndex, offset: u
3201 } else return WValue{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };3210 } else return WValue{ .memory_offset = .{ .pointer = target_sym_index, .offset = offset } };
3202}3211}
32033212
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
3220/// Asserts that `isByRef` returns `false` for `ty`.3213/// Asserts that `isByRef` returns `false` for `ty`.
3221fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {3214fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3222 const mod = func.bin_file.base.comp.module.?;3215 const mod = func.bin_file.base.comp.module.?;
...@@ -3268,18 +3261,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3268,18 +3261,12 @@ fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3268 const int_info = ty.intInfo(mod);3261 const int_info = ty.intInfo(mod);
3269 switch (int_info.signedness) {3262 switch (int_info.signedness) {
3270 .signed => switch (int_info.bits) {3263 .signed => switch (int_info.bits) {
3271 0...32 => return WValue{ .imm32 = @as(u32, @intCast(toTwosComplement(3264 0...32 => return WValue{ .imm32 = @bitCast(@as(i32, @intCast(val.toSignedInt(mod)))) },
3272 val.toSignedInt(mod),3265 33...64 => return WValue{ .imm64 = @bitCast(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 ) },
3279 else => unreachable,3266 else => unreachable,
3280 },3267 },
3281 .unsigned => switch (int_info.bits) {3268 .unsigned => switch (int_info.bits) {
3282 0...32 => return WValue{ .imm32 = @as(u32, @intCast(val.toUnsignedInt(mod))) },3269 0...32 => return WValue{ .imm32 = @intCast(val.toUnsignedInt(mod)) },
3283 33...64 => return WValue{ .imm64 = val.toUnsignedInt(mod) },3270 33...64 => return WValue{ .imm64 = val.toUnsignedInt(mod) },
3284 else => unreachable,3271 else => unreachable,
3285 },3272 },
...@@ -3447,7 +3434,7 @@ fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 {...@@ -3447,7 +3434,7 @@ fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 {
3447 assert(ptr.base_addr == .int);3434 assert(ptr.base_addr == .int);
3448 return @intCast(ptr.byte_offset);3435 return @intCast(ptr.byte_offset);
3449 },3436 },
3450 .err => |err| @as(i32, @bitCast(@as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(err.name).?)))),3437 .err => |err| @as(i32, @bitCast(@as(Zcu.ErrorInt, @intCast(mod.global_error_set.getIndex(err.name).?)))),
3451 else => unreachable,3438 else => unreachable,
3452 },3439 },
3453 }3440 }
...@@ -3458,11 +3445,11 @@ fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 {...@@ -3458,11 +3445,11 @@ fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 {
3458 };3445 };
3459}3446}
34603447
3461fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, mod: *Module) i32 {3448fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, mod: *Zcu) i32 {
3462 return intStorageAsI32(ip.indexToKey(int).int.storage, mod);3449 return intStorageAsI32(ip.indexToKey(int).int.storage, mod);
3463}3450}
34643451
3465fn intStorageAsI32(storage: InternPool.Key.Int.Storage, mod: *Module) i32 {3452fn intStorageAsI32(storage: InternPool.Key.Int.Storage, mod: *Zcu) i32 {
3466 return switch (storage) {3453 return switch (storage) {
3467 .i64 => |x| @as(i32, @intCast(x)),3454 .i64 => |x| @as(i32, @intCast(x)),
3468 .u64 => |x| @as(i32, @bitCast(@as(u32, @intCast(x)))),3455 .u64 => |x| @as(i32, @bitCast(@as(u32, @intCast(x)))),
...@@ -3618,29 +3605,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO...@@ -3618,29 +3605,11 @@ fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareO
3618 // incase of an actual integer, we emit the correct signedness3605 // incase of an actual integer, we emit the correct signedness
3619 break :blk ty.intInfo(mod).signedness;3606 break :blk ty.intInfo(mod).signedness;
3620 };3607 };
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;
36393608
3640 // ensure that when we compare pointers, we emit3609 // ensure that when we compare pointers, we emit
3641 // the true pointer of a stack value, rather than the stack pointer.3610 // the true pointer of a stack value, rather than the stack pointer.
3642 try func.lowerToStack(lhs_wasm);3611 try func.lowerToStack(lhs);
3643 try func.lowerToStack(rhs_wasm);3612 try func.lowerToStack(rhs);
36443613
3645 const opcode: wasm.Opcode = buildOpcode(.{3614 const opcode: wasm.Opcode = buildOpcode(.{
3646 .valtype1 = typeToValtype(ty, mod),3615 .valtype1 = typeToValtype(ty, mod),
...@@ -3760,32 +3729,49 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3760,32 +3729,49 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3760 try func.addLabel(.local_set, not_tmp.local.value);3729 try func.addLabel(.local_set, not_tmp.local.value);
3761 break :result not_tmp;3730 break :result not_tmp;
3762 } else {3731 } else {
3763 const operand_bits = operand_ty.intInfo(mod).bits;3732 const int_info = operand_ty.intInfo(mod);
3764 const wasm_bits = toWasmBits(operand_bits) orelse {3733 const wasm_bits = toWasmBits(int_info.bits) orelse {
3765 return func.fail("TODO: Implement binary NOT for integer with bitsize '{d}'", .{operand_bits});3734 return func.fail("TODO: Implement binary NOT for {}", .{operand_ty.fmt(mod)});
3766 };3735 };
37673736
3768 switch (wasm_bits) {3737 switch (wasm_bits) {
3769 32 => {3738 32 => {
3770 const bin_op = try func.binOp(operand, .{ .imm32 = ~@as(u32, 0) }, operand_ty, .xor);3739 try func.emitWValue(operand);
3771 break :result try (try func.wrapOperand(bin_op, operand_ty)).toLocal(func, operand_ty);3740 try func.addImm32(switch (int_info.signedness) {
3741 .unsigned => ~@as(u32, 0) >> @intCast(32 - int_info.bits),
3742 .signed => ~@as(u32, 0),
3743 });
3744 try func.addTag(.i32_xor);
3745 break :result try @as(WValue, .stack).toLocal(func, operand_ty);
3772 },3746 },
3773 64 => {3747 64 => {
3774 const bin_op = try func.binOp(operand, .{ .imm64 = ~@as(u64, 0) }, operand_ty, .xor);3748 try func.emitWValue(operand);
3775 break :result try (try func.wrapOperand(bin_op, operand_ty)).toLocal(func, operand_ty);3749 try func.addImm64(switch (int_info.signedness) {
3750 .unsigned => ~@as(u64, 0) >> @intCast(64 - int_info.bits),
3751 .signed => ~@as(u64, 0),
3752 });
3753 try func.addTag(.i64_xor);
3754 break :result try @as(WValue, .stack).toLocal(func, operand_ty);
3776 },3755 },
3777 128 => {3756 128 => {
3778 const result_ptr = try func.allocStack(operand_ty);3757 const ptr = try func.allocStack(operand_ty);
3779 try func.emitWValue(result_ptr);3758
3780 const msb = try func.load(operand, Type.u64, 0);3759 try func.emitWValue(ptr);
3781 const msb_xor = try func.binOp(msb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);3760 _ = try func.load(operand, Type.u64, 0);
3782 try func.store(.{ .stack = {} }, msb_xor, Type.u64, 0 + result_ptr.offset());3761 try func.addImm64(~@as(u64, 0));
37833762 try func.addTag(.i64_xor);
3784 try func.emitWValue(result_ptr);3763 try func.store(.stack, .stack, Type.u64, ptr.offset());
3785 const lsb = try func.load(operand, Type.u64, 8);3764
3786 const lsb_xor = try func.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);3765 try func.emitWValue(ptr);
3787 try func.store(result_ptr, lsb_xor, Type.u64, 8 + result_ptr.offset());3766 _ = try func.load(operand, Type.u64, 8);
3788 break :result result_ptr;3767 try func.addImm64(switch (int_info.signedness) {
3768 .unsigned => ~@as(u64, 0) >> @intCast(128 - int_info.bits),
3769 .signed => ~@as(u64, 0),
3770 });
3771 try func.addTag(.i64_xor);
3772 try func.store(.stack, .stack, Type.u64, ptr.offset() + 8);
3773
3774 break :result ptr;
3789 },3775 },
3790 else => unreachable,3776 else => unreachable,
3791 }3777 }
...@@ -3812,25 +3798,44 @@ fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3812,25 +3798,44 @@ fn airUnreachable(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3812}3798}
38133799
3814fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {3800fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3801 const mod = func.bin_file.base.comp.module.?;
3815 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3802 const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3803 const operand = try func.resolveInst(ty_op.operand);
3804 const wanted_ty = func.typeOfIndex(inst);
3805 const given_ty = func.typeOf(ty_op.operand);
3806
3807 const bit_size = given_ty.bitSize(mod);
3808 const needs_wrapping = (given_ty.isSignedInt(mod) != wanted_ty.isSignedInt(mod)) and
3809 bit_size != 32 and bit_size != 64 and bit_size != 128;
3810
3816 const result = result: {3811 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 if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) {3812 if (given_ty.isAnyFloat() or wanted_ty.isAnyFloat()) {
3821 const bitcast_result = try func.bitcast(wanted_ty, given_ty, operand);3813 const bitcast_result = try func.bitcast(wanted_ty, given_ty, operand);
3822 break :result try bitcast_result.toLocal(func, wanted_ty);3814 break :result try bitcast_result.toLocal(func, wanted_ty);
3823 }3815 }
3824 const mod = func.bin_file.base.comp.module.?;3816
3825 if (isByRef(given_ty, mod) and !isByRef(wanted_ty, mod)) {3817 if (isByRef(given_ty, mod) and !isByRef(wanted_ty, mod)) {
3826 const loaded_memory = try func.load(operand, wanted_ty, 0);3818 const loaded_memory = try func.load(operand, wanted_ty, 0);
3827 break :result try loaded_memory.toLocal(func, wanted_ty);3819 if (needs_wrapping) {
3820 break :result try (try func.wrapOperand(loaded_memory, wanted_ty)).toLocal(func, wanted_ty);
3821 } else {
3822 break :result try loaded_memory.toLocal(func, wanted_ty);
3823 }
3828 }3824 }
3829 if (!isByRef(given_ty, mod) and isByRef(wanted_ty, mod)) {3825 if (!isByRef(given_ty, mod) and isByRef(wanted_ty, mod)) {
3830 const stack_memory = try func.allocStack(wanted_ty);3826 const stack_memory = try func.allocStack(wanted_ty);
3831 try func.store(stack_memory, operand, given_ty, 0);3827 try func.store(stack_memory, operand, given_ty, 0);
3832 break :result stack_memory;3828 if (needs_wrapping) {
3829 break :result try (try func.wrapOperand(stack_memory, wanted_ty)).toLocal(func, wanted_ty);
3830 } else {
3831 break :result stack_memory;
3832 }
3833 }
3834
3835 if (needs_wrapping) {
3836 break :result try (try func.wrapOperand(operand, wanted_ty)).toLocal(func, wanted_ty);
3833 }3837 }
3838
3834 break :result func.reuseOperand(ty_op.operand, operand);3839 break :result func.reuseOperand(ty_op.operand, operand);
3835 };3840 };
3836 func.finishAir(inst, result, &.{ty_op.operand});3841 func.finishAir(inst, result, &.{ty_op.operand});
...@@ -4355,7 +4360,7 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -4355,7 +4360,7 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
43554360
4356 const op_bits = toWasmBits(@as(u16, @intCast(operand_ty.bitSize(mod)))).?;4361 const op_bits = toWasmBits(@as(u16, @intCast(operand_ty.bitSize(mod)))).?;
4357 const wanted_bits = toWasmBits(@as(u16, @intCast(ty.bitSize(mod)))).?;4362 const wanted_bits = toWasmBits(@as(u16, @intCast(ty.bitSize(mod)))).?;
4358 const result = if (op_bits == wanted_bits and !ty.isSignedInt(mod))4363 const result = if (op_bits == wanted_bits)
4359 func.reuseOperand(ty_op.operand, operand)4364 func.reuseOperand(ty_op.operand, operand)
4360 else4365 else
4361 try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty);4366 try (try func.intcast(operand, operand_ty, ty)).toLocal(func, ty);
...@@ -4377,37 +4382,17 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -4377,37 +4382,17 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
4377 const op_bits = toWasmBits(given_bitsize).?;4382 const op_bits = toWasmBits(given_bitsize).?;
4378 const wanted_bits = toWasmBits(wanted_bitsize).?;4383 const wanted_bits = toWasmBits(wanted_bitsize).?;
4379 if (op_bits == wanted_bits) {4384 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 return operand;4385 return operand;
4391 }4386 }
43924387
4393 if (op_bits > 32 and op_bits <= 64 and wanted_bits == 32) {4388 if (op_bits == 64 and wanted_bits == 32) {
4394 try func.emitWValue(operand);4389 try func.emitWValue(operand);
4395 try func.addTag(.i32_wrap_i64);4390 try func.addTag(.i32_wrap_i64);
4396 if (given.isSignedInt(mod) and wanted_bitsize < 32)4391 return .stack;
4397 return func.wrapOperand(.{ .stack = {} }, wanted)4392 } else if (op_bits == 32 and wanted_bits == 64) {
4398 else4393 try func.emitWValue(operand);
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);
4406 try func.addTag(if (wanted.isSignedInt(mod)) .i64_extend_i32_s else .i64_extend_i32_u);4394 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)4395 return .stack;
4408 return func.wrapOperand(.{ .stack = {} }, wanted)
4409 else
4410 return WValue{ .stack = {} };
4411 } else if (wanted_bits == 128) {4396 } else if (wanted_bits == 128) {
4412 // for 128bit integers we store the integer in the virtual stack, rather than a local4397 // for 128bit integers we store the integer in the virtual stack, rather than a local
4413 const stack_ptr = try func.allocStack(wanted);4398 const stack_ptr = try func.allocStack(wanted);
...@@ -4416,17 +4401,18 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro...@@ -4416,17 +4401,18 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro
4416 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it4401 // for 32 bit integers, we first coerce the value into a 64 bit integer before storing it
4417 // meaning less store operations are required.4402 // meaning less store operations are required.
4418 const lhs = if (op_bits == 32) blk: {4403 const lhs = if (op_bits == 32) blk: {
4419 break :blk try func.intcast(operand, given, if (wanted.isSignedInt(mod)) Type.i64 else Type.u64);4404 const sign_ty = if (wanted.isSignedInt(mod)) Type.i64 else Type.u64;
4405 break :blk try (try func.intcast(operand, given, sign_ty)).toLocal(func, sign_ty);
4420 } else operand;4406 } else operand;
44214407
4422 // store msb first4408 // store msb first
4423 try func.store(.{ .stack = {} }, lhs, Type.u64, 0 + stack_ptr.offset());4409 try func.store(.stack, lhs, Type.u64, 0 + stack_ptr.offset());
44244410
4425 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value4411 // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value
4426 if (wanted.isSignedInt(mod)) {4412 if (wanted.isSignedInt(mod)) {
4427 try func.emitWValue(stack_ptr);4413 try func.emitWValue(stack_ptr);
4428 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);4414 const shr = try func.binOp(lhs, .{ .imm64 = 63 }, Type.i64, .shr);
4429 try func.store(.{ .stack = {} }, shr, Type.u64, 8 + stack_ptr.offset());4415 try func.store(.stack, shr, Type.u64, 8 + stack_ptr.offset());
4430 } else {4416 } else {
4431 // Ensure memory of lsb is zero'd4417 // Ensure memory of lsb is zero'd
4432 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);4418 try func.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
...@@ -5823,25 +5809,34 @@ fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5823,25 +5809,34 @@ fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5823 };5809 };
58245810
5825 switch (wasm_bits) {5811 switch (wasm_bits) {
5812 32 => {
5813 try func.emitWValue(operand);
5814 if (op_ty.isSignedInt(mod) and bits != wasm_bits) {
5815 _ = try func.wrapOperand(.stack, try mod.intType(.unsigned, bits));
5816 }
5817 try func.addTag(.i32_popcnt);
5818 },
5819 64 => {
5820 try func.emitWValue(operand);
5821 if (op_ty.isSignedInt(mod) and bits != wasm_bits) {
5822 _ = try func.wrapOperand(.stack, try mod.intType(.unsigned, bits));
5823 }
5824 try func.addTag(.i64_popcnt);
5825 try func.addTag(.i32_wrap_i64);
5826 try func.emitWValue(operand);
5827 },
5826 128 => {5828 128 => {
5827 _ = try func.load(operand, Type.u64, 0);5829 _ = try func.load(operand, Type.u64, 0);
5828 try func.addTag(.i64_popcnt);5830 try func.addTag(.i64_popcnt);
5829 _ = try func.load(operand, Type.u64, 8);5831 _ = try func.load(operand, Type.u64, 8);
5832 if (op_ty.isSignedInt(mod) and bits != wasm_bits) {
5833 _ = try func.wrapOperand(.stack, try mod.intType(.unsigned, bits - 64));
5834 }
5830 try func.addTag(.i64_popcnt);5835 try func.addTag(.i64_popcnt);
5831 try func.addTag(.i64_add);5836 try func.addTag(.i64_add);
5832 try func.addTag(.i32_wrap_i64);5837 try func.addTag(.i32_wrap_i64);
5833 },5838 },
5834 else => {5839 else => unreachable,
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 },
5845 }5840 }
58465841
5847 const result = try func.allocLocal(result_ty);5842 const result = try func.allocLocal(result_ty);
...@@ -5877,7 +5872,7 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5877,7 +5872,7 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5877 const reversed = if (bits == 32)5872 const reversed = if (bits == 32)
5878 intrin_ret5873 intrin_ret
5879 else5874 else
5880 try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, Type.u32, .shr);5875 try func.binOp(intrin_ret, .{ .imm32 = 32 - bits }, ty, .shr);
5881 const result = try reversed.toLocal(func, ty);5876 const result = try reversed.toLocal(func, ty);
5882 func.finishAir(inst, result, &.{ty_op.operand});5877 func.finishAir(inst, result, &.{ty_op.operand});
5883 },5878 },
...@@ -5891,7 +5886,7 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5891,7 +5886,7 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5891 const reversed = if (bits == 64)5886 const reversed = if (bits == 64)
5892 intrin_ret5887 intrin_ret
5893 else5888 else
5894 try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, Type.u64, .shr);5889 try func.binOp(intrin_ret, .{ .imm64 = 64 - bits }, ty, .shr);
5895 const result = try reversed.toLocal(func, ty);5890 const result = try reversed.toLocal(func, ty);
5896 func.finishAir(inst, result, &.{ty_op.operand});5891 func.finishAir(inst, result, &.{ty_op.operand});
5897 },5892 },
...@@ -5928,7 +5923,11 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -5928,7 +5923,11 @@ fn airBitReverse(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
5928 defer tmp.free(func);5923 defer tmp.free(func);
5929 try func.addLabel(.local_tee, tmp.local.value);5924 try func.addLabel(.local_tee, tmp.local.value);
5930 try func.emitWValue(.{ .imm64 = 128 - bits });5925 try func.emitWValue(.{ .imm64 = 128 - bits });
5931 try func.addTag(.i64_shr_u);5926 if (ty.isSignedInt(mod)) {
5927 try func.addTag(.i64_shr_s);
5928 } else {
5929 try func.addTag(.i64_shr_u);
5930 }
5932 try func.store(.stack, .stack, Type.u64, result.offset() + 8);5931 try func.store(.stack, .stack, Type.u64, result.offset() + 8);
5933 try func.addLabel(.local_get, tmp.local.value);5932 try func.addLabel(.local_get, tmp.local.value);
5934 try func.emitWValue(.{ .imm64 = bits - 64 });5933 try func.emitWValue(.{ .imm64 = bits - 64 });
...@@ -5996,8 +5995,8 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro...@@ -5996,8 +5995,8 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
5996 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5995 const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5997 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;5996 const extra = func.air.extraData(Air.Bin, ty_pl.payload).data;
59985997
5999 const lhs_op = try func.resolveInst(extra.lhs);5998 const lhs = try func.resolveInst(extra.lhs);
6000 const rhs_op = try func.resolveInst(extra.rhs);5999 const rhs = try func.resolveInst(extra.rhs);
6001 const lhs_ty = func.typeOf(extra.lhs);6000 const lhs_ty = func.typeOf(extra.lhs);
6002 const mod = func.bin_file.base.comp.module.?;6001 const mod = func.bin_file.base.comp.module.?;
60036002
...@@ -6012,7 +6011,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro...@@ -6012,7 +6011,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
6012 };6011 };
60136012
6014 if (wasm_bits == 128) {6013 if (wasm_bits == 128) {
6015 const result = try func.addSubWithOverflowBigInt(lhs_op, rhs_op, lhs_ty, func.typeOfIndex(inst), op);6014 const result = try func.addSubWithOverflowBigInt(lhs, rhs, lhs_ty, func.typeOfIndex(inst), op);
6016 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });6015 return func.finishAir(inst, result, &.{ extra.lhs, extra.rhs });
6017 }6016 }
60186017
...@@ -6022,24 +6021,6 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro...@@ -6022,24 +6021,6 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
6022 else => unreachable,6021 else => unreachable,
6023 };6022 };
60246023
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 const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);6024 const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, op)).toLocal(func, lhs_ty);
6044 var result = if (wasm_bits != int_info.bits) blk: {6025 var result = if (wasm_bits != int_info.bits) blk: {
6045 break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty);6026 break :blk try (try func.wrapOperand(bin_op, lhs_ty)).toLocal(func, lhs_ty);
...@@ -6053,8 +6034,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro...@@ -6053,8 +6034,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro
6053 const lt = try func.cmp(bin_op, lhs, lhs_ty, .lt);6034 const lt = try func.cmp(bin_op, lhs, lhs_ty, .lt);
6054 break :blk try func.binOp(cmp_zero, lt, Type.u32, .xor);6035 break :blk try func.binOp(cmp_zero, lt, Type.u32, .xor);
6055 }6036 }
6056 const abs = try func.signExtendInt(bin_op, lhs_ty);6037 break :blk try func.cmp(bin_op, bin_op, lhs_ty, .neq);
6057 break :blk try func.cmp(abs, bin_op, lhs_ty, .neq);
6058 } else if (wasm_bits == int_info.bits)6038 } else if (wasm_bits == int_info.bits)
6059 try func.cmp(bin_op, lhs, lhs_ty, cmp_op)6039 try func.cmp(bin_op, lhs, lhs_ty, cmp_op)
6060 else6040 else
...@@ -6150,7 +6130,6 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6150,7 +6130,6 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6150 }6130 }
61516131
6152 const int_info = lhs_ty.intInfo(mod);6132 const int_info = lhs_ty.intInfo(mod);
6153 const is_signed = int_info.signedness == .signed;
6154 const wasm_bits = toWasmBits(int_info.bits) orelse {6133 const wasm_bits = toWasmBits(int_info.bits) orelse {
6155 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});6134 return func.fail("TODO: Implement shl_with_overflow for integer bitsize: {d}", .{int_info.bits});
6156 };6135 };
...@@ -6170,13 +6149,7 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6170,13 +6149,7 @@ fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6170 } else shl;6149 } else shl;
6171 defer result.free(func); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits)6150 defer result.free(func); // it's a no-op to free the same local twice (when wasm_bits == int_info.bits)
61726151
6173 const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: {6152 const overflow_bit = 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: {
6180 try func.emitWValue(lhs);6153 try func.emitWValue(lhs);
6181 const shr = try func.binOp(result, rhs_final, lhs_ty, .shr);6154 const shr = try func.binOp(result, rhs_final, lhs_ty, .shr);
6182 break :blk try func.cmp(.{ .stack = {} }, shr, lhs_ty, .neq);6155 break :blk try func.cmp(.{ .stack = {} }, shr, lhs_ty, .neq);
...@@ -6245,10 +6218,8 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6245,10 +6218,8 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6245 break :blk down_cast;6218 break :blk down_cast;
6246 }6219 }
6247 } else if (int_info.signedness == .signed and wasm_bits == 32) blk: {6220 } else if (int_info.signedness == .signed and wasm_bits == 32) blk: {
6248 const lhs_abs = try func.signExtendInt(lhs, lhs_ty);6221 const bin_op = try (try func.binOp(lhs, rhs, lhs_ty, .mul)).toLocal(func, lhs_ty);
6249 const rhs_abs = try func.signExtendInt(rhs, lhs_ty);6222 const mul_abs = try func.wrapOperand(bin_op, 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);
6252 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);6223 _ = try func.cmp(mul_abs, bin_op, lhs_ty, .neq);
6253 try func.addLabel(.local_set, overflow_bit.local.value);6224 try func.addLabel(.local_set, overflow_bit.local.value);
6254 break :blk try func.wrapOperand(bin_op, lhs_ty);6225 break :blk try func.wrapOperand(bin_op, lhs_ty);
...@@ -6697,6 +6668,9 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6697,6 +6668,9 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6697 return func.fail("TODO: @byteSwap for vectors", .{});6668 return func.fail("TODO: @byteSwap for vectors", .{});
6698 }6669 }
6699 const int_info = ty.intInfo(mod);6670 const int_info = ty.intInfo(mod);
6671 const wasm_bits = toWasmBits(int_info.bits) orelse {
6672 return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits});
6673 };
67006674
6701 // bytes are no-op6675 // bytes are no-op
6702 if (int_info.bits == 8) {6676 if (int_info.bits == 8) {
...@@ -6704,73 +6678,34 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6704,73 +6678,34 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6704 }6678 }
67056679
6706 const result = result: {6680 const result = result: {
6707 switch (int_info.bits) {6681 switch (wasm_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 },
6737 32 => {6682 32 => {
6738 const shl_tmp = try func.binOp(operand, .{ .imm32 = 8 }, Type.u32, .shl);6683 const intrin_ret = try func.callIntrinsic(
6739 const lhs = try func.binOp(shl_tmp, .{ .imm32 = 0xFF00FF00 }, Type.u32, .@"and");6684 "__bswapsi2",
6740 const shr_tmp = try func.binOp(operand, .{ .imm32 = 8 }, Type.u32, .shr);6685 &.{.u32_type},
6741 const rhs = try func.binOp(shr_tmp, .{ .imm32 = 0x00FF00FF }, Type.u32, .@"and");6686 Type.u32,
6742 var tmp_or = try (try func.binOp(lhs, rhs, Type.u32, .@"or")).toLocal(func, Type.u32);6687 &.{operand},
67436688 );
6744 const shl = try func.binOp(tmp_or, .{ .imm32 = 16 }, Type.u32, .shl);6689 const swapped = if (int_info.bits == 32)
6745 const shr = try func.binOp(tmp_or, .{ .imm32 = 16 }, Type.u32, .shr);6690 intrin_ret
67466691 else
6747 tmp_or.free(func);6692 try func.binOp(intrin_ret, .{ .imm32 = 32 - int_info.bits }, ty, .shr);
67486693
6749 break :result try (try func.binOp(shl, shr, Type.u32, .@"or")).toLocal(func, Type.u32);6694 break :result try swapped.toLocal(func, ty);
6750 },6695 },
6751 64 => {6696 64 => {
6752 const shl_tmp_1 = try func.binOp(operand, .{ .imm64 = 8 }, Type.u64, .shl);6697 const intrin_ret = try func.callIntrinsic(
6753 const lhs_1 = try func.binOp(shl_tmp_1, .{ .imm64 = 0xFF00FF00FF00FF00 }, Type.u64, .@"and");6698 "__bswapdi2",
67546699 &.{.u64_type},
6755 const shr_tmp_1 = try func.binOp(operand, .{ .imm64 = 8 }, Type.u64, .shr);6700 Type.u64,
6756 const rhs_1 = try func.binOp(shr_tmp_1, .{ .imm64 = 0x00FF00FF00FF00FF }, Type.u64, .@"and");6701 &.{operand},
67576702 );
6758 var tmp_or_1 = try (try func.binOp(lhs_1, rhs_1, Type.u64, .@"or")).toLocal(func, Type.u64);6703 const swapped = if (int_info.bits == 64)
67596704 intrin_ret
6760 const shl_tmp_2 = try func.binOp(tmp_or_1, .{ .imm64 = 16 }, Type.u64, .shl);6705 else
6761 const lhs_2 = try func.binOp(shl_tmp_2, .{ .imm64 = 0xFFFF0000FFFF0000 }, Type.u64, .@"and");6706 try func.binOp(intrin_ret, .{ .imm64 = 64 - int_info.bits }, ty, .shr);
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);
67726707
6773 break :result try (try func.binOp(shl, shr, Type.u64, .@"or")).toLocal(func, Type.u64);6708 break :result try swapped.toLocal(func, ty);
6774 },6709 },
6775 else => return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),6710 else => return func.fail("TODO: @byteSwap for integers with bitsize {d}", .{int_info.bits}),
6776 }6711 }
...@@ -6779,32 +6714,24 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6779,32 +6714,24 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6779}6714}
67806715
6781fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6716fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6782 const mod = func.bin_file.base.comp.module.?;
6783 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6717 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67846718
6785 const ty = func.typeOfIndex(inst);6719 const ty = func.typeOfIndex(inst);
6786 const lhs = try func.resolveInst(bin_op.lhs);6720 const lhs = try func.resolveInst(bin_op.lhs);
6787 const rhs = try func.resolveInst(bin_op.rhs);6721 const rhs = try func.resolveInst(bin_op.rhs);
67886722
6789 const result = if (ty.isSignedInt(mod))6723 const result = try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
6790 try func.divSigned(lhs, rhs, ty)
6791 else
6792 try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
6793 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6724 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6794}6725}
67956726
6796fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6727fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6797 const mod = func.bin_file.base.comp.module.?;
6798 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6728 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67996729
6800 const ty = func.typeOfIndex(inst);6730 const ty = func.typeOfIndex(inst);
6801 const lhs = try func.resolveInst(bin_op.lhs);6731 const lhs = try func.resolveInst(bin_op.lhs);
6802 const rhs = try func.resolveInst(bin_op.rhs);6732 const rhs = try func.resolveInst(bin_op.rhs);
68036733
6804 const div_result = if (ty.isSignedInt(mod))6734 const div_result = try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
6805 try func.divSigned(lhs, rhs, ty)
6806 else
6807 try (try func.binOp(lhs, rhs, ty, .div)).toLocal(func, ty);
68086735
6809 if (ty.isAnyFloat()) {6736 if (ty.isAnyFloat()) {
6810 const trunc_result = try (try func.floatOp(.trunc, ty, &.{div_result})).toLocal(func, ty);6737 const trunc_result = try (try func.floatOp(.trunc, ty, &.{div_result})).toLocal(func, ty);
...@@ -6834,16 +6761,6 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6834,16 +6761,6 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6834 return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});6761 return func.fail("TODO: `@divFloor` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
6835 }6762 }
68366763
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 const zero = switch (wasm_bits) {6764 const zero = switch (wasm_bits) {
6848 32 => WValue{ .imm32 = 0 },6765 32 => WValue{ .imm32 = 0 },
6849 64 => WValue{ .imm64 = 0 },6766 64 => WValue{ .imm64 = 0 },
...@@ -6852,7 +6769,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6852,7 +6769,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
68526769
6853 // tee leaves the value on the stack and stores it in a local.6770 // tee leaves the value on the stack and stores it in a local.
6854 const quotient = try func.allocLocal(ty);6771 const quotient = try func.allocLocal(ty);
6855 _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .div);6772 _ = try func.binOp(lhs, rhs, ty, .div);
6856 try func.addLabel(.local_tee, quotient.local.value);6773 try func.addLabel(.local_tee, quotient.local.value);
68576774
6858 // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow6775 // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow
...@@ -6864,7 +6781,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6864,7 +6781,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6864 }6781 }
68656782
6866 // 0 if the signs of rhs_wasm and lhs_wasm are the same, 1 otherwise.6783 // 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);6784 _ = try func.binOp(lhs, rhs, ty, .xor);
6868 _ = try func.cmp(.stack, zero, ty, .lt);6785 _ = try func.cmp(.stack, zero, ty, .lt);
68696786
6870 switch (wasm_bits) {6787 switch (wasm_bits) {
...@@ -6879,7 +6796,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6879,7 +6796,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6879 else => unreachable,6796 else => unreachable,
6880 }6797 }
68816798
6882 _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .rem);6799 _ = try func.binOp(lhs, rhs, ty, .rem);
68836800
6884 if (wasm_bits == 64) {6801 if (wasm_bits == 64) {
6885 try func.addTag(.i64_eqz);6802 try func.addTag(.i64_eqz);
...@@ -6929,68 +6846,14 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -6929,68 +6846,14 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6929 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6846 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
6930}6847}
69316848
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
6963fn airRem(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {6849fn airRem(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6964 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6850 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
69656851
6966 const mod = func.bin_file.base.comp.module.?;
6967 const ty = func.typeOfIndex(inst);6852 const ty = func.typeOfIndex(inst);
6968 const lhs = try func.resolveInst(bin_op.lhs);6853 const lhs = try func.resolveInst(bin_op.lhs);
6969 const rhs = try func.resolveInst(bin_op.rhs);6854 const rhs = try func.resolveInst(bin_op.rhs);
69706855
6971 const result = if (ty.isSignedInt(mod)) result: {6856 const result = try func.binOp(lhs, rhs, ty, .rem);
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);
69946857
6995 const return_local = try result.toLocal(func, ty);6858 const return_local = try result.toLocal(func, ty);
6996 func.finishAir(inst, return_local, &.{ bin_op.lhs, bin_op.rhs });6859 func.finishAir(inst, return_local, &.{ bin_op.lhs, bin_op.rhs });
...@@ -7022,19 +6885,9 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7022,19 +6885,9 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7022 return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});6885 return func.fail("TODO: `@mod` for signed integers larger than 64 bits ({d} bits requested)", .{int_bits});
7023 }6886 }
70246887
7025 const lhs_wasm = if (wasm_bits != int_bits)6888 _ = try func.binOp(lhs, rhs, ty, .rem);
7026 try (try func.signExtendInt(lhs, ty)).toLocal(func, ty)6889 _ = try func.binOp(.stack, rhs, ty, .add);
7027 else6890 _ = try func.binOp(.stack, rhs, ty, .rem);
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);
7038 } else {6891 } else {
7039 return func.fail("TODO: implement `@mod` on floating point types for {}", .{func.target.cpu.arch});6892 return func.fail("TODO: implement `@mod` on floating point types for {}", .{func.target.cpu.arch});
7040 }6893 }
...@@ -7044,42 +6897,6 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7044,42 +6897,6 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7044 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6897 func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
7045}6898}
70466899
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
7083fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {6900fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
7084 assert(op == .add or op == .sub);6901 assert(op == .add or op == .sub);
7085 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6902 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
...@@ -7131,20 +6948,13 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {...@@ -7131,20 +6948,13 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
7131 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });6948 return func.finishAir(inst, result, &.{ bin_op.lhs, bin_op.rhs });
7132}6949}
71336950
7134fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue {6951fn signedSat(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue {
7135 const mod = func.bin_file.base.comp.module.?;6952 const mod = func.bin_file.base.comp.module.?;
7136 const int_info = ty.intInfo(mod);6953 const int_info = ty.intInfo(mod);
7137 const wasm_bits = toWasmBits(int_info.bits).?;6954 const wasm_bits = toWasmBits(int_info.bits).?;
7138 const is_wasm_bits = wasm_bits == int_info.bits;6955 const is_wasm_bits = wasm_bits == int_info.bits;
7139 const ext_ty = if (!is_wasm_bits) try mod.intType(int_info.signedness, wasm_bits) else ty;6956 const ext_ty = if (!is_wasm_bits) try mod.intType(int_info.signedness, wasm_bits) else ty;
71406957
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 const max_val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits - 1))) - 1));6958 const max_val: u64 = @as(u64, @intCast((@as(u65, 1) << @as(u7, @intCast(int_info.bits - 1))) - 1));
7149 const min_val: i64 = (-@as(i64, @intCast(@as(u63, @intCast(max_val))))) - 1;6959 const min_val: i64 = (-@as(i64, @intCast(@as(u63, @intCast(max_val))))) - 1;
7150 const max_wvalue = switch (wasm_bits) {6960 const max_wvalue = switch (wasm_bits) {
...@@ -7161,8 +6971,6 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,...@@ -7161,8 +6971,6 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type,
7161 var bin_result = try (try func.binOp(lhs, rhs, ext_ty, op)).toLocal(func, ext_ty);6971 var bin_result = try (try func.binOp(lhs, rhs, ext_ty, op)).toLocal(func, ext_ty);
7162 if (!is_wasm_bits) {6972 if (!is_wasm_bits) {
7163 defer bin_result.free(func); // not returned in this branch6973 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 try func.emitWValue(bin_result);6974 try func.emitWValue(bin_result);
7167 try func.emitWValue(max_wvalue);6975 try func.emitWValue(max_wvalue);
7168 _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt);6976 _ = try func.cmp(bin_result, max_wvalue, ext_ty, .lt);
...@@ -7547,7 +7355,7 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7547,7 +7355,7 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7547 var lowest: ?u32 = null;7355 var lowest: ?u32 = null;
7548 var highest: ?u32 = null;7356 var highest: ?u32 = null;
7549 for (0..names.len) |name_index| {7357 for (0..names.len) |name_index| {
7550 const err_int: Module.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[name_index]).?);7358 const err_int: Zcu.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[name_index]).?);
7551 if (lowest) |*l| {7359 if (lowest) |*l| {
7552 if (err_int < l.*) {7360 if (err_int < l.*) {
7553 l.* = err_int;7361 l.* = err_int;
src/arch/wasm/Emit.zig+2-4
...@@ -6,8 +6,6 @@ const std = @import("std");...@@ -6,8 +6,6 @@ const std = @import("std");
6const Mir = @import("Mir.zig");6const Mir = @import("Mir.zig");
7const link = @import("../../link.zig");7const link = @import("../../link.zig");
8const Zcu = @import("../../Zcu.zig");8const Zcu = @import("../../Zcu.zig");
9/// Deprecated.
10const Module = Zcu;
11const InternPool = @import("../../InternPool.zig");9const InternPool = @import("../../InternPool.zig");
12const codegen = @import("../../codegen.zig");10const codegen = @import("../../codegen.zig");
13const leb128 = std.leb;11const leb128 = std.leb;
...@@ -18,7 +16,7 @@ mir: Mir,...@@ -18,7 +16,7 @@ mir: Mir,
18bin_file: *link.File.Wasm,16bin_file: *link.File.Wasm,
19/// Possible error message. When set, the value is allocated and17/// Possible error message. When set, the value is allocated and
20/// must be freed manually.18/// must be freed manually.
21error_msg: ?*Module.ErrorMsg = null,19error_msg: ?*Zcu.ErrorMsg = null,
22/// The binary representation that will be emit by this module.20/// The binary representation that will be emit by this module.
23code: *std.ArrayList(u8),21code: *std.ArrayList(u8),
24/// List of allocated locals.22/// List of allocated locals.
...@@ -259,7 +257,7 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {...@@ -259,7 +257,7 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
259 const comp = emit.bin_file.base.comp;257 const comp = emit.bin_file.base.comp;
260 const zcu = comp.module.?;258 const zcu = comp.module.?;
261 const gpa = comp.gpa;259 const gpa = comp.gpa;
262 emit.error_msg = try Module.ErrorMsg.create(gpa, zcu.declPtr(emit.decl_index).navSrcLoc(zcu).upgrade(zcu), format, args);260 emit.error_msg = try Zcu.ErrorMsg.create(gpa, zcu.declPtr(emit.decl_index).navSrcLoc(zcu).upgrade(zcu), format, args);
263 return error.EmitFail;261 return error.EmitFail;
264}262}
265263
src/arch/wasm/abi.zig+2-4
...@@ -10,8 +10,6 @@ const assert = std.debug.assert;...@@ -10,8 +10,6 @@ const assert = std.debug.assert;
1010
11const Type = @import("../../type.zig").Type;11const Type = @import("../../type.zig").Type;
12const Zcu = @import("../../Zcu.zig");12const Zcu = @import("../../Zcu.zig");
13/// Deprecated.
14const Module = Zcu;
1513
16/// Defines how to pass a type as part of a function signature,14/// Defines how to pass a type as part of a function signature,
17/// both for parameters as well as return values.15/// both for parameters as well as return values.
...@@ -24,7 +22,7 @@ const direct: [2]Class = .{ .direct, .none };...@@ -24,7 +22,7 @@ const direct: [2]Class = .{ .direct, .none };
24/// Classifies a given Zig type to determine how they must be passed22/// Classifies a given Zig type to determine how they must be passed
25/// or returned as value within a wasm function.23/// or returned as value within a wasm function.
26/// When all elements result in `.none`, no value must be passed in or returned.24/// When all elements result in `.none`, no value must be passed in or returned.
27pub fn classifyType(ty: Type, mod: *Module) [2]Class {25pub fn classifyType(ty: Type, mod: *Zcu) [2]Class {
28 const ip = &mod.intern_pool;26 const ip = &mod.intern_pool;
29 const target = mod.getTarget();27 const target = mod.getTarget();
30 if (!ty.hasRuntimeBitsIgnoreComptime(mod)) return none;28 if (!ty.hasRuntimeBitsIgnoreComptime(mod)) return none;
...@@ -102,7 +100,7 @@ pub fn classifyType(ty: Type, mod: *Module) [2]Class {...@@ -102,7 +100,7 @@ pub fn classifyType(ty: Type, mod: *Module) [2]Class {
102/// Returns the scalar type a given type can represent.100/// Returns the scalar type a given type can represent.
103/// Asserts given type can be represented as scalar, such as101/// Asserts given type can be represented as scalar, such as
104/// a struct with a single scalar field.102/// a struct with a single scalar field.
105pub fn scalarType(ty: Type, mod: *Module) Type {103pub fn scalarType(ty: Type, mod: *Zcu) Type {
106 const ip = &mod.intern_pool;104 const ip = &mod.intern_pool;
107 switch (ty.zigTypeTag(mod)) {105 switch (ty.zigTypeTag(mod)) {
108 .Struct => {106 .Struct => {
test/behavior/math.zig+50-14
...@@ -393,9 +393,39 @@ fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int...@@ -393,9 +393,39 @@ fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int
393 return a + b;393 return a + b;
394}394}
395395
396fn not(comptime T: type, a: T) T {
397 return ~a;
398}
399
396test "binary not" {400test "binary not" {
397 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;401 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
398402
403 try expect(not(u0, 0) == 0);
404 try expect(not(u1, 0) == 1);
405 try expect(not(u1, 1) == 0);
406 try expect(not(u5, 0b01001) == 0b10110);
407 try expect(not(u5, 0b10110) == 0b01001);
408 try expect(not(u16, 0b10101010_10101010) == 0b01010101_01010101);
409 try expect(not(u16, 0b01010101_01010101) == 0b10101010_10101010);
410 try expect(not(u32, 0xAAAA_3333) == 0x5555_CCCC);
411 try expect(not(u32, 0x5555_CCCC) == 0xAAAA_3333);
412 try expect(not(u35, 0x4_1111_FFFF) == 0x3_EEEE_0000);
413 try expect(not(u35, 0x3_EEEE_0000) == 0x4_1111_FFFF);
414 try expect(not(u48, 0x4567_89AB_CDEF) == 0xBA98_7654_3210);
415 try expect(not(u48, 0xBA98_7654_3210) == 0x4567_89AB_CDEF);
416 try expect(not(u64, 0x0123_4567_89AB_CDEF) == 0xFEDC_BA98_7654_3210);
417 try expect(not(u64, 0xFEDC_BA98_7654_3210) == 0x0123_4567_89AB_CDEF);
418
419 try expect(not(i0, 0) == 0);
420 try expect(not(i1, 0) == -1);
421 try expect(not(i1, -1) == 0);
422 try expect(not(i5, -2) == 1);
423 try expect(not(i5, 3) == -4);
424 try expect(not(i32, 0) == -1);
425 try expect(not(i32, -2147483648) == 2147483647);
426 try expect(not(i64, -1) == 0);
427 try expect(not(i64, 0) == -1);
428
399 try expect(comptime x: {429 try expect(comptime x: {
400 break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;430 break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;
401 });431 });
...@@ -405,34 +435,40 @@ test "binary not" {...@@ -405,34 +435,40 @@ test "binary not" {
405 try expect(comptime x: {435 try expect(comptime x: {
406 break :x ~@as(u0, 0) == 0;436 break :x ~@as(u0, 0) == 0;
407 });437 });
408 try testBinaryNot(0b1010101010101010);
409}438}
410439
411fn testBinaryNot(x: u16) !void {440test "binary not big int <= 128 bits" {
412 try expect(~x == 0b0101010101010101);
413}
414
415test "binary not 128-bit" {
416 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
417 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO441 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
418 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO442 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
419 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO443 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
420 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;444 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
421 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;445 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
422446
447 try expect(not(u65, 1) == 0x1_FFFFFFFF_FFFFFFFE);
448 try expect(not(u65, 0x1_FFFFFFFF_FFFFFFFE) == 1);
449
450 try expect(not(u96, 0x01234567_89ABCDEF_00000001) == 0xFEDCBA98_76543210_FFFFFFFE);
451 try expect(not(u96, 0xFEDCBA98_76543210_FFFFFFFE) == 0x01234567_89ABCDEF_00000001);
452
453 try expect(not(u128, 0xAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA) == 0x55555555_55555555_55555555_55555555);
454 try expect(not(u128, 0x55555555_55555555_55555555_55555555) == 0xAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA);
455
456 try expect(not(i65, -1) == 0);
457 try expect(not(i65, 0) == -1);
458 try expect(not(i65, -18446744073709551616) == 18446744073709551615);
459 try expect(not(i65, 18446744073709551615) == -18446744073709551616);
460
461 try expect(not(i128, -1) == 0);
462 try expect(not(i128, 0) == -1);
463 try expect(not(i128, -200) == 199);
464 try expect(not(i128, 199) == -200);
465
423 try expect(comptime x: {466 try expect(comptime x: {
424 break :x ~@as(u128, 0x55555555_55555555_55555555_55555555) == 0xaaaaaaaa_aaaaaaaa_aaaaaaaa_aaaaaaaa;467 break :x ~@as(u128, 0x55555555_55555555_55555555_55555555) == 0xaaaaaaaa_aaaaaaaa_aaaaaaaa_aaaaaaaa;
425 });468 });
426 try expect(comptime x: {469 try expect(comptime x: {
427 break :x ~@as(i128, 0x55555555_55555555_55555555_55555555) == @as(i128, @bitCast(@as(u128, 0xaaaaaaaa_aaaaaaaa_aaaaaaaa_aaaaaaaa)));470 break :x ~@as(i128, 0x55555555_55555555_55555555_55555555) == @as(i128, @bitCast(@as(u128, 0xaaaaaaaa_aaaaaaaa_aaaaaaaa_aaaaaaaa)));
428 });471 });
429
430 try testBinaryNot128(u128, 0xaaaaaaaa_aaaaaaaa_aaaaaaaa_aaaaaaaa);
431 try testBinaryNot128(i128, @as(i128, @bitCast(@as(u128, 0xaaaaaaaa_aaaaaaaa_aaaaaaaa_aaaaaaaa))));
432}
433
434fn testBinaryNot128(comptime Type: type, x: Type) !void {
435 try expect(~x == @as(Type, 0x55555555_55555555_55555555_55555555));
436}472}
437473
438test "division" {474test "division" {