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 {...@@ -54,6 +54,8 @@ comptime {
54 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });54 @export(__ledf2, .{ .name = "__ledf2", .linkage = linkage });
55 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;55 const __letf2 = @import("compiler_rt/compareXf2.zig").__letf2;
56 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });56 @export(__letf2, .{ .name = "__letf2", .linkage = linkage });
57 const __lexf2 = @import("compiler_rt/compareXf2.zig").__lexf2;
58 @export(__lexf2, .{ .name = "__lexf2", .linkage = linkage });
5759
58 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;60 const __gesf2 = @import("compiler_rt/compareXf2.zig").__gesf2;
59 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });61 @export(__gesf2, .{ .name = "__gesf2", .linkage = linkage });
...@@ -61,26 +63,36 @@ comptime {...@@ -61,26 +63,36 @@ comptime {
61 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });63 @export(__gedf2, .{ .name = "__gedf2", .linkage = linkage });
62 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;64 const __getf2 = @import("compiler_rt/compareXf2.zig").__getf2;
63 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });65 @export(__getf2, .{ .name = "__getf2", .linkage = linkage });
66 const __gexf2 = @import("compiler_rt/compareXf2.zig").__gexf2;
67 @export(__gexf2, .{ .name = "__gexf2", .linkage = linkage });
6468
65 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;69 const __eqsf2 = @import("compiler_rt/compareXf2.zig").__eqsf2;
66 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });70 @export(__eqsf2, .{ .name = "__eqsf2", .linkage = linkage });
67 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;71 const __eqdf2 = @import("compiler_rt/compareXf2.zig").__eqdf2;
68 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });72 @export(__eqdf2, .{ .name = "__eqdf2", .linkage = linkage });
73 const __eqxf2 = @import("compiler_rt/compareXf2.zig").__eqxf2;
74 @export(__eqxf2, .{ .name = "__eqxf2", .linkage = linkage });
6975
70 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;76 const __ltsf2 = @import("compiler_rt/compareXf2.zig").__ltsf2;
71 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });77 @export(__ltsf2, .{ .name = "__ltsf2", .linkage = linkage });
72 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;78 const __ltdf2 = @import("compiler_rt/compareXf2.zig").__ltdf2;
73 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });79 @export(__ltdf2, .{ .name = "__ltdf2", .linkage = linkage });
80 const __ltxf2 = @import("compiler_rt/compareXf2.zig").__ltxf2;
81 @export(__ltxf2, .{ .name = "__ltxf2", .linkage = linkage });
7482
75 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;83 const __nesf2 = @import("compiler_rt/compareXf2.zig").__nesf2;
76 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });84 @export(__nesf2, .{ .name = "__nesf2", .linkage = linkage });
77 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;85 const __nedf2 = @import("compiler_rt/compareXf2.zig").__nedf2;
78 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });86 @export(__nedf2, .{ .name = "__nedf2", .linkage = linkage });
87 const __nexf2 = @import("compiler_rt/compareXf2.zig").__nexf2;
88 @export(__nexf2, .{ .name = "__nexf2", .linkage = linkage });
7989
80 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;90 const __gtsf2 = @import("compiler_rt/compareXf2.zig").__gtsf2;
81 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });91 @export(__gtsf2, .{ .name = "__gtsf2", .linkage = linkage });
82 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;92 const __gtdf2 = @import("compiler_rt/compareXf2.zig").__gtdf2;
83 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });93 @export(__gtdf2, .{ .name = "__gtdf2", .linkage = linkage });
94 const __gtxf2 = @import("compiler_rt/compareXf2.zig").__gtxf2;
95 @export(__gtxf2, .{ .name = "__gtxf2", .linkage = linkage });
8496
85 if (!is_test) {97 if (!is_test) {
86 @export(__lesf2, .{ .name = "__cmpsf2", .linkage = linkage });98 @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 {...@@ -144,6 +144,73 @@ pub fn __gtdf2(a: f64, b: f64) callconv(.C) i32 {
144 return __gedf2(a, b);144 return __gedf2(a, b);
145}145}
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
147// Comparison between f128214// Comparison between f128
148215
149pub fn __letf2(a: f128, b: f128) callconv(.C) i32 {216pub 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...@@ -3234,6 +3234,49 @@ static LLVMValueRef get_soft_f80_bin_op_func(CodeGen *g, const char *name, int p
3234 return LLVMAddFunction(g->module, name, fn_type);3234 return LLVMAddFunction(g->module, name, fn_type);
3235}3235}
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
3237static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,3280static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
3238 Stage1AirInstBinOp *bin_op_instruction)3281 Stage1AirInstBinOp *bin_op_instruction)
3239{3282{
...@@ -3249,6 +3292,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3249,6 +3292,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
3249 LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type;3292 LLVMTypeRef return_type = g->builtin_types.entry_f80->llvm_type;
3250 int param_count = 2;3293 int param_count = 2;
3251 const char *func_name;3294 const char *func_name;
3295 SoftF80Icmp res_icmp = NONE;
3252 switch (op_id) {3296 switch (op_id) {
3253 case IrBinOpInvalid:3297 case IrBinOpInvalid:
3254 case IrBinOpArrayCat:3298 case IrBinOpArrayCat:
...@@ -3274,20 +3318,32 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3274,20 +3318,32 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
3274 case IrBinOpCmpEq:3318 case IrBinOpCmpEq:
3275 return_type = g->builtin_types.entry_i32->llvm_type;3319 return_type = g->builtin_types.entry_i32->llvm_type;
3276 func_name = "__eqxf2";3320 func_name = "__eqxf2";
3321 res_icmp = EQ_ZERO;
3277 break;3322 break;
3278 case IrBinOpCmpNotEq:3323 case IrBinOpCmpNotEq:
3279 return_type = g->builtin_types.entry_i32->llvm_type;3324 return_type = g->builtin_types.entry_i32->llvm_type;
3280 func_name = "__nexf2";3325 func_name = "__nexf2";
3326 res_icmp = NE_ZERO;
3281 break;3327 break;
3282 case IrBinOpCmpLessOrEq:3328 case IrBinOpCmpLessOrEq:
3329 return_type = g->builtin_types.entry_i32->llvm_type;
3330 func_name = "__lexf2";
3331 res_icmp = LE_ZERO;
3332 break;
3283 case IrBinOpCmpLessThan:3333 case IrBinOpCmpLessThan:
3284 return_type = g->builtin_types.entry_i32->llvm_type;3334 return_type = g->builtin_types.entry_i32->llvm_type;
3285 func_name = "__lexf2";3335 func_name = "__lexf2";
3336 res_icmp = EQ_NEG;
3286 break;3337 break;
3287 case IrBinOpCmpGreaterOrEq:3338 case IrBinOpCmpGreaterOrEq:
3339 return_type = g->builtin_types.entry_i32->llvm_type;
3340 func_name = "__gexf2";
3341 res_icmp = GE_ZERO;
3342 break;
3288 case IrBinOpCmpGreaterThan:3343 case IrBinOpCmpGreaterThan:
3289 return_type = g->builtin_types.entry_i32->llvm_type;3344 return_type = g->builtin_types.entry_i32->llvm_type;
3290 func_name = "__gexf2";3345 func_name = "__gexf2";
3346 res_icmp = EQ_ONE;
3291 break;3347 break;
3292 case IrBinOpMaximum:3348 case IrBinOpMaximum:
3293 func_name = "__fmaxx";3349 func_name = "__fmaxx";
...@@ -3338,8 +3394,11 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3338,8 +3394,11 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
3338 if (vector_len == 0) {3394 if (vector_len == 0) {
3339 LLVMValueRef params[2] = {op1_value, op2_value};3395 LLVMValueRef params[2] = {op1_value, op2_value};
3340 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");3396 result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3397 result = add_f80_icmp(g, result, res_icmp);
3341 } else {3398 } 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);
3343 }3402 }
33443403
3345 LLVMTypeRef usize_ref = g->builtin_types.entry_usize->llvm_type;3404 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,...@@ -3350,6 +3409,7 @@ static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable,
3350 LLVMBuildExtractElement(g->builder, op2_value, index_value, ""),3409 LLVMBuildExtractElement(g->builder, op2_value, index_value, ""),
3351 };3410 };
3352 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");3411 LLVMValueRef call_result = LLVMBuildCall(g->builder, func_ref, params, param_count, "");
3412 call_result = add_f80_icmp(g, call_result, res_icmp);
3353 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),3413 LLVMBuildInsertElement(g->builder, LLVMBuildLoad(g->builder, result, ""),
3354 call_result, index_value, "");3414 call_result, index_value, "");
3355 }3415 }