| author | |
| committer | |
| log | 50a29f7c213d4a906839dfd625b6280663348781 |
| tree | f0371b90219e295987d33f6335ad0aeb60fd2f7a |
| parent | fc105f268149b195ea4a4189da59d40e96e455b4 |
@select(
comptime T: type,
pred: std.meta.Vector(len, bool),
a: std.meta.Vector(len, T),
b: std.meta.Vector(len, T)
) std.meta.Vector(len, T)
Constructs a vector from a & b, based on the values in the predicate vector. For indices where the predicate value is true, the corresponding
element from the a vector is selected, and otherwise from b.12 files changed, 309 insertions(+), 2 deletions(-)
doc/langref.html.in+10| ... | ... | @@ -7125,6 +7125,7 @@ fn func(y: *i32) void { |
| 7125 | 7125 | an integer or an enum. |
| 7126 | 7126 | </p> |
| 7127 | 7127 | {#header_close#} |
| 7128 | ||
| 7128 | 7129 | {#header_open|@bitCast#} |
| 7129 | 7130 | <pre>{#syntax#}@bitCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre> |
| 7130 | 7131 | <p> |
| ... | ... | @@ -8177,6 +8178,15 @@ test "@wasmMemoryGrow" { |
| 8177 | 8178 | a calling function, the returned address will apply to the calling function. |
| 8178 | 8179 | </p> |
| 8179 | 8180 | {#header_close#} |
| 8181 | ||
| 8182 | {#header_open|@select#} | |
| 8183 | <pre>{#syntax#}@select(comptime T: type, pred: std.meta.Vector(len, bool), a: std.meta.Vector(len, T), b: std.meta.Vector(len, T)) std.meta.Vector(len, T){#endsyntax#}</pre> | |
| 8184 | <p> | |
| 8185 | Selects values element-wise from {#syntax#}a{#endsyntax#} or {#syntax#}b{#endsyntax#} based on {#syntax#}pred{#endsyntax#}. If {#syntax#}pred[i]{#endsyntax#} is {#syntax#}true{#endsyntax#}, the corresponding element in the result will be {#syntax#}a[i]{#endsyntax#} and otherwise {#syntax#}b[i]{#endsyntax#}. | |
| 8186 | </p> | |
| 8187 | {#see_also|SIMD|Vectors#} | |
| 8188 | {#header_close#} | |
| 8189 | ||
| 8180 | 8190 | {#header_open|@setAlignStack#} |
| 8181 | 8191 | <pre>{#syntax#}@setAlignStack(comptime alignment: u29){#endsyntax#}</pre> |
| 8182 | 8192 | <p> |
src/AstGen.zig+10| ... | ... | @@ -2090,6 +2090,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2090 | 2090 | .splat, |
| 2091 | 2091 | .reduce, |
| 2092 | 2092 | .shuffle, |
| 2093 | .select, | |
| 2093 | 2094 | .atomic_load, |
| 2094 | 2095 | .atomic_rmw, |
| 2095 | 2096 | .atomic_store, |
| ... | ... | @@ -7375,6 +7376,15 @@ fn builtinCall( |
| 7375 | 7376 | }); |
| 7376 | 7377 | return rvalue(gz, rl, result, node); |
| 7377 | 7378 | }, |
| 7379 | .select => { | |
| 7380 | const result = try gz.addPlNode(.select, node, Zir.Inst.Select{ | |
| 7381 | .elem_type = try typeExpr(gz, scope, params[0]), | |
| 7382 | .pred = try expr(gz, scope, .none, params[1]), | |
| 7383 | .a = try expr(gz, scope, .none, params[2]), | |
| 7384 | .b = try expr(gz, scope, .none, params[3]), | |
| 7385 | }); | |
| 7386 | return rvalue(gz, rl, result, node); | |
| 7387 | }, | |
| 7378 | 7388 | .async_call => { |
| 7379 | 7389 | const result = try gz.addPlNode(.builtin_async_call, node, Zir.Inst.AsyncCall{ |
| 7380 | 7390 | .frame_buffer = try expr(gz, scope, .none, params[0]), |
src/BuiltinFn.zig+8| ... | ... | @@ -69,6 +69,7 @@ pub const Tag = enum { |
| 69 | 69 | ptr_to_int, |
| 70 | 70 | rem, |
| 71 | 71 | return_address, |
| 72 | select, | |
| 72 | 73 | set_align_stack, |
| 73 | 74 | set_cold, |
| 74 | 75 | set_eval_branch_quota, |
| ... | ... | @@ -601,6 +602,13 @@ pub const list = list: { |
| 601 | 602 | .param_count = 0, |
| 602 | 603 | }, |
| 603 | 604 | }, |
| 605 | .{ | |
| 606 | "@select", | |
| 607 | .{ | |
| 608 | .tag = .select, | |
| 609 | .param_count = 4, | |
| 610 | }, | |
| 611 | }, | |
| 604 | 612 | .{ |
| 605 | 613 | "@setAlignStack", |
| 606 | 614 | .{ |
src/Sema.zig+7| ... | ... | @@ -338,6 +338,7 @@ pub fn analyzeBody( |
| 338 | 338 | .splat => try sema.zirSplat(block, inst), |
| 339 | 339 | .reduce => try sema.zirReduce(block, inst), |
| 340 | 340 | .shuffle => try sema.zirShuffle(block, inst), |
| 341 | .select => try sema.zirSelect(block, inst), | |
| 341 | 342 | .atomic_load => try sema.zirAtomicLoad(block, inst), |
| 342 | 343 | .atomic_rmw => try sema.zirAtomicRmw(block, inst), |
| 343 | 344 | .atomic_store => try sema.zirAtomicStore(block, inst), |
| ... | ... | @@ -6099,6 +6100,12 @@ fn zirShuffle(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 6099 | 6100 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirShuffle", .{}); |
| 6100 | 6101 | } |
| 6101 | 6102 | |
| 6103 | fn zirSelect(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 6104 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 6105 | const src = inst_data.src(); | |
| 6106 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirSelect", .{}); | |
| 6107 | } | |
| 6108 | ||
| 6102 | 6109 | fn zirAtomicLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6103 | 6110 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 6104 | 6111 | const src = inst_data.src(); |
src/Zir.zig+13| ... | ... | @@ -890,6 +890,9 @@ pub const Inst = struct { |
| 890 | 890 | /// Implements the `@shuffle` builtin. |
| 891 | 891 | /// Uses the `pl_node` union field with payload `Shuffle`. |
| 892 | 892 | shuffle, |
| 893 | /// Implements the `@select` builtin. | |
| 894 | /// Uses the `pl_node` union field with payload `Select`. | |
| 895 | select, | |
| 893 | 896 | /// Implements the `@atomicLoad` builtin. |
| 894 | 897 | /// Uses the `pl_node` union field with payload `Bin`. |
| 895 | 898 | atomic_load, |
| ... | ... | @@ -1181,6 +1184,7 @@ pub const Inst = struct { |
| 1181 | 1184 | .splat, |
| 1182 | 1185 | .reduce, |
| 1183 | 1186 | .shuffle, |
| 1187 | .select, | |
| 1184 | 1188 | .atomic_load, |
| 1185 | 1189 | .atomic_rmw, |
| 1186 | 1190 | .atomic_store, |
| ... | ... | @@ -1451,6 +1455,7 @@ pub const Inst = struct { |
| 1451 | 1455 | .splat = .pl_node, |
| 1452 | 1456 | .reduce = .pl_node, |
| 1453 | 1457 | .shuffle = .pl_node, |
| 1458 | .select = .pl_node, | |
| 1454 | 1459 | .atomic_load = .pl_node, |
| 1455 | 1460 | .atomic_rmw = .pl_node, |
| 1456 | 1461 | .atomic_store = .pl_node, |
| ... | ... | @@ -2725,6 +2730,13 @@ pub const Inst = struct { |
| 2725 | 2730 | mask: Ref, |
| 2726 | 2731 | }; |
| 2727 | 2732 | |
| 2733 | pub const Select = struct { | |
| 2734 | elem_type: Ref, | |
| 2735 | pred: Ref, | |
| 2736 | a: Ref, | |
| 2737 | b: Ref, | |
| 2738 | }; | |
| 2739 | ||
| 2728 | 2740 | pub const AsyncCall = struct { |
| 2729 | 2741 | frame_buffer: Ref, |
| 2730 | 2742 | result_ptr: Ref, |
| ... | ... | @@ -2935,6 +2947,7 @@ const Writer = struct { |
| 2935 | 2947 | .cmpxchg_strong, |
| 2936 | 2948 | .cmpxchg_weak, |
| 2937 | 2949 | .shuffle, |
| 2950 | .select, | |
| 2938 | 2951 | .atomic_rmw, |
| 2939 | 2952 | .atomic_store, |
| 2940 | 2953 | .mul_add, |
src/stage1/all_types.hpp+20| ... | ... | @@ -1755,6 +1755,7 @@ enum BuiltinFnId { |
| 1755 | 1755 | BuiltinFnIdIntToEnum, |
| 1756 | 1756 | BuiltinFnIdVectorType, |
| 1757 | 1757 | BuiltinFnIdShuffle, |
| 1758 | BuiltinFnIdSelect, | |
| 1758 | 1759 | BuiltinFnIdSplat, |
| 1759 | 1760 | BuiltinFnIdSetCold, |
| 1760 | 1761 | BuiltinFnIdSetRuntimeSafety, |
| ... | ... | @@ -2544,6 +2545,7 @@ enum Stage1ZirInstId : uint8_t { |
| 2544 | 2545 | Stage1ZirInstIdBoolToInt, |
| 2545 | 2546 | Stage1ZirInstIdVectorType, |
| 2546 | 2547 | Stage1ZirInstIdShuffleVector, |
| 2548 | Stage1ZirInstIdSelect, | |
| 2547 | 2549 | Stage1ZirInstIdSplat, |
| 2548 | 2550 | Stage1ZirInstIdBoolNot, |
| 2549 | 2551 | Stage1ZirInstIdMemset, |
| ... | ... | @@ -2664,6 +2666,7 @@ enum Stage1AirInstId : uint8_t { |
| 2664 | 2666 | Stage1AirInstIdReduce, |
| 2665 | 2667 | Stage1AirInstIdTruncate, |
| 2666 | 2668 | Stage1AirInstIdShuffleVector, |
| 2669 | Stage1AirInstIdSelect, | |
| 2667 | 2670 | Stage1AirInstIdSplat, |
| 2668 | 2671 | Stage1AirInstIdBoolNot, |
| 2669 | 2672 | Stage1AirInstIdMemset, |
| ... | ... | @@ -4295,6 +4298,23 @@ struct Stage1AirInstShuffleVector { |
| 4295 | 4298 | Stage1AirInst *mask; // This is in zig-format, not llvm format |
| 4296 | 4299 | }; |
| 4297 | 4300 | |
| 4301 | struct Stage1ZirInstSelect { | |
| 4302 | Stage1ZirInst base; | |
| 4303 | ||
| 4304 | Stage1ZirInst *scalar_type; | |
| 4305 | Stage1ZirInst *pred; // This is in zig-format, not llvm format | |
| 4306 | Stage1ZirInst *a; | |
| 4307 | Stage1ZirInst *b; | |
| 4308 | }; | |
| 4309 | ||
| 4310 | struct Stage1AirInstSelect { | |
| 4311 | Stage1AirInst base; | |
| 4312 | ||
| 4313 | Stage1AirInst *pred; // This is in zig-format, not llvm format | |
| 4314 | Stage1AirInst *a; | |
| 4315 | Stage1AirInst *b; | |
| 4316 | }; | |
| 4317 | ||
| 4298 | 4318 | struct Stage1ZirInstSplat { |
| 4299 | 4319 | Stage1ZirInst base; |
| 4300 | 4320 |
src/stage1/astgen.cpp+51| ... | ... | @@ -196,6 +196,8 @@ void destroy_instruction_src(Stage1ZirInst *inst) { |
| 196 | 196 | return heap::c_allocator.destroy(reinterpret_cast<Stage1ZirInstVectorType *>(inst)); |
| 197 | 197 | case Stage1ZirInstIdShuffleVector: |
| 198 | 198 | return heap::c_allocator.destroy(reinterpret_cast<Stage1ZirInstShuffleVector *>(inst)); |
| 199 | case Stage1ZirInstIdSelect: | |
| 200 | return heap::c_allocator.destroy(reinterpret_cast<Stage1ZirInstSelect *>(inst)); | |
| 199 | 201 | case Stage1ZirInstIdSplat: |
| 200 | 202 | return heap::c_allocator.destroy(reinterpret_cast<Stage1ZirInstSplat *>(inst)); |
| 201 | 203 | case Stage1ZirInstIdBoolNot: |
| ... | ... | @@ -651,6 +653,10 @@ static constexpr Stage1ZirInstId ir_inst_id(Stage1ZirInstShuffleVector *) { |
| 651 | 653 | return Stage1ZirInstIdShuffleVector; |
| 652 | 654 | } |
| 653 | 655 | |
| 656 | static constexpr Stage1ZirInstId ir_inst_id(Stage1ZirInstSelect *) { | |
| 657 | return Stage1ZirInstIdSelect; | |
| 658 | } | |
| 659 | ||
| 654 | 660 | static constexpr Stage1ZirInstId ir_inst_id(Stage1ZirInstSplat *) { |
| 655 | 661 | return Stage1ZirInstIdSplat; |
| 656 | 662 | } |
| ... | ... | @@ -2037,6 +2043,22 @@ static Stage1ZirInst *ir_build_shuffle_vector(Stage1AstGen *ag, Scope *scope, As |
| 2037 | 2043 | return &instruction->base; |
| 2038 | 2044 | } |
| 2039 | 2045 | |
| 2046 | static Stage1ZirInst *ir_build_select(Stage1AstGen *ag, Scope *scope, AstNode *source_node, | |
| 2047 | Stage1ZirInst *scalar_type, Stage1ZirInst *pred, Stage1ZirInst *a, Stage1ZirInst *b) | |
| 2048 | { | |
| 2049 | Stage1ZirInstSelect *instruction = ir_build_instruction<Stage1ZirInstSelect>(ag, scope, source_node); | |
| 2050 | instruction->scalar_type = scalar_type; | |
| 2051 | instruction->pred = pred; | |
| 2052 | instruction->a = a; | |
| 2053 | instruction->b = b; | |
| 2054 | ||
| 2055 | ir_ref_instruction(pred, ag->current_basic_block); | |
| 2056 | ir_ref_instruction(a, ag->current_basic_block); | |
| 2057 | ir_ref_instruction(b, ag->current_basic_block); | |
| 2058 | ||
| 2059 | return &instruction->base; | |
| 2060 | } | |
| 2061 | ||
| 2040 | 2062 | static Stage1ZirInst *ir_build_splat_src(Stage1AstGen *ag, Scope *scope, AstNode *source_node, |
| 2041 | 2063 | Stage1ZirInst *len, Stage1ZirInst *scalar) |
| 2042 | 2064 | { |
| ... | ... | @@ -4619,6 +4641,35 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast |
| 4619 | 4641 | arg0_value, arg1_value, arg2_value, arg3_value); |
| 4620 | 4642 | return ir_lval_wrap(ag, scope, shuffle_vector, lval, result_loc); |
| 4621 | 4643 | } |
| 4644 | case BuiltinFnIdSelect: | |
| 4645 | { | |
| 4646 | // Used for the type expr | |
| 4647 | Scope *comptime_scope = create_comptime_scope(ag->codegen, node, scope); | |
| 4648 | ||
| 4649 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | |
| 4650 | Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, comptime_scope); | |
| 4651 | if (arg0_value == ag->codegen->invalid_inst_src) | |
| 4652 | return arg0_value; | |
| 4653 | ||
| 4654 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | |
| 4655 | Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope); | |
| 4656 | if (arg0_value == ag->codegen->invalid_inst_src) | |
| 4657 | return arg1_value; | |
| 4658 | ||
| 4659 | AstNode *arg2_node = node->data.fn_call_expr.params.at(2); | |
| 4660 | Stage1ZirInst *arg2_value = astgen_node(ag, arg2_node, scope); | |
| 4661 | if (arg1_value == ag->codegen->invalid_inst_src) | |
| 4662 | return arg2_value; | |
| 4663 | ||
| 4664 | AstNode *arg3_node = node->data.fn_call_expr.params.at(3); | |
| 4665 | Stage1ZirInst *arg3_value = astgen_node(ag, arg3_node, scope); | |
| 4666 | if (arg2_value == ag->codegen->invalid_inst_src) | |
| 4667 | return arg3_value; | |
| 4668 | ||
| 4669 | Stage1ZirInst *select = ir_build_select(ag, scope, node, | |
| 4670 | arg0_value, arg1_value, arg2_value, arg3_value); | |
| 4671 | return ir_lval_wrap(ag, scope, select, lval, result_loc); | |
| 4672 | } | |
| 4622 | 4673 | case BuiltinFnIdSplat: |
| 4623 | 4674 | { |
| 4624 | 4675 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
src/stage1/codegen.cpp+10| ... | ... | @@ -5162,6 +5162,13 @@ static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, Stage1Air *executable, |
| 5162 | 5162 | llvm_mask_value, ""); |
| 5163 | 5163 | } |
| 5164 | 5164 | |
| 5165 | static LLVMValueRef ir_render_select(CodeGen *g, Stage1Air *executable, Stage1AirInstSelect *instruction) { | |
| 5166 | LLVMValueRef pred = ir_llvm_value(g, instruction->pred); | |
| 5167 | LLVMValueRef a = ir_llvm_value(g, instruction->a); | |
| 5168 | LLVMValueRef b = ir_llvm_value(g, instruction->b); | |
| 5169 | return LLVMBuildSelect(g->builder, pred, a, b, ""); | |
| 5170 | } | |
| 5171 | ||
| 5165 | 5172 | static LLVMValueRef ir_render_splat(CodeGen *g, Stage1Air *executable, Stage1AirInstSplat *instruction) { |
| 5166 | 5173 | ZigType *result_type = instruction->base.value->type; |
| 5167 | 5174 | ir_assert(result_type->id == ZigTypeIdVector, &instruction->base); |
| ... | ... | @@ -7015,6 +7022,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, Stage1Air *executable, Sta |
| 7015 | 7022 | return ir_render_spill_end(g, executable, (Stage1AirInstSpillEnd *)instruction); |
| 7016 | 7023 | case Stage1AirInstIdShuffleVector: |
| 7017 | 7024 | return ir_render_shuffle_vector(g, executable, (Stage1AirInstShuffleVector *) instruction); |
| 7025 | case Stage1AirInstIdSelect: | |
| 7026 | return ir_render_select(g, executable, (Stage1AirInstSelect *) instruction); | |
| 7018 | 7027 | case Stage1AirInstIdSplat: |
| 7019 | 7028 | return ir_render_splat(g, executable, (Stage1AirInstSplat *) instruction); |
| 7020 | 7029 | case Stage1AirInstIdVectorExtractElem: |
| ... | ... | @@ -8920,6 +8929,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 8920 | 8929 | create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); |
| 8921 | 8930 | create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2); |
| 8922 | 8931 | create_builtin_fn(g, BuiltinFnIdShuffle, "shuffle", 4); |
| 8932 | create_builtin_fn(g, BuiltinFnIdSelect, "select", 4); | |
| 8923 | 8933 | create_builtin_fn(g, BuiltinFnIdSplat, "splat", 2); |
| 8924 | 8934 | create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1); |
| 8925 | 8935 | create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1); |
src/stage1/ir.cpp+122-2| ... | ... | @@ -355,6 +355,8 @@ void destroy_instruction_gen(Stage1AirInst *inst) { |
| 355 | 355 | return heap::c_allocator.destroy(reinterpret_cast<Stage1AirInstTruncate *>(inst)); |
| 356 | 356 | case Stage1AirInstIdShuffleVector: |
| 357 | 357 | return heap::c_allocator.destroy(reinterpret_cast<Stage1AirInstShuffleVector *>(inst)); |
| 358 | case Stage1AirInstIdSelect: | |
| 359 | return heap::c_allocator.destroy(reinterpret_cast<Stage1AirInstSelect *>(inst)); | |
| 358 | 360 | case Stage1AirInstIdSplat: |
| 359 | 361 | return heap::c_allocator.destroy(reinterpret_cast<Stage1AirInstSplat *>(inst)); |
| 360 | 362 | case Stage1AirInstIdBoolNot: |
| ... | ... | @@ -901,6 +903,10 @@ static constexpr Stage1AirInstId ir_inst_id(Stage1AirInstShuffleVector *) { |
| 901 | 903 | return Stage1AirInstIdShuffleVector; |
| 902 | 904 | } |
| 903 | 905 | |
| 906 | static constexpr Stage1AirInstId ir_inst_id(Stage1AirInstSelect *) { | |
| 907 | return Stage1AirInstIdSelect; | |
| 908 | } | |
| 909 | ||
| 904 | 910 | static constexpr Stage1AirInstId ir_inst_id(Stage1AirInstSplat *) { |
| 905 | 911 | return Stage1AirInstIdSplat; |
| 906 | 912 | } |
| ... | ... | @@ -1756,6 +1762,22 @@ static Stage1AirInst *ir_build_shuffle_vector_gen(IrAnalyze *ira, Scope *scope, |
| 1756 | 1762 | return &inst->base; |
| 1757 | 1763 | } |
| 1758 | 1764 | |
| 1765 | static Stage1AirInst *ir_build_select_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, | |
| 1766 | ZigType *result_type, Stage1AirInst *pred, Stage1AirInst *a, Stage1AirInst *b) | |
| 1767 | { | |
| 1768 | Stage1AirInstSelect *inst = ir_build_inst_gen<Stage1AirInstSelect>(&ira->new_irb, scope, source_node); | |
| 1769 | inst->base.value->type = result_type; | |
| 1770 | inst->pred = pred; | |
| 1771 | inst->a = a; | |
| 1772 | inst->b = b; | |
| 1773 | ||
| 1774 | ir_ref_inst_gen(pred); | |
| 1775 | ir_ref_inst_gen(a); | |
| 1776 | ir_ref_inst_gen(b); | |
| 1777 | ||
| 1778 | return &inst->base; | |
| 1779 | } | |
| 1780 | ||
| 1759 | 1781 | static Stage1AirInst *ir_build_splat_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, ZigType *result_type, |
| 1760 | 1782 | Stage1AirInst *scalar) |
| 1761 | 1783 | { |
| ... | ... | @@ -20318,6 +20340,100 @@ static Stage1AirInst *ir_analyze_instruction_shuffle_vector(IrAnalyze *ira, Stag |
| 20318 | 20340 | return ir_analyze_shuffle_vector(ira, instruction->base.scope, instruction->base.source_node, scalar_type, a, b, mask); |
| 20319 | 20341 | } |
| 20320 | 20342 | |
| 20343 | static Stage1AirInst *ir_analyze_instruction_select(IrAnalyze *ira, Stage1ZirInstSelect *instruction) { | |
| 20344 | Error err; | |
| 20345 | ||
| 20346 | ZigType *scalar_type = ir_resolve_vector_elem_type(ira, instruction->scalar_type->child); | |
| 20347 | if (type_is_invalid(scalar_type)) | |
| 20348 | return ira->codegen->invalid_inst_gen; | |
| 20349 | ||
| 20350 | if ((err = ir_validate_vector_elem_type(ira, instruction->base.source_node, scalar_type))) | |
| 20351 | return ira->codegen->invalid_inst_gen; | |
| 20352 | ||
| 20353 | Stage1AirInst *pred = instruction->pred->child; | |
| 20354 | if (type_is_invalid(pred->value->type)) | |
| 20355 | return ira->codegen->invalid_inst_gen; | |
| 20356 | ||
| 20357 | Stage1AirInst *a = instruction->a->child; | |
| 20358 | if (type_is_invalid(a->value->type)) | |
| 20359 | return ira->codegen->invalid_inst_gen; | |
| 20360 | ||
| 20361 | Stage1AirInst *b = instruction->b->child; | |
| 20362 | if (type_is_invalid(b->value->type)) | |
| 20363 | return ira->codegen->invalid_inst_gen; | |
| 20364 | ||
| 20365 | if (pred->value->type->id != ZigTypeIdVector) { | |
| 20366 | ir_add_error(ira, pred, | |
| 20367 | buf_sprintf("expected vector type, found '%s'", | |
| 20368 | buf_ptr(&pred->value->type->name))); | |
| 20369 | return ira->codegen->invalid_inst_gen; | |
| 20370 | } | |
| 20371 | ||
| 20372 | uint32_t pred_len = pred->value->type->data.vector.len; | |
| 20373 | pred = ir_implicit_cast(ira, pred, get_vector_type(ira->codegen, pred_len, | |
| 20374 | ira->codegen->builtin_types.entry_bool)); | |
| 20375 | if (type_is_invalid(pred->value->type)) | |
| 20376 | return ira->codegen->invalid_inst_gen; | |
| 20377 | ||
| 20378 | if (a->value->type->id != ZigTypeIdVector) { | |
| 20379 | ir_add_error(ira, a, | |
| 20380 | buf_sprintf("expected vector type, found '%s'", | |
| 20381 | buf_ptr(&a->value->type->name))); | |
| 20382 | return ira->codegen->invalid_inst_gen; | |
| 20383 | } | |
| 20384 | ||
| 20385 | if (b->value->type->id != ZigTypeIdVector) { | |
| 20386 | ir_add_error(ira, b, | |
| 20387 | buf_sprintf("expected vector type, found '%s'", | |
| 20388 | buf_ptr(&b->value->type->name))); | |
| 20389 | return ira->codegen->invalid_inst_gen; | |
| 20390 | } | |
| 20391 | ||
| 20392 | ZigType *result_type = get_vector_type(ira->codegen, pred_len, scalar_type); | |
| 20393 | ||
| 20394 | a = ir_implicit_cast(ira, a, result_type); | |
| 20395 | if (type_is_invalid(a->value->type)) | |
| 20396 | return ira->codegen->invalid_inst_gen; | |
| 20397 | ||
| 20398 | b = ir_implicit_cast(ira, b, result_type); | |
| 20399 | if (type_is_invalid(a->value->type)) | |
| 20400 | return ira->codegen->invalid_inst_gen; | |
| 20401 | ||
| 20402 | if (instr_is_comptime(pred) && instr_is_comptime(a) && instr_is_comptime(b)) { | |
| 20403 | ZigValue *pred_val = ir_resolve_const(ira, pred, UndefBad); | |
| 20404 | if (pred_val == nullptr) | |
| 20405 | return ira->codegen->invalid_inst_gen; | |
| 20406 | ||
| 20407 | ZigValue *a_val = ir_resolve_const(ira, a, UndefBad); | |
| 20408 | if (a_val == nullptr) | |
| 20409 | return ira->codegen->invalid_inst_gen; | |
| 20410 | ||
| 20411 | ZigValue *b_val = ir_resolve_const(ira, b, UndefBad); | |
| 20412 | if (b_val == nullptr) | |
| 20413 | return ira->codegen->invalid_inst_gen; | |
| 20414 | ||
| 20415 | expand_undef_array(ira->codegen, a_val); | |
| 20416 | expand_undef_array(ira->codegen, b_val); | |
| 20417 | ||
| 20418 | Stage1AirInst *result = ir_const(ira, instruction->base.scope, instruction->base.source_node, result_type); | |
| 20419 | result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(pred_len); | |
| 20420 | ||
| 20421 | for (uint64_t i = 0; i < pred_len; i += 1) { | |
| 20422 | ZigValue *dst_elem_val = &result->value->data.x_array.data.s_none.elements[i]; | |
| 20423 | ZigValue *pred_elem_val = &pred_val->data.x_array.data.s_none.elements[i]; | |
| 20424 | ZigValue *a_elem_val = &a_val->data.x_array.data.s_none.elements[i]; | |
| 20425 | ZigValue *b_elem_val = &b_val->data.x_array.data.s_none.elements[i]; | |
| 20426 | ZigValue *result_elem_val = pred_elem_val->data.x_bool ? a_elem_val : b_elem_val; | |
| 20427 | copy_const_val(ira->codegen, dst_elem_val, result_elem_val); | |
| 20428 | } | |
| 20429 | ||
| 20430 | result->value->special = ConstValSpecialStatic; | |
| 20431 | return result; | |
| 20432 | } | |
| 20433 | ||
| 20434 | return ir_build_select_gen(ira, instruction->base.scope, instruction->base.source_node, result_type, pred, a, b); | |
| 20435 | } | |
| 20436 | ||
| 20321 | 20437 | static Stage1AirInst *ir_analyze_instruction_splat(IrAnalyze *ira, Stage1ZirInstSplat *instruction) { |
| 20322 | 20438 | Error err; |
| 20323 | 20439 | |
| ... | ... | @@ -24595,7 +24711,9 @@ static Stage1AirInst *ir_analyze_instruction_base(IrAnalyze *ira, Stage1ZirInst |
| 24595 | 24711 | return ir_analyze_instruction_vector_type(ira, (Stage1ZirInstVectorType *)instruction); |
| 24596 | 24712 | case Stage1ZirInstIdShuffleVector: |
| 24597 | 24713 | return ir_analyze_instruction_shuffle_vector(ira, (Stage1ZirInstShuffleVector *)instruction); |
| 24598 | case Stage1ZirInstIdSplat: | |
| 24714 | case Stage1ZirInstIdSelect: | |
| 24715 | return ir_analyze_instruction_select(ira, (Stage1ZirInstSelect *)instruction); | |
| 24716 | case Stage1ZirInstIdSplat: | |
| 24599 | 24717 | return ir_analyze_instruction_splat(ira, (Stage1ZirInstSplat *)instruction); |
| 24600 | 24718 | case Stage1ZirInstIdBoolNot: |
| 24601 | 24719 | return ir_analyze_instruction_bool_not(ira, (Stage1ZirInstBoolNot *)instruction); |
| ... | ... | @@ -24931,6 +25049,7 @@ bool ir_inst_gen_has_side_effects(Stage1AirInst *instruction) { |
| 24931 | 25049 | case Stage1AirInstIdUnionTag: |
| 24932 | 25050 | case Stage1AirInstIdTruncate: |
| 24933 | 25051 | case Stage1AirInstIdShuffleVector: |
| 25052 | case Stage1AirInstIdSelect: | |
| 24934 | 25053 | case Stage1AirInstIdSplat: |
| 24935 | 25054 | case Stage1AirInstIdBoolNot: |
| 24936 | 25055 | case Stage1AirInstIdReturnAddress: |
| ... | ... | @@ -25084,6 +25203,7 @@ bool ir_inst_src_has_side_effects(Stage1ZirInst *instruction) { |
| 25084 | 25203 | case Stage1ZirInstIdTruncate: |
| 25085 | 25204 | case Stage1ZirInstIdVectorType: |
| 25086 | 25205 | case Stage1ZirInstIdShuffleVector: |
| 25206 | case Stage1ZirInstIdSelect: | |
| 25087 | 25207 | case Stage1ZirInstIdSplat: |
| 25088 | 25208 | case Stage1ZirInstIdBoolNot: |
| 25089 | 25209 | case Stage1ZirInstIdSlice: |
| ... | ... | @@ -25751,7 +25871,7 @@ static Error ir_resolve_lazy_recurse_array(AstNode *source_node, ZigValue *val, |
| 25751 | 25871 | |
| 25752 | 25872 | static Error ir_resolve_lazy_recurse(AstNode *source_node, ZigValue *val) { |
| 25753 | 25873 | Error err; |
| 25754 | if ((err = ir_resolve_lazy_raw(source_node, val))) | |
| 25874 | if ((err = ir_resolve_lazy_raw(source_node, val))) | |
| 25755 | 25875 | return err; |
| 25756 | 25876 | assert(val->special != ConstValSpecialRuntime); |
| 25757 | 25877 | assert(val->special != ConstValSpecialLazy); |
src/stage1/ir_print.cpp+32| ... | ... | @@ -93,6 +93,8 @@ const char* ir_inst_src_type_str(Stage1ZirInstId id) { |
| 93 | 93 | return "SrcInvalid"; |
| 94 | 94 | case Stage1ZirInstIdShuffleVector: |
| 95 | 95 | return "SrcShuffle"; |
| 96 | case Stage1ZirInstIdSelect: | |
| 97 | return "SrcSelect"; | |
| 96 | 98 | case Stage1ZirInstIdSplat: |
| 97 | 99 | return "SrcSplat"; |
| 98 | 100 | case Stage1ZirInstIdDeclVar: |
| ... | ... | @@ -379,6 +381,8 @@ const char* ir_inst_gen_type_str(Stage1AirInstId id) { |
| 379 | 381 | return "GenInvalid"; |
| 380 | 382 | case Stage1AirInstIdShuffleVector: |
| 381 | 383 | return "GenShuffle"; |
| 384 | case Stage1AirInstIdSelect: | |
| 385 | return "GenSelect"; | |
| 382 | 386 | case Stage1AirInstIdSplat: |
| 383 | 387 | return "GenSplat"; |
| 384 | 388 | case Stage1AirInstIdDeclVar: |
| ... | ... | @@ -1722,6 +1726,28 @@ static void ir_print_shuffle_vector(IrPrintGen *irp, Stage1AirInstShuffleVector |
| 1722 | 1726 | fprintf(irp->f, ")"); |
| 1723 | 1727 | } |
| 1724 | 1728 | |
| 1729 | static void ir_print_select(IrPrintSrc *irp, Stage1ZirInstSelect *instruction) { | |
| 1730 | fprintf(irp->f, "@select("); | |
| 1731 | ir_print_other_inst_src(irp, instruction->scalar_type); | |
| 1732 | fprintf(irp->f, ", "); | |
| 1733 | ir_print_other_inst_src(irp, instruction->pred); | |
| 1734 | fprintf(irp->f, ", "); | |
| 1735 | ir_print_other_inst_src(irp, instruction->a); | |
| 1736 | fprintf(irp->f, ", "); | |
| 1737 | ir_print_other_inst_src(irp, instruction->b); | |
| 1738 | fprintf(irp->f, ")"); | |
| 1739 | } | |
| 1740 | ||
| 1741 | static void ir_print_select(IrPrintGen *irp, Stage1AirInstSelect *instruction) { | |
| 1742 | fprintf(irp->f, "@select("); | |
| 1743 | ir_print_other_inst_gen(irp, instruction->pred); | |
| 1744 | fprintf(irp->f, ", "); | |
| 1745 | ir_print_other_inst_gen(irp, instruction->a); | |
| 1746 | fprintf(irp->f, ", "); | |
| 1747 | ir_print_other_inst_gen(irp, instruction->b); | |
| 1748 | fprintf(irp->f, ")"); | |
| 1749 | } | |
| 1750 | ||
| 1725 | 1751 | static void ir_print_splat_src(IrPrintSrc *irp, Stage1ZirInstSplat *instruction) { |
| 1726 | 1752 | fprintf(irp->f, "@splat("); |
| 1727 | 1753 | ir_print_other_inst_src(irp, instruction->len); |
| ... | ... | @@ -2836,6 +2862,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, Stage1ZirInst *instruction, bool |
| 2836 | 2862 | case Stage1ZirInstIdShuffleVector: |
| 2837 | 2863 | ir_print_shuffle_vector(irp, (Stage1ZirInstShuffleVector *)instruction); |
| 2838 | 2864 | break; |
| 2865 | case Stage1ZirInstIdSelect: | |
| 2866 | ir_print_select(irp, (Stage1ZirInstSelect *)instruction); | |
| 2867 | break; | |
| 2839 | 2868 | case Stage1ZirInstIdSplat: |
| 2840 | 2869 | ir_print_splat_src(irp, (Stage1ZirInstSplat *)instruction); |
| 2841 | 2870 | break; |
| ... | ... | @@ -3178,6 +3207,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, Stage1AirInst *instruction, bool |
| 3178 | 3207 | case Stage1AirInstIdShuffleVector: |
| 3179 | 3208 | ir_print_shuffle_vector(irp, (Stage1AirInstShuffleVector *)instruction); |
| 3180 | 3209 | break; |
| 3210 | case Stage1AirInstIdSelect: | |
| 3211 | ir_print_select(irp, (Stage1AirInstSelect *)instruction); | |
| 3212 | break; | |
| 3181 | 3213 | case Stage1AirInstIdSplat: |
| 3182 | 3214 | ir_print_splat_gen(irp, (Stage1AirInstSplat *)instruction); |
| 3183 | 3215 | break; |
test/behavior.zig+1| ... | ... | @@ -118,6 +118,7 @@ test { |
| 118 | 118 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
| 119 | 119 | _ = @import("behavior/reflection.zig"); |
| 120 | 120 | _ = @import("behavior/shuffle.zig"); |
| 121 | _ = @import("behavior/select.zig"); | |
| 121 | 122 | _ = @import("behavior/sizeof_and_typeof.zig"); |
| 122 | 123 | _ = @import("behavior/slice.zig"); |
| 123 | 124 | _ = @import("behavior/slice_sentinel_comptime.zig"); |
test/behavior/select.zig created+25| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const mem = std.mem; | |
| 4 | const expect = std.testing.expect; | |
| 5 | const Vector = std.meta.Vector; | |
| 6 | ||
| 7 | test "@select" { | |
| 8 | const S = struct { | |
| 9 | fn doTheTest() !void { | |
| 10 | var a: Vector(4, bool) = [4]bool{ true, false, true, false }; | |
| 11 | var b: Vector(4, i32) = [4]i32{ -1, 4, 999, -31 }; | |
| 12 | var c: Vector(4, i32) = [4]i32{ -5, 1, 0, 1234 }; | |
| 13 | var abc = @select(i32, a, b, c); | |
| 14 | try expect(mem.eql(i32, &@as([4]i32, abc), &[4]i32{ -1, 1, 999, 1234 })); | |
| 15 | ||
| 16 | var x: Vector(4, bool) = [4]bool{ false, false, false, true }; | |
| 17 | var y: Vector(4, f32) = [4]f32{ 0.001, 33.4, 836, -3381.233 }; | |
| 18 | var z: Vector(4, f32) = [4]f32{ 0.0, 312.1, -145.9, 9993.55 }; | |
| 19 | var xyz = @select(f32, x, y, z); | |
| 20 | try expect(mem.eql(f32, &@as([4]f32, xyz), &[4]f32{ 0.0, 312.1, -145.9, -3381.233 })); | |
| 21 | } | |
| 22 | }; | |
| 23 | try S.doTheTest(); | |
| 24 | comptime try S.doTheTest(); | |
| 25 | } |