| author | |
| committer | |
| log | 2e1cef75086a0b606fd8f1f7a0cda4ab2f0b7a49 |
| tree | f7c21db843acab17d344a7dfa2bf4be21d5facb1 |
| parent | d530e7f9c7e19b2c9d9117c3120cd75855f4023b |
| parent | f4ed5d7d480db29d5b3142bacb9d0b98eee0fa2b |
11 files changed, 211 insertions(+), 13 deletions(-)
doc/langref.html.in+43| ... | @@ -8209,6 +8209,49 @@ test "vector @splat" { | ... | @@ -8209,6 +8209,49 @@ test "vector @splat" { |
| 8209 | </p> | 8209 | </p> |
| 8210 | {#see_also|Vectors|@shuffle#} | 8210 | {#see_also|Vectors|@shuffle#} |
| 8211 | {#header_close#} | 8211 | {#header_close#} |
| 8212 | |||
| 8213 | {#header_open|@reduce#} | ||
| 8214 | <pre>{#syntax#}@reduce(comptime op: builtin.ReduceOp, value: anytype) std.meta.Child(value){#endsyntax#}</pre> | ||
| 8215 | <p> | ||
| 8216 | Transforms a {#link|vector|Vectors#} into a scalar value by performing a | ||
| 8217 | sequential horizontal reduction of its elements using the specified | ||
| 8218 | specified operator {#syntax#}op{#endsyntax#}. | ||
| 8219 | </p> | ||
| 8220 | <p> | ||
| 8221 | Not every operator is available for every vector element type: | ||
| 8222 | </p> | ||
| 8223 | <ul> | ||
| 8224 | <li>{#syntax#}.And{#endsyntax#}, {#syntax#}.Or{#endsyntax#}, | ||
| 8225 | {#syntax#}.Xor{#endsyntax#} are available for | ||
| 8226 | {#syntax#}bool{#endsyntax#} vectors,</li> | ||
| 8227 | <li>{#syntax#}.Min{#endsyntax#}, {#syntax#}.Max{#endsyntax#}, | ||
| 8228 | {#syntax#}.Add{#endsyntax#}, {#syntax#}.Mul{#endsyntax#} are | ||
| 8229 | available for {#link|floating point|Floats#} vectors,</li> | ||
| 8230 | <li>Every operator is available for {#link|integer|Integers#} vectors. | ||
| 8231 | </ul> | ||
| 8232 | <p> | ||
| 8233 | Note that {#syntax#}.Add{#endsyntax#} and {#syntax#}.Mul{#endsyntax#} | ||
| 8234 | reductions on integral types are wrapping; when applied on floating point | ||
| 8235 | types the operation associativity is preserved, unless the float mode is | ||
| 8236 | set to {#syntax#}Optimized{#endsyntax#}. | ||
| 8237 | </p> | ||
| 8238 | {#code_begin|test#} | ||
| 8239 | const std = @import("std"); | ||
| 8240 | const expect = std.testing.expect; | ||
| 8241 | |||
| 8242 | test "vector @reduce" { | ||
| 8243 | const value: std.meta.Vector(4, i32) = [_]i32{ 1, -1, 1, -1 }; | ||
| 8244 | const result = value > @splat(4, @as(i32, 0)); | ||
| 8245 | // result is { true, false, true, false }; | ||
| 8246 | comptime expect(@TypeOf(result) == std.meta.Vector(4, bool)); | ||
| 8247 | const is_all_true = @reduce(.And, result); | ||
| 8248 | comptime expect(@TypeOf(is_all_true) == bool); | ||
| 8249 | expect(is_all_true == false); | ||
| 8250 | } | ||
| 8251 | {#code_end#} | ||
| 8252 | {#see_also|Vectors|@setFloatMode#} | ||
| 8253 | {#header_close#} | ||
| 8254 | |||
| 8212 | {#header_open|@src#} | 8255 | {#header_open|@src#} |
| 8213 | <pre>{#syntax#}@src() std.builtin.SourceLocation{#endsyntax#}</pre> | 8256 | <pre>{#syntax#}@src() std.builtin.SourceLocation{#endsyntax#}</pre> |
| 8214 | <p> | 8257 | <p> |
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 |
lib/std/testing.zig+21-9| ... | @@ -4,6 +4,7 @@ | ... | @@ -4,6 +4,7 @@ |
| 4 | // The MIT license requires this copyright notice to be included in all copies | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | // and substantial portions of the software. | 5 | // and substantial portions of the software. |
| 6 | const std = @import("std.zig"); | 6 | const std = @import("std.zig"); |
| 7 | const math = std.math; | ||
| 7 | const print = std.debug.print; | 8 | const print = std.debug.print; |
| 8 | 9 | ||
| 9 | pub const FailingAllocator = @import("testing/failing_allocator.zig").FailingAllocator; | 10 | pub const FailingAllocator = @import("testing/failing_allocator.zig").FailingAllocator; |
| ... | @@ -198,11 +199,16 @@ pub fn expectWithinMargin(expected: anytype, actual: @TypeOf(expected), margin: | ... | @@ -198,11 +199,16 @@ pub fn expectWithinMargin(expected: anytype, actual: @TypeOf(expected), margin: |
| 198 | } | 199 | } |
| 199 | } | 200 | } |
| 200 | 201 | ||
| 201 | test "expectWithinMargin.f32" { | 202 | test "expectWithinMargin" { |
| 202 | const x: f32 = 12.0; | 203 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { |
| 203 | const y: f32 = 12.06; | 204 | const pos_x: T = 12.0; |
| 205 | const pos_y: T = 12.06; | ||
| 206 | const neg_x: T = -12.0; | ||
| 207 | const neg_y: T = -12.06; | ||
| 204 | 208 | ||
| 205 | expectWithinMargin(x, y, 0.1); | 209 | expectWithinMargin(pos_x, pos_y, 0.1); |
| 210 | expectWithinMargin(neg_x, neg_y, 0.1); | ||
| 211 | } | ||
| 206 | } | 212 | } |
| 207 | 213 | ||
| 208 | /// This function is intended to be used only in tests. When the actual value is not | 214 | /// This function is intended to be used only in tests. When the actual value is not |
| ... | @@ -212,7 +218,8 @@ test "expectWithinMargin.f32" { | ... | @@ -212,7 +218,8 @@ test "expectWithinMargin.f32" { |
| 212 | pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon: @TypeOf(expected)) void { | 218 | pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon: @TypeOf(expected)) void { |
| 213 | std.debug.assert(epsilon >= 0.0 and epsilon <= 1.0); | 219 | std.debug.assert(epsilon >= 0.0 and epsilon <= 1.0); |
| 214 | 220 | ||
| 215 | const margin = epsilon * expected; | 221 | // Relative epsilon test. |
| 222 | const margin = math.max(math.fabs(expected), math.fabs(actual)) * epsilon; | ||
| 216 | switch (@typeInfo(@TypeOf(actual))) { | 223 | switch (@typeInfo(@TypeOf(actual))) { |
| 217 | .Float, | 224 | .Float, |
| 218 | .ComptimeFloat, | 225 | .ComptimeFloat, |
| ... | @@ -225,11 +232,16 @@ pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon | ... | @@ -225,11 +232,16 @@ pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon |
| 225 | } | 232 | } |
| 226 | } | 233 | } |
| 227 | 234 | ||
| 228 | test "expectWithinEpsilon.f32" { | 235 | test "expectWithinEpsilon" { |
| 229 | const x: f32 = 12.0; | 236 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { |
| 230 | const y: f32 = 13.2; | 237 | const pos_x: T = 12.0; |
| 238 | const pos_y: T = 13.2; | ||
| 239 | const neg_x: T = -12.0; | ||
| 240 | const neg_y: T = -13.2; | ||
| 231 | 241 | ||
| 232 | expectWithinEpsilon(x, y, 0.1); | 242 | expectWithinEpsilon(pos_x, pos_y, 0.1); |
| 243 | expectWithinEpsilon(neg_x, neg_y, 0.1); | ||
| 244 | } | ||
| 233 | } | 245 | } |
| 234 | 246 | ||
| 235 | /// This function is intended to be used only in tests. When the two slices are not | 247 | /// This function is intended to be used only in tests. When the two slices are not |
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+41-3| ... | @@ -10953,13 +10953,13 @@ static bool float_is_nan(ZigValue *op) { | ... | @@ -10953,13 +10953,13 @@ static bool float_is_nan(ZigValue *op) { |
| 10953 | } else if (op->type->id == ZigTypeIdFloat) { | 10953 | } else if (op->type->id == ZigTypeIdFloat) { |
| 10954 | switch (op->type->data.floating.bit_count) { | 10954 | switch (op->type->data.floating.bit_count) { |
| 10955 | case 16: | 10955 | case 16: |
| 10956 | return f16_isSignalingNaN(op->data.x_f16); | 10956 | return zig_f16_isNaN(op->data.x_f16); |
| 10957 | case 32: | 10957 | case 32: |
| 10958 | return op->data.x_f32 != op->data.x_f32; | 10958 | return op->data.x_f32 != op->data.x_f32; |
| 10959 | case 64: | 10959 | case 64: |
| 10960 | return op->data.x_f64 != op->data.x_f64; | 10960 | return op->data.x_f64 != op->data.x_f64; |
| 10961 | case 128: | 10961 | case 128: |
| 10962 | return f128M_isSignalingNaN(&op->data.x_f128); | 10962 | return zig_f128_isNaN(&op->data.x_f128); |
| 10963 | default: | 10963 | default: |
| 10964 | zig_unreachable(); | 10964 | zig_unreachable(); |
| 10965 | } | 10965 | } |
| ... | @@ -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/stage1/softfloat.hpp+13| ... | @@ -29,4 +29,17 @@ static inline double zig_f16_to_double(float16_t x) { | ... | @@ -29,4 +29,17 @@ static inline double zig_f16_to_double(float16_t x) { |
| 29 | return z; | 29 | return z; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | static inline bool zig_f16_isNaN(float16_t a) { | ||
| 33 | union { uint16_t ui; float16_t f; } uA; | ||
| 34 | uA.f = a; | ||
| 35 | return 0x7C00 < (uA.ui & 0x7FFF); | ||
| 36 | } | ||
| 37 | |||
| 38 | static inline bool zig_f128_isNaN(float128_t *aPtr) { | ||
| 39 | uint64_t absA64 = aPtr->v[1] & UINT64_C(0x7FFFFFFFFFFFFFFF); | ||
| 40 | return | ||
| 41 | (UINT64_C(0x7FFF000000000000) < absA64) | ||
| 42 | || ((absA64 == UINT64_C(0x7FFF000000000000)) && aPtr->v[0]); | ||
| 43 | } | ||
| 44 | |||
| 32 | #endif | 45 | #endif |
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 |