| author | |
| committer | |
| log | 9bbd3ab257137c97f695d187436e14c622f877c8 |
| tree | 116a0a129e75705a120678c6d5420f4e67594f1c |
| parent | 72cef17b1a23c4704b3931540b7f10f4297870b9 |
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 }); | ||
| 57 | 59 | ||
| 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 }); | ||
| 64 | 68 | ||
| 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 }); | ||
| 69 | 75 | ||
| 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 }); | ||
| 74 | 82 | ||
| 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 }); | ||
| 79 | 89 | ||
| 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 }); | ||
| 84 | 96 | ||
| 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 | } |
| 146 | 146 | ||
| 147 | // Comparison between f80 | ||
| 148 | |||
| 149 | pub 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 | |||
| 186 | pub 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 | |||
| 192 | pub 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 | |||
| 198 | pub fn __eqxf2(a: f80, b: f80) callconv(.C) i32 { | ||
| 199 | return __lexf2(a, b); | ||
| 200 | } | ||
| 201 | |||
| 202 | pub fn __ltxf2(a: f80, b: f80) callconv(.C) i32 { | ||
| 203 | return __lexf2(a, b); | ||
| 204 | } | ||
| 205 | |||
| 206 | pub fn __nexf2(a: f80, b: f80) callconv(.C) i32 { | ||
| 207 | return __lexf2(a, b); | ||
| 208 | } | ||
| 209 | |||
| 210 | pub fn __gtxf2(a: f80, b: f80) callconv(.C) i32 { | ||
| 211 | return __gexf2(a, b); | ||
| 212 | } | ||
| 213 | |||
| 147 | // Comparison between f128 | 214 | // Comparison between f128 |
| 148 | 215 | ||
| 149 | pub fn __letf2(a: f128, b: f128) callconv(.C) i32 { | 216 | pub 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 | } |
| 3236 | 3236 | ||
| 3237 | enum SoftF80Icmp { | ||
| 3238 | NONE, | ||
| 3239 | EQ_ZERO, | ||
| 3240 | NE_ZERO, | ||
| 3241 | LE_ZERO, | ||
| 3242 | EQ_NEG, | ||
| 3243 | GE_ZERO, | ||
| 3244 | EQ_ONE, | ||
| 3245 | }; | ||
| 3246 | |||
| 3247 | static 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 | |||
| 3237 | static LLVMValueRef ir_render_soft_f80_bin_op(CodeGen *g, Stage1Air *executable, | 3280 | static 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 | } |
| 3344 | 3403 | ||
| 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 | } |