| author | |
| committer | |
| log | 71e014caecaa54fdd8a0516710d2d9597da41398 |
| tree | 4cfc85ed66ba9aec49e3f672a80c39b98e598177 |
| parent | ebde2ff899c16612c7ff58df61f3946be47c51c8 |
and expand @sqrt
This revealed that the accuracy of ln is not as good as the current algorithm in
musl and glibc, and should be ported again.
v2: actually include tests
v3: fix reversal of in and out arguments on f128M_sqrt()
add test for @sqrt on comptime_float
do not include @nearbyInt() until it works on all targets.11 files changed, 719 insertions(+), 131 deletions(-)
doc/langref.html.in+83-2| ... | ... | @@ -7354,10 +7354,91 @@ test "@setRuntimeSafety" { |
| 7354 | 7354 | <pre>{#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}</pre> |
| 7355 | 7355 | <p> |
| 7356 | 7356 | Performs the square root of a floating point number. Uses a dedicated hardware instruction |
| 7357 | when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO. | |
| 7357 | when available. Supports f16, f32, f64, and f128, as well as vectors. | |
| 7358 | 7358 | </p> |
| 7359 | {#header_close#} | |
| 7360 | {#header_open|@sin#} | |
| 7361 | <pre>{#syntax#}@sin(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7362 | <p> | |
| 7363 | Sine trigometric function on a floating point number. Uses a dedicated hardware instruction | |
| 7364 | when available. Currently supports f32 and f64. | |
| 7365 | </p> | |
| 7366 | {#header_close#} | |
| 7367 | {#header_open|@cos#} | |
| 7368 | <pre>{#syntax#}@cos(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7369 | <p> | |
| 7370 | Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction | |
| 7371 | when available. Currently supports f32 and f64. | |
| 7372 | </p> | |
| 7373 | {#header_close#} | |
| 7374 | {#header_open|@exp#} | |
| 7375 | <pre>{#syntax#}@exp(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7376 | <p> | |
| 7377 | Base-e exponential function on a floating point number. Uses a dedicated hardware instruction | |
| 7378 | when available. Currently supports f32 and f64. | |
| 7379 | </p> | |
| 7380 | {#header_close#} | |
| 7381 | {#header_open|@exp2#} | |
| 7382 | <pre>{#syntax#}@exp2(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7383 | <p> | |
| 7384 | Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction | |
| 7385 | when available. Currently supports f32 and f64. | |
| 7386 | </p> | |
| 7387 | {#header_close#} | |
| 7388 | {#header_open|@ln#} | |
| 7389 | <pre>{#syntax#}@ln(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7390 | <p> | |
| 7391 | Returns the natural logarithm of a floating point number. Uses a dedicated hardware instruction | |
| 7392 | when available. Currently supports f32 and f64. | |
| 7393 | </p> | |
| 7394 | {#header_close#} | |
| 7395 | {#header_open|@log2#} | |
| 7396 | <pre>{#syntax#}@log2(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7397 | <p> | |
| 7398 | Returns the logarithm to the base 2 of a floating point number. Uses a dedicated hardware instruction | |
| 7399 | when available. Currently supports f32 and f64. | |
| 7400 | </p> | |
| 7401 | {#header_close#} | |
| 7402 | {#header_open|@log10#} | |
| 7403 | <pre>{#syntax#}@log10(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7404 | <p> | |
| 7405 | Returns the logarithm to the base 10 of a floating point number. Uses a dedicated hardware instruction | |
| 7406 | when available. Currently supports f32 and f64. | |
| 7407 | </p> | |
| 7408 | {#header_close#} | |
| 7409 | {#header_open|@fabs#} | |
| 7410 | <pre>{#syntax#}@fabs(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7411 | <p> | |
| 7412 | Returns the absolute value of a floating point number. Uses a dedicated hardware instruction | |
| 7413 | when available. Currently supports f32 and f64. | |
| 7414 | </p> | |
| 7415 | {#header_close#} | |
| 7416 | {#header_open|@floor#} | |
| 7417 | <pre>{#syntax#}@floor(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7418 | <p> | |
| 7419 | Returns the largest integral value not greater than the given floating point number. Uses a dedicated hardware instruction | |
| 7420 | when available. Currently supports f32 and f64. | |
| 7421 | </p> | |
| 7422 | {#header_close#} | |
| 7423 | {#header_open|@ceil#} | |
| 7424 | <pre>{#syntax#}@ceil(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7425 | <p> | |
| 7426 | Returns the largest integral value not less than the given floating point number. Uses a dedicated hardware instruction | |
| 7427 | when available. Currently supports f32 and f64. | |
| 7428 | </p> | |
| 7429 | {#header_close#} | |
| 7430 | {#header_open|@trunc#} | |
| 7431 | <pre>{#syntax#}@trunc(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7432 | <p> | |
| 7433 | Rounds the given floating point number to an integer, towards zero. Uses a dedicated hardware instruction | |
| 7434 | when available. Currently supports f32 and f64. | |
| 7435 | </p> | |
| 7436 | {#header_close#} | |
| 7437 | {#header_open|@round#} | |
| 7438 | <pre>{#syntax#}@round(comptime T: type, value: T) T{#endsyntax#}</pre> | |
| 7359 | 7439 | <p> |
| 7360 | This is a low-level intrinsic. Most code can use {#syntax#}std.math.sqrt{#endsyntax#} instead. | |
| 7440 | Rounds the given floating point number to an integer, away from zero. Uses a dedicated hardware instruction | |
| 7441 | when available. Currently supports f32 and f64. | |
| 7361 | 7442 | </p> |
| 7362 | 7443 | {#header_close#} |
| 7363 | 7444 |
src/all_types.hpp+20-6| ... | ... | @@ -1434,6 +1434,19 @@ enum BuiltinFnId { |
| 1434 | 1434 | BuiltinFnIdRem, |
| 1435 | 1435 | BuiltinFnIdMod, |
| 1436 | 1436 | BuiltinFnIdSqrt, |
| 1437 | BuiltinFnIdSin, | |
| 1438 | BuiltinFnIdCos, | |
| 1439 | BuiltinFnIdExp, | |
| 1440 | BuiltinFnIdExp2, | |
| 1441 | BuiltinFnIdLn, | |
| 1442 | BuiltinFnIdLog2, | |
| 1443 | BuiltinFnIdLog10, | |
| 1444 | BuiltinFnIdFabs, | |
| 1445 | BuiltinFnIdFloor, | |
| 1446 | BuiltinFnIdCeil, | |
| 1447 | BuiltinFnIdTrunc, | |
| 1448 | BuiltinFnIdNearbyInt, | |
| 1449 | BuiltinFnIdRound, | |
| 1437 | 1450 | BuiltinFnIdTruncate, |
| 1438 | 1451 | BuiltinFnIdIntCast, |
| 1439 | 1452 | BuiltinFnIdFloatCast, |
| ... | ... | @@ -1556,9 +1569,7 @@ enum ZigLLVMFnId { |
| 1556 | 1569 | ZigLLVMFnIdPopCount, |
| 1557 | 1570 | ZigLLVMFnIdOverflowArithmetic, |
| 1558 | 1571 | ZigLLVMFnIdFMA, |
| 1559 | ZigLLVMFnIdFloor, | |
| 1560 | ZigLLVMFnIdCeil, | |
| 1561 | ZigLLVMFnIdSqrt, | |
| 1572 | ZigLLVMFnIdFloatOp, | |
| 1562 | 1573 | ZigLLVMFnIdBswap, |
| 1563 | 1574 | ZigLLVMFnIdBitReverse, |
| 1564 | 1575 | }; |
| ... | ... | @@ -1585,6 +1596,7 @@ struct ZigLLVMFnKey { |
| 1585 | 1596 | uint32_t bit_count; |
| 1586 | 1597 | } pop_count; |
| 1587 | 1598 | struct { |
| 1599 | BuiltinFnId op; | |
| 1588 | 1600 | uint32_t bit_count; |
| 1589 | 1601 | uint32_t vector_len; // 0 means not a vector |
| 1590 | 1602 | } floating; |
| ... | ... | @@ -2239,6 +2251,7 @@ enum IrInstructionId { |
| 2239 | 2251 | IrInstructionIdAlignOf, |
| 2240 | 2252 | IrInstructionIdOverflowOp, |
| 2241 | 2253 | IrInstructionIdMulAdd, |
| 2254 | IrInstructionIdFloatOp, | |
| 2242 | 2255 | IrInstructionIdTestErr, |
| 2243 | 2256 | IrInstructionIdUnwrapErrCode, |
| 2244 | 2257 | IrInstructionIdUnwrapErrPayload, |
| ... | ... | @@ -2300,7 +2313,6 @@ enum IrInstructionId { |
| 2300 | 2313 | IrInstructionIdAddImplicitReturnType, |
| 2301 | 2314 | IrInstructionIdMergeErrRetTraces, |
| 2302 | 2315 | IrInstructionIdMarkErrRetTracePtr, |
| 2303 | IrInstructionIdSqrt, | |
| 2304 | 2316 | IrInstructionIdErrSetCast, |
| 2305 | 2317 | IrInstructionIdToBytes, |
| 2306 | 2318 | IrInstructionIdFromBytes, |
| ... | ... | @@ -3474,11 +3486,13 @@ struct IrInstructionMarkErrRetTracePtr { |
| 3474 | 3486 | IrInstruction *err_ret_trace_ptr; |
| 3475 | 3487 | }; |
| 3476 | 3488 | |
| 3477 | struct IrInstructionSqrt { | |
| 3489 | // For float ops which take a single argument | |
| 3490 | struct IrInstructionFloatOp { | |
| 3478 | 3491 | IrInstruction base; |
| 3479 | 3492 | |
| 3493 | BuiltinFnId op; | |
| 3480 | 3494 | IrInstruction *type; |
| 3481 | IrInstruction *op; | |
| 3495 | IrInstruction *op1; | |
| 3482 | 3496 | }; |
| 3483 | 3497 | |
| 3484 | 3498 | struct IrInstructionCheckRuntimeScope { |
src/analyze.cpp+8-7| ... | ... | @@ -5736,9 +5736,10 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { |
| 5736 | 5736 | return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; |
| 5737 | 5737 | case ZigLLVMFnIdPopCount: |
| 5738 | 5738 | return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049; |
| 5739 | case ZigLLVMFnIdFloor: | |
| 5740 | case ZigLLVMFnIdCeil: | |
| 5741 | case ZigLLVMFnIdSqrt: | |
| 5739 | case ZigLLVMFnIdFloatOp: | |
| 5740 | return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) + | |
| 5741 | (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025) + | |
| 5742 | (uint32_t)(x.data.floating.op) * (uint32_t)43789879; | |
| 5742 | 5743 | case ZigLLVMFnIdFMA: |
| 5743 | 5744 | return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) + |
| 5744 | 5745 | (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025); |
| ... | ... | @@ -5769,10 +5770,10 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { |
| 5769 | 5770 | return a.data.bswap.bit_count == b.data.bswap.bit_count; |
| 5770 | 5771 | case ZigLLVMFnIdBitReverse: |
| 5771 | 5772 | return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count; |
| 5772 | case ZigLLVMFnIdFloor: | |
| 5773 | case ZigLLVMFnIdCeil: | |
| 5774 | case ZigLLVMFnIdSqrt: | |
| 5775 | return a.data.floating.bit_count == b.data.floating.bit_count; | |
| 5773 | case ZigLLVMFnIdFloatOp: | |
| 5774 | return a.data.floating.bit_count == b.data.floating.bit_count && | |
| 5775 | a.data.floating.vector_len == b.data.floating.vector_len && | |
| 5776 | a.data.floating.op == b.data.floating.op; | |
| 5776 | 5777 | case ZigLLVMFnIdFMA: |
| 5777 | 5778 | return a.data.floating.bit_count == b.data.floating.bit_count && |
| 5778 | 5779 | a.data.floating.vector_len == b.data.floating.vector_len; |
src/codegen.cpp+36-32| ... | ... | @@ -806,7 +806,7 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSu |
| 806 | 806 | return fn_val; |
| 807 | 807 | } |
| 808 | 808 | |
| 809 | static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id) { | |
| 809 | static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) { | |
| 810 | 810 | assert(type_entry->id == ZigTypeIdFloat || |
| 811 | 811 | type_entry->id == ZigTypeIdVector); |
| 812 | 812 | |
| ... | ... | @@ -817,6 +817,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn |
| 817 | 817 | key.id = fn_id; |
| 818 | 818 | key.data.floating.bit_count = (uint32_t)float_type->data.floating.bit_count; |
| 819 | 819 | key.data.floating.vector_len = is_vector ? (uint32_t)type_entry->data.vector.len : 0; |
| 820 | key.data.floating.op = op; | |
| 820 | 821 | |
| 821 | 822 | auto existing_entry = g->llvm_fn_table.maybe_get(key); |
| 822 | 823 | if (existing_entry) |
| ... | ... | @@ -824,18 +825,12 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn |
| 824 | 825 | |
| 825 | 826 | const char *name; |
| 826 | 827 | uint32_t num_args; |
| 827 | if (fn_id == ZigLLVMFnIdFloor) { | |
| 828 | name = "floor"; | |
| 829 | num_args = 1; | |
| 830 | } else if (fn_id == ZigLLVMFnIdCeil) { | |
| 831 | name = "ceil"; | |
| 832 | num_args = 1; | |
| 833 | } else if (fn_id == ZigLLVMFnIdSqrt) { | |
| 834 | name = "sqrt"; | |
| 835 | num_args = 1; | |
| 836 | } else if (fn_id == ZigLLVMFnIdFMA) { | |
| 828 | if (fn_id == ZigLLVMFnIdFMA) { | |
| 837 | 829 | name = "fma"; |
| 838 | 830 | num_args = 3; |
| 831 | } else if (fn_id == ZigLLVMFnIdFloatOp) { | |
| 832 | name = float_op_to_name(op, true); | |
| 833 | num_args = 1; | |
| 839 | 834 | } else { |
| 840 | 835 | zig_unreachable(); |
| 841 | 836 | } |
| ... | ... | @@ -2480,22 +2475,17 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2480 | 2475 | return result; |
| 2481 | 2476 | } |
| 2482 | 2477 | |
| 2483 | static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, ZigType *type_entry) { | |
| 2484 | if (type_entry->id == ZigTypeIdInt) | |
| 2478 | static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { | |
| 2479 | if ((op == BuiltinFnIdCeil || | |
| 2480 | op == BuiltinFnIdFloor) && | |
| 2481 | type_entry->id == ZigTypeIdInt) | |
| 2485 | 2482 | return val; |
| 2483 | assert(type_entry->id == ZigTypeIdFloat); | |
| 2486 | 2484 | |
| 2487 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor); | |
| 2485 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); | |
| 2488 | 2486 | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); |
| 2489 | 2487 | } |
| 2490 | 2488 | |
| 2491 | static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, ZigType *type_entry) { | |
| 2492 | if (type_entry->id == ZigTypeIdInt) | |
| 2493 | return val; | |
| 2494 | ||
| 2495 | LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil); | |
| 2496 | return LLVMBuildCall(g->builder, ceil_fn, &val, 1, ""); | |
| 2497 | } | |
| 2498 | ||
| 2499 | 2489 | enum DivKind { |
| 2500 | 2490 | DivKindFloat, |
| 2501 | 2491 | DivKindTrunc, |
| ... | ... | @@ -2571,7 +2561,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2571 | 2561 | return result; |
| 2572 | 2562 | case DivKindExact: |
| 2573 | 2563 | if (want_runtime_safety) { |
| 2574 | LLVMValueRef floored = gen_floor(g, result, type_entry); | |
| 2564 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2575 | 2565 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2576 | 2566 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2577 | 2567 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); |
| ... | ... | @@ -2593,12 +2583,12 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2593 | 2583 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2594 | 2584 | |
| 2595 | 2585 | LLVMPositionBuilderAtEnd(g->builder, ltz_block); |
| 2596 | LLVMValueRef ceiled = gen_ceil(g, result, type_entry); | |
| 2586 | LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil); | |
| 2597 | 2587 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); |
| 2598 | 2588 | LLVMBuildBr(g->builder, end_block); |
| 2599 | 2589 | |
| 2600 | 2590 | LLVMPositionBuilderAtEnd(g->builder, gez_block); |
| 2601 | LLVMValueRef floored = gen_floor(g, result, type_entry); | |
| 2591 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2602 | 2592 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); |
| 2603 | 2593 | LLVMBuildBr(g->builder, end_block); |
| 2604 | 2594 | |
| ... | ... | @@ -2610,7 +2600,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2610 | 2600 | return phi; |
| 2611 | 2601 | } |
| 2612 | 2602 | case DivKindFloor: |
| 2613 | return gen_floor(g, result, type_entry); | |
| 2603 | return gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2614 | 2604 | } |
| 2615 | 2605 | zig_unreachable(); |
| 2616 | 2606 | } |
| ... | ... | @@ -5450,10 +5440,10 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e |
| 5450 | 5440 | return nullptr; |
| 5451 | 5441 | } |
| 5452 | 5442 | |
| 5453 | static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) { | |
| 5454 | LLVMValueRef op = ir_llvm_value(g, instruction->op); | |
| 5443 | static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) { | |
| 5444 | LLVMValueRef op = ir_llvm_value(g, instruction->op1); | |
| 5455 | 5445 | assert(instruction->base.value.type->id == ZigTypeIdFloat); |
| 5456 | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt); | |
| 5446 | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFloatOp, instruction->op); | |
| 5457 | 5447 | return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); |
| 5458 | 5448 | } |
| 5459 | 5449 | |
| ... | ... | @@ -5463,7 +5453,7 @@ static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrIn |
| 5463 | 5453 | LLVMValueRef op3 = ir_llvm_value(g, instruction->op3); |
| 5464 | 5454 | assert(instruction->base.value.type->id == ZigTypeIdFloat || |
| 5465 | 5455 | instruction->base.value.type->id == ZigTypeIdVector); |
| 5466 | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA); | |
| 5456 | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd); | |
| 5467 | 5457 | LLVMValueRef args[3] = { |
| 5468 | 5458 | op1, |
| 5469 | 5459 | op2, |
| ... | ... | @@ -5814,8 +5804,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5814 | 5804 | return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction); |
| 5815 | 5805 | case IrInstructionIdMarkErrRetTracePtr: |
| 5816 | 5806 | return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 5817 | case IrInstructionIdSqrt: | |
| 5818 | return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); | |
| 5807 | case IrInstructionIdFloatOp: | |
| 5808 | return ir_render_float_op(g, executable, (IrInstructionFloatOp *)instruction); | |
| 5819 | 5809 | case IrInstructionIdMulAdd: |
| 5820 | 5810 | return ir_render_mul_add(g, executable, (IrInstructionMulAdd *)instruction); |
| 5821 | 5811 | case IrInstructionIdArrayToVector: |
| ... | ... | @@ -7435,6 +7425,20 @@ static void define_builtin_fns(CodeGen *g) { |
| 7435 | 7425 | create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); |
| 7436 | 7426 | create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); |
| 7437 | 7427 | create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); |
| 7428 | create_builtin_fn(g, BuiltinFnIdSin, "sin", 2); | |
| 7429 | create_builtin_fn(g, BuiltinFnIdCos, "cos", 2); | |
| 7430 | create_builtin_fn(g, BuiltinFnIdExp, "exp", 2); | |
| 7431 | create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 2); | |
| 7432 | create_builtin_fn(g, BuiltinFnIdLn, "ln", 2); | |
| 7433 | create_builtin_fn(g, BuiltinFnIdLog2, "log2", 2); | |
| 7434 | create_builtin_fn(g, BuiltinFnIdLog10, "log10", 2); | |
| 7435 | create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 2); | |
| 7436 | create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2); | |
| 7437 | create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2); | |
| 7438 | create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2); | |
| 7439 | //Needs library support on Windows | |
| 7440 | //create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2); | |
| 7441 | create_builtin_fn(g, BuiltinFnIdRound, "round", 2); | |
| 7438 | 7442 | create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); |
| 7439 | 7443 | create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); |
| 7440 | 7444 | create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); |
src/ir.cpp+294-61| ... | ... | @@ -991,8 +991,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP |
| 991 | 991 | return IrInstructionIdMarkErrRetTracePtr; |
| 992 | 992 | } |
| 993 | 993 | |
| 994 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { | |
| 995 | return IrInstructionIdSqrt; | |
| 994 | static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) { | |
| 995 | return IrInstructionIdFloatOp; | |
| 996 | 996 | } |
| 997 | 997 | |
| 998 | 998 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) { |
| ... | ... | @@ -2312,6 +2312,59 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode |
| 2312 | 2312 | return &instruction->base; |
| 2313 | 2313 | } |
| 2314 | 2314 | |
| 2315 | ||
| 2316 | //TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, | |
| 2317 | // lround, llround, lrint, llrint | |
| 2318 | // So far this is only non-complicated type functions. | |
| 2319 | const char *float_op_to_name(BuiltinFnId op, bool llvm_name) { | |
| 2320 | const bool b = llvm_name; | |
| 2321 | ||
| 2322 | switch (op) { | |
| 2323 | case BuiltinFnIdSqrt: | |
| 2324 | return "sqrt"; | |
| 2325 | case BuiltinFnIdSin: | |
| 2326 | return "sin"; | |
| 2327 | case BuiltinFnIdCos: | |
| 2328 | return "cos"; | |
| 2329 | case BuiltinFnIdExp: | |
| 2330 | return "exp"; | |
| 2331 | case BuiltinFnIdExp2: | |
| 2332 | return "exp2"; | |
| 2333 | case BuiltinFnIdLn: | |
| 2334 | return b ? "log" : "ln"; | |
| 2335 | case BuiltinFnIdLog10: | |
| 2336 | return "log10"; | |
| 2337 | case BuiltinFnIdLog2: | |
| 2338 | return "log2"; | |
| 2339 | case BuiltinFnIdFabs: | |
| 2340 | return "fabs"; | |
| 2341 | case BuiltinFnIdFloor: | |
| 2342 | return "floor"; | |
| 2343 | case BuiltinFnIdCeil: | |
| 2344 | return "ceil"; | |
| 2345 | case BuiltinFnIdTrunc: | |
| 2346 | return "trunc"; | |
| 2347 | case BuiltinFnIdNearbyInt: | |
| 2348 | return b ? "nearbyint" : "nearbyInt"; | |
| 2349 | case BuiltinFnIdRound: | |
| 2350 | return "round"; | |
| 2351 | default: | |
| 2352 | zig_unreachable(); | |
| 2353 | } | |
| 2354 | } | |
| 2355 | ||
| 2356 | static IrInstruction *ir_build_float_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op1, BuiltinFnId op) { | |
| 2357 | IrInstructionFloatOp *instruction = ir_build_instruction<IrInstructionFloatOp>(irb, scope, source_node); | |
| 2358 | instruction->type = type; | |
| 2359 | instruction->op1 = op1; | |
| 2360 | instruction->op = op; | |
| 2361 | ||
| 2362 | if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); | |
| 2363 | ir_ref_instruction(op1, irb->current_basic_block); | |
| 2364 | ||
| 2365 | return &instruction->base; | |
| 2366 | } | |
| 2367 | ||
| 2315 | 2368 | static IrInstruction *ir_build_mul_add(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2316 | 2369 | IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *op3) { |
| 2317 | 2370 | IrInstructionMulAdd *instruction = ir_build_instruction<IrInstructionMulAdd>(irb, scope, source_node); |
| ... | ... | @@ -3033,17 +3086,6 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco |
| 3033 | 3086 | return &instruction->base; |
| 3034 | 3087 | } |
| 3035 | 3088 | |
| 3036 | static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) { | |
| 3037 | IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node); | |
| 3038 | instruction->type = type; | |
| 3039 | instruction->op = op; | |
| 3040 | ||
| 3041 | if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); | |
| 3042 | ir_ref_instruction(op, irb->current_basic_block); | |
| 3043 | ||
| 3044 | return &instruction->base; | |
| 3045 | } | |
| 3046 | ||
| 3047 | 3089 | static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 3048 | 3090 | IrInstruction *container, IrInstruction *name) |
| 3049 | 3091 | { |
| ... | ... | @@ -4400,6 +4442,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4400 | 4442 | return ir_lval_wrap(irb, scope, bin_op, lval); |
| 4401 | 4443 | } |
| 4402 | 4444 | case BuiltinFnIdSqrt: |
| 4445 | case BuiltinFnIdSin: | |
| 4446 | case BuiltinFnIdCos: | |
| 4447 | case BuiltinFnIdExp: | |
| 4448 | case BuiltinFnIdExp2: | |
| 4449 | case BuiltinFnIdLn: | |
| 4450 | case BuiltinFnIdLog2: | |
| 4451 | case BuiltinFnIdLog10: | |
| 4452 | case BuiltinFnIdFabs: | |
| 4453 | case BuiltinFnIdFloor: | |
| 4454 | case BuiltinFnIdCeil: | |
| 4455 | case BuiltinFnIdTrunc: | |
| 4456 | case BuiltinFnIdNearbyInt: | |
| 4457 | case BuiltinFnIdRound: | |
| 4403 | 4458 | { |
| 4404 | 4459 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| 4405 | 4460 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); |
| ... | ... | @@ -4411,7 +4466,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4411 | 4466 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4412 | 4467 | return arg1_value; |
| 4413 | 4468 | |
| 4414 | IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); | |
| 4469 | IrInstruction *ir_sqrt = ir_build_float_op(irb, scope, node, arg0_value, arg1_value, builtin_fn->id); | |
| 4415 | 4470 | return ir_lval_wrap(irb, scope, ir_sqrt, lval); |
| 4416 | 4471 | } |
| 4417 | 4472 | case BuiltinFnIdTruncate: |
| ... | ... | @@ -23214,70 +23269,248 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i |
| 23214 | 23269 | return result; |
| 23215 | 23270 | } |
| 23216 | 23271 | |
| 23217 | static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { | |
| 23218 | ZigType *float_type = ir_resolve_type(ira, instruction->type->child); | |
| 23219 | if (type_is_invalid(float_type)) | |
| 23220 | return ira->codegen->invalid_instruction; | |
| 23272 | static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type, | |
| 23273 | ConstExprValue *op, ConstExprValue *out_val) { | |
| 23274 | assert(ira && source_instr && float_type && out_val && op); | |
| 23275 | assert(float_type->id == ZigTypeIdFloat || | |
| 23276 | float_type->id == ZigTypeIdComptimeFloat); | |
| 23221 | 23277 | |
| 23222 | IrInstruction *op = instruction->op->child; | |
| 23223 | if (type_is_invalid(op->value.type)) | |
| 23278 | BuiltinFnId fop = source_instr->op; | |
| 23279 | unsigned bits; | |
| 23280 | ||
| 23281 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 23282 | bits = 128; | |
| 23283 | } else if (float_type->id == ZigTypeIdFloat) | |
| 23284 | bits = float_type->data.floating.bit_count; | |
| 23285 | ||
| 23286 | switch (bits) { | |
| 23287 | case 16: { | |
| 23288 | switch (fop) { | |
| 23289 | case BuiltinFnIdSqrt: | |
| 23290 | out_val->data.x_f16 = f16_sqrt(op->data.x_f16); | |
| 23291 | break; | |
| 23292 | case BuiltinFnIdSin: | |
| 23293 | case BuiltinFnIdCos: | |
| 23294 | case BuiltinFnIdExp: | |
| 23295 | case BuiltinFnIdExp2: | |
| 23296 | case BuiltinFnIdLn: | |
| 23297 | case BuiltinFnIdLog10: | |
| 23298 | case BuiltinFnIdLog2: | |
| 23299 | case BuiltinFnIdFabs: | |
| 23300 | case BuiltinFnIdFloor: | |
| 23301 | case BuiltinFnIdCeil: | |
| 23302 | case BuiltinFnIdTrunc: | |
| 23303 | case BuiltinFnIdNearbyInt: | |
| 23304 | case BuiltinFnIdRound: | |
| 23305 | zig_panic("unimplemented f16 builtin"); | |
| 23306 | default: | |
| 23307 | zig_unreachable(); | |
| 23308 | }; | |
| 23309 | break; | |
| 23310 | }; | |
| 23311 | case 32: { | |
| 23312 | switch (fop) { | |
| 23313 | case BuiltinFnIdSqrt: | |
| 23314 | out_val->data.x_f32 = sqrtf(op->data.x_f32); | |
| 23315 | break; | |
| 23316 | case BuiltinFnIdSin: | |
| 23317 | out_val->data.x_f32 = sinf(op->data.x_f32); | |
| 23318 | break; | |
| 23319 | case BuiltinFnIdCos: | |
| 23320 | out_val->data.x_f32 = cosf(op->data.x_f32); | |
| 23321 | break; | |
| 23322 | case BuiltinFnIdExp: | |
| 23323 | out_val->data.x_f32 = expf(op->data.x_f32); | |
| 23324 | break; | |
| 23325 | case BuiltinFnIdExp2: | |
| 23326 | out_val->data.x_f32 = exp2f(op->data.x_f32); | |
| 23327 | break; | |
| 23328 | case BuiltinFnIdLn: | |
| 23329 | out_val->data.x_f32 = logf(op->data.x_f32); | |
| 23330 | break; | |
| 23331 | case BuiltinFnIdLog10: | |
| 23332 | out_val->data.x_f32 = log10f(op->data.x_f32); | |
| 23333 | break; | |
| 23334 | case BuiltinFnIdLog2: | |
| 23335 | out_val->data.x_f32 = log2f(op->data.x_f32); | |
| 23336 | break; | |
| 23337 | case BuiltinFnIdFabs: | |
| 23338 | out_val->data.x_f32 = fabsf(op->data.x_f32); | |
| 23339 | break; | |
| 23340 | case BuiltinFnIdFloor: | |
| 23341 | out_val->data.x_f32 = floorf(op->data.x_f32); | |
| 23342 | break; | |
| 23343 | case BuiltinFnIdCeil: | |
| 23344 | out_val->data.x_f32 = ceilf(op->data.x_f32); | |
| 23345 | break; | |
| 23346 | case BuiltinFnIdTrunc: | |
| 23347 | out_val->data.x_f32 = truncf(op->data.x_f32); | |
| 23348 | break; | |
| 23349 | case BuiltinFnIdNearbyInt: | |
| 23350 | out_val->data.x_f32 = nearbyintf(op->data.x_f32); | |
| 23351 | break; | |
| 23352 | case BuiltinFnIdRound: | |
| 23353 | out_val->data.x_f32 = roundf(op->data.x_f32); | |
| 23354 | break; | |
| 23355 | default: | |
| 23356 | zig_unreachable(); | |
| 23357 | }; | |
| 23358 | break; | |
| 23359 | }; | |
| 23360 | case 64: { | |
| 23361 | switch (fop) { | |
| 23362 | case BuiltinFnIdSqrt: | |
| 23363 | out_val->data.x_f64 = sqrt(op->data.x_f64); | |
| 23364 | break; | |
| 23365 | case BuiltinFnIdSin: | |
| 23366 | out_val->data.x_f64 = sin(op->data.x_f64); | |
| 23367 | break; | |
| 23368 | case BuiltinFnIdCos: | |
| 23369 | out_val->data.x_f64 = cos(op->data.x_f64); | |
| 23370 | break; | |
| 23371 | case BuiltinFnIdExp: | |
| 23372 | out_val->data.x_f64 = exp(op->data.x_f64); | |
| 23373 | break; | |
| 23374 | case BuiltinFnIdExp2: | |
| 23375 | out_val->data.x_f64 = exp2(op->data.x_f64); | |
| 23376 | break; | |
| 23377 | case BuiltinFnIdLn: | |
| 23378 | out_val->data.x_f64 = log(op->data.x_f64); | |
| 23379 | break; | |
| 23380 | case BuiltinFnIdLog10: | |
| 23381 | out_val->data.x_f64 = log10(op->data.x_f64); | |
| 23382 | break; | |
| 23383 | case BuiltinFnIdLog2: | |
| 23384 | out_val->data.x_f64 = log2(op->data.x_f64); | |
| 23385 | break; | |
| 23386 | case BuiltinFnIdFabs: | |
| 23387 | out_val->data.x_f64 = fabs(op->data.x_f64); | |
| 23388 | break; | |
| 23389 | case BuiltinFnIdFloor: | |
| 23390 | out_val->data.x_f64 = floor(op->data.x_f64); | |
| 23391 | break; | |
| 23392 | case BuiltinFnIdCeil: | |
| 23393 | out_val->data.x_f64 = ceil(op->data.x_f64); | |
| 23394 | break; | |
| 23395 | case BuiltinFnIdTrunc: | |
| 23396 | out_val->data.x_f64 = trunc(op->data.x_f64); | |
| 23397 | break; | |
| 23398 | case BuiltinFnIdNearbyInt: | |
| 23399 | out_val->data.x_f64 = nearbyint(op->data.x_f64); | |
| 23400 | break; | |
| 23401 | case BuiltinFnIdRound: | |
| 23402 | out_val->data.x_f64 = round(op->data.x_f64); | |
| 23403 | break; | |
| 23404 | default: | |
| 23405 | zig_unreachable(); | |
| 23406 | } | |
| 23407 | break; | |
| 23408 | }; | |
| 23409 | case 128: { | |
| 23410 | float128_t *out, *in; | |
| 23411 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 23412 | out = &out_val->data.x_bigfloat.value; | |
| 23413 | in = &op->data.x_bigfloat.value; | |
| 23414 | } else { | |
| 23415 | out = &out_val->data.x_f128; | |
| 23416 | in = &op->data.x_f128; | |
| 23417 | } | |
| 23418 | switch (fop) { | |
| 23419 | case BuiltinFnIdSqrt: | |
| 23420 | f128M_sqrt(in, out); | |
| 23421 | break; | |
| 23422 | case BuiltinFnIdNearbyInt: | |
| 23423 | case BuiltinFnIdSin: | |
| 23424 | case BuiltinFnIdCos: | |
| 23425 | case BuiltinFnIdExp: | |
| 23426 | case BuiltinFnIdExp2: | |
| 23427 | case BuiltinFnIdLn: | |
| 23428 | case BuiltinFnIdLog10: | |
| 23429 | case BuiltinFnIdLog2: | |
| 23430 | case BuiltinFnIdFabs: | |
| 23431 | case BuiltinFnIdFloor: | |
| 23432 | case BuiltinFnIdCeil: | |
| 23433 | case BuiltinFnIdTrunc: | |
| 23434 | case BuiltinFnIdRound: | |
| 23435 | zig_panic("unimplemented f128 builtin"); | |
| 23436 | default: | |
| 23437 | zig_unreachable(); | |
| 23438 | } | |
| 23439 | break; | |
| 23440 | }; | |
| 23441 | default: | |
| 23442 | zig_unreachable(); | |
| 23443 | } | |
| 23444 | } | |
| 23445 | ||
| 23446 | static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) { | |
| 23447 | IrInstruction *type = instruction->type->child; | |
| 23448 | if (type_is_invalid(type->value.type)) | |
| 23449 | return ira->codegen->invalid_instruction; | |
| 23450 | ||
| 23451 | ZigType *expr_type = ir_resolve_type(ira, type); | |
| 23452 | if (type_is_invalid(expr_type)) | |
| 23224 | 23453 | return ira->codegen->invalid_instruction; |
| 23225 | 23454 | |
| 23226 | bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat; | |
| 23227 | if (!ok_type) { | |
| 23228 | ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); | |
| 23455 | // Only allow float types, and vectors of floats. | |
| 23456 | ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; | |
| 23457 | if (float_type->id != ZigTypeIdFloat && float_type->id != ZigTypeIdComptimeFloat) { | |
| 23458 | ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name))); | |
| 23229 | 23459 | return ira->codegen->invalid_instruction; |
| 23230 | 23460 | } |
| 23231 | 23461 | |
| 23232 | IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type); | |
| 23233 | if (type_is_invalid(casted_op->value.type)) | |
| 23462 | IrInstruction *op1 = instruction->op1->child; | |
| 23463 | if (type_is_invalid(op1->value.type)) | |
| 23234 | 23464 | return ira->codegen->invalid_instruction; |
| 23235 | 23465 | |
| 23236 | if (instr_is_comptime(casted_op)) { | |
| 23237 | ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); | |
| 23238 | if (!val) | |
| 23466 | IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type); | |
| 23467 | if (type_is_invalid(casted_op1->value.type)) | |
| 23468 | return ira->codegen->invalid_instruction; | |
| 23469 | ||
| 23470 | if (instr_is_comptime(casted_op1)) { | |
| 23471 | // Our comptime 16-bit and 128-bit support is quite limited. | |
| 23472 | if ((float_type->id == ZigTypeIdComptimeFloat || | |
| 23473 | float_type->data.floating.bit_count == 16 || | |
| 23474 | float_type->data.floating.bit_count == 128) && | |
| 23475 | instruction->op != BuiltinFnIdSqrt) { | |
| 23476 | ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name))); | |
| 23239 | 23477 | return ira->codegen->invalid_instruction; |
| 23478 | } | |
| 23240 | 23479 | |
| 23241 | IrInstruction *result = ir_const(ira, &instruction->base, float_type); | |
| 23480 | ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); | |
| 23481 | if (!op1_const) | |
| 23482 | return ira->codegen->invalid_instruction; | |
| 23483 | ||
| 23484 | IrInstruction *result = ir_const(ira, &instruction->base, expr_type); | |
| 23242 | 23485 | ConstExprValue *out_val = &result->value; |
| 23243 | 23486 | |
| 23244 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 23245 | bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); | |
| 23246 | } else if (float_type->id == ZigTypeIdFloat) { | |
| 23247 | switch (float_type->data.floating.bit_count) { | |
| 23248 | case 16: | |
| 23249 | out_val->data.x_f16 = f16_sqrt(val->data.x_f16); | |
| 23250 | break; | |
| 23251 | case 32: | |
| 23252 | out_val->data.x_f32 = sqrtf(val->data.x_f32); | |
| 23253 | break; | |
| 23254 | case 64: | |
| 23255 | out_val->data.x_f64 = sqrt(val->data.x_f64); | |
| 23256 | break; | |
| 23257 | case 128: | |
| 23258 | f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128); | |
| 23259 | break; | |
| 23260 | default: | |
| 23261 | zig_unreachable(); | |
| 23487 | if (expr_type->id == ZigTypeIdVector) { | |
| 23488 | expand_undef_array(ira->codegen, op1_const); | |
| 23489 | out_val->special = ConstValSpecialUndef; | |
| 23490 | expand_undef_array(ira->codegen, out_val); | |
| 23491 | size_t len = expr_type->data.vector.len; | |
| 23492 | for (size_t i = 0; i < len; i += 1) { | |
| 23493 | ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; | |
| 23494 | ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; | |
| 23495 | assert(float_operand_op1->type == float_type); | |
| 23496 | assert(float_out_val->type == float_type); | |
| 23497 | ir_eval_float_op(ira, instruction, float_type, | |
| 23498 | op1_const, float_out_val); | |
| 23499 | float_out_val->type = float_type; | |
| 23262 | 23500 | } |
| 23501 | out_val->type = expr_type; | |
| 23502 | out_val->special = ConstValSpecialStatic; | |
| 23263 | 23503 | } else { |
| 23264 | zig_unreachable(); | |
| 23504 | ir_eval_float_op(ira, instruction, float_type, op1_const, out_val); | |
| 23265 | 23505 | } |
| 23266 | ||
| 23267 | 23506 | return result; |
| 23268 | 23507 | } |
| 23269 | 23508 | |
| 23270 | 23509 | ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base); |
| 23271 | if (float_type->data.floating.bit_count != 16 && | |
| 23272 | float_type->data.floating.bit_count != 32 && | |
| 23273 | float_type->data.floating.bit_count != 64) { | |
| 23274 | ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); | |
| 23275 | return ira->codegen->invalid_instruction; | |
| 23276 | } | |
| 23277 | 23510 | |
| 23278 | IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope, | |
| 23279 | instruction->base.source_node, nullptr, casted_op); | |
| 23280 | result->value.type = float_type; | |
| 23511 | IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope, | |
| 23512 | instruction->base.source_node, nullptr, casted_op1, instruction->op); | |
| 23513 | result->value.type = expr_type; | |
| 23281 | 23514 | return result; |
| 23282 | 23515 | } |
| 23283 | 23516 | |
| ... | ... | @@ -23762,8 +23995,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio |
| 23762 | 23995 | return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); |
| 23763 | 23996 | case IrInstructionIdMarkErrRetTracePtr: |
| 23764 | 23997 | return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 23765 | case IrInstructionIdSqrt: | |
| 23766 | return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); | |
| 23998 | case IrInstructionIdFloatOp: | |
| 23999 | return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction); | |
| 23767 | 24000 | case IrInstructionIdMulAdd: |
| 23768 | 24001 | return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)instruction); |
| 23769 | 24002 | case IrInstructionIdIntToErr: |
| ... | ... | @@ -24004,7 +24237,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 24004 | 24237 | case IrInstructionIdCoroFree: |
| 24005 | 24238 | case IrInstructionIdCoroPromise: |
| 24006 | 24239 | case IrInstructionIdPromiseResultType: |
| 24007 | case IrInstructionIdSqrt: | |
| 24240 | case IrInstructionIdFloatOp: | |
| 24008 | 24241 | case IrInstructionIdMulAdd: |
| 24009 | 24242 | case IrInstructionIdAtomicLoad: |
| 24010 | 24243 | case IrInstructionIdIntCast: |
src/ir.hpp+1| ... | ... | @@ -26,5 +26,6 @@ bool ir_has_side_effects(IrInstruction *instruction); |
| 26 | 26 | struct IrAnalyze; |
| 27 | 27 | ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val, |
| 28 | 28 | AstNode *source_node); |
| 29 | const char *float_op_to_name(BuiltinFnId op, bool llvm_name); | |
| 29 | 30 | |
| 30 | 31 | #endif |
src/ir_print.cpp+6-5| ... | ... | @@ -1427,15 +1427,16 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe |
| 1427 | 1427 | fprintf(irp->f, ")"); |
| 1428 | 1428 | } |
| 1429 | 1429 | |
| 1430 | static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { | |
| 1431 | fprintf(irp->f, "@sqrt("); | |
| 1430 | static void ir_print_float_op(IrPrint *irp, IrInstructionFloatOp *instruction) { | |
| 1431 | ||
| 1432 | fprintf(irp->f, "@%s(", float_op_to_name(instruction->op, false)); | |
| 1432 | 1433 | if (instruction->type != nullptr) { |
| 1433 | 1434 | ir_print_other_instruction(irp, instruction->type); |
| 1434 | 1435 | } else { |
| 1435 | 1436 | fprintf(irp->f, "null"); |
| 1436 | 1437 | } |
| 1437 | 1438 | fprintf(irp->f, ","); |
| 1438 | ir_print_other_instruction(irp, instruction->op); | |
| 1439 | ir_print_other_instruction(irp, instruction->op1); | |
| 1439 | 1440 | fprintf(irp->f, ")"); |
| 1440 | 1441 | } |
| 1441 | 1442 | |
| ... | ... | @@ -1918,8 +1919,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1918 | 1919 | case IrInstructionIdMarkErrRetTracePtr: |
| 1919 | 1920 | ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 1920 | 1921 | break; |
| 1921 | case IrInstructionIdSqrt: | |
| 1922 | ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); | |
| 1922 | case IrInstructionIdFloatOp: | |
| 1923 | ir_print_float_op(irp, (IrInstructionFloatOp *)instruction); | |
| 1923 | 1924 | break; |
| 1924 | 1925 | case IrInstructionIdMulAdd: |
| 1925 | 1926 | ir_print_mul_add(irp, (IrInstructionMulAdd *)instruction); |
src/util.cpp+1| ... | ... | @@ -13,6 +13,7 @@ |
| 13 | 13 | #include "userland.h" |
| 14 | 14 | |
| 15 | 15 | void zig_panic(const char *format, ...) { |
| 16 | abort(); | |
| 16 | 17 | va_list ap; |
| 17 | 18 | va_start(ap, format); |
| 18 | 19 | vfprintf(stderr, format, ap); |
std/special/c.zig+26-18| ... | ... | @@ -254,24 +254,32 @@ export fn fmod(x: f64, y: f64) f64 { |
| 254 | 254 | |
| 255 | 255 | // TODO add intrinsics for these (and probably the double version too) |
| 256 | 256 | // and have the math stuff use the intrinsic. same as @mod and @rem |
| 257 | export fn floorf(x: f32) f32 { | |
| 258 | return math.floor(x); | |
| 259 | } | |
| 260 | export fn ceilf(x: f32) f32 { | |
| 261 | return math.ceil(x); | |
| 262 | } | |
| 263 | export fn floor(x: f64) f64 { | |
| 264 | return math.floor(x); | |
| 265 | } | |
| 266 | export fn ceil(x: f64) f64 { | |
| 267 | return math.ceil(x); | |
| 268 | } | |
| 269 | export fn fma(a: f64, b: f64, c: f64) f64 { | |
| 270 | return math.fma(f64, a, b, c); | |
| 271 | } | |
| 272 | export fn fmaf(a: f32, b: f32, c: f32) f32 { | |
| 273 | return math.fma(f32, a, b, c); | |
| 274 | } | |
| 257 | export fn floorf(x: f32) f32 {return math.floor(x);} | |
| 258 | export fn ceilf(x: f32) f32 {return math.ceil(x);} | |
| 259 | export fn floor(x: f64) f64 {return math.floor(x);} | |
| 260 | export fn ceil(x: f64) f64 {return math.ceil(x);} | |
| 261 | export fn fma(a: f64, b: f64, c: f64) f64 {return math.fma(f64, a, b, c);} | |
| 262 | export fn fmaf(a: f32, b: f32, c: f32) f32 {return math.fma(f32, a, b, c);} | |
| 263 | export fn sin(a: f64) f64 {return math.sin(a);} | |
| 264 | export fn sinf(a: f32) f32 {return math.sin(a);} | |
| 265 | export fn cos(a: f64) f64 {return math.cos(a);} | |
| 266 | export fn cosf(a: f32) f32 {return math.cos(a);} | |
| 267 | export fn exp(a: f64) f64 {return math.exp(a);} | |
| 268 | export fn expf(a: f32) f32 {return math.exp(a);} | |
| 269 | export fn exp2(a: f64) f64 {return math.exp2(a);} | |
| 270 | export fn exp2f(a: f32) f32 {return math.exp2(a);} | |
| 271 | export fn log(a: f64) f64 {return math.ln(a);} | |
| 272 | export fn logf(a: f32) f32 {return math.ln(a);} | |
| 273 | export fn log2(a: f64) f64 {return math.log2(a);} | |
| 274 | export fn log2f(a: f32) f32 {return math.log2(a);} | |
| 275 | export fn log10(a: f64) f64 {return math.log10(a);} | |
| 276 | export fn log10f(a: f32) f32 {return math.log10(a);} | |
| 277 | export fn fabs(a: f64) f64 {return math.fabs(a);} | |
| 278 | export fn fabsf(a: f32) f32 {return math.fabs(a);} | |
| 279 | export fn trunc(a: f64) f64 {return math.trunc(a);} | |
| 280 | export fn truncf(a: f32) f32 {return math.trunc(a);} | |
| 281 | export fn round(a: f64) f64 {return math.round(a);} | |
| 282 | export fn roundf(a: f32) f32 {return math.round(a);} | |
| 275 | 283 | fn generic_fmod(comptime T: type, x: T, y: T) T { |
| 276 | 284 | @setRuntimeSafety(false); |
| 277 | 285 |
test/stage1/behavior.zig+1| ... | ... | @@ -71,6 +71,7 @@ comptime { |
| 71 | 71 | _ = @import("behavior/pointers.zig"); |
| 72 | 72 | _ = @import("behavior/popcount.zig"); |
| 73 | 73 | _ = @import("behavior/muladd.zig"); |
| 74 | _ = @import("behavior/floatop.zig"); | |
| 74 | 75 | _ = @import("behavior/ptrcast.zig"); |
| 75 | 76 | _ = @import("behavior/pub_enum.zig"); |
| 76 | 77 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
test/stage1/behavior/floatop.zig created+243| ... | ... | @@ -0,0 +1,243 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | const pi = @import("std").math.pi; | |
| 3 | const e = @import("std").math.e; | |
| 4 | ||
| 5 | test "@sqrt" { | |
| 6 | comptime testSqrt(); | |
| 7 | testSqrt(); | |
| 8 | } | |
| 9 | ||
| 10 | fn testSqrt() void { | |
| 11 | { | |
| 12 | var a: f16 = 4; | |
| 13 | expect(@sqrt(f16, a) == 2); | |
| 14 | } | |
| 15 | { | |
| 16 | var a: f32 = 9; | |
| 17 | expect(@sqrt(f32, a) == 3); | |
| 18 | } | |
| 19 | { | |
| 20 | var a: f64 = 25; | |
| 21 | expect(@sqrt(f64, a) == 5); | |
| 22 | } | |
| 23 | { | |
| 24 | const a: comptime_float = 25.0; | |
| 25 | expect(@sqrt(comptime_float, a) == 5.0); | |
| 26 | } | |
| 27 | // Waiting on a c.zig implementation | |
| 28 | //{ | |
| 29 | // var a: f128 = 49; | |
| 30 | // expect(@sqrt(f128, a) == 7); | |
| 31 | //} | |
| 32 | } | |
| 33 | ||
| 34 | test "@sin" { | |
| 35 | comptime testSin(); | |
| 36 | testSin(); | |
| 37 | } | |
| 38 | ||
| 39 | fn testSin() void { | |
| 40 | // TODO - this is actually useful and should be implemented | |
| 41 | // (all the trig functions for f16) | |
| 42 | // but will probably wait till self-hosted | |
| 43 | //{ | |
| 44 | // var a: f16 = pi; | |
| 45 | // expect(@sin(f16, a/2) == 1); | |
| 46 | //} | |
| 47 | { | |
| 48 | var a: f32 = 0; | |
| 49 | expect(@sin(f32, a) == 0); | |
| 50 | } | |
| 51 | { | |
| 52 | var a: f64 = 0; | |
| 53 | expect(@sin(f64, a) == 0); | |
| 54 | } | |
| 55 | // TODO | |
| 56 | //{ | |
| 57 | // var a: f16 = pi; | |
| 58 | // expect(@sqrt(f128, a/2) == 1); | |
| 59 | //} | |
| 60 | } | |
| 61 | ||
| 62 | test "@cos" { | |
| 63 | comptime testCos(); | |
| 64 | testCos(); | |
| 65 | } | |
| 66 | ||
| 67 | fn testCos() void { | |
| 68 | { | |
| 69 | var a: f32 = 0; | |
| 70 | expect(@cos(f32, a) == 1); | |
| 71 | } | |
| 72 | { | |
| 73 | var a: f64 = 0; | |
| 74 | expect(@cos(f64, a) == 1); | |
| 75 | } | |
| 76 | } | |
| 77 | ||
| 78 | test "@exp" { | |
| 79 | comptime testExp(); | |
| 80 | testExp(); | |
| 81 | } | |
| 82 | ||
| 83 | fn testExp() void { | |
| 84 | { | |
| 85 | var a: f32 = 0; | |
| 86 | expect(@exp(f32, a) == 1); | |
| 87 | } | |
| 88 | { | |
| 89 | var a: f64 = 0; | |
| 90 | expect(@exp(f64, a) == 1); | |
| 91 | } | |
| 92 | } | |
| 93 | ||
| 94 | test "@exp2" { | |
| 95 | comptime testExp2(); | |
| 96 | testExp2(); | |
| 97 | } | |
| 98 | ||
| 99 | fn testExp2() void { | |
| 100 | { | |
| 101 | var a: f32 = 2; | |
| 102 | expect(@exp2(f32, a) == 4); | |
| 103 | } | |
| 104 | { | |
| 105 | var a: f64 = 2; | |
| 106 | expect(@exp2(f64, a) == 4); | |
| 107 | } | |
| 108 | } | |
| 109 | ||
| 110 | test "@ln" { | |
| 111 | // Old musl (and glibc?), and our current math.ln implementation do not return 1 | |
| 112 | // so also accept those values. | |
| 113 | comptime testLn(); | |
| 114 | testLn(); | |
| 115 | } | |
| 116 | ||
| 117 | fn testLn() void { | |
| 118 | { | |
| 119 | var a: f32 = e; | |
| 120 | expect(@ln(f32, a) == 1 or @ln(f32, a) == @bitCast(f32, u32(0x3f7fffff))); | |
| 121 | } | |
| 122 | { | |
| 123 | var a: f64 = e; | |
| 124 | expect(@ln(f64, a) == 1 or @ln(f64, a) == @bitCast(f64, u64(0x3ff0000000000000))); | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | test "@log2" { | |
| 129 | comptime testLog2(); | |
| 130 | testLog2(); | |
| 131 | } | |
| 132 | ||
| 133 | fn testLog2() void { | |
| 134 | { | |
| 135 | var a: f32 = 4; | |
| 136 | expect(@log2(f32, a) == 2); | |
| 137 | } | |
| 138 | { | |
| 139 | var a: f64 = 4; | |
| 140 | expect(@log2(f64, a) == 2); | |
| 141 | } | |
| 142 | } | |
| 143 | ||
| 144 | test "@log10" { | |
| 145 | comptime testLog10(); | |
| 146 | testLog10(); | |
| 147 | } | |
| 148 | ||
| 149 | fn testLog10() void { | |
| 150 | { | |
| 151 | var a: f32 = 100; | |
| 152 | expect(@log10(f32, a) == 2); | |
| 153 | } | |
| 154 | { | |
| 155 | var a: f64 = 1000; | |
| 156 | expect(@log10(f64, a) == 3); | |
| 157 | } | |
| 158 | } | |
| 159 | ||
| 160 | test "@fabs" { | |
| 161 | comptime testFabs(); | |
| 162 | testFabs(); | |
| 163 | } | |
| 164 | ||
| 165 | fn testFabs() void { | |
| 166 | { | |
| 167 | var a: f32 = -2.5; | |
| 168 | var b: f32 = 2.5; | |
| 169 | expect(@fabs(f32, a) == 2.5); | |
| 170 | expect(@fabs(f32, b) == 2.5); | |
| 171 | } | |
| 172 | { | |
| 173 | var a: f64 = -2.5; | |
| 174 | var b: f64 = 2.5; | |
| 175 | expect(@fabs(f64, a) == 2.5); | |
| 176 | expect(@fabs(f64, b) == 2.5); | |
| 177 | } | |
| 178 | } | |
| 179 | ||
| 180 | test "@floor" { | |
| 181 | comptime testFloor(); | |
| 182 | testFloor(); | |
| 183 | } | |
| 184 | ||
| 185 | fn testFloor() void { | |
| 186 | { | |
| 187 | var a: f32 = 2.1; | |
| 188 | expect(@floor(f32, a) == 2); | |
| 189 | } | |
| 190 | { | |
| 191 | var a: f64 = 3.5; | |
| 192 | expect(@floor(f64, a) == 3); | |
| 193 | } | |
| 194 | } | |
| 195 | ||
| 196 | test "@ceil" { | |
| 197 | comptime testCeil(); | |
| 198 | testCeil(); | |
| 199 | } | |
| 200 | ||
| 201 | fn testCeil() void { | |
| 202 | { | |
| 203 | var a: f32 = 2.1; | |
| 204 | expect(@ceil(f32, a) == 3); | |
| 205 | } | |
| 206 | { | |
| 207 | var a: f64 = 3.5; | |
| 208 | expect(@ceil(f64, a) == 4); | |
| 209 | } | |
| 210 | } | |
| 211 | ||
| 212 | test "@trunc" { | |
| 213 | comptime testTrunc(); | |
| 214 | testTrunc(); | |
| 215 | } | |
| 216 | ||
| 217 | fn testTrunc() void { | |
| 218 | { | |
| 219 | var a: f32 = 2.1; | |
| 220 | expect(@trunc(f32, a) == 2); | |
| 221 | } | |
| 222 | { | |
| 223 | var a: f64 = -3.5; | |
| 224 | expect(@trunc(f64, a) == -3); | |
| 225 | } | |
| 226 | } | |
| 227 | ||
| 228 | // This is waiting on library support for the Windows build (not sure why the other's don't need it) | |
| 229 | //test "@nearbyInt" { | |
| 230 | // comptime testNearbyInt(); | |
| 231 | // testNearbyInt(); | |
| 232 | //} | |
| 233 | ||
| 234 | //fn testNearbyInt() void { | |
| 235 | // { | |
| 236 | // var a: f32 = 2.1; | |
| 237 | // expect(@nearbyInt(f32, a) == 2); | |
| 238 | // } | |
| 239 | // { | |
| 240 | // var a: f64 = -3.75; | |
| 241 | // expect(@nearbyInt(f64, a) == -4); | |
| 242 | // } | |
| 243 | //} |