| author | |
| committer | |
| log | 3085d29af83d07769582f751b3c2f5f36634e34b |
| tree | ab5e72656369bccb19857277707b90ce1fc78ec3 |
| parent | 5cd4753bea9e8e79bf30217b9d0b7a4485271588 |
| parent | 07c0d484eec327ca3bd1e3ce081c1ced230536a9 |
| signature | Commit is signed but in an unrecognized format. |
21 files changed, 1475 insertions(+), 507 deletions(-)
CMakeLists.txt+8-3| ... | ... | @@ -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" |
| ... | ... | @@ -6653,15 +6655,18 @@ set(OPTIMIZED_C_FLAGS "-std=c99 -O3") |
| 6653 | 6655 | set(EXE_LDFLAGS " ") |
| 6654 | 6656 | if(MSVC) |
| 6655 | 6657 | set(EXE_LDFLAGS "/STACK:16777216") |
| 6656 | elseif(ZIG_STATIC) | |
| 6658 | elseif(MINGW) | |
| 6659 | set(EXE_LDFLAGS "${EXE_LDFLAGS} -Wl,--stack,16777216") | |
| 6660 | endif() | |
| 6661 | ||
| 6662 | if(ZIG_STATIC) | |
| 6657 | 6663 | if(APPLE) |
| 6658 | 6664 | set(EXE_LDFLAGS "-static-libgcc -static-libstdc++") |
| 6659 | 6665 | else() |
| 6660 | 6666 | set(EXE_LDFLAGS "-static") |
| 6661 | 6667 | endif() |
| 6662 | else() | |
| 6663 | set(EXE_LDFLAGS " ") | |
| 6664 | 6668 | endif() |
| 6669 | ||
| 6665 | 6670 | if(ZIG_TEST_COVERAGE) |
| 6666 | 6671 | set(EXE_CFLAGS "${EXE_CFLAGS} -fprofile-arcs -ftest-coverage") |
| 6667 | 6672 | set(EXE_LDFLAGS "${EXE_LDFLAGS} -fprofile-arcs -ftest-coverage") |
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-self-hosted/dep_tokenizer.zig+1-1| ... | ... | @@ -998,7 +998,7 @@ fn printCharValues(out: var, bytes: []const u8) !void { |
| 998 | 998 | |
| 999 | 999 | fn printUnderstandableChar(out: var, char: u8) !void { |
| 1000 | 1000 | if (!std.ascii.isPrint(char) or char == ' ') { |
| 1001 | std.fmt.format(out.context, anyerror, out.output, "\\x{X2}", char) catch {}; | |
| 1001 | std.fmt.format(out.context, anyerror, out.output, "\\x{X:2}", char) catch {}; | |
| 1002 | 1002 | } else { |
| 1003 | 1003 | try out.write("'"); |
| 1004 | 1004 | try out.write([_]u8{printable_char_tab[char]}); |
src/all_types.hpp+33-6| ... | ... | @@ -1419,6 +1419,7 @@ enum BuiltinFnId { |
| 1419 | 1419 | BuiltinFnIdSubWithOverflow, |
| 1420 | 1420 | BuiltinFnIdMulWithOverflow, |
| 1421 | 1421 | BuiltinFnIdShlWithOverflow, |
| 1422 | BuiltinFnIdMulAdd, | |
| 1422 | 1423 | BuiltinFnIdCInclude, |
| 1423 | 1424 | BuiltinFnIdCDefine, |
| 1424 | 1425 | BuiltinFnIdCUndef, |
| ... | ... | @@ -1446,6 +1447,19 @@ enum BuiltinFnId { |
| 1446 | 1447 | BuiltinFnIdRem, |
| 1447 | 1448 | BuiltinFnIdMod, |
| 1448 | 1449 | BuiltinFnIdSqrt, |
| 1450 | BuiltinFnIdSin, | |
| 1451 | BuiltinFnIdCos, | |
| 1452 | BuiltinFnIdExp, | |
| 1453 | BuiltinFnIdExp2, | |
| 1454 | BuiltinFnIdLn, | |
| 1455 | BuiltinFnIdLog2, | |
| 1456 | BuiltinFnIdLog10, | |
| 1457 | BuiltinFnIdFabs, | |
| 1458 | BuiltinFnIdFloor, | |
| 1459 | BuiltinFnIdCeil, | |
| 1460 | BuiltinFnIdTrunc, | |
| 1461 | BuiltinFnIdNearbyInt, | |
| 1462 | BuiltinFnIdRound, | |
| 1449 | 1463 | BuiltinFnIdTruncate, |
| 1450 | 1464 | BuiltinFnIdIntCast, |
| 1451 | 1465 | BuiltinFnIdFloatCast, |
| ... | ... | @@ -1567,9 +1581,8 @@ enum ZigLLVMFnId { |
| 1567 | 1581 | ZigLLVMFnIdClz, |
| 1568 | 1582 | ZigLLVMFnIdPopCount, |
| 1569 | 1583 | ZigLLVMFnIdOverflowArithmetic, |
| 1570 | ZigLLVMFnIdFloor, | |
| 1571 | ZigLLVMFnIdCeil, | |
| 1572 | ZigLLVMFnIdSqrt, | |
| 1584 | ZigLLVMFnIdFMA, | |
| 1585 | ZigLLVMFnIdFloatOp, | |
| 1573 | 1586 | ZigLLVMFnIdBswap, |
| 1574 | 1587 | ZigLLVMFnIdBitReverse, |
| 1575 | 1588 | }; |
| ... | ... | @@ -1596,7 +1609,9 @@ struct ZigLLVMFnKey { |
| 1596 | 1609 | uint32_t bit_count; |
| 1597 | 1610 | } pop_count; |
| 1598 | 1611 | struct { |
| 1612 | BuiltinFnId op; | |
| 1599 | 1613 | uint32_t bit_count; |
| 1614 | uint32_t vector_len; // 0 means not a vector | |
| 1600 | 1615 | } floating; |
| 1601 | 1616 | struct { |
| 1602 | 1617 | AddSubMul add_sub_mul; |
| ... | ... | @@ -2260,6 +2275,8 @@ enum IrInstructionId { |
| 2260 | 2275 | IrInstructionIdOverflowOp, |
| 2261 | 2276 | IrInstructionIdTestErrSrc, |
| 2262 | 2277 | IrInstructionIdTestErrGen, |
| 2278 | IrInstructionIdMulAdd, | |
| 2279 | IrInstructionIdFloatOp, | |
| 2263 | 2280 | IrInstructionIdUnwrapErrCode, |
| 2264 | 2281 | IrInstructionIdUnwrapErrPayload, |
| 2265 | 2282 | IrInstructionIdErrWrapCode, |
| ... | ... | @@ -2324,7 +2341,6 @@ enum IrInstructionId { |
| 2324 | 2341 | IrInstructionIdAddImplicitReturnType, |
| 2325 | 2342 | IrInstructionIdMergeErrRetTraces, |
| 2326 | 2343 | IrInstructionIdMarkErrRetTracePtr, |
| 2327 | IrInstructionIdSqrt, | |
| 2328 | 2344 | IrInstructionIdErrSetCast, |
| 2329 | 2345 | IrInstructionIdToBytes, |
| 2330 | 2346 | IrInstructionIdFromBytes, |
| ... | ... | @@ -3080,6 +3096,15 @@ struct IrInstructionOverflowOp { |
| 3080 | 3096 | ZigType *result_ptr_type; |
| 3081 | 3097 | }; |
| 3082 | 3098 | |
| 3099 | struct IrInstructionMulAdd { | |
| 3100 | IrInstruction base; | |
| 3101 | ||
| 3102 | IrInstruction *type_value; | |
| 3103 | IrInstruction *op1; | |
| 3104 | IrInstruction *op2; | |
| 3105 | IrInstruction *op3; | |
| 3106 | }; | |
| 3107 | ||
| 3083 | 3108 | struct IrInstructionAlignOf { |
| 3084 | 3109 | IrInstruction base; |
| 3085 | 3110 | |
| ... | ... | @@ -3512,11 +3537,13 @@ struct IrInstructionMarkErrRetTracePtr { |
| 3512 | 3537 | IrInstruction *err_ret_trace_ptr; |
| 3513 | 3538 | }; |
| 3514 | 3539 | |
| 3515 | struct IrInstructionSqrt { | |
| 3540 | // For float ops which take a single argument | |
| 3541 | struct IrInstructionFloatOp { | |
| 3516 | 3542 | IrInstruction base; |
| 3517 | 3543 | |
| 3544 | BuiltinFnId op; | |
| 3518 | 3545 | IrInstruction *type; |
| 3519 | IrInstruction *op; | |
| 3546 | IrInstruction *op1; | |
| 3520 | 3547 | }; |
| 3521 | 3548 | |
| 3522 | 3549 | 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| ... | ... | @@ -808,32 +808,47 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSu |
| 808 | 808 | return fn_val; |
| 809 | 809 | } |
| 810 | 810 | |
| 811 | static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id) { | |
| 812 | assert(type_entry->id == ZigTypeIdFloat); | |
| 811 | static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) { | |
| 812 | assert(type_entry->id == ZigTypeIdFloat || | |
| 813 | type_entry->id == ZigTypeIdVector); | |
| 814 | ||
| 815 | bool is_vector = (type_entry->id == ZigTypeIdVector); | |
| 816 | ZigType *float_type = is_vector ? type_entry->data.vector.elem_type : type_entry; | |
| 813 | 817 | |
| 814 | 818 | ZigLLVMFnKey key = {}; |
| 815 | 819 | key.id = fn_id; |
| 816 | key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count; | |
| 820 | key.data.floating.bit_count = (uint32_t)float_type->data.floating.bit_count; | |
| 821 | key.data.floating.vector_len = is_vector ? (uint32_t)type_entry->data.vector.len : 0; | |
| 822 | key.data.floating.op = op; | |
| 817 | 823 | |
| 818 | 824 | auto existing_entry = g->llvm_fn_table.maybe_get(key); |
| 819 | 825 | if (existing_entry) |
| 820 | 826 | return existing_entry->value; |
| 821 | 827 | |
| 822 | 828 | const char *name; |
| 823 | if (fn_id == ZigLLVMFnIdFloor) { | |
| 824 | name = "floor"; | |
| 825 | } else if (fn_id == ZigLLVMFnIdCeil) { | |
| 826 | name = "ceil"; | |
| 827 | } else if (fn_id == ZigLLVMFnIdSqrt) { | |
| 828 | name = "sqrt"; | |
| 829 | uint32_t num_args; | |
| 830 | if (fn_id == ZigLLVMFnIdFMA) { | |
| 831 | name = "fma"; | |
| 832 | num_args = 3; | |
| 833 | } else if (fn_id == ZigLLVMFnIdFloatOp) { | |
| 834 | name = float_op_to_name(op, true); | |
| 835 | num_args = 1; | |
| 829 | 836 | } else { |
| 830 | 837 | zig_unreachable(); |
| 831 | 838 | } |
| 832 | 839 | |
| 833 | 840 | char fn_name[64]; |
| 834 | sprintf(fn_name, "llvm.%s.f%" ZIG_PRI_usize "", name, type_entry->data.floating.bit_count); | |
| 841 | if (is_vector) | |
| 842 | sprintf(fn_name, "llvm.%s.v%" PRIu32 "f%" PRIu32, name, key.data.floating.vector_len, key.data.floating.bit_count); | |
| 843 | else | |
| 844 | sprintf(fn_name, "llvm.%s.f%" PRIu32, name, key.data.floating.bit_count); | |
| 835 | 845 | LLVMTypeRef float_type_ref = get_llvm_type(g, type_entry); |
| 836 | LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, &float_type_ref, 1, false); | |
| 846 | LLVMTypeRef return_elem_types[3] = { | |
| 847 | float_type_ref, | |
| 848 | float_type_ref, | |
| 849 | float_type_ref, | |
| 850 | }; | |
| 851 | LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, return_elem_types, num_args, false); | |
| 837 | 852 | LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); |
| 838 | 853 | assert(LLVMGetIntrinsicID(fn_val)); |
| 839 | 854 | |
| ... | ... | @@ -2483,22 +2498,17 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2483 | 2498 | return result; |
| 2484 | 2499 | } |
| 2485 | 2500 | |
| 2486 | static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, ZigType *type_entry) { | |
| 2487 | if (type_entry->id == ZigTypeIdInt) | |
| 2501 | static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { | |
| 2502 | if ((op == BuiltinFnIdCeil || | |
| 2503 | op == BuiltinFnIdFloor) && | |
| 2504 | type_entry->id == ZigTypeIdInt) | |
| 2488 | 2505 | return val; |
| 2506 | assert(type_entry->id == ZigTypeIdFloat); | |
| 2489 | 2507 | |
| 2490 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor); | |
| 2508 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); | |
| 2491 | 2509 | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); |
| 2492 | 2510 | } |
| 2493 | 2511 | |
| 2494 | static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, ZigType *type_entry) { | |
| 2495 | if (type_entry->id == ZigTypeIdInt) | |
| 2496 | return val; | |
| 2497 | ||
| 2498 | LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil); | |
| 2499 | return LLVMBuildCall(g->builder, ceil_fn, &val, 1, ""); | |
| 2500 | } | |
| 2501 | ||
| 2502 | 2512 | enum DivKind { |
| 2503 | 2513 | DivKindFloat, |
| 2504 | 2514 | DivKindTrunc, |
| ... | ... | @@ -2574,7 +2584,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2574 | 2584 | return result; |
| 2575 | 2585 | case DivKindExact: |
| 2576 | 2586 | if (want_runtime_safety) { |
| 2577 | LLVMValueRef floored = gen_floor(g, result, type_entry); | |
| 2587 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2578 | 2588 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2579 | 2589 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2580 | 2590 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); |
| ... | ... | @@ -2596,12 +2606,12 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2596 | 2606 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2597 | 2607 | |
| 2598 | 2608 | LLVMPositionBuilderAtEnd(g->builder, ltz_block); |
| 2599 | LLVMValueRef ceiled = gen_ceil(g, result, type_entry); | |
| 2609 | LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil); | |
| 2600 | 2610 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); |
| 2601 | 2611 | LLVMBuildBr(g->builder, end_block); |
| 2602 | 2612 | |
| 2603 | 2613 | LLVMPositionBuilderAtEnd(g->builder, gez_block); |
| 2604 | LLVMValueRef floored = gen_floor(g, result, type_entry); | |
| 2614 | LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2605 | 2615 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); |
| 2606 | 2616 | LLVMBuildBr(g->builder, end_block); |
| 2607 | 2617 | |
| ... | ... | @@ -2613,7 +2623,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2613 | 2623 | return phi; |
| 2614 | 2624 | } |
| 2615 | 2625 | case DivKindFloor: |
| 2616 | return gen_floor(g, result, type_entry); | |
| 2626 | return gen_float_op(g, result, type_entry, BuiltinFnIdFloor); | |
| 2617 | 2627 | } |
| 2618 | 2628 | zig_unreachable(); |
| 2619 | 2629 | } |
| ... | ... | @@ -5417,13 +5427,28 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e |
| 5417 | 5427 | return nullptr; |
| 5418 | 5428 | } |
| 5419 | 5429 | |
| 5420 | static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) { | |
| 5421 | LLVMValueRef op = ir_llvm_value(g, instruction->op); | |
| 5430 | static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) { | |
| 5431 | LLVMValueRef op = ir_llvm_value(g, instruction->op1); | |
| 5422 | 5432 | assert(instruction->base.value.type->id == ZigTypeIdFloat); |
| 5423 | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt); | |
| 5433 | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFloatOp, instruction->op); | |
| 5424 | 5434 | return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); |
| 5425 | 5435 | } |
| 5426 | 5436 | |
| 5437 | static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrInstructionMulAdd *instruction) { | |
| 5438 | LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); | |
| 5439 | LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); | |
| 5440 | LLVMValueRef op3 = ir_llvm_value(g, instruction->op3); | |
| 5441 | assert(instruction->base.value.type->id == ZigTypeIdFloat || | |
| 5442 | instruction->base.value.type->id == ZigTypeIdVector); | |
| 5443 | LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd); | |
| 5444 | LLVMValueRef args[3] = { | |
| 5445 | op1, | |
| 5446 | op2, | |
| 5447 | op3, | |
| 5448 | }; | |
| 5449 | return LLVMBuildCall(g->builder, fn_val, args, 3, ""); | |
| 5450 | } | |
| 5451 | ||
| 5427 | 5452 | static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { |
| 5428 | 5453 | LLVMValueRef op = ir_llvm_value(g, instruction->op); |
| 5429 | 5454 | ZigType *int_type = instruction->base.value.type; |
| ... | ... | @@ -5770,8 +5795,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5770 | 5795 | return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction); |
| 5771 | 5796 | case IrInstructionIdMarkErrRetTracePtr: |
| 5772 | 5797 | return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 5773 | case IrInstructionIdSqrt: | |
| 5774 | return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); | |
| 5798 | case IrInstructionIdFloatOp: | |
| 5799 | return ir_render_float_op(g, executable, (IrInstructionFloatOp *)instruction); | |
| 5800 | case IrInstructionIdMulAdd: | |
| 5801 | return ir_render_mul_add(g, executable, (IrInstructionMulAdd *)instruction); | |
| 5775 | 5802 | case IrInstructionIdArrayToVector: |
| 5776 | 5803 | return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction); |
| 5777 | 5804 | case IrInstructionIdVectorToArray: |
| ... | ... | @@ -7356,6 +7383,21 @@ static void define_builtin_fns(CodeGen *g) { |
| 7356 | 7383 | create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); |
| 7357 | 7384 | create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); |
| 7358 | 7385 | create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); |
| 7386 | create_builtin_fn(g, BuiltinFnIdSin, "sin", 2); | |
| 7387 | create_builtin_fn(g, BuiltinFnIdCos, "cos", 2); | |
| 7388 | create_builtin_fn(g, BuiltinFnIdExp, "exp", 2); | |
| 7389 | create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 2); | |
| 7390 | create_builtin_fn(g, BuiltinFnIdLn, "ln", 2); | |
| 7391 | create_builtin_fn(g, BuiltinFnIdLog2, "log2", 2); | |
| 7392 | create_builtin_fn(g, BuiltinFnIdLog10, "log10", 2); | |
| 7393 | create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 2); | |
| 7394 | create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2); | |
| 7395 | create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2); | |
| 7396 | create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2); | |
| 7397 | //Needs library support on Windows | |
| 7398 | //create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2); | |
| 7399 | create_builtin_fn(g, BuiltinFnIdRound, "round", 2); | |
| 7400 | create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); | |
| 7359 | 7401 | create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); |
| 7360 | 7402 | create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); |
| 7361 | 7403 | create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); |
src/ir.cpp+471-61| ... | ... | @@ -777,6 +777,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErrGen *) { |
| 777 | 777 | return IrInstructionIdTestErrGen; |
| 778 | 778 | } |
| 779 | 779 | |
| 780 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMulAdd *) { | |
| 781 | return IrInstructionIdMulAdd; | |
| 782 | } | |
| 783 | ||
| 780 | 784 | static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) { |
| 781 | 785 | return IrInstructionIdUnwrapErrCode; |
| 782 | 786 | } |
| ... | ... | @@ -1037,8 +1041,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP |
| 1037 | 1041 | return IrInstructionIdMarkErrRetTracePtr; |
| 1038 | 1042 | } |
| 1039 | 1043 | |
| 1040 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { | |
| 1041 | return IrInstructionIdSqrt; | |
| 1044 | static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) { | |
| 1045 | return IrInstructionIdFloatOp; | |
| 1042 | 1046 | } |
| 1043 | 1047 | |
| 1044 | 1048 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) { |
| ... | ... | @@ -2437,6 +2441,75 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode |
| 2437 | 2441 | return &instruction->base; |
| 2438 | 2442 | } |
| 2439 | 2443 | |
| 2444 | ||
| 2445 | //TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign, | |
| 2446 | // lround, llround, lrint, llrint | |
| 2447 | // So far this is only non-complicated type functions. | |
| 2448 | const char *float_op_to_name(BuiltinFnId op, bool llvm_name) { | |
| 2449 | const bool b = llvm_name; | |
| 2450 | ||
| 2451 | switch (op) { | |
| 2452 | case BuiltinFnIdSqrt: | |
| 2453 | return "sqrt"; | |
| 2454 | case BuiltinFnIdSin: | |
| 2455 | return "sin"; | |
| 2456 | case BuiltinFnIdCos: | |
| 2457 | return "cos"; | |
| 2458 | case BuiltinFnIdExp: | |
| 2459 | return "exp"; | |
| 2460 | case BuiltinFnIdExp2: | |
| 2461 | return "exp2"; | |
| 2462 | case BuiltinFnIdLn: | |
| 2463 | return b ? "log" : "ln"; | |
| 2464 | case BuiltinFnIdLog10: | |
| 2465 | return "log10"; | |
| 2466 | case BuiltinFnIdLog2: | |
| 2467 | return "log2"; | |
| 2468 | case BuiltinFnIdFabs: | |
| 2469 | return "fabs"; | |
| 2470 | case BuiltinFnIdFloor: | |
| 2471 | return "floor"; | |
| 2472 | case BuiltinFnIdCeil: | |
| 2473 | return "ceil"; | |
| 2474 | case BuiltinFnIdTrunc: | |
| 2475 | return "trunc"; | |
| 2476 | case BuiltinFnIdNearbyInt: | |
| 2477 | return b ? "nearbyint" : "nearbyInt"; | |
| 2478 | case BuiltinFnIdRound: | |
| 2479 | return "round"; | |
| 2480 | default: | |
| 2481 | zig_unreachable(); | |
| 2482 | } | |
| 2483 | } | |
| 2484 | ||
| 2485 | static IrInstruction *ir_build_float_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op1, BuiltinFnId op) { | |
| 2486 | IrInstructionFloatOp *instruction = ir_build_instruction<IrInstructionFloatOp>(irb, scope, source_node); | |
| 2487 | instruction->type = type; | |
| 2488 | instruction->op1 = op1; | |
| 2489 | instruction->op = op; | |
| 2490 | ||
| 2491 | if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); | |
| 2492 | ir_ref_instruction(op1, irb->current_basic_block); | |
| 2493 | ||
| 2494 | return &instruction->base; | |
| 2495 | } | |
| 2496 | ||
| 2497 | static IrInstruction *ir_build_mul_add(IrBuilder *irb, Scope *scope, AstNode *source_node, | |
| 2498 | IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *op3) { | |
| 2499 | IrInstructionMulAdd *instruction = ir_build_instruction<IrInstructionMulAdd>(irb, scope, source_node); | |
| 2500 | instruction->type_value = type_value; | |
| 2501 | instruction->op1 = op1; | |
| 2502 | instruction->op2 = op2; | |
| 2503 | instruction->op3 = op3; | |
| 2504 | ||
| 2505 | ir_ref_instruction(type_value, irb->current_basic_block); | |
| 2506 | ir_ref_instruction(op1, irb->current_basic_block); | |
| 2507 | ir_ref_instruction(op2, irb->current_basic_block); | |
| 2508 | ir_ref_instruction(op3, irb->current_basic_block); | |
| 2509 | ||
| 2510 | return &instruction->base; | |
| 2511 | } | |
| 2512 | ||
| 2440 | 2513 | static IrInstruction *ir_build_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { |
| 2441 | 2514 | IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node); |
| 2442 | 2515 | instruction->type_value = type_value; |
| ... | ... | @@ -3201,17 +3274,6 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco |
| 3201 | 3274 | return &instruction->base; |
| 3202 | 3275 | } |
| 3203 | 3276 | |
| 3204 | static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) { | |
| 3205 | IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node); | |
| 3206 | instruction->type = type; | |
| 3207 | instruction->op = op; | |
| 3208 | ||
| 3209 | if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block); | |
| 3210 | ir_ref_instruction(op, irb->current_basic_block); | |
| 3211 | ||
| 3212 | return &instruction->base; | |
| 3213 | } | |
| 3214 | ||
| 3215 | 3277 | static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 3216 | 3278 | IrInstruction *container, IrInstruction *name) |
| 3217 | 3279 | { |
| ... | ... | @@ -4380,6 +4442,33 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * |
| 4380 | 4442 | return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); |
| 4381 | 4443 | } |
| 4382 | 4444 | |
| 4445 | static IrInstruction *ir_gen_mul_add(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 4446 | assert(node->type == NodeTypeFnCallExpr); | |
| 4447 | ||
| 4448 | AstNode *type_node = node->data.fn_call_expr.params.at(0); | |
| 4449 | AstNode *op1_node = node->data.fn_call_expr.params.at(1); | |
| 4450 | AstNode *op2_node = node->data.fn_call_expr.params.at(2); | |
| 4451 | AstNode *op3_node = node->data.fn_call_expr.params.at(3); | |
| 4452 | ||
| 4453 | IrInstruction *type_value = ir_gen_node(irb, type_node, scope); | |
| 4454 | if (type_value == irb->codegen->invalid_instruction) | |
| 4455 | return irb->codegen->invalid_instruction; | |
| 4456 | ||
| 4457 | IrInstruction *op1 = ir_gen_node(irb, op1_node, scope); | |
| 4458 | if (op1 == irb->codegen->invalid_instruction) | |
| 4459 | return irb->codegen->invalid_instruction; | |
| 4460 | ||
| 4461 | IrInstruction *op2 = ir_gen_node(irb, op2_node, scope); | |
| 4462 | if (op2 == irb->codegen->invalid_instruction) | |
| 4463 | return irb->codegen->invalid_instruction; | |
| 4464 | ||
| 4465 | IrInstruction *op3 = ir_gen_node(irb, op3_node, scope); | |
| 4466 | if (op3 == irb->codegen->invalid_instruction) | |
| 4467 | return irb->codegen->invalid_instruction; | |
| 4468 | ||
| 4469 | return ir_build_mul_add(irb, scope, node, type_value, op1, op2, op3); | |
| 4470 | } | |
| 4471 | ||
| 4383 | 4472 | static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) { |
| 4384 | 4473 | for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) { |
| 4385 | 4474 | if (it_scope->id == ScopeIdDecls) { |
| ... | ... | @@ -4708,6 +4797,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4708 | 4797 | return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); |
| 4709 | 4798 | } |
| 4710 | 4799 | case BuiltinFnIdSqrt: |
| 4800 | case BuiltinFnIdSin: | |
| 4801 | case BuiltinFnIdCos: | |
| 4802 | case BuiltinFnIdExp: | |
| 4803 | case BuiltinFnIdExp2: | |
| 4804 | case BuiltinFnIdLn: | |
| 4805 | case BuiltinFnIdLog2: | |
| 4806 | case BuiltinFnIdLog10: | |
| 4807 | case BuiltinFnIdFabs: | |
| 4808 | case BuiltinFnIdFloor: | |
| 4809 | case BuiltinFnIdCeil: | |
| 4810 | case BuiltinFnIdTrunc: | |
| 4811 | case BuiltinFnIdNearbyInt: | |
| 4812 | case BuiltinFnIdRound: | |
| 4711 | 4813 | { |
| 4712 | 4814 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| 4713 | 4815 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); |
| ... | ... | @@ -4719,7 +4821,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4719 | 4821 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4720 | 4822 | return arg1_value; |
| 4721 | 4823 | |
| 4722 | IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value); | |
| 4824 | IrInstruction *ir_sqrt = ir_build_float_op(irb, scope, node, arg0_value, arg1_value, builtin_fn->id); | |
| 4723 | 4825 | return ir_lval_wrap(irb, scope, ir_sqrt, lval, result_loc); |
| 4724 | 4826 | } |
| 4725 | 4827 | case BuiltinFnIdTruncate: |
| ... | ... | @@ -5043,6 +5145,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5043 | 5145 | return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc); |
| 5044 | 5146 | case BuiltinFnIdShlWithOverflow: |
| 5045 | 5147 | return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc); |
| 5148 | case BuiltinFnIdMulAdd: | |
| 5149 | return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc); | |
| 5046 | 5150 | case BuiltinFnIdTypeName: |
| 5047 | 5151 | { |
| 5048 | 5152 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | ... | @@ -22709,6 +22813,125 @@ static IrInstruction *ir_analyze_instruction_result_ptr(IrAnalyze *ira, IrInstru |
| 22709 | 22813 | return ir_get_ref(ira, &instruction->base, result, true, false); |
| 22710 | 22814 | } |
| 22711 | 22815 | |
| 22816 | static void ir_eval_mul_add(IrAnalyze *ira, IrInstructionMulAdd *source_instr, ZigType *float_type, | |
| 22817 | ConstExprValue *op1, ConstExprValue *op2, ConstExprValue *op3, ConstExprValue *out_val) { | |
| 22818 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 22819 | f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value, | |
| 22820 | &op3->data.x_bigfloat.value); | |
| 22821 | } else if (float_type->id == ZigTypeIdFloat) { | |
| 22822 | switch (float_type->data.floating.bit_count) { | |
| 22823 | case 16: | |
| 22824 | out_val->data.x_f16 = f16_mulAdd(op1->data.x_f16, op2->data.x_f16, op3->data.x_f16); | |
| 22825 | break; | |
| 22826 | case 32: | |
| 22827 | out_val->data.x_f32 = fmaf(op1->data.x_f32, op2->data.x_f32, op3->data.x_f32); | |
| 22828 | break; | |
| 22829 | case 64: | |
| 22830 | out_val->data.x_f64 = fma(op1->data.x_f64, op2->data.x_f64, op3->data.x_f64); | |
| 22831 | break; | |
| 22832 | case 128: | |
| 22833 | f128M_mulAdd(&op1->data.x_f128, &op2->data.x_f128, &op3->data.x_f128, &out_val->data.x_f128); | |
| 22834 | break; | |
| 22835 | default: | |
| 22836 | zig_unreachable(); | |
| 22837 | } | |
| 22838 | } else { | |
| 22839 | zig_unreachable(); | |
| 22840 | } | |
| 22841 | } | |
| 22842 | ||
| 22843 | static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructionMulAdd *instruction) { | |
| 22844 | IrInstruction *type_value = instruction->type_value->child; | |
| 22845 | if (type_is_invalid(type_value->value.type)) | |
| 22846 | return ira->codegen->invalid_instruction; | |
| 22847 | ||
| 22848 | ZigType *expr_type = ir_resolve_type(ira, type_value); | |
| 22849 | if (type_is_invalid(expr_type)) | |
| 22850 | return ira->codegen->invalid_instruction; | |
| 22851 | ||
| 22852 | // Only allow float types, and vectors of floats. | |
| 22853 | ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; | |
| 22854 | if (float_type->id != ZigTypeIdFloat) { | |
| 22855 | ir_add_error(ira, type_value, | |
| 22856 | buf_sprintf("expected float or vector of float type, found '%s'", buf_ptr(&float_type->name))); | |
| 22857 | return ira->codegen->invalid_instruction; | |
| 22858 | } | |
| 22859 | ||
| 22860 | IrInstruction *op1 = instruction->op1->child; | |
| 22861 | if (type_is_invalid(op1->value.type)) | |
| 22862 | return ira->codegen->invalid_instruction; | |
| 22863 | ||
| 22864 | IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, expr_type); | |
| 22865 | if (type_is_invalid(casted_op1->value.type)) | |
| 22866 | return ira->codegen->invalid_instruction; | |
| 22867 | ||
| 22868 | IrInstruction *op2 = instruction->op2->child; | |
| 22869 | if (type_is_invalid(op2->value.type)) | |
| 22870 | return ira->codegen->invalid_instruction; | |
| 22871 | ||
| 22872 | IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, expr_type); | |
| 22873 | if (type_is_invalid(casted_op2->value.type)) | |
| 22874 | return ira->codegen->invalid_instruction; | |
| 22875 | ||
| 22876 | IrInstruction *op3 = instruction->op3->child; | |
| 22877 | if (type_is_invalid(op3->value.type)) | |
| 22878 | return ira->codegen->invalid_instruction; | |
| 22879 | ||
| 22880 | IrInstruction *casted_op3 = ir_implicit_cast(ira, op3, expr_type); | |
| 22881 | if (type_is_invalid(casted_op3->value.type)) | |
| 22882 | return ira->codegen->invalid_instruction; | |
| 22883 | ||
| 22884 | if (instr_is_comptime(casted_op1) && | |
| 22885 | instr_is_comptime(casted_op2) && | |
| 22886 | instr_is_comptime(casted_op3)) { | |
| 22887 | ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); | |
| 22888 | if (!op1_const) | |
| 22889 | return ira->codegen->invalid_instruction; | |
| 22890 | ConstExprValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad); | |
| 22891 | if (!op2_const) | |
| 22892 | return ira->codegen->invalid_instruction; | |
| 22893 | ConstExprValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad); | |
| 22894 | if (!op3_const) | |
| 22895 | return ira->codegen->invalid_instruction; | |
| 22896 | ||
| 22897 | IrInstruction *result = ir_const(ira, &instruction->base, expr_type); | |
| 22898 | ConstExprValue *out_val = &result->value; | |
| 22899 | ||
| 22900 | if (expr_type->id == ZigTypeIdVector) { | |
| 22901 | expand_undef_array(ira->codegen, op1_const); | |
| 22902 | expand_undef_array(ira->codegen, op2_const); | |
| 22903 | expand_undef_array(ira->codegen, op3_const); | |
| 22904 | out_val->special = ConstValSpecialUndef; | |
| 22905 | expand_undef_array(ira->codegen, out_val); | |
| 22906 | size_t len = expr_type->data.vector.len; | |
| 22907 | for (size_t i = 0; i < len; i += 1) { | |
| 22908 | ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; | |
| 22909 | ConstExprValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i]; | |
| 22910 | ConstExprValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i]; | |
| 22911 | ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; | |
| 22912 | assert(float_operand_op1->type == float_type); | |
| 22913 | assert(float_operand_op2->type == float_type); | |
| 22914 | assert(float_operand_op3->type == float_type); | |
| 22915 | assert(float_out_val->type == float_type); | |
| 22916 | ir_eval_mul_add(ira, instruction, float_type, | |
| 22917 | op1_const, op2_const, op3_const, float_out_val); | |
| 22918 | float_out_val->type = float_type; | |
| 22919 | } | |
| 22920 | out_val->type = expr_type; | |
| 22921 | out_val->special = ConstValSpecialStatic; | |
| 22922 | } else { | |
| 22923 | ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, out_val); | |
| 22924 | } | |
| 22925 | return result; | |
| 22926 | } | |
| 22927 | ||
| 22928 | IrInstruction *result = ir_build_mul_add(&ira->new_irb, | |
| 22929 | instruction->base.scope, instruction->base.source_node, | |
| 22930 | type_value, casted_op1, casted_op2, casted_op3); | |
| 22931 | result->value.type = expr_type; | |
| 22932 | return result; | |
| 22933 | } | |
| 22934 | ||
| 22712 | 22935 | static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErrSrc *instruction) { |
| 22713 | 22936 | IrInstruction *base_ptr = instruction->base_ptr->child; |
| 22714 | 22937 | if (type_is_invalid(base_ptr->value.type)) |
| ... | ... | @@ -24542,70 +24765,254 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i |
| 24542 | 24765 | return result; |
| 24543 | 24766 | } |
| 24544 | 24767 | |
| 24545 | static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { | |
| 24546 | ZigType *float_type = ir_resolve_type(ira, instruction->type->child); | |
| 24547 | if (type_is_invalid(float_type)) | |
| 24548 | return ira->codegen->invalid_instruction; | |
| 24768 | static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type, | |
| 24769 | ConstExprValue *op, ConstExprValue *out_val) { | |
| 24770 | assert(ira && source_instr && float_type && out_val && op); | |
| 24771 | assert(float_type->id == ZigTypeIdFloat || | |
| 24772 | float_type->id == ZigTypeIdComptimeFloat); | |
| 24549 | 24773 | |
| 24550 | IrInstruction *op = instruction->op->child; | |
| 24551 | if (type_is_invalid(op->value.type)) | |
| 24774 | BuiltinFnId fop = source_instr->op; | |
| 24775 | unsigned bits; | |
| 24776 | ||
| 24777 | switch (float_type->id) { | |
| 24778 | case ZigTypeIdComptimeFloat: | |
| 24779 | bits = 128; | |
| 24780 | break; | |
| 24781 | case ZigTypeIdFloat: | |
| 24782 | bits = float_type->data.floating.bit_count; | |
| 24783 | break; | |
| 24784 | default: | |
| 24785 | zig_unreachable(); | |
| 24786 | } | |
| 24787 | ||
| 24788 | switch (bits) { | |
| 24789 | case 16: { | |
| 24790 | switch (fop) { | |
| 24791 | case BuiltinFnIdSqrt: | |
| 24792 | out_val->data.x_f16 = f16_sqrt(op->data.x_f16); | |
| 24793 | break; | |
| 24794 | case BuiltinFnIdSin: | |
| 24795 | case BuiltinFnIdCos: | |
| 24796 | case BuiltinFnIdExp: | |
| 24797 | case BuiltinFnIdExp2: | |
| 24798 | case BuiltinFnIdLn: | |
| 24799 | case BuiltinFnIdLog10: | |
| 24800 | case BuiltinFnIdLog2: | |
| 24801 | case BuiltinFnIdFabs: | |
| 24802 | case BuiltinFnIdFloor: | |
| 24803 | case BuiltinFnIdCeil: | |
| 24804 | case BuiltinFnIdTrunc: | |
| 24805 | case BuiltinFnIdNearbyInt: | |
| 24806 | case BuiltinFnIdRound: | |
| 24807 | zig_panic("unimplemented f16 builtin"); | |
| 24808 | default: | |
| 24809 | zig_unreachable(); | |
| 24810 | }; | |
| 24811 | break; | |
| 24812 | }; | |
| 24813 | case 32: { | |
| 24814 | switch (fop) { | |
| 24815 | case BuiltinFnIdSqrt: | |
| 24816 | out_val->data.x_f32 = sqrtf(op->data.x_f32); | |
| 24817 | break; | |
| 24818 | case BuiltinFnIdSin: | |
| 24819 | out_val->data.x_f32 = sinf(op->data.x_f32); | |
| 24820 | break; | |
| 24821 | case BuiltinFnIdCos: | |
| 24822 | out_val->data.x_f32 = cosf(op->data.x_f32); | |
| 24823 | break; | |
| 24824 | case BuiltinFnIdExp: | |
| 24825 | out_val->data.x_f32 = expf(op->data.x_f32); | |
| 24826 | break; | |
| 24827 | case BuiltinFnIdExp2: | |
| 24828 | out_val->data.x_f32 = exp2f(op->data.x_f32); | |
| 24829 | break; | |
| 24830 | case BuiltinFnIdLn: | |
| 24831 | out_val->data.x_f32 = logf(op->data.x_f32); | |
| 24832 | break; | |
| 24833 | case BuiltinFnIdLog10: | |
| 24834 | out_val->data.x_f32 = log10f(op->data.x_f32); | |
| 24835 | break; | |
| 24836 | case BuiltinFnIdLog2: | |
| 24837 | out_val->data.x_f32 = log2f(op->data.x_f32); | |
| 24838 | break; | |
| 24839 | case BuiltinFnIdFabs: | |
| 24840 | out_val->data.x_f32 = fabsf(op->data.x_f32); | |
| 24841 | break; | |
| 24842 | case BuiltinFnIdFloor: | |
| 24843 | out_val->data.x_f32 = floorf(op->data.x_f32); | |
| 24844 | break; | |
| 24845 | case BuiltinFnIdCeil: | |
| 24846 | out_val->data.x_f32 = ceilf(op->data.x_f32); | |
| 24847 | break; | |
| 24848 | case BuiltinFnIdTrunc: | |
| 24849 | out_val->data.x_f32 = truncf(op->data.x_f32); | |
| 24850 | break; | |
| 24851 | case BuiltinFnIdNearbyInt: | |
| 24852 | out_val->data.x_f32 = nearbyintf(op->data.x_f32); | |
| 24853 | break; | |
| 24854 | case BuiltinFnIdRound: | |
| 24855 | out_val->data.x_f32 = roundf(op->data.x_f32); | |
| 24856 | break; | |
| 24857 | default: | |
| 24858 | zig_unreachable(); | |
| 24859 | }; | |
| 24860 | break; | |
| 24861 | }; | |
| 24862 | case 64: { | |
| 24863 | switch (fop) { | |
| 24864 | case BuiltinFnIdSqrt: | |
| 24865 | out_val->data.x_f64 = sqrt(op->data.x_f64); | |
| 24866 | break; | |
| 24867 | case BuiltinFnIdSin: | |
| 24868 | out_val->data.x_f64 = sin(op->data.x_f64); | |
| 24869 | break; | |
| 24870 | case BuiltinFnIdCos: | |
| 24871 | out_val->data.x_f64 = cos(op->data.x_f64); | |
| 24872 | break; | |
| 24873 | case BuiltinFnIdExp: | |
| 24874 | out_val->data.x_f64 = exp(op->data.x_f64); | |
| 24875 | break; | |
| 24876 | case BuiltinFnIdExp2: | |
| 24877 | out_val->data.x_f64 = exp2(op->data.x_f64); | |
| 24878 | break; | |
| 24879 | case BuiltinFnIdLn: | |
| 24880 | out_val->data.x_f64 = log(op->data.x_f64); | |
| 24881 | break; | |
| 24882 | case BuiltinFnIdLog10: | |
| 24883 | out_val->data.x_f64 = log10(op->data.x_f64); | |
| 24884 | break; | |
| 24885 | case BuiltinFnIdLog2: | |
| 24886 | out_val->data.x_f64 = log2(op->data.x_f64); | |
| 24887 | break; | |
| 24888 | case BuiltinFnIdFabs: | |
| 24889 | out_val->data.x_f64 = fabs(op->data.x_f64); | |
| 24890 | break; | |
| 24891 | case BuiltinFnIdFloor: | |
| 24892 | out_val->data.x_f64 = floor(op->data.x_f64); | |
| 24893 | break; | |
| 24894 | case BuiltinFnIdCeil: | |
| 24895 | out_val->data.x_f64 = ceil(op->data.x_f64); | |
| 24896 | break; | |
| 24897 | case BuiltinFnIdTrunc: | |
| 24898 | out_val->data.x_f64 = trunc(op->data.x_f64); | |
| 24899 | break; | |
| 24900 | case BuiltinFnIdNearbyInt: | |
| 24901 | out_val->data.x_f64 = nearbyint(op->data.x_f64); | |
| 24902 | break; | |
| 24903 | case BuiltinFnIdRound: | |
| 24904 | out_val->data.x_f64 = round(op->data.x_f64); | |
| 24905 | break; | |
| 24906 | default: | |
| 24907 | zig_unreachable(); | |
| 24908 | } | |
| 24909 | break; | |
| 24910 | }; | |
| 24911 | case 128: { | |
| 24912 | float128_t *out, *in; | |
| 24913 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 24914 | out = &out_val->data.x_bigfloat.value; | |
| 24915 | in = &op->data.x_bigfloat.value; | |
| 24916 | } else { | |
| 24917 | out = &out_val->data.x_f128; | |
| 24918 | in = &op->data.x_f128; | |
| 24919 | } | |
| 24920 | switch (fop) { | |
| 24921 | case BuiltinFnIdSqrt: | |
| 24922 | f128M_sqrt(in, out); | |
| 24923 | break; | |
| 24924 | case BuiltinFnIdNearbyInt: | |
| 24925 | case BuiltinFnIdSin: | |
| 24926 | case BuiltinFnIdCos: | |
| 24927 | case BuiltinFnIdExp: | |
| 24928 | case BuiltinFnIdExp2: | |
| 24929 | case BuiltinFnIdLn: | |
| 24930 | case BuiltinFnIdLog10: | |
| 24931 | case BuiltinFnIdLog2: | |
| 24932 | case BuiltinFnIdFabs: | |
| 24933 | case BuiltinFnIdFloor: | |
| 24934 | case BuiltinFnIdCeil: | |
| 24935 | case BuiltinFnIdTrunc: | |
| 24936 | case BuiltinFnIdRound: | |
| 24937 | zig_panic("unimplemented f128 builtin"); | |
| 24938 | default: | |
| 24939 | zig_unreachable(); | |
| 24940 | } | |
| 24941 | break; | |
| 24942 | }; | |
| 24943 | default: | |
| 24944 | zig_unreachable(); | |
| 24945 | } | |
| 24946 | } | |
| 24947 | ||
| 24948 | static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) { | |
| 24949 | IrInstruction *type = instruction->type->child; | |
| 24950 | if (type_is_invalid(type->value.type)) | |
| 24951 | return ira->codegen->invalid_instruction; | |
| 24952 | ||
| 24953 | ZigType *expr_type = ir_resolve_type(ira, type); | |
| 24954 | if (type_is_invalid(expr_type)) | |
| 24552 | 24955 | return ira->codegen->invalid_instruction; |
| 24553 | 24956 | |
| 24554 | bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat; | |
| 24555 | if (!ok_type) { | |
| 24556 | ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); | |
| 24957 | // Only allow float types, and vectors of floats. | |
| 24958 | ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; | |
| 24959 | if (float_type->id != ZigTypeIdFloat && float_type->id != ZigTypeIdComptimeFloat) { | |
| 24960 | 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))); | |
| 24557 | 24961 | return ira->codegen->invalid_instruction; |
| 24558 | 24962 | } |
| 24559 | 24963 | |
| 24560 | IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type); | |
| 24561 | if (type_is_invalid(casted_op->value.type)) | |
| 24964 | IrInstruction *op1 = instruction->op1->child; | |
| 24965 | if (type_is_invalid(op1->value.type)) | |
| 24562 | 24966 | return ira->codegen->invalid_instruction; |
| 24563 | 24967 | |
| 24564 | if (instr_is_comptime(casted_op)) { | |
| 24565 | ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); | |
| 24566 | if (!val) | |
| 24968 | IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type); | |
| 24969 | if (type_is_invalid(casted_op1->value.type)) | |
| 24970 | return ira->codegen->invalid_instruction; | |
| 24971 | ||
| 24972 | if (instr_is_comptime(casted_op1)) { | |
| 24973 | // Our comptime 16-bit and 128-bit support is quite limited. | |
| 24974 | if ((float_type->id == ZigTypeIdComptimeFloat || | |
| 24975 | float_type->data.floating.bit_count == 16 || | |
| 24976 | float_type->data.floating.bit_count == 128) && | |
| 24977 | instruction->op != BuiltinFnIdSqrt) { | |
| 24978 | 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))); | |
| 24979 | return ira->codegen->invalid_instruction; | |
| 24980 | } | |
| 24981 | ||
| 24982 | ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); | |
| 24983 | if (!op1_const) | |
| 24567 | 24984 | return ira->codegen->invalid_instruction; |
| 24568 | 24985 | |
| 24569 | IrInstruction *result = ir_const(ira, &instruction->base, float_type); | |
| 24986 | IrInstruction *result = ir_const(ira, &instruction->base, expr_type); | |
| 24570 | 24987 | ConstExprValue *out_val = &result->value; |
| 24571 | 24988 | |
| 24572 | if (float_type->id == ZigTypeIdComptimeFloat) { | |
| 24573 | bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); | |
| 24574 | } else if (float_type->id == ZigTypeIdFloat) { | |
| 24575 | switch (float_type->data.floating.bit_count) { | |
| 24576 | case 16: | |
| 24577 | out_val->data.x_f16 = f16_sqrt(val->data.x_f16); | |
| 24578 | break; | |
| 24579 | case 32: | |
| 24580 | out_val->data.x_f32 = sqrtf(val->data.x_f32); | |
| 24581 | break; | |
| 24582 | case 64: | |
| 24583 | out_val->data.x_f64 = sqrt(val->data.x_f64); | |
| 24584 | break; | |
| 24585 | case 128: | |
| 24586 | f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128); | |
| 24587 | break; | |
| 24588 | default: | |
| 24589 | zig_unreachable(); | |
| 24989 | if (expr_type->id == ZigTypeIdVector) { | |
| 24990 | expand_undef_array(ira->codegen, op1_const); | |
| 24991 | out_val->special = ConstValSpecialUndef; | |
| 24992 | expand_undef_array(ira->codegen, out_val); | |
| 24993 | size_t len = expr_type->data.vector.len; | |
| 24994 | for (size_t i = 0; i < len; i += 1) { | |
| 24995 | ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; | |
| 24996 | ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; | |
| 24997 | assert(float_operand_op1->type == float_type); | |
| 24998 | assert(float_out_val->type == float_type); | |
| 24999 | ir_eval_float_op(ira, instruction, float_type, | |
| 25000 | op1_const, float_out_val); | |
| 25001 | float_out_val->type = float_type; | |
| 24590 | 25002 | } |
| 25003 | out_val->type = expr_type; | |
| 25004 | out_val->special = ConstValSpecialStatic; | |
| 24591 | 25005 | } else { |
| 24592 | zig_unreachable(); | |
| 25006 | ir_eval_float_op(ira, instruction, float_type, op1_const, out_val); | |
| 24593 | 25007 | } |
| 24594 | ||
| 24595 | 25008 | return result; |
| 24596 | 25009 | } |
| 24597 | 25010 | |
| 24598 | 25011 | ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base); |
| 24599 | if (float_type->data.floating.bit_count != 16 && | |
| 24600 | float_type->data.floating.bit_count != 32 && | |
| 24601 | float_type->data.floating.bit_count != 64) { | |
| 24602 | ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name))); | |
| 24603 | return ira->codegen->invalid_instruction; | |
| 24604 | } | |
| 24605 | 25012 | |
| 24606 | IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope, | |
| 24607 | instruction->base.source_node, nullptr, casted_op); | |
| 24608 | result->value.type = float_type; | |
| 25013 | IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope, | |
| 25014 | instruction->base.source_node, nullptr, casted_op1, instruction->op); | |
| 25015 | result->value.type = expr_type; | |
| 24609 | 25016 | return result; |
| 24610 | 25017 | } |
| 24611 | 25018 | |
| ... | ... | @@ -25143,8 +25550,10 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 25143 | 25550 | return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); |
| 25144 | 25551 | case IrInstructionIdMarkErrRetTracePtr: |
| 25145 | 25552 | return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 25146 | case IrInstructionIdSqrt: | |
| 25147 | return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); | |
| 25553 | case IrInstructionIdFloatOp: | |
| 25554 | return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction); | |
| 25555 | case IrInstructionIdMulAdd: | |
| 25556 | return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)instruction); | |
| 25148 | 25557 | case IrInstructionIdIntToErr: |
| 25149 | 25558 | return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); |
| 25150 | 25559 | case IrInstructionIdErrToInt: |
| ... | ... | @@ -25391,7 +25800,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 25391 | 25800 | case IrInstructionIdCoroFree: |
| 25392 | 25801 | case IrInstructionIdCoroPromise: |
| 25393 | 25802 | case IrInstructionIdPromiseResultType: |
| 25394 | case IrInstructionIdSqrt: | |
| 25803 | case IrInstructionIdFloatOp: | |
| 25804 | case IrInstructionIdMulAdd: | |
| 25395 | 25805 | case IrInstructionIdAtomicLoad: |
| 25396 | 25806 | case IrInstructionIdIntCast: |
| 25397 | 25807 | 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| ... | ... | @@ -1563,15 +1563,32 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe |
| 1563 | 1563 | fprintf(irp->f, ")"); |
| 1564 | 1564 | } |
| 1565 | 1565 | |
| 1566 | static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { | |
| 1567 | fprintf(irp->f, "@sqrt("); | |
| 1566 | static void ir_print_float_op(IrPrint *irp, IrInstructionFloatOp *instruction) { | |
| 1567 | ||
| 1568 | fprintf(irp->f, "@%s(", float_op_to_name(instruction->op, false)); | |
| 1568 | 1569 | if (instruction->type != nullptr) { |
| 1569 | 1570 | ir_print_other_instruction(irp, instruction->type); |
| 1570 | 1571 | } else { |
| 1571 | 1572 | fprintf(irp->f, "null"); |
| 1572 | 1573 | } |
| 1573 | 1574 | fprintf(irp->f, ","); |
| 1574 | ir_print_other_instruction(irp, instruction->op); | |
| 1575 | ir_print_other_instruction(irp, instruction->op1); | |
| 1576 | fprintf(irp->f, ")"); | |
| 1577 | } | |
| 1578 | ||
| 1579 | static void ir_print_mul_add(IrPrint *irp, IrInstructionMulAdd *instruction) { | |
| 1580 | fprintf(irp->f, "@mulAdd("); | |
| 1581 | if (instruction->type_value != nullptr) { | |
| 1582 | ir_print_other_instruction(irp, instruction->type_value); | |
| 1583 | } else { | |
| 1584 | fprintf(irp->f, "null"); | |
| 1585 | } | |
| 1586 | fprintf(irp->f, ","); | |
| 1587 | ir_print_other_instruction(irp, instruction->op1); | |
| 1588 | fprintf(irp->f, ","); | |
| 1589 | ir_print_other_instruction(irp, instruction->op2); | |
| 1590 | fprintf(irp->f, ","); | |
| 1591 | ir_print_other_instruction(irp, instruction->op3); | |
| 1575 | 1592 | fprintf(irp->f, ")"); |
| 1576 | 1593 | } |
| 1577 | 1594 | |
| ... | ... | @@ -2053,8 +2070,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 2053 | 2070 | case IrInstructionIdMarkErrRetTracePtr: |
| 2054 | 2071 | ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 2055 | 2072 | break; |
| 2056 | case IrInstructionIdSqrt: | |
| 2057 | ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); | |
| 2073 | case IrInstructionIdFloatOp: | |
| 2074 | ir_print_float_op(irp, (IrInstructionFloatOp *)instruction); | |
| 2075 | break; | |
| 2076 | case IrInstructionIdMulAdd: | |
| 2077 | ir_print_mul_add(irp, (IrInstructionMulAdd *)instruction); | |
| 2058 | 2078 | break; |
| 2059 | 2079 | case IrInstructionIdAtomicLoad: |
| 2060 | 2080 | ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); |
std/fmt.zig+397-363| ... | ... | @@ -10,6 +10,42 @@ const lossyCast = std.math.lossyCast; |
| 10 | 10 | |
| 11 | 11 | pub const default_max_depth = 3; |
| 12 | 12 | |
| 13 | pub const Alignment = enum { | |
| 14 | Left, | |
| 15 | Center, | |
| 16 | Right, | |
| 17 | }; | |
| 18 | ||
| 19 | pub const FormatOptions = struct { | |
| 20 | precision: ?usize = null, | |
| 21 | width: ?usize = null, | |
| 22 | alignment: ?Alignment = null, | |
| 23 | fill: u8 = ' ', | |
| 24 | }; | |
| 25 | ||
| 26 | fn nextArg(comptime used_pos_args: *u32, comptime maybe_pos_arg: ?comptime_int, comptime next_arg: *comptime_int) comptime_int { | |
| 27 | if (maybe_pos_arg) |pos_arg| { | |
| 28 | used_pos_args.* |= 1 << pos_arg; | |
| 29 | return pos_arg; | |
| 30 | } else { | |
| 31 | const arg = next_arg.*; | |
| 32 | next_arg.* += 1; | |
| 33 | return arg; | |
| 34 | } | |
| 35 | } | |
| 36 | ||
| 37 | fn peekIsAlign(comptime fmt: []const u8) bool { | |
| 38 | // Should only be called during a state transition to the format segment. | |
| 39 | std.debug.assert(fmt[0] == ':'); | |
| 40 | ||
| 41 | inline for (([_]u8{ 1, 2 })[0..]) |i| { | |
| 42 | if (fmt.len > i and (fmt[i] == '<' or fmt[i] == '^' or fmt[i] == '>')) { | |
| 43 | return true; | |
| 44 | } | |
| 45 | } | |
| 46 | return false; | |
| 47 | } | |
| 48 | ||
| 13 | 49 | /// Renders fmt string with args, calling output with slices of bytes. |
| 14 | 50 | /// If `output` returns an error, the error is returned from `format` and |
| 15 | 51 | /// `output` is not called again. |
| ... | ... | @@ -20,17 +56,30 @@ pub fn format( |
| 20 | 56 | comptime fmt: []const u8, |
| 21 | 57 | args: ..., |
| 22 | 58 | ) Errors!void { |
| 59 | const ArgSetType = @IntType(false, 32); | |
| 60 | if (args.len > ArgSetType.bit_count) { | |
| 61 | @compileError("32 arguments max are supported per format call"); | |
| 62 | } | |
| 63 | ||
| 23 | 64 | const State = enum { |
| 24 | 65 | Start, |
| 25 | OpenBrace, | |
| 66 | Positional, | |
| 26 | 67 | CloseBrace, |
| 27 | FormatString, | |
| 68 | Specifier, | |
| 69 | FormatFillAndAlign, | |
| 70 | FormatWidth, | |
| 71 | FormatPrecision, | |
| 28 | 72 | Pointer, |
| 29 | 73 | }; |
| 30 | 74 | |
| 31 | 75 | comptime var start_index = 0; |
| 32 | 76 | comptime var state = State.Start; |
| 33 | 77 | comptime var next_arg = 0; |
| 78 | comptime var maybe_pos_arg: ?comptime_int = null; | |
| 79 | comptime var used_pos_args: ArgSetType = 0; | |
| 80 | comptime var specifier_start = 0; | |
| 81 | comptime var specifier_end = 0; | |
| 82 | comptime var options = FormatOptions{}; | |
| 34 | 83 | |
| 35 | 84 | inline for (fmt) |c, i| { |
| 36 | 85 | switch (state) { |
| ... | ... | @@ -39,58 +88,183 @@ pub fn format( |
| 39 | 88 | if (start_index < i) { |
| 40 | 89 | try output(context, fmt[start_index..i]); |
| 41 | 90 | } |
| 91 | ||
| 42 | 92 | start_index = i; |
| 43 | state = State.OpenBrace; | |
| 93 | specifier_start = i + 1; | |
| 94 | specifier_end = i + 1; | |
| 95 | maybe_pos_arg = null; | |
| 96 | state = .Positional; | |
| 97 | options = FormatOptions{}; | |
| 44 | 98 | }, |
| 45 | ||
| 46 | 99 | '}' => { |
| 47 | 100 | if (start_index < i) { |
| 48 | 101 | try output(context, fmt[start_index..i]); |
| 49 | 102 | } |
| 50 | state = State.CloseBrace; | |
| 103 | state = .CloseBrace; | |
| 51 | 104 | }, |
| 52 | 105 | else => {}, |
| 53 | 106 | }, |
| 54 | .OpenBrace => switch (c) { | |
| 107 | .Positional => switch (c) { | |
| 55 | 108 | '{' => { |
| 56 | state = State.Start; | |
| 109 | state = .Start; | |
| 57 | 110 | start_index = i; |
| 58 | 111 | }, |
| 112 | '*' => { | |
| 113 | state = .Pointer; | |
| 114 | }, | |
| 115 | ':' => { | |
| 116 | state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; | |
| 117 | specifier_end = i; | |
| 118 | }, | |
| 119 | '0'...'9' => { | |
| 120 | if (maybe_pos_arg == null) { | |
| 121 | maybe_pos_arg = 0; | |
| 122 | } | |
| 123 | ||
| 124 | maybe_pos_arg.? *= 10; | |
| 125 | maybe_pos_arg.? += c - '0'; | |
| 126 | specifier_start = i + 1; | |
| 127 | ||
| 128 | if (maybe_pos_arg.? >= args.len) { | |
| 129 | @compileError("Positional value refers to non-existent argument"); | |
| 130 | } | |
| 131 | }, | |
| 59 | 132 | '}' => { |
| 60 | try formatType(args[next_arg], fmt[0..0], context, Errors, output, default_max_depth); | |
| 61 | next_arg += 1; | |
| 62 | state = State.Start; | |
| 133 | const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); | |
| 134 | ||
| 135 | try formatType( | |
| 136 | args[arg_to_print], | |
| 137 | fmt[0..0], | |
| 138 | options, | |
| 139 | context, | |
| 140 | Errors, | |
| 141 | output, | |
| 142 | default_max_depth, | |
| 143 | ); | |
| 144 | ||
| 145 | state = .Start; | |
| 63 | 146 | start_index = i + 1; |
| 64 | 147 | }, |
| 65 | '*' => state = State.Pointer, | |
| 66 | 148 | else => { |
| 67 | state = State.FormatString; | |
| 149 | state = .Specifier; | |
| 150 | specifier_start = i; | |
| 68 | 151 | }, |
| 69 | 152 | }, |
| 70 | 153 | .CloseBrace => switch (c) { |
| 71 | 154 | '}' => { |
| 72 | state = State.Start; | |
| 155 | state = .Start; | |
| 73 | 156 | start_index = i; |
| 74 | 157 | }, |
| 75 | 158 | else => @compileError("Single '}' encountered in format string"), |
| 76 | 159 | }, |
| 77 | .FormatString => switch (c) { | |
| 160 | .Specifier => switch (c) { | |
| 161 | ':' => { | |
| 162 | specifier_end = i; | |
| 163 | state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; | |
| 164 | }, | |
| 78 | 165 | '}' => { |
| 79 | const s = start_index + 1; | |
| 80 | try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth); | |
| 81 | next_arg += 1; | |
| 82 | state = State.Start; | |
| 166 | const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); | |
| 167 | ||
| 168 | try formatType( | |
| 169 | args[arg_to_print], | |
| 170 | fmt[specifier_start..i], | |
| 171 | options, | |
| 172 | context, | |
| 173 | Errors, | |
| 174 | output, | |
| 175 | default_max_depth, | |
| 176 | ); | |
| 177 | state = .Start; | |
| 83 | 178 | start_index = i + 1; |
| 84 | 179 | }, |
| 85 | 180 | else => {}, |
| 86 | 181 | }, |
| 182 | // Only entered if the format string contains a fill/align segment. | |
| 183 | .FormatFillAndAlign => switch (c) { | |
| 184 | '<' => { | |
| 185 | options.alignment = Alignment.Left; | |
| 186 | state = .FormatWidth; | |
| 187 | }, | |
| 188 | '^' => { | |
| 189 | options.alignment = Alignment.Center; | |
| 190 | state = .FormatWidth; | |
| 191 | }, | |
| 192 | '>' => { | |
| 193 | options.alignment = Alignment.Right; | |
| 194 | state = .FormatWidth; | |
| 195 | }, | |
| 196 | else => { | |
| 197 | options.fill = c; | |
| 198 | }, | |
| 199 | }, | |
| 200 | .FormatWidth => switch (c) { | |
| 201 | '0'...'9' => { | |
| 202 | if (options.width == null) { | |
| 203 | options.width = 0; | |
| 204 | } | |
| 205 | ||
| 206 | options.width.? *= 10; | |
| 207 | options.width.? += c - '0'; | |
| 208 | }, | |
| 209 | '.' => { | |
| 210 | state = .FormatPrecision; | |
| 211 | }, | |
| 212 | '}' => { | |
| 213 | const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); | |
| 214 | ||
| 215 | try formatType( | |
| 216 | args[arg_to_print], | |
| 217 | fmt[specifier_start..specifier_end], | |
| 218 | options, | |
| 219 | context, | |
| 220 | Errors, | |
| 221 | output, | |
| 222 | default_max_depth, | |
| 223 | ); | |
| 224 | state = .Start; | |
| 225 | start_index = i + 1; | |
| 226 | }, | |
| 227 | else => { | |
| 228 | @compileError("Unexpected character in width value: " ++ [_]u8{c}); | |
| 229 | }, | |
| 230 | }, | |
| 231 | .FormatPrecision => switch (c) { | |
| 232 | '0'...'9' => { | |
| 233 | if (options.precision == null) { | |
| 234 | options.precision = 0; | |
| 235 | } | |
| 236 | ||
| 237 | options.precision.? *= 10; | |
| 238 | options.precision.? += c - '0'; | |
| 239 | }, | |
| 240 | '}' => { | |
| 241 | const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); | |
| 242 | ||
| 243 | try formatType( | |
| 244 | args[arg_to_print], | |
| 245 | fmt[specifier_start..specifier_end], | |
| 246 | options, | |
| 247 | context, | |
| 248 | Errors, | |
| 249 | output, | |
| 250 | default_max_depth, | |
| 251 | ); | |
| 252 | state = .Start; | |
| 253 | start_index = i + 1; | |
| 254 | }, | |
| 255 | else => { | |
| 256 | @compileError("Unexpected character in precision value: " ++ [_]u8{c}); | |
| 257 | }, | |
| 258 | }, | |
| 87 | 259 | .Pointer => switch (c) { |
| 88 | 260 | '}' => { |
| 89 | try output(context, @typeName(@typeOf(args[next_arg]).Child)); | |
| 261 | const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); | |
| 262 | ||
| 263 | try output(context, @typeName(@typeOf(args[arg_to_print]).Child)); | |
| 90 | 264 | try output(context, "@"); |
| 91 | try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); | |
| 92 | next_arg += 1; | |
| 93 | state = State.Start; | |
| 265 | try formatInt(@ptrToInt(args[arg_to_print]), 16, false, 0, context, Errors, output); | |
| 266 | ||
| 267 | state = .Start; | |
| 94 | 268 | start_index = i + 1; |
| 95 | 269 | }, |
| 96 | 270 | else => @compileError("Unexpected format character after '*'"), |
| ... | ... | @@ -98,7 +272,13 @@ pub fn format( |
| 98 | 272 | } |
| 99 | 273 | } |
| 100 | 274 | comptime { |
| 101 | if (args.len != next_arg) { | |
| 275 | // All arguments must have been printed but we allow mixing positional and fixed to achieve this. | |
| 276 | var i: usize = 0; | |
| 277 | inline while (i < next_arg) : (i += 1) { | |
| 278 | used_pos_args |= 1 << i; | |
| 279 | } | |
| 280 | ||
| 281 | if (@popCount(ArgSetType, used_pos_args) != args.len) { | |
| 102 | 282 | @compileError("Unused arguments"); |
| 103 | 283 | } |
| 104 | 284 | if (state != State.Start) { |
| ... | ... | @@ -113,6 +293,7 @@ pub fn format( |
| 113 | 293 | pub fn formatType( |
| 114 | 294 | value: var, |
| 115 | 295 | comptime fmt: []const u8, |
| 296 | comptime options: FormatOptions, | |
| 116 | 297 | context: var, |
| 117 | 298 | comptime Errors: type, |
| 118 | 299 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -121,7 +302,7 @@ pub fn formatType( |
| 121 | 302 | const T = @typeOf(value); |
| 122 | 303 | switch (@typeInfo(T)) { |
| 123 | 304 | .ComptimeInt, .Int, .Float => { |
| 124 | return formatValue(value, fmt, context, Errors, output); | |
| 305 | return formatValue(value, fmt, options, context, Errors, output); | |
| 125 | 306 | }, |
| 126 | 307 | .Void => { |
| 127 | 308 | return output(context, "void"); |
| ... | ... | @@ -131,16 +312,16 @@ pub fn formatType( |
| 131 | 312 | }, |
| 132 | 313 | .Optional => { |
| 133 | 314 | if (value) |payload| { |
| 134 | return formatType(payload, fmt, context, Errors, output, max_depth); | |
| 315 | return formatType(payload, fmt, options, context, Errors, output, max_depth); | |
| 135 | 316 | } else { |
| 136 | 317 | return output(context, "null"); |
| 137 | 318 | } |
| 138 | 319 | }, |
| 139 | 320 | .ErrorUnion => { |
| 140 | 321 | if (value) |payload| { |
| 141 | return formatType(payload, fmt, context, Errors, output, max_depth); | |
| 322 | return formatType(payload, fmt, options, context, Errors, output, max_depth); | |
| 142 | 323 | } else |err| { |
| 143 | return formatType(err, fmt, context, Errors, output, max_depth); | |
| 324 | return formatType(err, fmt, options, context, Errors, output, max_depth); | |
| 144 | 325 | } |
| 145 | 326 | }, |
| 146 | 327 | .ErrorSet => { |
| ... | ... | @@ -152,16 +333,16 @@ pub fn formatType( |
| 152 | 333 | }, |
| 153 | 334 | .Enum => { |
| 154 | 335 | if (comptime std.meta.trait.hasFn("format")(T)) { |
| 155 | return value.format(fmt, context, Errors, output); | |
| 336 | return value.format(fmt, options, context, Errors, output); | |
| 156 | 337 | } |
| 157 | 338 | |
| 158 | 339 | try output(context, @typeName(T)); |
| 159 | 340 | try output(context, "."); |
| 160 | return formatType(@tagName(value), "", context, Errors, output, max_depth); | |
| 341 | return formatType(@tagName(value), "", options, context, Errors, output, max_depth); | |
| 161 | 342 | }, |
| 162 | 343 | .Union => { |
| 163 | 344 | if (comptime std.meta.trait.hasFn("format")(T)) { |
| 164 | return value.format(fmt, context, Errors, output); | |
| 345 | return value.format(fmt, options, context, Errors, output); | |
| 165 | 346 | } |
| 166 | 347 | |
| 167 | 348 | try output(context, @typeName(T)); |
| ... | ... | @@ -175,7 +356,7 @@ pub fn formatType( |
| 175 | 356 | try output(context, " = "); |
| 176 | 357 | inline for (info.fields) |u_field| { |
| 177 | 358 | if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { |
| 178 | try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1); | |
| 359 | try formatType(@field(value, u_field.name), "", options, context, Errors, output, max_depth - 1); | |
| 179 | 360 | } |
| 180 | 361 | } |
| 181 | 362 | try output(context, " }"); |
| ... | ... | @@ -185,7 +366,7 @@ pub fn formatType( |
| 185 | 366 | }, |
| 186 | 367 | .Struct => { |
| 187 | 368 | if (comptime std.meta.trait.hasFn("format")(T)) { |
| 188 | return value.format(fmt, context, Errors, output); | |
| 369 | return value.format(fmt, options, context, Errors, output); | |
| 189 | 370 | } |
| 190 | 371 | |
| 191 | 372 | try output(context, @typeName(T)); |
| ... | ... | @@ -201,7 +382,7 @@ pub fn formatType( |
| 201 | 382 | } |
| 202 | 383 | try output(context, @memberName(T, field_i)); |
| 203 | 384 | try output(context, " = "); |
| 204 | try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1); | |
| 385 | try formatType(@field(value, @memberName(T, field_i)), "", options, context, Errors, output, max_depth - 1); | |
| 205 | 386 | } |
| 206 | 387 | try output(context, " }"); |
| 207 | 388 | }, |
| ... | ... | @@ -209,12 +390,12 @@ pub fn formatType( |
| 209 | 390 | .One => switch (@typeInfo(ptr_info.child)) { |
| 210 | 391 | builtin.TypeId.Array => |info| { |
| 211 | 392 | if (info.child == u8) { |
| 212 | return formatText(value, fmt, context, Errors, output); | |
| 393 | return formatText(value, fmt, options, context, Errors, output); | |
| 213 | 394 | } |
| 214 | 395 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); |
| 215 | 396 | }, |
| 216 | 397 | builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { |
| 217 | return formatType(value.*, fmt, context, Errors, output, max_depth); | |
| 398 | return formatType(value.*, fmt, options, context, Errors, output, max_depth); | |
| 218 | 399 | }, |
| 219 | 400 | else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), |
| 220 | 401 | }, |
| ... | ... | @@ -222,17 +403,17 @@ pub fn formatType( |
| 222 | 403 | if (ptr_info.child == u8) { |
| 223 | 404 | if (fmt.len > 0 and fmt[0] == 's') { |
| 224 | 405 | const len = mem.len(u8, value); |
| 225 | return formatText(value[0..len], fmt, context, Errors, output); | |
| 406 | return formatText(value[0..len], fmt, options, context, Errors, output); | |
| 226 | 407 | } |
| 227 | 408 | } |
| 228 | 409 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); |
| 229 | 410 | }, |
| 230 | 411 | .Slice => { |
| 231 | 412 | if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { |
| 232 | return formatText(value, fmt, context, Errors, output); | |
| 413 | return formatText(value, fmt, options, context, Errors, output); | |
| 233 | 414 | } |
| 234 | 415 | if (ptr_info.child == u8) { |
| 235 | return formatText(value, fmt, context, Errors, output); | |
| 416 | return formatText(value, fmt, options, context, Errors, output); | |
| 236 | 417 | } |
| 237 | 418 | return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr)); |
| 238 | 419 | }, |
| ... | ... | @@ -242,7 +423,7 @@ pub fn formatType( |
| 242 | 423 | }, |
| 243 | 424 | .Array => |info| { |
| 244 | 425 | if (info.child == u8) { |
| 245 | return formatText(value, fmt, context, Errors, output); | |
| 426 | return formatText(value, fmt, options, context, Errors, output); | |
| 246 | 427 | } |
| 247 | 428 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); |
| 248 | 429 | }, |
| ... | ... | @@ -256,28 +437,25 @@ pub fn formatType( |
| 256 | 437 | fn formatValue( |
| 257 | 438 | value: var, |
| 258 | 439 | comptime fmt: []const u8, |
| 440 | comptime options: FormatOptions, | |
| 259 | 441 | context: var, |
| 260 | 442 | comptime Errors: type, |
| 261 | 443 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 262 | 444 | ) Errors!void { |
| 263 | if (fmt.len > 0 and fmt[0] == 'B') { | |
| 264 | comptime var width: ?usize = null; | |
| 265 | if (fmt.len > 1) { | |
| 266 | if (fmt[1] == 'i') { | |
| 267 | if (fmt.len > 2) { | |
| 268 | width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); | |
| 269 | } | |
| 270 | return formatBytes(value, width, 1024, context, Errors, output); | |
| 271 | } | |
| 272 | width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | |
| 273 | } | |
| 274 | return formatBytes(value, width, 1000, context, Errors, output); | |
| 445 | if (comptime std.mem.eql(u8, fmt, "B")) { | |
| 446 | // TODO https://github.com/ziglang/zig/issues/2725 | |
| 447 | if (options.width) |w| return formatBytes(value, w, 1000, context, Errors, output); | |
| 448 | return formatBytes(value, null, 1000, context, Errors, output); | |
| 449 | } else if (comptime std.mem.eql(u8, fmt, "Bi")) { | |
| 450 | // TODO https://github.com/ziglang/zig/issues/2725 | |
| 451 | if (options.width) |w| return formatBytes(value, w, 1024, context, Errors, output); | |
| 452 | return formatBytes(value, null, 1024, context, Errors, output); | |
| 275 | 453 | } |
| 276 | 454 | |
| 277 | 455 | const T = @typeOf(value); |
| 278 | 456 | switch (@typeId(T)) { |
| 279 | .Float => return formatFloatValue(value, fmt, context, Errors, output), | |
| 280 | .Int, .ComptimeInt => return formatIntValue(value, fmt, context, Errors, output), | |
| 457 | .Float => return formatFloatValue(value, fmt, options, context, Errors, output), | |
| 458 | .Int, .ComptimeInt => return formatIntValue(value, fmt, options, context, Errors, output), | |
| 281 | 459 | else => comptime unreachable, |
| 282 | 460 | } |
| 283 | 461 | } |
| ... | ... | @@ -285,13 +463,13 @@ fn formatValue( |
| 285 | 463 | pub fn formatIntValue( |
| 286 | 464 | value: var, |
| 287 | 465 | comptime fmt: []const u8, |
| 466 | comptime options: FormatOptions, | |
| 288 | 467 | context: var, |
| 289 | 468 | comptime Errors: type, |
| 290 | 469 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 291 | 470 | ) Errors!void { |
| 292 | 471 | comptime var radix = 10; |
| 293 | 472 | comptime var uppercase = false; |
| 294 | comptime var width = 0; | |
| 295 | 473 | |
| 296 | 474 | const int_value = if (@typeOf(value) == comptime_int) blk: { |
| 297 | 475 | const Int = math.IntFittingRange(value, value); |
| ... | ... | @@ -299,83 +477,75 @@ pub fn formatIntValue( |
| 299 | 477 | } else |
| 300 | 478 | value; |
| 301 | 479 | |
| 302 | if (fmt.len > 0) { | |
| 303 | switch (fmt[0]) { | |
| 304 | 'c' => { | |
| 305 | if (@typeOf(int_value).bit_count <= 8) { | |
| 306 | if (fmt.len > 1) | |
| 307 | @compileError("Unknown format character: " ++ [_]u8{fmt[1]}); | |
| 308 | return formatAsciiChar(u8(int_value), context, Errors, output); | |
| 309 | } | |
| 310 | }, | |
| 311 | 'b' => { | |
| 312 | radix = 2; | |
| 313 | uppercase = false; | |
| 314 | width = 0; | |
| 315 | }, | |
| 316 | 'd' => { | |
| 317 | radix = 10; | |
| 318 | uppercase = false; | |
| 319 | width = 0; | |
| 320 | }, | |
| 321 | 'x' => { | |
| 322 | radix = 16; | |
| 323 | uppercase = false; | |
| 324 | width = 0; | |
| 325 | }, | |
| 326 | 'X' => { | |
| 327 | radix = 16; | |
| 328 | uppercase = true; | |
| 329 | width = 0; | |
| 330 | }, | |
| 331 | else => @compileError("Unknown format character: " ++ [_]u8{fmt[0]}), | |
| 480 | if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) { | |
| 481 | radix = 10; | |
| 482 | uppercase = false; | |
| 483 | } else if (comptime std.mem.eql(u8, fmt, "c")) { | |
| 484 | if (@typeOf(int_value).bit_count <= 8) { | |
| 485 | return formatAsciiChar(u8(int_value), context, Errors, output); | |
| 486 | } else { | |
| 487 | @compileError("Cannot print integer that is larger than 8 bits as a ascii"); | |
| 332 | 488 | } |
| 333 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | |
| 489 | } else if (comptime std.mem.eql(u8, fmt, "b")) { | |
| 490 | radix = 2; | |
| 491 | uppercase = false; | |
| 492 | } else if (comptime std.mem.eql(u8, fmt, "x")) { | |
| 493 | radix = 16; | |
| 494 | uppercase = false; | |
| 495 | } else if (comptime std.mem.eql(u8, fmt, "X")) { | |
| 496 | radix = 16; | |
| 497 | uppercase = true; | |
| 498 | } else { | |
| 499 | @compileError("Unknown format string: '" ++ fmt ++ "'"); | |
| 334 | 500 | } |
| 335 | return formatInt(int_value, radix, uppercase, width, context, Errors, output); | |
| 501 | ||
| 502 | // TODO https://github.com/ziglang/zig/issues/2725 | |
| 503 | if (options.width) |w| return formatInt(int_value, radix, uppercase, w, context, Errors, output); | |
| 504 | return formatInt(int_value, radix, uppercase, 0, context, Errors, output); | |
| 336 | 505 | } |
| 337 | 506 | |
| 338 | 507 | fn formatFloatValue( |
| 339 | 508 | value: var, |
| 340 | 509 | comptime fmt: []const u8, |
| 510 | comptime options: FormatOptions, | |
| 341 | 511 | context: var, |
| 342 | 512 | comptime Errors: type, |
| 343 | 513 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 344 | 514 | ) Errors!void { |
| 345 | comptime var width: ?usize = null; | |
| 346 | comptime var float_fmt = 'e'; | |
| 347 | if (fmt.len > 0) { | |
| 348 | float_fmt = fmt[0]; | |
| 349 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | |
| 350 | } | |
| 351 | ||
| 352 | switch (float_fmt) { | |
| 353 | 'e' => try formatFloatScientific(value, width, context, Errors, output), | |
| 354 | '.' => try formatFloatDecimal(value, width, context, Errors, output), | |
| 355 | else => @compileError("Unknown format character: " ++ [_]u8{float_fmt}), | |
| 515 | if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { | |
| 516 | // TODO https://github.com/ziglang/zig/issues/2725 | |
| 517 | if (options.precision) |p| return formatFloatScientific(value, p, context, Errors, output); | |
| 518 | return formatFloatScientific(value, null, context, Errors, output); | |
| 519 | } else if (comptime std.mem.eql(u8, fmt, "d")) { | |
| 520 | // TODO https://github.com/ziglang/zig/issues/2725 | |
| 521 | if (options.precision) |p| return formatFloatDecimal(value, p, context, Errors, output); | |
| 522 | return formatFloatDecimal(value, null, context, Errors, output); | |
| 523 | } else { | |
| 524 | @compileError("Unknown format string: '" ++ fmt ++ "'"); | |
| 356 | 525 | } |
| 357 | 526 | } |
| 358 | 527 | |
| 359 | 528 | pub fn formatText( |
| 360 | 529 | bytes: []const u8, |
| 361 | 530 | comptime fmt: []const u8, |
| 531 | comptime options: FormatOptions, | |
| 362 | 532 | context: var, |
| 363 | 533 | comptime Errors: type, |
| 364 | 534 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 365 | 535 | ) Errors!void { |
| 366 | if (fmt.len > 0) { | |
| 367 | if (fmt[0] == 's') { | |
| 368 | comptime var width = 0; | |
| 369 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | |
| 370 | return formatBuf(bytes, width, context, Errors, output); | |
| 371 | } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) { | |
| 372 | for (bytes) |c| { | |
| 373 | try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); | |
| 374 | } | |
| 375 | return; | |
| 376 | } else @compileError("Unknown format character: " ++ [_]u8{fmt[0]}); | |
| 536 | if (fmt.len == 0) { | |
| 537 | return output(context, bytes); | |
| 538 | } else if (comptime std.mem.eql(u8, fmt, "s")) { | |
| 539 | if (options.width) |w| return formatBuf(bytes, w, context, Errors, output); | |
| 540 | return formatBuf(bytes, 0, context, Errors, output); | |
| 541 | } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { | |
| 542 | for (bytes) |c| { | |
| 543 | try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); | |
| 544 | } | |
| 545 | return; | |
| 546 | } else { | |
| 547 | @compileError("Unknown format string: '" ++ fmt ++ "'"); | |
| 377 | 548 | } |
| 378 | return output(context, bytes); | |
| 379 | 549 | } |
| 380 | 550 | |
| 381 | 551 | pub fn formatAsciiChar( |
| ... | ... | @@ -868,7 +1038,7 @@ test "parseUnsigned" { |
| 868 | 1038 | |
| 869 | 1039 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; |
| 870 | 1040 | |
| 871 | test "fmt.parseFloat" { | |
| 1041 | test "parseFloat" { | |
| 872 | 1042 | _ = @import("fmt/parse_float.zig"); |
| 873 | 1043 | } |
| 874 | 1044 | |
| ... | ... | @@ -960,7 +1130,7 @@ test "parse unsigned comptime" { |
| 960 | 1130 | } |
| 961 | 1131 | } |
| 962 | 1132 | |
| 963 | test "fmt.optional" { | |
| 1133 | test "optional" { | |
| 964 | 1134 | { |
| 965 | 1135 | const value: ?i32 = 1234; |
| 966 | 1136 | try testFmt("optional: 1234\n", "optional: {}\n", value); |
| ... | ... | @@ -971,7 +1141,7 @@ test "fmt.optional" { |
| 971 | 1141 | } |
| 972 | 1142 | } |
| 973 | 1143 | |
| 974 | test "fmt.error" { | |
| 1144 | test "error" { | |
| 975 | 1145 | { |
| 976 | 1146 | const value: anyerror!i32 = 1234; |
| 977 | 1147 | try testFmt("error union: 1234\n", "error union: {}\n", value); |
| ... | ... | @@ -982,14 +1152,14 @@ test "fmt.error" { |
| 982 | 1152 | } |
| 983 | 1153 | } |
| 984 | 1154 | |
| 985 | test "fmt.int.small" { | |
| 1155 | test "int.small" { | |
| 986 | 1156 | { |
| 987 | 1157 | const value: u3 = 0b101; |
| 988 | 1158 | try testFmt("u3: 5\n", "u3: {}\n", value); |
| 989 | 1159 | } |
| 990 | 1160 | } |
| 991 | 1161 | |
| 992 | test "fmt.int.specifier" { | |
| 1162 | test "int.specifier" { | |
| 993 | 1163 | { |
| 994 | 1164 | const value: u8 = 'a'; |
| 995 | 1165 | try testFmt("u8: a\n", "u8: {c}\n", value); |
| ... | ... | @@ -1000,27 +1170,31 @@ test "fmt.int.specifier" { |
| 1000 | 1170 | } |
| 1001 | 1171 | } |
| 1002 | 1172 | |
| 1003 | test "fmt.buffer" { | |
| 1173 | test "int.padded" { | |
| 1174 | try testFmt("u8: '0001'", "u8: '{:4}'", u8(1)); | |
| 1175 | } | |
| 1176 | ||
| 1177 | test "buffer" { | |
| 1004 | 1178 | { |
| 1005 | 1179 | var buf1: [32]u8 = undefined; |
| 1006 | 1180 | var context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1007 | try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); | |
| 1181 | try formatType(1234, "", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); | |
| 1008 | 1182 | var res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1009 | 1183 | testing.expect(mem.eql(u8, res, "1234")); |
| 1010 | 1184 | |
| 1011 | 1185 | context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1012 | try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); | |
| 1186 | try formatType('a', "c", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); | |
| 1013 | 1187 | res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1014 | 1188 | testing.expect(mem.eql(u8, res, "a")); |
| 1015 | 1189 | |
| 1016 | 1190 | context = BufPrintContext{ .remaining = buf1[0..] }; |
| 1017 | try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); | |
| 1191 | try formatType(0b1100, "b", FormatOptions{}, &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth); | |
| 1018 | 1192 | res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1019 | 1193 | testing.expect(mem.eql(u8, res, "1100")); |
| 1020 | 1194 | } |
| 1021 | 1195 | } |
| 1022 | 1196 | |
| 1023 | test "fmt.array" { | |
| 1197 | test "array" { | |
| 1024 | 1198 | { |
| 1025 | 1199 | const value: [3]u8 = "abc"; |
| 1026 | 1200 | try testFmt("array: abc\n", "array: {}\n", value); |
| ... | ... | @@ -1035,7 +1209,7 @@ test "fmt.array" { |
| 1035 | 1209 | } |
| 1036 | 1210 | } |
| 1037 | 1211 | |
| 1038 | test "fmt.slice" { | |
| 1212 | test "slice" { | |
| 1039 | 1213 | { |
| 1040 | 1214 | const value: []const u8 = "abc"; |
| 1041 | 1215 | try testFmt("slice: abc\n", "slice: {}\n", value); |
| ... | ... | @@ -1045,11 +1219,11 @@ test "fmt.slice" { |
| 1045 | 1219 | try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value); |
| 1046 | 1220 | } |
| 1047 | 1221 | |
| 1048 | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); | |
| 1222 | try testFmt("buf: Test \n", "buf: {s:5}\n", "Test"); | |
| 1049 | 1223 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); |
| 1050 | 1224 | } |
| 1051 | 1225 | |
| 1052 | test "fmt.pointer" { | |
| 1226 | test "pointer" { | |
| 1053 | 1227 | { |
| 1054 | 1228 | const value = @intToPtr(*i32, 0xdeadbeef); |
| 1055 | 1229 | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); |
| ... | ... | @@ -1065,17 +1239,17 @@ test "fmt.pointer" { |
| 1065 | 1239 | } |
| 1066 | 1240 | } |
| 1067 | 1241 | |
| 1068 | test "fmt.cstr" { | |
| 1242 | test "cstr" { | |
| 1069 | 1243 | try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); |
| 1070 | try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C"); | |
| 1244 | try testFmt("cstr: Test C \n", "cstr: {s:10}\n", c"Test C"); | |
| 1071 | 1245 | } |
| 1072 | 1246 | |
| 1073 | test "fmt.filesize" { | |
| 1247 | test "filesize" { | |
| 1074 | 1248 | try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); |
| 1075 | try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); | |
| 1249 | try testFmt("file size: 66.06MB\n", "file size: {B:2}\n", usize(63 * 1024 * 1024)); | |
| 1076 | 1250 | } |
| 1077 | 1251 | |
| 1078 | test "fmt.struct" { | |
| 1252 | test "struct" { | |
| 1079 | 1253 | { |
| 1080 | 1254 | const Struct = struct { |
| 1081 | 1255 | field: u8, |
| ... | ... | @@ -1094,7 +1268,7 @@ test "fmt.struct" { |
| 1094 | 1268 | } |
| 1095 | 1269 | } |
| 1096 | 1270 | |
| 1097 | test "fmt.enum" { | |
| 1271 | test "enum" { | |
| 1098 | 1272 | const Enum = enum { |
| 1099 | 1273 | One, |
| 1100 | 1274 | Two, |
| ... | ... | @@ -1104,229 +1278,71 @@ test "fmt.enum" { |
| 1104 | 1278 | try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); |
| 1105 | 1279 | } |
| 1106 | 1280 | |
| 1107 | test "fmt.float.scientific" { | |
| 1108 | { | |
| 1109 | var buf1: [32]u8 = undefined; | |
| 1110 | const value: f32 = 1.34; | |
| 1111 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | |
| 1112 | testing.expect(mem.eql(u8, result, "f32: 1.34000003e+00\n")); | |
| 1113 | } | |
| 1114 | { | |
| 1115 | var buf1: [32]u8 = undefined; | |
| 1116 | const value: f32 = 12.34; | |
| 1117 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | |
| 1118 | testing.expect(mem.eql(u8, result, "f32: 1.23400001e+01\n")); | |
| 1119 | } | |
| 1120 | { | |
| 1121 | var buf1: [32]u8 = undefined; | |
| 1122 | const value: f64 = -12.34e10; | |
| 1123 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | |
| 1124 | testing.expect(mem.eql(u8, result, "f64: -1.234e+11\n")); | |
| 1125 | } | |
| 1126 | { | |
| 1127 | // This fails on release due to a minor rounding difference. | |
| 1128 | // --release-fast outputs 9.999960000000001e-40 vs. the expected. | |
| 1129 | // TODO fix this, it should be the same in Debug and ReleaseFast | |
| 1130 | if (builtin.mode == builtin.Mode.Debug) { | |
| 1131 | var buf1: [32]u8 = undefined; | |
| 1132 | const value: f64 = 9.999960e-40; | |
| 1133 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | |
| 1134 | testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n")); | |
| 1135 | } | |
| 1136 | } | |
| 1281 | test "float.scientific" { | |
| 1282 | try testFmt("f32: 1.34000003e+00", "f32: {e}", f32(1.34)); | |
| 1283 | try testFmt("f32: 1.23400001e+01", "f32: {e}", f32(12.34)); | |
| 1284 | try testFmt("f64: -1.234e+11", "f64: {e}", f64(-12.34e10)); | |
| 1285 | try testFmt("f64: 9.99996e-40", "f64: {e}", f64(9.999960e-40)); | |
| 1137 | 1286 | } |
| 1138 | 1287 | |
| 1139 | test "fmt.float.scientific.precision" { | |
| 1140 | { | |
| 1141 | var buf1: [32]u8 = undefined; | |
| 1142 | const value: f64 = 1.409706e-42; | |
| 1143 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | |
| 1144 | testing.expect(mem.eql(u8, result, "f64: 1.40971e-42\n")); | |
| 1145 | } | |
| 1146 | { | |
| 1147 | var buf1: [32]u8 = undefined; | |
| 1148 | const value: f64 = @bitCast(f32, u32(814313563)); | |
| 1149 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | |
| 1150 | testing.expect(mem.eql(u8, result, "f64: 1.00000e-09\n")); | |
| 1151 | } | |
| 1152 | { | |
| 1153 | var buf1: [32]u8 = undefined; | |
| 1154 | const value: f64 = @bitCast(f32, u32(1006632960)); | |
| 1155 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | |
| 1156 | testing.expect(mem.eql(u8, result, "f64: 7.81250e-03\n")); | |
| 1157 | } | |
| 1158 | { | |
| 1159 | // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. | |
| 1160 | // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. | |
| 1161 | var buf1: [32]u8 = undefined; | |
| 1162 | const value: f64 = @bitCast(f32, u32(1203982400)); | |
| 1163 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | |
| 1164 | testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n")); | |
| 1165 | } | |
| 1288 | test "float.scientific.precision" { | |
| 1289 | try testFmt("f64: 1.40971e-42", "f64: {e:.5}", f64(1.409706e-42)); | |
| 1290 | try testFmt("f64: 1.00000e-09", "f64: {e:.5}", f64(@bitCast(f32, u32(814313563)))); | |
| 1291 | try testFmt("f64: 7.81250e-03", "f64: {e:.5}", f64(@bitCast(f32, u32(1006632960)))); | |
| 1292 | // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. | |
| 1293 | // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. | |
| 1294 | try testFmt("f64: 1.00001e+05", "f64: {e:.5}", f64(@bitCast(f32, u32(1203982400)))); | |
| 1166 | 1295 | } |
| 1167 | 1296 | |
| 1168 | test "fmt.float.special" { | |
| 1169 | { | |
| 1170 | var buf1: [32]u8 = undefined; | |
| 1171 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); | |
| 1172 | testing.expect(mem.eql(u8, result, "f64: nan\n")); | |
| 1173 | } | |
| 1297 | test "float.special" { | |
| 1298 | try testFmt("f64: nan", "f64: {}", math.nan_f64); | |
| 1299 | // negative nan is not defined by IEE 754, | |
| 1300 | // and ARM thus normalizes it to positive nan | |
| 1174 | 1301 | if (builtin.arch != builtin.Arch.arm) { |
| 1175 | // negative nan is not defined by IEE 754, | |
| 1176 | // and ARM thus normalizes it to positive nan | |
| 1177 | var buf1: [32]u8 = undefined; | |
| 1178 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); | |
| 1179 | testing.expect(mem.eql(u8, result, "f64: -nan\n")); | |
| 1180 | } | |
| 1181 | { | |
| 1182 | var buf1: [32]u8 = undefined; | |
| 1183 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); | |
| 1184 | testing.expect(mem.eql(u8, result, "f64: inf\n")); | |
| 1185 | } | |
| 1186 | { | |
| 1187 | var buf1: [32]u8 = undefined; | |
| 1188 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); | |
| 1189 | testing.expect(mem.eql(u8, result, "f64: -inf\n")); | |
| 1302 | try testFmt("f64: -nan", "f64: {}", -math.nan_f64); | |
| 1190 | 1303 | } |
| 1304 | try testFmt("f64: inf", "f64: {}", math.inf_f64); | |
| 1305 | try testFmt("f64: -inf", "f64: {}", -math.inf_f64); | |
| 1191 | 1306 | } |
| 1192 | 1307 | |
| 1193 | test "fmt.float.decimal" { | |
| 1194 | { | |
| 1195 | var buf1: [64]u8 = undefined; | |
| 1196 | const value: f64 = 1.52314e+29; | |
| 1197 | const result = try bufPrint(buf1[0..], "f64: {.}\n", value); | |
| 1198 | testing.expect(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); | |
| 1199 | } | |
| 1200 | { | |
| 1201 | var buf1: [32]u8 = undefined; | |
| 1202 | const value: f32 = 1.1234; | |
| 1203 | const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); | |
| 1204 | testing.expect(mem.eql(u8, result, "f32: 1.1\n")); | |
| 1205 | } | |
| 1206 | { | |
| 1207 | var buf1: [32]u8 = undefined; | |
| 1208 | const value: f32 = 1234.567; | |
| 1209 | const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); | |
| 1210 | testing.expect(mem.eql(u8, result, "f32: 1234.57\n")); | |
| 1211 | } | |
| 1212 | { | |
| 1213 | var buf1: [32]u8 = undefined; | |
| 1214 | const value: f32 = -11.1234; | |
| 1215 | const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); | |
| 1216 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). | |
| 1217 | // -11.12339... is rounded back up to -11.1234 | |
| 1218 | testing.expect(mem.eql(u8, result, "f32: -11.1234\n")); | |
| 1219 | } | |
| 1220 | { | |
| 1221 | var buf1: [32]u8 = undefined; | |
| 1222 | const value: f32 = 91.12345; | |
| 1223 | const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); | |
| 1224 | testing.expect(mem.eql(u8, result, "f32: 91.12345\n")); | |
| 1225 | } | |
| 1226 | { | |
| 1227 | var buf1: [32]u8 = undefined; | |
| 1228 | const value: f64 = 91.12345678901235; | |
| 1229 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); | |
| 1230 | testing.expect(mem.eql(u8, result, "f64: 91.1234567890\n")); | |
| 1231 | } | |
| 1232 | { | |
| 1233 | var buf1: [32]u8 = undefined; | |
| 1234 | const value: f64 = 0.0; | |
| 1235 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1236 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | |
| 1237 | } | |
| 1238 | { | |
| 1239 | var buf1: [32]u8 = undefined; | |
| 1240 | const value: f64 = 5.700; | |
| 1241 | const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); | |
| 1242 | testing.expect(mem.eql(u8, result, "f64: 6\n")); | |
| 1243 | } | |
| 1244 | { | |
| 1245 | var buf1: [32]u8 = undefined; | |
| 1246 | const value: f64 = 9.999; | |
| 1247 | const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); | |
| 1248 | testing.expect(mem.eql(u8, result, "f64: 10.0\n")); | |
| 1249 | } | |
| 1250 | { | |
| 1251 | var buf1: [32]u8 = undefined; | |
| 1252 | const value: f64 = 1.0; | |
| 1253 | const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); | |
| 1254 | testing.expect(mem.eql(u8, result, "f64: 1.000\n")); | |
| 1255 | } | |
| 1256 | { | |
| 1257 | var buf1: [32]u8 = undefined; | |
| 1258 | const value: f64 = 0.0003; | |
| 1259 | const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); | |
| 1260 | testing.expect(mem.eql(u8, result, "f64: 0.00030000\n")); | |
| 1261 | } | |
| 1262 | { | |
| 1263 | var buf1: [32]u8 = undefined; | |
| 1264 | const value: f64 = 1.40130e-45; | |
| 1265 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1266 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | |
| 1267 | } | |
| 1268 | { | |
| 1269 | var buf1: [32]u8 = undefined; | |
| 1270 | const value: f64 = 9.999960e-40; | |
| 1271 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1272 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | |
| 1273 | } | |
| 1308 | test "float.decimal" { | |
| 1309 | try testFmt("f64: 152314000000000000000000000000", "f64: {d}", f64(1.52314e+29)); | |
| 1310 | try testFmt("f32: 1.1", "f32: {d:.1}", f32(1.1234)); | |
| 1311 | try testFmt("f32: 1234.57", "f32: {d:.2}", f32(1234.567)); | |
| 1312 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). | |
| 1313 | // -11.12339... is rounded back up to -11.1234 | |
| 1314 | try testFmt("f32: -11.1234", "f32: {d:.4}", f32(-11.1234)); | |
| 1315 | try testFmt("f32: 91.12345", "f32: {d:.5}", f32(91.12345)); | |
| 1316 | try testFmt("f64: 91.1234567890", "f64: {d:.10}", f64(91.12345678901235)); | |
| 1317 | try testFmt("f64: 0.00000", "f64: {d:.5}", f64(0.0)); | |
| 1318 | try testFmt("f64: 6", "f64: {d:.0}", f64(5.700)); | |
| 1319 | try testFmt("f64: 10.0", "f64: {d:.1}", f64(9.999)); | |
| 1320 | try testFmt("f64: 1.000", "f64: {d:.3}", f64(1.0)); | |
| 1321 | try testFmt("f64: 0.00030000", "f64: {d:.8}", f64(0.0003)); | |
| 1322 | try testFmt("f64: 0.00000", "f64: {d:.5}", f64(1.40130e-45)); | |
| 1323 | try testFmt("f64: 0.00000", "f64: {d:.5}", f64(9.999960e-40)); | |
| 1274 | 1324 | } |
| 1275 | 1325 | |
| 1276 | test "fmt.float.libc.sanity" { | |
| 1277 | { | |
| 1278 | var buf1: [32]u8 = undefined; | |
| 1279 | const value: f64 = f64(@bitCast(f32, u32(916964781))); | |
| 1280 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1281 | testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); | |
| 1282 | } | |
| 1283 | { | |
| 1284 | var buf1: [32]u8 = undefined; | |
| 1285 | const value: f64 = f64(@bitCast(f32, u32(925353389))); | |
| 1286 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1287 | testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); | |
| 1288 | } | |
| 1289 | { | |
| 1290 | var buf1: [32]u8 = undefined; | |
| 1291 | const value: f64 = f64(@bitCast(f32, u32(1036831278))); | |
| 1292 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1293 | testing.expect(mem.eql(u8, result, "f64: 0.10000\n")); | |
| 1294 | } | |
| 1295 | { | |
| 1296 | var buf1: [32]u8 = undefined; | |
| 1297 | const value: f64 = f64(@bitCast(f32, u32(1065353133))); | |
| 1298 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1299 | testing.expect(mem.eql(u8, result, "f64: 1.00000\n")); | |
| 1300 | } | |
| 1301 | { | |
| 1302 | var buf1: [32]u8 = undefined; | |
| 1303 | const value: f64 = f64(@bitCast(f32, u32(1092616192))); | |
| 1304 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1305 | testing.expect(mem.eql(u8, result, "f64: 10.00000\n")); | |
| 1306 | } | |
| 1326 | test "float.libc.sanity" { | |
| 1327 | try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(916964781)))); | |
| 1328 | try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(925353389)))); | |
| 1329 | try testFmt("f64: 0.10000", "f64: {d:.5}", f64(@bitCast(f32, u32(1036831278)))); | |
| 1330 | try testFmt("f64: 1.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1065353133)))); | |
| 1331 | try testFmt("f64: 10.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1092616192)))); | |
| 1332 | ||
| 1307 | 1333 | // libc differences |
| 1308 | { | |
| 1309 | var buf1: [32]u8 = undefined; | |
| 1310 | // This is 0.015625 exactly according to gdb. We thus round down, | |
| 1311 | // however glibc rounds up for some reason. This occurs for all | |
| 1312 | // floats of the form x.yyyy25 on a precision point. | |
| 1313 | const value: f64 = f64(@bitCast(f32, u32(1015021568))); | |
| 1314 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1315 | testing.expect(mem.eql(u8, result, "f64: 0.01563\n")); | |
| 1316 | } | |
| 1317 | // std-windows-x86_64-Debug-bare test case fails | |
| 1318 | { | |
| 1319 | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 | |
| 1320 | // also rounds to 630 so I'm inclined to believe libc is not | |
| 1321 | // optimal here. | |
| 1322 | var buf1: [32]u8 = undefined; | |
| 1323 | const value: f64 = f64(@bitCast(f32, u32(1518338049))); | |
| 1324 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 1325 | testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); | |
| 1326 | } | |
| 1334 | // | |
| 1335 | // This is 0.015625 exactly according to gdb. We thus round down, | |
| 1336 | // however glibc rounds up for some reason. This occurs for all | |
| 1337 | // floats of the form x.yyyy25 on a precision point. | |
| 1338 | try testFmt("f64: 0.01563", "f64: {d:.5}", f64(@bitCast(f32, u32(1015021568)))); | |
| 1339 | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 | |
| 1340 | // also rounds to 630 so I'm inclined to believe libc is not | |
| 1341 | // optimal here. | |
| 1342 | try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1518338049)))); | |
| 1327 | 1343 | } |
| 1328 | 1344 | |
| 1329 | test "fmt.custom" { | |
| 1345 | test "custom" { | |
| 1330 | 1346 | const Vec2 = struct { |
| 1331 | 1347 | const SelfType = @This(); |
| 1332 | 1348 | x: f32, |
| ... | ... | @@ -1335,20 +1351,17 @@ test "fmt.custom" { |
| 1335 | 1351 | pub fn format( |
| 1336 | 1352 | self: SelfType, |
| 1337 | 1353 | comptime fmt: []const u8, |
| 1354 | comptime options: FormatOptions, | |
| 1338 | 1355 | context: var, |
| 1339 | 1356 | comptime Errors: type, |
| 1340 | 1357 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 1341 | 1358 | ) Errors!void { |
| 1342 | switch (fmt.len) { | |
| 1343 | 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), | |
| 1344 | 1 => switch (fmt[0]) { | |
| 1345 | //point format | |
| 1346 | 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), | |
| 1347 | //dimension format | |
| 1348 | 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), | |
| 1349 | else => unreachable, | |
| 1350 | }, | |
| 1351 | else => unreachable, | |
| 1359 | if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { | |
| 1360 | return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y); | |
| 1361 | } else if (comptime std.mem.eql(u8, fmt, "d")) { | |
| 1362 | return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", self.x, self.y); | |
| 1363 | } else { | |
| 1364 | @compileError("Unknown format character: '" ++ fmt ++ "'"); | |
| 1352 | 1365 | } |
| 1353 | 1366 | } |
| 1354 | 1367 | }; |
| ... | ... | @@ -1366,7 +1379,7 @@ test "fmt.custom" { |
| 1366 | 1379 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); |
| 1367 | 1380 | } |
| 1368 | 1381 | |
| 1369 | test "fmt.struct" { | |
| 1382 | test "struct" { | |
| 1370 | 1383 | const S = struct { |
| 1371 | 1384 | a: u32, |
| 1372 | 1385 | b: anyerror, |
| ... | ... | @@ -1380,7 +1393,7 @@ test "fmt.struct" { |
| 1380 | 1393 | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); |
| 1381 | 1394 | } |
| 1382 | 1395 | |
| 1383 | test "fmt.union" { | |
| 1396 | test "union" { | |
| 1384 | 1397 | const TU = union(enum) { |
| 1385 | 1398 | float: f32, |
| 1386 | 1399 | int: u32, |
| ... | ... | @@ -1410,7 +1423,7 @@ test "fmt.union" { |
| 1410 | 1423 | testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); |
| 1411 | 1424 | } |
| 1412 | 1425 | |
| 1413 | test "fmt.enum" { | |
| 1426 | test "enum" { | |
| 1414 | 1427 | const E = enum { |
| 1415 | 1428 | One, |
| 1416 | 1429 | Two, |
| ... | ... | @@ -1422,7 +1435,7 @@ test "fmt.enum" { |
| 1422 | 1435 | try testFmt("E.Two", "{}", inst); |
| 1423 | 1436 | } |
| 1424 | 1437 | |
| 1425 | test "fmt.struct.self-referential" { | |
| 1438 | test "struct.self-referential" { | |
| 1426 | 1439 | const S = struct { |
| 1427 | 1440 | const SelfType = @This(); |
| 1428 | 1441 | a: ?*SelfType, |
| ... | ... | @@ -1436,7 +1449,7 @@ test "fmt.struct.self-referential" { |
| 1436 | 1449 | try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst); |
| 1437 | 1450 | } |
| 1438 | 1451 | |
| 1439 | test "fmt.bytes.hex" { | |
| 1452 | test "bytes.hex" { | |
| 1440 | 1453 | const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1441 | 1454 | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); |
| 1442 | 1455 | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); |
| ... | ... | @@ -1478,7 +1491,7 @@ pub fn trim(buf: []const u8) []const u8 { |
| 1478 | 1491 | return buf[start..end]; |
| 1479 | 1492 | } |
| 1480 | 1493 | |
| 1481 | test "fmt.trim" { | |
| 1494 | test "trim" { | |
| 1482 | 1495 | testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); |
| 1483 | 1496 | testing.expect(mem.eql(u8, "", trim(" "))); |
| 1484 | 1497 | testing.expect(mem.eql(u8, "", trim(""))); |
| ... | ... | @@ -1505,22 +1518,22 @@ pub fn hexToBytes(out: []u8, input: []const u8) !void { |
| 1505 | 1518 | } |
| 1506 | 1519 | } |
| 1507 | 1520 | |
| 1508 | test "fmt.hexToBytes" { | |
| 1521 | test "hexToBytes" { | |
| 1509 | 1522 | const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; |
| 1510 | 1523 | var pb: [32]u8 = undefined; |
| 1511 | 1524 | try hexToBytes(pb[0..], test_hex_str); |
| 1512 | 1525 | try testFmt(test_hex_str, "{X}", pb); |
| 1513 | 1526 | } |
| 1514 | 1527 | |
| 1515 | test "fmt.formatIntValue with comptime_int" { | |
| 1528 | test "formatIntValue with comptime_int" { | |
| 1516 | 1529 | const value: comptime_int = 123456789123456789; |
| 1517 | 1530 | |
| 1518 | 1531 | var buf = try std.Buffer.init(std.debug.global_allocator, ""); |
| 1519 | try formatIntValue(value, "", &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append); | |
| 1532 | try formatIntValue(value, "", FormatOptions{}, &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append); | |
| 1520 | 1533 | assert(mem.eql(u8, buf.toSlice(), "123456789123456789")); |
| 1521 | 1534 | } |
| 1522 | 1535 | |
| 1523 | test "fmt.formatType max_depth" { | |
| 1536 | test "formatType max_depth" { | |
| 1524 | 1537 | const Vec2 = struct { |
| 1525 | 1538 | const SelfType = @This(); |
| 1526 | 1539 | x: f32, |
| ... | ... | @@ -1529,11 +1542,16 @@ test "fmt.formatType max_depth" { |
| 1529 | 1542 | pub fn format( |
| 1530 | 1543 | self: SelfType, |
| 1531 | 1544 | comptime fmt: []const u8, |
| 1545 | comptime options: FormatOptions, | |
| 1532 | 1546 | context: var, |
| 1533 | 1547 | comptime Errors: type, |
| 1534 | 1548 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 1535 | 1549 | ) Errors!void { |
| 1536 | return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y); | |
| 1550 | if (fmt.len == 0) { | |
| 1551 | return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y); | |
| 1552 | } else { | |
| 1553 | @compileError("Unknown format string: '" ++ fmt ++ "'"); | |
| 1554 | } | |
| 1537 | 1555 | } |
| 1538 | 1556 | }; |
| 1539 | 1557 | const E = enum { |
| ... | ... | @@ -1565,18 +1583,34 @@ test "fmt.formatType max_depth" { |
| 1565 | 1583 | inst.tu.ptr = &inst.tu; |
| 1566 | 1584 | |
| 1567 | 1585 | var buf0 = try std.Buffer.init(std.debug.global_allocator, ""); |
| 1568 | try formatType(inst, "", &buf0, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0); | |
| 1586 | try formatType(inst, "", FormatOptions{}, &buf0, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0); | |
| 1569 | 1587 | assert(mem.eql(u8, buf0.toSlice(), "S{ ... }")); |
| 1570 | 1588 | |
| 1571 | 1589 | var buf1 = try std.Buffer.init(std.debug.global_allocator, ""); |
| 1572 | try formatType(inst, "", &buf1, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1); | |
| 1590 | try formatType(inst, "", FormatOptions{}, &buf1, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1); | |
| 1573 | 1591 | assert(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1574 | 1592 | |
| 1575 | 1593 | var buf2 = try std.Buffer.init(std.debug.global_allocator, ""); |
| 1576 | try formatType(inst, "", &buf2, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2); | |
| 1594 | try formatType(inst, "", FormatOptions{}, &buf2, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2); | |
| 1577 | 1595 | assert(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1578 | 1596 | |
| 1579 | 1597 | var buf3 = try std.Buffer.init(std.debug.global_allocator, ""); |
| 1580 | try formatType(inst, "", &buf3, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3); | |
| 1598 | try formatType(inst, "", FormatOptions{}, &buf3, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3); | |
| 1581 | 1599 | assert(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1582 | 1600 | } |
| 1601 | ||
| 1602 | test "positional" { | |
| 1603 | try testFmt("2 1 0", "{2} {1} {0}", usize(0), usize(1), usize(2)); | |
| 1604 | try testFmt("2 1 0", "{2} {1} {}", usize(0), usize(1), usize(2)); | |
| 1605 | try testFmt("0 0", "{0} {0}", usize(0)); | |
| 1606 | try testFmt("0 1", "{} {1}", usize(0), usize(1)); | |
| 1607 | try testFmt("1 0 0 1", "{1} {} {0} {}", usize(0), usize(1)); | |
| 1608 | } | |
| 1609 | ||
| 1610 | test "positional with specifier" { | |
| 1611 | try testFmt("10.0", "{0d:.1}", f64(9.999)); | |
| 1612 | } | |
| 1613 | ||
| 1614 | test "positional/alignment/width/precision" { | |
| 1615 | try testFmt("10.0", "{0d: >3.1}", f64(9.999)); | |
| 1616 | } |
std/math/big/int.zig+1| ... | ... | @@ -519,6 +519,7 @@ pub const Int = struct { |
| 519 | 519 | pub fn format( |
| 520 | 520 | self: Int, |
| 521 | 521 | comptime fmt: []const u8, |
| 522 | comptime options: std.fmt.FormatOptions, | |
| 522 | 523 | context: var, |
| 523 | 524 | comptime FmtError: type, |
| 524 | 525 | output: fn (@typeOf(context), []const u8) FmtError!void, |
std/net.zig-1| ... | ... | @@ -33,7 +33,6 @@ pub const Address = struct { |
| 33 | 33 | |
| 34 | 34 | pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address { |
| 35 | 35 | return Address{ |
| 36 | .family = os.AF_INET6, | |
| 37 | 36 | .os_addr = os.sockaddr{ |
| 38 | 37 | .in6 = os.sockaddr_in6{ |
| 39 | 38 | .family = os.AF_INET6, |
std/special/build_runner.zig+2-2| ... | ... | @@ -167,7 +167,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { |
| 167 | 167 | |
| 168 | 168 | const allocator = builder.allocator; |
| 169 | 169 | for (builder.top_level_steps.toSliceConst()) |top_level_step| { |
| 170 | try out_stream.print(" {s22} {}\n", top_level_step.step.name, top_level_step.description); | |
| 170 | try out_stream.print(" {s:22} {}\n", top_level_step.step.name, top_level_step.description); | |
| 171 | 171 | } |
| 172 | 172 | |
| 173 | 173 | try out_stream.write( |
| ... | ... | @@ -188,7 +188,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { |
| 188 | 188 | for (builder.available_options_list.toSliceConst()) |option| { |
| 189 | 189 | const name = try fmt.allocPrint(allocator, " -D{}=[{}]", option.name, Builder.typeIdName(option.type_id)); |
| 190 | 190 | defer allocator.free(name); |
| 191 | try out_stream.print("{s24} {}\n", name, option.description); | |
| 191 | try out_stream.print("{s:24} {}\n", name, option.description); | |
| 192 | 192 | } |
| 193 | 193 | } |
| 194 | 194 |
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 |
std/special/compiler_rt.zig+39-6| ... | ... | @@ -405,15 +405,15 @@ const use_thumb_1 = usesThumb1(builtin.arch); |
| 405 | 405 | |
| 406 | 406 | fn usesThumb1(arch: builtin.Arch) bool { |
| 407 | 407 | return switch (arch) { |
| 408 | .arm => switch (arch.arm) { | |
| 408 | .arm => |sub_arch| switch (sub_arch) { | |
| 409 | 409 | .v6m => true, |
| 410 | 410 | else => false, |
| 411 | 411 | }, |
| 412 | .armeb => switch (arch.armeb) { | |
| 412 | .armeb => |sub_arch| switch (sub_arch) { | |
| 413 | 413 | .v6m => true, |
| 414 | 414 | else => false, |
| 415 | 415 | }, |
| 416 | .thumb => switch (arch.thumb) { | |
| 416 | .thumb => |sub_arch| switch (sub_arch) { | |
| 417 | 417 | .v5, |
| 418 | 418 | .v5te, |
| 419 | 419 | .v4t, |
| ... | ... | @@ -423,7 +423,7 @@ fn usesThumb1(arch: builtin.Arch) bool { |
| 423 | 423 | => true, |
| 424 | 424 | else => false, |
| 425 | 425 | }, |
| 426 | .thumbeb => switch (arch.thumbeb) { | |
| 426 | .thumbeb => |sub_arch| switch (sub_arch) { | |
| 427 | 427 | .v5, |
| 428 | 428 | .v5te, |
| 429 | 429 | .v4t, |
| ... | ... | @@ -471,6 +471,22 @@ test "usesThumb1" { |
| 471 | 471 | //etc. |
| 472 | 472 | } |
| 473 | 473 | |
| 474 | const use_thumb_1_pre_armv6 = usesThumb1PreArmv6(builtin.arch); | |
| 475 | ||
| 476 | fn usesThumb1PreArmv6(arch: builtin.Arch) bool { | |
| 477 | return switch (arch) { | |
| 478 | .thumb => |sub_arch| switch (sub_arch) { | |
| 479 | .v5, .v5te, .v4t => true, | |
| 480 | else => false, | |
| 481 | }, | |
| 482 | .thumbeb => |sub_arch| switch (sub_arch) { | |
| 483 | .v5, .v5te, .v4t => true, | |
| 484 | else => false, | |
| 485 | }, | |
| 486 | else => false, | |
| 487 | }; | |
| 488 | } | |
| 489 | ||
| 474 | 490 | nakedcc fn __aeabi_memcpy() noreturn { |
| 475 | 491 | @setRuntimeSafety(false); |
| 476 | 492 | if (use_thumb_1) { |
| ... | ... | @@ -505,7 +521,16 @@ nakedcc fn __aeabi_memmove() noreturn { |
| 505 | 521 | |
| 506 | 522 | nakedcc fn __aeabi_memset() noreturn { |
| 507 | 523 | @setRuntimeSafety(false); |
| 508 | if (use_thumb_1) { | |
| 524 | if (use_thumb_1_pre_armv6) { | |
| 525 | asm volatile ( | |
| 526 | \\ eors r1, r2 | |
| 527 | \\ eors r2, r1 | |
| 528 | \\ eors r1, r2 | |
| 529 | \\ push {r7, lr} | |
| 530 | \\ b memset | |
| 531 | \\ pop {r7, pc} | |
| 532 | ); | |
| 533 | } else if (use_thumb_1) { | |
| 509 | 534 | asm volatile ( |
| 510 | 535 | \\ mov r3, r1 |
| 511 | 536 | \\ mov r1, r2 |
| ... | ... | @@ -527,7 +552,15 @@ nakedcc fn __aeabi_memset() noreturn { |
| 527 | 552 | |
| 528 | 553 | nakedcc fn __aeabi_memclr() noreturn { |
| 529 | 554 | @setRuntimeSafety(false); |
| 530 | if (use_thumb_1) { | |
| 555 | if (use_thumb_1_pre_armv6) { | |
| 556 | asm volatile ( | |
| 557 | \\ adds r2, r1, #0 | |
| 558 | \\ movs r1, #0 | |
| 559 | \\ push {r7, lr} | |
| 560 | \\ bl memset | |
| 561 | \\ pop {r7, pc} | |
| 562 | ); | |
| 563 | } else if (use_thumb_1) { | |
| 531 | 564 | asm volatile ( |
| 532 | 565 | \\ mov r2, r1 |
| 533 | 566 | \\ movs r1, #0 |
std/zig/parse.zig+2-2| ... | ... | @@ -2833,8 +2833,8 @@ fn parseIf(arena: *Allocator, it: *TokenIterator, tree: *Tree, bodyParseFn: Node |
| 2833 | 2833 | |
| 2834 | 2834 | const else_token = eatToken(it, .Keyword_else) orelse return node; |
| 2835 | 2835 | const payload = try parsePayload(arena, it, tree); |
| 2836 | const else_expr = try expectNode(arena, it, tree, parseExpr, AstError{ | |
| 2837 | .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, | |
| 2836 | const else_expr = try expectNode(arena, it, tree, bodyParseFn, AstError{ | |
| 2837 | .InvalidToken = AstError.InvalidToken{ .token = it.index }, | |
| 2838 | 2838 | }); |
| 2839 | 2839 | const else_node = try arena.create(Node.Else); |
| 2840 | 2840 | else_node.* = Node.Else{ |
std/zig/parser_test.zig+12| ... | ... | @@ -2234,6 +2234,18 @@ test "zig fmt: multiline string in array" { |
| 2234 | 2234 | ); |
| 2235 | 2235 | } |
| 2236 | 2236 | |
| 2237 | test "zig fmt: if type expr" { | |
| 2238 | try testCanonical( | |
| 2239 | \\const mycond = true; | |
| 2240 | \\pub fn foo() if (mycond) i32 else void { | |
| 2241 | \\ if (mycond) { | |
| 2242 | \\ return 42; | |
| 2243 | \\ } | |
| 2244 | \\} | |
| 2245 | \\ | |
| 2246 | ); | |
| 2247 | } | |
| 2248 | ||
| 2237 | 2249 | const std = @import("std"); |
| 2238 | 2250 | const mem = std.mem; |
| 2239 | 2251 | const warn = std.debug.warn; |
test/compare_output.zig+1-1| ... | ... | @@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 122 | 122 | \\ |
| 123 | 123 | \\pub fn main() void { |
| 124 | 124 | \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; |
| 125 | \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable; | |
| 125 | \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable; | |
| 126 | 126 | \\} |
| 127 | 127 | , "Hello, world!\n0012 012 a\n"); |
| 128 | 128 |
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 |