authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-01-21 15:26:43+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-02-04 22:22:43+02:00
log9bbd3ab257137c97f695d187436e14c622f877c8
tree116a0a129e75705a120678c6d5420f4e67594f1c
parent72cef17b1a23c4704b3931540b7f10f4297870b9

compiler-rt: add comparison functions for f80


3 files changed, 140 insertions(+), 1 deletions(-)

lib/std/special/compiler_rt.zig+12
......@@ -54,6 +54,8 @@ comptime {
5454 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });
5555 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;
5656 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });
57 const __lexf2 = @import("compiler_rt/compareXf2.zig").__lexf2;
58 @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage });
5759
5860 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;
5961 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });
......@@ -61,26 +63,36 @@ comptime {
6163 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });
6264 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;
6365 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });
66 const __gexf2 = @import("compiler_rt/compareXf2.zig").__gexf2;
67 @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage });
6468
6569 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;
6670 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });
6771 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;
6872 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });
73 const __eqxf2 = @import("compiler_rt/compareXf2.zig").__eqxf2;
74 @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage });
6975
7076 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;
7177 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });
7278 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;
7379 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });
80 const __ltxf2 = @import("compiler_rt/compareXf2.zig").__ltxf2;
81 @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage });
7482
7583 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;
7684 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });
7785 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;
7886 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });
87 const __nexf2 = @import("compiler_rt/compareXf2.zig").__nexf2;
88 @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage });
7989
8090 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;
8191 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });
8292 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;
8393 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });
94 const __gtxf2 = @import("compiler_rt/compareXf2.zig").__gtxf2;
95 @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage });
8496
8597 if (!is_test) {
8698 @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage });
lib/std/special/compiler_rt/compareXf2.zig+67
......@@ -144,6 +144,73 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {
144144 return __gedf2(a, b);
145145}
146146
147// Comparison between f80
148
149pub inline fn cmp_f80(comptime RT: type, a: f80, b: f80) RT {
150 const a_rep = @ptrCast(*const std.math.F80Repr, &a).*;
151 const b_rep = @ptrCast(*const std.math.F80Repr, &b).*;
152 const sig_bits = std.math.floatMantissaBits(f80);
153 const int_bit = 0x8000000000000000;
154 const sign_bit = 0x8000;
155 const special_exp = 0x7FFF;
156
157 // If either a or b is NaN, they are unordered.
158 if ((a_rep.exp & special_exp == special_exp and a_rep.fraction ^ int_bit != 0) or
159 (b_rep.exp & special_exp == special_exp and b_rep.fraction ^ int_bit != 0))
160 return RT.Unordered;
161
162 // If a and b are both zeros, they are equal.
163 if ((a_rep.fraction | b_rep.fraction) | ((a_rep.exp | b_rep.exp) & special_exp) == 0)
164 return .Equal;
165
166 if (@boolToInt(a_rep.exp == b_rep.exp) & @boolToInt(a_rep.fraction == b_rep.fraction) != 0) {
167 return .Equal;
168 } else if (a_rep.exp & sign_bit != b_rep.exp & sign_bit) {
169 // signs are different
170 if (@bitCast(i16, a_rep.exp) < @bitCast(i16, b_rep.exp)) {
171 return .Less;
172 } else {
173 return .Greater;
174 }
175 } else {
176 const a_fraction = a_rep.fraction | (@as(u80, a_rep.exp) << sig_bits);
177 const b_fraction = b_rep.fraction | (@as(u80, b_rep.exp) << sig_bits);
178 if (a_fraction < b_fraction) {
179 return .Less;
180 } else {
181 return .Greater;
182 }
183 }
184}
185
186pub fn __lexf2(a: f80, b: f80) callconv(.C) i32 {
187 @setRuntimeSafety(builtin.is_test);
188 const float = cmp_f80(LE, a, b);
189 return @bitCast(i32, float);
190}
191
192pub fn __gexf2(a: f80, b: f80) callconv(.C) i32 {
193 @setRuntimeSafety(builtin.is_test);
194 const float = cmp_f80(GE, a, b);
195 return @bitCast(i32, float);
196}
197
198pub fn __eqxf2(a: f80, b: f80) callconv(.C) i32 {
199 return __lexf2(a, b);
200}
201
202pub fn __ltxf2(a: f80, b: f80) callconv(.C) i32 {
203 return __lexf2(a, b);
204}
205
206pub fn __nexf2(a: f80, b: f80) callconv(.C) i32 {
207 return __lexf2(a, b);
208}
209
210pub fn __gtxf2(a: f80, b: f80) callconv(.C) i32 {
211 return __gexf2(a, b);
212}
213
147214// Comparison between f128
148215
149216pub fn __letf2(a: f128, b: f128) callconv(.C) i32 {
src/stage1/codegen.cpp+61-1
......@@ -3234,6 +3234,49 @@ static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int p
32343234 return LLVMAddFunction(g->module, name, fn_type);
32353235}
32363236
3237enum SoftF80Icmp {
3238 NONE,
3239 EQ_ZERO,
3240 NE_ZERO,
3241 LE_ZERO,
3242 EQ_NEG,
3243 GE_ZERO,
3244 EQ_ONE,
3245};
3246
3247static LLVMValueRef add_f80_icmp(CodeGen *g, LLVMValueRef val, SoftF80Icmp kind) {
3248 switch (kind) {
3249 case NONE:
3250 return val;
3251 case EQ_ZERO: {
3252 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true);
3253 return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, "");
3254 }
3255 case NE_ZERO: {
3256 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true);
3257 return LLVMBuildICmp(g->builder, LLVMIntNE, val, zero, "");
3258 }
3259 case LE_ZERO: {
3260 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true);
3261 return LLVMBuildICmp(g->builder, LLVMIntSLE, val, zero, "");
3262 }
3263 case EQ_NEG: {
3264 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, -1, true);
3265 return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, "");
3266 }
3267 case GE_ZERO: {
3268 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 0, true);
3269 return LLVMBuildICmp(g->builder, LLVMIntSGE, val, zero, "");
3270 }
3271 case EQ_ONE: {
3272 LLVMValueRef zero = LLVMConstInt(g->builtin_types.entry_i32->llvm_type, 1, true);
3273 return LLVMBuildICmp(g->builder, LLVMIntEQ, val, zero, "");
3274 }
3275 default:
3276 zig_unreachable();
3277 }
3278}
3279
32373280static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
32383281 Stage1AirInstBinOp *bin_op_instruction)
32393282{
......@@ -3249,6 +3292,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
32493292 LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type;
32503293 int param_count = 2;
32513294 const char *func_name;
3295 SoftF80Icmp res_icmp = NONE;
32523296 switch (op_id) {
32533297 case IrBinOpInvalid:
32543298 case IrBinOpArrayCat:
......@@ -3274,20 +3318,32 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
32743318 case IrBinOpCmpEq:
32753319 return_type = g->builtin_types.entry_i32->llvm_type;
32763320 func_name = "__eqxf2";
3321 res_icmp = EQ_ZERO;
32773322 break;
32783323 case IrBinOpCmpNotEq:
32793324 return_type = g->builtin_types.entry_i32->llvm_type;
32803325 func_name = "__nexf2";
3326 res_icmp = NE_ZERO;
32813327 break;
32823328 case IrBinOpCmpLessOrEq:
3329 return_type = g->builtin_types.entry_i32->llvm_type;
3330 func_name = "__lexf2";
3331 res_icmp = LE_ZERO;
3332 break;
32833333 case IrBinOpCmpLessThan:
32843334 return_type = g->builtin_types.entry_i32->llvm_type;
32853335 func_name = "__lexf2";
3336 res_icmp = EQ_NEG;
32863337 break;
32873338 case IrBinOpCmpGreaterOrEq:
3339 return_type = g->builtin_types.entry_i32->llvm_type;
3340 func_name = "__gexf2";
3341 res_icmp = GE_ZERO;
3342 break;
32883343 case IrBinOpCmpGreaterThan:
32893344 return_type = g->builtin_types.entry_i32->llvm_type;
32903345 func_name = "__gexf2";
3346 res_icmp = EQ_ONE;
32913347 break;
32923348 case IrBinOpMaximum:
32933349 func_name = "__fmaxx";
......@@ -3338,8 +3394,11 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
33383394 if (vector_len == 0) {
33393395 LLVMValueRef params[2] = {op1_value, op2_value};
33403396 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3397 result = add_f80_icmp(g, result, res_icmp);
33413398 } else {
3342 result = build_alloca(g, op1->value->type, "", 0);
3399 ZigType *alloca_ty = op1->value->type;
3400 if (res_icmp != NONE) alloca_ty = get_vector_type(g, vector_len, g->builtin_types.entry_bool);
3401 result = build_alloca(g, alloca_ty, "", 0);
33433402 }
33443403
33453404 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,
33503409 LLVMBuildExtractElement(g->builder, op2_value, index_value, ""),
33513410 };
33523411 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3412 call_result = add_f80_icmp(g, call_result, res_icmp);
33533413 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
33543414 call_result, index_value, "");
33553415 }