From 3bd1b9e15f04128fa3a2c0c3c3969852b7cde9f2 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 23 Sep 2023 06:21:03 -0400 Subject: [PATCH 1/7] x86_64: implement and test unary float builtins --- src/Compilation.zig | 3 +- src/InternPool.zig | 25 +- src/Sema.zig | 11 +- src/arch/x86_64/CodeGen.zig | 320 ++++++++++++---------- src/codegen/llvm.zig | 9 - test/behavior/floatop.zig | 531 ++++++++++++++++++++++++++---------- test/behavior/math.zig | 27 +- 7 files changed, 596 insertions(+), 330 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 71dff4a442..8d157d245f 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1871,8 +1871,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { comp.job_queued_compiler_rt_lib = true; } else if (options.output_mode != .Obj) { log.debug("queuing a job to build compiler_rt_obj", .{}); - // If build-obj with -fcompiler-rt is requested, that is handled specially - // elsewhere. In this case we are making a static library, so we ask + // In this case we are making a static library, so we ask // for a compiler-rt object to put in it. comp.job_queued_compiler_rt_obj = true; } diff --git a/src/InternPool.zig b/src/InternPool.zig index d2073d3c67..d7c0e4861d 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -5407,19 +5407,19 @@ pub fn getAnonStructType(ip: *InternPool, gpa: Allocator, ini: AnonStructTypeIni /// This is equivalent to `Key.FuncType` but adjusted to have a slice for `param_types`. pub const GetFuncTypeKey = struct { - param_types: []Index, + param_types: []const Index, return_type: Index, - comptime_bits: u32, - noalias_bits: u32, + comptime_bits: u32 = 0, + noalias_bits: u32 = 0, /// `null` means generic. - alignment: ?Alignment, + alignment: ?Alignment = .none, /// `null` means generic. - cc: ?std.builtin.CallingConvention, - is_var_args: bool, - is_generic: bool, - is_noinline: bool, - section_is_generic: bool, - addrspace_is_generic: bool, + cc: ?std.builtin.CallingConvention = .Unspecified, + is_var_args: bool = false, + is_generic: bool = false, + is_noinline: bool = false, + section_is_generic: bool = false, + addrspace_is_generic: bool = false, }; pub fn getFuncType(ip: *InternPool, gpa: Allocator, key: GetFuncTypeKey) Allocator.Error!Index { @@ -5754,15 +5754,10 @@ pub fn getFuncInstance(ip: *InternPool, gpa: Allocator, arg: GetFuncInstanceKey) const func_ty = try ip.getFuncType(gpa, .{ .param_types = arg.param_types, .return_type = arg.bare_return_type, - .comptime_bits = 0, .noalias_bits = arg.noalias_bits, .alignment = arg.alignment, .cc = arg.cc, - .is_var_args = false, - .is_generic = false, .is_noinline = arg.is_noinline, - .section_is_generic = false, - .addrspace_is_generic = false, }); const generic_owner = unwrapCoercedFunc(ip, arg.generic_owner); diff --git a/src/Sema.zig b/src/Sema.zig index 88dd5f5b09..096ebb0589 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7373,10 +7373,10 @@ fn analyzeCall( const memoized_arg_values = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); const owner_info = mod.typeToFunc(fn_owner_decl.ty).?; + const new_param_types = try sema.arena.alloc(InternPool.Index, owner_info.param_types.len); var new_fn_info: InternPool.GetFuncTypeKey = .{ - .param_types = try sema.arena.alloc(InternPool.Index, owner_info.param_types.len), + .param_types = new_param_types, .return_type = owner_info.return_type, - .comptime_bits = 0, .noalias_bits = owner_info.noalias_bits, .alignment = if (owner_info.align_is_generic) null else owner_info.alignment, .cc = if (owner_info.cc_is_generic) null else owner_info.cc, @@ -7403,7 +7403,7 @@ fn analyzeCall( block, &child_block, inst, - new_fn_info.param_types, + new_param_types, &arg_i, args_info, is_comptime_call, @@ -21144,16 +21144,11 @@ fn zirReify( const ty = try mod.funcType(.{ .param_types = param_types, - .comptime_bits = 0, .noalias_bits = noalias_bits, .return_type = return_type.toIntern(), .alignment = alignment, .cc = cc, .is_var_args = is_var_args, - .is_generic = false, - .is_noinline = false, - .section_is_generic = false, - .addrspace_is_generic = false, }); return Air.internedToRef(ty.toIntern()); }, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2886ba3bda..3bdcaf7c42 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1807,7 +1807,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .log2, .log10, .round, - => try self.airUnaryMath(inst), + => |tag| try self.airUnaryMath(inst, tag), .floor => try self.airRound(inst, 0b1_0_01), .ceil => try self.airRound(inst, 0b1_0_10), @@ -5280,13 +5280,35 @@ fn airSqrt(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ un_op, .none, .none }); } -fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { +fn airUnaryMath(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; - _ = un_op; - return self.fail("TODO implement airUnaryMath for {}", .{ - self.air.instructions.items(.tag)[inst], - }); - //return self.finishAir(inst, result, .{ un_op, .none, .none }); + const ty = self.typeOf(un_op).toIntern(); + const result = try self.genCall(.{ .lib = .{ + .return_type = ty, + .param_types = &.{ty}, + .callee = switch (tag) { + inline .sin, + .cos, + .tan, + .exp, + .exp2, + .log, + .log2, + .log10, + .round, + => |comptime_tag| switch (ty) { + .f16_type => "__" ++ @tagName(comptime_tag) ++ "h", + .f32_type => @tagName(comptime_tag) ++ "f", + .f64_type => @tagName(comptime_tag), + .f80_type => "__" ++ @tagName(comptime_tag) ++ "x", + .f128_type => @tagName(comptime_tag) ++ "q", + .c_longdouble_type => @tagName(comptime_tag) ++ "l", + else => unreachable, + }, + else => unreachable, + }, + } }, &.{un_op}); + return self.finishAir(inst, result, .{ un_op, .none, .none }); } fn reuseOperand( @@ -7290,7 +7312,7 @@ fn genBinOp( switch (air_tag) { .add, .add_wrap, .sub, .sub_wrap, .mul, .mul_wrap, .div_float, .div_exact => {}, - .div_trunc, .div_floor => if (self.hasFeature(.sse4_1)) try self.genRound( + .div_trunc, .div_floor => try self.genRound( lhs_ty, dst_reg, .{ .register = dst_reg }, @@ -7299,9 +7321,7 @@ fn genBinOp( .div_floor => 0b1_0_01, else => unreachable, }, - ) else return self.fail("TODO implement genBinOp for {s} {} without sse4_1 feature", .{ - @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), - }), + ), .bit_and, .bit_or, .xor => {}, .max, .min => if (maybe_mask_reg) |mask_reg| if (self.hasFeature(.avx)) { const rhs_copy_reg = registerAlias(src_mcv.getReg().?, abi_size); @@ -8124,31 +8144,59 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void { } fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { - const mod = self.bin_file.options.module.?; if (modifier == .always_tail) return self.fail("TODO implement tail calls for x86_64", .{}); + const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const callee = pl_op.operand; const extra = self.air.extraData(Air.Call, pl_op.payload); const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); - const ty = self.typeOf(callee); - const fn_ty = switch (ty.zigTypeTag(mod)) { - .Fn => ty, - .Pointer => ty.childType(mod), - else => unreachable, + const ret = try self.genCall(.{ .air = pl_op.operand }, args); + + var bt = self.liveness.iterateBigTomb(inst); + self.feed(&bt, pl_op.operand); + for (args) |arg| self.feed(&bt, arg); + + const result = if (self.liveness.isUnused(inst)) .unreach else ret; + return self.finishAirResult(inst, result); +} + +fn genCall(self: *Self, info: union(enum) { + air: Air.Inst.Ref, + lib: struct { + return_type: InternPool.Index, + param_types: []const InternPool.Index, + lib: ?[]const u8 = null, + callee: []const u8, + }, +}, args: []const Air.Inst.Ref) !MCValue { + const mod = self.bin_file.options.module.?; + + const fn_ty = switch (info) { + .air => |callee| fn_info: { + const callee_ty = self.typeOf(callee); + break :fn_info switch (callee_ty.zigTypeTag(mod)) { + .Fn => callee_ty, + .Pointer => callee_ty.childType(mod), + else => unreachable, + }; + }, + .lib => |lib| try mod.funcType(.{ + .param_types = lib.param_types, + .return_type = lib.return_type, + .cc = .C, + }), }; - const fn_info = mod.typeToFunc(fn_ty).?; - var info = try self.resolveCallingConventionValues(fn_info, args[fn_info.param_types.len..], .call_frame); - defer info.deinit(self); + var call_info = + try self.resolveCallingConventionValues(fn_info, args[fn_info.param_types.len..], .call_frame); + defer call_info.deinit(self); // We need a properly aligned and sized call frame to be able to call this function. { - const needed_call_frame = - FrameAlloc.init(.{ - .size = info.stack_byte_count, - .alignment = info.stack_align, + const needed_call_frame = FrameAlloc.init(.{ + .size = call_info.stack_byte_count, + .alignment = call_info.stack_align, }); const frame_allocs_slice = self.frame_allocs.slice(); const stack_frame_size = @@ -8164,24 +8212,20 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier // set stack arguments first because this can clobber registers // also clobber spill arguments as we go - switch (info.return_value.long) { + switch (call_info.return_value.long) { .none, .unreach => {}, .indirect => |reg_off| try self.spillRegisters(&.{reg_off.reg}), else => unreachable, } - for (args, info.args) |arg, mc_arg| { - const arg_ty = self.typeOf(arg); - const arg_mcv = try self.resolveInst(arg); - switch (mc_arg) { - .none => {}, - .register => |reg| try self.spillRegisters(&.{reg}), - .load_frame => try self.genCopy(arg_ty, mc_arg, arg_mcv), - else => unreachable, - } - } + for (call_info.args, args) |dst_arg, src_arg| switch (dst_arg) { + .none => {}, + .register => |reg| try self.spillRegisters(&.{reg}), + .load_frame => try self.genCopy(self.typeOf(src_arg), dst_arg, try self.resolveInst(src_arg)), + else => unreachable, + }; // now we are free to set register arguments - const ret_lock = switch (info.return_value.long) { + const ret_lock = switch (call_info.return_value.long) { .none, .unreach => null, .indirect => |reg_off| lock: { const ret_ty = fn_info.return_type.toType(); @@ -8189,125 +8233,80 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier try self.genSetReg(reg_off.reg, Type.usize, .{ .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, }); - info.return_value.short = .{ .load_frame = .{ .index = frame_index } }; + call_info.return_value.short = .{ .load_frame = .{ .index = frame_index } }; break :lock self.register_manager.lockRegAssumeUnused(reg_off.reg); }, else => unreachable, }; defer if (ret_lock) |lock| self.register_manager.unlockReg(lock); - for (args, info.args) |arg, mc_arg| { - const arg_ty = self.typeOf(arg); - const arg_mcv = try self.resolveInst(arg); - switch (mc_arg) { + for (call_info.args, args) |dst_arg, src_arg| { + switch (dst_arg) { .none, .load_frame => {}, - .register => try self.genCopy(arg_ty, mc_arg, arg_mcv), + .register => try self.genCopy(self.typeOf(src_arg), dst_arg, try self.resolveInst(src_arg)), else => unreachable, } } // Due to incremental compilation, how function calls are generated depends // on linking. - if (try self.air.value(callee, mod)) |func_value| { - const func_key = mod.intern_pool.indexToKey(func_value.ip_index); - if (switch (func_key) { - .func => |func| func.owner_decl, - .ptr => |ptr| switch (ptr.addr) { - .decl => |decl| decl, + switch (info) { + .air => |callee| if (try self.air.value(callee, mod)) |func_value| { + const func_key = mod.intern_pool.indexToKey(func_value.ip_index); + if (switch (func_key) { + .func => |func| func.owner_decl, + .ptr => |ptr| switch (ptr.addr) { + .decl => |decl| decl, + else => null, + }, else => null, - }, - else => null, - }) |owner_decl| { - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); - const sym = elf_file.symbol(sym_index); - sym.flags.needs_got = true; - _ = try sym.getOrCreateGotEntry(sym_index, elf_file); - _ = try self.addInst(.{ - .tag = .call, - .ops = .direct_got_reloc, - .data = .{ .reloc = .{ - .atom_index = try self.owner.getSymbolIndex(self), - .sym_index = sym.esym_index, - } }, - }); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom = try coff_file.getOrCreateAtomForDecl(owner_decl); - const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; - try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); - try self.asmRegister(.{ ._, .call }, .rax); - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom = try macho_file.getOrCreateAtomForDecl(owner_decl); - const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; - try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); - try self.asmRegister(.{ ._, .call }, .rax); - } else if (self.bin_file.cast(link.File.Plan9)) |p9| { - const atom_index = try p9.seeDecl(owner_decl); - const atom = p9.getAtom(atom_index); - try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ - .base = .{ .reg = .ds }, - .disp = @intCast(atom.getOffsetTableAddress(p9)), - })); - } else unreachable; - } else if (func_value.getExternFunc(mod)) |extern_func| { - const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name); - const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name); - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const atom_index = try self.owner.getSymbolIndex(self); - const sym_index = try elf_file.getGlobalSymbol(decl_name, lib_name); - _ = try self.addInst(.{ - .tag = .call, - .ops = .extern_fn_reloc, - .data = .{ .reloc = .{ - .atom_index = atom_index, - .sym_index = sym_index, - } }, - }); - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = try self.owner.getSymbolIndex(self); - const sym_index = try coff_file.getGlobalSymbol(decl_name, lib_name); - _ = try self.addInst(.{ - .tag = .mov, - .ops = .import_reloc, - .data = .{ .rx = .{ - .r1 = .rax, - .payload = try self.addExtra(Mir.Reloc{ - .atom_index = atom_index, - .sym_index = sym_index, - }), - } }, - }); - try self.asmRegister(.{ ._, .call }, .rax); - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const atom_index = try self.owner.getSymbolIndex(self); - const sym_index = try macho_file.getGlobalSymbol(decl_name, lib_name); - _ = try self.addInst(.{ - .tag = .call, - .ops = .extern_fn_reloc, - .data = .{ .reloc = .{ - .atom_index = atom_index, - .sym_index = sym_index, - } }, - }); + }) |owner_decl| { + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl); + const sym = elf_file.symbol(sym_index); + sym.flags.needs_got = true; + _ = try sym.getOrCreateGotEntry(sym_index, elf_file); + _ = try self.addInst(.{ + .tag = .call, + .ops = .direct_got_reloc, + .data = .{ .reloc = .{ + .atom_index = try self.owner.getSymbolIndex(self), + .sym_index = sym.esym_index, + } }, + }); + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const atom = try coff_file.getOrCreateAtomForDecl(owner_decl); + const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; + try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); + try self.asmRegister(.{ ._, .call }, .rax); + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const atom = try macho_file.getOrCreateAtomForDecl(owner_decl); + const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; + try self.genSetReg(.rax, Type.usize, .{ .lea_got = sym_index }); + try self.asmRegister(.{ ._, .call }, .rax); + } else if (self.bin_file.cast(link.File.Plan9)) |p9| { + const atom_index = try p9.seeDecl(owner_decl); + const atom = p9.getAtom(atom_index); + try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{ + .base = .{ .reg = .ds }, + .disp = @intCast(atom.getOffsetTableAddress(p9)), + })); + } else unreachable; + } else if (func_value.getExternFunc(mod)) |extern_func| { + const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name); + const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name); + try self.genExternSymbolRef(.call, lib_name, decl_name); } else { - return self.fail("TODO implement calling extern functions", .{}); + return self.fail("TODO implement calling bitcasted functions", .{}); } } else { - return self.fail("TODO implement calling bitcasted functions", .{}); - } - } else { - assert(ty.zigTypeTag(mod) == .Pointer); - const mcv = try self.resolveInst(callee); - try self.genSetReg(.rax, Type.usize, mcv); - try self.asmRegister(.{ ._, .call }, .rax); + assert(self.typeOf(callee).zigTypeTag(mod) == .Pointer); + try self.genSetReg(.rax, Type.usize, try self.resolveInst(callee)); + try self.asmRegister(.{ ._, .call }, .rax); + }, + .lib => |lib| try self.genExternSymbolRef(.call, lib.lib, lib.callee), } - - var bt = self.liveness.iterateBigTomb(inst); - self.feed(&bt, callee); - for (args) |arg| self.feed(&bt, arg); - - const result = if (self.liveness.isUnused(inst)) .unreach else info.return_value.short; - return self.finishAirResult(inst, result); + return call_info.return_value.short; } fn airRet(self: *Self, inst: Air.Inst.Index) !void { @@ -10281,6 +10280,51 @@ fn genInlineMemset(self: *Self, dst_ptr: MCValue, value: MCValue, len: MCValue) try self.asmOpOnly(.{ .@"rep _sb", .sto }); } +fn genExternSymbolRef( + self: *Self, + comptime tag: Mir.Inst.Tag, + lib: ?[]const u8, + callee: []const u8, +) InnerError!void { + const atom_index = try self.owner.getSymbolIndex(self); + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + _ = try self.addInst(.{ + .tag = tag, + .ops = .extern_fn_reloc, + .data = .{ .reloc = .{ + .atom_index = atom_index, + .sym_index = try elf_file.getGlobalSymbol(callee, lib), + } }, + }); + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + _ = try self.addInst(.{ + .tag = .mov, + .ops = .import_reloc, + .data = .{ .rx = .{ + .r1 = .rax, + .payload = try self.addExtra(Mir.Reloc{ + .atom_index = atom_index, + .sym_index = try coff_file.getGlobalSymbol(callee, lib), + }), + } }, + }); + switch (tag) { + .mov => {}, + .call => try self.asmRegister(.{ ._, .call }, .rax), + else => unreachable, + } + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + _ = try self.addInst(.{ + .tag = .call, + .ops = .extern_fn_reloc, + .data = .{ .reloc = .{ + .atom_index = atom_index, + .sym_index = try macho_file.getGlobalSymbol(callee, lib), + } }, + }); + } else return self.fail("TODO implement calling extern functions", .{}); +} + fn genLazySymbolRef( self: *Self, comptime tag: Mir.Inst.Tag, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 4e6f7733fe..8cd9f6604f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6394,15 +6394,6 @@ pub const FuncGen = struct { const fn_ty = try mod.funcType(.{ .param_types = &.{}, .return_type = .void_type, - .alignment = .none, - .noalias_bits = 0, - .comptime_bits = 0, - .cc = .Unspecified, - .is_var_args = false, - .is_generic = false, - .is_noinline = false, - .section_is_generic = false, - .addrspace_is_generic = false, }); const fn_di_ty = try o.lowerDebugType(fn_ty, .full); const subprogram = dib.createFunction( diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 7ac965d5b4..a65e40997e 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -2,8 +2,6 @@ const std = @import("std"); const builtin = @import("builtin"); const expect = std.testing.expect; const math = std.math; -const pi = std.math.pi; -const e = std.math.e; const has_f80_rt = switch (builtin.cpu.arch) { .x86_64, .x86 => true, else => false, @@ -11,7 +9,7 @@ const has_f80_rt = switch (builtin.cpu.arch) { const no_x86_64_hardware_f16_support = builtin.zig_backend == .stage2_x86_64 and !std.Target.x86.featureSetHas(builtin.cpu.features, .f16c); -const epsilon_16 = 0.001; +const epsilon_16 = 0.002; const epsilon = 0.000001; fn epsForType(comptime T: type) T { @@ -29,10 +27,10 @@ test "floating point comparisons" { } fn testFloatComparisons() !void { - inline for ([_]type{ f16, f32, f64, f128 }) |ty| { + inline for ([_]type{ f16, f32, f64, f128 }) |T| { // No decimal part { - const x: ty = 1.0; + const x: T = 1.0; try expect(x == 1); try expect(x != 0); try expect(x > 0); @@ -42,7 +40,7 @@ fn testFloatComparisons() !void { } // Non-zero decimal part { - const x: ty = 1.5; + const x: T = 1.5; try expect(x != 1); try expect(x != 2); try expect(x > 1); @@ -54,11 +52,11 @@ fn testFloatComparisons() !void { } test "different sized float comparisons" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testDifferentSizedFloatComparisons(); try comptime testDifferentSizedFloatComparisons(); @@ -73,9 +71,9 @@ fn testDifferentSizedFloatComparisons() !void { test "f80 comparisons" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try expect(compareF80(0.0, .eq, -0.0)); try expect(compareF80(0.0, .lte, -0.0)); @@ -125,8 +123,8 @@ test "@sqrt" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - try comptime testSqrt(); try testSqrt(); + try comptime testSqrt(); } fn testSqrt() !void { @@ -163,8 +161,8 @@ test "@sqrt with vectors" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - try comptime testSqrtWithVectors(); try testSqrtWithVectors(); + try comptime testSqrtWithVectors(); } fn testSqrtWithVectors() !void { @@ -177,11 +175,11 @@ fn testSqrtWithVectors() !void { } test "more @sqrt f16 tests" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO these are not all passing at comptime try expect(@sqrt(@as(f16, 0.0)) == 0.0); @@ -205,8 +203,8 @@ test "more @sqrt f16 tests" { test "another, possibly redundant @sqrt test" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testSqrtLegacy(f64, 12.0); try comptime testSqrtLegacy(f64, 12.0); @@ -228,36 +226,61 @@ fn testSqrtLegacy(comptime T: type, x: T) !void { try expect(@sqrt(x * x) == x); } -test "@sin" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO +test "@sin f16" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try comptime testSin(); - try testSin(); + try testSin(&.{f16}); + try comptime testSin(&.{f16}); } -fn testSin() !void { - inline for ([_]type{ f16, f32, f64 }) |ty| { - const eps = epsForType(ty); - try expect(@sin(@as(ty, 0)) == 0); - try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi)), 0, eps)); - try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 2.0)), 1, eps)); - try expect(math.approxEqAbs(ty, @sin(@as(ty, std.math.pi / 4.0)), 0.7071067811865475, eps)); +test "@sin f32/f64" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testSin(&.{ f32, f64 }); + try comptime testSin(&.{ f32, f64 }); +} + +test "@sin f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testSin(&.{ f80, f128, c_longdouble }); + try comptime testSin(&.{ f80, f128, c_longdouble }); +} + +fn testSin(comptime Ts: []const type) !void { + inline for (Ts) |T| { + const eps = epsForType(T); + var zero: T = 0; + try expect(@sin(zero) == 0); + var pi: T = std.math.pi; + try expect(math.approxEqAbs(T, @sin(pi), 0, eps)); + try expect(math.approxEqAbs(T, @sin(pi / 2.0), 1, eps)); + try expect(math.approxEqAbs(T, @sin(pi / 4.0), 0.7071067811865475, eps)); } } test "@sin with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try comptime testSinWithVectors(); try testSinWithVectors(); + try comptime testSinWithVectors(); } fn testSinWithVectors() !void { @@ -269,36 +292,61 @@ fn testSinWithVectors() !void { try expect(math.approxEqAbs(f32, @sin(@as(f32, 4.4)), result[3], epsilon)); } -test "@cos" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO +test "@cos f16" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try comptime testCos(); - try testCos(); + try testCos(&.{f16}); + try comptime testCos(&.{f16}); } -fn testCos() !void { - inline for ([_]type{ f16, f32, f64 }) |ty| { - const eps = epsForType(ty); - try expect(@cos(@as(ty, 0)) == 1); - try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi)), -1, eps)); - try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 2.0)), 0, eps)); - try expect(math.approxEqAbs(ty, @cos(@as(ty, std.math.pi / 4.0)), 0.7071067811865475, eps)); +test "@cos f32/f64" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testCos(&.{ f32, f64 }); + try comptime testCos(&.{ f32, f64 }); +} + +test "@cos f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testCos(&.{ f80, f128, c_longdouble }); + try comptime testCos(&.{ f80, f128, c_longdouble }); +} + +fn testCos(comptime Ts: []const type) !void { + inline for (Ts) |T| { + const eps = epsForType(T); + var zero: T = 0; + try expect(@cos(zero) == 1); + var pi: T = std.math.pi; + try expect(math.approxEqAbs(T, @cos(pi), -1, eps)); + try expect(math.approxEqAbs(T, @cos(pi / 2.0), 0, eps)); + try expect(math.approxEqAbs(T, @cos(pi / 4.0), 0.7071067811865475, eps)); } } test "@cos with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try comptime testCosWithVectors(); try testCosWithVectors(); + try comptime testCosWithVectors(); } fn testCosWithVectors() !void { @@ -310,35 +358,127 @@ fn testCosWithVectors() !void { try expect(math.approxEqAbs(f32, @cos(@as(f32, 4.4)), result[3], epsilon)); } -test "@exp" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO +test "@tan f16" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try comptime testExp(); - try testExp(); + try testTan(&.{f16}); + try comptime testTan(&.{f16}); } -fn testExp() !void { - inline for ([_]type{ f16, f32, f64 }) |ty| { - const eps = epsForType(ty); - try expect(@exp(@as(ty, 0)) == 1); - try expect(math.approxEqAbs(ty, @exp(@as(ty, 2)), 7.389056098930650, eps)); - try expect(math.approxEqAbs(ty, @exp(@as(ty, 5)), 148.4131591025766, eps)); +test "@tan f32/f64" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testTan(&.{ f32, f64 }); + try comptime testTan(&.{ f32, f64 }); +} + +test "@tan f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testTan(&.{ f80, f128, c_longdouble }); + try comptime testTan(&.{ f80, f128, c_longdouble }); +} + +fn testTan(comptime Ts: []const type) !void { + inline for (Ts) |T| { + const eps = epsForType(T); + var zero: T = 0; + try expect(@tan(zero) == 0); + var pi: T = std.math.pi; + try expect(math.approxEqAbs(T, @tan(pi), 0, eps)); + try expect(math.approxEqAbs(T, @tan(pi / 3.0), 1.732050807568878, eps)); + try expect(math.approxEqAbs(T, @tan(pi / 4.0), 1, eps)); + } +} + +test "@tan with vectors" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testTanWithVectors(); + try comptime testTanWithVectors(); +} + +fn testTanWithVectors() !void { + var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 }; + var result = @tan(v); + try expect(math.approxEqAbs(f32, @tan(@as(f32, 1.1)), result[0], epsilon)); + try expect(math.approxEqAbs(f32, @tan(@as(f32, 2.2)), result[1], epsilon)); + try expect(math.approxEqAbs(f32, @tan(@as(f32, 3.3)), result[2], epsilon)); + try expect(math.approxEqAbs(f32, @tan(@as(f32, 4.4)), result[3], epsilon)); +} + +test "@exp f16" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; + + try testExp(&.{f16}); + try comptime testExp(&.{f16}); +} + +test "@exp f32/f64" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testExp(&.{ f32, f64 }); + try comptime testExp(&.{ f32, f64 }); +} + +test "@exp f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testExp(&.{ f80, f128, c_longdouble }); + try comptime testExp(&.{ f80, f128, c_longdouble }); +} + +fn testExp(comptime Ts: []const type) !void { + inline for (Ts) |T| { + const eps = epsForType(T); + var zero: T = 0; + try expect(@exp(zero) == 1); + var two: T = 2; + try expect(math.approxEqAbs(T, @exp(two), 7.389056098930650, eps)); + var five: T = 5; + try expect(math.approxEqAbs(T, @exp(five), 148.4131591025766, eps)); } } test "@exp with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try comptime testExpWithVectors(); try testExpWithVectors(); + try comptime testExpWithVectors(); } fn testExpWithVectors() !void { @@ -350,35 +490,61 @@ fn testExpWithVectors() !void { try expect(math.approxEqAbs(f32, @exp(@as(f32, 0.4)), result[3], epsilon)); } -test "@exp2" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO +test "@exp2 f16" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try comptime testExp2(); - try testExp2(); + try testExp2(&.{f16}); + try comptime testExp2(&.{f16}); } -fn testExp2() !void { - inline for ([_]type{ f16, f32, f64 }) |ty| { - const eps = epsForType(ty); - try expect(@exp2(@as(ty, 2)) == 4); - try expect(math.approxEqAbs(ty, @exp2(@as(ty, 1.5)), 2.8284271247462, eps)); - try expect(math.approxEqAbs(ty, @exp2(@as(ty, 4.5)), 22.627416997969, eps)); +test "@exp2 f32/f64" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testExp2(&.{ f32, f64 }); + try comptime testExp2(&.{ f32, f64 }); +} + +test "@exp2 f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testExp2(&.{ f80, f128, c_longdouble }); + try comptime testExp2(&.{ f80, f128, c_longdouble }); +} + +fn testExp2(comptime Ts: []const type) !void { + inline for (Ts) |T| { + const eps = epsForType(T); + var two: T = 2; + try expect(@exp2(two) == 4); + var one_point_five: T = 1.5; + try expect(math.approxEqAbs(T, @exp2(one_point_five), 2.8284271247462, eps)); + var four_point_five: T = 4.5; + try expect(math.approxEqAbs(T, @exp2(four_point_five), 22.627416997969, eps)); } } test "@exp2 with @vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try comptime testExp2WithVectors(); try testExp2WithVectors(); + try comptime testExp2WithVectors(); } fn testExp2WithVectors() !void { @@ -390,44 +556,59 @@ fn testExp2WithVectors() !void { try expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.4)), result[3], epsilon)); } -test "@log" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO +test "@log f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try comptime testLog(); - try testLog(); + try testLog(&.{f16}); + try comptime testLog(&.{f16}); } -fn testLog() !void { - { - var a: f16 = e; - try expect(math.approxEqAbs(f16, @log(a), 1, epsilon)); - } - { - var a: f32 = e; - try expect(@log(a) == 1 or @log(a) == @as(f32, @bitCast(@as(u32, 0x3f7fffff)))); - } - { - var a: f64 = e; - try expect(@log(a) == 1 or @log(a) == @as(f64, @bitCast(@as(u64, 0x3ff0000000000000)))); - } - inline for ([_]type{ f16, f32, f64 }) |ty| { - const eps = epsForType(ty); - try expect(math.approxEqAbs(ty, @log(@as(ty, 2)), 0.6931471805599, eps)); - try expect(math.approxEqAbs(ty, @log(@as(ty, 5)), 1.6094379124341, eps)); +test "@log f32/f64" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testLog(&.{ f32, f64 }); + try comptime testLog(&.{ f32, f64 }); +} + +test "@log f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testLog(&.{ f80, f128, c_longdouble }); + try comptime testLog(&.{ f80, f128, c_longdouble }); +} + +fn testLog(comptime Ts: []const type) !void { + inline for (Ts) |T| { + const eps = epsForType(T); + var e: T = std.math.e; + try expect(math.approxEqAbs(T, @log(e), 1, eps)); + var two: T = 2; + try expect(math.approxEqAbs(T, @log(two), 0.6931471805599, eps)); + var five: T = 5; + try expect(math.approxEqAbs(T, @log(five), 1.6094379124341, eps)); } } test "@log with @vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; { var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 }; @@ -439,29 +620,54 @@ test "@log with @vectors" { } } -test "@log2" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO +test "@log2 f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try comptime testLog2(); - try testLog2(); + try testLog2(&.{f16}); + try comptime testLog2(&.{f16}); } -fn testLog2() !void { - inline for ([_]type{ f16, f32, f64 }) |ty| { - const eps = epsForType(ty); - try expect(@log2(@as(ty, 4)) == 2); - try expect(math.approxEqAbs(ty, @log2(@as(ty, 6)), 2.5849625007212, eps)); - try expect(math.approxEqAbs(ty, @log2(@as(ty, 10)), 3.3219280948874, eps)); +test "@log2 f32/f64" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testLog2(&.{ f32, f64 }); + try comptime testLog2(&.{ f32, f64 }); +} + +test "@log2 f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testLog2(&.{ f80, f128, c_longdouble }); + try comptime testLog2(&.{ f80, f128, c_longdouble }); +} + +fn testLog2(comptime Ts: []const type) !void { + inline for (Ts) |T| { + const eps = epsForType(T); + var four: T = 4; + try expect(@log2(four) == 2); + var six: T = 6; + try expect(math.approxEqAbs(T, @log2(six), 2.5849625007212, eps)); + var ten: T = 10; + try expect(math.approxEqAbs(T, @log2(ten), 3.3219280948874, eps)); } } test "@log2 with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; @@ -469,9 +675,10 @@ test "@log2 with vectors" { if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64 and builtin.os.tag == .windows) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try comptime testLog2WithVectors(); try testLog2WithVectors(); + try comptime testLog2WithVectors(); } fn testLog2WithVectors() !void { @@ -483,35 +690,61 @@ fn testLog2WithVectors() !void { try expect(@log2(@as(f32, 0.4)) == result[3]); } -test "@log10" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO +test "@log10 f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try comptime testLog10(); - try testLog10(); + try testLog10(&.{f16}); + try comptime testLog10(&.{f16}); } -fn testLog10() !void { - inline for ([_]type{ f16, f32, f64 }) |ty| { - const eps = epsForType(ty); - try expect(@log10(@as(ty, 100)) == 2); - try expect(math.approxEqAbs(ty, @log10(@as(ty, 15)), 1.176091259056, eps)); - try expect(math.approxEqAbs(ty, @log10(@as(ty, 50)), 1.698970004336, eps)); +test "@log10 f32/f64" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testLog10(&.{ f32, f64 }); + try comptime testLog10(&.{ f32, f64 }); +} + +test "@log10 f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testLog10(&.{ f80, f128, c_longdouble }); + try comptime testLog10(&.{ f80, f128, c_longdouble }); +} + +fn testLog10(comptime Ts: []const type) !void { + inline for (Ts) |T| { + const eps = epsForType(T); + var hundred: T = 100; + try expect(@log10(hundred) == 2); + var fifteen: T = 15; + try expect(math.approxEqAbs(T, @log10(fifteen), 1.176091259056, eps)); + var fifty: T = 50; + try expect(math.approxEqAbs(T, @log10(fifty), 1.698970004336, eps)); } } test "@log10 with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try comptime testLog10WithVectors(); try testLog10WithVectors(); + try comptime testLog10WithVectors(); } fn testLog10WithVectors() !void { @@ -528,8 +761,8 @@ test "@abs" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - try comptime testFabs(); try testFabs(); + try comptime testFabs(); } fn testFabs() !void { @@ -556,8 +789,8 @@ test "@abs with vectors" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - try comptime testFabsWithVectors(); try testFabsWithVectors(); + try comptime testFabsWithVectors(); } fn testFabsWithVectors() !void { @@ -573,9 +806,9 @@ test "another, possibly redundant, @abs test" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testFabsLegacy(f128, 12.0); try comptime testFabsLegacy(f128, 12.0); @@ -596,9 +829,9 @@ test "@abs f80" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testFabsLegacy(f80, 12.0); try comptime testFabsLegacy(f80, 12.0); @@ -614,9 +847,9 @@ test "a third @abs test, surely there should not be three fabs tests" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { // normals @@ -645,8 +878,8 @@ test "@floor" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - try comptime testFloor(); try testFloor(); + try comptime testFloor(); } fn testFloor() !void { @@ -664,14 +897,14 @@ fn testFloor() !void { test "@floor with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and - !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and + !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; - try comptime testFloorWithVectors(); try testFloorWithVectors(); + try comptime testFloorWithVectors(); } fn testFloorWithVectors() !void { @@ -686,8 +919,8 @@ fn testFloorWithVectors() !void { test "another, possibly redundant, @floor test" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testFloorLegacy(f64, 12.0); try comptime testFloorLegacy(f64, 12.0); @@ -705,9 +938,9 @@ test "another, possibly redundant, @floor test" { test "@floor f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { // https://github.com/ziglang/zig/issues/12602 @@ -721,9 +954,9 @@ test "@floor f80" { test "@floor f128" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testFloorLegacy(f128, 12.0); try comptime testFloorLegacy(f128, 12.0); @@ -740,8 +973,8 @@ test "@ceil" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - try comptime testCeil(); try testCeil(); + try comptime testCeil(); } fn testCeil() !void { @@ -759,14 +992,14 @@ fn testCeil() !void { test "@ceil with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and - !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and + !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; - try comptime testCeilWithVectors(); try testCeilWithVectors(); + try comptime testCeilWithVectors(); } fn testCeilWithVectors() !void { @@ -781,8 +1014,8 @@ fn testCeilWithVectors() !void { test "another, possibly redundant, @ceil test" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testCeilLegacy(f64, 12.0); try comptime testCeilLegacy(f64, 12.0); @@ -800,9 +1033,9 @@ test "another, possibly redundant, @ceil test" { test "@ceil f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { // https://github.com/ziglang/zig/issues/12602 @@ -816,9 +1049,9 @@ test "@ceil f80" { test "@ceil f128" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testCeilLegacy(f128, 12.0); try comptime testCeilLegacy(f128, 12.0); @@ -835,8 +1068,8 @@ test "@trunc" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - try comptime testTrunc(); try testTrunc(); + try comptime testTrunc(); } fn testTrunc() !void { @@ -854,14 +1087,14 @@ fn testTrunc() !void { test "@trunc with vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and - !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and + !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; - try comptime testTruncWithVectors(); try testTruncWithVectors(); + try comptime testTruncWithVectors(); } fn testTruncWithVectors() !void { @@ -876,8 +1109,8 @@ fn testTruncWithVectors() !void { test "another, possibly redundant, @trunc test" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { // https://github.com/ziglang/zig/issues/16846 @@ -900,9 +1133,9 @@ test "another, possibly redundant, @trunc test" { test "@trunc f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { // https://github.com/ziglang/zig/issues/12602 @@ -922,9 +1155,9 @@ test "@trunc f80" { test "@trunc f128" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; try testTruncLegacy(f128, 12.0); try comptime testTruncLegacy(f128, 12.0); @@ -945,11 +1178,11 @@ fn testTruncLegacy(comptime T: type, x: T) !void { } test "negation f16" { - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; if (builtin.os.tag == .freebsd) { // TODO file issue to track this failure @@ -1011,11 +1244,11 @@ test "negation f64" { } test "negation f80" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -1032,11 +1265,11 @@ test "negation f80" { } test "negation f128" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -1075,11 +1308,11 @@ test "f128 at compile time is lossy" { } test "comptime fixed-width float zero divided by zero produces NaN" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; inline for (.{ f16, f32, f64, f80, f128 }) |F| { try expect(math.isNan(@as(F, 0) / @as(F, 0))); @@ -1182,11 +1415,11 @@ test "nan negation f128" { } test "nan negation f80" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const nan_comptime = comptime math.nan(f80); const neg_nan_comptime = -nan_comptime; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 18c39e7b8e..9c32a604b2 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -7,6 +7,8 @@ const maxInt = std.math.maxInt; const minInt = std.math.minInt; const mem = std.mem; const math = std.math; +const no_x86_64_hardware_f16_support = builtin.zig_backend == .stage2_x86_64 and + !std.Target.x86.featureSetHas(builtin.cpu.features, .f16c); test "assignment operators" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1437,19 +1439,29 @@ fn fmodOne(comptime T: type, a: T, b: T, c: T, epsilon: T) !void { try expect(@abs(@mod(@as(T, a), @as(T, b)) - @as(T, c)) < epsilon); } -test "@round" { +test "@round f16" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO + + try testRound(f16, 12.0); + try comptime testRound(f16, 12.0); +} + +test "@round f32/f64" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testRound(f64, 12.0); try comptime testRound(f64, 12.0); try testRound(f32, 12.0); try comptime testRound(f32, 12.0); - try testRound(f16, 12.0); - try comptime testRound(f16, 12.0); const x = 14.0; const y = x + 0.4; @@ -1464,6 +1476,7 @@ test "@round f80" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testRound(f80, 12.0); try comptime testRound(f80, 12.0); @@ -1476,6 +1489,7 @@ test "@round f128" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testRound(f128, 12.0); try comptime testRound(f128, 12.0); @@ -1608,11 +1622,6 @@ test "signed zeros are represented properly" { try comptime S.doTheTest(); } -test "comptime sin and ln" { - const v = comptime (@sin(@as(f32, 1)) + @log(@as(f32, 5))); - try expect(v == @sin(@as(f32, 1)) + @log(@as(f32, 5))); -} - test "absFloat" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO From 6d5cbdb86394d517a3428242a7ab26384843fc0c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 24 Sep 2023 22:04:37 -0400 Subject: [PATCH 2/7] behavior: cleanup floatop tests --- src/arch/x86_64/CodeGen.zig | 4 +- test/behavior/floatop.zig | 1164 ++++++++++++++++------------------- test/behavior/vector.zig | 10 +- 3 files changed, 534 insertions(+), 644 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 3bdcaf7c42..8529339184 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5303,7 +5303,9 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { .f80_type => "__" ++ @tagName(comptime_tag) ++ "x", .f128_type => @tagName(comptime_tag) ++ "q", .c_longdouble_type => @tagName(comptime_tag) ++ "l", - else => unreachable, + else => return self.fail("TODO implement airUnaryMath for {s} of {}", .{ + @tagName(tag), ty.toType().fmt(self.bin_file.options.module.?), + }), }, else => unreachable, }, diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index a65e40997e..e20aea3be0 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -19,34 +19,93 @@ fn epsForType(comptime T: type) T { }; } -test "floating point comparisons" { +test "cmp f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testFloatComparisons(); - try comptime testFloatComparisons(); + try testCmp(f16); + try comptime testCmp(f16); } -fn testFloatComparisons() !void { - inline for ([_]type{ f16, f32, f64, f128 }) |T| { +test "cmp f32/f64" { + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testCmp(f32); + try comptime testCmp(f32); + try testCmp(f64); + try comptime testCmp(f64); +} + +test "cmp f128" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testCmp(f128); + try comptime testCmp(f128); +} + +test "cmp f80/c_longdouble" { + if (true) return error.SkipZigTest; + + try testCmp(f80); + try comptime testCmp(f80); + try testCmp(c_longdouble); + try comptime testCmp(c_longdouble); +} + +fn testCmp(comptime T: type) !void { + { // No decimal part - { - const x: T = 1.0; - try expect(x == 1); - try expect(x != 0); - try expect(x > 0); - try expect(x < 2); - try expect(x >= 1); - try expect(x <= 1); - } + var x: T = 1.0; + try expect(x == 1.0); + try expect(x != 0.0); + try expect(x > 0.0); + try expect(x < 2.0); + try expect(x >= 1.0); + try expect(x <= 1.0); + } + { // Non-zero decimal part - { - const x: T = 1.5; - try expect(x != 1); - try expect(x != 2); - try expect(x > 1); - try expect(x < 2); - try expect(x >= 1); - try expect(x <= 2); + var x: T = 1.5; + try expect(x != 1.0); + try expect(x != 2.0); + try expect(x > 1.0); + try expect(x < 2.0); + try expect(x >= 1.0); + try expect(x <= 2.0); + } + + @setEvalBranchQuota(2_000); + var edges = [_]T{ + -math.inf(T), + -math.floatMax(T), + -math.floatMin(T), + -math.floatTrueMin(T), + -0.0, + math.nan(T), + 0.0, + math.floatTrueMin(T), + math.floatMin(T), + math.floatMax(T), + math.inf(T), + }; + for (edges, 0..) |rhs, rhs_i| { + for (edges, 0..) |lhs, lhs_i| { + const no_nan = lhs_i != 5 and rhs_i != 5; + const lhs_order = if (lhs_i < 5) lhs_i else lhs_i - 2; + const rhs_order = if (rhs_i < 5) rhs_i else rhs_i - 2; + try expect((lhs == rhs) == (no_nan and lhs_order == rhs_order)); + try expect((lhs != rhs) == !(no_nan and lhs_order == rhs_order)); + try expect((lhs < rhs) == (no_nan and lhs_order < rhs_order)); + try expect((lhs > rhs) == (no_nan and lhs_order > rhs_order)); + try expect((lhs <= rhs) == (no_nan and lhs_order <= rhs_order)); + try expect((lhs >= rhs) == (no_nan and lhs_order >= rhs_order)); } } } @@ -68,27 +127,6 @@ fn testDifferentSizedFloatComparisons() !void { try expect(a < b); } -test "f80 comparisons" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try expect(compareF80(0.0, .eq, -0.0)); - try expect(compareF80(0.0, .lte, -0.0)); - try expect(compareF80(0.0, .gte, -0.0)); - try expect(compareF80(1.0, .neq, -1.0)); - try expect(compareF80(2.0, .lt, 4.0)); - try expect(compareF80(2.0, .lte, 4.0)); - try expect(compareF80(-2.0, .gt, -4.0)); - try expect(compareF80(-2.0, .gte, -4.0)); -} - -fn compareF80(x: f80, op: math.CompareOperator, y: f80) bool { - return math.compare(x, op, y); -} - // TODO This is waiting on library support for the Windows build (not sure why the other's don't need it) //test "@nearbyint" { // comptime testNearbyInt(); @@ -234,8 +272,8 @@ test "@sin f16" { if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testSin(&.{f16}); - try comptime testSin(&.{f16}); + try testSin(f16); + try comptime testSin(f16); } test "@sin f32/f64" { @@ -245,8 +283,10 @@ test "@sin f32/f64" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testSin(&.{ f32, f64 }); - try comptime testSin(&.{ f32, f64 }); + try testSin(f32); + comptime try testSin(f32); + try testSin(f64); + comptime try testSin(f64); } test "@sin f80/f128/c_longdouble" { @@ -256,20 +296,22 @@ test "@sin f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testSin(&.{ f80, f128, c_longdouble }); - try comptime testSin(&.{ f80, f128, c_longdouble }); + try testSin(f80); + comptime try testSin(f80); + try testSin(f128); + comptime try testSin(f128); + try testSin(c_longdouble); + comptime try testSin(c_longdouble); } -fn testSin(comptime Ts: []const type) !void { - inline for (Ts) |T| { - const eps = epsForType(T); - var zero: T = 0; - try expect(@sin(zero) == 0); - var pi: T = std.math.pi; - try expect(math.approxEqAbs(T, @sin(pi), 0, eps)); - try expect(math.approxEqAbs(T, @sin(pi / 2.0), 1, eps)); - try expect(math.approxEqAbs(T, @sin(pi / 4.0), 0.7071067811865475, eps)); - } +fn testSin(comptime T: type) !void { + const eps = epsForType(T); + var zero: T = 0; + try expect(@sin(zero) == 0); + var pi: T = math.pi; + try expect(math.approxEqAbs(T, @sin(pi), 0, eps)); + try expect(math.approxEqAbs(T, @sin(pi / 2.0), 1, eps)); + try expect(math.approxEqAbs(T, @sin(pi / 4.0), 0.7071067811865475, eps)); } test "@sin with vectors" { @@ -300,8 +342,8 @@ test "@cos f16" { if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testCos(&.{f16}); - try comptime testCos(&.{f16}); + try testCos(f16); + try comptime testCos(f16); } test "@cos f32/f64" { @@ -311,8 +353,10 @@ test "@cos f32/f64" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testCos(&.{ f32, f64 }); - try comptime testCos(&.{ f32, f64 }); + try testCos(f32); + try comptime testCos(f32); + try testCos(f64); + try comptime testCos(f64); } test "@cos f80/f128/c_longdouble" { @@ -322,20 +366,22 @@ test "@cos f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testCos(&.{ f80, f128, c_longdouble }); - try comptime testCos(&.{ f80, f128, c_longdouble }); + try testCos(f80); + try comptime testCos(f80); + try testCos(f128); + try comptime testCos(f128); + try testCos(c_longdouble); + try comptime testCos(c_longdouble); } -fn testCos(comptime Ts: []const type) !void { - inline for (Ts) |T| { - const eps = epsForType(T); - var zero: T = 0; - try expect(@cos(zero) == 1); - var pi: T = std.math.pi; - try expect(math.approxEqAbs(T, @cos(pi), -1, eps)); - try expect(math.approxEqAbs(T, @cos(pi / 2.0), 0, eps)); - try expect(math.approxEqAbs(T, @cos(pi / 4.0), 0.7071067811865475, eps)); - } +fn testCos(comptime T: type) !void { + const eps = epsForType(T); + var zero: T = 0; + try expect(@cos(zero) == 1); + var pi: T = math.pi; + try expect(math.approxEqAbs(T, @cos(pi), -1, eps)); + try expect(math.approxEqAbs(T, @cos(pi / 2.0), 0, eps)); + try expect(math.approxEqAbs(T, @cos(pi / 4.0), 0.7071067811865475, eps)); } test "@cos with vectors" { @@ -366,8 +412,8 @@ test "@tan f16" { if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testTan(&.{f16}); - try comptime testTan(&.{f16}); + try testTan(f16); + try comptime testTan(f16); } test "@tan f32/f64" { @@ -377,8 +423,10 @@ test "@tan f32/f64" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testTan(&.{ f32, f64 }); - try comptime testTan(&.{ f32, f64 }); + try testTan(f32); + try comptime testTan(f32); + try testTan(f64); + try comptime testTan(f64); } test "@tan f80/f128/c_longdouble" { @@ -388,20 +436,22 @@ test "@tan f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testTan(&.{ f80, f128, c_longdouble }); - try comptime testTan(&.{ f80, f128, c_longdouble }); + try testTan(f80); + try comptime testTan(f80); + try testTan(f128); + try comptime testTan(f128); + try testTan(c_longdouble); + try comptime testTan(c_longdouble); } -fn testTan(comptime Ts: []const type) !void { - inline for (Ts) |T| { - const eps = epsForType(T); - var zero: T = 0; - try expect(@tan(zero) == 0); - var pi: T = std.math.pi; - try expect(math.approxEqAbs(T, @tan(pi), 0, eps)); - try expect(math.approxEqAbs(T, @tan(pi / 3.0), 1.732050807568878, eps)); - try expect(math.approxEqAbs(T, @tan(pi / 4.0), 1, eps)); - } +fn testTan(comptime T: type) !void { + const eps = epsForType(T); + var zero: T = 0; + try expect(@tan(zero) == 0); + var pi: T = math.pi; + try expect(math.approxEqAbs(T, @tan(pi), 0, eps)); + try expect(math.approxEqAbs(T, @tan(pi / 3.0), 1.732050807568878, eps)); + try expect(math.approxEqAbs(T, @tan(pi / 4.0), 1, eps)); } test "@tan with vectors" { @@ -432,8 +482,8 @@ test "@exp f16" { if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testExp(&.{f16}); - try comptime testExp(&.{f16}); + try testExp(f16); + try comptime testExp(f16); } test "@exp f32/f64" { @@ -443,8 +493,10 @@ test "@exp f32/f64" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testExp(&.{ f32, f64 }); - try comptime testExp(&.{ f32, f64 }); + try testExp(f32); + try comptime testExp(f32); + try testExp(f64); + try comptime testExp(f64); } test "@exp f80/f128/c_longdouble" { @@ -454,20 +506,22 @@ test "@exp f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testExp(&.{ f80, f128, c_longdouble }); - try comptime testExp(&.{ f80, f128, c_longdouble }); + try testExp(f80); + try comptime testExp(f80); + try testExp(f128); + try comptime testExp(f128); + try testExp(c_longdouble); + try comptime testExp(c_longdouble); } -fn testExp(comptime Ts: []const type) !void { - inline for (Ts) |T| { - const eps = epsForType(T); - var zero: T = 0; - try expect(@exp(zero) == 1); - var two: T = 2; - try expect(math.approxEqAbs(T, @exp(two), 7.389056098930650, eps)); - var five: T = 5; - try expect(math.approxEqAbs(T, @exp(five), 148.4131591025766, eps)); - } +fn testExp(comptime T: type) !void { + const eps = epsForType(T); + var zero: T = 0; + try expect(@exp(zero) == 1); + var two: T = 2; + try expect(math.approxEqAbs(T, @exp(two), 7.389056098930650, eps)); + var five: T = 5; + try expect(math.approxEqAbs(T, @exp(five), 148.4131591025766, eps)); } test "@exp with vectors" { @@ -498,8 +552,8 @@ test "@exp2 f16" { if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testExp2(&.{f16}); - try comptime testExp2(&.{f16}); + try testExp2(f16); + try comptime testExp2(f16); } test "@exp2 f32/f64" { @@ -509,8 +563,10 @@ test "@exp2 f32/f64" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testExp2(&.{ f32, f64 }); - try comptime testExp2(&.{ f32, f64 }); + try testExp2(f32); + try comptime testExp2(f32); + try testExp2(f64); + try comptime testExp2(f64); } test "@exp2 f80/f128/c_longdouble" { @@ -520,20 +576,22 @@ test "@exp2 f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testExp2(&.{ f80, f128, c_longdouble }); - try comptime testExp2(&.{ f80, f128, c_longdouble }); + try testExp2(f80); + try comptime testExp2(f80); + try testExp2(f128); + try comptime testExp2(f128); + try testExp2(c_longdouble); + try comptime testExp2(c_longdouble); } -fn testExp2(comptime Ts: []const type) !void { - inline for (Ts) |T| { - const eps = epsForType(T); - var two: T = 2; - try expect(@exp2(two) == 4); - var one_point_five: T = 1.5; - try expect(math.approxEqAbs(T, @exp2(one_point_five), 2.8284271247462, eps)); - var four_point_five: T = 4.5; - try expect(math.approxEqAbs(T, @exp2(four_point_five), 22.627416997969, eps)); - } +fn testExp2(comptime T: type) !void { + const eps = epsForType(T); + var two: T = 2; + try expect(@exp2(two) == 4); + var one_point_five: T = 1.5; + try expect(math.approxEqAbs(T, @exp2(one_point_five), 2.8284271247462, eps)); + var four_point_five: T = 4.5; + try expect(math.approxEqAbs(T, @exp2(four_point_five), 22.627416997969, eps)); } test "@exp2 with @vectors" { @@ -564,8 +622,8 @@ test "@log f16" { if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testLog(&.{f16}); - try comptime testLog(&.{f16}); + try testLog(f16); + try comptime testLog(f16); } test "@log f32/f64" { @@ -575,8 +633,10 @@ test "@log f32/f64" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testLog(&.{ f32, f64 }); - try comptime testLog(&.{ f32, f64 }); + try testLog(f32); + try comptime testLog(f32); + try testLog(f64); + try comptime testLog(f64); } test "@log f80/f128/c_longdouble" { @@ -586,20 +646,22 @@ test "@log f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testLog(&.{ f80, f128, c_longdouble }); - try comptime testLog(&.{ f80, f128, c_longdouble }); + try testLog(f80); + try comptime testLog(f80); + try testLog(f128); + try comptime testLog(f128); + try testLog(c_longdouble); + try comptime testLog(c_longdouble); } -fn testLog(comptime Ts: []const type) !void { - inline for (Ts) |T| { - const eps = epsForType(T); - var e: T = std.math.e; - try expect(math.approxEqAbs(T, @log(e), 1, eps)); - var two: T = 2; - try expect(math.approxEqAbs(T, @log(two), 0.6931471805599, eps)); - var five: T = 5; - try expect(math.approxEqAbs(T, @log(five), 1.6094379124341, eps)); - } +fn testLog(comptime T: type) !void { + const eps = epsForType(T); + var e: T = math.e; + try expect(math.approxEqAbs(T, @log(e), 1, eps)); + var two: T = 2; + try expect(math.approxEqAbs(T, @log(two), 0.6931471805599, eps)); + var five: T = 5; + try expect(math.approxEqAbs(T, @log(five), 1.6094379124341, eps)); } test "@log with @vectors" { @@ -628,8 +690,8 @@ test "@log2 f16" { if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testLog2(&.{f16}); - try comptime testLog2(&.{f16}); + try testLog2(f16); + try comptime testLog2(f16); } test "@log2 f32/f64" { @@ -639,8 +701,10 @@ test "@log2 f32/f64" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testLog2(&.{ f32, f64 }); - try comptime testLog2(&.{ f32, f64 }); + try testLog2(f32); + try comptime testLog2(f32); + try testLog2(f64); + try comptime testLog2(f64); } test "@log2 f80/f128/c_longdouble" { @@ -650,20 +714,22 @@ test "@log2 f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testLog2(&.{ f80, f128, c_longdouble }); - try comptime testLog2(&.{ f80, f128, c_longdouble }); + try testLog2(f80); + try comptime testLog2(f80); + try testLog2(f128); + try comptime testLog2(f128); + try testLog2(c_longdouble); + try comptime testLog2(c_longdouble); } -fn testLog2(comptime Ts: []const type) !void { - inline for (Ts) |T| { - const eps = epsForType(T); - var four: T = 4; - try expect(@log2(four) == 2); - var six: T = 6; - try expect(math.approxEqAbs(T, @log2(six), 2.5849625007212, eps)); - var ten: T = 10; - try expect(math.approxEqAbs(T, @log2(ten), 3.3219280948874, eps)); - } +fn testLog2(comptime T: type) !void { + const eps = epsForType(T); + var four: T = 4; + try expect(@log2(four) == 2); + var six: T = 6; + try expect(math.approxEqAbs(T, @log2(six), 2.5849625007212, eps)); + var ten: T = 10; + try expect(math.approxEqAbs(T, @log2(ten), 3.3219280948874, eps)); } test "@log2 with vectors" { @@ -698,8 +764,8 @@ test "@log10 f16" { if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - try testLog10(&.{f16}); - try comptime testLog10(&.{f16}); + try testLog10(f16); + try comptime testLog10(f16); } test "@log10 f32/f64" { @@ -709,8 +775,10 @@ test "@log10 f32/f64" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testLog10(&.{ f32, f64 }); - try comptime testLog10(&.{ f32, f64 }); + try testLog10(f32); + try comptime testLog10(f32); + try testLog10(f64); + try comptime testLog10(f64); } test "@log10 f80/f128/c_longdouble" { @@ -720,20 +788,22 @@ test "@log10 f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testLog10(&.{ f80, f128, c_longdouble }); - try comptime testLog10(&.{ f80, f128, c_longdouble }); + try testLog10(f80); + try comptime testLog10(f80); + try testLog10(f128); + try comptime testLog10(f128); + try testLog10(c_longdouble); + try comptime testLog10(c_longdouble); } -fn testLog10(comptime Ts: []const type) !void { - inline for (Ts) |T| { - const eps = epsForType(T); - var hundred: T = 100; - try expect(@log10(hundred) == 2); - var fifteen: T = 15; - try expect(math.approxEqAbs(T, @log10(fifteen), 1.176091259056, eps)); - var fifty: T = 50; - try expect(math.approxEqAbs(T, @log10(fifty), 1.698970004336, eps)); - } +fn testLog10(comptime T: type) !void { + const eps = epsForType(T); + var hundred: T = 100; + try expect(@log10(hundred) == 2); + var fifteen: T = 15; + try expect(math.approxEqAbs(T, @log10(fifteen), 1.176091259056, eps)); + var fifty: T = 50; + try expect(math.approxEqAbs(T, @log10(fifty), 1.698970004336, eps)); } test "@log10 with vectors" { @@ -756,38 +826,91 @@ fn testLog10WithVectors() !void { try expect(@log10(@as(f32, 0.4)) == result[3]); } -test "@abs" { +test "@abs f16" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (no_x86_64_hardware_f16_support) return error.SkipZigTest; + + try testFabs(f16); + try comptime testFabs(f16); +} + +test "@abs f32/f64" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - try testFabs(); - try comptime testFabs(); + try testFabs(f32); + try comptime testFabs(f32); + try testFabs(f64); + try comptime testFabs(f64); } -fn testFabs() !void { - try expect(@abs(@as(f16, -2.5)) == 2.5); - try expect(@abs(@as(f16, 2.5)) == 2.5); - try expect(@abs(@as(f32, -2.5)) == 2.5); - try expect(@abs(@as(f32, 2.5)) == 2.5); - try expect(@abs(@as(f64, -2.5)) == 2.5); - try expect(@abs(@as(f64, 2.5)) == 2.5); +test "@abs f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - // { - // var a: f80 = -2.5; - // var b: f80 = 2.5; - // try expect(@abs(a) == 2.5); - // try expect(@abs(b) == 2.5); - // } + try testFabs(f80); + try comptime testFabs(f80); + try testFabs(f128); + try comptime testFabs(f128); + try testFabs(c_longdouble); + try comptime testFabs(c_longdouble); +} + +fn testFabs(comptime T: type) !void { + var two_point_five: T = 2.5; + try expect(@abs(two_point_five) == 2.5); + var neg_two_point_five: T = -2.5; + try expect(@abs(neg_two_point_five) == 2.5); + + var twelve: T = 12.0; + try expect(@abs(twelve) == 12.0); + var neg_fourteen: T = -14.0; + try expect(@abs(neg_fourteen) == 14.0); + + // normals + var one: T = 1.0; + try expect(@abs(one) == 1.0); + var neg_one: T = -1.0; + try expect(@abs(neg_one) == 1.0); + var min: T = math.floatMin(T); + try expect(@abs(min) == math.floatMin(T)); + var neg_min: T = -math.floatMin(T); + try expect(@abs(neg_min) == math.floatMin(T)); + var max: T = math.floatMax(T); + try expect(@abs(max) == math.floatMax(T)); + var neg_max: T = -math.floatMax(T); + try expect(@abs(neg_max) == math.floatMax(T)); + + // subnormals + var zero: T = 0.0; + try expect(@abs(zero) == 0.0); + var neg_zero: T = -0.0; + try expect(@abs(neg_zero) == 0.0); + var true_min: T = math.floatTrueMin(T); + try expect(@abs(true_min) == math.floatTrueMin(T)); + var neg_true_min: T = -math.floatTrueMin(T); + try expect(@abs(neg_true_min) == math.floatTrueMin(T)); + + // non-finite numbers + var inf: T = math.inf(T); + try expect(math.isPositiveInf(@abs(inf))); + var neg_inf: T = -math.inf(T); + try expect(math.isPositiveInf(@abs(neg_inf))); + var nan: T = math.nan(T); + try expect(math.isNan(@abs(nan))); } test "@abs with vectors" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testFabsWithVectors(); try comptime testFabsWithVectors(); @@ -802,104 +925,72 @@ fn testFabsWithVectors() !void { try expect(math.approxEqAbs(f32, @abs(@as(f32, -0.4)), result[3], epsilon)); } -test "another, possibly redundant, @abs test" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO +test "@floor f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try testFabsLegacy(f128, 12.0); - try comptime testFabsLegacy(f128, 12.0); - try testFabsLegacy(f64, 12.0); - try comptime testFabsLegacy(f64, 12.0); - try testFabsLegacy(f32, 12.0); - try comptime testFabsLegacy(f32, 12.0); - try testFabsLegacy(f16, 12.0); - try comptime testFabsLegacy(f16, 12.0); - - const x = 14.0; - const y = -x; - const z = @abs(y); - try comptime std.testing.expectEqual(x, z); -} - -test "@abs f80" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try testFabsLegacy(f80, 12.0); - try comptime testFabsLegacy(f80, 12.0); -} - -fn testFabsLegacy(comptime T: type, x: T) !void { - const y = -x; - const z = @abs(y); - try expect(x == z); -} - -test "a third @abs test, surely there should not be three fabs tests" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { - // normals - try expect(@abs(@as(T, 1.0)) == 1.0); - try expect(@abs(@as(T, -1.0)) == 1.0); - try expect(@abs(math.floatMin(T)) == math.floatMin(T)); - try expect(@abs(-math.floatMin(T)) == math.floatMin(T)); - try expect(@abs(math.floatMax(T)) == math.floatMax(T)); - try expect(@abs(-math.floatMax(T)) == math.floatMax(T)); - - // subnormals - try expect(@abs(@as(T, 0.0)) == 0.0); - try expect(@abs(@as(T, -0.0)) == 0.0); - try expect(@abs(math.floatTrueMin(T)) == math.floatTrueMin(T)); - try expect(@abs(-math.floatTrueMin(T)) == math.floatTrueMin(T)); - - // non-finite numbers - try expect(math.isPositiveInf(@abs(math.inf(T)))); - try expect(math.isPositiveInf(@abs(-math.inf(T)))); - try expect(math.isNan(@abs(math.nan(T)))); - } -} - -test "@floor" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testFloor(); - try comptime testFloor(); + try testFloor(f16); + try comptime testFloor(f16); } -fn testFloor() !void { - try expect(@floor(@as(f16, 2.1)) == 2); - try expect(@floor(@as(f32, 2.1)) == 2); - try expect(@floor(@as(f64, 3.5)) == 3); +test "@floor f32/f64" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf or !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1))) return error.SkipZigTest; - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - // { - // var a: f80 = 3.5; - // try expect(@floor(a) == 3); - // } + try testFloor(f32); + try comptime testFloor(f32); + try testFloor(f64); + try comptime testFloor(f64); +} + +test "@floor f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { + // https://github.com/ziglang/zig/issues/12602 + return error.SkipZigTest; + } + + try testFloor(f80); + try comptime testFloor(f80); + try testFloor(f128); + try comptime testFloor(f128); + try testFloor(c_longdouble); + try comptime testFloor(c_longdouble); +} + +fn testFloor(comptime T: type) !void { + var two_point_one: T = 2.1; + try expect(@floor(two_point_one) == 2.0); + var neg_two_point_one: T = -2.1; + try expect(@floor(neg_two_point_one) == -3.0); + var three_point_five: T = 3.5; + try expect(@floor(three_point_five) == 3.0); + var neg_three_point_five: T = -3.5; + try expect(@floor(neg_three_point_five) == -4.0); + var twelve: T = 12.0; + try expect(@floor(twelve) == 12.0); + var neg_twelve: T = -12.0; + try expect(@floor(neg_twelve) == -12.0); + var fourteen_point_seven: T = 14.7; + try expect(@floor(fourteen_point_seven) == 14.0); + var neg_fourteen_point_seven: T = -14.7; + try expect(@floor(neg_fourteen_point_seven) == -15.0); } test "@floor with vectors" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64 and !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; @@ -916,30 +1007,33 @@ fn testFloorWithVectors() !void { try expect(math.approxEqAbs(f32, @floor(@as(f32, -0.4)), result[3], epsilon)); } -test "another, possibly redundant, @floor test" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO +test "@ceil f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testFloorLegacy(f64, 12.0); - try comptime testFloorLegacy(f64, 12.0); - try testFloorLegacy(f32, 12.0); - try comptime testFloorLegacy(f32, 12.0); - try testFloorLegacy(f16, 12.0); - try comptime testFloorLegacy(f16, 12.0); - - const x = 14.0; - const y = x + 0.7; - const z = @floor(y); - try comptime expect(x == z); + try testCeil(f16); + try comptime testCeil(f16); } -test "@floor f80" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO +test "@ceil f32/f64" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf or !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1))) return error.SkipZigTest; + + try testCeil(f32); + try comptime testCeil(f32); + try testCeil(f64); + try comptime testCeil(f64); +} + +test "@ceil f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { @@ -947,54 +1041,38 @@ test "@floor f80" { return error.SkipZigTest; } - try testFloorLegacy(f80, 12.0); - try comptime testFloorLegacy(f80, 12.0); + try testCeil(f80); + try comptime testCeil(f80); + try testCeil(f128); + try comptime testCeil(f128); + try testCeil(c_longdouble); + try comptime testCeil(c_longdouble); } -test "@floor f128" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try testFloorLegacy(f128, 12.0); - try comptime testFloorLegacy(f128, 12.0); -} - -fn testFloorLegacy(comptime T: type, x: T) !void { - const y = x + 0.6; - const z = @floor(y); - try expect(x == z); -} - -test "@ceil" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - - try testCeil(); - try comptime testCeil(); -} - -fn testCeil() !void { - try expect(@ceil(@as(f16, 2.1)) == 3); - try expect(@ceil(@as(f32, 2.1)) == 3); - try expect(@ceil(@as(f64, 3.5)) == 4); - - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - // { - // var a: f80 = 3.5; - // try expect(@ceil(a) == 4); - // } +fn testCeil(comptime T: type) !void { + var two_point_one: T = 2.1; + try expect(@ceil(two_point_one) == 3.0); + var neg_two_point_one: T = -2.1; + try expect(@ceil(neg_two_point_one) == -2.0); + var three_point_five: T = 3.5; + try expect(@ceil(three_point_five) == 4.0); + var neg_three_point_five: T = -3.5; + try expect(@ceil(neg_three_point_five) == -3.0); + var twelve: T = 12.0; + try expect(@ceil(twelve) == 12.0); + var neg_twelve: T = -12.0; + try expect(@ceil(neg_twelve) == -12.0); + var fourteen_point_seven: T = 14.7; + try expect(@ceil(fourteen_point_seven) == 15.0); + var neg_fourteen_point_seven: T = -14.7; + try expect(@ceil(neg_fourteen_point_seven) == -14.0); } test "@ceil with vectors" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64 and !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; @@ -1011,30 +1089,43 @@ fn testCeilWithVectors() !void { try expect(math.approxEqAbs(f32, @ceil(@as(f32, -0.4)), result[3], epsilon)); } -test "another, possibly redundant, @ceil test" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO +test "@trunc f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - try testCeilLegacy(f64, 12.0); - try comptime testCeilLegacy(f64, 12.0); - try testCeilLegacy(f32, 12.0); - try comptime testCeilLegacy(f32, 12.0); - try testCeilLegacy(f16, 12.0); - try comptime testCeilLegacy(f16, 12.0); + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { + // https://github.com/ziglang/zig/issues/16846 + return error.SkipZigTest; + } - const x = 14.0; - const y = x - 0.7; - const z = @ceil(y); - try comptime expect(x == z); + try testTrunc(f16); + try comptime testTrunc(f16); } -test "@ceil f80" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO +test "@trunc f32/f64" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf or !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1))) return error.SkipZigTest; + + if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { + // https://github.com/ziglang/zig/issues/16846 + return error.SkipZigTest; + } + + try testTrunc(f32); + try comptime testTrunc(f32); + try testTrunc(f64); + try comptime testTrunc(f64); +} + +test "@trunc f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { @@ -1042,47 +1133,31 @@ test "@ceil f80" { return error.SkipZigTest; } - try testCeilLegacy(f80, 12.0); - try comptime testCeilLegacy(f80, 12.0); + try testTrunc(f80); + try comptime testTrunc(f80); + try testTrunc(f128); + try comptime testTrunc(f128); + try testTrunc(c_longdouble); + try comptime testTrunc(c_longdouble); } -test "@ceil f128" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try testCeilLegacy(f128, 12.0); - try comptime testCeilLegacy(f128, 12.0); -} - -fn testCeilLegacy(comptime T: type, x: T) !void { - const y = x - 0.8; - const z = @ceil(y); - try expect(x == z); -} - -test "@trunc" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - - try testTrunc(); - try comptime testTrunc(); -} - -fn testTrunc() !void { - try expect(@trunc(@as(f16, 2.1)) == 2); - try expect(@trunc(@as(f32, 2.1)) == 2); - try expect(@trunc(@as(f64, -3.5)) == -3); - - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - // { - // var a: f80 = -3.5; - // try expect(@trunc(a) == -3); - // } +fn testTrunc(comptime T: type) !void { + var two_point_one: T = 2.1; + try expect(@trunc(two_point_one) == 2.0); + var neg_two_point_one: T = -2.1; + try expect(@trunc(neg_two_point_one) == -2.0); + var three_point_five: T = 3.5; + try expect(@trunc(three_point_five) == 3.0); + var neg_three_point_five: T = -3.5; + try expect(@trunc(neg_three_point_five) == -3.0); + var twelve: T = 12.0; + try expect(@trunc(twelve) == 12.0); + var neg_twelve: T = -12.0; + try expect(@trunc(neg_twelve) == -12.0); + var fourteen_point_seven: T = 14.7; + try expect(@trunc(fourteen_point_seven) == 14.0); + var neg_fourteen_point_seven: T = -14.7; + try expect(@trunc(neg_fourteen_point_seven) == -14.0); } test "@trunc with vectors" { @@ -1106,82 +1181,12 @@ fn testTruncWithVectors() !void { try expect(math.approxEqAbs(f32, @trunc(@as(f32, -0.4)), result[3], epsilon)); } -test "another, possibly redundant, @trunc test" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO +test "neg f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { - // https://github.com/ziglang/zig/issues/16846 - return error.SkipZigTest; - } - - try testTruncLegacy(f64, 12.0); - try comptime testTruncLegacy(f64, 12.0); - try testTruncLegacy(f32, 12.0); - try comptime testTruncLegacy(f32, 12.0); - try testTruncLegacy(f16, 12.0); - try comptime testTruncLegacy(f16, 12.0); - - const x = 14.0; - const y = x + 0.7; - const z = @trunc(y); - try comptime expect(x == z); -} - -test "@trunc f80" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) { - // https://github.com/ziglang/zig/issues/12602 - return error.SkipZigTest; - } - - try testTruncLegacy(f80, 12.0); - try comptime testTruncLegacy(f80, 12.0); - comptime { - const x: f80 = 12.0; - const y = x + 0.8; - const z = @trunc(y); - try expect(x == z); - } -} - -test "@trunc f128" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - try testTruncLegacy(f128, 12.0); - try comptime testTruncLegacy(f128, 12.0); -} - -fn testTruncLegacy(comptime T: type, x: T) !void { - { - const y = x + 0.8; - const z = @trunc(y); - try expect(x == z); - } - - { - const y = -x - 0.8; - const z = @trunc(y); - try expect(-x == z); - } -} - -test "negation f16" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (no_x86_64_hardware_f16_support) return error.SkipZigTest; if (builtin.os.tag == .freebsd) { @@ -1189,100 +1194,85 @@ test "negation f16" { return error.SkipZigTest; } - const S = struct { - fn doTheTest() !void { - var a: f16 = 1; - a = -a; - try expect(a == -1); - a = -a; - try expect(a == 1); - } - }; - - try S.doTheTest(); - try comptime S.doTheTest(); + try testNeg(f16); + try comptime testNeg(f16); } -test "negation f32" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO +test "neg f32/f64" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - const S = struct { - fn doTheTest() !void { - var a: f32 = 1; - a = -a; - try expect(a == -1); - a = -a; - try expect(a == 1); - } - }; - - try S.doTheTest(); - try comptime S.doTheTest(); + try testNeg(f32); + try comptime testNeg(f32); + try testNeg(f64); + try comptime testNeg(f64); } -test "negation f64" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - - const S = struct { - fn doTheTest() !void { - var a: f64 = 1; - a = -a; - try expect(a == -1); - a = -a; - try expect(a == 1); - } - }; - - try S.doTheTest(); - try comptime S.doTheTest(); -} - -test "negation f80" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO +test "neg f80/f128/c_longdouble" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - const S = struct { - fn doTheTest() !void { - var a: f80 = 1; - a = -a; - try expect(a == -1); - a = -a; - try expect(a == 1); - } - }; - - try S.doTheTest(); - try comptime S.doTheTest(); + try testNeg(f80); + try comptime testNeg(f80); + try testNeg(f128); + try comptime testNeg(f128); + try testNeg(c_longdouble); + try comptime testNeg(c_longdouble); } -test "negation f128" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; +fn testNeg(comptime T: type) !void { + var two_point_five: T = 2.5; + try expect(-two_point_five == -2.5); + var neg_two_point_five: T = -2.5; + try expect(-neg_two_point_five == 2.5); - const S = struct { - fn doTheTest() !void { - var a: f128 = 1; - a = -a; - try expect(a == -1); - a = -a; - try expect(a == 1); - } - }; + var twelve: T = 12.0; + try expect(-twelve == -12.0); + var neg_fourteen: T = -14.0; + try expect(-neg_fourteen == 14.0); - try S.doTheTest(); - try comptime S.doTheTest(); + // normals + var one: T = 1.0; + try expect(-one == -1.0); + var neg_one: T = -1.0; + try expect(-neg_one == 1.0); + var min: T = math.floatMin(T); + try expect(-min == -math.floatMin(T)); + var neg_min: T = -math.floatMin(T); + try expect(-neg_min == math.floatMin(T)); + var max: T = math.floatMax(T); + try expect(-max == -math.floatMax(T)); + var neg_max: T = -math.floatMax(T); + try expect(-neg_max == math.floatMax(T)); + + // subnormals + var zero: T = 0.0; + try expect(-zero == -0.0); + var neg_zero: T = -0.0; + try expect(-neg_zero == 0.0); + var true_min: T = math.floatTrueMin(T); + try expect(-true_min == -math.floatTrueMin(T)); + var neg_true_min: T = -math.floatTrueMin(T); + try expect(-neg_true_min == math.floatTrueMin(T)); + + // non-finite numbers + var inf: T = math.inf(T); + try expect(math.isNegativeInf(-inf)); + var neg_inf: T = -math.inf(T); + try expect(math.isPositiveInf(-neg_inf)); + var nan: T = math.nan(T); + try expect(math.isNan(-nan)); + try expect(math.signbit(-nan)); + var neg_nan: T = -math.nan(T); + try expect(math.isNan(-neg_nan)); + try expect(!math.signbit(-neg_nan)); } test "eval @setFloatMode at compile-time" { @@ -1337,99 +1327,3 @@ test "comptime fixed-width float non-zero divided by zero produces signed Inf" { test "comptime_float zero divided by zero produces zero" { try expect((0.0 / 0.0) == 0.0); } - -test "nan negation f16" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - - const nan_comptime = comptime math.nan(f16); - const neg_nan_comptime = -nan_comptime; - - var nan_runtime = math.nan(f16); - const neg_nan_runtime = -nan_runtime; - - try expect(!math.signbit(nan_runtime)); - try expect(math.signbit(neg_nan_runtime)); - - try expect(!math.signbit(nan_comptime)); - try expect(math.signbit(neg_nan_comptime)); -} - -test "nan negation f32" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - - const nan_comptime = comptime math.nan(f32); - const neg_nan_comptime = -nan_comptime; - - var nan_runtime = math.nan(f32); - const neg_nan_runtime = -nan_runtime; - - try expect(!math.signbit(nan_runtime)); - try expect(math.signbit(neg_nan_runtime)); - - try expect(!math.signbit(nan_comptime)); - try expect(math.signbit(neg_nan_comptime)); -} - -test "nan negation f64" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - - const nan_comptime = comptime math.nan(f64); - const neg_nan_comptime = -nan_comptime; - - var nan_runtime = math.nan(f64); - const neg_nan_runtime = -nan_runtime; - - try expect(!math.signbit(nan_runtime)); - try expect(math.signbit(neg_nan_runtime)); - - try expect(!math.signbit(nan_comptime)); - try expect(math.signbit(neg_nan_comptime)); -} - -test "nan negation f128" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - - const nan_comptime = comptime math.nan(f128); - const neg_nan_comptime = -nan_comptime; - - var nan_runtime = math.nan(f128); - const neg_nan_runtime = -nan_runtime; - - try expect(!math.signbit(nan_runtime)); - try expect(math.signbit(neg_nan_runtime)); - - try expect(!math.signbit(nan_comptime)); - try expect(math.signbit(neg_nan_comptime)); -} - -test "nan negation f80" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - - const nan_comptime = comptime math.nan(f80); - const neg_nan_comptime = -nan_comptime; - - var nan_runtime = math.nan(f80); - const neg_nan_runtime = -nan_runtime; - - try expect(!math.signbit(nan_runtime)); - try expect(math.signbit(neg_nan_runtime)); - - try expect(!math.signbit(nan_comptime)); - try expect(math.signbit(neg_nan_comptime)); -} diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 0ad326d36e..183eaf9131 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -8,7 +8,6 @@ const expectEqual = std.testing.expectEqual; test "implicit cast vector to array - bool" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -214,13 +213,12 @@ test "array vector coercion - odd sizes" { } test "array to vector with element type coercion" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and - !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .f16c)) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -236,7 +234,6 @@ test "array to vector with element type coercion" { test "peer type resolution with coercible element types" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1440,7 +1437,6 @@ test "vector pointer is indexable" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1466,7 +1462,6 @@ test "boolean vector with 2 or more booleans" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO // TODO: try removing this after : @@ -1483,7 +1478,6 @@ test "bitcast to vector with different child type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO From 8470652f10a07f03b24746ffd786f6f5f4aabccc Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 24 Sep 2023 23:32:05 -0400 Subject: [PATCH 3/7] x86_64: implement float compare and cast builtins --- lib/compiler_rt/common.zig | 4 +- src/arch/x86_64/CodeGen.zig | 584 +++++++++++++++++++++++------------ test/behavior/asm.zig | 2 - test/behavior/bitcast.zig | 2 - test/behavior/bugs/12680.zig | 3 +- test/behavior/bugs/529.zig | 2 +- test/behavior/cast.zig | 23 -- test/behavior/defer.zig | 1 - test/behavior/floatop.zig | 3 +- test/behavior/struct.zig | 2 - test/behavior/union.zig | 3 - 11 files changed, 387 insertions(+), 242 deletions(-) diff --git a/lib/compiler_rt/common.zig b/lib/compiler_rt/common.zig index 3766d5a48a..eccd27e790 100644 --- a/lib/compiler_rt/common.zig +++ b/lib/compiler_rt/common.zig @@ -82,7 +82,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, _: ? /// need for extending them to wider fp types. /// TODO remove this; do this type selection in the language rather than /// here in compiler-rt. -pub fn F16T(comptime other_type: type) type { +pub fn F16T(comptime OtherType: type) type { return switch (builtin.cpu.arch) { .arm, .armeb, .thumb, .thumbeb => if (std.Target.arm.featureSetHas(builtin.cpu.features, .has_v8)) switch (builtin.abi.floatAbi()) { @@ -93,7 +93,7 @@ pub fn F16T(comptime other_type: type) type { u16, .aarch64, .aarch64_be, .aarch64_32 => f16, .riscv64 => if (builtin.zig_backend == .stage1) u16 else f16, - .x86, .x86_64 => if (builtin.target.isDarwin()) switch (other_type) { + .x86, .x86_64 => if (builtin.target.isDarwin()) switch (OtherType) { // Starting with LLVM 16, Darwin uses different abi for f16 // depending on the type of the other return/argument..??? f32, f64 => u16, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8529339184..73ec015782 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2515,63 +2515,96 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { const src_ty = self.typeOf(ty_op.operand); const src_bits = src_ty.floatBits(self.target.*); - const src_mcv = try self.resolveInst(ty_op.operand); - const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) - src_mcv - else - try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); - const dst_reg = dst_mcv.getReg().?.to128(); - const dst_lock = self.register_manager.lockReg(dst_reg); - defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + const result = result: { + if (switch (dst_bits) { + 16 => switch (src_bits) { + 32 => !self.hasFeature(.f16c), + 64, 80, 128 => true, + else => unreachable, + }, + 32 => switch (src_bits) { + 64 => false, + 80, 128 => true, + else => unreachable, + }, + 64 => switch (src_bits) { + 80, 128 => true, + else => unreachable, + }, + 80 => switch (dst_bits) { + 128 => true, + else => unreachable, + }, + else => unreachable, + }) { + var callee: ["__trunc?f?f2".len]u8 = undefined; + break :result try self.genCall(.{ .lib = .{ + .return_type = self.floatCompilerRtAbiType(dst_ty, src_ty).toIntern(), + .param_types = &.{self.floatCompilerRtAbiType(src_ty, dst_ty).toIntern()}, + .callee = std.fmt.bufPrint(&callee, "__trunc{c}f{c}f2", .{ + floatCompilerRtAbiName(src_bits), + floatCompilerRtAbiName(dst_bits), + }) catch unreachable, + } }, &.{ty_op.operand}); + } - if (dst_bits == 16 and self.hasFeature(.f16c)) { - switch (src_bits) { - 32 => { - const mat_src_reg = if (src_mcv.isRegister()) + const src_mcv = try self.resolveInst(ty_op.operand); + const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) + src_mcv + else + try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); + const dst_reg = dst_mcv.getReg().?.to128(); + const dst_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + + if (dst_bits == 16) { + assert(self.hasFeature(.f16c)); + switch (src_bits) { + 32 => { + const mat_src_reg = if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(src_ty, src_mcv); + try self.asmRegisterRegisterImmediate( + .{ .v_, .cvtps2ph }, + dst_reg, + mat_src_reg.to128(), + Immediate.u(0b1_00), + ); + }, + else => unreachable, + } + } else { + assert(src_bits == 64 and dst_bits == 32); + if (self.hasFeature(.avx)) if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory( + .{ .v_ss, .cvtsd2 }, + dst_reg, + dst_reg, + src_mcv.mem(.qword), + ) else try self.asmRegisterRegisterRegister( + .{ .v_ss, .cvtsd2 }, + dst_reg, + dst_reg, + (if (src_mcv.isRegister()) src_mcv.getReg().? else - try self.copyToTmpRegister(src_ty, src_mcv); - try self.asmRegisterRegisterImmediate( - .{ .v_, .cvtps2ph }, - dst_reg, - mat_src_reg.to128(), - Immediate.u(0b1_00), - ); - }, - else => return self.fail("TODO implement airFptrunc from {} to {}", .{ - src_ty.fmt(self.bin_file.options.module.?), dst_ty.fmt(self.bin_file.options.module.?), - }), + try self.copyToTmpRegister(src_ty, src_mcv)).to128(), + ) else if (src_mcv.isMemory()) try self.asmRegisterMemory( + .{ ._ss, .cvtsd2 }, + dst_reg, + src_mcv.mem(.qword), + ) else try self.asmRegisterRegister( + .{ ._ss, .cvtsd2 }, + dst_reg, + (if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(src_ty, src_mcv)).to128(), + ); } - } else if (src_bits == 64 and dst_bits == 32) { - if (self.hasFeature(.avx)) if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory( - .{ .v_ss, .cvtsd2 }, - dst_reg, - dst_reg, - src_mcv.mem(.qword), - ) else try self.asmRegisterRegisterRegister( - .{ .v_ss, .cvtsd2 }, - dst_reg, - dst_reg, - (if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(src_ty, src_mcv)).to128(), - ) else if (src_mcv.isMemory()) try self.asmRegisterMemory( - .{ ._ss, .cvtsd2 }, - dst_reg, - src_mcv.mem(.qword), - ) else try self.asmRegisterRegister( - .{ ._ss, .cvtsd2 }, - dst_reg, - (if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(src_ty, src_mcv)).to128(), - ); - } else return self.fail("TODO implement airFptrunc from {} to {}", .{ - src_ty.fmt(self.bin_file.options.module.?), dst_ty.fmt(self.bin_file.options.module.?), - }); - return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); + break :result dst_mcv; + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } fn airFpext(self: *Self, inst: Air.Inst.Index) !void { @@ -2581,58 +2614,96 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { const src_ty = self.typeOf(ty_op.operand); const src_bits = src_ty.floatBits(self.target.*); - const src_mcv = try self.resolveInst(ty_op.operand); - const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) - src_mcv - else - try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); - const dst_reg = dst_mcv.getReg().?.to128(); - const dst_lock = self.register_manager.lockReg(dst_reg); - defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); - - if (src_bits == 16 and self.hasFeature(.f16c)) { - const mat_src_reg = if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(src_ty, src_mcv); - try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, mat_src_reg.to128()); - switch (dst_bits) { - 32 => {}, - 64 => try self.asmRegisterRegisterRegister(.{ .v_sd, .cvtss2 }, dst_reg, dst_reg, dst_reg), - else => return self.fail("TODO implement airFpext from {} to {}", .{ - src_ty.fmt(self.bin_file.options.module.?), dst_ty.fmt(self.bin_file.options.module.?), - }), + const result = result: { + if (switch (src_bits) { + 16 => switch (dst_bits) { + 32, 64 => !self.hasFeature(.f16c), + 80, 128 => true, + else => unreachable, + }, + 32 => switch (dst_bits) { + 64 => false, + 80, 128 => true, + else => unreachable, + }, + 64 => switch (dst_bits) { + 80, 128 => true, + else => unreachable, + }, + 80 => switch (dst_bits) { + 128 => true, + else => unreachable, + }, + else => unreachable, + }) { + var callee: ["__extend?f?f2".len]u8 = undefined; + break :result try self.genCall(.{ .lib = .{ + .return_type = self.floatCompilerRtAbiType(dst_ty, src_ty).toIntern(), + .param_types = &.{self.floatCompilerRtAbiType(src_ty, dst_ty).toIntern()}, + .callee = std.fmt.bufPrint(&callee, "__extend{c}f{c}f2", .{ + floatCompilerRtAbiName(src_bits), + floatCompilerRtAbiName(dst_bits), + }) catch unreachable, + } }, &.{ty_op.operand}); } - } else if (src_bits == 32 and dst_bits == 64) { - if (self.hasFeature(.avx)) if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory( - .{ .v_sd, .cvtss2 }, - dst_reg, - dst_reg, - src_mcv.mem(.dword), - ) else try self.asmRegisterRegisterRegister( - .{ .v_sd, .cvtss2 }, - dst_reg, - dst_reg, - (if (src_mcv.isRegister()) + + const src_mcv = try self.resolveInst(ty_op.operand); + const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) + src_mcv + else + try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); + const dst_reg = dst_mcv.getReg().?.to128(); + const dst_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + + if (src_bits == 16) { + assert(self.hasFeature(.f16c)); + const mat_src_reg = if (src_mcv.isRegister()) src_mcv.getReg().? else - try self.copyToTmpRegister(src_ty, src_mcv)).to128(), - ) else if (src_mcv.isMemory()) try self.asmRegisterMemory( - .{ ._sd, .cvtss2 }, - dst_reg, - src_mcv.mem(.dword), - ) else try self.asmRegisterRegister( - .{ ._sd, .cvtss2 }, - dst_reg, - (if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(src_ty, src_mcv)).to128(), - ); - } else return self.fail("TODO implement airFpext from {} to {}", .{ - src_ty.fmt(self.bin_file.options.module.?), dst_ty.fmt(self.bin_file.options.module.?), - }); - return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); + try self.copyToTmpRegister(src_ty, src_mcv); + try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, mat_src_reg.to128()); + switch (dst_bits) { + 32 => {}, + 64 => try self.asmRegisterRegisterRegister( + .{ .v_sd, .cvtss2 }, + dst_reg, + dst_reg, + dst_reg, + ), + else => unreachable, + } + } else { + assert(src_bits == 32 and dst_bits == 64); + if (self.hasFeature(.avx)) if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory( + .{ .v_sd, .cvtss2 }, + dst_reg, + dst_reg, + src_mcv.mem(.dword), + ) else try self.asmRegisterRegisterRegister( + .{ .v_sd, .cvtss2 }, + dst_reg, + dst_reg, + (if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(src_ty, src_mcv)).to128(), + ) else if (src_mcv.isMemory()) try self.asmRegisterMemory( + .{ ._sd, .cvtss2 }, + dst_reg, + src_mcv.mem(.dword), + ) else try self.asmRegisterRegister( + .{ ._sd, .cvtss2 }, + dst_reg, + (if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(src_ty, src_mcv)).to128(), + ); + } + break :result dst_mcv; + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { @@ -8358,26 +8429,64 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const ty = self.typeOf(bin_op.lhs); - try self.spillEflagsIfOccupied(); - self.eflags_inst = inst; + const result: Condition = result: { + switch (ty.zigTypeTag(mod)) { + .Float => { + const float_bits = ty.floatBits(self.target.*); + if (switch (float_bits) { + 16 => !self.hasFeature(.f16c), + 32, 64 => false, + 80, 128 => true, + else => unreachable, + }) { + var callee: ["__???f2".len]u8 = undefined; + const ret = try self.genCall(.{ .lib = .{ + .return_type = .i32_type, + .param_types = &.{ ty.toIntern(), ty.toIntern() }, + .callee = std.fmt.bufPrint(&callee, "__{s}{c}f2", .{ + switch (op) { + .eq => "eq", + .neq => "ne", + .lt => "lt", + .lte => "le", + .gt => "gt", + .gte => "ge", + }, + floatCompilerRtAbiName(float_bits), + }) catch unreachable, + } }, &.{ bin_op.lhs, bin_op.rhs }); + try self.genBinOpMir(.{ ._, .@"test" }, Type.i32, ret, ret); + break :result switch (op) { + .eq => .e, + .neq => .ne, + .lt => .l, + .lte => .le, + .gt => .g, + .gte => .ge, + }; + } + }, + else => {}, + } - const lhs_mcv = try self.resolveInst(bin_op.lhs); - const lhs_lock = switch (lhs_mcv) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); + try self.spillEflagsIfOccupied(); - const rhs_mcv = try self.resolveInst(bin_op.rhs); - const rhs_lock = switch (rhs_mcv) { - .register => |reg| self.register_manager.lockReg(reg), - else => null, - }; - defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + const lhs_mcv = try self.resolveInst(bin_op.lhs); + const lhs_lock = switch (lhs_mcv) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); - const result = MCValue{ - .eflags = switch (ty.zigTypeTag(mod)) { - else => result: { + const rhs_mcv = try self.resolveInst(bin_op.rhs); + const rhs_lock = switch (rhs_mcv) { + .register => |reg| self.register_manager.lockReg(reg), + else => null, + }; + defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); + + switch (ty.zigTypeTag(mod)) { + else => { const abi_size: u16 = @intCast(ty.abiSize(mod)); const may_flip: enum { may_flip, @@ -8479,7 +8588,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { }, ); }, - .Float => result: { + .Float => { const flipped = switch (op) { .lt, .lte => true, .eq, .gte, .gt, .neq => false, @@ -8495,7 +8604,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const src_mcv = if (flipped) lhs_mcv else rhs_mcv; switch (ty.floatBits(self.target.*)) { - 16 => if (self.hasFeature(.f16c)) { + 16 => { + assert(self.hasFeature(.f16c)); const tmp1_reg = (try self.register_manager.allocReg(null, sse)).to128(); const tmp1_mcv = MCValue{ .register = tmp1_reg }; const tmp1_lock = self.register_manager.lockRegAssumeUnused(tmp1_reg); @@ -8524,9 +8634,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, tmp1_reg, tmp1_reg); try self.asmRegisterRegister(.{ .v_, .movshdup }, tmp2_reg, tmp1_reg); try self.genBinOpMir(.{ ._ss, .ucomi }, ty, tmp1_mcv, tmp2_mcv); - } else return self.fail("TODO implement airCmp for {}", .{ - ty.fmt(mod), - }), + }, 32 => try self.genBinOpMir( .{ ._ss, .ucomi }, ty, @@ -8539,9 +8647,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { .{ .register = dst_reg }, src_mcv, ), - else => return self.fail("TODO implement airCmp for {}", .{ - ty.fmt(mod), - }), + else => unreachable, } break :result switch (if (flipped) op.reverse() else op) { @@ -8552,9 +8658,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { .neq => .nz_or_p, }; }, - }, + } }; - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); + + self.eflags_inst = inst; + return self.finishAir(inst, .{ .eflags = result }, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { @@ -8572,7 +8680,6 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { try self.genLazySymbolRef(.lea, addr_reg, link.File.LazySymbol.initDecl(.const_data, null, mod)); try self.spillEflagsIfOccupied(); - self.eflags_inst = inst; const op_ty = self.typeOf(un_op); const op_abi_size: u32 = @intCast(op_ty.abiSize(mod)); @@ -8586,8 +8693,9 @@ fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { registerAlias(dst_reg, op_abi_size), Memory.sib(Memory.PtrSize.fromSize(op_abi_size), .{ .base = .{ .reg = addr_reg } }), ); - const result = MCValue{ .eflags = .b }; - return self.finishAir(inst, result, .{ un_op, .none, .none }); + + self.eflags_inst = inst; + return self.finishAir(inst, .{ .eflags = .b }, .{ un_op, .none, .none }); } fn airTry(self: *Self, inst: Air.Inst.Index) !void { @@ -8777,7 +8885,6 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC } try self.spillEflagsIfOccupied(); - self.eflags_inst = inst; const pl_ty = opt_ty.optionalChild(mod); @@ -8786,6 +8893,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC else .{ .off = @intCast(pl_ty.abiSize(mod)), .ty = Type.bool }; + self.eflags_inst = inst; switch (opt_mcv) { .none, .unreach, @@ -8867,7 +8975,6 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC fn isNullPtr(self: *Self, inst: Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) !MCValue { const mod = self.bin_file.options.module.?; try self.spillEflagsIfOccupied(); - self.eflags_inst = inst; const opt_ty = ptr_ty.childType(mod); const pl_ty = opt_ty.optionalChild(mod); @@ -8893,6 +9000,8 @@ fn isNullPtr(self: *Self, inst: Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) }), Immediate.u(0), ); + + self.eflags_inst = inst; return .{ .eflags = .e }; } @@ -8905,9 +9014,6 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) ! } try self.spillEflagsIfOccupied(); - if (maybe_inst) |inst| { - self.eflags_inst = inst; - } const err_off = errUnionErrorOffset(ty.errorUnionPayload(mod), mod); switch (operand) { @@ -8945,6 +9051,7 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) ! else => return self.fail("TODO implement isErr for {}", .{operand}), } + if (maybe_inst) |inst| self.eflags_inst = inst; return MCValue{ .eflags = .a }; } @@ -10526,106 +10633,150 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.options.module.?; const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const dst_ty = self.typeOfIndex(inst); + const dst_bits = dst_ty.floatBits(self.target.*); + const src_ty = self.typeOf(ty_op.operand); const src_bits: u32 = @intCast(src_ty.bitSize(mod)); const src_signedness = if (src_ty.isAbiInt(mod)) src_ty.intInfo(mod).signedness else .unsigned; - const dst_ty = self.typeOfIndex(inst); - const src_size = math.divCeil(u32, @max(switch (src_signedness) { .signed => src_bits, .unsigned => src_bits + 1, }, 32), 8) catch unreachable; - if (src_size > 8) return self.fail("TODO implement airFloatFromInt from {} to {}", .{ - src_ty.fmt(mod), dst_ty.fmt(mod), - }); - const src_mcv = try self.resolveInst(ty_op.operand); - const src_reg = if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(src_ty, src_mcv); - const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); - defer self.register_manager.unlockReg(src_lock); - - if (src_bits < src_size * 8) try self.truncateRegister(src_ty, src_reg); - - const dst_reg = try self.register_manager.allocReg(inst, regClassForType(dst_ty, mod)); - const dst_mcv = MCValue{ .register = dst_reg }; - const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); - defer self.register_manager.unlockReg(dst_lock); - - const mir_tag = @as(?Mir.Inst.FixedTag, switch (dst_ty.zigTypeTag(mod)) { - .Float => switch (dst_ty.floatBits(self.target.*)) { - 32 => if (self.hasFeature(.avx)) .{ .v_ss, .cvtsi2 } else .{ ._ss, .cvtsi2 }, - 64 => if (self.hasFeature(.avx)) .{ .v_sd, .cvtsi2 } else .{ ._sd, .cvtsi2 }, - 16, 80, 128 => null, + const result = result: { + if (switch (dst_bits) { + 16, 80, 128 => true, + 32, 64 => src_size > 8 and src_size < 16, else => unreachable, - }, - else => null, - }) orelse return self.fail("TODO implement airFloatFromInt from {} to {}", .{ - src_ty.fmt(mod), dst_ty.fmt(mod), - }); - const dst_alias = dst_reg.to128(); - const src_alias = registerAlias(src_reg, src_size); - switch (mir_tag[0]) { - .v_ss, .v_sd => try self.asmRegisterRegisterRegister(mir_tag, dst_alias, dst_alias, src_alias), - else => try self.asmRegisterRegister(mir_tag, dst_alias, src_alias), - } + }) { + var callee: ["__floatun?i?f".len]u8 = undefined; + break :result try self.genCall(.{ .lib = .{ + .return_type = dst_ty.toIntern(), + .param_types = &.{src_ty.toIntern()}, + .callee = std.fmt.bufPrint(&callee, "__float{s}{c}i{c}f", .{ + switch (src_signedness) { + .signed => "", + .unsigned => "un", + }, + intCompilerRtAbiName(src_bits), + floatCompilerRtAbiName(dst_bits), + }) catch unreachable, + } }, &.{ty_op.operand}); + } - return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); + if (src_size > 8) return self.fail("TODO implement airFloatFromInt from {} to {}", .{ + src_ty.fmt(mod), dst_ty.fmt(mod), + }); + + const src_mcv = try self.resolveInst(ty_op.operand); + const src_reg = if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(src_ty, src_mcv); + const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); + defer self.register_manager.unlockReg(src_lock); + + if (src_bits < src_size * 8) try self.truncateRegister(src_ty, src_reg); + + const dst_reg = try self.register_manager.allocReg(inst, regClassForType(dst_ty, mod)); + const dst_mcv = MCValue{ .register = dst_reg }; + const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_lock); + + const mir_tag = @as(?Mir.Inst.FixedTag, switch (dst_ty.zigTypeTag(mod)) { + .Float => switch (dst_ty.floatBits(self.target.*)) { + 32 => if (self.hasFeature(.avx)) .{ .v_ss, .cvtsi2 } else .{ ._ss, .cvtsi2 }, + 64 => if (self.hasFeature(.avx)) .{ .v_sd, .cvtsi2 } else .{ ._sd, .cvtsi2 }, + 16, 80, 128 => null, + else => unreachable, + }, + else => null, + }) orelse return self.fail("TODO implement airFloatFromInt from {} to {}", .{ + src_ty.fmt(mod), dst_ty.fmt(mod), + }); + const dst_alias = dst_reg.to128(); + const src_alias = registerAlias(src_reg, src_size); + switch (mir_tag[0]) { + .v_ss, .v_sd => try self.asmRegisterRegisterRegister(mir_tag, dst_alias, dst_alias, src_alias), + else => try self.asmRegisterRegister(mir_tag, dst_alias, src_alias), + } + + break :result dst_mcv; + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { const mod = self.bin_file.options.module.?; const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const src_ty = self.typeOf(ty_op.operand); const dst_ty = self.typeOfIndex(inst); const dst_bits: u32 = @intCast(dst_ty.bitSize(mod)); const dst_signedness = if (dst_ty.isAbiInt(mod)) dst_ty.intInfo(mod).signedness else .unsigned; - const dst_size = math.divCeil(u32, @max(switch (dst_signedness) { .signed => dst_bits, .unsigned => dst_bits + 1, }, 32), 8) catch unreachable; - if (dst_size > 8) return self.fail("TODO implement airIntFromFloat from {} to {}", .{ - src_ty.fmt(self.bin_file.options.module.?), dst_ty.fmt(self.bin_file.options.module.?), - }); - const src_mcv = try self.resolveInst(ty_op.operand); - const src_reg = if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(src_ty, src_mcv); - const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); - defer self.register_manager.unlockReg(src_lock); + const src_ty = self.typeOf(ty_op.operand); + const src_bits = src_ty.floatBits(self.target.*); - const dst_reg = try self.register_manager.allocReg(inst, regClassForType(dst_ty, mod)); - const dst_mcv = MCValue{ .register = dst_reg }; - const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); - defer self.register_manager.unlockReg(dst_lock); + const result = result: { + if (switch (src_bits) { + 16, 80, 128 => true, + 32, 64 => dst_size > 8 and dst_size < 16, + else => unreachable, + }) { + var callee: ["__fixuns?f?i".len]u8 = undefined; + break :result try self.genCall(.{ .lib = .{ + .return_type = dst_ty.toIntern(), + .param_types = &.{src_ty.toIntern()}, + .callee = std.fmt.bufPrint(&callee, "__fix{s}{c}f{c}i", .{ + switch (dst_signedness) { + .signed => "", + .unsigned => "uns", + }, + floatCompilerRtAbiName(src_bits), + intCompilerRtAbiName(dst_bits), + }) catch unreachable, + } }, &.{ty_op.operand}); + } - try self.asmRegisterRegister( - @as(?Mir.Inst.FixedTag, switch (src_ty.zigTypeTag(mod)) { - .Float => switch (src_ty.floatBits(self.target.*)) { + if (dst_size > 8) return self.fail("TODO implement airIntFromFloat from {} to {}", .{ + src_ty.fmt(mod), dst_ty.fmt(mod), + }); + + const src_mcv = try self.resolveInst(ty_op.operand); + const src_reg = if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(src_ty, src_mcv); + const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); + defer self.register_manager.unlockReg(src_lock); + + const dst_reg = try self.register_manager.allocReg(inst, regClassForType(dst_ty, mod)); + const dst_mcv = MCValue{ .register = dst_reg }; + const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); + defer self.register_manager.unlockReg(dst_lock); + + try self.asmRegisterRegister( + switch (src_bits) { 32 => if (self.hasFeature(.avx)) .{ .v_, .cvttss2si } else .{ ._, .cvttss2si }, 64 => if (self.hasFeature(.avx)) .{ .v_, .cvttsd2si } else .{ ._, .cvttsd2si }, - 16, 80, 128 => null, else => unreachable, }, - else => null, - }) orelse return self.fail("TODO implement airIntFromFloat from {} to {}", .{ - src_ty.fmt(self.bin_file.options.module.?), dst_ty.fmt(self.bin_file.options.module.?), - }), - registerAlias(dst_reg, dst_size), - src_reg.to128(), - ); + registerAlias(dst_reg, dst_size), + src_reg.to128(), + ); - if (dst_bits < dst_size * 8) try self.truncateRegister(dst_ty, dst_reg); + if (dst_bits < dst_size * 8) try self.truncateRegister(dst_ty, dst_reg); - return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); + break :result dst_mcv; + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { @@ -12276,3 +12427,30 @@ fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { const mod = self.bin_file.options.module.?; return self.air.typeOfIndex(inst, &mod.intern_pool); } + +fn intCompilerRtAbiName(int_bits: u32) u8 { + return switch (int_bits) { + 1...32 => 's', + 33...64 => 'd', + 65...128 => 't', + else => unreachable, + }; +} + +fn floatCompilerRtAbiName(float_bits: u32) u8 { + return switch (float_bits) { + 16 => 'h', + 32 => 's', + 64 => 'd', + 80 => 'x', + 128 => 't', + else => unreachable, + }; +} + +fn floatCompilerRtAbiType(self: *Self, ty: Type, other_ty: Type) Type { + if (ty.toIntern() == .f16_type and + (other_ty.toIntern() == .f32_type or other_ty.toIntern() == .f64_type) and + self.target.isDarwin()) return Type.u16; + return ty; +} diff --git a/test/behavior/asm.zig b/test/behavior/asm.zig index fb08020ff3..cde0b0cc8a 100644 --- a/test/behavior/asm.zig +++ b/test/behavior/asm.zig @@ -85,7 +85,6 @@ test "alternative constraints" { test "sized integer/float in asm input" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -137,7 +136,6 @@ test "sized integer/float in asm input" { test "struct/array/union types as input values" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 4294cf117a..e00a5bc90b 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -470,7 +470,6 @@ test "@bitCast of packed struct of bools all true" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -492,7 +491,6 @@ test "@bitCast of packed struct of bools all false" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/12680.zig b/test/behavior/bugs/12680.zig index b86a015e68..f7d1963011 100644 --- a/test/behavior/bugs/12680.zig +++ b/test/behavior/bugs/12680.zig @@ -9,9 +9,10 @@ test "export a function twice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + if (builtin.os.tag == .macos and builtin.zig_backend == .stage2_c) { // TODO: test.c: error: aliases are not supported on darwin return error.SkipZigTest; diff --git a/test/behavior/bugs/529.zig b/test/behavior/bugs/529.zig index 49a9b0a46d..5705399061 100644 --- a/test/behavior/bugs/529.zig +++ b/test/behavior/bugs/529.zig @@ -11,11 +11,11 @@ comptime { const builtin = @import("builtin"); test "issue 529 fixed" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; @import("529_other_file.zig").issue529(null); issue529(null); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index dcd7a8b426..5da8eccd11 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1611,7 +1611,6 @@ test "coercion from single-item pointer to @as to slice" { test "peer type resolution: const sentinel slice and mutable non-sentinel slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1664,7 +1663,6 @@ test "peer type resolution: float and comptime-known fixed-width integer" { test "peer type resolution: same array type with sentinel" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1687,7 +1685,6 @@ test "peer type resolution: same array type with sentinel" { test "peer type resolution: array with sentinel and array without sentinel" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1710,7 +1707,6 @@ test "peer type resolution: array with sentinel and array without sentinel" { test "peer type resolution: array and vector with same child type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1756,7 +1752,6 @@ test "peer type resolution: array with smaller child type and vector with larger test "peer type resolution: error union and optional of same type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1780,7 +1775,6 @@ test "peer type resolution: error union and optional of same type" { test "peer type resolution: C pointer and @TypeOf(null)" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1803,7 +1797,6 @@ test "peer type resolution: C pointer and @TypeOf(null)" { test "peer type resolution: three-way resolution combines error set and optional" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1846,7 +1839,6 @@ test "peer type resolution: three-way resolution combines error set and optional test "peer type resolution: vector and optional vector" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1869,7 +1861,6 @@ test "peer type resolution: vector and optional vector" { test "peer type resolution: optional fixed-width int and comptime_int" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1892,7 +1883,6 @@ test "peer type resolution: optional fixed-width int and comptime_int" { test "peer type resolution: array and tuple" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1916,7 +1906,6 @@ test "peer type resolution: array and tuple" { test "peer type resolution: vector and tuple" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1940,7 +1929,6 @@ test "peer type resolution: vector and tuple" { test "peer type resolution: vector and array and tuple" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -1983,7 +1971,6 @@ test "peer type resolution: vector and array and tuple" { test "peer type resolution: empty tuple pointer and slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2005,7 +1992,6 @@ test "peer type resolution: empty tuple pointer and slice" { test "peer type resolution: tuple pointer and slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2027,7 +2013,6 @@ test "peer type resolution: tuple pointer and slice" { test "peer type resolution: tuple pointer and optional slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2049,7 +2034,6 @@ test "peer type resolution: tuple pointer and optional slice" { test "peer type resolution: many compatible pointers" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2116,7 +2100,6 @@ test "peer type resolution: many compatible pointers" { test "peer type resolution: tuples with comptime fields" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2149,7 +2132,6 @@ test "peer type resolution: tuples with comptime fields" { test "peer type resolution: C pointer and many pointer" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2173,7 +2155,6 @@ test "peer type resolution: C pointer and many pointer" { test "peer type resolution: pointer attributes are combined correctly" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2218,7 +2199,6 @@ test "peer type resolution: pointer attributes are combined correctly" { test "cast builtins can wrap result in optional" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2258,7 +2238,6 @@ test "cast builtins can wrap result in optional" { test "cast builtins can wrap result in error union" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -2298,7 +2277,6 @@ test "cast builtins can wrap result in error union" { test "cast builtins can wrap result in error union and optional" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO @@ -2496,7 +2474,6 @@ test "@as does not corrupt values with incompatible representations" { test "result information is preserved through many nested structures" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index 86a9a40246..c70fad4b45 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -133,7 +133,6 @@ test "errdefer with payload" { } test "reference to errdefer payload" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index e20aea3be0..1c8f0aa886 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -23,7 +23,6 @@ test "cmp f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testCmp(f16); try comptime testCmp(f16); @@ -115,7 +114,7 @@ test "different sized float comparisons" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testDifferentSizedFloatComparisons(); try comptime testDifferentSizedFloatComparisons(); diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index f6fb45f46b..1c61c60bfd 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1651,7 +1651,6 @@ test "instantiate struct with comptime field" { test "struct field pointer has correct alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1684,7 +1683,6 @@ test "struct field pointer has correct alignment" { test "extern struct field pointer has correct alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/union.zig b/test/behavior/union.zig index bc3a7b044d..48d4ac9a68 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1534,7 +1534,6 @@ test "coerce enum literal to union in result loc" { test "defined-layout union field pointer has correct alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1572,7 +1571,6 @@ test "defined-layout union field pointer has correct alignment" { test "undefined-layout union field pointer has correct alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1610,7 +1608,6 @@ test "undefined-layout union field pointer has correct alignment" { test "packed union field pointer has correct alignment" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From c3042cbe12dec72c07e021a76f7e5438a03dc854 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 25 Sep 2023 01:35:44 -0400 Subject: [PATCH 4/7] x86_64: add missing caller preserved regs All allocatable registers have to be either callee preserved or caller preserved. --- src/arch/x86_64/abi.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 7809c4559c..1794abeb0b 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -444,7 +444,7 @@ pub const SysV = struct { /// These registers need to be preserved (saved on the stack) and restored by the caller before /// the caller relinquishes control to a subroutine via call instruction (or similar). /// In other words, these registers are free to use by the callee. - pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; + pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 } ++ sse_avx_regs; pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; @@ -457,7 +457,7 @@ pub const Win64 = struct { /// These registers need to be preserved (saved on the stack) and restored by the caller before /// the caller relinquishes control to a subroutine via call instruction (or similar). /// In other words, these registers are free to use by the callee. - pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 }; + pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .r8, .r9, .r10, .r11 } ++ sse_avx_regs; pub const c_abi_int_param_regs = [_]Register{ .rcx, .rdx, .r8, .r9 }; pub const c_abi_int_return_regs = [_]Register{.rax}; From 1eb023908d50e601f68f2200c3cf126ca3035167 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 30 Sep 2023 20:07:59 -0400 Subject: [PATCH 5/7] x86_64: implement float round builtins --- src/arch/x86_64/CodeGen.zig | 275 +++++++++++++++++++++--------- test/behavior/abs.zig | 3 +- test/behavior/floatop.zig | 16 +- test/behavior/math.zig | 3 +- test/behavior/maximum_minimum.zig | 15 +- test/behavior/vector.zig | 4 +- 6 files changed, 216 insertions(+), 100 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 73ec015782..c7967dde0c 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -215,6 +215,7 @@ pub const MCValue = union(enum) { /// but it has not been spilled there yet in the current control flow. /// Payload is a frame index. reserved_frame: FrameIndex, + air_ref: Air.Inst.Ref, fn isMemory(mcv: MCValue) bool { return switch (mcv) { @@ -278,6 +279,7 @@ pub const MCValue = union(enum) { .lea_tlv, .lea_frame, .reserved_frame, + .air_ref, => unreachable, // not in memory .memory => |addr| .{ .immediate = addr }, .indirect => |reg_off| switch (reg_off.off) { @@ -306,6 +308,7 @@ pub const MCValue = union(enum) { .load_tlv, .load_frame, .reserved_frame, + .air_ref, => unreachable, // not dereferenceable .immediate => |addr| .{ .memory = addr }, .register => |reg| .{ .indirect = .{ .reg = reg } }, @@ -335,6 +338,7 @@ pub const MCValue = union(enum) { .lea_tlv, .load_frame, .reserved_frame, + .air_ref, => unreachable, // not offsettable .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) }, .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } }, @@ -366,6 +370,7 @@ pub const MCValue = union(enum) { .lea_tlv, .lea_frame, .reserved_frame, + .air_ref, => unreachable, .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| Memory.sib(ptr_size, .{ .base = .{ .reg = .ds }, .disp = small_addr }) @@ -405,6 +410,7 @@ pub const MCValue = union(enum) { .load_frame => |pl| try writer.print("[{} + 0x{x}]", .{ pl.index, pl.off }), .lea_frame => |pl| try writer.print("{} + 0x{x}", .{ pl.index, pl.off }), .reserved_frame => |pl| try writer.print("(dead:{})", .{pl}), + .air_ref => |pl| try writer.print("(air:0x{x})", .{@intFromEnum(pl)}), } } }; @@ -433,6 +439,7 @@ const InstTracking = struct { => result, .dead, .reserved_frame, + .air_ref, => unreachable, .eflags, .register, @@ -491,6 +498,7 @@ const InstTracking = struct { .register_overflow, .indirect, .reserved_frame, + .air_ref, => unreachable, }; } @@ -531,6 +539,7 @@ const InstTracking = struct { .register_offset, .register_overflow, .indirect, + .air_ref, => unreachable, } } @@ -1809,9 +1818,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .round, => |tag| try self.airUnaryMath(inst, tag), - .floor => try self.airRound(inst, 0b1_0_01), - .ceil => try self.airRound(inst, 0b1_0_10), - .trunc_float => try self.airRound(inst, 0b1_0_11), + .floor => try self.airRound(inst, .{ .mode = .down, .precision = .inexact }), + .ceil => try self.airRound(inst, .{ .mode = .up, .precision = .inexact }), + .trunc_float => try self.airRound(inst, .{ .mode = .zero, .precision = .inexact }), .sqrt => try self.airSqrt(inst), .neg => try self.airFloatSign(inst), @@ -2439,7 +2448,7 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt } pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { - const tracking = self.inst_tracking.getPtr(inst).?; + const tracking = self.inst_tracking.getPtr(inst) orelse return; assert(tracking.getReg().?.id() == reg.id()); try tracking.spill(self, inst); tracking.trackSpill(self, inst); @@ -2545,7 +2554,7 @@ fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { floatCompilerRtAbiName(src_bits), floatCompilerRtAbiName(dst_bits), }) catch unreachable, - } }, &.{ty_op.operand}); + } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }}); } const src_mcv = try self.resolveInst(ty_op.operand); @@ -2644,7 +2653,7 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { floatCompilerRtAbiName(src_bits), floatCompilerRtAbiName(dst_bits), }) catch unreachable, - } }, &.{ty_op.operand}); + } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }}); } const src_mcv = try self.resolveInst(ty_op.operand); @@ -5073,7 +5082,26 @@ fn airFloatSign(self: *Self, inst: Air.Inst.Index) !void { return self.floatSign(inst, un_op, ty); } -fn airRound(self: *Self, inst: Air.Inst.Index, mode: u4) !void { +const RoundMode = packed struct(u5) { + mode: enum(u4) { + /// Round to nearest (even) + nearest = 0b0_00, + /// Round down (toward -∞) + down = 0b0_01, + /// Round up (toward +∞) + up = 0b0_10, + /// Round toward zero (truncate) + zero = 0b0_11, + /// Use current rounding mode of MXCSR.RC + mxcsr = 0b1_00, + }, + precision: enum(u1) { + normal = 0b0, + inexact = 0b1, + }, +}; + +fn airRound(self: *Self, inst: Air.Inst.Index, mode: RoundMode) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const ty = self.typeOf(un_op); @@ -5089,12 +5117,9 @@ fn airRound(self: *Self, inst: Air.Inst.Index, mode: u4) !void { return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); } -fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: u4) !void { +fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: RoundMode) !void { const mod = self.bin_file.options.module.?; - if (!self.hasFeature(.sse4_1)) - return self.fail("TODO implement genRound without sse4_1 feature", .{}); - - const mir_tag = @as(?Mir.Inst.FixedTag, switch (ty.zigTypeTag(mod)) { + const mir_tag = @as(?Mir.Inst.FixedTag, if (self.hasFeature(.sse4_1)) switch (ty.zigTypeTag(mod)) { .Float => switch (ty.floatBits(self.target.*)) { 32 => if (self.hasFeature(.avx)) .{ .v_ss, .round } else .{ ._ss, .round }, 64 => if (self.hasFeature(.avx)) .{ .v_sd, .round } else .{ ._sd, .round }, @@ -5121,9 +5146,28 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: u4 else => null, }, else => unreachable, - }) orelse return self.fail("TODO implement genRound for {}", .{ - ty.fmt(self.bin_file.options.module.?), - }); + } else null) orelse { + if (ty.zigTypeTag(mod) != .Float) return self.fail("TODO implement genRound for {}", .{ + ty.fmt(self.bin_file.options.module.?), + }); + + var callee: ["__trunc?".len]u8 = undefined; + const res = try self.genCall(.{ .lib = .{ + .return_type = ty.toIntern(), + .param_types = &.{ty.toIntern()}, + .callee = std.fmt.bufPrint(&callee, "{s}{s}{s}", .{ + floatLibcAbiPrefix(ty), + switch (mode.mode) { + .down => "floor", + .up => "ceil", + .zero => "trunc", + else => unreachable, + }, + floatLibcAbiSuffix(ty), + }) catch unreachable, + } }, &.{ty}, &.{src_mcv}); + return self.genSetReg(dst_reg, ty, res); + }; const abi_size: u32 = @intCast(ty.abiSize(mod)); const dst_alias = registerAlias(dst_reg, abi_size); switch (mir_tag[0]) { @@ -5132,7 +5176,7 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: u4 dst_alias, dst_alias, src_mcv.mem(Memory.PtrSize.fromSize(abi_size)), - Immediate.u(mode), + Immediate.u(@as(u5, @bitCast(mode))), ) else try self.asmRegisterRegisterRegisterImmediate( mir_tag, dst_alias, @@ -5141,13 +5185,13 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: u4 src_mcv.getReg().? else try self.copyToTmpRegister(ty, src_mcv), abi_size), - Immediate.u(mode), + Immediate.u(@as(u5, @bitCast(mode))), ), else => if (src_mcv.isMemory()) try self.asmRegisterMemoryImmediate( mir_tag, dst_alias, src_mcv.mem(Memory.PtrSize.fromSize(abi_size)), - Immediate.u(mode), + Immediate.u(@as(u5, @bitCast(mode))), ) else try self.asmRegisterRegisterImmediate( mir_tag, dst_alias, @@ -5155,7 +5199,7 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: u4 src_mcv.getReg().? else try self.copyToTmpRegister(ty, src_mcv), abi_size), - Immediate.u(mode), + Immediate.u(@as(u5, @bitCast(mode))), ), } } @@ -5353,34 +5397,29 @@ fn airSqrt(self: *Self, inst: Air.Inst.Index) !void { fn airUnaryMath(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; - const ty = self.typeOf(un_op).toIntern(); + const ty = self.typeOf(un_op); + var callee: ["__round?".len]u8 = undefined; const result = try self.genCall(.{ .lib = .{ - .return_type = ty, - .param_types = &.{ty}, - .callee = switch (tag) { - inline .sin, - .cos, - .tan, - .exp, - .exp2, - .log, - .log2, - .log10, - .round, - => |comptime_tag| switch (ty) { - .f16_type => "__" ++ @tagName(comptime_tag) ++ "h", - .f32_type => @tagName(comptime_tag) ++ "f", - .f64_type => @tagName(comptime_tag), - .f80_type => "__" ++ @tagName(comptime_tag) ++ "x", - .f128_type => @tagName(comptime_tag) ++ "q", - .c_longdouble_type => @tagName(comptime_tag) ++ "l", - else => return self.fail("TODO implement airUnaryMath for {s} of {}", .{ - @tagName(tag), ty.toType().fmt(self.bin_file.options.module.?), - }), + .return_type = ty.toIntern(), + .param_types = &.{ty.toIntern()}, + .callee = std.fmt.bufPrint(&callee, "{s}{s}{s}", .{ + floatLibcAbiPrefix(ty), + switch (tag) { + .sin, + .cos, + .tan, + .exp, + .exp2, + .log, + .log2, + .log10, + .round, + => @tagName(tag), + else => unreachable, }, - else => unreachable, - }, - } }, &.{un_op}); + floatLibcAbiSuffix(ty), + }) catch unreachable, + } }, &.{ty}, &.{.{ .air_ref = un_op }}); return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -5534,6 +5573,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } }); }, + .air_ref => |ref| try self.load(dst_mcv, ptr_ty, try self.resolveInst(ref)), } } @@ -5679,6 +5719,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv); }, + .air_ref => |ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ref)), } } @@ -6020,6 +6061,7 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MC .lea_tlv, .lea_frame, .reserved_frame, + .air_ref, => unreachable, // unmodifiable destination .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), .memory, .load_got, .load_direct, .load_tlv => { @@ -6679,7 +6721,11 @@ fn genBinOp( .min, .max, => { - const mat_src_mcv: MCValue = if (switch (src_mcv) { + const resolved_src_mcv = switch (src_mcv) { + else => src_mcv, + .air_ref => |ref| try self.resolveInst(ref), + }; + const mat_src_mcv: MCValue = if (switch (resolved_src_mcv) { .immediate, .eflags, .register_offset, @@ -6693,7 +6739,10 @@ fn genBinOp( => true, .memory => |addr| math.cast(i32, @as(i64, @bitCast(addr))) == null, else => false, - }) .{ .register = try self.copyToTmpRegister(rhs_ty, src_mcv) } else src_mcv; + }) + .{ .register = try self.copyToTmpRegister(rhs_ty, resolved_src_mcv) } + else + resolved_src_mcv; const mat_mcv_lock = switch (mat_src_mcv) { .register => |reg| self.register_manager.lockReg(reg), else => null, @@ -6740,6 +6789,7 @@ fn genBinOp( .lea_tlv, .lea_frame, .reserved_frame, + .air_ref, => unreachable, .register => |src_reg| try self.asmCmovccRegisterRegister( registerAlias(tmp_reg, cmov_abi_size), @@ -7389,11 +7439,11 @@ fn genBinOp( lhs_ty, dst_reg, .{ .register = dst_reg }, - switch (air_tag) { - .div_trunc => 0b1_0_11, - .div_floor => 0b1_0_01, + .{ .mode = switch (air_tag) { + .div_trunc => .zero, + .div_floor => .down, else => unreachable, - }, + }, .precision = .inexact }, ), .bit_and, .bit_or, .xor => {}, .max, .min => if (maybe_mask_reg) |mask_reg| if (self.hasFeature(.avx)) { @@ -7651,6 +7701,7 @@ fn genBinOpMir( .lea_tlv, .lea_frame, .reserved_frame, + .air_ref, => unreachable, // unmodifiable destination .register, .register_offset => { assert(dst_mcv.isRegister()); @@ -7766,6 +7817,7 @@ fn genBinOpMir( else => unreachable, } }, + .air_ref => |ref| try self.genBinOpMir(mir_tag, ty, dst_mcv, try self.resolveInst(ref)), } }, .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => { @@ -7789,13 +7841,18 @@ fn genBinOpMir( }; defer if (dst_info) |info| self.register_manager.unlockReg(info.addr_lock); - const src_info: OpInfo = switch (src_mcv) { + const resolved_src_mcv = switch (src_mcv) { + else => src_mcv, + .air_ref => |ref| try self.resolveInst(ref), + }; + const src_info: OpInfo = switch (resolved_src_mcv) { .none, .unreach, .dead, .undef, .register_overflow, .reserved_frame, + .air_ref, => unreachable, .immediate, .register, @@ -7809,7 +7866,7 @@ fn genBinOpMir( .lea_frame, => null, .memory, .load_got, .load_direct, .load_tlv => src: { - switch (src_mcv) { + switch (resolved_src_mcv) { .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr))) != null and math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null) break :src null, @@ -7821,7 +7878,7 @@ fn genBinOpMir( const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); errdefer self.register_manager.unlockReg(src_addr_lock); - try self.genSetReg(src_addr_reg, Type.usize, src_mcv.address()); + try self.genSetReg(src_addr_reg, Type.usize, resolved_src_mcv.address()); break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock, @@ -7868,13 +7925,14 @@ fn genBinOpMir( else => unreachable, }, ); - switch (src_mcv) { + switch (resolved_src_mcv) { .none, .unreach, .dead, .undef, .register_overflow, .reserved_frame, + .air_ref, => unreachable, .register => |src_reg| switch (off) { 0 => try self.asmMemoryRegister( @@ -7950,7 +8008,7 @@ fn genBinOpMir( => { const src_limb_reg = try self.copyToTmpRegister(limb_ty, if (src_info) |info| .{ .indirect = .{ .reg = info.addr_reg, .off = off }, - } else switch (src_mcv) { + } else switch (resolved_src_mcv) { .eflags, .register_offset, .lea_direct, @@ -7958,7 +8016,7 @@ fn genBinOpMir( .lea_tlv, .lea_frame, => switch (off) { - 0 => src_mcv, + 0 => resolved_src_mcv, else => .{ .immediate = 0 }, }, .memory => |addr| .{ .memory = @bitCast(@as(i64, @bitCast(addr)) + off) }, @@ -8003,19 +8061,25 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .lea_tlv, .lea_frame, .reserved_frame, + .air_ref, => unreachable, // unmodifiable destination .register => |dst_reg| { const dst_alias = registerAlias(dst_reg, abi_size); const dst_lock = self.register_manager.lockReg(dst_reg); defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); - switch (src_mcv) { + const resolved_src_mcv = switch (src_mcv) { + else => src_mcv, + .air_ref => |ref| try self.resolveInst(ref), + }; + switch (resolved_src_mcv) { .none, .unreach, .dead, .undef, .register_overflow, .reserved_frame, + .air_ref, => unreachable, .register => |src_reg| try self.asmRegisterRegister( .{ .i_, .mul }, @@ -8031,7 +8095,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M Immediate.s(small), ); } else { - const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv); + const src_reg = try self.copyToTmpRegister(dst_ty, resolved_src_mcv); return self.genIntMulComplexOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg }); } }, @@ -8047,19 +8111,22 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M => try self.asmRegisterRegister( .{ .i_, .mul }, dst_alias, - registerAlias(try self.copyToTmpRegister(dst_ty, src_mcv), abi_size), + registerAlias(try self.copyToTmpRegister(dst_ty, resolved_src_mcv), abi_size), ), .memory, .indirect, .load_frame => try self.asmRegisterMemory( .{ .i_, .mul }, dst_alias, - Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) { + Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (resolved_src_mcv) { .memory => |addr| .{ .base = .{ .reg = .ds }, .disp = math.cast(i32, @as(i64, @bitCast(addr))) orelse return self.asmRegisterRegister( .{ .i_, .mul }, dst_alias, - registerAlias(try self.copyToTmpRegister(dst_ty, src_mcv), abi_size), + registerAlias( + try self.copyToTmpRegister(dst_ty, resolved_src_mcv), + abi_size, + ), ), }, .indirect => |reg_off| .{ @@ -8221,13 +8288,30 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const pl_op = self.air.instructions.items(.data)[inst].pl_op; const extra = self.air.extraData(Air.Call, pl_op.payload); - const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); + const arg_refs: []const Air.Inst.Ref = + @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); - const ret = try self.genCall(.{ .air = pl_op.operand }, args); + const ExpectedContents = extern struct { + tys: [16][@sizeOf(Type)]u8 align(@alignOf(Type)), + vals: [16][@sizeOf(MCValue)]u8 align(@alignOf(MCValue)), + }; + var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = + std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + const allocator = stack.get(); + + const arg_tys = try allocator.alloc(Type, arg_refs.len); + defer allocator.free(arg_tys); + for (arg_tys, arg_refs) |*arg_ty, arg_ref| arg_ty.* = self.typeOf(arg_ref); + + const arg_vals = try allocator.alloc(MCValue, arg_refs.len); + defer allocator.free(arg_vals); + for (arg_vals, arg_refs) |*arg_val, arg_ref| arg_val.* = .{ .air_ref = arg_ref }; + + const ret = try self.genCall(.{ .air = pl_op.operand }, arg_tys, arg_vals); var bt = self.liveness.iterateBigTomb(inst); self.feed(&bt, pl_op.operand); - for (args) |arg| self.feed(&bt, arg); + for (arg_refs) |arg_ref| self.feed(&bt, arg_ref); const result = if (self.liveness.isUnused(inst)) .unreach else ret; return self.finishAirResult(inst, result); @@ -8241,7 +8325,7 @@ fn genCall(self: *Self, info: union(enum) { lib: ?[]const u8 = null, callee: []const u8, }, -}, args: []const Air.Inst.Ref) !MCValue { +}, arg_types: []const Type, args: []const MCValue) !MCValue { const mod = self.bin_file.options.module.?; const fn_ty = switch (info) { @@ -8261,8 +8345,17 @@ fn genCall(self: *Self, info: union(enum) { }; const fn_info = mod.typeToFunc(fn_ty).?; + const ExpectedContents = [16]Type; + var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = + std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + const allocator = stack.get(); + + const var_args = try allocator.alloc(Type, args.len - fn_info.param_types.len); + defer allocator.free(var_args); + for (var_args, arg_types[fn_info.param_types.len..]) |*var_arg, arg_ty| var_arg.* = arg_ty; + var call_info = - try self.resolveCallingConventionValues(fn_info, args[fn_info.param_types.len..], .call_frame); + try self.resolveCallingConventionValues(fn_info, var_args, .call_frame); defer call_info.deinit(self); // We need a properly aligned and sized call frame to be able to call this function. @@ -8290,10 +8383,10 @@ fn genCall(self: *Self, info: union(enum) { .indirect => |reg_off| try self.spillRegisters(&.{reg_off.reg}), else => unreachable, } - for (call_info.args, args) |dst_arg, src_arg| switch (dst_arg) { + for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| switch (dst_arg) { .none => {}, .register => |reg| try self.spillRegisters(&.{reg}), - .load_frame => try self.genCopy(self.typeOf(src_arg), dst_arg, try self.resolveInst(src_arg)), + .load_frame => try self.genCopy(arg_ty, dst_arg, src_arg), else => unreachable, }; @@ -8313,10 +8406,10 @@ fn genCall(self: *Self, info: union(enum) { }; defer if (ret_lock) |lock| self.register_manager.unlockReg(lock); - for (call_info.args, args) |dst_arg, src_arg| { + for (call_info.args, arg_types, args) |dst_arg, arg_ty, src_arg| { switch (dst_arg) { .none, .load_frame => {}, - .register => try self.genCopy(self.typeOf(src_arg), dst_arg, try self.resolveInst(src_arg)), + .register => try self.genCopy(arg_ty, dst_arg, src_arg), else => unreachable, } } @@ -8374,7 +8467,7 @@ fn genCall(self: *Self, info: union(enum) { } } else { assert(self.typeOf(callee).zigTypeTag(mod) == .Pointer); - try self.genSetReg(.rax, Type.usize, try self.resolveInst(callee)); + try self.genSetReg(.rax, Type.usize, .{ .air_ref = callee }); try self.asmRegister(.{ ._, .call }, .rax); }, .lib => |lib| try self.genExternSymbolRef(.call, lib.lib, lib.callee), @@ -8454,7 +8547,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { }, floatCompilerRtAbiName(float_bits), }) catch unreachable, - } }, &.{ bin_op.lhs, bin_op.rhs }); + } }, &.{ ty, ty }, &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs } }); try self.genBinOpMir(.{ ._, .@"test" }, Type.i32, ret, ret); break :result switch (op) { .eq => .e, @@ -8908,6 +9001,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC .lea_tlv, .lea_frame, .reserved_frame, + .air_ref, => unreachable, .register => |opt_reg| { @@ -9933,6 +10027,7 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError .lea_tlv, .lea_frame, .reserved_frame, + .air_ref, => unreachable, // unmodifiable destination .register => |reg| try self.genSetReg(reg, ty, src_mcv), .register_offset => |dst_reg_off| try self.genSetReg(dst_reg_off.reg, ty, switch (src_mcv) { @@ -10258,6 +10353,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr @tagName(self.bin_file.tag), }); }, + .air_ref => |ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(ref)), } } @@ -10370,6 +10466,7 @@ fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCVal }, else => try self.genInlineMemcpy(dst_ptr_mcv, src_mcv.address(), .{ .immediate = abi_size }), }, + .air_ref => |ref| try self.genSetMem(base, disp, ty, try self.resolveInst(ref)), } } @@ -10663,7 +10760,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { intCompilerRtAbiName(src_bits), floatCompilerRtAbiName(dst_bits), }) catch unreachable, - } }, &.{ty_op.operand}); + } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }}); } if (src_size > 8) return self.fail("TODO implement airFloatFromInt from {} to {}", .{ @@ -10742,7 +10839,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { floatCompilerRtAbiName(src_bits), intCompilerRtAbiName(dst_bits), }) catch unreachable, - } }, &.{ty_op.operand}); + } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }}); } if (dst_size > 8) return self.fail("TODO implement airIntFromFloat from {} to {}", .{ @@ -12103,7 +12200,7 @@ const CallMCValues = struct { fn resolveCallingConventionValues( self: *Self, fn_info: InternPool.Key.FuncType, - var_args: []const Air.Inst.Ref, + var_args: []const Type, stack_frame_base: FrameIndex, ) !CallMCValues { const mod = self.bin_file.options.module.?; @@ -12116,9 +12213,7 @@ fn resolveCallingConventionValues( dest.* = src.toType(); } // TODO: promote var arg types - for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg| { - param_ty.* = self.typeOf(arg); - } + for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg_ty| param_ty.* = arg_ty; var result: CallMCValues = .{ .args = try self.gpa.alloc(MCValue, param_types.len), @@ -12454,3 +12549,25 @@ fn floatCompilerRtAbiType(self: *Self, ty: Type, other_ty: Type) Type { self.target.isDarwin()) return Type.u16; return ty; } + +fn floatLibcAbiPrefix(ty: Type) []const u8 { + return switch (ty.toIntern()) { + .f16_type, + .f80_type, + => "__", + .f32_type, .f64_type, .f128_type, .c_longdouble_type => "", + else => unreachable, + }; +} + +fn floatLibcAbiSuffix(ty: Type) []const u8 { + return switch (ty.toIntern()) { + .f16_type => "h", + .f32_type => "f", + .f64_type => "", + .f80_type => "x", + .f128_type => "q", + .c_longdouble_type => "l", + else => unreachable, + }; +} diff --git a/test/behavior/abs.zig b/test/behavior/abs.zig index eefea306c4..f7e669b4f6 100644 --- a/test/behavior/abs.zig +++ b/test/behavior/abs.zig @@ -86,9 +86,10 @@ test "@abs floats" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try comptime testAbsFloats(f16); - if (builtin.zig_backend != .stage2_x86_64) try testAbsFloats(f16); + try testAbsFloats(f16); try comptime testAbsFloats(f32); try testAbsFloats(f32); try comptime testAbsFloats(f64); diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 1c8f0aa886..27a89f3d21 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -928,7 +928,7 @@ test "@floor f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testFloor(f16); try comptime testFloor(f16); @@ -938,7 +938,7 @@ test "@floor f32/f64" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf or !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1))) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testFloor(f32); try comptime testFloor(f32); @@ -1010,7 +1010,7 @@ test "@ceil f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testCeil(f16); try comptime testCeil(f16); @@ -1020,7 +1020,7 @@ test "@ceil f32/f64" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf or !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1))) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testCeil(f32); try comptime testCeil(f32); @@ -1092,7 +1092,7 @@ test "@trunc f16" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { // https://github.com/ziglang/zig/issues/16846 @@ -1107,7 +1107,7 @@ test "@trunc f32/f64" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and (builtin.target.ofmt != .elf or !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1))) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { // https://github.com/ziglang/zig/issues/16846 @@ -1160,10 +1160,10 @@ fn testTrunc(comptime T: type) !void { } test "@trunc with vectors" { - if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64 and !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 9c32a604b2..801aa2aeaa 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -412,12 +412,11 @@ fn testBinaryNot128(comptime Type: type, x: Type) !void { test "division" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and - !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) { // https://github.com/ziglang/zig/issues/16846 diff --git a/test/behavior/maximum_minimum.zig b/test/behavior/maximum_minimum.zig index 22514128d0..fc168adee7 100644 --- a/test/behavior/maximum_minimum.zig +++ b/test/behavior/maximum_minimum.zig @@ -10,6 +10,7 @@ test "@max" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -25,12 +26,11 @@ test "@max" { test "@max on vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and - !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -74,12 +74,11 @@ test "@min" { test "@min for vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and - !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -104,12 +103,12 @@ test "@min for vectors" { } test "@min/max for floats" { - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const S = struct { fn doTheTest(comptime T: type) !void { @@ -179,11 +178,11 @@ test "@min/@max notices bounds" { test "@min/@max notices vector bounds" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var x: @Vector(2, u16) = .{ 140, 40 }; const y: @Vector(2, u64) = .{ 5, 100 }; @@ -232,11 +231,11 @@ test "@min/@max notices bounds from types" { test "@min/@max notices bounds from vector types" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var x: @Vector(2, u16) = .{ 30, 67 }; var y: @Vector(2, u32) = .{ 20, 500 }; @@ -272,11 +271,11 @@ test "@min/@max notices bounds from types when comptime-known value is undef" { test "@min/@max notices bounds from vector types when element of comptime-known vector is undef" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; var x: @Vector(2, u32) = .{ 1_000_000, 12345 }; const y: @Vector(2, u16) = .{ 10, undefined }; diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 183eaf9131..81e51f4983 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -26,12 +26,12 @@ test "implicit cast vector to array - bool" { test "vector wrap operators" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64 and - !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and + !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { From fbe5bf469e17ffd57b34762fd0206587f5758a52 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 30 Sep 2023 22:11:51 -0400 Subject: [PATCH 6/7] x86_64: implement float arithmetic builtins --- src/arch/x86_64/CodeGen.zig | 378 +++++++++++++++++++----------------- test/behavior/floatop.zig | 107 ++++++++-- test/behavior/math.zig | 3 - 3 files changed, 301 insertions(+), 187 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c7967dde0c..c8859a7164 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -6570,6 +6570,34 @@ fn genBinOp( const lhs_ty = self.typeOf(lhs_air); const rhs_ty = self.typeOf(rhs_air); const abi_size: u32 = @intCast(lhs_ty.abiSize(mod)); + + if (lhs_ty.isRuntimeFloat() and switch (lhs_ty.floatBits(self.target.*)) { + 16 => !self.hasFeature(.f16c), + 32, 64 => false, + 80, 128 => true, + else => unreachable, + }) { + var callee: ["__add?f3".len]u8 = undefined; + return self.genCall(.{ .lib = .{ + .return_type = lhs_ty.toIntern(), + .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern() }, + .callee = switch (air_tag) { + .add, .sub, .mul, .div_float => std.fmt.bufPrint(&callee, "__{s}{c}f3", .{ + @tagName(air_tag)[0..3], + floatCompilerRtAbiName(lhs_ty.floatBits(self.target.*)), + }), + .min, .max => std.fmt.bufPrint(&callee, "{s}f{s}{s}", .{ + floatLibcAbiPrefix(lhs_ty), + @tagName(air_tag), + floatLibcAbiSuffix(lhs_ty), + }), + else => return self.fail("TODO implement genBinOp for {s} {}", .{ + @tagName(air_tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), + } catch unreachable, + } }, &.{ lhs_ty, rhs_ty }, &.{ .{ .air_ref = lhs_air }, .{ .air_ref = rhs_air } }); + } + if ((lhs_ty.scalarType(mod).isRuntimeFloat() and lhs_ty.scalarType(mod).floatBits(self.target.*) == 80) or lhs_ty.abiSize(mod) > @as(u6, if (self.hasFeature(.avx)) 32 else 16)) @@ -6830,7 +6858,8 @@ fn genBinOp( const mir_tag = @as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag(mod)) { else => unreachable, .Float => switch (lhs_ty.floatBits(self.target.*)) { - 16 => if (self.hasFeature(.f16c)) { + 16 => { + assert(self.hasFeature(.f16c)); const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128(); const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); defer self.register_manager.unlockReg(tmp_lock); @@ -6873,7 +6902,7 @@ fn genBinOp( Immediate.u(0b1_00), ); return dst_mcv; - } else null, + }, 32 => switch (air_tag) { .add => if (self.hasFeature(.avx)) .{ .v_ss, .add } else .{ ._ss, .add }, .sub => if (self.hasFeature(.avx)) .{ .v_ss, .sub } else .{ ._ss, .sub }, @@ -7134,181 +7163,184 @@ fn genBinOp( else => null, }, .Float => switch (lhs_ty.childType(mod).floatBits(self.target.*)) { - 16 => if (self.hasFeature(.f16c)) switch (lhs_ty.vectorLen(mod)) { - 1 => { - const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128(); - const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_lock); + 16 => tag: { + assert(self.hasFeature(.f16c)); + switch (lhs_ty.vectorLen(mod)) { + 1 => { + const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128(); + const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_lock); - if (src_mcv.isMemory()) try self.asmRegisterRegisterMemoryImmediate( - .{ .vp_w, .insr }, - dst_reg, - dst_reg, - src_mcv.mem(.word), - Immediate.u(1), - ) else try self.asmRegisterRegisterRegister( - .{ .vp_, .unpcklwd }, - dst_reg, - dst_reg, - (if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), - ); - try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); - try self.asmRegisterRegister(.{ .v_, .movshdup }, tmp_reg, dst_reg); - try self.asmRegisterRegisterRegister( - switch (air_tag) { - .add => .{ .v_ss, .add }, - .sub => .{ .v_ss, .sub }, - .mul => .{ .v_ss, .mul }, - .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ss, .div }, - .max => .{ .v_ss, .max }, - .min => .{ .v_ss, .max }, - else => unreachable, - }, - dst_reg, - dst_reg, - tmp_reg, - ); - try self.asmRegisterRegisterImmediate( - .{ .v_, .cvtps2ph }, - dst_reg, - dst_reg, - Immediate.u(0b1_00), - ); - return dst_mcv; - }, - 2 => { - const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128(); - const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_lock); + if (src_mcv.isMemory()) try self.asmRegisterRegisterMemoryImmediate( + .{ .vp_w, .insr }, + dst_reg, + dst_reg, + src_mcv.mem(.word), + Immediate.u(1), + ) else try self.asmRegisterRegisterRegister( + .{ .vp_, .unpcklwd }, + dst_reg, + dst_reg, + (if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), + ); + try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); + try self.asmRegisterRegister(.{ .v_, .movshdup }, tmp_reg, dst_reg); + try self.asmRegisterRegisterRegister( + switch (air_tag) { + .add => .{ .v_ss, .add }, + .sub => .{ .v_ss, .sub }, + .mul => .{ .v_ss, .mul }, + .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ss, .div }, + .max => .{ .v_ss, .max }, + .min => .{ .v_ss, .max }, + else => unreachable, + }, + dst_reg, + dst_reg, + tmp_reg, + ); + try self.asmRegisterRegisterImmediate( + .{ .v_, .cvtps2ph }, + dst_reg, + dst_reg, + Immediate.u(0b1_00), + ); + return dst_mcv; + }, + 2 => { + const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128(); + const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_lock); - if (src_mcv.isMemory()) try self.asmRegisterMemoryImmediate( - .{ .vp_d, .insr }, - dst_reg, - src_mcv.mem(.dword), - Immediate.u(1), - ) else try self.asmRegisterRegisterRegister( - .{ .v_ps, .unpckl }, - dst_reg, - dst_reg, - (if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), - ); - try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); - try self.asmRegisterRegisterRegister( - .{ .v_ps, .movhl }, - tmp_reg, - dst_reg, - dst_reg, - ); - try self.asmRegisterRegisterRegister( - switch (air_tag) { - .add => .{ .v_ps, .add }, - .sub => .{ .v_ps, .sub }, - .mul => .{ .v_ps, .mul }, - .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, - .max => .{ .v_ps, .max }, - .min => .{ .v_ps, .max }, - else => unreachable, - }, - dst_reg, - dst_reg, - tmp_reg, - ); - try self.asmRegisterRegisterImmediate( - .{ .v_, .cvtps2ph }, - dst_reg, - dst_reg, - Immediate.u(0b1_00), - ); - return dst_mcv; - }, - 3...4 => { - const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128(); - const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_lock); + if (src_mcv.isMemory()) try self.asmRegisterMemoryImmediate( + .{ .vp_d, .insr }, + dst_reg, + src_mcv.mem(.dword), + Immediate.u(1), + ) else try self.asmRegisterRegisterRegister( + .{ .v_ps, .unpckl }, + dst_reg, + dst_reg, + (if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), + ); + try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); + try self.asmRegisterRegisterRegister( + .{ .v_ps, .movhl }, + tmp_reg, + dst_reg, + dst_reg, + ); + try self.asmRegisterRegisterRegister( + switch (air_tag) { + .add => .{ .v_ps, .add }, + .sub => .{ .v_ps, .sub }, + .mul => .{ .v_ps, .mul }, + .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, + .max => .{ .v_ps, .max }, + .min => .{ .v_ps, .max }, + else => unreachable, + }, + dst_reg, + dst_reg, + tmp_reg, + ); + try self.asmRegisterRegisterImmediate( + .{ .v_, .cvtps2ph }, + dst_reg, + dst_reg, + Immediate.u(0b1_00), + ); + return dst_mcv; + }, + 3...4 => { + const tmp_reg = (try self.register_manager.allocReg(null, sse)).to128(); + const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_lock); - try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); - if (src_mcv.isMemory()) try self.asmRegisterMemory( - .{ .v_ps, .cvtph2 }, - tmp_reg, - src_mcv.mem(.qword), - ) else try self.asmRegisterRegister( - .{ .v_ps, .cvtph2 }, - tmp_reg, - (if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), - ); - try self.asmRegisterRegisterRegister( - switch (air_tag) { - .add => .{ .v_ps, .add }, - .sub => .{ .v_ps, .sub }, - .mul => .{ .v_ps, .mul }, - .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, - .max => .{ .v_ps, .max }, - .min => .{ .v_ps, .max }, - else => unreachable, - }, - dst_reg, - dst_reg, - tmp_reg, - ); - try self.asmRegisterRegisterImmediate( - .{ .v_, .cvtps2ph }, - dst_reg, - dst_reg, - Immediate.u(0b1_00), - ); - return dst_mcv; - }, - 5...8 => { - const tmp_reg = (try self.register_manager.allocReg(null, sse)).to256(); - const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); - defer self.register_manager.unlockReg(tmp_lock); + try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); + if (src_mcv.isMemory()) try self.asmRegisterMemory( + .{ .v_ps, .cvtph2 }, + tmp_reg, + src_mcv.mem(.qword), + ) else try self.asmRegisterRegister( + .{ .v_ps, .cvtph2 }, + tmp_reg, + (if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), + ); + try self.asmRegisterRegisterRegister( + switch (air_tag) { + .add => .{ .v_ps, .add }, + .sub => .{ .v_ps, .sub }, + .mul => .{ .v_ps, .mul }, + .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, + .max => .{ .v_ps, .max }, + .min => .{ .v_ps, .max }, + else => unreachable, + }, + dst_reg, + dst_reg, + tmp_reg, + ); + try self.asmRegisterRegisterImmediate( + .{ .v_, .cvtps2ph }, + dst_reg, + dst_reg, + Immediate.u(0b1_00), + ); + return dst_mcv; + }, + 5...8 => { + const tmp_reg = (try self.register_manager.allocReg(null, sse)).to256(); + const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); + defer self.register_manager.unlockReg(tmp_lock); - try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg.to256(), dst_reg); - if (src_mcv.isMemory()) try self.asmRegisterMemory( - .{ .v_ps, .cvtph2 }, - tmp_reg, - src_mcv.mem(.xword), - ) else try self.asmRegisterRegister( - .{ .v_ps, .cvtph2 }, - tmp_reg, - (if (src_mcv.isRegister()) - src_mcv.getReg().? - else - try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), - ); - try self.asmRegisterRegisterRegister( - switch (air_tag) { - .add => .{ .v_ps, .add }, - .sub => .{ .v_ps, .sub }, - .mul => .{ .v_ps, .mul }, - .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, - .max => .{ .v_ps, .max }, - .min => .{ .v_ps, .max }, - else => unreachable, - }, - dst_reg.to256(), - dst_reg.to256(), - tmp_reg, - ); - try self.asmRegisterRegisterImmediate( - .{ .v_, .cvtps2ph }, - dst_reg, - dst_reg.to256(), - Immediate.u(0b1_00), - ); - return dst_mcv; - }, - else => null, - } else null, + try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg.to256(), dst_reg); + if (src_mcv.isMemory()) try self.asmRegisterMemory( + .{ .v_ps, .cvtph2 }, + tmp_reg, + src_mcv.mem(.xword), + ) else try self.asmRegisterRegister( + .{ .v_ps, .cvtph2 }, + tmp_reg, + (if (src_mcv.isRegister()) + src_mcv.getReg().? + else + try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), + ); + try self.asmRegisterRegisterRegister( + switch (air_tag) { + .add => .{ .v_ps, .add }, + .sub => .{ .v_ps, .sub }, + .mul => .{ .v_ps, .mul }, + .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, + .max => .{ .v_ps, .max }, + .min => .{ .v_ps, .max }, + else => unreachable, + }, + dst_reg.to256(), + dst_reg.to256(), + tmp_reg, + ); + try self.asmRegisterRegisterImmediate( + .{ .v_, .cvtps2ph }, + dst_reg, + dst_reg.to256(), + Immediate.u(0b1_00), + ); + return dst_mcv; + }, + else => break :tag null, + } + }, 32 => switch (lhs_ty.vectorLen(mod)) { 1 => switch (air_tag) { .add => if (self.hasFeature(.avx)) .{ .v_ss, .add } else .{ ._ss, .add }, diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index 27a89f3d21..ab9859e08d 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -19,6 +19,99 @@ fn epsForType(comptime T: type) T { }; } +test "add f16" { + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testAdd(f16); + try comptime testAdd(f16); +} + +test "add f32/f64" { + try testAdd(f32); + try comptime testAdd(f32); + try testAdd(f64); + try comptime testAdd(f64); +} + +test "add f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testAdd(f80); + try comptime testAdd(f80); + try testAdd(f128); + try comptime testAdd(f128); + try testAdd(c_longdouble); + try comptime testAdd(c_longdouble); +} + +fn testAdd(comptime T: type) !void { + var one_point_two_five: T = 1.25; + var two_point_seven_five: T = 2.75; + try expect(one_point_two_five + two_point_seven_five == 4); +} + +test "sub f16" { + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testSub(f16); + try comptime testSub(f16); +} + +test "sub f32/f64" { + try testSub(f32); + try comptime testSub(f32); + try testSub(f64); + try comptime testSub(f64); +} + +test "sub f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testSub(f80); + try comptime testSub(f80); + try testSub(f128); + try comptime testSub(f128); + try testSub(c_longdouble); + try comptime testSub(c_longdouble); +} + +fn testSub(comptime T: type) !void { + var one_point_two_five: T = 1.25; + var two_point_seven_five: T = 2.75; + try expect(one_point_two_five - two_point_seven_five == -1.5); +} + +test "mul f16" { + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; + + try testMul(f16); + try comptime testMul(f16); +} + +test "mul f32/f64" { + try testMul(f32); + try comptime testMul(f32); + try testMul(f64); + try comptime testMul(f64); +} + +test "mul f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + try testMul(f80); + try comptime testMul(f80); + try testMul(f128); + try comptime testMul(f128); + try testMul(c_longdouble); + try comptime testMul(c_longdouble); +} + +fn testMul(comptime T: type) !void { + var one_point_two_five: T = 1.25; + var two_point_seven_five: T = 2.75; + try expect(one_point_two_five * two_point_seven_five == 3.4375); +} + test "cmp f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; @@ -216,7 +309,7 @@ test "more @sqrt f16 tests" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; // TODO these are not all passing at comptime try expect(@sqrt(@as(f16, 0.0)) == 0.0); @@ -269,7 +362,6 @@ test "@sin f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testSin(f16); try comptime testSin(f16); @@ -339,7 +431,6 @@ test "@cos f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testCos(f16); try comptime testCos(f16); @@ -409,7 +500,6 @@ test "@tan f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testTan(f16); try comptime testTan(f16); @@ -479,7 +569,6 @@ test "@exp f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testExp(f16); try comptime testExp(f16); @@ -549,7 +638,6 @@ test "@exp2 f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testExp2(f16); try comptime testExp2(f16); @@ -619,7 +707,6 @@ test "@log f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testLog(f16); try comptime testLog(f16); @@ -687,7 +774,6 @@ test "@log2 f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testLog2(f16); try comptime testLog2(f16); @@ -761,7 +847,6 @@ test "@log10 f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; try testLog10(f16); try comptime testLog10(f16); @@ -829,7 +914,7 @@ test "@abs f16" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; try testFabs(f16); try comptime testFabs(f16); @@ -1186,7 +1271,7 @@ test "neg f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; if (builtin.os.tag == .freebsd) { // TODO file issue to track this failure diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 801aa2aeaa..91ba47e329 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -7,8 +7,6 @@ const maxInt = std.math.maxInt; const minInt = std.math.minInt; const mem = std.mem; const math = std.math; -const no_x86_64_hardware_f16_support = builtin.zig_backend == .stage2_x86_64 and - !std.Target.x86.featureSetHas(builtin.cpu.features, .f16c); test "assignment operators" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1444,7 +1442,6 @@ test "@round f16" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; // TODO try testRound(f16, 12.0); try comptime testRound(f16, 12.0); From da335f0ee4dfe5a736919acc5ab20777838b42c7 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 30 Sep 2023 22:59:39 -0400 Subject: [PATCH 7/7] x86_64: implement float `@sqrt` builtin --- src/arch/x86_64/CodeGen.zig | 18 +++- test/behavior/floatop.zig | 163 +++++++++++++++++------------------- 2 files changed, 93 insertions(+), 88 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index c8859a7164..adb3ef68b4 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5359,9 +5359,21 @@ fn airSqrt(self: *Self, inst: Air.Inst.Index) !void { else => unreachable, }, else => unreachable, - }) orelse return self.fail("TODO implement airSqrt for {}", .{ - ty.fmt(mod), - }); + }) orelse { + if (ty.zigTypeTag(mod) != .Float) return self.fail("TODO implement airSqrt for {}", .{ + ty.fmt(mod), + }); + + var callee: ["__sqrt?".len]u8 = undefined; + break :result try self.genCall(.{ .lib = .{ + .return_type = ty.toIntern(), + .param_types = &.{ty.toIntern()}, + .callee = std.fmt.bufPrint(&callee, "{s}sqrt{s}", .{ + floatLibcAbiPrefix(ty), + floatLibcAbiSuffix(ty), + }) catch unreachable, + } }, &.{ty}, &.{src_mcv}); + }; switch (mir_tag[0]) { .v_ss, .v_sd => if (src_mcv.isMemory()) try self.asmRegisterRegisterMemory( mir_tag, diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index ab9859e08d..ab32a3b03b 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -2,12 +2,6 @@ const std = @import("std"); const builtin = @import("builtin"); const expect = std.testing.expect; const math = std.math; -const has_f80_rt = switch (builtin.cpu.arch) { - .x86_64, .x86 => true, - else => false, -}; -const no_x86_64_hardware_f16_support = builtin.zig_backend == .stage2_x86_64 and - !std.Target.x86.featureSetHas(builtin.cpu.features, .f16c); const epsilon_16 = 0.002; const epsilon = 0.000001; @@ -247,42 +241,93 @@ test "negative f128 intFromFloat at compile-time" { try expect(@as(i64, -2) == b); } -test "@sqrt" { +test "@sqrt f16" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - try testSqrt(); - try comptime testSqrt(); + try testSqrt(f16); + try comptime testSqrt(f16); } -fn testSqrt() !void { - try expect(@sqrt(@as(f16, 4)) == 2); - try expect(@sqrt(@as(f32, 9)) == 3); - try expect(@sqrt(@as(f64, 25)) == 5); - try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), 1.0488088481701516, epsilon)); - try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.0)), 1.4142135623730950, epsilon)); +test "@sqrt f32/f64" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - if (false) { - if (has_f80_rt) { - // TODO https://github.com/ziglang/zig/issues/10875 - if (builtin.os.tag != .freebsd) { - var a: f80 = 25; - try expect(@sqrt(a) == 5); - } - } - { - const a: comptime_float = 25.0; - try expect(@sqrt(a) == 5.0); - } - // TODO test f128, and c_longdouble - // https://github.com/ziglang/zig/issues/4026 - //{ - // var a: f128 = 49; - //try expect(@sqrt(a) == 7); - //} + try testSqrt(f32); + try comptime testSqrt(f32); + try testSqrt(f64); + try comptime testSqrt(f64); +} + +test "@sqrt f80/f128/c_longdouble" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + if (builtin.os.tag == .freebsd) { + // TODO https://github.com/ziglang/zig/issues/10875 + return error.SkipZigTest; } + + try testSqrt(f80); + try comptime testSqrt(f80); + try testSqrt(f128); + try comptime testSqrt(f128); + try testSqrt(c_longdouble); + try comptime testSqrt(c_longdouble); +} + +fn testSqrt(comptime T: type) !void { + const eps = epsForType(T); + var four: T = 4.0; + try expect(@sqrt(four) == 2.0); + var nine: T = 9.0; + try expect(@sqrt(nine) == 3.0); + var twenty_five: T = 25.0; + try expect(@sqrt(twenty_five) == 5.0); + var sixty_four: T = 64.0; + try expect(@sqrt(sixty_four) == 8.0); + var one_point_one: T = 1.1; + + try expect(math.approxEqAbs(T, @sqrt(one_point_one), 1.0488088481701516, eps)); + var two: T = 2.0; + try expect(math.approxEqAbs(T, @sqrt(two), 1.4142135623730950, eps)); + var three_point_six: T = 3.6; + try expect(math.approxEqAbs(T, @sqrt(three_point_six), 1.8973665961010276, eps)); + var sixty_four_point_one: T = 64.1; + try expect(math.approxEqAbs(T, @sqrt(sixty_four_point_one), 8.00624756049923802, eps)); + var twelve: T = 12.0; + try expect(math.approxEqAbs(T, @sqrt(twelve), 3.46410161513775459, eps)); + var thirteen: T = 13.0; + try expect(math.approxEqAbs(T, @sqrt(thirteen), 3.60555127546398929, eps)); + var fourteen: T = 14.0; + try expect(math.approxEqAbs(T, @sqrt(fourteen), 3.74165738677394139, eps)); + var a: T = 7.539840; + try expect(math.approxEqAbs(T, @sqrt(a), 2.74587690911300684, eps)); + var b: T = 19.230934; + try expect(math.approxEqAbs(T, @sqrt(b), 4.38530888307767894, eps)); + var c: T = 8942.230469; + try expect(math.approxEqAbs(T, @sqrt(c), 94.5633674791671111, eps)); + + // special cases + var inf: T = math.inf(T); + try expect(math.isPositiveInf(@sqrt(inf))); + var zero: T = 0.0; + try expect(@sqrt(zero) == 0.0); + var neg_zero: T = -0.0; + try expect(@sqrt(neg_zero) == 0.0); + var neg_one: T = -1.0; + try expect(math.isNan(@sqrt(neg_one))); + var nan: T = math.nan(T); + try expect(math.isNan(@sqrt(nan))); } test "@sqrt with vectors" { @@ -304,58 +349,6 @@ fn testSqrtWithVectors() !void { try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon)); } -test "more @sqrt f16 tests" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest; - - // TODO these are not all passing at comptime - try expect(@sqrt(@as(f16, 0.0)) == 0.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 2.0)), 1.414214, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 3.6)), 1.897367, epsilon)); - try expect(@sqrt(@as(f16, 4.0)) == 2.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 7.539840)), 2.745877, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 19.230934)), 4.385309, epsilon)); - try expect(@sqrt(@as(f16, 64.0)) == 8.0); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 64.1)), 8.006248, epsilon)); - try expect(math.approxEqAbs(f16, @sqrt(@as(f16, 8942.230469)), 94.563370, epsilon)); - - // special cases - try expect(math.isPositiveInf(@sqrt(@as(f16, math.inf(f16))))); - try expect(@sqrt(@as(f16, 0.0)) == 0.0); - try expect(@sqrt(@as(f16, -0.0)) == -0.0); - try expect(math.isNan(@sqrt(@as(f16, -1.0)))); - try expect(math.isNan(@sqrt(@as(f16, math.nan(f16))))); -} - -test "another, possibly redundant @sqrt test" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; - if (no_x86_64_hardware_f16_support) return error.SkipZigTest; - - try testSqrtLegacy(f64, 12.0); - try comptime testSqrtLegacy(f64, 12.0); - try testSqrtLegacy(f32, 13.0); - try comptime testSqrtLegacy(f32, 13.0); - try testSqrtLegacy(f16, 13.0); - try comptime testSqrtLegacy(f16, 13.0); - - // TODO: make this pass - if (false) { - const x = 14.0; - const y = x * x; - const z = @sqrt(y); - try comptime expect(z == x); - } -} - -fn testSqrtLegacy(comptime T: type, x: T) !void { - try expect(@sqrt(x * x) == x); -} - test "@sin f16" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO