From 9bbd3ab257137c97f695d187436e14c622f877c8 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 21 Jan 2022 15:26:43 +0200 Subject: [PATCH] compiler-rt: add comparison functions for f80 --- lib/std/special/compiler_rt.zig | 12 ++++ lib/std/special/compiler_rt/compareXf2.zig | 67 ++++++++++++++++++++++ src/stage1/codegen.cpp | 62 +++++++++++++++++++- 3 files changed, 140 insertions(+), 1 deletion(-) diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 555a7a49d3..d83e94be8f 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -54,6 +54,8 @@ comptime { @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage }); const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2; @export(__letf2, .{ .name = "__letf2", .linkage = linkage }); + const __lexf2 = @import("compiler_rt/compareXf2.zig").__lexf2; + @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage }); const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2; @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage }); @@ -61,26 +63,36 @@ comptime { @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage }); const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2; @export(__getf2, .{ .name = "__getf2", .linkage = linkage }); + const __gexf2 = @import("compiler_rt/compareXf2.zig").__gexf2; + @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage }); const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2; @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage }); const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2; @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage }); + const __eqxf2 = @import("compiler_rt/compareXf2.zig").__eqxf2; + @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage }); const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2; @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage }); const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2; @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage }); + const __ltxf2 = @import("compiler_rt/compareXf2.zig").__ltxf2; + @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage }); const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2; @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage }); const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2; @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage }); + const __nexf2 = @import("compiler_rt/compareXf2.zig").__nexf2; + @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage }); const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2; @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage }); const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2; @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage }); + const __gtxf2 = @import("compiler_rt/compareXf2.zig").__gtxf2; + @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage }); if (!is_test) { @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/compareXf2.zig b/lib/std/special/compiler_rt/compareXf2.zig index 9f3750094e..36f6f5f1c1 100644 --- a/lib/std/special/compiler_rt/compareXf2.zig +++ b/lib/std/special/compiler_rt/compareXf2.zig @@ -144,6 +144,73 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 { return __gedf2(a, b); } +// Comparison between f80 + +pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT { + const a_rep = @ptrCast(*const std.math.F80Repr, &a).*; + const b_rep = @ptrCast(*const std.math.F80Repr, &b).*; + const sig_bits = std.math.floatMantissaBits(f80); + const int_bit = 0x8000000000000000; + const sign_bit = 0x8000; + const special_exp = 0x7FFF; + + // If either a or b is NaN, they are unordered. + if ((a_rep.exp & special_exp == special_exp and a_rep.fraction ^ int_bit != 0) or + (b_rep.exp & special_exp == special_exp and b_rep.fraction ^ int_bit != 0)) + return RT.Unordered; + + // If a and b are both zeros, they are equal. + if ((a_rep.fraction | b_rep.fraction) | ((a_rep.exp | b_rep.exp) & special_exp) == 0) + return .Equal; + + if (@boolToInt(a_rep.exp == b_rep.exp) & @boolToInt(a_rep.fraction == b_rep.fraction) != 0) { + return .Equal; + } else if (a_rep.exp & sign_bit != b_rep.exp & sign_bit) { + // signs are different + if (@bitCast(i16, a_rep.exp) < @bitCast(i16, b_rep.exp)) { + return .Less; + } else { + return .Greater; + } + } else { + const a_fraction = a_rep.fraction | (@as(u80, a_rep.exp) << sig_bits); + const b_fraction = b_rep.fraction | (@as(u80, b_rep.exp) << sig_bits); + if (a_fraction < b_fraction) { + return .Less; + } else { + return .Greater; + } + } +} + +pub fn __lexf2(a: f80, b: f80) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + const float = cmp_f80(LE, a, b); + return @bitCast(i32, float); +} + +pub fn __gexf2(a: f80, b: f80) callconv(.C) i32 { + @setRuntimeSafety(builtin.is_test); + const float = cmp_f80(GE, a, b); + return @bitCast(i32, float); +} + +pub fn __eqxf2(a: f80, b: f80) callconv(.C) i32 { + return __lexf2(a, b); +} + +pub fn __ltxf2(a: f80, b: f80) callconv(.C) i32 { + return __lexf2(a, b); +} + +pub fn __nexf2(a: f80, b: f80) callconv(.C) i32 { + return __lexf2(a, b); +} + +pub fn __gtxf2(a: f80, b: f80) callconv(.C) i32 { + return __gexf2(a, b); +} + // Comparison between f128 pub fn __letf2(a: f128, b: f128) callconv(.C) i32 { diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index b97f009d62..a62daf0d63 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -3234,6 +3234,49 @@ static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int p return LLVMAddFunction(g->module, name, fn_type); } +enum SoftF80Icmp { + NONE, + EQ_ZERO, + NE_ZERO, + LE_ZERO, + EQ_NEG, + GE_ZERO, + EQ_ONE, +}; + +static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) { + switch (kind) { + case NONE: + return val; + case EQ_ZERO: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true); + return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, ""); + } + case NE_ZERO: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true); + return LLVMBuildICmp(g->builder, LLVMIntNE, val, zero, ""); + } + case LE_ZERO: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true); + return LLVMBuildICmp(g->builder, LLVMIntSLE, val, zero, ""); + } + case EQ_NEG: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, -1, true); + return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, ""); + } + case GE_ZERO: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true); + return LLVMBuildICmp(g->builder, LLVMIntSGE, val, zero, ""); + } + case EQ_ONE: { + LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 1, true); + return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, ""); + } + default: + zig_unreachable(); + } +} + static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, Stage1AirInstBinOp *bin_op_instruction) { @@ -3249,6 +3292,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type; int param_count = 2; const char *func_name; + SoftF80Icmp res_icmp = NONE; switch (op_id) { case IrBinOpInvalid: case IrBinOpArrayCat: @@ -3274,20 +3318,32 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, case IrBinOpCmpEq: return_type = g->builtin_types.entry_i32->llvm_type; func_name = "__eqxf2"; + res_icmp = EQ_ZERO; break; case IrBinOpCmpNotEq: return_type = g->builtin_types.entry_i32->llvm_type; func_name = "__nexf2"; + res_icmp = NE_ZERO; break; case IrBinOpCmpLessOrEq: + return_type = g->builtin_types.entry_i32->llvm_type; + func_name = "__lexf2"; + res_icmp = LE_ZERO; + break; case IrBinOpCmpLessThan: return_type = g->builtin_types.entry_i32->llvm_type; func_name = "__lexf2"; + res_icmp = EQ_NEG; break; case IrBinOpCmpGreaterOrEq: + return_type = g->builtin_types.entry_i32->llvm_type; + func_name = "__gexf2"; + res_icmp = GE_ZERO; + break; case IrBinOpCmpGreaterThan: return_type = g->builtin_types.entry_i32->llvm_type; func_name = "__gexf2"; + res_icmp = EQ_ONE; break; case IrBinOpMaximum: func_name = "__fmaxx"; @@ -3338,8 +3394,11 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, if (vector_len == 0) { LLVMValueRef params[2] = {op1_value, op2_value}; result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + result = add_f80_icmp(g, result, res_icmp); } else { - result = build_alloca(g, op1->value->type, "", 0); + ZigType *alloca_ty = op1->value->type; + if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool); + result = build_alloca(g, alloca_ty, "", 0); } LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type; @@ -3350,6 +3409,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, LLVMBuildExtractElement(g->builder, op2_value, index_value, ""), }; LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, ""); + call_result = add_f80_icmp(g, call_result, res_icmp); LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""), call_result, index_value, ""); }