authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-06-23 17:14:28-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-06-23 17:14:28-04:00
log24400d5882aa2b83a4aed3e4c1503d0393e1c541
treed06324bd3d1e58757d2843c97a2b6392dc5a3505
parentca3660f6bf3a1f8d77692acf72eefe148802d342
parent7f4de2dfdbeac4457e6e3170dad9ba9bf67ac9c8
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'shawnl-simd2'


12 files changed, 1008 insertions(+), 128 deletions(-)

CMakeLists.txt+2
......@@ -389,6 +389,8 @@ set(EMBEDDED_SOFTFLOAT_SOURCES
389389 "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF32.c"
390390 "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_subMagsF64.c"
391391 "${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"
392394 "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/softfloat_state.c"
393395 "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui32_to_f128M.c"
394396 "${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/ui64_to_f128M.c"
doc/langref.html.in+90-2
......@@ -6259,6 +6259,13 @@ comptime {
62596259 This function is only valid within function scope.
62606260 </p>
62616261
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>
62626269 {#header_close#}
62636270
62646271 {#header_open|@byteSwap#}
......@@ -7347,10 +7354,91 @@ test "@setRuntimeSafety" {
73477354 <pre>{#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}</pre>
73487355 <p>
73497356 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.
73517365 </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>
73527439 <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.
73547442 </p>
73557443 {#header_close#}
73567444
src/all_types.hpp+33-6
......@@ -1406,6 +1406,7 @@ enum BuiltinFnId {
14061406 BuiltinFnIdSubWithOverflow,
14071407 BuiltinFnIdMulWithOverflow,
14081408 BuiltinFnIdShlWithOverflow,
1409 BuiltinFnIdMulAdd,
14091410 BuiltinFnIdCInclude,
14101411 BuiltinFnIdCDefine,
14111412 BuiltinFnIdCUndef,
......@@ -1433,6 +1434,19 @@ enum BuiltinFnId {
14331434 BuiltinFnIdRem,
14341435 BuiltinFnIdMod,
14351436 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,
14361450 BuiltinFnIdTruncate,
14371451 BuiltinFnIdIntCast,
14381452 BuiltinFnIdFloatCast,
......@@ -1554,9 +1568,8 @@ enum ZigLLVMFnId {
15541568 ZigLLVMFnIdClz,
15551569 ZigLLVMFnIdPopCount,
15561570 ZigLLVMFnIdOverflowArithmetic,
1557 ZigLLVMFnIdFloor,
1558 ZigLLVMFnIdCeil,
1559 ZigLLVMFnIdSqrt,
1571 ZigLLVMFnIdFMA,
1572 ZigLLVMFnIdFloatOp,
15601573 ZigLLVMFnIdBswap,
15611574 ZigLLVMFnIdBitReverse,
15621575};
......@@ -1583,7 +1596,9 @@ struct ZigLLVMFnKey {
15831596 uint32_t bit_count;
15841597 } pop_count;
15851598 struct {
1599 BuiltinFnId op;
15861600 uint32_t bit_count;
1601 uint32_t vector_len; // 0 means not a vector
15871602 } floating;
15881603 struct {
15891604 AddSubMul add_sub_mul;
......@@ -2235,6 +2250,8 @@ enum IrInstructionId {
22352250 IrInstructionIdHandle,
22362251 IrInstructionIdAlignOf,
22372252 IrInstructionIdOverflowOp,
2253 IrInstructionIdMulAdd,
2254 IrInstructionIdFloatOp,
22382255 IrInstructionIdTestErr,
22392256 IrInstructionIdUnwrapErrCode,
22402257 IrInstructionIdUnwrapErrPayload,
......@@ -2296,7 +2313,6 @@ enum IrInstructionId {
22962313 IrInstructionIdAddImplicitReturnType,
22972314 IrInstructionIdMergeErrRetTraces,
22982315 IrInstructionIdMarkErrRetTracePtr,
2299 IrInstructionIdSqrt,
23002316 IrInstructionIdErrSetCast,
23012317 IrInstructionIdToBytes,
23022318 IrInstructionIdFromBytes,
......@@ -3038,6 +3054,15 @@ struct IrInstructionOverflowOp {
30383054 ZigType *result_ptr_type;
30393055};
30403056
3057struct IrInstructionMulAdd {
3058 IrInstruction base;
3059
3060 IrInstruction *type_value;
3061 IrInstruction *op1;
3062 IrInstruction *op2;
3063 IrInstruction *op3;
3064};
3065
30413066struct IrInstructionAlignOf {
30423067 IrInstruction base;
30433068
......@@ -3461,11 +3486,13 @@ struct IrInstructionMarkErrRetTracePtr {
34613486 IrInstruction *err_ret_trace_ptr;
34623487};
34633488
3464struct IrInstructionSqrt {
3489// For float ops which take a single argument
3490struct IrInstructionFloatOp {
34653491 IrInstruction base;
34663492
3493 BuiltinFnId op;
34673494 IrInstruction *type;
3468 IrInstruction *op;
3495 IrInstruction *op1;
34693496};
34703497
34713498struct IrInstructionCheckRuntimeScope {
src/analyze.cpp+14-10
......@@ -5736,12 +5736,13 @@ 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 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;
5741 case ZigLLVMFnIdCeil:
5742 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089;
5743 case ZigLLVMFnIdSqrt:
5744 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385;
5739 case ZigLLVMFnIdFloatOp:
5740 return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
5741 (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025) +
5742 (uint32_t)(x.data.floating.op) * (uint32_t)43789879;
5743 case ZigLLVMFnIdFMA:
5744 return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
5745 (uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025);
57455746 case ZigLLVMFnIdBswap:
57465747 return (uint32_t)(x.data.bswap.bit_count) * (uint32_t)3661994335;
57475748 case ZigLLVMFnIdBitReverse:
......@@ -5769,10 +5770,13 @@ 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;
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;
57765780 case ZigLLVMFnIdOverflowArithmetic:
57775781 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
57785782 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
src/codegen.cpp+73-31
......@@ -806,32 +806,47 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSu
806806 return fn_val;
807807}
808808
809static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id) {
810 assert(type_entry->id == ZigTypeIdFloat);
809static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) {
810 assert(type_entry->id == ZigTypeIdFloat ||
811 type_entry->id == ZigTypeIdVector);
812
813 bool is_vector = (type_entry->id == ZigTypeIdVector);
814 ZigType *float_type = is_vector ? type_entry->data.vector.elem_type : type_entry;
811815
812816 ZigLLVMFnKey key = {};
813817 key.id = fn_id;
814 key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count;
818 key.data.floating.bit_count = (uint32_t)float_type->data.floating.bit_count;
819 key.data.floating.vector_len = is_vector ? (uint32_t)type_entry->data.vector.len : 0;
820 key.data.floating.op = op;
815821
816822 auto existing_entry = g->llvm_fn_table.maybe_get(key);
817823 if (existing_entry)
818824 return existing_entry->value;
819825
820826 const char *name;
821 if (fn_id == ZigLLVMFnIdFloor) {
822 name = "floor";
823 } else if (fn_id == ZigLLVMFnIdCeil) {
824 name = "ceil";
825 } else if (fn_id == ZigLLVMFnIdSqrt) {
826 name = "sqrt";
827 uint32_t num_args;
828 if (fn_id == ZigLLVMFnIdFMA) {
829 name = "fma";
830 num_args = 3;
831 } else if (fn_id == ZigLLVMFnIdFloatOp) {
832 name = float_op_to_name(op, true);
833 num_args = 1;
827834 } else {
828835 zig_unreachable();
829836 }
830837
831838 char fn_name[64];
832 sprintf(fn_name, "llvm.%s.f%" ZIG_PRI_usize "", name, type_entry->data.floating.bit_count);
839 if (is_vector)
840 sprintf(fn_name, "llvm.%s.v%" PRIu32 "f%" PRIu32, name, key.data.floating.vector_len, key.data.floating.bit_count);
841 else
842 sprintf(fn_name, "llvm.%s.f%" PRIu32, name, key.data.floating.bit_count);
833843 LLVMTypeRef float_type_ref = get_llvm_type(g, type_entry);
834 LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, &float_type_ref, 1, false);
844 LLVMTypeRef return_elem_types[3] = {
845 float_type_ref,
846 float_type_ref,
847 float_type_ref,
848 };
849 LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, return_elem_types, num_args, false);
835850 LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type);
836851 assert(LLVMGetIntrinsicID(fn_val));
837852
......@@ -2460,22 +2475,17 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry,
24602475 return result;
24612476}
24622477
2463static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, ZigType *type_entry) {
2464 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)
24652482 return val;
2483 assert(type_entry->id == ZigTypeIdFloat);
24662484
2467 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor);
2485 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op);
24682486 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
24692487}
24702488
2471static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, ZigType *type_entry) {
2472 if (type_entry->id == ZigTypeIdInt)
2473 return val;
2474
2475 LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil);
2476 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
2477}
2478
24792489enum DivKind {
24802490 DivKindFloat,
24812491 DivKindTrunc,
......@@ -2551,7 +2561,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
25512561 return result;
25522562 case DivKindExact:
25532563 if (want_runtime_safety) {
2554 LLVMValueRef floored = gen_floor(g, result, type_entry);
2564 LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
25552565 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
25562566 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
25572567 LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, "");
......@@ -2573,12 +2583,12 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
25732583 LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block);
25742584
25752585 LLVMPositionBuilderAtEnd(g->builder, ltz_block);
2576 LLVMValueRef ceiled = gen_ceil(g, result, type_entry);
2586 LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil);
25772587 LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder);
25782588 LLVMBuildBr(g->builder, end_block);
25792589
25802590 LLVMPositionBuilderAtEnd(g->builder, gez_block);
2581 LLVMValueRef floored = gen_floor(g, result, type_entry);
2591 LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
25822592 LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder);
25832593 LLVMBuildBr(g->builder, end_block);
25842594
......@@ -2590,7 +2600,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
25902600 return phi;
25912601 }
25922602 case DivKindFloor:
2593 return gen_floor(g, result, type_entry);
2603 return gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
25942604 }
25952605 zig_unreachable();
25962606 }
......@@ -5430,13 +5440,28 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e
54305440 return nullptr;
54315441}
54325442
5433static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) {
5434 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);
54355445 assert(instruction->base.value.type->id == ZigTypeIdFloat);
5436 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt);
5446 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFloatOp, instruction->op);
54375447 return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
54385448}
54395449
5450static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrInstructionMulAdd *instruction) {
5451 LLVMValueRef op1 = ir_llvm_value(g, instruction->op1);
5452 LLVMValueRef op2 = ir_llvm_value(g, instruction->op2);
5453 LLVMValueRef op3 = ir_llvm_value(g, instruction->op3);
5454 assert(instruction->base.value.type->id == ZigTypeIdFloat ||
5455 instruction->base.value.type->id == ZigTypeIdVector);
5456 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd);
5457 LLVMValueRef args[3] = {
5458 op1,
5459 op2,
5460 op3,
5461 };
5462 return LLVMBuildCall(g->builder, fn_val, args, 3, "");
5463}
5464
54405465static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutable *executable, IrInstructionBswap *instruction) {
54415466 LLVMValueRef op = ir_llvm_value(g, instruction->op);
54425467 ZigType *int_type = instruction->base.value.type;
......@@ -5779,8 +5804,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
57795804 return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
57805805 case IrInstructionIdMarkErrRetTracePtr:
57815806 return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
5782 case IrInstructionIdSqrt:
5783 return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
5807 case IrInstructionIdFloatOp:
5808 return ir_render_float_op(g, executable, (IrInstructionFloatOp *)instruction);
5809 case IrInstructionIdMulAdd:
5810 return ir_render_mul_add(g, executable, (IrInstructionMulAdd *)instruction);
57845811 case IrInstructionIdArrayToVector:
57855812 return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction);
57865813 case IrInstructionIdVectorToArray:
......@@ -7398,6 +7425,21 @@ static void define_builtin_fns(CodeGen *g) {
73987425 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
73997426 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
74007427 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
7428 create_builtin_fn(g, BuiltinFnIdSin, "sin", 2);
7429 create_builtin_fn(g, BuiltinFnIdCos, "cos", 2);
7430 create_builtin_fn(g, BuiltinFnIdExp, "exp", 2);
7431 create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 2);
7432 create_builtin_fn(g, BuiltinFnIdLn, "ln", 2);
7433 create_builtin_fn(g, BuiltinFnIdLog2, "log2", 2);
7434 create_builtin_fn(g, BuiltinFnIdLog10, "log10", 2);
7435 create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 2);
7436 create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2);
7437 create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2);
7438 create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2);
7439 //Needs library support on Windows
7440 //create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2);
7441 create_builtin_fn(g, BuiltinFnIdRound, "round", 2);
7442 create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4);
74017443 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
74027444 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
74037445 create_builtin_fn(g, BuiltinFnIdNewStackCall, "newStackCall", SIZE_MAX);
src/ir.cpp+465-61
......@@ -747,6 +747,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErr *) {
747747 return IrInstructionIdTestErr;
748748}
749749
750static constexpr IrInstructionId ir_instruction_id(IrInstructionMulAdd *) {
751 return IrInstructionIdMulAdd;
752}
753
750754static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) {
751755 return IrInstructionIdUnwrapErrCode;
752756}
......@@ -987,8 +991,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP
987991 return IrInstructionIdMarkErrRetTracePtr;
988992}
989993
990static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
991 return IrInstructionIdSqrt;
994static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) {
995 return IrInstructionIdFloatOp;
992996}
993997
994998static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) {
......@@ -2308,6 +2312,75 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode
23082312 return &instruction->base;
23092313}
23102314
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
2368static IrInstruction *ir_build_mul_add(IrBuilder *irb, Scope *scope, AstNode *source_node,
2369 IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *op3) {
2370 IrInstructionMulAdd *instruction = ir_build_instruction<IrInstructionMulAdd>(irb, scope, source_node);
2371 instruction->type_value = type_value;
2372 instruction->op1 = op1;
2373 instruction->op2 = op2;
2374 instruction->op3 = op3;
2375
2376 ir_ref_instruction(type_value, irb->current_basic_block);
2377 ir_ref_instruction(op1, irb->current_basic_block);
2378 ir_ref_instruction(op2, irb->current_basic_block);
2379 ir_ref_instruction(op3, irb->current_basic_block);
2380
2381 return &instruction->base;
2382}
2383
23112384static IrInstruction *ir_build_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) {
23122385 IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node);
23132386 instruction->type_value = type_value;
......@@ -3013,17 +3086,6 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco
30133086 return &instruction->base;
30143087}
30153088
3016static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
3017 IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node);
3018 instruction->type = type;
3019 instruction->op = op;
3020
3021 if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
3022 ir_ref_instruction(op, irb->current_basic_block);
3023
3024 return &instruction->base;
3025}
3026
30273089static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node,
30283090 IrInstruction *container, IrInstruction *name)
30293091{
......@@ -4028,6 +4090,33 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *
40284090 return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);
40294091}
40304092
4093static IrInstruction *ir_gen_mul_add(IrBuilder *irb, Scope *scope, AstNode *node) {
4094 assert(node->type == NodeTypeFnCallExpr);
4095
4096 AstNode *type_node = node->data.fn_call_expr.params.at(0);
4097 AstNode *op1_node = node->data.fn_call_expr.params.at(1);
4098 AstNode *op2_node = node->data.fn_call_expr.params.at(2);
4099 AstNode *op3_node = node->data.fn_call_expr.params.at(3);
4100
4101 IrInstruction *type_value = ir_gen_node(irb, type_node, scope);
4102 if (type_value == irb->codegen->invalid_instruction)
4103 return irb->codegen->invalid_instruction;
4104
4105 IrInstruction *op1 = ir_gen_node(irb, op1_node, scope);
4106 if (op1 == irb->codegen->invalid_instruction)
4107 return irb->codegen->invalid_instruction;
4108
4109 IrInstruction *op2 = ir_gen_node(irb, op2_node, scope);
4110 if (op2 == irb->codegen->invalid_instruction)
4111 return irb->codegen->invalid_instruction;
4112
4113 IrInstruction *op3 = ir_gen_node(irb, op3_node, scope);
4114 if (op3 == irb->codegen->invalid_instruction)
4115 return irb->codegen->invalid_instruction;
4116
4117 return ir_build_mul_add(irb, scope, node, type_value, op1, op2, op3);
4118}
4119
40314120static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) {
40324121 for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) {
40334122 if (it_scope->id == ScopeIdDecls) {
......@@ -4353,6 +4442,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
43534442 return ir_lval_wrap(irb, scope, bin_op, lval);
43544443 }
43554444 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:
43564458 {
43574459 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
43584460 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
......@@ -4364,7 +4466,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
43644466 if (arg1_value == irb->codegen->invalid_instruction)
43654467 return arg1_value;
43664468
4367 IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value);
4469 IrInstruction *ir_sqrt = ir_build_float_op(irb, scope, node, arg0_value, arg1_value, builtin_fn->id);
43684470 return ir_lval_wrap(irb, scope, ir_sqrt, lval);
43694471 }
43704472 case BuiltinFnIdTruncate:
......@@ -4687,6 +4789,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
46874789 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval);
46884790 case BuiltinFnIdShlWithOverflow:
46894791 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval);
4792 case BuiltinFnIdMulAdd:
4793 return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval);
46904794 case BuiltinFnIdTypeName:
46914795 {
46924796 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
......@@ -21187,6 +21291,125 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr
2118721291 return result;
2118821292}
2118921293
21294static void ir_eval_mul_add(IrAnalyze *ira, IrInstructionMulAdd *source_instr, ZigType *float_type,
21295 ConstExprValue *op1, ConstExprValue *op2, ConstExprValue *op3, ConstExprValue *out_val) {
21296 if (float_type->id == ZigTypeIdComptimeFloat) {
21297 f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value,
21298 &op3->data.x_bigfloat.value);
21299 } else if (float_type->id == ZigTypeIdFloat) {
21300 switch (float_type->data.floating.bit_count) {
21301 case 16:
21302 out_val->data.x_f16 = f16_mulAdd(op1->data.x_f16, op2->data.x_f16, op3->data.x_f16);
21303 break;
21304 case 32:
21305 out_val->data.x_f32 = fmaf(op1->data.x_f32, op2->data.x_f32, op3->data.x_f32);
21306 break;
21307 case 64:
21308 out_val->data.x_f64 = fma(op1->data.x_f64, op2->data.x_f64, op3->data.x_f64);
21309 break;
21310 case 128:
21311 f128M_mulAdd(&op1->data.x_f128, &op2->data.x_f128, &op3->data.x_f128, &out_val->data.x_f128);
21312 break;
21313 default:
21314 zig_unreachable();
21315 }
21316 } else {
21317 zig_unreachable();
21318 }
21319}
21320
21321static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructionMulAdd *instruction) {
21322 IrInstruction *type_value = instruction->type_value->child;
21323 if (type_is_invalid(type_value->value.type))
21324 return ira->codegen->invalid_instruction;
21325
21326 ZigType *expr_type = ir_resolve_type(ira, type_value);
21327 if (type_is_invalid(expr_type))
21328 return ira->codegen->invalid_instruction;
21329
21330 // Only allow float types, and vectors of floats.
21331 ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
21332 if (float_type->id != ZigTypeIdFloat) {
21333 ir_add_error(ira, type_value,
21334 buf_sprintf("expected float or vector of float type, found '%s'", buf_ptr(&float_type->name)));
21335 return ira->codegen->invalid_instruction;
21336 }
21337
21338 IrInstruction *op1 = instruction->op1->child;
21339 if (type_is_invalid(op1->value.type))
21340 return ira->codegen->invalid_instruction;
21341
21342 IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, expr_type);
21343 if (type_is_invalid(casted_op1->value.type))
21344 return ira->codegen->invalid_instruction;
21345
21346 IrInstruction *op2 = instruction->op2->child;
21347 if (type_is_invalid(op2->value.type))
21348 return ira->codegen->invalid_instruction;
21349
21350 IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, expr_type);
21351 if (type_is_invalid(casted_op2->value.type))
21352 return ira->codegen->invalid_instruction;
21353
21354 IrInstruction *op3 = instruction->op3->child;
21355 if (type_is_invalid(op3->value.type))
21356 return ira->codegen->invalid_instruction;
21357
21358 IrInstruction *casted_op3 = ir_implicit_cast(ira, op3, expr_type);
21359 if (type_is_invalid(casted_op3->value.type))
21360 return ira->codegen->invalid_instruction;
21361
21362 if (instr_is_comptime(casted_op1) &&
21363 instr_is_comptime(casted_op2) &&
21364 instr_is_comptime(casted_op3)) {
21365 ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad);
21366 if (!op1_const)
21367 return ira->codegen->invalid_instruction;
21368 ConstExprValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad);
21369 if (!op2_const)
21370 return ira->codegen->invalid_instruction;
21371 ConstExprValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad);
21372 if (!op3_const)
21373 return ira->codegen->invalid_instruction;
21374
21375 IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
21376 ConstExprValue *out_val = &result->value;
21377
21378 if (expr_type->id == ZigTypeIdVector) {
21379 expand_undef_array(ira->codegen, op1_const);
21380 expand_undef_array(ira->codegen, op2_const);
21381 expand_undef_array(ira->codegen, op3_const);
21382 out_val->special = ConstValSpecialUndef;
21383 expand_undef_array(ira->codegen, out_val);
21384 size_t len = expr_type->data.vector.len;
21385 for (size_t i = 0; i < len; i += 1) {
21386 ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i];
21387 ConstExprValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i];
21388 ConstExprValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i];
21389 ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i];
21390 assert(float_operand_op1->type == float_type);
21391 assert(float_operand_op2->type == float_type);
21392 assert(float_operand_op3->type == float_type);
21393 assert(float_out_val->type == float_type);
21394 ir_eval_mul_add(ira, instruction, float_type,
21395 op1_const, op2_const, op3_const, float_out_val);
21396 float_out_val->type = float_type;
21397 }
21398 out_val->type = expr_type;
21399 out_val->special = ConstValSpecialStatic;
21400 } else {
21401 ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, out_val);
21402 }
21403 return result;
21404 }
21405
21406 IrInstruction *result = ir_build_mul_add(&ira->new_irb,
21407 instruction->base.scope, instruction->base.source_node,
21408 type_value, casted_op1, casted_op2, casted_op3);
21409 result->value.type = expr_type;
21410 return result;
21411}
21412
2119021413static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErr *instruction) {
2119121414 IrInstruction *value = instruction->value->child;
2119221415 if (type_is_invalid(value->value.type))
......@@ -23048,70 +23271,248 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i
2304823271 return result;
2304923272}
2305023273
23051static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
23052 ZigType *float_type = ir_resolve_type(ira, instruction->type->child);
23053 if (type_is_invalid(float_type))
23054 return ira->codegen->invalid_instruction;
23274static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type,
23275 ConstExprValue *op, ConstExprValue *out_val) {
23276 assert(ira && source_instr && float_type && out_val && op);
23277 assert(float_type->id == ZigTypeIdFloat ||
23278 float_type->id == ZigTypeIdComptimeFloat);
2305523279
23056 IrInstruction *op = instruction->op->child;
23057 if (type_is_invalid(op->value.type))
23280 BuiltinFnId fop = source_instr->op;
23281 unsigned bits;
23282
23283 if (float_type->id == ZigTypeIdComptimeFloat) {
23284 bits = 128;
23285 } else if (float_type->id == ZigTypeIdFloat)
23286 bits = float_type->data.floating.bit_count;
23287
23288 switch (bits) {
23289 case 16: {
23290 switch (fop) {
23291 case BuiltinFnIdSqrt:
23292 out_val->data.x_f16 = f16_sqrt(op->data.x_f16);
23293 break;
23294 case BuiltinFnIdSin:
23295 case BuiltinFnIdCos:
23296 case BuiltinFnIdExp:
23297 case BuiltinFnIdExp2:
23298 case BuiltinFnIdLn:
23299 case BuiltinFnIdLog10:
23300 case BuiltinFnIdLog2:
23301 case BuiltinFnIdFabs:
23302 case BuiltinFnIdFloor:
23303 case BuiltinFnIdCeil:
23304 case BuiltinFnIdTrunc:
23305 case BuiltinFnIdNearbyInt:
23306 case BuiltinFnIdRound:
23307 zig_panic("unimplemented f16 builtin");
23308 default:
23309 zig_unreachable();
23310 };
23311 break;
23312 };
23313 case 32: {
23314 switch (fop) {
23315 case BuiltinFnIdSqrt:
23316 out_val->data.x_f32 = sqrtf(op->data.x_f32);
23317 break;
23318 case BuiltinFnIdSin:
23319 out_val->data.x_f32 = sinf(op->data.x_f32);
23320 break;
23321 case BuiltinFnIdCos:
23322 out_val->data.x_f32 = cosf(op->data.x_f32);
23323 break;
23324 case BuiltinFnIdExp:
23325 out_val->data.x_f32 = expf(op->data.x_f32);
23326 break;
23327 case BuiltinFnIdExp2:
23328 out_val->data.x_f32 = exp2f(op->data.x_f32);
23329 break;
23330 case BuiltinFnIdLn:
23331 out_val->data.x_f32 = logf(op->data.x_f32);
23332 break;
23333 case BuiltinFnIdLog10:
23334 out_val->data.x_f32 = log10f(op->data.x_f32);
23335 break;
23336 case BuiltinFnIdLog2:
23337 out_val->data.x_f32 = log2f(op->data.x_f32);
23338 break;
23339 case BuiltinFnIdFabs:
23340 out_val->data.x_f32 = fabsf(op->data.x_f32);
23341 break;
23342 case BuiltinFnIdFloor:
23343 out_val->data.x_f32 = floorf(op->data.x_f32);
23344 break;
23345 case BuiltinFnIdCeil:
23346 out_val->data.x_f32 = ceilf(op->data.x_f32);
23347 break;
23348 case BuiltinFnIdTrunc:
23349 out_val->data.x_f32 = truncf(op->data.x_f32);
23350 break;
23351 case BuiltinFnIdNearbyInt:
23352 out_val->data.x_f32 = nearbyintf(op->data.x_f32);
23353 break;
23354 case BuiltinFnIdRound:
23355 out_val->data.x_f32 = roundf(op->data.x_f32);
23356 break;
23357 default:
23358 zig_unreachable();
23359 };
23360 break;
23361 };
23362 case 64: {
23363 switch (fop) {
23364 case BuiltinFnIdSqrt:
23365 out_val->data.x_f64 = sqrt(op->data.x_f64);
23366 break;
23367 case BuiltinFnIdSin:
23368 out_val->data.x_f64 = sin(op->data.x_f64);
23369 break;
23370 case BuiltinFnIdCos:
23371 out_val->data.x_f64 = cos(op->data.x_f64);
23372 break;
23373 case BuiltinFnIdExp:
23374 out_val->data.x_f64 = exp(op->data.x_f64);
23375 break;
23376 case BuiltinFnIdExp2:
23377 out_val->data.x_f64 = exp2(op->data.x_f64);
23378 break;
23379 case BuiltinFnIdLn:
23380 out_val->data.x_f64 = log(op->data.x_f64);
23381 break;
23382 case BuiltinFnIdLog10:
23383 out_val->data.x_f64 = log10(op->data.x_f64);
23384 break;
23385 case BuiltinFnIdLog2:
23386 out_val->data.x_f64 = log2(op->data.x_f64);
23387 break;
23388 case BuiltinFnIdFabs:
23389 out_val->data.x_f64 = fabs(op->data.x_f64);
23390 break;
23391 case BuiltinFnIdFloor:
23392 out_val->data.x_f64 = floor(op->data.x_f64);
23393 break;
23394 case BuiltinFnIdCeil:
23395 out_val->data.x_f64 = ceil(op->data.x_f64);
23396 break;
23397 case BuiltinFnIdTrunc:
23398 out_val->data.x_f64 = trunc(op->data.x_f64);
23399 break;
23400 case BuiltinFnIdNearbyInt:
23401 out_val->data.x_f64 = nearbyint(op->data.x_f64);
23402 break;
23403 case BuiltinFnIdRound:
23404 out_val->data.x_f64 = round(op->data.x_f64);
23405 break;
23406 default:
23407 zig_unreachable();
23408 }
23409 break;
23410 };
23411 case 128: {
23412 float128_t *out, *in;
23413 if (float_type->id == ZigTypeIdComptimeFloat) {
23414 out = &out_val->data.x_bigfloat.value;
23415 in = &op->data.x_bigfloat.value;
23416 } else {
23417 out = &out_val->data.x_f128;
23418 in = &op->data.x_f128;
23419 }
23420 switch (fop) {
23421 case BuiltinFnIdSqrt:
23422 f128M_sqrt(in, out);
23423 break;
23424 case BuiltinFnIdNearbyInt:
23425 case BuiltinFnIdSin:
23426 case BuiltinFnIdCos:
23427 case BuiltinFnIdExp:
23428 case BuiltinFnIdExp2:
23429 case BuiltinFnIdLn:
23430 case BuiltinFnIdLog10:
23431 case BuiltinFnIdLog2:
23432 case BuiltinFnIdFabs:
23433 case BuiltinFnIdFloor:
23434 case BuiltinFnIdCeil:
23435 case BuiltinFnIdTrunc:
23436 case BuiltinFnIdRound:
23437 zig_panic("unimplemented f128 builtin");
23438 default:
23439 zig_unreachable();
23440 }
23441 break;
23442 };
23443 default:
23444 zig_unreachable();
23445 }
23446}
23447
23448static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) {
23449 IrInstruction *type = instruction->type->child;
23450 if (type_is_invalid(type->value.type))
23451 return ira->codegen->invalid_instruction;
23452
23453 ZigType *expr_type = ir_resolve_type(ira, type);
23454 if (type_is_invalid(expr_type))
2305823455 return ira->codegen->invalid_instruction;
2305923456
23060 bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat;
23061 if (!ok_type) {
23062 ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
23457 // Only allow float types, and vectors of floats.
23458 ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
23459 if (float_type->id != ZigTypeIdFloat && float_type->id != ZigTypeIdComptimeFloat) {
23460 ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name)));
2306323461 return ira->codegen->invalid_instruction;
2306423462 }
2306523463
23066 IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
23067 if (type_is_invalid(casted_op->value.type))
23464 IrInstruction *op1 = instruction->op1->child;
23465 if (type_is_invalid(op1->value.type))
2306823466 return ira->codegen->invalid_instruction;
2306923467
23070 if (instr_is_comptime(casted_op)) {
23071 ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
23072 if (!val)
23468 IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type);
23469 if (type_is_invalid(casted_op1->value.type))
23470 return ira->codegen->invalid_instruction;
23471
23472 if (instr_is_comptime(casted_op1)) {
23473 // Our comptime 16-bit and 128-bit support is quite limited.
23474 if ((float_type->id == ZigTypeIdComptimeFloat ||
23475 float_type->data.floating.bit_count == 16 ||
23476 float_type->data.floating.bit_count == 128) &&
23477 instruction->op != BuiltinFnIdSqrt) {
23478 ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name)));
23479 return ira->codegen->invalid_instruction;
23480 }
23481
23482 ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad);
23483 if (!op1_const)
2307323484 return ira->codegen->invalid_instruction;
2307423485
23075 IrInstruction *result = ir_const(ira, &instruction->base, float_type);
23486 IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
2307623487 ConstExprValue *out_val = &result->value;
2307723488
23078 if (float_type->id == ZigTypeIdComptimeFloat) {
23079 bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
23080 } else if (float_type->id == ZigTypeIdFloat) {
23081 switch (float_type->data.floating.bit_count) {
23082 case 16:
23083 out_val->data.x_f16 = f16_sqrt(val->data.x_f16);
23084 break;
23085 case 32:
23086 out_val->data.x_f32 = sqrtf(val->data.x_f32);
23087 break;
23088 case 64:
23089 out_val->data.x_f64 = sqrt(val->data.x_f64);
23090 break;
23091 case 128:
23092 f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
23093 break;
23094 default:
23095 zig_unreachable();
23489 if (expr_type->id == ZigTypeIdVector) {
23490 expand_undef_array(ira->codegen, op1_const);
23491 out_val->special = ConstValSpecialUndef;
23492 expand_undef_array(ira->codegen, out_val);
23493 size_t len = expr_type->data.vector.len;
23494 for (size_t i = 0; i < len; i += 1) {
23495 ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i];
23496 ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i];
23497 assert(float_operand_op1->type == float_type);
23498 assert(float_out_val->type == float_type);
23499 ir_eval_float_op(ira, instruction, float_type,
23500 op1_const, float_out_val);
23501 float_out_val->type = float_type;
2309623502 }
23503 out_val->type = expr_type;
23504 out_val->special = ConstValSpecialStatic;
2309723505 } else {
23098 zig_unreachable();
23506 ir_eval_float_op(ira, instruction, float_type, op1_const, out_val);
2309923507 }
23100
2310123508 return result;
2310223509 }
2310323510
2310423511 ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base);
23105 if (float_type->data.floating.bit_count != 16 &&
23106 float_type->data.floating.bit_count != 32 &&
23107 float_type->data.floating.bit_count != 64) {
23108 ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
23109 return ira->codegen->invalid_instruction;
23110 }
2311123512
23112 IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
23113 instruction->base.source_node, nullptr, casted_op);
23114 result->value.type = float_type;
23513 IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope,
23514 instruction->base.source_node, nullptr, casted_op1, instruction->op);
23515 result->value.type = expr_type;
2311523516 return result;
2311623517}
2311723518
......@@ -23596,8 +23997,10 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
2359623997 return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
2359723998 case IrInstructionIdMarkErrRetTracePtr:
2359823999 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
23599 case IrInstructionIdSqrt:
23600 return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
24000 case IrInstructionIdFloatOp:
24001 return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction);
24002 case IrInstructionIdMulAdd:
24003 return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)instruction);
2360124004 case IrInstructionIdIntToErr:
2360224005 return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction);
2360324006 case IrInstructionIdErrToInt:
......@@ -23836,7 +24239,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2383624239 case IrInstructionIdCoroFree:
2383724240 case IrInstructionIdCoroPromise:
2383824241 case IrInstructionIdPromiseResultType:
23839 case IrInstructionIdSqrt:
24242 case IrInstructionIdFloatOp:
24243 case IrInstructionIdMulAdd:
2384024244 case IrInstructionIdAtomicLoad:
2384124245 case IrInstructionIdIntCast:
2384224246 case IrInstructionIdFloatCast:
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+25-5
......@@ -1427,15 +1427,32 @@ 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);
1440 fprintf(irp->f, ")");
1441}
1442
1443static void ir_print_mul_add(IrPrint *irp, IrInstructionMulAdd *instruction) {
1444 fprintf(irp->f, "@mulAdd(");
1445 if (instruction->type_value != nullptr) {
1446 ir_print_other_instruction(irp, instruction->type_value);
1447 } else {
1448 fprintf(irp->f, "null");
1449 }
1450 fprintf(irp->f, ",");
1451 ir_print_other_instruction(irp, instruction->op1);
1452 fprintf(irp->f, ",");
1453 ir_print_other_instruction(irp, instruction->op2);
1454 fprintf(irp->f, ",");
1455 ir_print_other_instruction(irp, instruction->op3);
14391456 fprintf(irp->f, ")");
14401457}
14411458
......@@ -1902,8 +1919,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
19021919 case IrInstructionIdMarkErrRetTracePtr:
19031920 ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
19041921 break;
1905 case IrInstructionIdSqrt:
1906 ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
1922 case IrInstructionIdFloatOp:
1923 ir_print_float_op(irp, (IrInstructionFloatOp *)instruction);
1924 break;
1925 case IrInstructionIdMulAdd:
1926 ir_print_mul_add(irp, (IrInstructionMulAdd *)instruction);
19071927 break;
19081928 case IrInstructionIdAtomicLoad:
19091929 ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction);
std/special/c.zig+26-13
......@@ -254,19 +254,32 @@ export fn fmod(x: f64, y: f64) f64 {
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}
269
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);}
270283fn generic_fmod(comptime T: type, x: T, y: T) T {
271284 @setRuntimeSafety(false);
272285
test/stage1/behavior.zig+2
......@@ -69,6 +69,8 @@ comptime {
6969 _ = @import("behavior/optional.zig");
7070 _ = @import("behavior/pointers.zig");
7171 _ = @import("behavior/popcount.zig");
72 _ = @import("behavior/muladd.zig");
73 _ = @import("behavior/floatop.zig");
7274 _ = @import("behavior/ptrcast.zig");
7375 _ = @import("behavior/pub_enum.zig");
7476 _ = @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//}
test/stage1/behavior/muladd.zig created+34
......@@ -0,0 +1,34 @@
1const expect = @import("std").testing.expect;
2
3test "@mulAdd" {
4 comptime testMulAdd();
5 testMulAdd();
6}
7
8fn 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