| author | |
| committer | |
| log | 3085d29af83d07769582f751b3c2f5f36634e34b |
| tree | ab5e72656369bccb19857277707b90ce1fc78ec3 |
| parent | 5cd4753bea9e8e79bf30217b9d0b7a4485271588 |
| parent | 07c0d484eec327ca3bd1e3ce081c1ced230536a9 |
| signature |
21 files changed, 1475 insertions(+), 507 deletions(-)
CMakeLists.txt+8-3| ... | @@ -389,6 +389,8 @@ set(EMBEDDED_SOFTFLOAT_SOURCES | ... | @@ -389,6 +389,8 @@ set(EMBEDDED_SOFTFLOAT_SOURCES |
| 389 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF32.c" | 389 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF32.c" |
| 390 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF64.c" | 390 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF64.c" |
| 391 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c" | 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 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/softfloat_state.c" | 394 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/softfloat_state.c" |
| 393 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui32_to_f128M.c" | 395 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui32_to_f128M.c" |
| 394 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui64_to_f128M.c" | 396 | "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui64_to_f128M.c" |
| ... | @@ -6653,15 +6655,18 @@ set(OPTIMIZED_C_FLAGS "-std=c99 -O3") | ... | @@ -6653,15 +6655,18 @@ set(OPTIMIZED_C_FLAGS "-std=c99 -O3") |
| 6653 | set(EXE_LDFLAGS " ") | 6655 | set(EXE_LDFLAGS " ") |
| 6654 | if(MSVC) | 6656 | if(MSVC) |
| 6655 | set(EXE_LDFLAGS "/STACK:16777216") | 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 | if(APPLE) | 6663 | if(APPLE) |
| 6658 | set(EXE_LDFLAGS "-static-libgcc -static-libstdc++") | 6664 | set(EXE_LDFLAGS "-static-libgcc -static-libstdc++") |
| 6659 | else() | 6665 | else() |
| 6660 | set(EXE_LDFLAGS "-static") | 6666 | set(EXE_LDFLAGS "-static") |
| 6661 | endif() | 6667 | endif() |
| 6662 | else() | ||
| 6663 | set(EXE_LDFLAGS " ") | ||
| 6664 | endif() | 6668 | endif() |
| 6669 | |||
| 6665 | if(ZIG_TEST_COVERAGE) | 6670 | if(ZIG_TEST_COVERAGE) |
| 6666 | set(EXE_CFLAGS "${EXE_CFLAGS} -fprofile-arcs -ftest-coverage") | 6671 | set(EXE_CFLAGS "${EXE_CFLAGS} -fprofile-arcs -ftest-coverage") |
| 6667 | set(EXE_LDFLAGS "${EXE_LDFLAGS} -fprofile-arcs -ftest-coverage") | 6672 | set(EXE_LDFLAGS "${EXE_LDFLAGS} -fprofile-arcs -ftest-coverage") |
doc/langref.html.in+90-2| ... | @@ -6259,6 +6259,13 @@ comptime { | ... | @@ -6259,6 +6259,13 @@ comptime { |
| 6259 | This function is only valid within function scope. | 6259 | This function is only valid within function scope. |
| 6260 | </p> | 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 | {#header_close#} | 6269 | {#header_close#} |
| 6263 | 6270 | ||
| 6264 | {#header_open|@byteSwap#} | 6271 | {#header_open|@byteSwap#} |
| ... | @@ -7347,10 +7354,91 @@ test "@setRuntimeSafety" { | ... | @@ -7347,10 +7354,91 @@ test "@setRuntimeSafety" { |
| 7347 | <pre>{#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}</pre> | 7354 | <pre>{#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}</pre> |
| 7348 | <p> | 7355 | <p> |
| 7349 | Performs the square root of a floating point number. Uses a dedicated hardware instruction | 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 | </p> | 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 | <p> | 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 | </p> | 7442 | </p> |
| 7355 | {#header_close#} | 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,7 +998,7 @@ fn printCharValues(out: var, bytes: []const u8) !void { |
| 998 | 998 | ||
| 999 | fn printUnderstandableChar(out: var, char: u8) !void { | 999 | fn printUnderstandableChar(out: var, char: u8) !void { |
| 1000 | if (!std.ascii.isPrint(char) or char == ' ') { | 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 | } else { | 1002 | } else { |
| 1003 | try out.write("'"); | 1003 | try out.write("'"); |
| 1004 | try out.write([_]u8{printable_char_tab[char]}); | 1004 | try out.write([_]u8{printable_char_tab[char]}); |
src/all_types.hpp+33-6| ... | @@ -1419,6 +1419,7 @@ enum BuiltinFnId { | ... | @@ -1419,6 +1419,7 @@ enum BuiltinFnId { |
| 1419 | BuiltinFnIdSubWithOverflow, | 1419 | BuiltinFnIdSubWithOverflow, |
| 1420 | BuiltinFnIdMulWithOverflow, | 1420 | BuiltinFnIdMulWithOverflow, |
| 1421 | BuiltinFnIdShlWithOverflow, | 1421 | BuiltinFnIdShlWithOverflow, |
| 1422 | BuiltinFnIdMulAdd, | ||
| 1422 | BuiltinFnIdCInclude, | 1423 | BuiltinFnIdCInclude, |
| 1423 | BuiltinFnIdCDefine, | 1424 | BuiltinFnIdCDefine, |
| 1424 | BuiltinFnIdCUndef, | 1425 | BuiltinFnIdCUndef, |
| ... | @@ -1446,6 +1447,19 @@ enum BuiltinFnId { | ... | @@ -1446,6 +1447,19 @@ enum BuiltinFnId { |
| 1446 | BuiltinFnIdRem, | 1447 | BuiltinFnIdRem, |
| 1447 | BuiltinFnIdMod, | 1448 | BuiltinFnIdMod, |
| 1448 | BuiltinFnIdSqrt, | 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 | BuiltinFnIdTruncate, | 1463 | BuiltinFnIdTruncate, |
| 1450 | BuiltinFnIdIntCast, | 1464 | BuiltinFnIdIntCast, |
| 1451 | BuiltinFnIdFloatCast, | 1465 | BuiltinFnIdFloatCast, |
| ... | @@ -1567,9 +1581,8 @@ enum ZigLLVMFnId { | ... | @@ -1567,9 +1581,8 @@ enum ZigLLVMFnId { |
| 1567 | ZigLLVMFnIdClz, | 1581 | ZigLLVMFnIdClz, |
| 1568 | ZigLLVMFnIdPopCount, | 1582 | ZigLLVMFnIdPopCount, |
| 1569 | ZigLLVMFnIdOverflowArithmetic, | 1583 | ZigLLVMFnIdOverflowArithmetic, |
| 1570 | ZigLLVMFnIdFloor, | 1584 | ZigLLVMFnIdFMA, |
| 1571 | ZigLLVMFnIdCeil, | 1585 | ZigLLVMFnIdFloatOp, |
| 1572 | ZigLLVMFnIdSqrt, | ||
| 1573 | ZigLLVMFnIdBswap, | 1586 | ZigLLVMFnIdBswap, |
| 1574 | ZigLLVMFnIdBitReverse, | 1587 | ZigLLVMFnIdBitReverse, |
| 1575 | }; | 1588 | }; |
| ... | @@ -1596,7 +1609,9 @@ struct ZigLLVMFnKey { | ... | @@ -1596,7 +1609,9 @@ struct ZigLLVMFnKey { |
| 1596 | uint32_t bit_count; | 1609 | uint32_t bit_count; |
| 1597 | } pop_count; | 1610 | } pop_count; |
| 1598 | struct { | 1611 | struct { |
| 1612 | BuiltinFnId op; | ||
| 1599 | uint32_t bit_count; | 1613 | uint32_t bit_count; |
| 1614 | uint32_t vector_len; // 0 means not a vector | ||
| 1600 | } floating; | 1615 | } floating; |
| 1601 | struct { | 1616 | struct { |
| 1602 | AddSubMul add_sub_mul; | 1617 | AddSubMul add_sub_mul; |
| ... | @@ -2260,6 +2275,8 @@ enum IrInstructionId { | ... | @@ -2260,6 +2275,8 @@ enum IrInstructionId { |
| 2260 | IrInstructionIdOverflowOp, | 2275 | IrInstructionIdOverflowOp, |
| 2261 | IrInstructionIdTestErrSrc, | 2276 | IrInstructionIdTestErrSrc, |
| 2262 | IrInstructionIdTestErrGen, | 2277 | IrInstructionIdTestErrGen, |
| 2278 | IrInstructionIdMulAdd, | ||
| 2279 | IrInstructionIdFloatOp, | ||
| 2263 | IrInstructionIdUnwrapErrCode, | 2280 | IrInstructionIdUnwrapErrCode, |
| 2264 | IrInstructionIdUnwrapErrPayload, | 2281 | IrInstructionIdUnwrapErrPayload, |
| 2265 | IrInstructionIdErrWrapCode, | 2282 | IrInstructionIdErrWrapCode, |
| ... | @@ -2324,7 +2341,6 @@ enum IrInstructionId { | ... | @@ -2324,7 +2341,6 @@ enum IrInstructionId { |
| 2324 | IrInstructionIdAddImplicitReturnType, | 2341 | IrInstructionIdAddImplicitReturnType, |
| 2325 | IrInstructionIdMergeErrRetTraces, | 2342 | IrInstructionIdMergeErrRetTraces, |
| 2326 | IrInstructionIdMarkErrRetTracePtr, | 2343 | IrInstructionIdMarkErrRetTracePtr, |
| 2327 | IrInstructionIdSqrt, | ||
| 2328 | IrInstructionIdErrSetCast, | 2344 | IrInstructionIdErrSetCast, |
| 2329 | IrInstructionIdToBytes, | 2345 | IrInstructionIdToBytes, |
| 2330 | IrInstructionIdFromBytes, | 2346 | IrInstructionIdFromBytes, |
| ... | @@ -3080,6 +3096,15 @@ struct IrInstructionOverflowOp { | ... | @@ -3080,6 +3096,15 @@ struct IrInstructionOverflowOp { |
| 3080 | ZigType *result_ptr_type; | 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 | struct IrInstructionAlignOf { | 3108 | struct IrInstructionAlignOf { |
| 3084 | IrInstruction base; | 3109 | IrInstruction base; |
| 3085 | 3110 | ||
| ... | @@ -3512,11 +3537,13 @@ struct IrInstructionMarkErrRetTracePtr { | ... | @@ -3512,11 +3537,13 @@ struct IrInstructionMarkErrRetTracePtr { |
| 3512 | IrInstruction *err_ret_trace_ptr; | 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 | IrInstruction base; | 3542 | IrInstruction base; |
| 3517 | 3543 | ||
| 3544 | BuiltinFnId op; | ||
| 3518 | IrInstruction *type; | 3545 | IrInstruction *type; |
| 3519 | IrInstruction *op; | 3546 | IrInstruction *op1; |
| 3520 | }; | 3547 | }; |
| 3521 | 3548 | ||
| 3522 | struct IrInstructionCheckRuntimeScope { | 3549 | struct IrInstructionCheckRuntimeScope { |
src/analyze.cpp+14-10| ... | @@ -5736,12 +5736,13 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { | ... | @@ -5736,12 +5736,13 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { |
| 5736 | return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; | 5736 | return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; |
| 5737 | case ZigLLVMFnIdPopCount: | 5737 | case ZigLLVMFnIdPopCount: |
| 5738 | return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049; | 5738 | return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049; |
| 5739 | case ZigLLVMFnIdFloor: | 5739 | case ZigLLVMFnIdFloatOp: |
| 5740 | return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168; | 5740 | return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) + |
| 5741 | case ZigLLVMFnIdCeil: | 5741 | (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025) + |
| 5742 | return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089; | 5742 | (uint32_t)(x.data.floating.op) * (uint32_t)43789879; |
| 5743 | case ZigLLVMFnIdSqrt: | 5743 | case ZigLLVMFnIdFMA: |
| 5744 | return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385; | 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 | case ZigLLVMFnIdBswap: | 5746 | case ZigLLVMFnIdBswap: |
| 5746 | return (uint32_t)(x.data.bswap.bit_count) * (uint32_t)3661994335; | 5747 | return (uint32_t)(x.data.bswap.bit_count) * (uint32_t)3661994335; |
| 5747 | case ZigLLVMFnIdBitReverse: | 5748 | case ZigLLVMFnIdBitReverse: |
| ... | @@ -5769,10 +5770,13 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { | ... | @@ -5769,10 +5770,13 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { |
| 5769 | return a.data.bswap.bit_count == b.data.bswap.bit_count; | 5770 | return a.data.bswap.bit_count == b.data.bswap.bit_count; |
| 5770 | case ZigLLVMFnIdBitReverse: | 5771 | case ZigLLVMFnIdBitReverse: |
| 5771 | return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count; | 5772 | return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count; |
| 5772 | case ZigLLVMFnIdFloor: | 5773 | case ZigLLVMFnIdFloatOp: |
| 5773 | case ZigLLVMFnIdCeil: | 5774 | return a.data.floating.bit_count == b.data.floating.bit_count && |
| 5774 | case ZigLLVMFnIdSqrt: | 5775 | a.data.floating.vector_len == b.data.floating.vector_len && |
| 5775 | return a.data.floating.bit_count == b.data.floating.bit_count; | 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 | case ZigLLVMFnIdOverflowArithmetic: | 5780 | case ZigLLVMFnIdOverflowArithmetic: |
| 5777 | return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && | 5781 | return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && |
| 5778 | (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) && | 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,32 +808,47 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSu |
| 808 | return fn_val; | 808 | return fn_val; |
| 809 | } | 809 | } |
| 810 | 810 | ||
| 811 | static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id) { | 811 | static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) { |
| 812 | assert(type_entry->id == ZigTypeIdFloat); | 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 | ZigLLVMFnKey key = {}; | 818 | ZigLLVMFnKey key = {}; |
| 815 | key.id = fn_id; | 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 | auto existing_entry = g->llvm_fn_table.maybe_get(key); | 824 | auto existing_entry = g->llvm_fn_table.maybe_get(key); |
| 819 | if (existing_entry) | 825 | if (existing_entry) |
| 820 | return existing_entry->value; | 826 | return existing_entry->value; |
| 821 | 827 | ||
| 822 | const char *name; | 828 | const char *name; |
| 823 | if (fn_id == ZigLLVMFnIdFloor) { | 829 | uint32_t num_args; |
| 824 | name = "floor"; | 830 | if (fn_id == ZigLLVMFnIdFMA) { |
| 825 | } else if (fn_id == ZigLLVMFnIdCeil) { | 831 | name = "fma"; |
| 826 | name = "ceil"; | 832 | num_args = 3; |
| 827 | } else if (fn_id == ZigLLVMFnIdSqrt) { | 833 | } else if (fn_id == ZigLLVMFnIdFloatOp) { |
| 828 | name = "sqrt"; | 834 | name = float_op_to_name(op, true); |
| 835 | num_args = 1; | ||
| 829 | } else { | 836 | } else { |
| 830 | zig_unreachable(); | 837 | zig_unreachable(); |
| 831 | } | 838 | } |
| 832 | 839 | ||
| 833 | char fn_name[64]; | 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 | LLVMTypeRef float_type_ref = get_llvm_type(g, type_entry); | 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 | LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); | 852 | LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); |
| 838 | assert(LLVMGetIntrinsicID(fn_val)); | 853 | assert(LLVMGetIntrinsicID(fn_val)); |
| 839 | 854 | ||
| ... | @@ -2483,22 +2498,17 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, | ... | @@ -2483,22 +2498,17 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, |
| 2483 | return result; | 2498 | return result; |
| 2484 | } | 2499 | } |
| 2485 | 2500 | ||
| 2486 | static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, ZigType *type_entry) { | 2501 | static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { |
| 2487 | if (type_entry->id == ZigTypeIdInt) | 2502 | if ((op == BuiltinFnIdCeil || |
| 2503 | op == BuiltinFnIdFloor) && | ||
| 2504 | type_entry->id == ZigTypeIdInt) | ||
| 2488 | return val; | 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 | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); | 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 | enum DivKind { | 2512 | enum DivKind { |
| 2503 | DivKindFloat, | 2513 | DivKindFloat, |
| 2504 | DivKindTrunc, | 2514 | DivKindTrunc, |
| ... | @@ -2574,7 +2584,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2574,7 +2584,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2574 | return result; | 2584 | return result; |
| 2575 | case DivKindExact: | 2585 | case DivKindExact: |
| 2576 | if (want_runtime_safety) { | 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 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); | 2588 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); |
| 2579 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | 2589 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2580 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); | 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,12 +2606,12 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2596 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); | 2606 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2597 | 2607 | ||
| 2598 | LLVMPositionBuilderAtEnd(g->builder, ltz_block); | 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 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); | 2610 | LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); |
| 2601 | LLVMBuildBr(g->builder, end_block); | 2611 | LLVMBuildBr(g->builder, end_block); |
| 2602 | 2612 | ||
| 2603 | LLVMPositionBuilderAtEnd(g->builder, gez_block); | 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 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); | 2615 | LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); |
| 2606 | LLVMBuildBr(g->builder, end_block); | 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,7 +2623,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2613 | return phi; | 2623 | return phi; |
| 2614 | } | 2624 | } |
| 2615 | case DivKindFloor: | 2625 | case DivKindFloor: |
| 2616 | return gen_floor(g, result, type_entry); | 2626 | return gen_float_op(g, result, type_entry, BuiltinFnIdFloor); |
| 2617 | } | 2627 | } |
| 2618 | zig_unreachable(); | 2628 | zig_unreachable(); |
| 2619 | } | 2629 | } |
| ... | @@ -5417,13 +5427,28 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e | ... | @@ -5417,13 +5427,28 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e |
| 5417 | return nullptr; | 5427 | return nullptr; |
| 5418 | } | 5428 | } |
| 5419 | 5429 | ||
| 5420 | static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) { | 5430 | static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) { |
| 5421 | LLVMValueRef op = ir_llvm_value(g, instruction->op); | 5431 | LLVMValueRef op = ir_llvm_value(g, instruction->op1); |
| 5422 | assert(instruction->base.value.type->id == ZigTypeIdFloat); | 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 | return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); | 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 | static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { | 5452 | static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) { |
| 5428 | LLVMValueRef op = ir_llvm_value(g, instruction->op); | 5453 | LLVMValueRef op = ir_llvm_value(g, instruction->op); |
| 5429 | ZigType *int_type = instruction->base.value.type; | 5454 | ZigType *int_type = instruction->base.value.type; |
| ... | @@ -5770,8 +5795,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -5770,8 +5795,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 5770 | return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction); | 5795 | return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction); |
| 5771 | case IrInstructionIdMarkErrRetTracePtr: | 5796 | case IrInstructionIdMarkErrRetTracePtr: |
| 5772 | return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); | 5797 | return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 5773 | case IrInstructionIdSqrt: | 5798 | case IrInstructionIdFloatOp: |
| 5774 | return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction); | 5799 | return ir_render_float_op(g, executable, (IrInstructionFloatOp *)instruction); |
| 5800 | case IrInstructionIdMulAdd: | ||
| 5801 | return ir_render_mul_add(g, executable, (IrInstructionMulAdd *)instruction); | ||
| 5775 | case IrInstructionIdArrayToVector: | 5802 | case IrInstructionIdArrayToVector: |
| 5776 | return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction); | 5803 | return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction); |
| 5777 | case IrInstructionIdVectorToArray: | 5804 | case IrInstructionIdVectorToArray: |
| ... | @@ -7356,6 +7383,21 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -7356,6 +7383,21 @@ static void define_builtin_fns(CodeGen *g) { |
| 7356 | create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); | 7383 | create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); |
| 7357 | create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); | 7384 | create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); |
| 7358 | create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); | 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 | create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); | 7401 | create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); |
| 7360 | create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); | 7402 | create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); |
| 7361 | create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX); | 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,6 +777,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErrGen *) { |
| 777 | return IrInstructionIdTestErrGen; | 777 | return IrInstructionIdTestErrGen; |
| 778 | } | 778 | } |
| 779 | 779 | ||
| 780 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMulAdd *) { | ||
| 781 | return IrInstructionIdMulAdd; | ||
| 782 | } | ||
| 783 | |||
| 780 | static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) { | 784 | static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) { |
| 781 | return IrInstructionIdUnwrapErrCode; | 785 | return IrInstructionIdUnwrapErrCode; |
| 782 | } | 786 | } |
| ... | @@ -1037,8 +1041,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP | ... | @@ -1037,8 +1041,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP |
| 1037 | return IrInstructionIdMarkErrRetTracePtr; | 1041 | return IrInstructionIdMarkErrRetTracePtr; |
| 1038 | } | 1042 | } |
| 1039 | 1043 | ||
| 1040 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { | 1044 | static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) { |
| 1041 | return IrInstructionIdSqrt; | 1045 | return IrInstructionIdFloatOp; |
| 1042 | } | 1046 | } |
| 1043 | 1047 | ||
| 1044 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) { | 1048 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) { |
| ... | @@ -2437,6 +2441,75 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode | ... | @@ -2437,6 +2441,75 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode |
| 2437 | return &instruction->base; | 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 | static IrInstruction *ir_build_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { | 2513 | static IrInstruction *ir_build_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) { |
| 2441 | IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node); | 2514 | IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node); |
| 2442 | instruction->type_value = type_value; | 2515 | instruction->type_value = type_value; |
| ... | @@ -3201,17 +3274,6 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco | ... | @@ -3201,17 +3274,6 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco |
| 3201 | return &instruction->base; | 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 | static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, | 3277 | static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 3216 | IrInstruction *container, IrInstruction *name) | 3278 | IrInstruction *container, IrInstruction *name) |
| 3217 | { | 3279 | { |
| ... | @@ -4380,6 +4442,33 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -4380,6 +4442,33 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * |
| 4380 | return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); | 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 | static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) { | 4472 | static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) { |
| 4384 | for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) { | 4473 | for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) { |
| 4385 | if (it_scope->id == ScopeIdDecls) { | 4474 | if (it_scope->id == ScopeIdDecls) { |
| ... | @@ -4708,6 +4797,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo | ... | @@ -4708,6 +4797,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4708 | return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); | 4797 | return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); |
| 4709 | } | 4798 | } |
| 4710 | case BuiltinFnIdSqrt: | 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 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | 4814 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| 4713 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | 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,7 +4821,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4719 | if (arg1_value == irb->codegen->invalid_instruction) | 4821 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4720 | return arg1_value; | 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 | return ir_lval_wrap(irb, scope, ir_sqrt, lval, result_loc); | 4825 | return ir_lval_wrap(irb, scope, ir_sqrt, lval, result_loc); |
| 4724 | } | 4826 | } |
| 4725 | case BuiltinFnIdTruncate: | 4827 | case BuiltinFnIdTruncate: |
| ... | @@ -5043,6 +5145,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo | ... | @@ -5043,6 +5145,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 5043 | return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc); | 5145 | return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc); |
| 5044 | case BuiltinFnIdShlWithOverflow: | 5146 | case BuiltinFnIdShlWithOverflow: |
| 5045 | return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc); | 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 | case BuiltinFnIdTypeName: | 5150 | case BuiltinFnIdTypeName: |
| 5047 | { | 5151 | { |
| 5048 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | 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,6 +22813,125 @@ static IrInstruction *ir_analyze_instruction_result_ptr(IrAnalyze *ira, IrInstru |
| 22709 | return ir_get_ref(ira, &instruction->base, result, true, false); | 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 | static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErrSrc *instruction) { | 22935 | static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErrSrc *instruction) { |
| 22713 | IrInstruction *base_ptr = instruction->base_ptr->child; | 22936 | IrInstruction *base_ptr = instruction->base_ptr->child; |
| 22714 | if (type_is_invalid(base_ptr->value.type)) | 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,70 +24765,254 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i |
| 24542 | return result; | 24765 | return result; |
| 24543 | } | 24766 | } |
| 24544 | 24767 | ||
| 24545 | static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { | 24768 | static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type, |
| 24546 | ZigType *float_type = ir_resolve_type(ira, instruction->type->child); | 24769 | ConstExprValue *op, ConstExprValue *out_val) { |
| 24547 | if (type_is_invalid(float_type)) | 24770 | assert(ira && source_instr && float_type && out_val && op); |
| 24548 | return ira->codegen->invalid_instruction; | 24771 | assert(float_type->id == ZigTypeIdFloat || |
| 24772 | float_type->id == ZigTypeIdComptimeFloat); | ||
| 24549 | 24773 | ||
| 24550 | IrInstruction *op = instruction->op->child; | 24774 | BuiltinFnId fop = source_instr->op; |
| 24551 | if (type_is_invalid(op->value.type)) | 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 | return ira->codegen->invalid_instruction; | 24955 | return ira->codegen->invalid_instruction; |
| 24553 | 24956 | ||
| 24554 | bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat; | 24957 | // Only allow float types, and vectors of floats. |
| 24555 | if (!ok_type) { | 24958 | ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; |
| 24556 | ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name))); | 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 | return ira->codegen->invalid_instruction; | 24961 | return ira->codegen->invalid_instruction; |
| 24558 | } | 24962 | } |
| 24559 | 24963 | ||
| 24560 | IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type); | 24964 | IrInstruction *op1 = instruction->op1->child; |
| 24561 | if (type_is_invalid(casted_op->value.type)) | 24965 | if (type_is_invalid(op1->value.type)) |
| 24562 | return ira->codegen->invalid_instruction; | 24966 | return ira->codegen->invalid_instruction; |
| 24563 | 24967 | ||
| 24564 | if (instr_is_comptime(casted_op)) { | 24968 | IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type); |
| 24565 | ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad); | 24969 | if (type_is_invalid(casted_op1->value.type)) |
| 24566 | if (!val) | 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 | return ira->codegen->invalid_instruction; | 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 | ConstExprValue *out_val = &result->value; | 24987 | ConstExprValue *out_val = &result->value; |
| 24571 | 24988 | ||
| 24572 | if (float_type->id == ZigTypeIdComptimeFloat) { | 24989 | if (expr_type->id == ZigTypeIdVector) { |
| 24573 | bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat); | 24990 | expand_undef_array(ira->codegen, op1_const); |
| 24574 | } else if (float_type->id == ZigTypeIdFloat) { | 24991 | out_val->special = ConstValSpecialUndef; |
| 24575 | switch (float_type->data.floating.bit_count) { | 24992 | expand_undef_array(ira->codegen, out_val); |
| 24576 | case 16: | 24993 | size_t len = expr_type->data.vector.len; |
| 24577 | out_val->data.x_f16 = f16_sqrt(val->data.x_f16); | 24994 | for (size_t i = 0; i < len; i += 1) { |
| 24578 | break; | 24995 | ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; |
| 24579 | case 32: | 24996 | ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; |
| 24580 | out_val->data.x_f32 = sqrtf(val->data.x_f32); | 24997 | assert(float_operand_op1->type == float_type); |
| 24581 | break; | 24998 | assert(float_out_val->type == float_type); |
| 24582 | case 64: | 24999 | ir_eval_float_op(ira, instruction, float_type, |
| 24583 | out_val->data.x_f64 = sqrt(val->data.x_f64); | 25000 | op1_const, float_out_val); |
| 24584 | break; | 25001 | float_out_val->type = float_type; |
| 24585 | case 128: | ||
| 24586 | f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128); | ||
| 24587 | break; | ||
| 24588 | default: | ||
| 24589 | zig_unreachable(); | ||
| 24590 | } | 25002 | } |
| 25003 | out_val->type = expr_type; | ||
| 25004 | out_val->special = ConstValSpecialStatic; | ||
| 24591 | } else { | 25005 | } else { |
| 24592 | zig_unreachable(); | 25006 | ir_eval_float_op(ira, instruction, float_type, op1_const, out_val); |
| 24593 | } | 25007 | } |
| 24594 | |||
| 24595 | return result; | 25008 | return result; |
| 24596 | } | 25009 | } |
| 24597 | 25010 | ||
| 24598 | ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base); | 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, | 25013 | IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope, |
| 24607 | instruction->base.source_node, nullptr, casted_op); | 25014 | instruction->base.source_node, nullptr, casted_op1, instruction->op); |
| 24608 | result->value.type = float_type; | 25015 | result->value.type = expr_type; |
| 24609 | return result; | 25016 | return result; |
| 24610 | } | 25017 | } |
| 24611 | 25018 | ||
| ... | @@ -25143,8 +25550,10 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction | ... | @@ -25143,8 +25550,10 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction |
| 25143 | return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); | 25550 | return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction); |
| 25144 | case IrInstructionIdMarkErrRetTracePtr: | 25551 | case IrInstructionIdMarkErrRetTracePtr: |
| 25145 | return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); | 25552 | return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 25146 | case IrInstructionIdSqrt: | 25553 | case IrInstructionIdFloatOp: |
| 25147 | return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction); | 25554 | return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction); |
| 25555 | case IrInstructionIdMulAdd: | ||
| 25556 | return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)instruction); | ||
| 25148 | case IrInstructionIdIntToErr: | 25557 | case IrInstructionIdIntToErr: |
| 25149 | return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); | 25558 | return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction); |
| 25150 | case IrInstructionIdErrToInt: | 25559 | case IrInstructionIdErrToInt: |
| ... | @@ -25391,7 +25800,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -25391,7 +25800,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 25391 | case IrInstructionIdCoroFree: | 25800 | case IrInstructionIdCoroFree: |
| 25392 | case IrInstructionIdCoroPromise: | 25801 | case IrInstructionIdCoroPromise: |
| 25393 | case IrInstructionIdPromiseResultType: | 25802 | case IrInstructionIdPromiseResultType: |
| 25394 | case IrInstructionIdSqrt: | 25803 | case IrInstructionIdFloatOp: |
| 25804 | case IrInstructionIdMulAdd: | ||
| 25395 | case IrInstructionIdAtomicLoad: | 25805 | case IrInstructionIdAtomicLoad: |
| 25396 | case IrInstructionIdIntCast: | 25806 | case IrInstructionIdIntCast: |
| 25397 | case IrInstructionIdFloatCast: | 25807 | case IrInstructionIdFloatCast: |
src/ir.hpp+1| ... | @@ -26,5 +26,6 @@ bool ir_has_side_effects(IrInstruction *instruction); | ... | @@ -26,5 +26,6 @@ bool ir_has_side_effects(IrInstruction *instruction); |
| 26 | struct IrAnalyze; | 26 | struct IrAnalyze; |
| 27 | ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val, | 27 | ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val, |
| 28 | AstNode *source_node); | 28 | AstNode *source_node); |
| 29 | const char *float_op_to_name(BuiltinFnId op, bool llvm_name); | ||
| 29 | 30 | ||
| 30 | #endif | 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,15 +1563,32 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe |
| 1563 | fprintf(irp->f, ")"); | 1563 | fprintf(irp->f, ")"); |
| 1564 | } | 1564 | } |
| 1565 | 1565 | ||
| 1566 | static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { | 1566 | static void ir_print_float_op(IrPrint *irp, IrInstructionFloatOp *instruction) { |
| 1567 | fprintf(irp->f, "@sqrt("); | 1567 | |
| 1568 | fprintf(irp->f, "@%s(", float_op_to_name(instruction->op, false)); | ||
| 1568 | if (instruction->type != nullptr) { | 1569 | if (instruction->type != nullptr) { |
| 1569 | ir_print_other_instruction(irp, instruction->type); | 1570 | ir_print_other_instruction(irp, instruction->type); |
| 1570 | } else { | 1571 | } else { |
| 1571 | fprintf(irp->f, "null"); | 1572 | fprintf(irp->f, "null"); |
| 1572 | } | 1573 | } |
| 1573 | fprintf(irp->f, ","); | 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 | fprintf(irp->f, ")"); | 1592 | fprintf(irp->f, ")"); |
| 1576 | } | 1593 | } |
| 1577 | 1594 | ||
| ... | @@ -2053,8 +2070,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -2053,8 +2070,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 2053 | case IrInstructionIdMarkErrRetTracePtr: | 2070 | case IrInstructionIdMarkErrRetTracePtr: |
| 2054 | ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction); | 2071 | ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction); |
| 2055 | break; | 2072 | break; |
| 2056 | case IrInstructionIdSqrt: | 2073 | case IrInstructionIdFloatOp: |
| 2057 | ir_print_sqrt(irp, (IrInstructionSqrt *)instruction); | 2074 | ir_print_float_op(irp, (IrInstructionFloatOp *)instruction); |
| 2075 | break; | ||
| 2076 | case IrInstructionIdMulAdd: | ||
| 2077 | ir_print_mul_add(irp, (IrInstructionMulAdd *)instruction); | ||
| 2058 | break; | 2078 | break; |
| 2059 | case IrInstructionIdAtomicLoad: | 2079 | case IrInstructionIdAtomicLoad: |
| 2060 | ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); | 2080 | ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction); |
std/fmt.zig+397-363| ... | @@ -10,6 +10,42 @@ const lossyCast = std.math.lossyCast; | ... | @@ -10,6 +10,42 @@ const lossyCast = std.math.lossyCast; |
| 10 | 10 | ||
| 11 | pub const default_max_depth = 3; | 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 | /// Renders fmt string with args, calling output with slices of bytes. | 49 | /// Renders fmt string with args, calling output with slices of bytes. |
| 14 | /// If `output` returns an error, the error is returned from `format` and | 50 | /// If `output` returns an error, the error is returned from `format` and |
| 15 | /// `output` is not called again. | 51 | /// `output` is not called again. |
| ... | @@ -20,17 +56,30 @@ pub fn format( | ... | @@ -20,17 +56,30 @@ pub fn format( |
| 20 | comptime fmt: []const u8, | 56 | comptime fmt: []const u8, |
| 21 | args: ..., | 57 | args: ..., |
| 22 | ) Errors!void { | 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 | const State = enum { | 64 | const State = enum { |
| 24 | Start, | 65 | Start, |
| 25 | OpenBrace, | 66 | Positional, |
| 26 | CloseBrace, | 67 | CloseBrace, |
| 27 | FormatString, | 68 | Specifier, |
| 69 | FormatFillAndAlign, | ||
| 70 | FormatWidth, | ||
| 71 | FormatPrecision, | ||
| 28 | Pointer, | 72 | Pointer, |
| 29 | }; | 73 | }; |
| 30 | 74 | ||
| 31 | comptime var start_index = 0; | 75 | comptime var start_index = 0; |
| 32 | comptime var state = State.Start; | 76 | comptime var state = State.Start; |
| 33 | comptime var next_arg = 0; | 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 | inline for (fmt) |c, i| { | 84 | inline for (fmt) |c, i| { |
| 36 | switch (state) { | 85 | switch (state) { |
| ... | @@ -39,58 +88,183 @@ pub fn format( | ... | @@ -39,58 +88,183 @@ pub fn format( |
| 39 | if (start_index < i) { | 88 | if (start_index < i) { |
| 40 | try output(context, fmt[start_index..i]); | 89 | try output(context, fmt[start_index..i]); |
| 41 | } | 90 | } |
| 91 | |||
| 42 | start_index = i; | 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 | if (start_index < i) { | 100 | if (start_index < i) { |
| 48 | try output(context, fmt[start_index..i]); | 101 | try output(context, fmt[start_index..i]); |
| 49 | } | 102 | } |
| 50 | state = State.CloseBrace; | 103 | state = .CloseBrace; |
| 51 | }, | 104 | }, |
| 52 | else => {}, | 105 | else => {}, |
| 53 | }, | 106 | }, |
| 54 | .OpenBrace => switch (c) { | 107 | .Positional => switch (c) { |
| 55 | '{' => { | 108 | '{' => { |
| 56 | state = State.Start; | 109 | state = .Start; |
| 57 | start_index = i; | 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); | 133 | const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); |
| 61 | next_arg += 1; | 134 | |
| 62 | state = State.Start; | 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 | start_index = i + 1; | 146 | start_index = i + 1; |
| 64 | }, | 147 | }, |
| 65 | '*' => state = State.Pointer, | ||
| 66 | else => { | 148 | else => { |
| 67 | state = State.FormatString; | 149 | state = .Specifier; |
| 150 | specifier_start = i; | ||
| 68 | }, | 151 | }, |
| 69 | }, | 152 | }, |
| 70 | .CloseBrace => switch (c) { | 153 | .CloseBrace => switch (c) { |
| 71 | '}' => { | 154 | '}' => { |
| 72 | state = State.Start; | 155 | state = .Start; |
| 73 | start_index = i; | 156 | start_index = i; |
| 74 | }, | 157 | }, |
| 75 | else => @compileError("Single '}' encountered in format string"), | 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; | 166 | const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg); |
| 80 | try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth); | 167 | |
| 81 | next_arg += 1; | 168 | try formatType( |
| 82 | state = State.Start; | 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 | start_index = i + 1; | 178 | start_index = i + 1; |
| 84 | }, | 179 | }, |
| 85 | else => {}, | 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 | .Pointer => switch (c) { | 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 | try output(context, "@"); | 264 | try output(context, "@"); |
| 91 | try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); | 265 | try formatInt(@ptrToInt(args[arg_to_print]), 16, false, 0, context, Errors, output); |
| 92 | next_arg += 1; | 266 | |
| 93 | state = State.Start; | 267 | state = .Start; |
| 94 | start_index = i + 1; | 268 | start_index = i + 1; |
| 95 | }, | 269 | }, |
| 96 | else => @compileError("Unexpected format character after '*'"), | 270 | else => @compileError("Unexpected format character after '*'"), |
| ... | @@ -98,7 +272,13 @@ pub fn format( | ... | @@ -98,7 +272,13 @@ pub fn format( |
| 98 | } | 272 | } |
| 99 | } | 273 | } |
| 100 | comptime { | 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 | @compileError("Unused arguments"); | 282 | @compileError("Unused arguments"); |
| 103 | } | 283 | } |
| 104 | if (state != State.Start) { | 284 | if (state != State.Start) { |
| ... | @@ -113,6 +293,7 @@ pub fn format( | ... | @@ -113,6 +293,7 @@ pub fn format( |
| 113 | pub fn formatType( | 293 | pub fn formatType( |
| 114 | value: var, | 294 | value: var, |
| 115 | comptime fmt: []const u8, | 295 | comptime fmt: []const u8, |
| 296 | comptime options: FormatOptions, | ||
| 116 | context: var, | 297 | context: var, |
| 117 | comptime Errors: type, | 298 | comptime Errors: type, |
| 118 | output: fn (@typeOf(context), []const u8) Errors!void, | 299 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | @@ -121,7 +302,7 @@ pub fn formatType( | ... | @@ -121,7 +302,7 @@ pub fn formatType( |
| 121 | const T = @typeOf(value); | 302 | const T = @typeOf(value); |
| 122 | switch (@typeInfo(T)) { | 303 | switch (@typeInfo(T)) { |
| 123 | .ComptimeInt, .Int, .Float => { | 304 | .ComptimeInt, .Int, .Float => { |
| 124 | return formatValue(value, fmt, context, Errors, output); | 305 | return formatValue(value, fmt, options, context, Errors, output); |
| 125 | }, | 306 | }, |
| 126 | .Void => { | 307 | .Void => { |
| 127 | return output(context, "void"); | 308 | return output(context, "void"); |
| ... | @@ -131,16 +312,16 @@ pub fn formatType( | ... | @@ -131,16 +312,16 @@ pub fn formatType( |
| 131 | }, | 312 | }, |
| 132 | .Optional => { | 313 | .Optional => { |
| 133 | if (value) |payload| { | 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 | } else { | 316 | } else { |
| 136 | return output(context, "null"); | 317 | return output(context, "null"); |
| 137 | } | 318 | } |
| 138 | }, | 319 | }, |
| 139 | .ErrorUnion => { | 320 | .ErrorUnion => { |
| 140 | if (value) |payload| { | 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 | } else |err| { | 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 | .ErrorSet => { | 327 | .ErrorSet => { |
| ... | @@ -152,16 +333,16 @@ pub fn formatType( | ... | @@ -152,16 +333,16 @@ pub fn formatType( |
| 152 | }, | 333 | }, |
| 153 | .Enum => { | 334 | .Enum => { |
| 154 | if (comptime std.meta.trait.hasFn("format")(T)) { | 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 | try output(context, @typeName(T)); | 339 | try output(context, @typeName(T)); |
| 159 | try output(context, "."); | 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 | .Union => { | 343 | .Union => { |
| 163 | if (comptime std.meta.trait.hasFn("format")(T)) { | 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 | try output(context, @typeName(T)); | 348 | try output(context, @typeName(T)); |
| ... | @@ -175,7 +356,7 @@ pub fn formatType( | ... | @@ -175,7 +356,7 @@ pub fn formatType( |
| 175 | try output(context, " = "); | 356 | try output(context, " = "); |
| 176 | inline for (info.fields) |u_field| { | 357 | inline for (info.fields) |u_field| { |
| 177 | if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { | 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 | try output(context, " }"); | 362 | try output(context, " }"); |
| ... | @@ -185,7 +366,7 @@ pub fn formatType( | ... | @@ -185,7 +366,7 @@ pub fn formatType( |
| 185 | }, | 366 | }, |
| 186 | .Struct => { | 367 | .Struct => { |
| 187 | if (comptime std.meta.trait.hasFn("format")(T)) { | 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 | try output(context, @typeName(T)); | 372 | try output(context, @typeName(T)); |
| ... | @@ -201,7 +382,7 @@ pub fn formatType( | ... | @@ -201,7 +382,7 @@ pub fn formatType( |
| 201 | } | 382 | } |
| 202 | try output(context, @memberName(T, field_i)); | 383 | try output(context, @memberName(T, field_i)); |
| 203 | try output(context, " = "); | 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 | try output(context, " }"); | 387 | try output(context, " }"); |
| 207 | }, | 388 | }, |
| ... | @@ -209,12 +390,12 @@ pub fn formatType( | ... | @@ -209,12 +390,12 @@ pub fn formatType( |
| 209 | .One => switch (@typeInfo(ptr_info.child)) { | 390 | .One => switch (@typeInfo(ptr_info.child)) { |
| 210 | builtin.TypeId.Array => |info| { | 391 | builtin.TypeId.Array => |info| { |
| 211 | if (info.child == u8) { | 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 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); | 395 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); |
| 215 | }, | 396 | }, |
| 216 | builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { | 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 | else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), | 400 | else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), |
| 220 | }, | 401 | }, |
| ... | @@ -222,17 +403,17 @@ pub fn formatType( | ... | @@ -222,17 +403,17 @@ pub fn formatType( |
| 222 | if (ptr_info.child == u8) { | 403 | if (ptr_info.child == u8) { |
| 223 | if (fmt.len > 0 and fmt[0] == 's') { | 404 | if (fmt.len > 0 and fmt[0] == 's') { |
| 224 | const len = mem.len(u8, value); | 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 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); | 409 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); |
| 229 | }, | 410 | }, |
| 230 | .Slice => { | 411 | .Slice => { |
| 231 | if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { | 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 | if (ptr_info.child == u8) { | 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 | return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr)); | 418 | return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr)); |
| 238 | }, | 419 | }, |
| ... | @@ -242,7 +423,7 @@ pub fn formatType( | ... | @@ -242,7 +423,7 @@ pub fn formatType( |
| 242 | }, | 423 | }, |
| 243 | .Array => |info| { | 424 | .Array => |info| { |
| 244 | if (info.child == u8) { | 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 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); | 428 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); |
| 248 | }, | 429 | }, |
| ... | @@ -256,28 +437,25 @@ pub fn formatType( | ... | @@ -256,28 +437,25 @@ pub fn formatType( |
| 256 | fn formatValue( | 437 | fn formatValue( |
| 257 | value: var, | 438 | value: var, |
| 258 | comptime fmt: []const u8, | 439 | comptime fmt: []const u8, |
| 440 | comptime options: FormatOptions, | ||
| 259 | context: var, | 441 | context: var, |
| 260 | comptime Errors: type, | 442 | comptime Errors: type, |
| 261 | output: fn (@typeOf(context), []const u8) Errors!void, | 443 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 262 | ) Errors!void { | 444 | ) Errors!void { |
| 263 | if (fmt.len > 0 and fmt[0] == 'B') { | 445 | if (comptime std.mem.eql(u8, fmt, "B")) { |
| 264 | comptime var width: ?usize = null; | 446 | // TODO https://github.com/ziglang/zig/issues/2725 |
| 265 | if (fmt.len > 1) { | 447 | if (options.width) |w| return formatBytes(value, w, 1000, context, Errors, output); |
| 266 | if (fmt[1] == 'i') { | 448 | return formatBytes(value, null, 1000, context, Errors, output); |
| 267 | if (fmt.len > 2) { | 449 | } else if (comptime std.mem.eql(u8, fmt, "Bi")) { |
| 268 | width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); | 450 | // TODO https://github.com/ziglang/zig/issues/2725 |
| 269 | } | 451 | if (options.width) |w| return formatBytes(value, w, 1024, context, Errors, output); |
| 270 | return formatBytes(value, width, 1024, context, Errors, output); | 452 | return formatBytes(value, null, 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); | ||
| 275 | } | 453 | } |
| 276 | 454 | ||
| 277 | const T = @typeOf(value); | 455 | const T = @typeOf(value); |
| 278 | switch (@typeId(T)) { | 456 | switch (@typeId(T)) { |
| 279 | .Float => return formatFloatValue(value, fmt, context, Errors, output), | 457 | .Float => return formatFloatValue(value, fmt, options, context, Errors, output), |
| 280 | .Int, .ComptimeInt => return formatIntValue(value, fmt, context, Errors, output), | 458 | .Int, .ComptimeInt => return formatIntValue(value, fmt, options, context, Errors, output), |
| 281 | else => comptime unreachable, | 459 | else => comptime unreachable, |
| 282 | } | 460 | } |
| 283 | } | 461 | } |
| ... | @@ -285,13 +463,13 @@ fn formatValue( | ... | @@ -285,13 +463,13 @@ fn formatValue( |
| 285 | pub fn formatIntValue( | 463 | pub fn formatIntValue( |
| 286 | value: var, | 464 | value: var, |
| 287 | comptime fmt: []const u8, | 465 | comptime fmt: []const u8, |
| 466 | comptime options: FormatOptions, | ||
| 288 | context: var, | 467 | context: var, |
| 289 | comptime Errors: type, | 468 | comptime Errors: type, |
| 290 | output: fn (@typeOf(context), []const u8) Errors!void, | 469 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 291 | ) Errors!void { | 470 | ) Errors!void { |
| 292 | comptime var radix = 10; | 471 | comptime var radix = 10; |
| 293 | comptime var uppercase = false; | 472 | comptime var uppercase = false; |
| 294 | comptime var width = 0; | ||
| 295 | 473 | ||
| 296 | const int_value = if (@typeOf(value) == comptime_int) blk: { | 474 | const int_value = if (@typeOf(value) == comptime_int) blk: { |
| 297 | const Int = math.IntFittingRange(value, value); | 475 | const Int = math.IntFittingRange(value, value); |
| ... | @@ -299,83 +477,75 @@ pub fn formatIntValue( | ... | @@ -299,83 +477,75 @@ pub fn formatIntValue( |
| 299 | } else | 477 | } else |
| 300 | value; | 478 | value; |
| 301 | 479 | ||
| 302 | if (fmt.len > 0) { | 480 | if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) { |
| 303 | switch (fmt[0]) { | 481 | radix = 10; |
| 304 | 'c' => { | 482 | uppercase = false; |
| 305 | if (@typeOf(int_value).bit_count <= 8) { | 483 | } else if (comptime std.mem.eql(u8, fmt, "c")) { |
| 306 | if (fmt.len > 1) | 484 | if (@typeOf(int_value).bit_count <= 8) { |
| 307 | @compileError("Unknown format character: " ++ [_]u8{fmt[1]}); | 485 | return formatAsciiChar(u8(int_value), context, Errors, output); |
| 308 | return formatAsciiChar(u8(int_value), context, Errors, output); | 486 | } else { |
| 309 | } | 487 | @compileError("Cannot print integer that is larger than 8 bits as a ascii"); |
| 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]}), | ||
| 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 | fn formatFloatValue( | 507 | fn formatFloatValue( |
| 339 | value: var, | 508 | value: var, |
| 340 | comptime fmt: []const u8, | 509 | comptime fmt: []const u8, |
| 510 | comptime options: FormatOptions, | ||
| 341 | context: var, | 511 | context: var, |
| 342 | comptime Errors: type, | 512 | comptime Errors: type, |
| 343 | output: fn (@typeOf(context), []const u8) Errors!void, | 513 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 344 | ) Errors!void { | 514 | ) Errors!void { |
| 345 | comptime var width: ?usize = null; | 515 | if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) { |
| 346 | comptime var float_fmt = 'e'; | 516 | // TODO https://github.com/ziglang/zig/issues/2725 |
| 347 | if (fmt.len > 0) { | 517 | if (options.precision) |p| return formatFloatScientific(value, p, context, Errors, output); |
| 348 | float_fmt = fmt[0]; | 518 | return formatFloatScientific(value, null, context, Errors, output); |
| 349 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | 519 | } else if (comptime std.mem.eql(u8, fmt, "d")) { |
| 350 | } | 520 | // TODO https://github.com/ziglang/zig/issues/2725 |
| 351 | 521 | if (options.precision) |p| return formatFloatDecimal(value, p, context, Errors, output); | |
| 352 | switch (float_fmt) { | 522 | return formatFloatDecimal(value, null, context, Errors, output); |
| 353 | 'e' => try formatFloatScientific(value, width, context, Errors, output), | 523 | } else { |
| 354 | '.' => try formatFloatDecimal(value, width, context, Errors, output), | 524 | @compileError("Unknown format string: '" ++ fmt ++ "'"); |
| 355 | else => @compileError("Unknown format character: " ++ [_]u8{float_fmt}), | ||
| 356 | } | 525 | } |
| 357 | } | 526 | } |
| 358 | 527 | ||
| 359 | pub fn formatText( | 528 | pub fn formatText( |
| 360 | bytes: []const u8, | 529 | bytes: []const u8, |
| 361 | comptime fmt: []const u8, | 530 | comptime fmt: []const u8, |
| 531 | comptime options: FormatOptions, | ||
| 362 | context: var, | 532 | context: var, |
| 363 | comptime Errors: type, | 533 | comptime Errors: type, |
| 364 | output: fn (@typeOf(context), []const u8) Errors!void, | 534 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 365 | ) Errors!void { | 535 | ) Errors!void { |
| 366 | if (fmt.len > 0) { | 536 | if (fmt.len == 0) { |
| 367 | if (fmt[0] == 's') { | 537 | return output(context, bytes); |
| 368 | comptime var width = 0; | 538 | } else if (comptime std.mem.eql(u8, fmt, "s")) { |
| 369 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | 539 | if (options.width) |w| return formatBuf(bytes, w, context, Errors, output); |
| 370 | return formatBuf(bytes, width, context, Errors, output); | 540 | return formatBuf(bytes, 0, context, Errors, output); |
| 371 | } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) { | 541 | } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { |
| 372 | for (bytes) |c| { | 542 | for (bytes) |c| { |
| 373 | try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); | 543 | try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); |
| 374 | } | 544 | } |
| 375 | return; | 545 | return; |
| 376 | } else @compileError("Unknown format character: " ++ [_]u8{fmt[0]}); | 546 | } else { |
| 547 | @compileError("Unknown format string: '" ++ fmt ++ "'"); | ||
| 377 | } | 548 | } |
| 378 | return output(context, bytes); | ||
| 379 | } | 549 | } |
| 380 | 550 | ||
| 381 | pub fn formatAsciiChar( | 551 | pub fn formatAsciiChar( |
| ... | @@ -868,7 +1038,7 @@ test "parseUnsigned" { | ... | @@ -868,7 +1038,7 @@ test "parseUnsigned" { |
| 868 | 1038 | ||
| 869 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; | 1039 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; |
| 870 | 1040 | ||
| 871 | test "fmt.parseFloat" { | 1041 | test "parseFloat" { |
| 872 | _ = @import("fmt/parse_float.zig"); | 1042 | _ = @import("fmt/parse_float.zig"); |
| 873 | } | 1043 | } |
| 874 | 1044 | ||
| ... | @@ -960,7 +1130,7 @@ test "parse unsigned comptime" { | ... | @@ -960,7 +1130,7 @@ test "parse unsigned comptime" { |
| 960 | } | 1130 | } |
| 961 | } | 1131 | } |
| 962 | 1132 | ||
| 963 | test "fmt.optional" { | 1133 | test "optional" { |
| 964 | { | 1134 | { |
| 965 | const value: ?i32 = 1234; | 1135 | const value: ?i32 = 1234; |
| 966 | try testFmt("optional: 1234\n", "optional: {}\n", value); | 1136 | try testFmt("optional: 1234\n", "optional: {}\n", value); |
| ... | @@ -971,7 +1141,7 @@ test "fmt.optional" { | ... | @@ -971,7 +1141,7 @@ test "fmt.optional" { |
| 971 | } | 1141 | } |
| 972 | } | 1142 | } |
| 973 | 1143 | ||
| 974 | test "fmt.error" { | 1144 | test "error" { |
| 975 | { | 1145 | { |
| 976 | const value: anyerror!i32 = 1234; | 1146 | const value: anyerror!i32 = 1234; |
| 977 | try testFmt("error union: 1234\n", "error union: {}\n", value); | 1147 | try testFmt("error union: 1234\n", "error union: {}\n", value); |
| ... | @@ -982,14 +1152,14 @@ test "fmt.error" { | ... | @@ -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 | const value: u3 = 0b101; | 1157 | const value: u3 = 0b101; |
| 988 | try testFmt("u3: 5\n", "u3: {}\n", value); | 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 | const value: u8 = 'a'; | 1164 | const value: u8 = 'a'; |
| 995 | try testFmt("u8: a\n", "u8: {c}\n", value); | 1165 | try testFmt("u8: a\n", "u8: {c}\n", value); |
| ... | @@ -1000,27 +1170,31 @@ test "fmt.int.specifier" { | ... | @@ -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 | var buf1: [32]u8 = undefined; | 1179 | var buf1: [32]u8 = undefined; |
| 1006 | var context = BufPrintContext{ .remaining = buf1[0..] }; | 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 | var res = buf1[0 .. buf1.len - context.remaining.len]; | 1182 | var res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1009 | testing.expect(mem.eql(u8, res, "1234")); | 1183 | testing.expect(mem.eql(u8, res, "1234")); |
| 1010 | 1184 | ||
| 1011 | context = BufPrintContext{ .remaining = buf1[0..] }; | 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 | res = buf1[0 .. buf1.len - context.remaining.len]; | 1187 | res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1014 | testing.expect(mem.eql(u8, res, "a")); | 1188 | testing.expect(mem.eql(u8, res, "a")); |
| 1015 | 1189 | ||
| 1016 | context = BufPrintContext{ .remaining = buf1[0..] }; | 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 | res = buf1[0 .. buf1.len - context.remaining.len]; | 1192 | res = buf1[0 .. buf1.len - context.remaining.len]; |
| 1019 | testing.expect(mem.eql(u8, res, "1100")); | 1193 | testing.expect(mem.eql(u8, res, "1100")); |
| 1020 | } | 1194 | } |
| 1021 | } | 1195 | } |
| 1022 | 1196 | ||
| 1023 | test "fmt.array" { | 1197 | test "array" { |
| 1024 | { | 1198 | { |
| 1025 | const value: [3]u8 = "abc"; | 1199 | const value: [3]u8 = "abc"; |
| 1026 | try testFmt("array: abc\n", "array: {}\n", value); | 1200 | try testFmt("array: abc\n", "array: {}\n", value); |
| ... | @@ -1035,7 +1209,7 @@ test "fmt.array" { | ... | @@ -1035,7 +1209,7 @@ test "fmt.array" { |
| 1035 | } | 1209 | } |
| 1036 | } | 1210 | } |
| 1037 | 1211 | ||
| 1038 | test "fmt.slice" { | 1212 | test "slice" { |
| 1039 | { | 1213 | { |
| 1040 | const value: []const u8 = "abc"; | 1214 | const value: []const u8 = "abc"; |
| 1041 | try testFmt("slice: abc\n", "slice: {}\n", value); | 1215 | try testFmt("slice: abc\n", "slice: {}\n", value); |
| ... | @@ -1045,11 +1219,11 @@ test "fmt.slice" { | ... | @@ -1045,11 +1219,11 @@ test "fmt.slice" { |
| 1045 | try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value); | 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 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); | 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 | const value = @intToPtr(*i32, 0xdeadbeef); | 1228 | const value = @intToPtr(*i32, 0xdeadbeef); |
| 1055 | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); | 1229 | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); |
| ... | @@ -1065,17 +1239,17 @@ test "fmt.pointer" { | ... | @@ -1065,17 +1239,17 @@ test "fmt.pointer" { |
| 1065 | } | 1239 | } |
| 1066 | } | 1240 | } |
| 1067 | 1241 | ||
| 1068 | test "fmt.cstr" { | 1242 | test "cstr" { |
| 1069 | try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); | 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 | try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); | 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 | const Struct = struct { | 1254 | const Struct = struct { |
| 1081 | field: u8, | 1255 | field: u8, |
| ... | @@ -1094,7 +1268,7 @@ test "fmt.struct" { | ... | @@ -1094,7 +1268,7 @@ test "fmt.struct" { |
| 1094 | } | 1268 | } |
| 1095 | } | 1269 | } |
| 1096 | 1270 | ||
| 1097 | test "fmt.enum" { | 1271 | test "enum" { |
| 1098 | const Enum = enum { | 1272 | const Enum = enum { |
| 1099 | One, | 1273 | One, |
| 1100 | Two, | 1274 | Two, |
| ... | @@ -1104,229 +1278,71 @@ test "fmt.enum" { | ... | @@ -1104,229 +1278,71 @@ test "fmt.enum" { |
| 1104 | try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); | 1278 | try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); |
| 1105 | } | 1279 | } |
| 1106 | 1280 | ||
| 1107 | test "fmt.float.scientific" { | 1281 | test "float.scientific" { |
| 1108 | { | 1282 | try testFmt("f32: 1.34000003e+00", "f32: {e}", f32(1.34)); |
| 1109 | var buf1: [32]u8 = undefined; | 1283 | try testFmt("f32: 1.23400001e+01", "f32: {e}", f32(12.34)); |
| 1110 | const value: f32 = 1.34; | 1284 | try testFmt("f64: -1.234e+11", "f64: {e}", f64(-12.34e10)); |
| 1111 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | 1285 | try testFmt("f64: 9.99996e-40", "f64: {e}", f64(9.999960e-40)); |
| 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 | } | ||
| 1137 | } | 1286 | } |
| 1138 | 1287 | ||
| 1139 | test "fmt.float.scientific.precision" { | 1288 | test "float.scientific.precision" { |
| 1140 | { | 1289 | try testFmt("f64: 1.40971e-42", "f64: {e:.5}", f64(1.409706e-42)); |
| 1141 | var buf1: [32]u8 = undefined; | 1290 | try testFmt("f64: 1.00000e-09", "f64: {e:.5}", f64(@bitCast(f32, u32(814313563)))); |
| 1142 | const value: f64 = 1.409706e-42; | 1291 | try testFmt("f64: 7.81250e-03", "f64: {e:.5}", f64(@bitCast(f32, u32(1006632960)))); |
| 1143 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | 1292 | // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. |
| 1144 | testing.expect(mem.eql(u8, result, "f64: 1.40971e-42\n")); | 1293 | // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. |
| 1145 | } | 1294 | try testFmt("f64: 1.00001e+05", "f64: {e:.5}", f64(@bitCast(f32, u32(1203982400)))); |
| 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 | } | ||
| 1166 | } | 1295 | } |
| 1167 | 1296 | ||
| 1168 | test "fmt.float.special" { | 1297 | test "float.special" { |
| 1169 | { | 1298 | try testFmt("f64: nan", "f64: {}", math.nan_f64); |
| 1170 | var buf1: [32]u8 = undefined; | 1299 | // negative nan is not defined by IEE 754, |
| 1171 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); | 1300 | // and ARM thus normalizes it to positive nan |
| 1172 | testing.expect(mem.eql(u8, result, "f64: nan\n")); | ||
| 1173 | } | ||
| 1174 | if (builtin.arch != builtin.Arch.arm) { | 1301 | if (builtin.arch != builtin.Arch.arm) { |
| 1175 | // negative nan is not defined by IEE 754, | 1302 | try testFmt("f64: -nan", "f64: {}", -math.nan_f64); |
| 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")); | ||
| 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" { | 1308 | test "float.decimal" { |
| 1194 | { | 1309 | try testFmt("f64: 152314000000000000000000000000", "f64: {d}", f64(1.52314e+29)); |
| 1195 | var buf1: [64]u8 = undefined; | 1310 | try testFmt("f32: 1.1", "f32: {d:.1}", f32(1.1234)); |
| 1196 | const value: f64 = 1.52314e+29; | 1311 | try testFmt("f32: 1234.57", "f32: {d:.2}", f32(1234.567)); |
| 1197 | const result = try bufPrint(buf1[0..], "f64: {.}\n", value); | 1312 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). |
| 1198 | testing.expect(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); | 1313 | // -11.12339... is rounded back up to -11.1234 |
| 1199 | } | 1314 | try testFmt("f32: -11.1234", "f32: {d:.4}", f32(-11.1234)); |
| 1200 | { | 1315 | try testFmt("f32: 91.12345", "f32: {d:.5}", f32(91.12345)); |
| 1201 | var buf1: [32]u8 = undefined; | 1316 | try testFmt("f64: 91.1234567890", "f64: {d:.10}", f64(91.12345678901235)); |
| 1202 | const value: f32 = 1.1234; | 1317 | try testFmt("f64: 0.00000", "f64: {d:.5}", f64(0.0)); |
| 1203 | const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); | 1318 | try testFmt("f64: 6", "f64: {d:.0}", f64(5.700)); |
| 1204 | testing.expect(mem.eql(u8, result, "f32: 1.1\n")); | 1319 | try testFmt("f64: 10.0", "f64: {d:.1}", f64(9.999)); |
| 1205 | } | 1320 | try testFmt("f64: 1.000", "f64: {d:.3}", f64(1.0)); |
| 1206 | { | 1321 | try testFmt("f64: 0.00030000", "f64: {d:.8}", f64(0.0003)); |
| 1207 | var buf1: [32]u8 = undefined; | 1322 | try testFmt("f64: 0.00000", "f64: {d:.5}", f64(1.40130e-45)); |
| 1208 | const value: f32 = 1234.567; | 1323 | try testFmt("f64: 0.00000", "f64: {d:.5}", f64(9.999960e-40)); |
| 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 | } | ||
| 1274 | } | 1324 | } |
| 1275 | 1325 | ||
| 1276 | test "fmt.float.libc.sanity" { | 1326 | test "float.libc.sanity" { |
| 1277 | { | 1327 | try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(916964781)))); |
| 1278 | var buf1: [32]u8 = undefined; | 1328 | try testFmt("f64: 0.00001", "f64: {d:.5}", f64(@bitCast(f32, u32(925353389)))); |
| 1279 | const value: f64 = f64(@bitCast(f32, u32(916964781))); | 1329 | try testFmt("f64: 0.10000", "f64: {d:.5}", f64(@bitCast(f32, u32(1036831278)))); |
| 1280 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | 1330 | try testFmt("f64: 1.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1065353133)))); |
| 1281 | testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); | 1331 | try testFmt("f64: 10.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1092616192)))); |
| 1282 | } | 1332 | |
| 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 | } | ||
| 1307 | // libc differences | 1333 | // libc differences |
| 1308 | { | 1334 | // |
| 1309 | var buf1: [32]u8 = undefined; | 1335 | // This is 0.015625 exactly according to gdb. We thus round down, |
| 1310 | // This is 0.015625 exactly according to gdb. We thus round down, | 1336 | // however glibc rounds up for some reason. This occurs for all |
| 1311 | // however glibc rounds up for some reason. This occurs for all | 1337 | // floats of the form x.yyyy25 on a precision point. |
| 1312 | // floats of the form x.yyyy25 on a precision point. | 1338 | try testFmt("f64: 0.01563", "f64: {d:.5}", f64(@bitCast(f32, u32(1015021568)))); |
| 1313 | const value: f64 = f64(@bitCast(f32, u32(1015021568))); | 1339 | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 |
| 1314 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | 1340 | // also rounds to 630 so I'm inclined to believe libc is not |
| 1315 | testing.expect(mem.eql(u8, result, "f64: 0.01563\n")); | 1341 | // optimal here. |
| 1316 | } | 1342 | try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", f64(@bitCast(f32, u32(1518338049)))); |
| 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 | } | ||
| 1327 | } | 1343 | } |
| 1328 | 1344 | ||
| 1329 | test "fmt.custom" { | 1345 | test "custom" { |
| 1330 | const Vec2 = struct { | 1346 | const Vec2 = struct { |
| 1331 | const SelfType = @This(); | 1347 | const SelfType = @This(); |
| 1332 | x: f32, | 1348 | x: f32, |
| ... | @@ -1335,20 +1351,17 @@ test "fmt.custom" { | ... | @@ -1335,20 +1351,17 @@ test "fmt.custom" { |
| 1335 | pub fn format( | 1351 | pub fn format( |
| 1336 | self: SelfType, | 1352 | self: SelfType, |
| 1337 | comptime fmt: []const u8, | 1353 | comptime fmt: []const u8, |
| 1354 | comptime options: FormatOptions, | ||
| 1338 | context: var, | 1355 | context: var, |
| 1339 | comptime Errors: type, | 1356 | comptime Errors: type, |
| 1340 | output: fn (@typeOf(context), []const u8) Errors!void, | 1357 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 1341 | ) Errors!void { | 1358 | ) Errors!void { |
| 1342 | switch (fmt.len) { | 1359 | if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) { |
| 1343 | 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), | 1360 | return std.fmt.format(context, Errors, output, "({d:.3},{d:.3})", self.x, self.y); |
| 1344 | 1 => switch (fmt[0]) { | 1361 | } else if (comptime std.mem.eql(u8, fmt, "d")) { |
| 1345 | //point format | 1362 | return std.fmt.format(context, Errors, output, "{d:.3}x{d:.3}", self.x, self.y); |
| 1346 | 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), | 1363 | } else { |
| 1347 | //dimension format | 1364 | @compileError("Unknown format character: '" ++ fmt ++ "'"); |
| 1348 | 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), | ||
| 1349 | else => unreachable, | ||
| 1350 | }, | ||
| 1351 | else => unreachable, | ||
| 1352 | } | 1365 | } |
| 1353 | } | 1366 | } |
| 1354 | }; | 1367 | }; |
| ... | @@ -1366,7 +1379,7 @@ test "fmt.custom" { | ... | @@ -1366,7 +1379,7 @@ test "fmt.custom" { |
| 1366 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); | 1379 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); |
| 1367 | } | 1380 | } |
| 1368 | 1381 | ||
| 1369 | test "fmt.struct" { | 1382 | test "struct" { |
| 1370 | const S = struct { | 1383 | const S = struct { |
| 1371 | a: u32, | 1384 | a: u32, |
| 1372 | b: anyerror, | 1385 | b: anyerror, |
| ... | @@ -1380,7 +1393,7 @@ test "fmt.struct" { | ... | @@ -1380,7 +1393,7 @@ test "fmt.struct" { |
| 1380 | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); | 1393 | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); |
| 1381 | } | 1394 | } |
| 1382 | 1395 | ||
| 1383 | test "fmt.union" { | 1396 | test "union" { |
| 1384 | const TU = union(enum) { | 1397 | const TU = union(enum) { |
| 1385 | float: f32, | 1398 | float: f32, |
| 1386 | int: u32, | 1399 | int: u32, |
| ... | @@ -1410,7 +1423,7 @@ test "fmt.union" { | ... | @@ -1410,7 +1423,7 @@ test "fmt.union" { |
| 1410 | testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); | 1423 | testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); |
| 1411 | } | 1424 | } |
| 1412 | 1425 | ||
| 1413 | test "fmt.enum" { | 1426 | test "enum" { |
| 1414 | const E = enum { | 1427 | const E = enum { |
| 1415 | One, | 1428 | One, |
| 1416 | Two, | 1429 | Two, |
| ... | @@ -1422,7 +1435,7 @@ test "fmt.enum" { | ... | @@ -1422,7 +1435,7 @@ test "fmt.enum" { |
| 1422 | try testFmt("E.Two", "{}", inst); | 1435 | try testFmt("E.Two", "{}", inst); |
| 1423 | } | 1436 | } |
| 1424 | 1437 | ||
| 1425 | test "fmt.struct.self-referential" { | 1438 | test "struct.self-referential" { |
| 1426 | const S = struct { | 1439 | const S = struct { |
| 1427 | const SelfType = @This(); | 1440 | const SelfType = @This(); |
| 1428 | a: ?*SelfType, | 1441 | a: ?*SelfType, |
| ... | @@ -1436,7 +1449,7 @@ test "fmt.struct.self-referential" { | ... | @@ -1436,7 +1449,7 @@ test "fmt.struct.self-referential" { |
| 1436 | try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst); | 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 | const some_bytes = "\xCA\xFE\xBA\xBE"; | 1453 | const some_bytes = "\xCA\xFE\xBA\xBE"; |
| 1441 | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); | 1454 | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); |
| 1442 | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); | 1455 | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); |
| ... | @@ -1478,7 +1491,7 @@ pub fn trim(buf: []const u8) []const u8 { | ... | @@ -1478,7 +1491,7 @@ pub fn trim(buf: []const u8) []const u8 { |
| 1478 | return buf[start..end]; | 1491 | return buf[start..end]; |
| 1479 | } | 1492 | } |
| 1480 | 1493 | ||
| 1481 | test "fmt.trim" { | 1494 | test "trim" { |
| 1482 | testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); | 1495 | testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); |
| 1483 | testing.expect(mem.eql(u8, "", trim(" "))); | 1496 | testing.expect(mem.eql(u8, "", trim(" "))); |
| 1484 | testing.expect(mem.eql(u8, "", trim(""))); | 1497 | testing.expect(mem.eql(u8, "", trim(""))); |
| ... | @@ -1505,22 +1518,22 @@ pub fn hexToBytes(out: []u8, input: []const u8) !void { | ... | @@ -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 | const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; | 1522 | const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; |
| 1510 | var pb: [32]u8 = undefined; | 1523 | var pb: [32]u8 = undefined; |
| 1511 | try hexToBytes(pb[0..], test_hex_str); | 1524 | try hexToBytes(pb[0..], test_hex_str); |
| 1512 | try testFmt(test_hex_str, "{X}", pb); | 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 | const value: comptime_int = 123456789123456789; | 1529 | const value: comptime_int = 123456789123456789; |
| 1517 | 1530 | ||
| 1518 | var buf = try std.Buffer.init(std.debug.global_allocator, ""); | 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 | assert(mem.eql(u8, buf.toSlice(), "123456789123456789")); | 1533 | assert(mem.eql(u8, buf.toSlice(), "123456789123456789")); |
| 1521 | } | 1534 | } |
| 1522 | 1535 | ||
| 1523 | test "fmt.formatType max_depth" { | 1536 | test "formatType max_depth" { |
| 1524 | const Vec2 = struct { | 1537 | const Vec2 = struct { |
| 1525 | const SelfType = @This(); | 1538 | const SelfType = @This(); |
| 1526 | x: f32, | 1539 | x: f32, |
| ... | @@ -1529,11 +1542,16 @@ test "fmt.formatType max_depth" { | ... | @@ -1529,11 +1542,16 @@ test "fmt.formatType max_depth" { |
| 1529 | pub fn format( | 1542 | pub fn format( |
| 1530 | self: SelfType, | 1543 | self: SelfType, |
| 1531 | comptime fmt: []const u8, | 1544 | comptime fmt: []const u8, |
| 1545 | comptime options: FormatOptions, | ||
| 1532 | context: var, | 1546 | context: var, |
| 1533 | comptime Errors: type, | 1547 | comptime Errors: type, |
| 1534 | output: fn (@typeOf(context), []const u8) Errors!void, | 1548 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 1535 | ) Errors!void { | 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 | const E = enum { | 1557 | const E = enum { |
| ... | @@ -1565,18 +1583,34 @@ test "fmt.formatType max_depth" { | ... | @@ -1565,18 +1583,34 @@ test "fmt.formatType max_depth" { |
| 1565 | inst.tu.ptr = &inst.tu; | 1583 | inst.tu.ptr = &inst.tu; |
| 1566 | 1584 | ||
| 1567 | var buf0 = try std.Buffer.init(std.debug.global_allocator, ""); | 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 | assert(mem.eql(u8, buf0.toSlice(), "S{ ... }")); | 1587 | assert(mem.eql(u8, buf0.toSlice(), "S{ ... }")); |
| 1570 | 1588 | ||
| 1571 | var buf1 = try std.Buffer.init(std.debug.global_allocator, ""); | 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 | assert(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); | 1591 | assert(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }")); |
| 1574 | 1592 | ||
| 1575 | var buf2 = try std.Buffer.init(std.debug.global_allocator, ""); | 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 | 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) }")); | 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 | var buf3 = try std.Buffer.init(std.debug.global_allocator, ""); | 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 | 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) }")); | 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,6 +519,7 @@ pub const Int = struct { |
| 519 | pub fn format( | 519 | pub fn format( |
| 520 | self: Int, | 520 | self: Int, |
| 521 | comptime fmt: []const u8, | 521 | comptime fmt: []const u8, |
| 522 | comptime options: std.fmt.FormatOptions, | ||
| 522 | context: var, | 523 | context: var, |
| 523 | comptime FmtError: type, | 524 | comptime FmtError: type, |
| 524 | output: fn (@typeOf(context), []const u8) FmtError!void, | 525 | output: fn (@typeOf(context), []const u8) FmtError!void, |
std/net.zig-1| ... | @@ -33,7 +33,6 @@ pub const Address = struct { | ... | @@ -33,7 +33,6 @@ pub const Address = struct { |
| 33 | 33 | ||
| 34 | pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address { | 34 | pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address { |
| 35 | return Address{ | 35 | return Address{ |
| 36 | .family = os.AF_INET6, | ||
| 37 | .os_addr = os.sockaddr{ | 36 | .os_addr = os.sockaddr{ |
| 38 | .in6 = os.sockaddr_in6{ | 37 | .in6 = os.sockaddr_in6{ |
| 39 | .family = os.AF_INET6, | 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,7 +167,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { |
| 167 | 167 | ||
| 168 | const allocator = builder.allocator; | 168 | const allocator = builder.allocator; |
| 169 | for (builder.top_level_steps.toSliceConst()) |top_level_step| { | 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 | try out_stream.write( | 173 | try out_stream.write( |
| ... | @@ -188,7 +188,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { | ... | @@ -188,7 +188,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { |
| 188 | for (builder.available_options_list.toSliceConst()) |option| { | 188 | for (builder.available_options_list.toSliceConst()) |option| { |
| 189 | const name = try fmt.allocPrint(allocator, " -D{}=[{}]", option.name, Builder.typeIdName(option.type_id)); | 189 | const name = try fmt.allocPrint(allocator, " -D{}=[{}]", option.name, Builder.typeIdName(option.type_id)); |
| 190 | defer allocator.free(name); | 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,19 +254,32 @@ export fn fmod(x: f64, y: f64) f64 { |
| 254 | 254 | ||
| 255 | // TODO add intrinsics for these (and probably the double version too) | 255 | // TODO add intrinsics for these (and probably the double version too) |
| 256 | // and have the math stuff use the intrinsic. same as @mod and @rem | 256 | // and have the math stuff use the intrinsic. same as @mod and @rem |
| 257 | export fn floorf(x: f32) f32 { | 257 | export fn floorf(x: f32) f32 {return math.floor(x);} |
| 258 | return math.floor(x); | 258 | export fn ceilf(x: f32) f32 {return math.ceil(x);} |
| 259 | } | 259 | export fn floor(x: f64) f64 {return math.floor(x);} |
| 260 | export fn ceilf(x: f32) f32 { | 260 | export fn ceil(x: f64) f64 {return math.ceil(x);} |
| 261 | return math.ceil(x); | 261 | export fn fma(a: f64, b: f64, c: f64) f64 {return math.fma(f64, a, b, c);} |
| 262 | } | 262 | export fn fmaf(a: f32, b: f32, c: f32) f32 {return math.fma(f32, a, b, c);} |
| 263 | export fn floor(x: f64) f64 { | 263 | export fn sin(a: f64) f64 {return math.sin(a);} |
| 264 | return math.floor(x); | 264 | export fn sinf(a: f32) f32 {return math.sin(a);} |
| 265 | } | 265 | export fn cos(a: f64) f64 {return math.cos(a);} |
| 266 | export fn ceil(x: f64) f64 { | 266 | export fn cosf(a: f32) f32 {return math.cos(a);} |
| 267 | return math.ceil(x); | 267 | export fn exp(a: f64) f64 {return math.exp(a);} |
| 268 | } | 268 | export fn expf(a: f32) f32 {return math.exp(a);} |
| 269 | 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 | fn generic_fmod(comptime T: type, x: T, y: T) T { | 283 | fn generic_fmod(comptime T: type, x: T, y: T) T { |
| 271 | @setRuntimeSafety(false); | 284 | @setRuntimeSafety(false); |
| 272 | 285 |
std/special/compiler_rt.zig+39-6| ... | @@ -405,15 +405,15 @@ const use_thumb_1 = usesThumb1(builtin.arch); | ... | @@ -405,15 +405,15 @@ const use_thumb_1 = usesThumb1(builtin.arch); |
| 405 | 405 | ||
| 406 | fn usesThumb1(arch: builtin.Arch) bool { | 406 | fn usesThumb1(arch: builtin.Arch) bool { |
| 407 | return switch (arch) { | 407 | return switch (arch) { |
| 408 | .arm => switch (arch.arm) { | 408 | .arm => |sub_arch| switch (sub_arch) { |
| 409 | .v6m => true, | 409 | .v6m => true, |
| 410 | else => false, | 410 | else => false, |
| 411 | }, | 411 | }, |
| 412 | .armeb => switch (arch.armeb) { | 412 | .armeb => |sub_arch| switch (sub_arch) { |
| 413 | .v6m => true, | 413 | .v6m => true, |
| 414 | else => false, | 414 | else => false, |
| 415 | }, | 415 | }, |
| 416 | .thumb => switch (arch.thumb) { | 416 | .thumb => |sub_arch| switch (sub_arch) { |
| 417 | .v5, | 417 | .v5, |
| 418 | .v5te, | 418 | .v5te, |
| 419 | .v4t, | 419 | .v4t, |
| ... | @@ -423,7 +423,7 @@ fn usesThumb1(arch: builtin.Arch) bool { | ... | @@ -423,7 +423,7 @@ fn usesThumb1(arch: builtin.Arch) bool { |
| 423 | => true, | 423 | => true, |
| 424 | else => false, | 424 | else => false, |
| 425 | }, | 425 | }, |
| 426 | .thumbeb => switch (arch.thumbeb) { | 426 | .thumbeb => |sub_arch| switch (sub_arch) { |
| 427 | .v5, | 427 | .v5, |
| 428 | .v5te, | 428 | .v5te, |
| 429 | .v4t, | 429 | .v4t, |
| ... | @@ -471,6 +471,22 @@ test "usesThumb1" { | ... | @@ -471,6 +471,22 @@ test "usesThumb1" { |
| 471 | //etc. | 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 | nakedcc fn __aeabi_memcpy() noreturn { | 490 | nakedcc fn __aeabi_memcpy() noreturn { |
| 475 | @setRuntimeSafety(false); | 491 | @setRuntimeSafety(false); |
| 476 | if (use_thumb_1) { | 492 | if (use_thumb_1) { |
| ... | @@ -505,7 +521,16 @@ nakedcc fn __aeabi_memmove() noreturn { | ... | @@ -505,7 +521,16 @@ nakedcc fn __aeabi_memmove() noreturn { |
| 505 | 521 | ||
| 506 | nakedcc fn __aeabi_memset() noreturn { | 522 | nakedcc fn __aeabi_memset() noreturn { |
| 507 | @setRuntimeSafety(false); | 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 | asm volatile ( | 534 | asm volatile ( |
| 510 | \\ mov r3, r1 | 535 | \\ mov r3, r1 |
| 511 | \\ mov r1, r2 | 536 | \\ mov r1, r2 |
| ... | @@ -527,7 +552,15 @@ nakedcc fn __aeabi_memset() noreturn { | ... | @@ -527,7 +552,15 @@ nakedcc fn __aeabi_memset() noreturn { |
| 527 | 552 | ||
| 528 | nakedcc fn __aeabi_memclr() noreturn { | 553 | nakedcc fn __aeabi_memclr() noreturn { |
| 529 | @setRuntimeSafety(false); | 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 | asm volatile ( | 564 | asm volatile ( |
| 532 | \\ mov r2, r1 | 565 | \\ mov r2, r1 |
| 533 | \\ movs r1, #0 | 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,8 +2833,8 @@ fn parseIf(arena: *Allocator, it: *TokenIterator, tree: *Tree, bodyParseFn: Node |
| 2833 | 2833 | ||
| 2834 | const else_token = eatToken(it, .Keyword_else) orelse return node; | 2834 | const else_token = eatToken(it, .Keyword_else) orelse return node; |
| 2835 | const payload = try parsePayload(arena, it, tree); | 2835 | const payload = try parsePayload(arena, it, tree); |
| 2836 | const else_expr = try expectNode(arena, it, tree, parseExpr, AstError{ | 2836 | const else_expr = try expectNode(arena, it, tree, bodyParseFn, AstError{ |
| 2837 | .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, | 2837 | .InvalidToken = AstError.InvalidToken{ .token = it.index }, |
| 2838 | }); | 2838 | }); |
| 2839 | const else_node = try arena.create(Node.Else); | 2839 | const else_node = try arena.create(Node.Else); |
| 2840 | else_node.* = Node.Else{ | 2840 | else_node.* = Node.Else{ |
std/zig/parser_test.zig+12| ... | @@ -2234,6 +2234,18 @@ test "zig fmt: multiline string in array" { | ... | @@ -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 | const std = @import("std"); | 2249 | const std = @import("std"); |
| 2238 | const mem = std.mem; | 2250 | const mem = std.mem; |
| 2239 | const warn = std.debug.warn; | 2251 | const warn = std.debug.warn; |
test/compare_output.zig+1-1| ... | @@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { | ... | @@ -122,7 +122,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 122 | \\ | 122 | \\ |
| 123 | \\pub fn main() void { | 123 | \\pub fn main() void { |
| 124 | \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; | 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 | , "Hello, world!\n0012 012 a\n"); | 127 | , "Hello, world!\n0012 012 a\n"); |
| 128 | 128 |
test/stage1/behavior.zig+2| ... | @@ -69,6 +69,8 @@ comptime { | ... | @@ -69,6 +69,8 @@ comptime { |
| 69 | _ = @import("behavior/optional.zig"); | 69 | _ = @import("behavior/optional.zig"); |
| 70 | _ = @import("behavior/pointers.zig"); | 70 | _ = @import("behavior/pointers.zig"); |
| 71 | _ = @import("behavior/popcount.zig"); | 71 | _ = @import("behavior/popcount.zig"); |
| 72 | _ = @import("behavior/muladd.zig"); | ||
| 73 | _ = @import("behavior/floatop.zig"); | ||
| 72 | _ = @import("behavior/ptrcast.zig"); | 74 | _ = @import("behavior/ptrcast.zig"); |
| 73 | _ = @import("behavior/pub_enum.zig"); | 75 | _ = @import("behavior/pub_enum.zig"); |
| 74 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); | 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 | |||