| ... | @@ -11,10 +11,8 @@ const log = std.log.scoped(.codegen); | ... | @@ -11,10 +11,8 @@ const log = std.log.scoped(.codegen); |
| 11 | | 11 | |
| 12 | const codegen = @import("../../codegen.zig"); | 12 | const codegen = @import("../../codegen.zig"); |
| 13 | const Zcu = @import("../../Zcu.zig"); | 13 | const Zcu = @import("../../Zcu.zig"); |
| 14 | /// Deprecated. | | |
| 15 | const Module = Zcu; | | |
| 16 | const InternPool = @import("../../InternPool.zig"); | 14 | const InternPool = @import("../../InternPool.zig"); |
| 17 | const Decl = Module.Decl; | 15 | const Decl = Zcu.Decl; |
| 18 | const Type = @import("../../type.zig").Type; | 16 | const Type = @import("../../type.zig").Type; |
| 19 | const Value = @import("../../Value.zig"); | 17 | const Value = @import("../../Value.zig"); |
| 20 | const Compilation = @import("../../Compilation.zig"); | 18 | const 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`. |
| 675 | arg_index: u32 = 0, | 673 | arg_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` |
| 677 | err_msg: *Module.ErrorMsg, | 675 | err_msg: *Zcu.ErrorMsg, |
| 678 | /// List of all locals' types generated throughout this declaration | 676 | /// 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. |
| 680 | locals: std.ArrayListUnmanaged(u8), | 678 | locals: std.ArrayListUnmanaged(u8), |
| ... | @@ -768,7 +766,7 @@ pub fn deinit(func: *CodeGen) void { | ... | @@ -768,7 +766,7 @@ pub fn deinit(func: *CodeGen) void { |
| 768 | fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError { | 766 | fn 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 | } |
| 774 | | 772 | |
| ... | @@ -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 | } |
| 993 | | 991 | |
| 994 | /// Using a given `Type`, returns the corresponding type | 992 | /// Using a given `Type`, returns the corresponding type |
| 995 | fn typeToValtype(ty: Type, mod: *Module) wasm.Valtype { | 993 | fn 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 | } |
| 1033 | | 1031 | |
| 1034 | /// Using a given `Type`, returns the byte representation of its wasm value type | 1032 | /// Using a given `Type`, returns the byte representation of its wasm value type |
| 1035 | fn genValtype(ty: Type, mod: *Module) u8 { | 1033 | fn genValtype(ty: Type, mod: *Zcu) u8 { |
| 1036 | return wasm.valtype(typeToValtype(ty, mod)); | 1034 | return wasm.valtype(typeToValtype(ty, mod)); |
| 1037 | } | 1035 | } |
| 1038 | | 1036 | |
| 1039 | /// Using a given `Type`, returns the corresponding wasm value type | 1037 | /// Using a given `Type`, returns the corresponding wasm value type |
| 1040 | /// Differently from `genValtype` this also allows `void` to create a block | 1038 | /// Differently from `genValtype` this also allows `void` to create a block |
| 1041 | /// with no return type | 1039 | /// with no return type |
| 1042 | fn genBlockType(ty: Type, mod: *Module) u8 { | 1040 | fn 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( |
| 1204 | | 1202 | |
| 1205 | pub fn generate( | 1203 | pub 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 | } |
| 1407 | | 1405 | |
| 1408 | fn firstParamSRet(cc: std.builtin.CallingConvention, return_type: Type, mod: *Module) bool { | 1406 | fn 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 { |
| 1713 | | 1711 | |
| 1714 | /// For a given `Type`, will return true when the type will be passed | 1712 | /// For a given `Type`, will return true when the type will be passed |
| 1715 | /// by reference, rather than by value | 1713 | /// by reference, rather than by value |
| 1716 | fn isByRef(ty: Type, mod: *Module) bool { | 1714 | fn 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-simd | 1783 | /// 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 store | 1784 | /// 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. |
| 1788 | fn determineSimdStoreStrategy(ty: Type, mod: *Module) SimdStoreStrategy { | 1786 | fn 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 | else | 2324 | 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)) }; |
| 2328 | | 2330 | |
| 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 stack | 2340 | // 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 | }); |
| 2520 | | 2523 | |
| 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); |
| 2809 | | 2809 | |
| ... | @@ -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); |
| 2829 | | 2824 | |
| ... | @@ -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); |
| 2845 | | 2838 | |
| 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); |
| 2853 | | 2842 | |
| ... | @@ -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 { |
| 2860 | | 2849 | |
| 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); | | |
| 2864 | | 2852 | |
| 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 { |
| 3078 | | 3080 | |
| 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); |
| 3084 | | 3093 | |
| 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 | } |
| 3203 | | 3212 | |
| 3204 | /// Converts a signed integer to its 2's complement form and returns | | |
| 3205 | /// an unsigned integer instead. | | |
| 3206 | /// Asserts bitsize <= 64 | | |
| 3207 | fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo(@TypeOf(value)).Int.bits) { | | |
| 3208 | const T = @TypeOf(value); | | |
| 3209 | comptime assert(@typeInfo(T) == .Int); | | |
| 3210 | comptime assert(@typeInfo(T).Int.signedness == .signed); | | |
| 3211 | assert(bits <= 64); | | |
| 3212 | const WantedT = std.meta.Int(.unsigned, @typeInfo(T).Int.bits); | | |
| 3213 | if (value >= 0) return @as(WantedT, @bitCast(value)); | | |
| 3214 | const max_value = @as(u64, @intCast((@as(u65, 1) << bits) - 1)); | | |
| 3215 | const flipped = @as(T, @intCast((~-@as(i65, value)) + 1)); | | |
| 3216 | const result = @as(WantedT, @bitCast(flipped)) & max_value; | | |
| 3217 | return @as(WantedT, @intCast(result)); | | |
| 3218 | } | | |
| 3219 | | | |
| 3220 | /// Asserts that `isByRef` returns `false` for `ty`. | 3213 | /// Asserts that `isByRef` returns `false` for `ty`. |
| 3221 | fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { | 3214 | fn 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 | } |
| 3460 | | 3447 | |
| 3461 | fn intIndexAsI32(ip: *const InternPool, int: InternPool.Index, mod: *Module) i32 { | 3448 | fn 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 | } |
| 3464 | | 3451 | |
| 3465 | fn intStorageAsI32(storage: InternPool.Key.Int.Storage, mod: *Module) i32 { | 3452 | fn 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 signedness | 3605 | // 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; | | |
| 3639 | | 3608 | |
| 3640 | // ensure that when we compare pointers, we emit | 3609 | // 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); |
| 3644 | | 3613 | |
| 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 | }; |
| 3767 | | 3736 | |
| 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)); |
| 3783 | | 3762 | 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 | } |
| 3813 | | 3799 | |
| 3814 | fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 3800 | fn 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 { |
| 4355 | | 4360 | |
| 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 | else | 4365 | 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 | } |
| 4392 | | 4387 | |
| 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 | else | 4393 | 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 local | 4397 | // 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 it | 4401 | // 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; |
| 4421 | | 4407 | |
| 4422 | // store msb first | 4408 | // 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()); |
| 4424 | | 4410 | |
| 4425 | // For signed integers we shift msb by 63 (64bit integer - 1 sign bit) and store remaining value | 4411 | // 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'd | 4417 | // 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 | }; |
| 5824 | | 5810 | |
| 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 | } |
| 5846 | | 5841 | |
| 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_ret | 5873 | intrin_ret |
| 5879 | else | 5874 | 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_ret | 5887 | intrin_ret |
| 5893 | else | 5888 | 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; |
| 5998 | | 5997 | |
| 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.?; |
| 6003 | | 6002 | |
| ... | @@ -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 | }; |
| 6013 | | 6012 | |
| 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 | } |
| 6018 | | 6017 | |
| ... | @@ -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 | }; |
| 6024 | | 6023 | |
| 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 | else | 6040 | 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 | } |
| 6151 | | 6131 | |
| 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) |
| 6172 | | 6151 | |
| 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 | }; |
| 6700 | | 6674 | |
| 6701 | // bytes are no-op | 6675 | // 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 | } |
| 6705 | | 6679 | |
| 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}, |
| 6743 | | 6688 | ); |
| 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 |
| 6746 | | 6691 | else |
| 6747 | tmp_or.free(func); | 6692 | try func.binOp(intrin_ret, .{ .imm32 = 32 - int_info.bits }, ty, .shr); |
| 6748 | | 6693 | |
| 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", |
| 6754 | | 6699 | &.{.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}, |
| 6757 | | 6702 | ); |
| 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) |
| 6759 | | 6704 | 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); | | |
| 6772 | | 6707 | |
| 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 | } |
| 6780 | | 6715 | |
| 6781 | fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6716 | fn 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; |
| 6784 | | 6718 | |
| 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); |
| 6788 | | 6722 | |
| 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 | } |
| 6795 | | 6726 | |
| 6796 | fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6727 | fn 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; |
| 6799 | | 6729 | |
| 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); |
| 6803 | | 6733 | |
| 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); | | |
| 6808 | | 6735 | |
| 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 | } |
| 6836 | | 6763 | |
| 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 { |
| 6852 | | 6769 | |
| 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); |
| 6857 | | 6774 | |
| 6858 | // select takes a 32 bit value as the condition, so in the 64 bit case we use eqz to narrow | 6775 | // 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 | } |
| 6865 | | 6782 | |
| 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); |
| 6869 | | 6786 | |
| 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 | } |
| 6881 | | 6798 | |
| 6882 | _ = try func.binOp(lhs_wasm, rhs_wasm, ty, .rem); | 6799 | _ = try func.binOp(lhs, rhs, ty, .rem); |
| 6883 | | 6800 | |
| 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 | } |
| 6931 | | 6848 | |
| 6932 | fn divSigned(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue { | | |
| 6933 | const mod = func.bin_file.base.comp.module.?; | | |
| 6934 | const int_bits = ty.intInfo(mod).bits; | | |
| 6935 | const wasm_bits = toWasmBits(int_bits) orelse { | | |
| 6936 | return func.fail("TODO: Implement signed division for integers with bitsize '{d}'", .{int_bits}); | | |
| 6937 | }; | | |
| 6938 | | | |
| 6939 | if (wasm_bits == 128) { | | |
| 6940 | return func.fail("TODO: Implement signed division for 128-bit integerrs", .{}); | | |
| 6941 | } | | |
| 6942 | | | |
| 6943 | if (wasm_bits != int_bits) { | | |
| 6944 | // Leave both values on the stack | | |
| 6945 | _ = try func.signExtendInt(lhs, ty); | | |
| 6946 | _ = try func.signExtendInt(rhs, ty); | | |
| 6947 | } else { | | |
| 6948 | try func.emitWValue(lhs); | | |
| 6949 | try func.emitWValue(rhs); | | |
| 6950 | } | | |
| 6951 | switch (wasm_bits) { | | |
| 6952 | 32 => try func.addTag(.i32_div_s), | | |
| 6953 | 64 => try func.addTag(.i64_div_s), | | |
| 6954 | else => unreachable, | | |
| 6955 | } | | |
| 6956 | _ = try func.wrapOperand(.stack, ty); | | |
| 6957 | | | |
| 6958 | const result = try func.allocLocal(ty); | | |
| 6959 | try func.addLabel(.local_set, result.local.value); | | |
| 6960 | return result; | | |
| 6961 | } | | |
| 6962 | | | |
| 6963 | fn airRem(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | 6849 | fn 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; |
| 6965 | | 6851 | |
| 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); |
| 6970 | | 6855 | |
| 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); | | |
| 6994 | | 6857 | |
| 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 | } |
| 7024 | | 6887 | |
| 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 | else | 6890 | _ = 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 | } |
| 7046 | | 6899 | |
| 7047 | /// Sign extends an N bit signed integer and pushes the result to the stack. | | |
| 7048 | /// The result will be sign extended to 32 bits if N <= 32 or 64 bits if N <= 64. | | |
| 7049 | /// Support for integers wider than 64 bits has not yet been implemented. | | |
| 7050 | fn signExtendInt(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { | | |
| 7051 | const mod = func.bin_file.base.comp.module.?; | | |
| 7052 | const int_bits = ty.intInfo(mod).bits; | | |
| 7053 | const wasm_bits = toWasmBits(int_bits) orelse { | | |
| 7054 | return func.fail("TODO: signExtendInt for signed integers larger than '{d}' bits", .{int_bits}); | | |
| 7055 | }; | | |
| 7056 | | | |
| 7057 | const shift_val = switch (wasm_bits) { | | |
| 7058 | 32 => WValue{ .imm32 = wasm_bits - int_bits }, | | |
| 7059 | 64 => WValue{ .imm64 = wasm_bits - int_bits }, | | |
| 7060 | else => return func.fail("TODO: signExtendInt for i128", .{}), | | |
| 7061 | }; | | |
| 7062 | | | |
| 7063 | try func.emitWValue(operand); | | |
| 7064 | switch (wasm_bits) { | | |
| 7065 | 32 => { | | |
| 7066 | try func.emitWValue(shift_val); | | |
| 7067 | try func.addTag(.i32_shl); | | |
| 7068 | try func.emitWValue(shift_val); | | |
| 7069 | try func.addTag(.i32_shr_s); | | |
| 7070 | }, | | |
| 7071 | 64 => { | | |
| 7072 | try func.emitWValue(shift_val); | | |
| 7073 | try func.addTag(.i64_shl); | | |
| 7074 | try func.emitWValue(shift_val); | | |
| 7075 | try func.addTag(.i64_shr_s); | | |
| 7076 | }, | | |
| 7077 | else => unreachable, | | |
| 7078 | } | | |
| 7079 | | | |
| 7080 | return WValue{ .stack = {} }; | | |
| 7081 | } | | |
| 7082 | | | |
| 7083 | fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { | 6900 | fn 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 | } |
| 7133 | | 6950 | |
| 7134 | fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue { | 6951 | fn 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; |
| 7140 | | 6957 | |
| 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 branch | 6973 | 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; |