authorgravatar for shawn@git.icuShawn Landden <shawn@git.icu> 2019-06-21 16:18:59-05:00
committergravatar for shawn@git.icuShawn Landden <shawn@git.icu> 2019-06-22 14:34:34-05:00
log71e014caecaa54fdd8a0516710d2d9597da41398
tree4cfc85ed66ba9aec49e3f672a80c39b98e598177
parentebde2ff899c16612c7ff58df61f3946be47c51c8

stage1: add @sin @cos @exp @exp2 @ln @log2 @log10 @fabs @floor @ceil @trunc @round

and expand @sqrt This revealed that the accuracy of ln is not as good as the current algorithm in musl and glibc, and should be ported again. v2: actually include tests v3: fix reversal of in and out arguments on f128M_sqrt() add test for @sqrt on comptime_float do not include @nearbyInt() until it works on all targets.

11 files changed, 719 insertions(+), 131 deletions(-)

doc/langref.html.in+83-2
......@@ -7354,10 +7354,91 @@ test "@setRuntimeSafety" {
73547354 <pre>{#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}</pre>
73557355 <p>
73567356 Performs the square root of a floating point number. Uses a dedicated hardware instruction
7357 when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO.
7357 when available. Supports f16, f32, f64, and f128, as well as vectors.
73587358 </p>
7359 {#header_close#}
7360 {#header_open|@sin#}
7361 <pre>{#syntax#}@sin(comptime T: type, value: T) T{#endsyntax#}</pre>
7362 <p>
7363 Sine trigometric function on a floating point number. Uses a dedicated hardware instruction
7364 when available. Currently supports f32 and f64.
7365 </p>
7366 {#header_close#}
7367 {#header_open|@cos#}
7368 <pre>{#syntax#}@cos(comptime T: type, value: T) T{#endsyntax#}</pre>
7369 <p>
7370 Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction
7371 when available. Currently supports f32 and f64.
7372 </p>
7373 {#header_close#}
7374 {#header_open|@exp#}
7375 <pre>{#syntax#}@exp(comptime T: type, value: T) T{#endsyntax#}</pre>
7376 <p>
7377 Base-e exponential function on a floating point number. Uses a dedicated hardware instruction
7378 when available. Currently supports f32 and f64.
7379 </p>
7380 {#header_close#}
7381 {#header_open|@exp2#}
7382 <pre>{#syntax#}@exp2(comptime T: type, value: T) T{#endsyntax#}</pre>
7383 <p>
7384 Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction
7385 when available. Currently supports f32 and f64.
7386 </p>
7387 {#header_close#}
7388 {#header_open|@ln#}
7389 <pre>{#syntax#}@ln(comptime T: type, value: T) T{#endsyntax#}</pre>
7390 <p>
7391 Returns the natural logarithm of a floating point number. Uses a dedicated hardware instruction
7392 when available. Currently supports f32 and f64.
7393 </p>
7394 {#header_close#}
7395 {#header_open|@log2#}
7396 <pre>{#syntax#}@log2(comptime T: type, value: T) T{#endsyntax#}</pre>
7397 <p>
7398 Returns the logarithm to the base 2 of a floating point number. Uses a dedicated hardware instruction
7399 when available. Currently supports f32 and f64.
7400 </p>
7401 {#header_close#}
7402 {#header_open|@log10#}
7403 <pre>{#syntax#}@log10(comptime T: type, value: T) T{#endsyntax#}</pre>
7404 <p>
7405 Returns the logarithm to the base 10 of a floating point number. Uses a dedicated hardware instruction
7406 when available. Currently supports f32 and f64.
7407 </p>
7408 {#header_close#}
7409 {#header_open|@fabs#}
7410 <pre>{#syntax#}@fabs(comptime T: type, value: T) T{#endsyntax#}</pre>
7411 <p>
7412 Returns the absolute value of a floating point number. Uses a dedicated hardware instruction
7413 when available. Currently supports f32 and f64.
7414 </p>
7415 {#header_close#}
7416 {#header_open|@floor#}
7417 <pre>{#syntax#}@floor(comptime T: type, value: T) T{#endsyntax#}</pre>
7418 <p>
7419 Returns the largest integral value not greater than the given floating point number. Uses a dedicated hardware instruction
7420 when available. Currently supports f32 and f64.
7421 </p>
7422 {#header_close#}
7423 {#header_open|@ceil#}
7424 <pre>{#syntax#}@ceil(comptime T: type, value: T) T{#endsyntax#}</pre>
7425 <p>
7426 Returns the largest integral value not less than the given floating point number. Uses a dedicated hardware instruction
7427 when available. Currently supports f32 and f64.
7428 </p>
7429 {#header_close#}
7430 {#header_open|@trunc#}
7431 <pre>{#syntax#}@trunc(comptime T: type, value: T) T{#endsyntax#}</pre>
7432 <p>
7433 Rounds the given floating point number to an integer, towards zero. Uses a dedicated hardware instruction
7434 when available. Currently supports f32 and f64.
7435 </p>
7436 {#header_close#}
7437 {#header_open|@round#}
7438 <pre>{#syntax#}@round(comptime T: type, value: T) T{#endsyntax#}</pre>
73597439 <p>
7360 This is a low-level intrinsic. Most code can use {#syntax#}std.math.sqrt{#endsyntax#} instead.
7440 Rounds the given floating point number to an integer, away from zero. Uses a dedicated hardware instruction
7441 when available. Currently supports f32 and f64.
73617442 </p>
73627443 {#header_close#}
73637444
src/all_types.hpp+20-6
......@@ -1434,6 +1434,19 @@ enum BuiltinFnId {
14341434 BuiltinFnIdRem,
14351435 BuiltinFnIdMod,
14361436 BuiltinFnIdSqrt,
1437 BuiltinFnIdSin,
1438 BuiltinFnIdCos,
1439 BuiltinFnIdExp,
1440 BuiltinFnIdExp2,
1441 BuiltinFnIdLn,
1442 BuiltinFnIdLog2,
1443 BuiltinFnIdLog10,
1444 BuiltinFnIdFabs,
1445 BuiltinFnIdFloor,
1446 BuiltinFnIdCeil,
1447 BuiltinFnIdTrunc,
1448 BuiltinFnIdNearbyInt,
1449 BuiltinFnIdRound,
14371450 BuiltinFnIdTruncate,
14381451 BuiltinFnIdIntCast,
14391452 BuiltinFnIdFloatCast,
......@@ -1556,9 +1569,7 @@ enum ZigLLVMFnId {
15561569 ZigLLVMFnIdPopCount,
15571570 ZigLLVMFnIdOverflowArithmetic,
15581571 ZigLLVMFnIdFMA,
1559 ZigLLVMFnIdFloor,
1560 ZigLLVMFnIdCeil,
1561 ZigLLVMFnIdSqrt,
1572 ZigLLVMFnIdFloatOp,
15621573 ZigLLVMFnIdBswap,
15631574 ZigLLVMFnIdBitReverse,
15641575};
......@@ -1585,6 +1596,7 @@ struct ZigLLVMFnKey {
15851596 uint32_t bit_count;
15861597 } pop_count;
15871598 struct {
1599 BuiltinFnId op;
15881600 uint32_t bit_count;
15891601 uint32_t vector_len; // 0 means not a vector
15901602 } floating;
......@@ -2239,6 +2251,7 @@ enum IrInstructionId {
22392251 IrInstructionIdAlignOf,
22402252 IrInstructionIdOverflowOp,
22412253 IrInstructionIdMulAdd,
2254 IrInstructionIdFloatOp,
22422255 IrInstructionIdTestErr,
22432256 IrInstructionIdUnwrapErrCode,
22442257 IrInstructionIdUnwrapErrPayload,
......@@ -2300,7 +2313,6 @@ enum IrInstructionId {
23002313 IrInstructionIdAddImplicitReturnType,
23012314 IrInstructionIdMergeErrRetTraces,
23022315 IrInstructionIdMarkErrRetTracePtr,
2303 IrInstructionIdSqrt,
23042316 IrInstructionIdErrSetCast,
23052317 IrInstructionIdToBytes,
23062318 IrInstructionIdFromBytes,
......@@ -3474,11 +3486,13 @@ struct IrInstructionMarkErrRetTracePtr {
34743486 IrInstruction *err_ret_trace_ptr;
34753487};
34763488
3477struct IrInstructionSqrt {
3489// For float ops which take a single argument
3490struct IrInstructionFloatOp {
34783491 IrInstruction base;
34793492
3493 BuiltinFnId op;
34803494 IrInstruction *type;
3481 IrInstruction *op;
3495 IrInstruction *op1;
34823496};
34833497
34843498struct IrInstructionCheckRuntimeScope {
src/analyze.cpp+8-7
......@@ -5736,9 +5736,10 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
57365736 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
57375737 case ZigLLVMFnIdPopCount:
57385738 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049;
5739 case ZigLLVMFnIdFloor:
5740 case ZigLLVMFnIdCeil:
5741 case ZigLLVMFnIdSqrt:
5739 case ZigLLVMFnIdFloatOp:
5740 return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
5741 (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025) +
5742 (uint32_t)(x.data.floating.op) * (uint32_t)43789879;
57425743 case ZigLLVMFnIdFMA:
57435744 return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
57445745 (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025);
......@@ -5769,10 +5770,10 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
57695770 return a.data.bswap.bit_count == b.data.bswap.bit_count;
57705771 case ZigLLVMFnIdBitReverse:
57715772 return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count;
5772 case ZigLLVMFnIdFloor:
5773 case ZigLLVMFnIdCeil:
5774 case ZigLLVMFnIdSqrt:
5775 return a.data.floating.bit_count == b.data.floating.bit_count;
5773 case ZigLLVMFnIdFloatOp:
5774 return a.data.floating.bit_count == b.data.floating.bit_count &&
5775 a.data.floating.vector_len == b.data.floating.vector_len &&
5776 a.data.floating.op == b.data.floating.op;
57765777 case ZigLLVMFnIdFMA:
57775778 return a.data.floating.bit_count == b.data.floating.bit_count &&
57785779 a.data.floating.vector_len == b.data.floating.vector_len;
src/codegen.cpp+36-32
......@@ -806,7 +806,7 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSu
806806 return fn_val;
807807}
808808
809static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id) {
809static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) {
810810 assert(type_entry->id == ZigTypeIdFloat ||
811811 type_entry->id == ZigTypeIdVector);
812812
......@@ -817,6 +817,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn
817817 key.id = fn_id;
818818 key.data.floating.bit_count = (uint32_t)float_type->data.floating.bit_count;
819819 key.data.floating.vector_len = is_vector ? (uint32_t)type_entry->data.vector.len : 0;
820 key.data.floating.op = op;
820821
821822 auto existing_entry = g->llvm_fn_table.maybe_get(key);
822823 if (existing_entry)
......@@ -824,18 +825,12 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn
824825
825826 const char *name;
826827 uint32_t num_args;
827 if (fn_id == ZigLLVMFnIdFloor) {
828 name = "floor";
829 num_args = 1;
830 } else if (fn_id == ZigLLVMFnIdCeil) {
831 name = "ceil";
832 num_args = 1;
833 } else if (fn_id == ZigLLVMFnIdSqrt) {
834 name = "sqrt";
835 num_args = 1;
836 } else if (fn_id == ZigLLVMFnIdFMA) {
828 if (fn_id == ZigLLVMFnIdFMA) {
837829 name = "fma";
838830 num_args = 3;
831 } else if (fn_id == ZigLLVMFnIdFloatOp) {
832 name = float_op_to_name(op, true);
833 num_args = 1;
839834 } else {
840835 zig_unreachable();
841836 }
......@@ -2480,22 +2475,17 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry,
24802475 return result;
24812476}
24822477
2483static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, ZigType *type_entry) {
2484 if (type_entry->id == ZigTypeIdInt)
2478static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) {
2479 if ((op == BuiltinFnIdCeil ||
2480 op == BuiltinFnIdFloor) &&
2481 type_entry->id == ZigTypeIdInt)
24852482 return val;
2483 assert(type_entry->id == ZigTypeIdFloat);
24862484
2487 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor);
2485 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op);
24882486 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
24892487}
24902488
2491static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, ZigType *type_entry) {
2492 if (type_entry->id == ZigTypeIdInt)
2493 return val;
2494
2495 LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil);
2496 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
2497}
2498
24992489enum DivKind {
25002490 DivKindFloat,
25012491 DivKindTrunc,
......@@ -2571,7 +2561,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
25712561 return result;
25722562 case DivKindExact:
25732563 if (want_runtime_safety) {
2574 LLVMValueRef floored = gen_floor(g, result, type_entry);
2564 LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
25752565 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
25762566 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
25772567 LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, "");
......@@ -2593,12 +2583,12 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
25932583 LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block);
25942584
25952585 LLVMPositionBuilderAtEnd(g->builder, ltz_block);
2596 LLVMValueRef ceiled = gen_ceil(g, result, type_entry);
2586 LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil);
25972587 LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder);
25982588 LLVMBuildBr(g->builder, end_block);
25992589
26002590 LLVMPositionBuilderAtEnd(g->builder, gez_block);
2601 LLVMValueRef floored = gen_floor(g, result, type_entry);
2591 LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
26022592 LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder);
26032593 LLVMBuildBr(g->builder, end_block);
26042594
......@@ -2610,7 +2600,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
26102600 return phi;
26112601 }
26122602 case DivKindFloor:
2613 return gen_floor(g, result, type_entry);
2603 return gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
26142604 }
26152605 zig_unreachable();
26162606 }
......@@ -5450,10 +5440,10 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e
54505440 return nullptr;
54515441}
54525442
5453static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) {
5454 LLVMValueRef op = ir_llvm_value(g, instruction->op);
5443static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) {
5444 LLVMValueRef op = ir_llvm_value(g, instruction->op1);
54555445 assert(instruction->base.value.type->id == ZigTypeIdFloat);
5456 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt);
5446 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFloatOp, instruction->op);
54575447 return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
54585448}
54595449
......@@ -5463,7 +5453,7 @@ static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrIn
54635453 LLVMValueRef op3 = ir_llvm_value(g, instruction->op3);
54645454 assert(instruction->base.value.type->id == ZigTypeIdFloat ||
54655455 instruction->base.value.type->id == ZigTypeIdVector);
5466 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA);
5456 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd);
54675457 LLVMValueRef args[3] = {
54685458 op1,
54695459 op2,
......@@ -5814,8 +5804,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
58145804 return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
58155805 case IrInstructionIdMarkErrRetTracePtr:
58165806 return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
5817 case IrInstructionIdSqrt:
5818 return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
5807 case IrInstructionIdFloatOp:
5808 return ir_render_float_op(g, executable, (IrInstructionFloatOp *)instruction);
58195809 case IrInstructionIdMulAdd:
58205810 return ir_render_mul_add(g, executable, (IrInstructionMulAdd *)instruction);
58215811 case IrInstructionIdArrayToVector:
......@@ -7435,6 +7425,20 @@ static void define_builtin_fns(CodeGen *g) {
74357425 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
74367426 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
74377427 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
7428 create_builtin_fn(g, BuiltinFnIdSin, "sin", 2);
7429 create_builtin_fn(g, BuiltinFnIdCos, "cos", 2);
7430 create_builtin_fn(g, BuiltinFnIdExp, "exp", 2);
7431 create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 2);
7432 create_builtin_fn(g, BuiltinFnIdLn, "ln", 2);
7433 create_builtin_fn(g, BuiltinFnIdLog2, "log2", 2);
7434 create_builtin_fn(g, BuiltinFnIdLog10, "log10", 2);
7435 create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 2);
7436 create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2);
7437 create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2);
7438 create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2);
7439 //Needs library support on Windows
7440 //create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2);
7441 create_builtin_fn(g, BuiltinFnIdRound, "round", 2);
74387442 create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4);
74397443 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
74407444 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
src/ir.cpp+294-61
......@@ -991,8 +991,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP
991991 return IrInstructionIdMarkErrRetTracePtr;
992992}
993993
994static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
995 return IrInstructionIdSqrt;
994static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) {
995 return IrInstructionIdFloatOp;
996996}
997997
998998static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) {
......@@ -2312,6 +2312,59 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode
23122312 return &instruction->base;
23132313}
23142314
2315
2316//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign,
2317// lround, llround, lrint, llrint
2318// So far this is only non-complicated type functions.
2319const char *float_op_to_name(BuiltinFnId op, bool llvm_name) {
2320 const bool b = llvm_name;
2321
2322 switch (op) {
2323 case BuiltinFnIdSqrt:
2324 return "sqrt";
2325 case BuiltinFnIdSin:
2326 return "sin";
2327 case BuiltinFnIdCos:
2328 return "cos";
2329 case BuiltinFnIdExp:
2330 return "exp";
2331 case BuiltinFnIdExp2:
2332 return "exp2";
2333 case BuiltinFnIdLn:
2334 return b ? "log" : "ln";
2335 case BuiltinFnIdLog10:
2336 return "log10";
2337 case BuiltinFnIdLog2:
2338 return "log2";
2339 case BuiltinFnIdFabs:
2340 return "fabs";
2341 case BuiltinFnIdFloor:
2342 return "floor";
2343 case BuiltinFnIdCeil:
2344 return "ceil";
2345 case BuiltinFnIdTrunc:
2346 return "trunc";
2347 case BuiltinFnIdNearbyInt:
2348 return b ? "nearbyint" : "nearbyInt";
2349 case BuiltinFnIdRound:
2350 return "round";
2351 default:
2352 zig_unreachable();
2353 }
2354}
2355
2356static IrInstruction *ir_build_float_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op1, BuiltinFnId op) {
2357 IrInstructionFloatOp *instruction = ir_build_instruction<IrInstructionFloatOp>(irb, scope, source_node);
2358 instruction->type = type;
2359 instruction->op1 = op1;
2360 instruction->op = op;
2361
2362 if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
2363 ir_ref_instruction(op1, irb->current_basic_block);
2364
2365 return &instruction->base;
2366}
2367
23152368static IrInstruction *ir_build_mul_add(IrBuilder *irb, Scope *scope, AstNode *source_node,
23162369 IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *op3) {
23172370 IrInstructionMulAdd *instruction = ir_build_instruction<IrInstructionMulAdd>(irb, scope, source_node);
......@@ -3033,17 +3086,6 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco
30333086 return &instruction->base;
30343087}
30353088
3036static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
3037 IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node);
3038 instruction->type = type;
3039 instruction->op = op;
3040
3041 if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
3042 ir_ref_instruction(op, irb->current_basic_block);
3043
3044 return &instruction->base;
3045}
3046
30473089static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node,
30483090 IrInstruction *container, IrInstruction *name)
30493091{
......@@ -4400,6 +4442,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
44004442 return ir_lval_wrap(irb, scope, bin_op, lval);
44014443 }
44024444 case BuiltinFnIdSqrt:
4445 case BuiltinFnIdSin:
4446 case BuiltinFnIdCos:
4447 case BuiltinFnIdExp:
4448 case BuiltinFnIdExp2:
4449 case BuiltinFnIdLn:
4450 case BuiltinFnIdLog2:
4451 case BuiltinFnIdLog10:
4452 case BuiltinFnIdFabs:
4453 case BuiltinFnIdFloor:
4454 case BuiltinFnIdCeil:
4455 case BuiltinFnIdTrunc:
4456 case BuiltinFnIdNearbyInt:
4457 case BuiltinFnIdRound:
44034458 {
44044459 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
44054460 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
......@@ -4411,7 +4466,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
44114466 if (arg1_value == irb->codegen->invalid_instruction)
44124467 return arg1_value;
44134468
4414 IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value);
4469 IrInstruction *ir_sqrt = ir_build_float_op(irb, scope, node, arg0_value, arg1_value, builtin_fn->id);
44154470 return ir_lval_wrap(irb, scope, ir_sqrt, lval);
44164471 }
44174472 case BuiltinFnIdTruncate:
......@@ -23214,70 +23269,248 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i
2321423269 return result;
2321523270}
2321623271
23217static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
23218 ZigType *float_type = ir_resolve_type(ira, instruction->type->child);
23219 if (type_is_invalid(float_type))
23220 return ira->codegen->invalid_instruction;
23272static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type,
23273 ConstExprValue *op, ConstExprValue *out_val) {
23274 assert(ira && source_instr && float_type && out_val && op);
23275 assert(float_type->id == ZigTypeIdFloat ||
23276 float_type->id == ZigTypeIdComptimeFloat);
2322123277
23222 IrInstruction *op = instruction->op->child;
23223 if (type_is_invalid(op->value.type))
23278 BuiltinFnId fop = source_instr->op;
23279 unsigned bits;
23280
23281 if (float_type->id == ZigTypeIdComptimeFloat) {
23282 bits = 128;
23283 } else if (float_type->id == ZigTypeIdFloat)
23284 bits = float_type->data.floating.bit_count;
23285
23286 switch (bits) {
23287 case 16: {
23288 switch (fop) {
23289 case BuiltinFnIdSqrt:
23290 out_val->data.x_f16 = f16_sqrt(op->data.x_f16);
23291 break;
23292 case BuiltinFnIdSin:
23293 case BuiltinFnIdCos:
23294 case BuiltinFnIdExp:
23295 case BuiltinFnIdExp2:
23296 case BuiltinFnIdLn:
23297 case BuiltinFnIdLog10:
23298 case BuiltinFnIdLog2:
23299 case BuiltinFnIdFabs:
23300 case BuiltinFnIdFloor:
23301 case BuiltinFnIdCeil:
23302 case BuiltinFnIdTrunc:
23303 case BuiltinFnIdNearbyInt:
23304 case BuiltinFnIdRound:
23305 zig_panic("unimplemented f16 builtin");
23306 default:
23307 zig_unreachable();
23308 };
23309 break;
23310 };
23311 case 32: {
23312 switch (fop) {
23313 case BuiltinFnIdSqrt:
23314 out_val->data.x_f32 = sqrtf(op->data.x_f32);
23315 break;
23316 case BuiltinFnIdSin:
23317 out_val->data.x_f32 = sinf(op->data.x_f32);
23318 break;
23319 case BuiltinFnIdCos:
23320 out_val->data.x_f32 = cosf(op->data.x_f32);
23321 break;
23322 case BuiltinFnIdExp:
23323 out_val->data.x_f32 = expf(op->data.x_f32);
23324 break;
23325 case BuiltinFnIdExp2:
23326 out_val->data.x_f32 = exp2f(op->data.x_f32);
23327 break;
23328 case BuiltinFnIdLn:
23329 out_val->data.x_f32 = logf(op->data.x_f32);
23330 break;
23331 case BuiltinFnIdLog10:
23332 out_val->data.x_f32 = log10f(op->data.x_f32);
23333 break;
23334 case BuiltinFnIdLog2:
23335 out_val->data.x_f32 = log2f(op->data.x_f32);
23336 break;
23337 case BuiltinFnIdFabs:
23338 out_val->data.x_f32 = fabsf(op->data.x_f32);
23339 break;
23340 case BuiltinFnIdFloor:
23341 out_val->data.x_f32 = floorf(op->data.x_f32);
23342 break;
23343 case BuiltinFnIdCeil:
23344 out_val->data.x_f32 = ceilf(op->data.x_f32);
23345 break;
23346 case BuiltinFnIdTrunc:
23347 out_val->data.x_f32 = truncf(op->data.x_f32);
23348 break;
23349 case BuiltinFnIdNearbyInt:
23350 out_val->data.x_f32 = nearbyintf(op->data.x_f32);
23351 break;
23352 case BuiltinFnIdRound:
23353 out_val->data.x_f32 = roundf(op->data.x_f32);
23354 break;
23355 default:
23356 zig_unreachable();
23357 };
23358 break;
23359 };
23360 case 64: {
23361 switch (fop) {
23362 case BuiltinFnIdSqrt:
23363 out_val->data.x_f64 = sqrt(op->data.x_f64);
23364 break;
23365 case BuiltinFnIdSin:
23366 out_val->data.x_f64 = sin(op->data.x_f64);
23367 break;
23368 case BuiltinFnIdCos:
23369 out_val->data.x_f64 = cos(op->data.x_f64);
23370 break;
23371 case BuiltinFnIdExp:
23372 out_val->data.x_f64 = exp(op->data.x_f64);
23373 break;
23374 case BuiltinFnIdExp2:
23375 out_val->data.x_f64 = exp2(op->data.x_f64);
23376 break;
23377 case BuiltinFnIdLn:
23378 out_val->data.x_f64 = log(op->data.x_f64);
23379 break;
23380 case BuiltinFnIdLog10:
23381 out_val->data.x_f64 = log10(op->data.x_f64);
23382 break;
23383 case BuiltinFnIdLog2:
23384 out_val->data.x_f64 = log2(op->data.x_f64);
23385 break;
23386 case BuiltinFnIdFabs:
23387 out_val->data.x_f64 = fabs(op->data.x_f64);
23388 break;
23389 case BuiltinFnIdFloor:
23390 out_val->data.x_f64 = floor(op->data.x_f64);
23391 break;
23392 case BuiltinFnIdCeil:
23393 out_val->data.x_f64 = ceil(op->data.x_f64);
23394 break;
23395 case BuiltinFnIdTrunc:
23396 out_val->data.x_f64 = trunc(op->data.x_f64);
23397 break;
23398 case BuiltinFnIdNearbyInt:
23399 out_val->data.x_f64 = nearbyint(op->data.x_f64);
23400 break;
23401 case BuiltinFnIdRound:
23402 out_val->data.x_f64 = round(op->data.x_f64);
23403 break;
23404 default:
23405 zig_unreachable();
23406 }
23407 break;
23408 };
23409 case 128: {
23410 float128_t *out, *in;
23411 if (float_type->id == ZigTypeIdComptimeFloat) {
23412 out = &out_val->data.x_bigfloat.value;
23413 in = &op->data.x_bigfloat.value;
23414 } else {
23415 out = &out_val->data.x_f128;
23416 in = &op->data.x_f128;
23417 }
23418 switch (fop) {
23419 case BuiltinFnIdSqrt:
23420 f128M_sqrt(in, out);
23421 break;
23422 case BuiltinFnIdNearbyInt:
23423 case BuiltinFnIdSin:
23424 case BuiltinFnIdCos:
23425 case BuiltinFnIdExp:
23426 case BuiltinFnIdExp2:
23427 case BuiltinFnIdLn:
23428 case BuiltinFnIdLog10:
23429 case BuiltinFnIdLog2:
23430 case BuiltinFnIdFabs:
23431 case BuiltinFnIdFloor:
23432 case BuiltinFnIdCeil:
23433 case BuiltinFnIdTrunc:
23434 case BuiltinFnIdRound:
23435 zig_panic("unimplemented f128 builtin");
23436 default:
23437 zig_unreachable();
23438 }
23439 break;
23440 };
23441 default:
23442 zig_unreachable();
23443 }
23444}
23445
23446static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) {
23447 IrInstruction *type = instruction->type->child;
23448 if (type_is_invalid(type->value.type))
23449 return ira->codegen->invalid_instruction;
23450
23451 ZigType *expr_type = ir_resolve_type(ira, type);
23452 if (type_is_invalid(expr_type))
2322423453 return ira->codegen->invalid_instruction;
2322523454
23226 bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat;
23227 if (!ok_type) {
23228 ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
23455 // Only allow float types, and vectors of floats.
23456 ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
23457 if (float_type->id != ZigTypeIdFloat && float_type->id != ZigTypeIdComptimeFloat) {
23458 ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name)));
2322923459 return ira->codegen->invalid_instruction;
2323023460 }
2323123461
23232 IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
23233 if (type_is_invalid(casted_op->value.type))
23462 IrInstruction *op1 = instruction->op1->child;
23463 if (type_is_invalid(op1->value.type))
2323423464 return ira->codegen->invalid_instruction;
2323523465
23236 if (instr_is_comptime(casted_op)) {
23237 ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
23238 if (!val)
23466 IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type);
23467 if (type_is_invalid(casted_op1->value.type))
23468 return ira->codegen->invalid_instruction;
23469
23470 if (instr_is_comptime(casted_op1)) {
23471 // Our comptime 16-bit and 128-bit support is quite limited.
23472 if ((float_type->id == ZigTypeIdComptimeFloat ||
23473 float_type->data.floating.bit_count == 16 ||
23474 float_type->data.floating.bit_count == 128) &&
23475 instruction->op != BuiltinFnIdSqrt) {
23476 ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name)));
2323923477 return ira->codegen->invalid_instruction;
23478 }
2324023479
23241 IrInstruction *result = ir_const(ira, &instruction->base, float_type);
23480 ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad);
23481 if (!op1_const)
23482 return ira->codegen->invalid_instruction;
23483
23484 IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
2324223485 ConstExprValue *out_val = &result->value;
2324323486
23244 if (float_type->id == ZigTypeIdComptimeFloat) {
23245 bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
23246 } else if (float_type->id == ZigTypeIdFloat) {
23247 switch (float_type->data.floating.bit_count) {
23248 case 16:
23249 out_val->data.x_f16 = f16_sqrt(val->data.x_f16);
23250 break;
23251 case 32:
23252 out_val->data.x_f32 = sqrtf(val->data.x_f32);
23253 break;
23254 case 64:
23255 out_val->data.x_f64 = sqrt(val->data.x_f64);
23256 break;
23257 case 128:
23258 f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
23259 break;
23260 default:
23261 zig_unreachable();
23487 if (expr_type->id == ZigTypeIdVector) {
23488 expand_undef_array(ira->codegen, op1_const);
23489 out_val->special = ConstValSpecialUndef;
23490 expand_undef_array(ira->codegen, out_val);
23491 size_t len = expr_type->data.vector.len;
23492 for (size_t i = 0; i < len; i += 1) {
23493 ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i];
23494 ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i];
23495 assert(float_operand_op1->type == float_type);
23496 assert(float_out_val->type == float_type);
23497 ir_eval_float_op(ira, instruction, float_type,
23498 op1_const, float_out_val);
23499 float_out_val->type = float_type;
2326223500 }
23501 out_val->type = expr_type;
23502 out_val->special = ConstValSpecialStatic;
2326323503 } else {
23264 zig_unreachable();
23504 ir_eval_float_op(ira, instruction, float_type, op1_const, out_val);
2326523505 }
23266
2326723506 return result;
2326823507 }
2326923508
2327023509 ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base);
23271 if (float_type->data.floating.bit_count != 16 &&
23272 float_type->data.floating.bit_count != 32 &&
23273 float_type->data.floating.bit_count != 64) {
23274 ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
23275 return ira->codegen->invalid_instruction;
23276 }
2327723510
23278 IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
23279 instruction->base.source_node, nullptr, casted_op);
23280 result->value.type = float_type;
23511 IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope,
23512 instruction->base.source_node, nullptr, casted_op1, instruction->op);
23513 result->value.type = expr_type;
2328123514 return result;
2328223515}
2328323516
......@@ -23762,8 +23995,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
2376223995 return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
2376323996 case IrInstructionIdMarkErrRetTracePtr:
2376423997 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
23765 case IrInstructionIdSqrt:
23766 return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
23998 case IrInstructionIdFloatOp:
23999 return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction);
2376724000 case IrInstructionIdMulAdd:
2376824001 return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)instruction);
2376924002 case IrInstructionIdIntToErr:
......@@ -24004,7 +24237,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2400424237 case IrInstructionIdCoroFree:
2400524238 case IrInstructionIdCoroPromise:
2400624239 case IrInstructionIdPromiseResultType:
24007 case IrInstructionIdSqrt:
24240 case IrInstructionIdFloatOp:
2400824241 case IrInstructionIdMulAdd:
2400924242 case IrInstructionIdAtomicLoad:
2401024243 case IrInstructionIdIntCast:
src/ir.hpp+1
......@@ -26,5 +26,6 @@ bool ir_has_side_effects(IrInstruction *instruction);
2626struct IrAnalyze;
2727ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val,
2828 AstNode *source_node);
29const char *float_op_to_name(BuiltinFnId op, bool llvm_name);
2930
3031#endif
src/ir_print.cpp+6-5
......@@ -1427,15 +1427,16 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe
14271427 fprintf(irp->f, ")");
14281428}
14291429
1430static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
1431 fprintf(irp->f, "@sqrt(");
1430static void ir_print_float_op(IrPrint *irp, IrInstructionFloatOp *instruction) {
1431
1432 fprintf(irp->f, "@%s(", float_op_to_name(instruction->op, false));
14321433 if (instruction->type != nullptr) {
14331434 ir_print_other_instruction(irp, instruction->type);
14341435 } else {
14351436 fprintf(irp->f, "null");
14361437 }
14371438 fprintf(irp->f, ",");
1438 ir_print_other_instruction(irp, instruction->op);
1439 ir_print_other_instruction(irp, instruction->op1);
14391440 fprintf(irp->f, ")");
14401441}
14411442
......@@ -1918,8 +1919,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
19181919 case IrInstructionIdMarkErrRetTracePtr:
19191920 ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
19201921 break;
1921 case IrInstructionIdSqrt:
1922 ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
1922 case IrInstructionIdFloatOp:
1923 ir_print_float_op(irp, (IrInstructionFloatOp *)instruction);
19231924 break;
19241925 case IrInstructionIdMulAdd:
19251926 ir_print_mul_add(irp, (IrInstructionMulAdd *)instruction);
src/util.cpp+1
......@@ -13,6 +13,7 @@
1313#include "userland.h"
1414
1515void zig_panic(const char *format, ...) {
16 abort();
1617 va_list ap;
1718 va_start(ap, format);
1819 vfprintf(stderr, format, ap);
std/special/c.zig+26-18
......@@ -254,24 +254,32 @@ export fn fmod(x: f64, y: f64) f64 {
254254
255255// TODO add intrinsics for these (and probably the double version too)
256256// and have the math stuff use the intrinsic. same as @mod and @rem
257export fn floorf(x: f32) f32 {
258 return math.floor(x);
259}
260export fn ceilf(x: f32) f32 {
261 return math.ceil(x);
262}
263export fn floor(x: f64) f64 {
264 return math.floor(x);
265}
266export fn ceil(x: f64) f64 {
267 return math.ceil(x);
268}
269export fn fma(a: f64, b: f64, c: f64) f64 {
270 return math.fma(f64, a, b, c);
271}
272export fn fmaf(a: f32, b: f32, c: f32) f32 {
273 return math.fma(f32, a, b, c);
274}
257export fn floorf(x: f32) f32 {return math.floor(x);}
258export fn ceilf(x: f32) f32 {return math.ceil(x);}
259export fn floor(x: f64) f64 {return math.floor(x);}
260export fn ceil(x: f64) f64 {return math.ceil(x);}
261export fn fma(a: f64, b: f64, c: f64) f64 {return math.fma(f64, a, b, c);}
262export fn fmaf(a: f32, b: f32, c: f32) f32 {return math.fma(f32, a, b, c);}
263export fn sin(a: f64) f64 {return math.sin(a);}
264export fn sinf(a: f32) f32 {return math.sin(a);}
265export fn cos(a: f64) f64 {return math.cos(a);}
266export fn cosf(a: f32) f32 {return math.cos(a);}
267export fn exp(a: f64) f64 {return math.exp(a);}
268export fn expf(a: f32) f32 {return math.exp(a);}
269export fn exp2(a: f64) f64 {return math.exp2(a);}
270export fn exp2f(a: f32) f32 {return math.exp2(a);}
271export fn log(a: f64) f64 {return math.ln(a);}
272export fn logf(a: f32) f32 {return math.ln(a);}
273export fn log2(a: f64) f64 {return math.log2(a);}
274export fn log2f(a: f32) f32 {return math.log2(a);}
275export fn log10(a: f64) f64 {return math.log10(a);}
276export fn log10f(a: f32) f32 {return math.log10(a);}
277export fn fabs(a: f64) f64 {return math.fabs(a);}
278export fn fabsf(a: f32) f32 {return math.fabs(a);}
279export fn trunc(a: f64) f64 {return math.trunc(a);}
280export fn truncf(a: f32) f32 {return math.trunc(a);}
281export fn round(a: f64) f64 {return math.round(a);}
282export fn roundf(a: f32) f32 {return math.round(a);}
275283fn generic_fmod(comptime T: type, x: T, y: T) T {
276284 @setRuntimeSafety(false);
277285
test/stage1/behavior.zig+1
......@@ -71,6 +71,7 @@ comptime {
7171 _ = @import("behavior/pointers.zig");
7272 _ = @import("behavior/popcount.zig");
7373 _ = @import("behavior/muladd.zig");
74 _ = @import("behavior/floatop.zig");
7475 _ = @import("behavior/ptrcast.zig");
7576 _ = @import("behavior/pub_enum.zig");
7677 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
test/stage1/behavior/floatop.zig created+243
......@@ -0,0 +1,243 @@
1const expect = @import("std").testing.expect;
2const pi = @import("std").math.pi;
3const e = @import("std").math.e;
4
5test "@sqrt" {
6 comptime testSqrt();
7 testSqrt();
8}
9
10fn 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
34test "@sin" {
35 comptime testSin();
36 testSin();
37}
38
39fn 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
62test "@cos" {
63 comptime testCos();
64 testCos();
65}
66
67fn 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
78test "@exp" {
79 comptime testExp();
80 testExp();
81}
82
83fn 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
94test "@exp2" {
95 comptime testExp2();
96 testExp2();
97}
98
99fn 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
110test "@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
117fn 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
128test "@log2" {
129 comptime testLog2();
130 testLog2();
131}
132
133fn 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
144test "@log10" {
145 comptime testLog10();
146 testLog10();
147}
148
149fn 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
160test "@fabs" {
161 comptime testFabs();
162 testFabs();
163}
164
165fn 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
180test "@floor" {
181 comptime testFloor();
182 testFloor();
183}
184
185fn 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
196test "@ceil" {
197 comptime testCeil();
198 testCeil();
199}
200
201fn 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
212test "@trunc" {
213 comptime testTrunc();
214 testTrunc();
215}
216
217fn 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//}