| author | |
| committer | |
| log | 24400d5882aa2b83a4aed3e4c1503d0393e1c541 |
| tree | d06324bd3d1e58757d2843c97a2b6392dc5a3505 |
| parent | ca3660f6bf3a1f8d77692acf72eefe148802d342 |
| parent | 7f4de2dfdbeac4457e6e3170dad9ba9bf67ac9c8 |
| signature |
12 files changed, 1008 insertions(+), 128 deletions(-)
CMakeLists.txt+2| ... | ... | @@ -389,6 +389,8 @@ set(EMBEDDED_SOFTFLOAT_SOURCES |
| 389 | 389 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF32.c" |
| 390 | 390 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF64.c" |
| 391 | 391 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c" |
| 392 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_mulAdd.c" | |
| 393 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_mulAdd.c" | |
| 392 | 394 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/softfloat_state.c" |
| 393 | 395 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui32_to_f128M.c" |
| 394 | 396 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui64_to_f128M.c" |
doc/langref.html.in+90-2| ... | ... | @@ -6259,6 +6259,13 @@ comptime { |
| 6259 | 6259 | This function is only valid within function scope. |
| 6260 | 6260 | </p> |
| 6261 | 6261 | |
| 6262 | {#header_close#} | |
| 6263 | {#header_open|@mulAdd#} | |
| 6264 | <pre>{#syntax#}@mulAdd(comptime T: type, a: T, b: T, c: T) T{#endsyntax#}</pre> | |
| 6265 | <p> | |
| 6266 | Fused multiply add (for floats), similar to {#syntax#}(a * b) + c{#endsyntax#}, except | |
| 6267 | only rounds once, and is thus more accurate. | |
| 6268 | </p> | |
| 6262 | 6269 | {#header_close#} |
| 6263 | 6270 | |
| 6264 | 6271 | {#header_open|@byteSwap#} |
| ... | ... | @@ -7347,10 +7354,91 @@ test "@setRuntimeSafety" { |
| 7347 | 7354 | <pre>{#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}</pre> |
| 7348 | 7355 | <p> |
| 7349 | 7356 | Performs the square root of a floating point number. Uses a dedicated hardware instruction |
| 7350 | 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 | </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. | |
| 7351 | 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> | |
| 7352 | 7439 | <p> |
| 7353 | 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. | |
| 7354 | 7442 | </p> |
| 7355 | 7443 | {#header_close#} |
| 7356 | 7444 |
src/all_types.hpp+33-6| ... | ... | @@ -1406,6 +1406,7 @@ enum BuiltinFnId { |
| 1406 | 1406 | BuiltinFnIdSubWithOverflow, |
| 1407 | 1407 | BuiltinFnIdMulWithOverflow, |
| 1408 | 1408 | BuiltinFnIdShlWithOverflow, |
| 1409 | BuiltinFnIdMulAdd, | |
| 1409 | 1410 | BuiltinFnIdCInclude, |
| 1410 | 1411 | BuiltinFnIdCDefine, |
| 1411 | 1412 | BuiltinFnIdCUndef, |
| ... | ... | @@ -1433,6 +1434,19 @@ enum BuiltinFnId { |
| 1433 | 1434 | BuiltinFnIdRem, |
| 1434 | 1435 | BuiltinFnIdMod, |
| 1435 | 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, | |
| 1436 | 1450 | BuiltinFnIdTruncate, |
| 1437 | 1451 | BuiltinFnIdIntCast, |
| 1438 | 1452 | BuiltinFnIdFloatCast, |
| ... | ... | @@ -1554,9 +1568,8 @@ enum ZigLLVMFnId { |
| 1554 | 1568 | ZigLLVMFnIdClz, |
| 1555 | 1569 | ZigLLVMFnIdPopCount, |
| 1556 | 1570 | ZigLLVMFnIdOverflowArithmetic, |
| 1557 | ZigLLVMFnIdFloor, | |
| 1558 | ZigLLVMFnIdCeil, | |
| 1559 | ZigLLVMFnIdSqrt, | |
| 1571 | ZigLLVMFnIdFMA, | |
| 1572 | ZigLLVMFnIdFloatOp, | |
| 1560 | 1573 | ZigLLVMFnIdBswap, |
| 1561 | 1574 | ZigLLVMFnIdBitReverse, |
| 1562 | 1575 | }; |
| ... | ... | @@ -1583,7 +1596,9 @@ struct ZigLLVMFnKey { |
| 1583 | 1596 | uint32_t bit_count; |
| 1584 | 1597 | } pop_count; |
| 1585 | 1598 | struct { |
| 1599 | BuiltinFnId op; | |
| 1586 | 1600 | uint32_t bit_count; |
| 1601 | uint32_t vector_len; // 0 means not a vector | |
| 1587 | 1602 | } floating; |
| 1588 | 1603 | struct { |
| 1589 | 1604 | AddSubMul add_sub_mul; |
| ... | ... | @@ -2235,6 +2250,8 @@ enum IrInstructionId { |
| 2235 | 2250 | IrInstructionIdHandle, |
| 2236 | 2251 | IrInstructionIdAlignOf, |
| 2237 | 2252 | IrInstructionIdOverflowOp, |
| 2253 | IrInstructionIdMulAdd, | |
| 2254 | IrInstructionIdFloatOp, | |
| 2238 | 2255 | IrInstructionIdTestErr, |
| 2239 | 2256 | IrInstructionIdUnwrapErrCode, |
| 2240 | 2257 | IrInstructionIdUnwrapErrPayload, |
| ... | ... | @@ -2296,7 +2313,6 @@ enum IrInstructionId { |
| 2296 | 2313 | IrInstructionIdAddImplicitReturnType, |
| 2297 | 2314 | IrInstructionIdMergeErrRetTraces, |
| 2298 | 2315 | IrInstructionIdMarkErrRetTracePtr, |
| 2299 | IrInstructionIdSqrt, | |
| 2300 | 2316 | IrInstructionIdErrSetCast, |
| 2301 | 2317 | IrInstructionIdToBytes, |
| 2302 | 2318 | IrInstructionIdFromBytes, |
| ... | ... | @@ -3038,6 +3054,15 @@ struct IrInstructionOverflowOp { |
| 3038 | 3054 | ZigType *result_ptr_type; |
| 3039 | 3055 | }; |
| 3040 | 3056 | |
| 3057 | struct IrInstructionMulAdd { | |
| 3058 | IrInstruction base; | |
| 3059 | ||
| 3060 | IrInstruction *type_value; | |
| 3061 | IrInstruction *op1; | |
| 3062 | IrInstruction *op2; | |
| 3063 | IrInstruction *op3; | |
| 3064 | }; | |
| 3065 | ||
| 3041 | 3066 | struct IrInstructionAlignOf { |
| 3042 | 3067 | IrInstruction base; |
| 3043 | 3068 | |
| ... | ... | @@ -3461,11 +3486,13 @@ struct IrInstructionMarkErrRetTracePtr { |
| 3461 | 3486 | IrInstruction *err_ret_trace_ptr; |
| 3462 | 3487 | }; |
| 3463 | 3488 | |
| 3464 | struct IrInstructionSqrt { | |
| 3489 | // For float ops which take a single argument | |
| 3490 | struct IrInstructionFloatOp { | |
| 3465 | 3491 | IrInstruction base; |
| 3466 | 3492 | |
| 3493 | BuiltinFnId op; | |
| 3467 | 3494 | IrInstruction *type; |
| 3468 | IrInstruction *op; | |
| 3495 | IrInstruction *op1; | |
| 3469 | 3496 | }; |
| 3470 | 3497 | |
| 3471 | 3498 | struct IrInstructionCheckRuntimeScope { |
src/analyze.cpp+14-10| ... | ... | @@ -5736,12 +5736,13 @@ 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 | return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168; | |
| 5741 | case ZigLLVMFnIdCeil: | |
| 5742 | return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089; | |
| 5743 | case ZigLLVMFnIdSqrt: | |
| 5744 | return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385; | |
| 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; | |
| 5743 | case ZigLLVMFnIdFMA: | |
| 5744 | return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) + | |
| 5745 | (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025); | |
| 5745 | 5746 | case ZigLLVMFnIdBswap: |
| 5746 | 5747 | return (uint32_t)(x.data.bswap.bit_count) * (uint32_t)3661994335; |
| 5747 | 5748 | case ZigLLVMFnIdBitReverse: |
| ... | ... | @@ -5769,10 +5770,13 @@ 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; | |
| 5777 | case ZigLLVMFnIdFMA: | |
| 5778 | return a.data.floating.bit_count == b.data.floating.bit_count && | |
| 5779 | a.data.floating.vector_len == b.data.floating.vector_len; | |
| 5776 | 5780 | case ZigLLVMFnIdOverflowArithmetic: |
| 5777 | 5781 | return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && |
| 5778 | 5782 | (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) && |
src/codegen.cpp+73-31| ... | ... | @@ -806,32 +806,47 @@ 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) { | |
| 810 | assert(type_entry->id == ZigTypeIdFloat); | |
| 809 | static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) { | |
| 810 | assert(type_entry->id == ZigTypeIdFloat || | |
| 811 | type_entry->id == ZigTypeIdVector); | |
| 812 | ||
| 813 | bool is_vector = (type_entry->id == ZigTypeIdVector); | |
| 814 | ZigType *float_type = is_vector ? type_entry->data.vector.elem_type : type_entry; | |
| 811 | 815 | |
| 812 | 816 | ZigLLVMFnKey key = {}; |
| 813 | 817 | key.id = fn_id; |
| 814 | key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count; | |
| 818 | key.data.floating.bit_count = (uint32_t)float_type->data.floating.bit_count; | |
| 819 | key.data.floating.vector_len = is_vector ? (uint32_t)type_entry->data.vector.len : 0; | |
| 820 | key.data.floating.op = op; | |
| 815 | 821 | |
| 816 | 822 | auto existing_entry = g->llvm_fn_table.maybe_get(key); |
| 817 | 823 | if (existing_entry) |
| 818 | 824 | return existing_entry->value; |
| 819 | 825 | |
| 820 | 826 | const char *name; |
| 821 | if (fn_id == ZigLLVMFnIdFloor) { | |
| 822 | name = "floor"; | |
| 823 | } else if (fn_id == ZigLLVMFnIdCeil) { | |
| 824 | name = "ceil"; | |
| 825 | } else if (fn_id == ZigLLVMFnIdSqrt) { | |
| 826 | name = "sqrt"; | |
| 827 | uint32_t num_args; | |
| 828 | if (fn_id == ZigLLVMFnIdFMA) { | |
| 829 | name = "fma"; | |
| 830 | num_args = 3; | |
| 831 | } else if (fn_id == ZigLLVMFnIdFloatOp) { | |
| 832 | name = float_op_to_name(op, true); | |
| 833 | num_args = 1; | |
| 827 | 834 | } else { |
| 828 | 835 | zig_unreachable(); |
| 829 | 836 | } |
| 830 | 837 | |
| 831 | 838 | char fn_name[64]; |
| 832 | sprintf(fn_name, "llvm.%s.f%" ZIG_PRI_usize "", name, type_entry->data.floating.bit_count); | |
| 839 | if (is_vector) | |
| 840 | sprintf(fn_name, "llvm.%s.v%" PRIu32 "f%" PRIu32, name, key.data.floating.vector_len, key.data.floating.bit_count); | |
| 841 | else | |
| 842 | sprintf(fn_name, "llvm.%s.f%" PRIu32, name, key.data.floating.bit_count); | |
| 833 | 843 | LLVMTypeRef float_type_ref = get_llvm_type(g, type_entry); |
| 834 | LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, &float_type_ref, 1, false); | |
| 844 | LLVMTypeRef return_elem_types[3] = { | |
| 845 | float_type_ref, | |
| 846 | float_type_ref, | |
| 847 | float_type_ref, | |
| 848 | }; | |
| 849 | LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, return_elem_types, num_args, false); | |
| 835 | 850 | LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); |
| 836 | 851 | assert(LLVMGetIntrinsicID(fn_val)); |
| 837 | 852 | |
| ... | ... | @@ -2460,22 +2475,17 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2460 | 2475 | return result; |
| 2461 | 2476 | } |
| 2462 | 2477 | |
| 2463 | static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, ZigType *type_entry) { | |
| 2464 | 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) | |
| 2465 | 2482 | return val; |
| 2483 | assert(type_entry->id == ZigTypeIdFloat); | |
| 2466 | 2484 | |
| 2467 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor); | |
| 2485 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); | |
| 2468 | 2486 | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); |
| 2469 | 2487 | } |
| 2470 | 2488 | |
| 2471 | static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, ZigType *type_entry) { | |
| 2472 | if (type_entry->id == ZigTypeIdInt) | |
| 2473 | return val; | |
| 2474 | ||
| 2475 | LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil); | |
| 2476 | return LLVMBuildCall(g->builder, ceil_fn, &val, 1, ""); | |
| 2477 | } | |
| 2478 | ||
| 2479 | 2489 | enum DivKind { |
| 2480 | 2490 | DivKindFloat, |
| 2481 | 2491 | DivKindTrunc, |
| ... | ... | @@ -2551,7 +2561,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2551 | 2561 | return result; |
| 2552 | 2562 | case DivKindExact: |
| 2553 | 2563 | if (want_runtime_safety) { |
| 2554 | LLVMValueRef floored = gen_floor(g, result, type_entry); | |
| 2564 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2555 | 2565 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2556 | 2566 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2557 | 2567 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); |
| ... | ... | @@ -2573,12 +2583,12 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2573 | 2583 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2574 | 2584 | |
| 2575 | 2585 | LLVMPositionBuilderAtEnd(g->builder, ltz_block); |
| 2576 | LLVMValueRef ceiled = gen_ceil(g, result, type_entry); | |
| 2586 | LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil); | |
| 2577 | 2587 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); |
| 2578 | 2588 | LLVMBuildBr(g->builder, end_block); |
| 2579 | 2589 | |
| 2580 | 2590 | LLVMPositionBuilderAtEnd(g->builder, gez_block); |
| 2581 | LLVMValueRef floored = gen_floor(g, result, type_entry); | |
| 2591 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2582 | 2592 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); |
| 2583 | 2593 | LLVMBuildBr(g->builder, end_block); |
| 2584 | 2594 | |
| ... | ... | @@ -2590,7 +2600,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2590 | 2600 | return phi; |
| 2591 | 2601 | } |
| 2592 | 2602 | case DivKindFloor: |
| 2593 | return gen_floor(g, result, type_entry); | |
| 2603 | return gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2594 | 2604 | } |
| 2595 | 2605 | zig_unreachable(); |
| 2596 | 2606 | } |
| ... | ... | @@ -5430,13 +5440,28 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e |
| 5430 | 5440 | return nullptr; |
| 5431 | 5441 | } |
| 5432 | 5442 | |
| 5433 | static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) { | |
| 5434 | 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); | |
| 5435 | 5445 | assert(instruction->base.value.type->id == ZigTypeIdFloat); |
| 5436 | 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); | |
| 5437 | 5447 | return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); |
| 5438 | 5448 | } |
| 5439 | 5449 | |
| 5450 | static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrInstructionMulAdd *instruction) { | |
| 5451 | LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); | |
| 5452 | LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); | |
| 5453 | LLVMValueRef op3 = ir_llvm_value(g, instruction->op3); | |
| 5454 | assert(instruction->base.value.type->id == ZigTypeIdFloat || | |
| 5455 | instruction->base.value.type->id == ZigTypeIdVector); | |
| 5456 | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd); | |
| 5457 | LLVMValueRef args[3] = { | |
| 5458 | op1, | |
| 5459 | op2, | |
| 5460 | op3, | |
| 5461 | }; | |
| 5462 | return LLVMBuildCall(g->builder, fn_val, args, 3, ""); | |
| 5463 | } | |
| 5464 | ||
| 5440 | 5465 | static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { |
| 5441 | 5466 | LLVMValueRef op = ir_llvm_value(g, instruction->op); |
| 5442 | 5467 | ZigType *int_type = instruction->base.value.type; |
| ... | ... | @@ -5779,8 +5804,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5779 | 5804 | return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction); |
| 5780 | 5805 | case IrInstructionIdMarkErrRetTracePtr: |
| 5781 | 5806 | return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 5782 | case IrInstructionIdSqrt: | |
| 5783 | return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); | |
| 5807 | case IrInstructionIdFloatOp: | |
| 5808 | return ir_render_float_op(g, executable, (IrInstructionFloatOp *)instruction); | |
| 5809 | case IrInstructionIdMulAdd: | |
| 5810 | return ir_render_mul_add(g, executable, (IrInstructionMulAdd *)instruction); | |
| 5784 | 5811 | case IrInstructionIdArrayToVector: |
| 5785 | 5812 | return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction); |
| 5786 | 5813 | case IrInstructionIdVectorToArray: |
| ... | ... | @@ -7398,6 +7425,21 @@ static void define_builtin_fns(CodeGen *g) { |
| 7398 | 7425 | create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); |
| 7399 | 7426 | create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); |
| 7400 | 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); | |
| 7442 | create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); | |
| 7401 | 7443 | create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); |
| 7402 | 7444 | create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); |
| 7403 | 7445 | create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); |
src/ir.cpp+465-61| ... | ... | @@ -747,6 +747,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErr *) { |
| 747 | 747 | return IrInstructionIdTestErr; |
| 748 | 748 | } |
| 749 | 749 | |
| 750 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMulAdd *) { | |
| 751 | return IrInstructionIdMulAdd; | |
| 752 | } | |
| 753 | ||
| 750 | 754 | static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) { |
| 751 | 755 | return IrInstructionIdUnwrapErrCode; |
| 752 | 756 | } |
| ... | ... | @@ -987,8 +991,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP |
| 987 | 991 | return IrInstructionIdMarkErrRetTracePtr; |
| 988 | 992 | } |
| 989 | 993 | |
| 990 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { | |
| 991 | return IrInstructionIdSqrt; | |
| 994 | static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) { | |
| 995 | return IrInstructionIdFloatOp; | |
| 992 | 996 | } |
| 993 | 997 | |
| 994 | 998 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) { |
| ... | ... | @@ -2308,6 +2312,75 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode |
| 2308 | 2312 | return &instruction->base; |
| 2309 | 2313 | } |
| 2310 | 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 | ||
| 2368 | static IrInstruction *ir_build_mul_add(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 2369 | IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *op3) { | |
| 2370 | IrInstructionMulAdd *instruction = ir_build_instruction<IrInstructionMulAdd>(irb, scope, source_node); | |
| 2371 | instruction->type_value = type_value; | |
| 2372 | instruction->op1 = op1; | |
| 2373 | instruction->op2 = op2; | |
| 2374 | instruction->op3 = op3; | |
| 2375 | ||
| 2376 | ir_ref_instruction(type_value, irb->current_basic_block); | |
| 2377 | ir_ref_instruction(op1, irb->current_basic_block); | |
| 2378 | ir_ref_instruction(op2, irb->current_basic_block); | |
| 2379 | ir_ref_instruction(op3, irb->current_basic_block); | |
| 2380 | ||
| 2381 | return &instruction->base; | |
| 2382 | } | |
| 2383 | ||
| 2311 | 2384 | static IrInstruction *ir_build_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { |
| 2312 | 2385 | IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node); |
| 2313 | 2386 | instruction->type_value = type_value; |
| ... | ... | @@ -3013,17 +3086,6 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco |
| 3013 | 3086 | return &instruction->base; |
| 3014 | 3087 | } |
| 3015 | 3088 | |
| 3016 | static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) { | |
| 3017 | IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node); | |
| 3018 | instruction->type = type; | |
| 3019 | instruction->op = op; | |
| 3020 | ||
| 3021 | if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); | |
| 3022 | ir_ref_instruction(op, irb->current_basic_block); | |
| 3023 | ||
| 3024 | return &instruction->base; | |
| 3025 | } | |
| 3026 | ||
| 3027 | 3089 | static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 3028 | 3090 | IrInstruction *container, IrInstruction *name) |
| 3029 | 3091 | { |
| ... | ... | @@ -4028,6 +4090,33 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * |
| 4028 | 4090 | return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); |
| 4029 | 4091 | } |
| 4030 | 4092 | |
| 4093 | static IrInstruction *ir_gen_mul_add(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 4094 | assert(node->type == NodeTypeFnCallExpr); | |
| 4095 | ||
| 4096 | AstNode *type_node = node->data.fn_call_expr.params.at(0); | |
| 4097 | AstNode *op1_node = node->data.fn_call_expr.params.at(1); | |
| 4098 | AstNode *op2_node = node->data.fn_call_expr.params.at(2); | |
| 4099 | AstNode *op3_node = node->data.fn_call_expr.params.at(3); | |
| 4100 | ||
| 4101 | IrInstruction *type_value = ir_gen_node(irb, type_node, scope); | |
| 4102 | if (type_value == irb->codegen->invalid_instruction) | |
| 4103 | return irb->codegen->invalid_instruction; | |
| 4104 | ||
| 4105 | IrInstruction *op1 = ir_gen_node(irb, op1_node, scope); | |
| 4106 | if (op1 == irb->codegen->invalid_instruction) | |
| 4107 | return irb->codegen->invalid_instruction; | |
| 4108 | ||
| 4109 | IrInstruction *op2 = ir_gen_node(irb, op2_node, scope); | |
| 4110 | if (op2 == irb->codegen->invalid_instruction) | |
| 4111 | return irb->codegen->invalid_instruction; | |
| 4112 | ||
| 4113 | IrInstruction *op3 = ir_gen_node(irb, op3_node, scope); | |
| 4114 | if (op3 == irb->codegen->invalid_instruction) | |
| 4115 | return irb->codegen->invalid_instruction; | |
| 4116 | ||
| 4117 | return ir_build_mul_add(irb, scope, node, type_value, op1, op2, op3); | |
| 4118 | } | |
| 4119 | ||
| 4031 | 4120 | static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) { |
| 4032 | 4121 | for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) { |
| 4033 | 4122 | if (it_scope->id == ScopeIdDecls) { |
| ... | ... | @@ -4353,6 +4442,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4353 | 4442 | return ir_lval_wrap(irb, scope, bin_op, lval); |
| 4354 | 4443 | } |
| 4355 | 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: | |
| 4356 | 4458 | { |
| 4357 | 4459 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| 4358 | 4460 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); |
| ... | ... | @@ -4364,7 +4466,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4364 | 4466 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4365 | 4467 | return arg1_value; |
| 4366 | 4468 | |
| 4367 | 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); | |
| 4368 | 4470 | return ir_lval_wrap(irb, scope, ir_sqrt, lval); |
| 4369 | 4471 | } |
| 4370 | 4472 | case BuiltinFnIdTruncate: |
| ... | ... | @@ -4687,6 +4789,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4687 | 4789 | return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval); |
| 4688 | 4790 | case BuiltinFnIdShlWithOverflow: |
| 4689 | 4791 | return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval); |
| 4792 | case BuiltinFnIdMulAdd: | |
| 4793 | return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval); | |
| 4690 | 4794 | case BuiltinFnIdTypeName: |
| 4691 | 4795 | { |
| 4692 | 4796 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -21187,6 +21291,125 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr |
| 21187 | 21291 | return result; |
| 21188 | 21292 | } |
| 21189 | 21293 | |
| 21294 | static void ir_eval_mul_add(IrAnalyze *ira, IrInstructionMulAdd *source_instr, ZigType *float_type, | |
| 21295 | ConstExprValue *op1, ConstExprValue *op2, ConstExprValue *op3, ConstExprValue *out_val) { | |
| 21296 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 21297 | f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value, | |
| 21298 | &op3->data.x_bigfloat.value); | |
| 21299 | } else if (float_type->id == ZigTypeIdFloat) { | |
| 21300 | switch (float_type->data.floating.bit_count) { | |
| 21301 | case 16: | |
| 21302 | out_val->data.x_f16 = f16_mulAdd(op1->data.x_f16, op2->data.x_f16, op3->data.x_f16); | |
| 21303 | break; | |
| 21304 | case 32: | |
| 21305 | out_val->data.x_f32 = fmaf(op1->data.x_f32, op2->data.x_f32, op3->data.x_f32); | |
| 21306 | break; | |
| 21307 | case 64: | |
| 21308 | out_val->data.x_f64 = fma(op1->data.x_f64, op2->data.x_f64, op3->data.x_f64); | |
| 21309 | break; | |
| 21310 | case 128: | |
| 21311 | f128M_mulAdd(&op1->data.x_f128, &op2->data.x_f128, &op3->data.x_f128, &out_val->data.x_f128); | |
| 21312 | break; | |
| 21313 | default: | |
| 21314 | zig_unreachable(); | |
| 21315 | } | |
| 21316 | } else { | |
| 21317 | zig_unreachable(); | |
| 21318 | } | |
| 21319 | } | |
| 21320 | ||
| 21321 | static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructionMulAdd *instruction) { | |
| 21322 | IrInstruction *type_value = instruction->type_value->child; | |
| 21323 | if (type_is_invalid(type_value->value.type)) | |
| 21324 | return ira->codegen->invalid_instruction; | |
| 21325 | ||
| 21326 | ZigType *expr_type = ir_resolve_type(ira, type_value); | |
| 21327 | if (type_is_invalid(expr_type)) | |
| 21328 | return ira->codegen->invalid_instruction; | |
| 21329 | ||
| 21330 | // Only allow float types, and vectors of floats. | |
| 21331 | ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; | |
| 21332 | if (float_type->id != ZigTypeIdFloat) { | |
| 21333 | ir_add_error(ira, type_value, | |
| 21334 | buf_sprintf("expected float or vector of float type, found '%s'", buf_ptr(&float_type->name))); | |
| 21335 | return ira->codegen->invalid_instruction; | |
| 21336 | } | |
| 21337 | ||
| 21338 | IrInstruction *op1 = instruction->op1->child; | |
| 21339 | if (type_is_invalid(op1->value.type)) | |
| 21340 | return ira->codegen->invalid_instruction; | |
| 21341 | ||
| 21342 | IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, expr_type); | |
| 21343 | if (type_is_invalid(casted_op1->value.type)) | |
| 21344 | return ira->codegen->invalid_instruction; | |
| 21345 | ||
| 21346 | IrInstruction *op2 = instruction->op2->child; | |
| 21347 | if (type_is_invalid(op2->value.type)) | |
| 21348 | return ira->codegen->invalid_instruction; | |
| 21349 | ||
| 21350 | IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, expr_type); | |
| 21351 | if (type_is_invalid(casted_op2->value.type)) | |
| 21352 | return ira->codegen->invalid_instruction; | |
| 21353 | ||
| 21354 | IrInstruction *op3 = instruction->op3->child; | |
| 21355 | if (type_is_invalid(op3->value.type)) | |
| 21356 | return ira->codegen->invalid_instruction; | |
| 21357 | ||
| 21358 | IrInstruction *casted_op3 = ir_implicit_cast(ira, op3, expr_type); | |
| 21359 | if (type_is_invalid(casted_op3->value.type)) | |
| 21360 | return ira->codegen->invalid_instruction; | |
| 21361 | ||
| 21362 | if (instr_is_comptime(casted_op1) && | |
| 21363 | instr_is_comptime(casted_op2) && | |
| 21364 | instr_is_comptime(casted_op3)) { | |
| 21365 | ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); | |
| 21366 | if (!op1_const) | |
| 21367 | return ira->codegen->invalid_instruction; | |
| 21368 | ConstExprValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad); | |
| 21369 | if (!op2_const) | |
| 21370 | return ira->codegen->invalid_instruction; | |
| 21371 | ConstExprValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad); | |
| 21372 | if (!op3_const) | |
| 21373 | return ira->codegen->invalid_instruction; | |
| 21374 | ||
| 21375 | IrInstruction *result = ir_const(ira, &instruction->base, expr_type); | |
| 21376 | ConstExprValue *out_val = &result->value; | |
| 21377 | ||
| 21378 | if (expr_type->id == ZigTypeIdVector) { | |
| 21379 | expand_undef_array(ira->codegen, op1_const); | |
| 21380 | expand_undef_array(ira->codegen, op2_const); | |
| 21381 | expand_undef_array(ira->codegen, op3_const); | |
| 21382 | out_val->special = ConstValSpecialUndef; | |
| 21383 | expand_undef_array(ira->codegen, out_val); | |
| 21384 | size_t len = expr_type->data.vector.len; | |
| 21385 | for (size_t i = 0; i < len; i += 1) { | |
| 21386 | ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; | |
| 21387 | ConstExprValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i]; | |
| 21388 | ConstExprValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i]; | |
| 21389 | ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; | |
| 21390 | assert(float_operand_op1->type == float_type); | |
| 21391 | assert(float_operand_op2->type == float_type); | |
| 21392 | assert(float_operand_op3->type == float_type); | |
| 21393 | assert(float_out_val->type == float_type); | |
| 21394 | ir_eval_mul_add(ira, instruction, float_type, | |
| 21395 | op1_const, op2_const, op3_const, float_out_val); | |
| 21396 | float_out_val->type = float_type; | |
| 21397 | } | |
| 21398 | out_val->type = expr_type; | |
| 21399 | out_val->special = ConstValSpecialStatic; | |
| 21400 | } else { | |
| 21401 | ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, out_val); | |
| 21402 | } | |
| 21403 | return result; | |
| 21404 | } | |
| 21405 | ||
| 21406 | IrInstruction *result = ir_build_mul_add(&ira->new_irb, | |
| 21407 | instruction->base.scope, instruction->base.source_node, | |
| 21408 | type_value, casted_op1, casted_op2, casted_op3); | |
| 21409 | result->value.type = expr_type; | |
| 21410 | return result; | |
| 21411 | } | |
| 21412 | ||
| 21190 | 21413 | static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErr *instruction) { |
| 21191 | 21414 | IrInstruction *value = instruction->value->child; |
| 21192 | 21415 | if (type_is_invalid(value->value.type)) |
| ... | ... | @@ -23048,70 +23271,248 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i |
| 23048 | 23271 | return result; |
| 23049 | 23272 | } |
| 23050 | 23273 | |
| 23051 | static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { | |
| 23052 | ZigType *float_type = ir_resolve_type(ira, instruction->type->child); | |
| 23053 | if (type_is_invalid(float_type)) | |
| 23054 | return ira->codegen->invalid_instruction; | |
| 23274 | static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type, | |
| 23275 | ConstExprValue *op, ConstExprValue *out_val) { | |
| 23276 | assert(ira && source_instr && float_type && out_val && op); | |
| 23277 | assert(float_type->id == ZigTypeIdFloat || | |
| 23278 | float_type->id == ZigTypeIdComptimeFloat); | |
| 23055 | 23279 | |
| 23056 | IrInstruction *op = instruction->op->child; | |
| 23057 | if (type_is_invalid(op->value.type)) | |
| 23280 | BuiltinFnId fop = source_instr->op; | |
| 23281 | unsigned bits; | |
| 23282 | ||
| 23283 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 23284 | bits = 128; | |
| 23285 | } else if (float_type->id == ZigTypeIdFloat) | |
| 23286 | bits = float_type->data.floating.bit_count; | |
| 23287 | ||
| 23288 | switch (bits) { | |
| 23289 | case 16: { | |
| 23290 | switch (fop) { | |
| 23291 | case BuiltinFnIdSqrt: | |
| 23292 | out_val->data.x_f16 = f16_sqrt(op->data.x_f16); | |
| 23293 | break; | |
| 23294 | case BuiltinFnIdSin: | |
| 23295 | case BuiltinFnIdCos: | |
| 23296 | case BuiltinFnIdExp: | |
| 23297 | case BuiltinFnIdExp2: | |
| 23298 | case BuiltinFnIdLn: | |
| 23299 | case BuiltinFnIdLog10: | |
| 23300 | case BuiltinFnIdLog2: | |
| 23301 | case BuiltinFnIdFabs: | |
| 23302 | case BuiltinFnIdFloor: | |
| 23303 | case BuiltinFnIdCeil: | |
| 23304 | case BuiltinFnIdTrunc: | |
| 23305 | case BuiltinFnIdNearbyInt: | |
| 23306 | case BuiltinFnIdRound: | |
| 23307 | zig_panic("unimplemented f16 builtin"); | |
| 23308 | default: | |
| 23309 | zig_unreachable(); | |
| 23310 | }; | |
| 23311 | break; | |
| 23312 | }; | |
| 23313 | case 32: { | |
| 23314 | switch (fop) { | |
| 23315 | case BuiltinFnIdSqrt: | |
| 23316 | out_val->data.x_f32 = sqrtf(op->data.x_f32); | |
| 23317 | break; | |
| 23318 | case BuiltinFnIdSin: | |
| 23319 | out_val->data.x_f32 = sinf(op->data.x_f32); | |
| 23320 | break; | |
| 23321 | case BuiltinFnIdCos: | |
| 23322 | out_val->data.x_f32 = cosf(op->data.x_f32); | |
| 23323 | break; | |
| 23324 | case BuiltinFnIdExp: | |
| 23325 | out_val->data.x_f32 = expf(op->data.x_f32); | |
| 23326 | break; | |
| 23327 | case BuiltinFnIdExp2: | |
| 23328 | out_val->data.x_f32 = exp2f(op->data.x_f32); | |
| 23329 | break; | |
| 23330 | case BuiltinFnIdLn: | |
| 23331 | out_val->data.x_f32 = logf(op->data.x_f32); | |
| 23332 | break; | |
| 23333 | case BuiltinFnIdLog10: | |
| 23334 | out_val->data.x_f32 = log10f(op->data.x_f32); | |
| 23335 | break; | |
| 23336 | case BuiltinFnIdLog2: | |
| 23337 | out_val->data.x_f32 = log2f(op->data.x_f32); | |
| 23338 | break; | |
| 23339 | case BuiltinFnIdFabs: | |
| 23340 | out_val->data.x_f32 = fabsf(op->data.x_f32); | |
| 23341 | break; | |
| 23342 | case BuiltinFnIdFloor: | |
| 23343 | out_val->data.x_f32 = floorf(op->data.x_f32); | |
| 23344 | break; | |
| 23345 | case BuiltinFnIdCeil: | |
| 23346 | out_val->data.x_f32 = ceilf(op->data.x_f32); | |
| 23347 | break; | |
| 23348 | case BuiltinFnIdTrunc: | |
| 23349 | out_val->data.x_f32 = truncf(op->data.x_f32); | |
| 23350 | break; | |
| 23351 | case BuiltinFnIdNearbyInt: | |
| 23352 | out_val->data.x_f32 = nearbyintf(op->data.x_f32); | |
| 23353 | break; | |
| 23354 | case BuiltinFnIdRound: | |
| 23355 | out_val->data.x_f32 = roundf(op->data.x_f32); | |
| 23356 | break; | |
| 23357 | default: | |
| 23358 | zig_unreachable(); | |
| 23359 | }; | |
| 23360 | break; | |
| 23361 | }; | |
| 23362 | case 64: { | |
| 23363 | switch (fop) { | |
| 23364 | case BuiltinFnIdSqrt: | |
| 23365 | out_val->data.x_f64 = sqrt(op->data.x_f64); | |
| 23366 | break; | |
| 23367 | case BuiltinFnIdSin: | |
| 23368 | out_val->data.x_f64 = sin(op->data.x_f64); | |
| 23369 | break; | |
| 23370 | case BuiltinFnIdCos: | |
| 23371 | out_val->data.x_f64 = cos(op->data.x_f64); | |
| 23372 | break; | |
| 23373 | case BuiltinFnIdExp: | |
| 23374 | out_val->data.x_f64 = exp(op->data.x_f64); | |
| 23375 | break; | |
| 23376 | case BuiltinFnIdExp2: | |
| 23377 | out_val->data.x_f64 = exp2(op->data.x_f64); | |
| 23378 | break; | |
| 23379 | case BuiltinFnIdLn: | |
| 23380 | out_val->data.x_f64 = log(op->data.x_f64); | |
| 23381 | break; | |
| 23382 | case BuiltinFnIdLog10: | |
| 23383 | out_val->data.x_f64 = log10(op->data.x_f64); | |
| 23384 | break; | |
| 23385 | case BuiltinFnIdLog2: | |
| 23386 | out_val->data.x_f64 = log2(op->data.x_f64); | |
| 23387 | break; | |
| 23388 | case BuiltinFnIdFabs: | |
| 23389 | out_val->data.x_f64 = fabs(op->data.x_f64); | |
| 23390 | break; | |
| 23391 | case BuiltinFnIdFloor: | |
| 23392 | out_val->data.x_f64 = floor(op->data.x_f64); | |
| 23393 | break; | |
| 23394 | case BuiltinFnIdCeil: | |
| 23395 | out_val->data.x_f64 = ceil(op->data.x_f64); | |
| 23396 | break; | |
| 23397 | case BuiltinFnIdTrunc: | |
| 23398 | out_val->data.x_f64 = trunc(op->data.x_f64); | |
| 23399 | break; | |
| 23400 | case BuiltinFnIdNearbyInt: | |
| 23401 | out_val->data.x_f64 = nearbyint(op->data.x_f64); | |
| 23402 | break; | |
| 23403 | case BuiltinFnIdRound: | |
| 23404 | out_val->data.x_f64 = round(op->data.x_f64); | |
| 23405 | break; | |
| 23406 | default: | |
| 23407 | zig_unreachable(); | |
| 23408 | } | |
| 23409 | break; | |
| 23410 | }; | |
| 23411 | case 128: { | |
| 23412 | float128_t *out, *in; | |
| 23413 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 23414 | out = &out_val->data.x_bigfloat.value; | |
| 23415 | in = &op->data.x_bigfloat.value; | |
| 23416 | } else { | |
| 23417 | out = &out_val->data.x_f128; | |
| 23418 | in = &op->data.x_f128; | |
| 23419 | } | |
| 23420 | switch (fop) { | |
| 23421 | case BuiltinFnIdSqrt: | |
| 23422 | f128M_sqrt(in, out); | |
| 23423 | break; | |
| 23424 | case BuiltinFnIdNearbyInt: | |
| 23425 | case BuiltinFnIdSin: | |
| 23426 | case BuiltinFnIdCos: | |
| 23427 | case BuiltinFnIdExp: | |
| 23428 | case BuiltinFnIdExp2: | |
| 23429 | case BuiltinFnIdLn: | |
| 23430 | case BuiltinFnIdLog10: | |
| 23431 | case BuiltinFnIdLog2: | |
| 23432 | case BuiltinFnIdFabs: | |
| 23433 | case BuiltinFnIdFloor: | |
| 23434 | case BuiltinFnIdCeil: | |
| 23435 | case BuiltinFnIdTrunc: | |
| 23436 | case BuiltinFnIdRound: | |
| 23437 | zig_panic("unimplemented f128 builtin"); | |
| 23438 | default: | |
| 23439 | zig_unreachable(); | |
| 23440 | } | |
| 23441 | break; | |
| 23442 | }; | |
| 23443 | default: | |
| 23444 | zig_unreachable(); | |
| 23445 | } | |
| 23446 | } | |
| 23447 | ||
| 23448 | static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) { | |
| 23449 | IrInstruction *type = instruction->type->child; | |
| 23450 | if (type_is_invalid(type->value.type)) | |
| 23451 | return ira->codegen->invalid_instruction; | |
| 23452 | ||
| 23453 | ZigType *expr_type = ir_resolve_type(ira, type); | |
| 23454 | if (type_is_invalid(expr_type)) | |
| 23058 | 23455 | return ira->codegen->invalid_instruction; |
| 23059 | 23456 | |
| 23060 | bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat; | |
| 23061 | if (!ok_type) { | |
| 23062 | ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); | |
| 23457 | // Only allow float types, and vectors of floats. | |
| 23458 | ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; | |
| 23459 | if (float_type->id != ZigTypeIdFloat && float_type->id != ZigTypeIdComptimeFloat) { | |
| 23460 | 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))); | |
| 23063 | 23461 | return ira->codegen->invalid_instruction; |
| 23064 | 23462 | } |
| 23065 | 23463 | |
| 23066 | IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type); | |
| 23067 | if (type_is_invalid(casted_op->value.type)) | |
| 23464 | IrInstruction *op1 = instruction->op1->child; | |
| 23465 | if (type_is_invalid(op1->value.type)) | |
| 23068 | 23466 | return ira->codegen->invalid_instruction; |
| 23069 | 23467 | |
| 23070 | if (instr_is_comptime(casted_op)) { | |
| 23071 | ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); | |
| 23072 | if (!val) | |
| 23468 | IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type); | |
| 23469 | if (type_is_invalid(casted_op1->value.type)) | |
| 23470 | return ira->codegen->invalid_instruction; | |
| 23471 | ||
| 23472 | if (instr_is_comptime(casted_op1)) { | |
| 23473 | // Our comptime 16-bit and 128-bit support is quite limited. | |
| 23474 | if ((float_type->id == ZigTypeIdComptimeFloat || | |
| 23475 | float_type->data.floating.bit_count == 16 || | |
| 23476 | float_type->data.floating.bit_count == 128) && | |
| 23477 | instruction->op != BuiltinFnIdSqrt) { | |
| 23478 | 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))); | |
| 23479 | return ira->codegen->invalid_instruction; | |
| 23480 | } | |
| 23481 | ||
| 23482 | ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); | |
| 23483 | if (!op1_const) | |
| 23073 | 23484 | return ira->codegen->invalid_instruction; |
| 23074 | 23485 | |
| 23075 | IrInstruction *result = ir_const(ira, &instruction->base, float_type); | |
| 23486 | IrInstruction *result = ir_const(ira, &instruction->base, expr_type); | |
| 23076 | 23487 | ConstExprValue *out_val = &result->value; |
| 23077 | 23488 | |
| 23078 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 23079 | bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); | |
| 23080 | } else if (float_type->id == ZigTypeIdFloat) { | |
| 23081 | switch (float_type->data.floating.bit_count) { | |
| 23082 | case 16: | |
| 23083 | out_val->data.x_f16 = f16_sqrt(val->data.x_f16); | |
| 23084 | break; | |
| 23085 | case 32: | |
| 23086 | out_val->data.x_f32 = sqrtf(val->data.x_f32); | |
| 23087 | break; | |
| 23088 | case 64: | |
| 23089 | out_val->data.x_f64 = sqrt(val->data.x_f64); | |
| 23090 | break; | |
| 23091 | case 128: | |
| 23092 | f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128); | |
| 23093 | break; | |
| 23094 | default: | |
| 23095 | zig_unreachable(); | |
| 23489 | if (expr_type->id == ZigTypeIdVector) { | |
| 23490 | expand_undef_array(ira->codegen, op1_const); | |
| 23491 | out_val->special = ConstValSpecialUndef; | |
| 23492 | expand_undef_array(ira->codegen, out_val); | |
| 23493 | size_t len = expr_type->data.vector.len; | |
| 23494 | for (size_t i = 0; i < len; i += 1) { | |
| 23495 | ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; | |
| 23496 | ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; | |
| 23497 | assert(float_operand_op1->type == float_type); | |
| 23498 | assert(float_out_val->type == float_type); | |
| 23499 | ir_eval_float_op(ira, instruction, float_type, | |
| 23500 | op1_const, float_out_val); | |
| 23501 | float_out_val->type = float_type; | |
| 23096 | 23502 | } |
| 23503 | out_val->type = expr_type; | |
| 23504 | out_val->special = ConstValSpecialStatic; | |
| 23097 | 23505 | } else { |
| 23098 | zig_unreachable(); | |
| 23506 | ir_eval_float_op(ira, instruction, float_type, op1_const, out_val); | |
| 23099 | 23507 | } |
| 23100 | ||
| 23101 | 23508 | return result; |
| 23102 | 23509 | } |
| 23103 | 23510 | |
| 23104 | 23511 | ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base); |
| 23105 | if (float_type->data.floating.bit_count != 16 && | |
| 23106 | float_type->data.floating.bit_count != 32 && | |
| 23107 | float_type->data.floating.bit_count != 64) { | |
| 23108 | ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); | |
| 23109 | return ira->codegen->invalid_instruction; | |
| 23110 | } | |
| 23111 | 23512 | |
| 23112 | IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope, | |
| 23113 | instruction->base.source_node, nullptr, casted_op); | |
| 23114 | result->value.type = float_type; | |
| 23513 | IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope, | |
| 23514 | instruction->base.source_node, nullptr, casted_op1, instruction->op); | |
| 23515 | result->value.type = expr_type; | |
| 23115 | 23516 | return result; |
| 23116 | 23517 | } |
| 23117 | 23518 | |
| ... | ... | @@ -23596,8 +23997,10 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio |
| 23596 | 23997 | return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); |
| 23597 | 23998 | case IrInstructionIdMarkErrRetTracePtr: |
| 23598 | 23999 | return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 23599 | case IrInstructionIdSqrt: | |
| 23600 | return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); | |
| 24000 | case IrInstructionIdFloatOp: | |
| 24001 | return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction); | |
| 24002 | case IrInstructionIdMulAdd: | |
| 24003 | return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)instruction); | |
| 23601 | 24004 | case IrInstructionIdIntToErr: |
| 23602 | 24005 | return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); |
| 23603 | 24006 | case IrInstructionIdErrToInt: |
| ... | ... | @@ -23836,7 +24239,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 23836 | 24239 | case IrInstructionIdCoroFree: |
| 23837 | 24240 | case IrInstructionIdCoroPromise: |
| 23838 | 24241 | case IrInstructionIdPromiseResultType: |
| 23839 | case IrInstructionIdSqrt: | |
| 24242 | case IrInstructionIdFloatOp: | |
| 24243 | case IrInstructionIdMulAdd: | |
| 23840 | 24244 | case IrInstructionIdAtomicLoad: |
| 23841 | 24245 | case IrInstructionIdIntCast: |
| 23842 | 24246 | case IrInstructionIdFloatCast: |
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+25-5| ... | ... | @@ -1427,15 +1427,32 @@ 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); | |
| 1440 | fprintf(irp->f, ")"); | |
| 1441 | } | |
| 1442 | ||
| 1443 | static void ir_print_mul_add(IrPrint *irp, IrInstructionMulAdd *instruction) { | |
| 1444 | fprintf(irp->f, "@mulAdd("); | |
| 1445 | if (instruction->type_value != nullptr) { | |
| 1446 | ir_print_other_instruction(irp, instruction->type_value); | |
| 1447 | } else { | |
| 1448 | fprintf(irp->f, "null"); | |
| 1449 | } | |
| 1450 | fprintf(irp->f, ","); | |
| 1451 | ir_print_other_instruction(irp, instruction->op1); | |
| 1452 | fprintf(irp->f, ","); | |
| 1453 | ir_print_other_instruction(irp, instruction->op2); | |
| 1454 | fprintf(irp->f, ","); | |
| 1455 | ir_print_other_instruction(irp, instruction->op3); | |
| 1439 | 1456 | fprintf(irp->f, ")"); |
| 1440 | 1457 | } |
| 1441 | 1458 | |
| ... | ... | @@ -1902,8 +1919,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1902 | 1919 | case IrInstructionIdMarkErrRetTracePtr: |
| 1903 | 1920 | ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 1904 | 1921 | break; |
| 1905 | case IrInstructionIdSqrt: | |
| 1906 | ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); | |
| 1922 | case IrInstructionIdFloatOp: | |
| 1923 | ir_print_float_op(irp, (IrInstructionFloatOp *)instruction); | |
| 1924 | break; | |
| 1925 | case IrInstructionIdMulAdd: | |
| 1926 | ir_print_mul_add(irp, (IrInstructionMulAdd *)instruction); | |
| 1907 | 1927 | break; |
| 1908 | 1928 | case IrInstructionIdAtomicLoad: |
| 1909 | 1929 | ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); |
std/special/c.zig+26-13| ... | ... | @@ -254,19 +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 | ||
| 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);} | |
| 270 | 283 | fn generic_fmod(comptime T: type, x: T, y: T) T { |
| 271 | 284 | @setRuntimeSafety(false); |
| 272 | 285 |
test/stage1/behavior.zig+2| ... | ... | @@ -69,6 +69,8 @@ comptime { |
| 69 | 69 | _ = @import("behavior/optional.zig"); |
| 70 | 70 | _ = @import("behavior/pointers.zig"); |
| 71 | 71 | _ = @import("behavior/popcount.zig"); |
| 72 | _ = @import("behavior/muladd.zig"); | |
| 73 | _ = @import("behavior/floatop.zig"); | |
| 72 | 74 | _ = @import("behavior/ptrcast.zig"); |
| 73 | 75 | _ = @import("behavior/pub_enum.zig"); |
| 74 | 76 | _ = @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 | //} |
test/stage1/behavior/muladd.zig created+34| ... | ... | @@ -0,0 +1,34 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | ||
| 3 | test "@mulAdd" { | |
| 4 | comptime testMulAdd(); | |
| 5 | testMulAdd(); | |
| 6 | } | |
| 7 | ||
| 8 | fn testMulAdd() void { | |
| 9 | { | |
| 10 | var a: f16 = 5.5; | |
| 11 | var b: f16 = 2.5; | |
| 12 | var c: f16 = 6.25; | |
| 13 | expect(@mulAdd(f16, a, b, c) == 20); | |
| 14 | } | |
| 15 | { | |
| 16 | var a: f32 = 5.5; | |
| 17 | var b: f32 = 2.5; | |
| 18 | var c: f32 = 6.25; | |
| 19 | expect(@mulAdd(f32, a, b, c) == 20); | |
| 20 | } | |
| 21 | { | |
| 22 | var a: f64 = 5.5; | |
| 23 | var b: f64 = 2.5; | |
| 24 | var c: f64 = 6.25; | |
| 25 | expect(@mulAdd(f64, a, b, c) == 20); | |
| 26 | } | |
| 27 | // Awaits implementation in libm.zig | |
| 28 | //{ | |
| 29 | // var a: f16 = 5.5; | |
| 30 | // var b: f128 = 2.5; | |
| 31 | // var c: f128 = 6.25; | |
| 32 | // expect(@mulAdd(f128, a, b, c) == 20); | |
| 33 | //} | |
| 34 | } | |
| \ No newline at end of file |