| author | |
| committer | |
| log | 0d6a7088dc82cfe686beb5ebfe540ba2b7935cd6 |
| tree | 8c7980fc48175487377b5890b382bfd5f0c25d7b |
| parent | 6f3d6c1f45edea883bc206b8e60dae3b6b34cbbb |
8 files changed, 132 insertions(+), 2 deletions(-)
lib/std/builtin.zig+2| ... | @@ -106,6 +106,8 @@ pub const ReduceOp = enum { | ... | @@ -106,6 +106,8 @@ pub const ReduceOp = enum { |
| 106 | Xor, | 106 | Xor, |
| 107 | Min, | 107 | Min, |
| 108 | Max, | 108 | Max, |
| 109 | Add, | ||
| 110 | Mul, | ||
| 109 | }; | 111 | }; |
| 110 | 112 | ||
| 111 | /// This data structure is used by the Zig language code generation and | 113 | /// This data structure is used by the Zig language code generation and |
src/stage1/all_types.hpp+2| ... | @@ -2447,6 +2447,8 @@ enum ReduceOp { | ... | @@ -2447,6 +2447,8 @@ enum ReduceOp { |
| 2447 | ReduceOp_xor, | 2447 | ReduceOp_xor, |
| 2448 | ReduceOp_min, | 2448 | ReduceOp_min, |
| 2449 | ReduceOp_max, | 2449 | ReduceOp_max, |
| 2450 | ReduceOp_add, | ||
| 2451 | ReduceOp_mul, | ||
| 2450 | }; | 2452 | }; |
| 2451 | 2453 | ||
| 2452 | // synchronized with the code in define_builtin_compile_vars | 2454 | // synchronized with the code in define_builtin_compile_vars |
src/stage1/codegen.cpp+20| ... | @@ -5460,6 +5460,8 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, IrExecutableGen *executable, Ir | ... | @@ -5460,6 +5460,8 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, IrExecutableGen *executable, Ir |
| 5460 | assert(value_type->id == ZigTypeIdVector); | 5460 | assert(value_type->id == ZigTypeIdVector); |
| 5461 | ZigType *scalar_type = value_type->data.vector.elem_type; | 5461 | ZigType *scalar_type = value_type->data.vector.elem_type; |
| 5462 | 5462 | ||
| 5463 | ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &instruction->base)); | ||
| 5464 | |||
| 5463 | LLVMValueRef result_val; | 5465 | LLVMValueRef result_val; |
| 5464 | switch (instruction->op) { | 5466 | switch (instruction->op) { |
| 5465 | case ReduceOp_and: | 5467 | case ReduceOp_and: |
| ... | @@ -5490,6 +5492,24 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, IrExecutableGen *executable, Ir | ... | @@ -5490,6 +5492,24 @@ static LLVMValueRef ir_render_reduce(CodeGen *g, IrExecutableGen *executable, Ir |
| 5490 | result_val = ZigLLVMBuildFPMaxReduce(g->builder, value); | 5492 | result_val = ZigLLVMBuildFPMaxReduce(g->builder, value); |
| 5491 | } else zig_unreachable(); | 5493 | } else zig_unreachable(); |
| 5492 | } break; | 5494 | } break; |
| 5495 | case ReduceOp_add: { | ||
| 5496 | if (scalar_type->id == ZigTypeIdInt) { | ||
| 5497 | result_val = ZigLLVMBuildAddReduce(g->builder, value); | ||
| 5498 | } else if (scalar_type->id == ZigTypeIdFloat) { | ||
| 5499 | LLVMValueRef neutral_value = LLVMConstReal( | ||
| 5500 | get_llvm_type(g, scalar_type), -0.0); | ||
| 5501 | result_val = ZigLLVMBuildFPAddReduce(g->builder, neutral_value, value); | ||
| 5502 | } else zig_unreachable(); | ||
| 5503 | } break; | ||
| 5504 | case ReduceOp_mul: { | ||
| 5505 | if (scalar_type->id == ZigTypeIdInt) { | ||
| 5506 | result_val = ZigLLVMBuildMulReduce(g->builder, value); | ||
| 5507 | } else if (scalar_type->id == ZigTypeIdFloat) { | ||
| 5508 | LLVMValueRef neutral_value = LLVMConstReal( | ||
| 5509 | get_llvm_type(g, scalar_type), 1.0); | ||
| 5510 | result_val = ZigLLVMBuildFPMulReduce(g->builder, neutral_value, value); | ||
| 5511 | } else zig_unreachable(); | ||
| 5512 | } break; | ||
| 5493 | default: | 5513 | default: |
| 5494 | zig_unreachable(); | 5514 | zig_unreachable(); |
| 5495 | } | 5515 | } |
src/stage1/ir.cpp+39-1| ... | @@ -27046,7 +27046,8 @@ static ErrorMsg *ir_eval_reduce(IrAnalyze *ira, IrInst *source_instr, ReduceOp o | ... | @@ -27046,7 +27046,8 @@ static ErrorMsg *ir_eval_reduce(IrAnalyze *ira, IrInst *source_instr, ReduceOp o |
| 27046 | return nullptr; | 27046 | return nullptr; |
| 27047 | } | 27047 | } |
| 27048 | 27048 | ||
| 27049 | if (op != ReduceOp_min && op != ReduceOp_max) { | 27049 | // Evaluate and/or/xor. |
| 27050 | if (op == ReduceOp_and || op == ReduceOp_or || op == ReduceOp_xor) { | ||
| 27050 | ZigValue *first_elem_val = &value->data.x_array.data.s_none.elements[0]; | 27051 | ZigValue *first_elem_val = &value->data.x_array.data.s_none.elements[0]; |
| 27051 | 27052 | ||
| 27052 | copy_const_val(ira->codegen, out_value, first_elem_val); | 27053 | copy_const_val(ira->codegen, out_value, first_elem_val); |
| ... | @@ -27071,6 +27072,43 @@ static ErrorMsg *ir_eval_reduce(IrAnalyze *ira, IrInst *source_instr, ReduceOp o | ... | @@ -27071,6 +27072,43 @@ static ErrorMsg *ir_eval_reduce(IrAnalyze *ira, IrInst *source_instr, ReduceOp o |
| 27071 | return nullptr; | 27072 | return nullptr; |
| 27072 | } | 27073 | } |
| 27073 | 27074 | ||
| 27075 | // Evaluate add/sub. | ||
| 27076 | // Perform the reduction sequentially, starting from the neutral value. | ||
| 27077 | if (op == ReduceOp_add || op == ReduceOp_mul) { | ||
| 27078 | if (scalar_type->id == ZigTypeIdInt) { | ||
| 27079 | if (op == ReduceOp_add) { | ||
| 27080 | bigint_init_unsigned(&out_value->data.x_bigint, 0); | ||
| 27081 | } else { | ||
| 27082 | bigint_init_unsigned(&out_value->data.x_bigint, 1); | ||
| 27083 | } | ||
| 27084 | } else { | ||
| 27085 | if (op == ReduceOp_add) { | ||
| 27086 | float_init_f64(out_value, -0.0); | ||
| 27087 | } else { | ||
| 27088 | float_init_f64(out_value, 1.0); | ||
| 27089 | } | ||
| 27090 | } | ||
| 27091 | |||
| 27092 | for (size_t i = 0; i < len; i++) { | ||
| 27093 | ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i]; | ||
| 27094 | |||
| 27095 | IrBinOp bin_op; | ||
| 27096 | switch (op) { | ||
| 27097 | case ReduceOp_add: bin_op = IrBinOpAdd; break; | ||
| 27098 | case ReduceOp_mul: bin_op = IrBinOpMult; break; | ||
| 27099 | default: zig_unreachable(); | ||
| 27100 | } | ||
| 27101 | |||
| 27102 | ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, | ||
| 27103 | out_value, bin_op, elem_val, out_value); | ||
| 27104 | if (msg != nullptr) | ||
| 27105 | return msg; | ||
| 27106 | } | ||
| 27107 | |||
| 27108 | return nullptr; | ||
| 27109 | } | ||
| 27110 | |||
| 27111 | // Evaluate min/max. | ||
| 27074 | ZigValue *candidate_elem_val = &value->data.x_array.data.s_none.elements[0]; | 27112 | ZigValue *candidate_elem_val = &value->data.x_array.data.s_none.elements[0]; |
| 27075 | 27113 | ||
| 27076 | ZigValue *dummy_cmp_value = ira->codegen->pass1_arena->create<ZigValue>(); | 27114 | ZigValue *dummy_cmp_value = ira->codegen->pass1_arena->create<ZigValue>(); |
src/stage1/ir_print.cpp+2| ... | @@ -1611,6 +1611,8 @@ static const char *reduce_op_str(ReduceOp op) { | ... | @@ -1611,6 +1611,8 @@ static const char *reduce_op_str(ReduceOp op) { |
| 1611 | case ReduceOp_xor: return "Xor"; | 1611 | case ReduceOp_xor: return "Xor"; |
| 1612 | case ReduceOp_min: return "Min"; | 1612 | case ReduceOp_min: return "Min"; |
| 1613 | case ReduceOp_max: return "Max"; | 1613 | case ReduceOp_max: return "Max"; |
| 1614 | case ReduceOp_add: return "Add"; | ||
| 1615 | case ReduceOp_mul: return "Mul"; | ||
| 1614 | } | 1616 | } |
| 1615 | zig_unreachable(); | 1617 | zig_unreachable(); |
| 1616 | } | 1618 | } |
src/zig_llvm.cpp+16| ... | @@ -1156,6 +1156,22 @@ LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ... | @@ -1156,6 +1156,22 @@ LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val) { |
| 1156 | return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Val))); | 1156 | return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Val))); |
| 1157 | } | 1157 | } |
| 1158 | 1158 | ||
| 1159 | LLVMValueRef ZigLLVMBuildAddReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1160 | return wrap(unwrap(B)->CreateAddReduce(unwrap(Val))); | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | LLVMValueRef ZigLLVMBuildMulReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1164 | return wrap(unwrap(B)->CreateMulReduce(unwrap(Val))); | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | LLVMValueRef ZigLLVMBuildFPAddReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Val) { | ||
| 1168 | return wrap(unwrap(B)->CreateFAddReduce(unwrap(Acc), unwrap(Val))); | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | LLVMValueRef ZigLLVMBuildFPMulReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Val) { | ||
| 1172 | return wrap(unwrap(B)->CreateFMulReduce(unwrap(Acc), unwrap(Val))); | ||
| 1173 | } | ||
| 1174 | |||
| 1159 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); | 1175 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); |
| 1160 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); | 1176 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); |
| 1161 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); | 1177 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); |
src/zig_llvm.h+4| ... | @@ -462,6 +462,10 @@ LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool i | ... | @@ -462,6 +462,10 @@ LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool i |
| 462 | LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); | 462 | LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); |
| 463 | LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val); | 463 | LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 464 | LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val); | 464 | LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val); |
| 465 | LLVMValueRef ZigLLVMBuildAddReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 466 | LLVMValueRef ZigLLVMBuildMulReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 467 | LLVMValueRef ZigLLVMBuildFPAddReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Val); | ||
| 468 | LLVMValueRef ZigLLVMBuildFPMulReduce(LLVMBuilderRef B, LLVMValueRef Acc, LLVMValueRef Val); | ||
| 465 | 469 | ||
| 466 | #define ZigLLVM_DIFlags_Zero 0U | 470 | #define ZigLLVM_DIFlags_Zero 0U |
| 467 | #define ZigLLVM_DIFlags_Private 1U | 471 | #define ZigLLVM_DIFlags_Private 1U |
test/stage1/behavior/vector.zig+47-1| ... | @@ -4,6 +4,7 @@ const mem = std.mem; | ... | @@ -4,6 +4,7 @@ const mem = std.mem; |
| 4 | const math = std.math; | 4 | const math = std.math; |
| 5 | const expect = std.testing.expect; | 5 | const expect = std.testing.expect; |
| 6 | const expectEqual = std.testing.expectEqual; | 6 | const expectEqual = std.testing.expectEqual; |
| 7 | const expectWithinEpsilon = std.testing.expectWithinEpsilon; | ||
| 7 | const Vector = std.meta.Vector; | 8 | const Vector = std.meta.Vector; |
| 8 | 9 | ||
| 9 | test "implicit cast vector to array - bool" { | 10 | test "implicit cast vector to array - bool" { |
| ... | @@ -492,7 +493,17 @@ test "vector reduce operation" { | ... | @@ -492,7 +493,17 @@ test "vector reduce operation" { |
| 492 | const TX = @typeInfo(@TypeOf(x)).Array.child; | 493 | const TX = @typeInfo(@TypeOf(x)).Array.child; |
| 493 | 494 | ||
| 494 | var r = @reduce(op, @as(Vector(N, TX), x)); | 495 | var r = @reduce(op, @as(Vector(N, TX), x)); |
| 495 | expectEqual(expected, r); | 496 | switch (@typeInfo(TX)) { |
| 497 | .Int, .Bool => expectEqual(expected, r), | ||
| 498 | .Float => { | ||
| 499 | if (math.isNan(expected) != math.isNan(r)) { | ||
| 500 | std.debug.panic("unexpected NaN value!", .{}); | ||
| 501 | } else { | ||
| 502 | expectWithinEpsilon(expected, r, 0.0001); | ||
| 503 | } | ||
| 504 | }, | ||
| 505 | else => unreachable, | ||
| 506 | } | ||
| 496 | } | 507 | } |
| 497 | fn doTheTest() void { | 508 | fn doTheTest() void { |
| 498 | doTheTestReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); | 509 | doTheTestReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); |
| ... | @@ -510,14 +521,49 @@ test "vector reduce operation" { | ... | @@ -510,14 +521,49 @@ test "vector reduce operation" { |
| 510 | doTheTestReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386)); | 521 | doTheTestReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386)); |
| 511 | doTheTestReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567)); | 522 | doTheTestReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567)); |
| 512 | 523 | ||
| 524 | doTheTestReduce(.Add, [4]i32{ -9, -99, -999, -9999 }, @as(i32, -11106)); | ||
| 525 | doTheTestReduce(.Add, [4]i64{ 9, 99, 999, 9999 }, @as(i64, 11106)); | ||
| 526 | |||
| 513 | doTheTestReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9)); | 527 | doTheTestReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9)); |
| 514 | doTheTestReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999)); | 528 | doTheTestReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999)); |
| 515 | 529 | ||
| 530 | doTheTestReduce(.Mul, [4]i32{ -9, -99, -999, 999 }, @as(i32, -889218891)); | ||
| 531 | doTheTestReduce(.Mul, [4]i64{ 9, 99, 999, 9999 }, @as(i64, 8900199891)); | ||
| 532 | |||
| 516 | doTheTestReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0)); | 533 | doTheTestReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0)); |
| 517 | doTheTestReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); | 534 | doTheTestReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); |
| 518 | 535 | ||
| 519 | doTheTestReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0)); | 536 | doTheTestReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0)); |
| 520 | doTheTestReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); | 537 | doTheTestReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); |
| 538 | |||
| 539 | doTheTestReduce(.Add, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 42.9)); | ||
| 540 | doTheTestReduce(.Add, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 42.9)); | ||
| 541 | |||
| 542 | doTheTestReduce(.Mul, [4]f32{ -1.9, 5.1, -60.3, 100.0 }, @as(f32, 58430.7)); | ||
| 543 | doTheTestReduce(.Mul, [4]f64{ -1.9, 5.1, -60.3, 100.0 }, @as(f64, 58430.7)); | ||
| 544 | |||
| 545 | // Test the reduction on vectors containing NaNs. | ||
| 546 | const f16_nan = math.nan(f16); | ||
| 547 | const f32_nan = math.nan(f32); | ||
| 548 | const f64_nan = math.nan(f64); | ||
| 549 | |||
| 550 | doTheTestReduce(.Add, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | ||
| 551 | doTheTestReduce(.Add, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | ||
| 552 | |||
| 553 | doTheTestReduce(.Add, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | ||
| 554 | doTheTestReduce(.Add, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | ||
| 555 | |||
| 556 | doTheTestReduce(.Add, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | ||
| 557 | doTheTestReduce(.Add, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | ||
| 558 | |||
| 559 | doTheTestReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | ||
| 560 | doTheTestReduce(.Mul, [4]f16{ -1.9, 5.1, f16_nan, 100.0 }, f16_nan); | ||
| 561 | |||
| 562 | doTheTestReduce(.Mul, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | ||
| 563 | doTheTestReduce(.Mul, [4]f32{ -1.9, 5.1, f32_nan, 100.0 }, f32_nan); | ||
| 564 | |||
| 565 | doTheTestReduce(.Mul, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | ||
| 566 | doTheTestReduce(.Mul, [4]f64{ -1.9, 5.1, f64_nan, 100.0 }, f64_nan); | ||
| 521 | } | 567 | } |
| 522 | }; | 568 | }; |
| 523 | 569 |