| ... | ... | @@ -45,104 +45,67 @@ pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features { |
| 45 | 45 | |
| 46 | 46 | pub const zig_call_abi_ver = 3; |
| 47 | 47 | |
| 48 | | const ControlFlow = union(enum) { |
| 49 | | const Structured = struct { |
| 50 | | /// This type indicates the way that a block is terminated. The |
| 51 | | /// state of a particular block is used to track how a jump from |
| 52 | | /// inside the block must reach the outside. |
| 53 | | const Block = union(enum) { |
| 54 | | const Incoming = struct { |
| 55 | | src_label: Id, |
| 56 | | /// Instruction that returns an u32 value of the |
| 57 | | /// `Air.Inst.Index` that control flow should jump to. |
| 58 | | next_block: Id, |
| 59 | | }; |
| 60 | | |
| 61 | | const SelectionMerge = struct { |
| 62 | | /// Incoming block from the `then` label. |
| 63 | | /// Note that hte incoming block from the `else` label is |
| 64 | | /// either given by the next element in the stack. |
| 65 | | incoming: Incoming, |
| 66 | | /// The label id of the cond_br's merge block. |
| 67 | | /// For the top-most element in the stack, this |
| 68 | | /// value is undefined. |
| 69 | | merge_block: Id, |
| 70 | | }; |
| 71 | | |
| 72 | | /// For a `selection` type block, we cannot use early exits, and we |
| 73 | | /// must generate a 'merge ladder' of OpSelection instructions. To that end, |
| 74 | | /// we keep a stack of the merges that still must be closed at the end of |
| 75 | | /// a block. |
| 76 | | /// |
| 77 | | /// This entire structure basically just resembles a tree like |
| 78 | | /// a x |
| 79 | | /// \ / |
| 80 | | /// b o merge |
| 81 | | /// \ / |
| 82 | | /// c o merge |
| 83 | | /// \ / |
| 84 | | /// o merge |
| 85 | | /// / |
| 86 | | /// o jump to next block |
| 87 | | selection: struct { |
| 88 | | /// In order to know which merges we still need to do, we need to keep |
| 89 | | /// a stack of those. |
| 90 | | merge_stack: std.ArrayList(SelectionMerge) = .empty, |
| 91 | | }, |
| 92 | | /// For a `loop` type block, we can early-exit the block by |
| 93 | | /// jumping to the loop exit node, and we don't need to generate |
| 94 | | /// an entire stack of merges. |
| 95 | | loop: struct { |
| 96 | | /// The next block to jump to can be determined from any number |
| 97 | | /// of conditions that jump to the loop exit. |
| 98 | | merges: std.ArrayList(Incoming) = .empty, |
| 99 | | /// The label id of the loop's merge block. |
| 100 | | merge_block: Id, |
| 101 | | }, |
| 102 | | |
| 103 | | fn deinit(block: *Structured.Block, gpa: Allocator) void { |
| 104 | | switch (block.*) { |
| 105 | | .selection => |*merge| merge.merge_stack.deinit(gpa), |
| 106 | | .loop => |*merge| merge.merges.deinit(gpa), |
| 107 | | } |
| 108 | | block.* = undefined; |
| 109 | | } |
| 110 | | }; |
| 111 | | /// This determines how exits from the current block must be handled. |
| 112 | | block_stack: std.ArrayList(*Structured.Block) = .empty, |
| 113 | | block_results: std.AutoHashMapUnmanaged(Air.Inst.Index, Id) = .empty, |
| 48 | const LoopSwitch = struct { cond_var: Id, continue_label: Id }; |
| 49 | |
| 50 | /// This type indicates the way that a block is terminated. The |
| 51 | /// state of a particular block is used to track how a jump from |
| 52 | /// inside the block must reach the outside. |
| 53 | const Block = union(enum) { |
| 54 | const Incoming = struct { |
| 55 | src_label: Id, |
| 56 | /// Instruction that returns an u32 value of the |
| 57 | /// `Air.Inst.Index` that control flow should jump to. |
| 58 | next_block: Id, |
| 114 | 59 | }; |
| 115 | 60 | |
| 116 | | const Unstructured = struct { |
| 117 | | const Incoming = struct { |
| 118 | | src_label: Id, |
| 119 | | break_value_id: Id, |
| 120 | | }; |
| 121 | | |
| 122 | | const Block = struct { |
| 123 | | label: ?Id = null, |
| 124 | | incoming_blocks: std.ArrayList(Incoming) = .empty, |
| 125 | | }; |
| 126 | | |
| 127 | | /// We need to keep track of result ids for block labels, as well as the 'incoming' |
| 128 | | /// blocks for a block. |
| 129 | | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *Block) = .empty, |
| 61 | const SelectionMerge = struct { |
| 62 | /// Incoming block from the `then` label. |
| 63 | /// Note that the incoming block from the `else` label is |
| 64 | /// either given by the next element in the stack. |
| 65 | incoming: Incoming, |
| 66 | /// The label id of the cond_br's merge block. |
| 67 | /// For the top-most element in the stack, this |
| 68 | /// value is undefined. |
| 69 | merge_block: Id, |
| 130 | 70 | }; |
| 131 | 71 | |
| 132 | | structured: Structured, |
| 133 | | unstructured: Unstructured, |
| 72 | /// For a `selection` type block, we cannot use early exits, and we |
| 73 | /// must generate a 'merge ladder' of OpSelection instructions. To that end, |
| 74 | /// we keep a stack of the merges that still must be closed at the end of |
| 75 | /// a block. |
| 76 | /// |
| 77 | /// This entire structure basically just resembles a tree like |
| 78 | /// a x |
| 79 | /// \ / |
| 80 | /// b o merge |
| 81 | /// \ / |
| 82 | /// c o merge |
| 83 | /// \ / |
| 84 | /// o merge |
| 85 | /// / |
| 86 | /// o jump to next block |
| 87 | selection: struct { |
| 88 | /// In order to know which merges we still need to do, we need to keep |
| 89 | /// a stack of those. |
| 90 | merge_stack: std.ArrayList(SelectionMerge) = .empty, |
| 91 | }, |
| 92 | /// For a `loop` type block, we can early-exit the block by |
| 93 | /// jumping to the loop exit node, and we don't need to generate |
| 94 | /// an entire stack of merges. |
| 95 | loop: struct { |
| 96 | /// The next block to jump to can be determined from any number |
| 97 | /// of conditions that jump to the loop exit. |
| 98 | merges: std.ArrayList(Incoming) = .empty, |
| 99 | /// The label id of the loop's merge block. |
| 100 | merge_block: Id, |
| 101 | }, |
| 134 | 102 | |
| 135 | | pub fn deinit(cg: *ControlFlow, gpa: Allocator) void { |
| 136 | | switch (cg.*) { |
| 137 | | .structured => |*cf| { |
| 138 | | cf.block_stack.deinit(gpa); |
| 139 | | cf.block_results.deinit(gpa); |
| 140 | | }, |
| 141 | | .unstructured => |*cf| { |
| 142 | | cf.blocks.deinit(gpa); |
| 143 | | }, |
| 103 | fn deinit(block: *Block, gpa: Allocator) void { |
| 104 | switch (block.*) { |
| 105 | .selection => |*merge| merge.merge_stack.deinit(gpa), |
| 106 | .loop => |*merge| merge.merges.deinit(gpa), |
| 144 | 107 | } |
| 145 | | cg.* = undefined; |
| 108 | block.* = undefined; |
| 146 | 109 | } |
| 147 | 110 | }; |
| 148 | 111 | |
| ... | ... | @@ -151,23 +114,27 @@ air: Air, |
| 151 | 114 | liveness: Air.Liveness, |
| 152 | 115 | owner_nav: InternPool.Nav.Index, |
| 153 | 116 | module: *Module, |
| 154 | | control_flow: ControlFlow, |
| 117 | block_stack: std.ArrayList(*Block) = .empty, |
| 118 | block_results: std.AutoHashMapUnmanaged(Air.Inst.Index, Id) = .empty, |
| 155 | 119 | base_line: u32, |
| 156 | 120 | block_label: Id = .none, |
| 157 | 121 | next_arg_index: u32 = 0, |
| 158 | 122 | args: std.ArrayList(Id) = .empty, |
| 159 | 123 | virtual_allocas: std.AutoHashMapUnmanaged(Id, ?Id) = .empty, |
| 160 | 124 | inst_results: std.AutoHashMapUnmanaged(Air.Inst.Index, Id) = .empty, |
| 125 | loop_switches: std.AutoHashMapUnmanaged(Air.Inst.Index, LoopSwitch) = .empty, |
| 161 | 126 | id_scratch: std.ArrayList(Id) = .empty, |
| 162 | 127 | prologue: Section = .{}, |
| 163 | 128 | body: Section = .{}, |
| 164 | 129 | |
| 165 | 130 | pub fn deinit(cg: *CodeGen) void { |
| 166 | 131 | const gpa = cg.module.gpa; |
| 167 | | cg.control_flow.deinit(gpa); |
| 132 | cg.block_stack.deinit(gpa); |
| 133 | cg.block_results.deinit(gpa); |
| 168 | 134 | cg.args.deinit(gpa); |
| 169 | 135 | cg.virtual_allocas.deinit(gpa); |
| 170 | 136 | cg.inst_results.deinit(gpa); |
| 137 | cg.loop_switches.deinit(gpa); |
| 171 | 138 | cg.id_scratch.deinit(gpa); |
| 172 | 139 | cg.prologue.deinit(gpa); |
| 173 | 140 | cg.body.deinit(gpa); |
| ... | ... | @@ -183,7 +150,6 @@ pub fn generate( |
| 183 | 150 | const zcu = pt.zcu; |
| 184 | 151 | const gpa = zcu.gpa; |
| 185 | 152 | const nav = zcu.funcInfo(func_index).owner_nav; |
| 186 | | const structured_cfg = zcu.navFileScope(nav).mod.?.structured_cfg; |
| 187 | 153 | |
| 188 | 154 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 189 | 155 | defer arena.deinit(); |
| ... | ... | @@ -200,10 +166,6 @@ pub fn generate( |
| 200 | 166 | .liveness = liveness.*.?, |
| 201 | 167 | .owner_nav = nav, |
| 202 | 168 | .module = &module, |
| 203 | | .control_flow = switch (structured_cfg) { |
| 204 | | true => .{ .structured = .{} }, |
| 205 | | false => .{ .unstructured = .{} }, |
| 206 | | }, |
| 207 | 169 | .base_line = zcu.navSrcLine(nav), |
| 208 | 170 | }; |
| 209 | 171 | defer cg.deinit(); |
| ... | ... | @@ -222,7 +184,6 @@ pub fn generateNav( |
| 222 | 184 | ) codegen.Error!Mir { |
| 223 | 185 | const zcu = pt.zcu; |
| 224 | 186 | const gpa = zcu.gpa; |
| 225 | | const structured_cfg = zcu.navFileScope(nav_index).mod.?.structured_cfg; |
| 226 | 187 | |
| 227 | 188 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 228 | 189 | defer arena.deinit(); |
| ... | ... | @@ -239,10 +200,6 @@ pub fn generateNav( |
| 239 | 200 | .liveness = undefined, |
| 240 | 201 | .owner_nav = nav_index, |
| 241 | 202 | .module = &module, |
| 242 | | .control_flow = switch (structured_cfg) { |
| 243 | | true => .{ .structured = .{} }, |
| 244 | | false => .{ .unstructured = .{} }, |
| 245 | | }, |
| 246 | 203 | .base_line = zcu.navSrcLine(nav_index), |
| 247 | 204 | }; |
| 248 | 205 | defer cg.deinit(); |
| ... | ... | @@ -433,17 +390,10 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 433 | 390 | cg.block_label = root_block_id; |
| 434 | 391 | |
| 435 | 392 | const main_body = cg.air.getMainBody(); |
| 436 | | switch (cg.control_flow) { |
| 437 | | .structured => { |
| 438 | | _ = try cg.genStructuredBody(.selection, main_body); |
| 439 | | // We always expect paths to here to end, but we still need the block |
| 440 | | // to act as a dummy merge block. |
| 441 | | try cg.body.emit(gpa, .OpUnreachable, {}); |
| 442 | | }, |
| 443 | | .unstructured => { |
| 444 | | try cg.genBody(main_body); |
| 445 | | }, |
| 446 | | } |
| 393 | _ = try cg.genStructuredBody(.selection, main_body); |
| 394 | // We always expect paths to here to end, but we still need the block |
| 395 | // to act as a dummy merge block. |
| 396 | try cg.body.emit(gpa, .OpUnreachable, {}); |
| 447 | 397 | try cg.body.emit(gpa, .OpFunctionEnd, {}); |
| 448 | 398 | // Append the actual code into the functions section. |
| 449 | 399 | try cg.module.sections.functions.append(gpa, cg.prologue); |
| ... | ... | @@ -1052,8 +1002,34 @@ fn constIntBig(cg: *CodeGen, ty: Type, val: Value) !Id { |
| 1052 | 1002 | return cg.constructComposite(result_ty_id, constituents); |
| 1053 | 1003 | } |
| 1054 | 1004 | |
| 1005 | /// Construct a composite value from its constituents. |
| 1006 | /// In logical addressing mode (Vulkan/OpenGL), OpCompositeConstruct cannot accept |
| 1007 | /// pointer operands, so for struct types we use alloc, store for each field and load instead. |
| 1055 | 1008 | pub fn constructComposite(cg: *CodeGen, result_ty_id: Id, constituents: []const Id) !Id { |
| 1056 | 1009 | const gpa = cg.module.gpa; |
| 1010 | |
| 1011 | if (cg.module.structFields(result_ty_id)) |fields| { |
| 1012 | assert(fields.len == constituents.len); |
| 1013 | const u32_ty_id = try cg.module.intType(.unsigned, 32); |
| 1014 | const var_id = try cg.alloc(result_ty_id, null); |
| 1015 | for (fields, constituents, 0..) |field_ty_id, constituent, i| { |
| 1016 | const field_ptr_ty_id = try cg.module.ptrType(field_ty_id, .function); |
| 1017 | const index_id = try cg.module.constant(u32_ty_id, .{ .uint32 = @intCast(i) }); |
| 1018 | const field_ptr = try cg.accessChainId(field_ptr_ty_id, var_id, &.{index_id}); |
| 1019 | try cg.body.emit(gpa, .OpStore, .{ |
| 1020 | .pointer = field_ptr, |
| 1021 | .object = constituent, |
| 1022 | }); |
| 1023 | } |
| 1024 | const result_id = cg.module.allocId(); |
| 1025 | try cg.body.emit(gpa, .OpLoad, .{ |
| 1026 | .id_result_type = result_ty_id, |
| 1027 | .id_result = result_id, |
| 1028 | .pointer = var_id, |
| 1029 | }); |
| 1030 | return result_id; |
| 1031 | } |
| 1032 | |
| 1057 | 1033 | const result_id = cg.module.allocId(); |
| 1058 | 1034 | try cg.body.emit(gpa, .OpCompositeConstruct, .{ |
| 1059 | 1035 | .id_result_type = result_ty_id, |
| ... | ... | @@ -3896,19 +3872,21 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { |
| 3896 | 3872 | .load => try cg.airLoad(inst), |
| 3897 | 3873 | .store, .store_safe => return cg.airStore(inst), |
| 3898 | 3874 | |
| 3899 | | .br => return cg.airBr(inst), |
| 3875 | .br => return cg.airBr(inst), |
| 3900 | 3876 | // For now just ignore this instruction. This effectively falls back on the old implementation, |
| 3901 | 3877 | // this doesn't change anything for us. |
| 3902 | | .repeat => return, |
| 3903 | | .breakpoint => return, |
| 3904 | | .cond_br => return cg.airCondBr(inst), |
| 3905 | | .loop => return cg.airLoop(inst), |
| 3906 | | .ret => return cg.airRet(inst), |
| 3907 | | .ret_safe => return cg.airRet(inst), // TODO |
| 3908 | | .ret_load => return cg.airRetLoad(inst), |
| 3909 | | .@"try" => try cg.airTry(inst), |
| 3910 | | .switch_br => return cg.airSwitchBr(inst), |
| 3911 | | .unreach, .trap => return cg.airUnreach(), |
| 3878 | .repeat => return, |
| 3879 | .breakpoint => return, |
| 3880 | .cond_br => return cg.airCondBr(inst), |
| 3881 | .loop => return cg.airLoop(inst), |
| 3882 | .ret => return cg.airRet(inst), |
| 3883 | .ret_safe => return cg.airRet(inst), // TODO |
| 3884 | .ret_load => return cg.airRetLoad(inst), |
| 3885 | .@"try" => try cg.airTry(inst), |
| 3886 | .switch_br => return cg.airSwitchBr(inst), |
| 3887 | .loop_switch_br => return cg.airLoopSwitchBr(inst), |
| 3888 | .switch_dispatch => return cg.airSwitchDispatch(inst), |
| 3889 | .unreach, .trap => return cg.airUnreach(), |
| 3912 | 3890 | |
| 3913 | 3891 | .dbg_empty_stmt => return, |
| 3914 | 3892 | .dbg_stmt => return cg.airDbgStmt(inst), |
| ... | ... | @@ -6426,7 +6404,27 @@ fn structFieldPtr( |
| 6426 | 6404 | return cg.accessChain(result_ty_id, object_ptr, &.{field_index}); |
| 6427 | 6405 | }, |
| 6428 | 6406 | .@"struct" => switch (object_ty.containerLayout(zcu)) { |
| 6429 | | .@"packed" => return cg.todo("implement field access for packed structs", .{}), |
| 6407 | .@"packed" => { |
| 6408 | const byte_offset = codegen.fieldOffset(object_ptr_ty, result_ptr_ty, field_index, zcu); |
| 6409 | if (byte_offset == 0) return object_ptr; |
| 6410 | const usize_ty_id = try cg.resolveType(.usize, .direct); |
| 6411 | const base_int = cg.module.allocId(); |
| 6412 | try cg.body.emit(cg.module.gpa, .OpConvertPtrToU, .{ |
| 6413 | .id_result_type = usize_ty_id, |
| 6414 | .id_result = base_int, |
| 6415 | .pointer = object_ptr, |
| 6416 | }); |
| 6417 | const offset_id = try cg.constInt(.usize, byte_offset); |
| 6418 | const adjusted = try cg.buildBinary(.OpIAdd, .{ .ty = .usize, .value = .{ .singleton = base_int } }, .{ .ty = .usize, .value = .{ .singleton = offset_id } }); |
| 6419 | const adjusted_id = try adjusted.materialize(cg); |
| 6420 | const result_id = cg.module.allocId(); |
| 6421 | try cg.body.emit(cg.module.gpa, .OpConvertUToPtr, .{ |
| 6422 | .id_result_type = result_ty_id, |
| 6423 | .id_result = result_id, |
| 6424 | .integer_value = adjusted_id, |
| 6425 | }); |
| 6426 | return result_id; |
| 6427 | }, |
| 6430 | 6428 | .auto, .@"extern" => { |
| 6431 | 6429 | return try cg.accessChain(result_ty_id, object_ptr, &.{field_index}); |
| 6432 | 6430 | }, |
| ... | ... | @@ -6532,9 +6530,7 @@ fn airArg(cg: *CodeGen) Id { |
| 6532 | 6530 | /// block to jump to. This function emits instructions, so it should be emitted |
| 6533 | 6531 | /// inside the merge block of the block. |
| 6534 | 6532 | /// This function should only be called with structured control flow generation. |
| 6535 | | fn structuredNextBlock(cg: *CodeGen, incoming: []const ControlFlow.Structured.Block.Incoming) !Id { |
| 6536 | | assert(cg.control_flow == .structured); |
| 6537 | | |
| 6533 | fn structuredNextBlock(cg: *CodeGen, incoming: []const Block.Incoming) !Id { |
| 6538 | 6534 | const result_id = cg.module.allocId(); |
| 6539 | 6535 | const block_id_ty_id = try cg.resolveType(.u32, .direct); |
| 6540 | 6536 | try cg.body.emitRaw(cg.module.gpa, .OpPhi, @intCast(2 + incoming.len * 2)); // result type + result + variable/parent... |
| ... | ... | @@ -6552,10 +6548,8 @@ fn structuredNextBlock(cg: *CodeGen, incoming: []const ControlFlow.Structured.Bl |
| 6552 | 6548 | /// terminating a body, there should be no instructions after it. |
| 6553 | 6549 | /// This function should only be called with structured control flow generation. |
| 6554 | 6550 | fn structuredBreak(cg: *CodeGen, target_block: Id) !void { |
| 6555 | | assert(cg.control_flow == .structured); |
| 6556 | | |
| 6557 | 6551 | const gpa = cg.module.gpa; |
| 6558 | | const sblock = cg.control_flow.structured.block_stack.getLast().?; |
| 6552 | const sblock = cg.block_stack.getLast().?; |
| 6559 | 6553 | const merge_block = switch (sblock.*) { |
| 6560 | 6554 | .selection => |*merge| blk: { |
| 6561 | 6555 | const merge_label = cg.module.allocId(); |
| ... | ... | @@ -6598,11 +6592,9 @@ fn genStructuredBody( |
| 6598 | 6592 | }, |
| 6599 | 6593 | body: []const Air.Inst.Index, |
| 6600 | 6594 | ) !Id { |
| 6601 | | assert(cg.control_flow == .structured); |
| 6602 | | |
| 6603 | 6595 | const gpa = cg.module.gpa; |
| 6604 | 6596 | |
| 6605 | | var sblock: ControlFlow.Structured.Block = switch (block_merge_type) { |
| 6597 | var sblock: Block = switch (block_merge_type) { |
| 6606 | 6598 | .loop => |merge| .{ .loop = .{ |
| 6607 | 6599 | .merge_block = merge.merge_label, |
| 6608 | 6600 | } }, |
| ... | ... | @@ -6611,8 +6603,8 @@ fn genStructuredBody( |
| 6611 | 6603 | defer sblock.deinit(gpa); |
| 6612 | 6604 | |
| 6613 | 6605 | { |
| 6614 | | try cg.control_flow.structured.block_stack.append(gpa, &sblock); |
| 6615 | | defer _ = cg.control_flow.structured.block_stack.pop(); |
| 6606 | try cg.block_stack.append(gpa, &sblock); |
| 6607 | defer _ = cg.block_stack.pop(); |
| 6616 | 6608 | |
| 6617 | 6609 | try cg.genBody(body); |
| 6618 | 6610 | } |
| ... | ... | @@ -6650,7 +6642,7 @@ fn genStructuredBody( |
| 6650 | 6642 | try cg.beginSpvBlock(merge_stack[merge_stack.len - 1].merge_block); |
| 6651 | 6643 | |
| 6652 | 6644 | // Now generate a merge ladder for the remaining merges in the stack. |
| 6653 | | var incoming: ControlFlow.Structured.Block.Incoming = .{ |
| 6645 | var incoming: Block.Incoming = .{ |
| 6654 | 6646 | .src_label = cg.block_label, |
| 6655 | 6647 | .next_block = merge_stack[merge_stack.len - 1].incoming.next_block, |
| 6656 | 6648 | }; |
| ... | ... | @@ -6699,65 +6691,19 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) |
| 6699 | 6691 | const ty = cg.typeOfIndex(inst); |
| 6700 | 6692 | const have_block_result = ty.hasRuntimeBits(zcu); |
| 6701 | 6693 | |
| 6702 | | const cf = switch (cg.control_flow) { |
| 6703 | | .structured => |*cf| cf, |
| 6704 | | .unstructured => |*cf| { |
| 6705 | | var block: ControlFlow.Unstructured.Block = .{}; |
| 6706 | | defer block.incoming_blocks.deinit(gpa); |
| 6707 | | |
| 6708 | | // 4 chosen as arbitrary initial capacity. |
| 6709 | | try block.incoming_blocks.ensureUnusedCapacity(gpa, 4); |
| 6710 | | |
| 6711 | | try cf.blocks.putNoClobber(gpa, inst, &block); |
| 6712 | | defer assert(cf.blocks.remove(inst)); |
| 6713 | | |
| 6714 | | try cg.genBody(body); |
| 6715 | | |
| 6716 | | // Only begin a new block if there were actually any breaks towards it. |
| 6717 | | if (block.label) |label| { |
| 6718 | | try cg.beginSpvBlock(label); |
| 6719 | | } |
| 6720 | | |
| 6721 | | if (!have_block_result) |
| 6722 | | return null; |
| 6723 | | |
| 6724 | | assert(block.label != null); |
| 6725 | | const result_id = cg.module.allocId(); |
| 6726 | | const result_type_id = try cg.resolveType(ty, .direct); |
| 6727 | | |
| 6728 | | try cg.body.emitRaw( |
| 6729 | | gpa, |
| 6730 | | .OpPhi, |
| 6731 | | // result type + result + variable/parent... |
| 6732 | | 2 + @as(u16, @intCast(block.incoming_blocks.items.len * 2)), |
| 6733 | | ); |
| 6734 | | cg.body.writeOperand(Id, result_type_id); |
| 6735 | | cg.body.writeOperand(Id, result_id); |
| 6736 | | |
| 6737 | | for (block.incoming_blocks.items) |incoming| { |
| 6738 | | cg.body.writeOperand( |
| 6739 | | spec.PairIdRefIdRef, |
| 6740 | | .{ incoming.break_value_id, incoming.src_label }, |
| 6741 | | ); |
| 6742 | | } |
| 6743 | | |
| 6744 | | return result_id; |
| 6745 | | }, |
| 6746 | | }; |
| 6747 | | |
| 6748 | 6694 | const maybe_block_result_var_id = if (have_block_result) blk: { |
| 6749 | 6695 | const ty_id = try cg.resolveType(ty, .indirect); |
| 6750 | 6696 | const block_result_var_id = try cg.alloc(ty_id, null); |
| 6751 | | try cf.block_results.putNoClobber(gpa, inst, block_result_var_id); |
| 6697 | try cg.block_results.putNoClobber(gpa, inst, block_result_var_id); |
| 6752 | 6698 | break :blk block_result_var_id; |
| 6753 | 6699 | } else null; |
| 6754 | | defer if (have_block_result) assert(cf.block_results.remove(inst)); |
| 6700 | defer if (have_block_result) assert(cg.block_results.remove(inst)); |
| 6755 | 6701 | |
| 6756 | 6702 | const next_block = try cg.genStructuredBody(.selection, body); |
| 6757 | 6703 | |
| 6758 | 6704 | // When encountering a block instruction, we are always at least in the function's scope, |
| 6759 | 6705 | // so there always has to be another entry. |
| 6760 | | assert(cf.block_stack.items.len > 0); |
| 6706 | assert(cg.block_stack.items.len > 0); |
| 6761 | 6707 | |
| 6762 | 6708 | // Check if the target of the branch was this current block. |
| 6763 | 6709 | const this_block = try cg.constInt(.u32, @intFromEnum(inst)); |
| ... | ... | @@ -6770,7 +6716,7 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) |
| 6770 | 6716 | .operand_2 = this_block, |
| 6771 | 6717 | }); |
| 6772 | 6718 | |
| 6773 | | const sblock = cf.block_stack.getLast().?; |
| 6719 | const sblock = cg.block_stack.getLast().?; |
| 6774 | 6720 | |
| 6775 | 6721 | if (ty.isNoReturn(zcu)) { |
| 6776 | 6722 | // If this block is noreturn, this instruction is the last of a block, |
| ... | ... | @@ -6828,41 +6774,18 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) |
| 6828 | 6774 | } |
| 6829 | 6775 | |
| 6830 | 6776 | fn airBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 6831 | | const gpa = cg.module.gpa; |
| 6832 | 6777 | const zcu = cg.module.zcu; |
| 6833 | 6778 | const br = cg.air.instructions.items(.data)[@intFromEnum(inst)].br; |
| 6834 | 6779 | const operand_ty = cg.typeOf(br.operand); |
| 6835 | 6780 | |
| 6836 | | switch (cg.control_flow) { |
| 6837 | | .structured => |*cf| { |
| 6838 | | if (operand_ty.hasRuntimeBits(zcu)) { |
| 6839 | | const operand_id = try cg.resolve(br.operand); |
| 6840 | | const block_result_var_id = cf.block_results.get(br.block_inst).?; |
| 6841 | | try cg.store(operand_ty, block_result_var_id, operand_id, .{}); |
| 6842 | | } |
| 6843 | | |
| 6844 | | const next_block = try cg.constInt(.u32, @intFromEnum(br.block_inst)); |
| 6845 | | try cg.structuredBreak(next_block); |
| 6846 | | }, |
| 6847 | | .unstructured => |cf| { |
| 6848 | | const block = cf.blocks.get(br.block_inst).?; |
| 6849 | | if (operand_ty.hasRuntimeBits(zcu)) { |
| 6850 | | const operand_id = try cg.resolve(br.operand); |
| 6851 | | // block_label should not be undefined here, lest there |
| 6852 | | // is a br or br_void in the function's body. |
| 6853 | | try block.incoming_blocks.append(gpa, .{ |
| 6854 | | .src_label = cg.block_label, |
| 6855 | | .break_value_id = operand_id, |
| 6856 | | }); |
| 6857 | | } |
| 6858 | | |
| 6859 | | if (block.label == null) { |
| 6860 | | block.label = cg.module.allocId(); |
| 6861 | | } |
| 6862 | | |
| 6863 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = block.label.? }); |
| 6864 | | }, |
| 6781 | if (operand_ty.hasRuntimeBits(zcu)) { |
| 6782 | const operand_id = try cg.resolve(br.operand); |
| 6783 | const block_result_var_id = cg.block_results.get(br.block_inst).?; |
| 6784 | try cg.store(operand_ty, block_result_var_id, operand_id, .{}); |
| 6865 | 6785 | } |
| 6786 | |
| 6787 | const next_block = try cg.constInt(.u32, @intFromEnum(br.block_inst)); |
| 6788 | try cg.structuredBreak(next_block); |
| 6866 | 6789 | } |
| 6867 | 6790 | |
| 6868 | 6791 | fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -6875,56 +6798,40 @@ fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 6875 | 6798 | const then_label = cg.module.allocId(); |
| 6876 | 6799 | const else_label = cg.module.allocId(); |
| 6877 | 6800 | |
| 6878 | | switch (cg.control_flow) { |
| 6879 | | .structured => { |
| 6880 | | const merge_label = cg.module.allocId(); |
| 6881 | | |
| 6882 | | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 6883 | | .merge_block = merge_label, |
| 6884 | | .selection_control = .{}, |
| 6885 | | }); |
| 6886 | | try cg.body.emit(gpa, .OpBranchConditional, .{ |
| 6887 | | .condition = condition_id, |
| 6888 | | .true_label = then_label, |
| 6889 | | .false_label = else_label, |
| 6890 | | }); |
| 6801 | const merge_label = cg.module.allocId(); |
| 6891 | 6802 | |
| 6892 | | try cg.beginSpvBlock(then_label); |
| 6893 | | const then_next = try cg.genStructuredBody(.selection, then_body); |
| 6894 | | const then_incoming: ControlFlow.Structured.Block.Incoming = .{ |
| 6895 | | .src_label = cg.block_label, |
| 6896 | | .next_block = then_next, |
| 6897 | | }; |
| 6803 | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 6804 | .merge_block = merge_label, |
| 6805 | .selection_control = .{}, |
| 6806 | }); |
| 6807 | try cg.body.emit(gpa, .OpBranchConditional, .{ |
| 6808 | .condition = condition_id, |
| 6809 | .true_label = then_label, |
| 6810 | .false_label = else_label, |
| 6811 | }); |
| 6898 | 6812 | |
| 6899 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label }); |
| 6813 | try cg.beginSpvBlock(then_label); |
| 6814 | const then_next = try cg.genStructuredBody(.selection, then_body); |
| 6815 | const then_incoming: Block.Incoming = .{ |
| 6816 | .src_label = cg.block_label, |
| 6817 | .next_block = then_next, |
| 6818 | }; |
| 6900 | 6819 | |
| 6901 | | try cg.beginSpvBlock(else_label); |
| 6902 | | const else_next = try cg.genStructuredBody(.selection, else_body); |
| 6903 | | const else_incoming: ControlFlow.Structured.Block.Incoming = .{ |
| 6904 | | .src_label = cg.block_label, |
| 6905 | | .next_block = else_next, |
| 6906 | | }; |
| 6820 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label }); |
| 6907 | 6821 | |
| 6908 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label }); |
| 6822 | try cg.beginSpvBlock(else_label); |
| 6823 | const else_next = try cg.genStructuredBody(.selection, else_body); |
| 6824 | const else_incoming: Block.Incoming = .{ |
| 6825 | .src_label = cg.block_label, |
| 6826 | .next_block = else_next, |
| 6827 | }; |
| 6909 | 6828 | |
| 6910 | | try cg.beginSpvBlock(merge_label); |
| 6911 | | const next_block = try cg.structuredNextBlock(&.{ then_incoming, else_incoming }); |
| 6829 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label }); |
| 6912 | 6830 | |
| 6913 | | try cg.structuredBreak(next_block); |
| 6914 | | }, |
| 6915 | | .unstructured => { |
| 6916 | | try cg.body.emit(gpa, .OpBranchConditional, .{ |
| 6917 | | .condition = condition_id, |
| 6918 | | .true_label = then_label, |
| 6919 | | .false_label = else_label, |
| 6920 | | }); |
| 6831 | try cg.beginSpvBlock(merge_label); |
| 6832 | const next_block = try cg.structuredNextBlock(&.{ then_incoming, else_incoming }); |
| 6921 | 6833 | |
| 6922 | | try cg.beginSpvBlock(then_label); |
| 6923 | | try cg.genBody(then_body); |
| 6924 | | try cg.beginSpvBlock(else_label); |
| 6925 | | try cg.genBody(else_body); |
| 6926 | | }, |
| 6927 | | } |
| 6834 | try cg.structuredBreak(next_block); |
| 6928 | 6835 | } |
| 6929 | 6836 | |
| 6930 | 6837 | fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -6933,67 +6840,85 @@ fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 6933 | 6840 | |
| 6934 | 6841 | const body_label = cg.module.allocId(); |
| 6935 | 6842 | |
| 6936 | | switch (cg.control_flow) { |
| 6937 | | .structured => { |
| 6938 | | const header_label = cg.module.allocId(); |
| 6939 | | const merge_label = cg.module.allocId(); |
| 6940 | | const continue_label = cg.module.allocId(); |
| 6941 | | |
| 6942 | | // The back-edge must point to the loop header, so generate a separate block for the |
| 6943 | | // loop header so that we don't accidentally include some instructions from there |
| 6944 | | // in the loop. |
| 6843 | const header_label = cg.module.allocId(); |
| 6844 | const merge_label = cg.module.allocId(); |
| 6845 | const continue_label = cg.module.allocId(); |
| 6945 | 6846 | |
| 6946 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = header_label }); |
| 6947 | | try cg.beginSpvBlock(header_label); |
| 6847 | // The back-edge must point to the loop header, so generate a separate block for the |
| 6848 | // loop header so that we don't accidentally include some instructions from there |
| 6849 | // in the loop. |
| 6948 | 6850 | |
| 6949 | | // Emit loop header and jump to loop body |
| 6950 | | try cg.body.emit(gpa, .OpLoopMerge, .{ |
| 6951 | | .merge_block = merge_label, |
| 6952 | | .continue_target = continue_label, |
| 6953 | | .loop_control = .{}, |
| 6954 | | }); |
| 6851 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = header_label }); |
| 6852 | try cg.beginSpvBlock(header_label); |
| 6955 | 6853 | |
| 6956 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label }); |
| 6854 | // Emit loop header and jump to loop body |
| 6855 | try cg.body.emit(gpa, .OpLoopMerge, .{ |
| 6856 | .merge_block = merge_label, |
| 6857 | .continue_target = continue_label, |
| 6858 | .loop_control = .{}, |
| 6859 | }); |
| 6957 | 6860 | |
| 6958 | | try cg.beginSpvBlock(body_label); |
| 6861 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label }); |
| 6959 | 6862 | |
| 6960 | | const next_block = try cg.genStructuredBody(.{ .loop = .{ |
| 6961 | | .merge_label = merge_label, |
| 6962 | | .continue_label = continue_label, |
| 6963 | | } }, block.body); |
| 6964 | | try cg.structuredBreak(next_block); |
| 6863 | try cg.beginSpvBlock(body_label); |
| 6965 | 6864 | |
| 6966 | | try cg.beginSpvBlock(continue_label); |
| 6865 | const next_block = try cg.genStructuredBody(.{ .loop = .{ |
| 6866 | .merge_label = merge_label, |
| 6867 | .continue_label = continue_label, |
| 6868 | } }, block.body); |
| 6869 | try cg.structuredBreak(next_block); |
| 6967 | 6870 | |
| 6968 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = header_label }); |
| 6969 | | }, |
| 6970 | | .unstructured => { |
| 6971 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label }); |
| 6972 | | try cg.beginSpvBlock(body_label); |
| 6973 | | try cg.genBody(block.body); |
| 6871 | try cg.beginSpvBlock(continue_label); |
| 6974 | 6872 | |
| 6975 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label }); |
| 6976 | | }, |
| 6977 | | } |
| 6873 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = header_label }); |
| 6978 | 6874 | } |
| 6979 | 6875 | |
| 6980 | 6876 | fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 6981 | 6877 | const zcu = cg.module.zcu; |
| 6878 | const pt = cg.pt; |
| 6982 | 6879 | const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 6983 | 6880 | const ptr_ty = cg.typeOf(ty_op.operand); |
| 6881 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 6984 | 6882 | const elem_ty = cg.typeOfIndex(inst); |
| 6985 | 6883 | const operand = try cg.resolve(ty_op.operand); |
| 6986 | 6884 | if (!ptr_ty.isVolatilePtr(zcu) and cg.liveness.isUnused(inst)) return null; |
| 6987 | 6885 | |
| 6988 | 6886 | if (cg.virtual_allocas.get(operand)) |stored| return stored.?; |
| 6989 | 6887 | |
| 6888 | if (ptr_info.packed_offset.host_size != 0 and |
| 6889 | ptr_info.flags.vector_index == .none) |
| 6890 | { |
| 6891 | const host_bits: u16 = ptr_info.packed_offset.host_size * 8; |
| 6892 | const elem_bit_size: u16 = @intCast(elem_ty.bitSize(zcu)); |
| 6893 | const host_int_ty = try pt.intType(.unsigned, host_bits); |
| 6894 | const host_val = try cg.load(host_int_ty, operand, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) }); |
| 6895 | const signedness: Signedness = if (elem_ty.isInt(zcu)) elem_ty.intInfo(zcu).signedness else .unsigned; |
| 6896 | const field_int_ty = try pt.intType(signedness, elem_bit_size); |
| 6897 | const narrowed = if (ptr_info.packed_offset.bit_offset > 0) blk: { |
| 6898 | const bit_offset_id = try cg.constInt(host_int_ty, ptr_info.packed_offset.bit_offset); |
| 6899 | const shifted = try cg.buildBinary(.OpShiftRightLogical, .{ .ty = host_int_ty, .value = .{ .singleton = host_val } }, .{ .ty = host_int_ty, .value = .{ .singleton = bit_offset_id } }); |
| 6900 | break :blk try shifted.materialize(cg); |
| 6901 | } else host_val; |
| 6902 | const result_id = blk: { |
| 6903 | if (cg.module.backingIntBits(elem_bit_size).@"0" == cg.module.backingIntBits(host_bits).@"0") |
| 6904 | break :blk try cg.bitCast(field_int_ty, host_int_ty, narrowed); |
| 6905 | const trunc = try cg.buildConvert(field_int_ty, .{ .ty = host_int_ty, .value = .{ .singleton = narrowed } }); |
| 6906 | break :blk try trunc.materialize(cg); |
| 6907 | }; |
| 6908 | if (elem_ty.ip_index == .bool_type) return try cg.convertToDirect(.bool, result_id); |
| 6909 | if (elem_ty.isInt(zcu)) return result_id; |
| 6910 | return try cg.bitCast(elem_ty, field_int_ty, result_id); |
| 6911 | } |
| 6912 | |
| 6990 | 6913 | return try cg.load(elem_ty, operand, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) }); |
| 6991 | 6914 | } |
| 6992 | 6915 | |
| 6993 | 6916 | fn airStore(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 6994 | 6917 | const zcu = cg.module.zcu; |
| 6918 | const pt = cg.pt; |
| 6995 | 6919 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 6996 | 6920 | const ptr_ty = cg.typeOf(bin_op.lhs); |
| 6921 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 6997 | 6922 | const elem_ty = ptr_ty.childType(zcu); |
| 6998 | 6923 | const ptr = try cg.resolve(bin_op.lhs); |
| 6999 | 6924 | const value = try cg.resolve(bin_op.rhs); |
| ... | ... | @@ -7003,6 +6928,48 @@ fn airStore(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 7003 | 6928 | return; |
| 7004 | 6929 | } |
| 7005 | 6930 | |
| 6931 | if (ptr_info.packed_offset.host_size != 0 and |
| 6932 | ptr_info.flags.vector_index == .none) |
| 6933 | { |
| 6934 | const host_bits: u16 = ptr_info.packed_offset.host_size * 8; |
| 6935 | const host_int_ty = try pt.intType(.unsigned, host_bits); |
| 6936 | const host_val = try cg.load(host_int_ty, ptr, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) }); |
| 6937 | const elem_bit_size: u16 = @intCast(elem_ty.bitSize(zcu)); |
| 6938 | const signedness: Signedness = if (elem_ty.isInt(zcu)) elem_ty.intInfo(zcu).signedness else .unsigned; |
| 6939 | const field_int_ty = try pt.intType(signedness, elem_bit_size); |
| 6940 | |
| 6941 | var value_as_int: Id = undefined; |
| 6942 | if (elem_ty.ip_index == .bool_type) { |
| 6943 | value_as_int = try cg.convertToIndirect(.bool, value); |
| 6944 | value_as_int = try cg.bitCast(field_int_ty, .u1, value_as_int); |
| 6945 | } else if (elem_ty.isInt(zcu)) { |
| 6946 | value_as_int = value; |
| 6947 | } else { |
| 6948 | value_as_int = try cg.bitCast(field_int_ty, elem_ty, value); |
| 6949 | } |
| 6950 | |
| 6951 | const extended = blk: { |
| 6952 | if (cg.module.backingIntBits(elem_bit_size).@"0" == cg.module.backingIntBits(host_bits).@"0") |
| 6953 | break :blk try cg.bitCast(host_int_ty, field_int_ty, value_as_int); |
| 6954 | const conv = try cg.buildConvert(host_int_ty, .{ .ty = field_int_ty, .value = .{ .singleton = value_as_int } }); |
| 6955 | break :blk try conv.materialize(cg); |
| 6956 | }; |
| 6957 | |
| 6958 | const bit_offset = ptr_info.packed_offset.bit_offset; |
| 6959 | const field_mask = (@as(u64, 1) << @as(u6, @intCast(elem_bit_size))) - 1; |
| 6960 | const host_mask = if (host_bits == 64) @as(u64, std.math.maxInt(u64)) else (@as(u64, 1) << @as(u6, @intCast(host_bits))) - 1; |
| 6961 | const clear_mask = ~(field_mask << @as(u6, @intCast(bit_offset))) & host_mask; |
| 6962 | const clear_mask_id = try cg.constInt(host_int_ty, clear_mask); |
| 6963 | const cleared = try cg.buildBinary(.OpBitwiseAnd, .{ .ty = host_int_ty, .value = .{ .singleton = host_val } }, .{ .ty = host_int_ty, .value = .{ .singleton = clear_mask_id } }); |
| 6964 | const bit_offset_id = try cg.constInt(host_int_ty, bit_offset); |
| 6965 | const shifted_val = try cg.buildBinary(.OpShiftLeftLogical, .{ .ty = host_int_ty, .value = .{ .singleton = extended } }, .{ .ty = host_int_ty, .value = .{ .singleton = bit_offset_id } }); |
| 6966 | const combined = try cg.buildBinary(.OpBitwiseOr, cleared, shifted_val); |
| 6967 | const combined_id = try combined.materialize(cg); |
| 6968 | |
| 6969 | try cg.store(host_int_ty, ptr, combined_id, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) }); |
| 6970 | return; |
| 6971 | } |
| 6972 | |
| 7006 | 6973 | try cg.store(elem_ty, ptr, value, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) }); |
| 7007 | 6974 | } |
| 7008 | 6975 | |
| ... | ... | @@ -7091,19 +7058,13 @@ fn airTry(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 7091 | 7058 | const err_block = cg.module.allocId(); |
| 7092 | 7059 | const ok_block = cg.module.allocId(); |
| 7093 | 7060 | |
| 7094 | | switch (cg.control_flow) { |
| 7095 | | .structured => { |
| 7096 | | // According to AIR documentation, this block is guaranteed |
| 7097 | | // to not break and end in a return instruction. Thus, |
| 7098 | | // for structured control flow, we can just naively use |
| 7099 | | // the ok block as the merge block here. |
| 7100 | | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 7101 | | .merge_block = ok_block, |
| 7102 | | .selection_control = .{}, |
| 7103 | | }); |
| 7104 | | }, |
| 7105 | | .unstructured => {}, |
| 7106 | | } |
| 7061 | // According to AIR documentation, this block is guaranteed |
| 7062 | // to not break and end in a return instruction. Thus, |
| 7063 | // we can just naively use the ok block as the merge block here. |
| 7064 | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 7065 | .merge_block = ok_block, |
| 7066 | .selection_control = .{}, |
| 7067 | }); |
| 7107 | 7068 | |
| 7108 | 7069 | try cg.body.emit(gpa, .OpBranchConditional, .{ |
| 7109 | 7070 | .condition = is_err_id, |
| ... | ... | @@ -7419,45 +7380,44 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 7419 | 7380 | |
| 7420 | 7381 | const num_cases = switch_br.cases_len; |
| 7421 | 7382 | |
| 7422 | | // Compute the total number of arms that we need. |
| 7423 | | // Zig switches are grouped by condition, so we need to loop through all of them |
| 7424 | | const num_conditions = blk: { |
| 7425 | | var num_conditions: u32 = 0; |
| 7383 | // compute the total number of scalar arms and find the last range case |
| 7384 | var num_conditions: u32 = 0; |
| 7385 | var last_range_case: ?u32 = null; |
| 7386 | { |
| 7426 | 7387 | var it = switch_br.iterateCases(); |
| 7427 | 7388 | while (it.next()) |case| { |
| 7428 | | if (case.ranges.len > 0) return cg.todo("switch with ranges", .{}); |
| 7429 | | num_conditions += @intCast(case.items.len); |
| 7389 | if (case.ranges.len > 0) { |
| 7390 | last_range_case = case.idx; |
| 7391 | } else { |
| 7392 | num_conditions += @intCast(case.items.len); |
| 7393 | } |
| 7430 | 7394 | } |
| 7431 | | break :blk num_conditions; |
| 7432 | | }; |
| 7395 | } |
| 7433 | 7396 | |
| 7434 | 7397 | // First, pre-allocate the labels for the cases. |
| 7435 | 7398 | const case_labels = cg.module.allocIds(num_cases); |
| 7436 | 7399 | // We always need the default case - if zig has none, we will generate unreachable there. |
| 7437 | | const default = cg.module.allocId(); |
| 7400 | const default_label = cg.module.allocId(); |
| 7401 | const switch_default = if (last_range_case != null) cg.module.allocId() else default_label; |
| 7438 | 7402 | |
| 7439 | | const merge_label = switch (cg.control_flow) { |
| 7440 | | .structured => cg.module.allocId(), |
| 7441 | | .unstructured => null, |
| 7442 | | }; |
| 7403 | const merge_label = cg.module.allocId(); |
| 7443 | 7404 | |
| 7444 | | if (cg.control_flow == .structured) { |
| 7445 | | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 7446 | | .merge_block = merge_label.?, |
| 7447 | | .selection_control = .{}, |
| 7448 | | }); |
| 7449 | | } |
| 7405 | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 7406 | .merge_block = merge_label, |
| 7407 | .selection_control = .{}, |
| 7408 | }); |
| 7450 | 7409 | |
| 7451 | 7410 | // Emit the instruction before generating the blocks. |
| 7452 | 7411 | try cg.body.emitRaw(gpa, .OpSwitch, 2 + (cond_words + 1) * num_conditions); |
| 7453 | 7412 | cg.body.writeOperand(Id, cond_indirect); |
| 7454 | | cg.body.writeOperand(Id, default); |
| 7413 | cg.body.writeOperand(Id, switch_default); |
| 7455 | 7414 | |
| 7456 | | // Emit each of the cases |
| 7415 | // Emit the non-range cases into the OpSwitch. |
| 7416 | // Cases with ranges are handled by the conditional chain below. |
| 7457 | 7417 | { |
| 7458 | 7418 | var it = switch_br.iterateCases(); |
| 7459 | 7419 | while (it.next()) |case| { |
| 7460 | | // SPIR-V needs a literal here, which' width depends on the case condition. |
| 7420 | if (case.ranges.len > 0) continue; |
| 7461 | 7421 | const label = case_labels.at(case.idx); |
| 7462 | 7422 | |
| 7463 | 7423 | for (case.items) |item| { |
| ... | ... | @@ -7480,62 +7440,394 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 7480 | 7440 | } |
| 7481 | 7441 | } |
| 7482 | 7442 | |
| 7483 | | var incoming_structured_blocks: std.ArrayList(ControlFlow.Structured.Block.Incoming) = .empty; |
| 7443 | var incoming_structured_blocks: std.ArrayList(Block.Incoming) = .empty; |
| 7484 | 7444 | defer incoming_structured_blocks.deinit(gpa); |
| 7445 | try incoming_structured_blocks.ensureUnusedCapacity(gpa, num_cases + 1); |
| 7446 | |
| 7447 | // emit the range-checking chain as nested if-else inside the switch's default branch. |
| 7448 | // each range case becomes: |
| 7449 | // - check condition, |
| 7450 | // - if true emit case body and branch to merge, |
| 7451 | // - else continue to next check or default |
| 7452 | if (last_range_case != null) { |
| 7453 | const cond_tmp: Temporary = .init(cond_ty, cond); |
| 7454 | const bool_ty_id = try cg.resolveType(.bool, .direct); |
| 7455 | |
| 7456 | try cg.beginSpvBlock(switch_default); |
| 7457 | |
| 7458 | var it_range = switch_br.iterateCases(); |
| 7459 | while (it_range.next()) |case| { |
| 7460 | if (case.ranges.len == 0) continue; |
| 7461 | |
| 7462 | var case_cond: ?Id = null; |
| 7463 | |
| 7464 | for (case.items) |item| { |
| 7465 | const item_tmp: Temporary = try cg.temporary(item); |
| 7466 | const eq = try (try cg.cmp(.eq, cond_tmp, item_tmp)).materialize(cg); |
| 7467 | case_cond = if (case_cond) |prev| blk: { |
| 7468 | const combined = cg.module.allocId(); |
| 7469 | try cg.body.emitRaw(gpa, .OpLogicalOr, 4); |
| 7470 | cg.body.writeOperand(Id, bool_ty_id); |
| 7471 | cg.body.writeOperand(Id, combined); |
| 7472 | cg.body.writeOperand(Id, prev); |
| 7473 | cg.body.writeOperand(Id, eq); |
| 7474 | break :blk combined; |
| 7475 | } else eq; |
| 7476 | } |
| 7477 | |
| 7478 | for (case.ranges) |range| { |
| 7479 | const lo_tmp: Temporary = try cg.temporary(range[0]); |
| 7480 | const hi_tmp: Temporary = try cg.temporary(range[1]); |
| 7481 | const ge = try (try cg.cmp(.gte, cond_tmp, lo_tmp)).materialize(cg); |
| 7482 | const le = try (try cg.cmp(.lte, cond_tmp, hi_tmp)).materialize(cg); |
| 7483 | const in_range = cg.module.allocId(); |
| 7484 | try cg.body.emitRaw(gpa, .OpLogicalAnd, 4); |
| 7485 | cg.body.writeOperand(Id, bool_ty_id); |
| 7486 | cg.body.writeOperand(Id, in_range); |
| 7487 | cg.body.writeOperand(Id, ge); |
| 7488 | cg.body.writeOperand(Id, le); |
| 7489 | case_cond = if (case_cond) |prev| blk: { |
| 7490 | const combined = cg.module.allocId(); |
| 7491 | try cg.body.emitRaw(gpa, .OpLogicalOr, 4); |
| 7492 | cg.body.writeOperand(Id, bool_ty_id); |
| 7493 | cg.body.writeOperand(Id, combined); |
| 7494 | cg.body.writeOperand(Id, prev); |
| 7495 | cg.body.writeOperand(Id, in_range); |
| 7496 | break :blk combined; |
| 7497 | } else in_range; |
| 7498 | } |
| 7485 | 7499 | |
| 7486 | | if (cg.control_flow == .structured) { |
| 7487 | | try incoming_structured_blocks.ensureUnusedCapacity(gpa, num_cases + 1); |
| 7500 | const case_label = case_labels.at(case.idx); |
| 7501 | const is_last = case.idx == last_range_case.?; |
| 7502 | const next_check = if (is_last) default_label else cg.module.allocId(); |
| 7503 | |
| 7504 | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 7505 | .merge_block = next_check, |
| 7506 | .selection_control = .{}, |
| 7507 | }); |
| 7508 | |
| 7509 | try cg.body.emit(gpa, .OpBranchConditional, .{ |
| 7510 | .condition = case_cond.?, |
| 7511 | .true_label = case_label, |
| 7512 | .false_label = next_check, |
| 7513 | }); |
| 7514 | |
| 7515 | if (!is_last) { |
| 7516 | try cg.beginSpvBlock(next_check); |
| 7517 | } |
| 7518 | } |
| 7488 | 7519 | } |
| 7489 | 7520 | |
| 7490 | | // Now, finally, we can start emitting each of the cases. |
| 7521 | // emit bodies |
| 7491 | 7522 | var it = switch_br.iterateCases(); |
| 7492 | 7523 | while (it.next()) |case| { |
| 7493 | 7524 | const label = case_labels.at(case.idx); |
| 7494 | 7525 | |
| 7495 | 7526 | try cg.beginSpvBlock(label); |
| 7496 | 7527 | |
| 7497 | | switch (cg.control_flow) { |
| 7498 | | .structured => { |
| 7499 | | const next_block = try cg.genStructuredBody(.selection, case.body); |
| 7500 | | incoming_structured_blocks.appendAssumeCapacity(.{ |
| 7501 | | .src_label = cg.block_label, |
| 7502 | | .next_block = next_block, |
| 7503 | | }); |
| 7528 | const next_block = try cg.genStructuredBody(.selection, case.body); |
| 7529 | incoming_structured_blocks.appendAssumeCapacity(.{ |
| 7530 | .src_label = cg.block_label, |
| 7531 | .next_block = next_block, |
| 7532 | }); |
| 7504 | 7533 | |
| 7505 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label.? }); |
| 7506 | | }, |
| 7507 | | .unstructured => { |
| 7508 | | try cg.genBody(case.body); |
| 7509 | | }, |
| 7510 | | } |
| 7534 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label }); |
| 7511 | 7535 | } |
| 7512 | 7536 | |
| 7513 | | const else_body = it.elseBody(); |
| 7514 | | try cg.beginSpvBlock(default); |
| 7537 | const else_body = blk: { |
| 7538 | var it_else = switch_br.iterateCases(); |
| 7539 | while (it_else.next()) |_| {} |
| 7540 | break :blk it_else.elseBody(); |
| 7541 | }; |
| 7542 | try cg.beginSpvBlock(default_label); |
| 7515 | 7543 | if (else_body.len != 0) { |
| 7516 | | switch (cg.control_flow) { |
| 7517 | | .structured => { |
| 7518 | | const next_block = try cg.genStructuredBody(.selection, else_body); |
| 7519 | | incoming_structured_blocks.appendAssumeCapacity(.{ |
| 7520 | | .src_label = cg.block_label, |
| 7521 | | .next_block = next_block, |
| 7522 | | }); |
| 7544 | const next_block = try cg.genStructuredBody(.selection, else_body); |
| 7545 | incoming_structured_blocks.appendAssumeCapacity(.{ |
| 7546 | .src_label = cg.block_label, |
| 7547 | .next_block = next_block, |
| 7548 | }); |
| 7523 | 7549 | |
| 7524 | | try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label.? }); |
| 7525 | | }, |
| 7526 | | .unstructured => { |
| 7527 | | try cg.genBody(else_body); |
| 7528 | | }, |
| 7529 | | } |
| 7550 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label }); |
| 7530 | 7551 | } else { |
| 7531 | 7552 | try cg.body.emit(gpa, .OpUnreachable, {}); |
| 7532 | 7553 | } |
| 7533 | 7554 | |
| 7534 | | if (cg.control_flow == .structured) { |
| 7535 | | try cg.beginSpvBlock(merge_label.?); |
| 7536 | | const next_block = try cg.structuredNextBlock(incoming_structured_blocks.items); |
| 7537 | | try cg.structuredBreak(next_block); |
| 7555 | try cg.beginSpvBlock(merge_label); |
| 7556 | const next_block = try cg.structuredNextBlock(incoming_structured_blocks.items); |
| 7557 | try cg.structuredBreak(next_block); |
| 7558 | } |
| 7559 | |
| 7560 | fn airLoopSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 7561 | const gpa = cg.module.gpa; |
| 7562 | const zcu = cg.module.zcu; |
| 7563 | const target = cg.module.zcu.getTarget(); |
| 7564 | const switch_br = cg.air.unwrapSwitch(inst); |
| 7565 | const cond_ty = cg.typeOf(switch_br.operand); |
| 7566 | const initial_cond = try cg.resolve(switch_br.operand); |
| 7567 | var initial_cond_indirect = try cg.convertToIndirect(cond_ty, initial_cond); |
| 7568 | |
| 7569 | const cond_words: u32 = switch (cond_ty.zigTypeTag(zcu)) { |
| 7570 | .bool, .error_set => 1, |
| 7571 | .int => blk: { |
| 7572 | const bits = cond_ty.intInfo(zcu).bits; |
| 7573 | const backing_bits, const big_int = cg.module.backingIntBits(bits); |
| 7574 | if (big_int) return cg.todo("implement composite int loop switch", .{}); |
| 7575 | break :blk if (backing_bits <= 32) 1 else 2; |
| 7576 | }, |
| 7577 | .@"enum" => blk: { |
| 7578 | const int_ty = cond_ty.intTagType(zcu); |
| 7579 | const int_info = int_ty.intInfo(zcu); |
| 7580 | const backing_bits, const big_int = cg.module.backingIntBits(int_info.bits); |
| 7581 | if (big_int) return cg.todo("implement composite int loop switch", .{}); |
| 7582 | break :blk if (backing_bits <= 32) 1 else 2; |
| 7583 | }, |
| 7584 | .pointer => blk: { |
| 7585 | initial_cond_indirect = try cg.intFromPtr(initial_cond_indirect); |
| 7586 | break :blk target.ptrBitWidth() / 32; |
| 7587 | }, |
| 7588 | else => return cg.todo("implement loop switch for type {s}", .{@tagName(cond_ty.zigTypeTag(zcu))}), |
| 7589 | }; |
| 7590 | |
| 7591 | const cond_ty_id = try cg.resolveType(cond_ty, .indirect); |
| 7592 | const cond_var = try cg.alloc(cond_ty_id, null); |
| 7593 | try cg.store(cond_ty, cond_var, initial_cond_indirect, .{}); |
| 7594 | |
| 7595 | const num_cases = switch_br.cases_len; |
| 7596 | |
| 7597 | var num_conditions: u32 = 0; |
| 7598 | var last_range_case: ?u32 = null; |
| 7599 | { |
| 7600 | var it = switch_br.iterateCases(); |
| 7601 | while (it.next()) |case| { |
| 7602 | if (case.ranges.len > 0) { |
| 7603 | last_range_case = case.idx; |
| 7604 | } else { |
| 7605 | num_conditions += @intCast(case.items.len); |
| 7606 | } |
| 7607 | } |
| 7538 | 7608 | } |
| 7609 | |
| 7610 | const case_labels = cg.module.allocIds(num_cases); |
| 7611 | const default_label = cg.module.allocId(); |
| 7612 | const switch_default = if (last_range_case != null) cg.module.allocId() else default_label; |
| 7613 | |
| 7614 | const header_label = cg.module.allocId(); |
| 7615 | const loop_merge = cg.module.allocId(); |
| 7616 | const continue_label = cg.module.allocId(); |
| 7617 | const switch_merge = cg.module.allocId(); |
| 7618 | const body_label = cg.module.allocId(); |
| 7619 | |
| 7620 | // switch_dispatch signals "continue the loop" by using this sentinel as the |
| 7621 | // next_block in structuredBreak. at switch_merge, a phi + comparison distinguishes |
| 7622 | // dispatch (continue) from break (exit) |
| 7623 | const dispatch_sentinel = try cg.constInt(.u32, @intFromEnum(inst)); |
| 7624 | |
| 7625 | try cg.loop_switches.putNoClobber(gpa, inst, .{ |
| 7626 | .cond_var = cond_var, |
| 7627 | .continue_label = dispatch_sentinel, |
| 7628 | }); |
| 7629 | defer assert(cg.loop_switches.remove(inst)); |
| 7630 | |
| 7631 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = header_label }); |
| 7632 | try cg.beginSpvBlock(header_label); |
| 7633 | |
| 7634 | try cg.body.emit(gpa, .OpLoopMerge, .{ |
| 7635 | .merge_block = loop_merge, |
| 7636 | .continue_target = continue_label, |
| 7637 | .loop_control = .{}, |
| 7638 | }); |
| 7639 | |
| 7640 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label }); |
| 7641 | try cg.beginSpvBlock(body_label); |
| 7642 | |
| 7643 | const cond = try cg.load(cond_ty, cond_var, .{}); |
| 7644 | const cond_indirect = try cg.convertToIndirect(cond_ty, cond); |
| 7645 | |
| 7646 | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 7647 | .merge_block = switch_merge, |
| 7648 | .selection_control = .{}, |
| 7649 | }); |
| 7650 | |
| 7651 | try cg.body.emitRaw(gpa, .OpSwitch, 2 + (cond_words + 1) * num_conditions); |
| 7652 | cg.body.writeOperand(Id, cond_indirect); |
| 7653 | cg.body.writeOperand(Id, switch_default); |
| 7654 | |
| 7655 | { |
| 7656 | var it = switch_br.iterateCases(); |
| 7657 | while (it.next()) |case| { |
| 7658 | if (case.ranges.len > 0) continue; |
| 7659 | const label = case_labels.at(case.idx); |
| 7660 | for (case.items) |item| { |
| 7661 | const value: Value = .fromInterned(item.toInterned().?); |
| 7662 | const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) { |
| 7663 | .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu), |
| 7664 | .@"enum" => value.intFromEnum(zcu).toUnsignedInt(zcu), |
| 7665 | .error_set => value.getErrorInt(zcu), |
| 7666 | .pointer => value.toUnsignedInt(zcu), |
| 7667 | else => unreachable, |
| 7668 | }; |
| 7669 | const int_lit: spec.LiteralContextDependentNumber = switch (cond_words) { |
| 7670 | 1 => .{ .uint32 = @intCast(int_val) }, |
| 7671 | 2 => .{ .uint64 = int_val }, |
| 7672 | else => unreachable, |
| 7673 | }; |
| 7674 | cg.body.writeOperand(spec.LiteralContextDependentNumber, int_lit); |
| 7675 | cg.body.writeOperand(Id, label); |
| 7676 | } |
| 7677 | } |
| 7678 | } |
| 7679 | |
| 7680 | var incoming_structured_blocks: std.ArrayList(Block.Incoming) = .empty; |
| 7681 | defer incoming_structured_blocks.deinit(gpa); |
| 7682 | try incoming_structured_blocks.ensureUnusedCapacity(gpa, num_cases + 1); |
| 7683 | |
| 7684 | if (last_range_case != null) { |
| 7685 | const cond_tmp: Temporary = .init(cond_ty, cond); |
| 7686 | const bool_ty_id = try cg.resolveType(.bool, .direct); |
| 7687 | |
| 7688 | try cg.beginSpvBlock(switch_default); |
| 7689 | |
| 7690 | var it_range = switch_br.iterateCases(); |
| 7691 | while (it_range.next()) |case| { |
| 7692 | if (case.ranges.len == 0) continue; |
| 7693 | |
| 7694 | var case_cond: ?Id = null; |
| 7695 | |
| 7696 | for (case.items) |item| { |
| 7697 | const item_tmp: Temporary = try cg.temporary(item); |
| 7698 | const eq = try (try cg.cmp(.eq, cond_tmp, item_tmp)).materialize(cg); |
| 7699 | case_cond = if (case_cond) |prev| blk: { |
| 7700 | const combined = cg.module.allocId(); |
| 7701 | try cg.body.emitRaw(gpa, .OpLogicalOr, 4); |
| 7702 | cg.body.writeOperand(Id, bool_ty_id); |
| 7703 | cg.body.writeOperand(Id, combined); |
| 7704 | cg.body.writeOperand(Id, prev); |
| 7705 | cg.body.writeOperand(Id, eq); |
| 7706 | break :blk combined; |
| 7707 | } else eq; |
| 7708 | } |
| 7709 | |
| 7710 | for (case.ranges) |range| { |
| 7711 | const lo_tmp: Temporary = try cg.temporary(range[0]); |
| 7712 | const hi_tmp: Temporary = try cg.temporary(range[1]); |
| 7713 | const ge = try (try cg.cmp(.gte, cond_tmp, lo_tmp)).materialize(cg); |
| 7714 | const le = try (try cg.cmp(.lte, cond_tmp, hi_tmp)).materialize(cg); |
| 7715 | const in_range = cg.module.allocId(); |
| 7716 | try cg.body.emitRaw(gpa, .OpLogicalAnd, 4); |
| 7717 | cg.body.writeOperand(Id, bool_ty_id); |
| 7718 | cg.body.writeOperand(Id, in_range); |
| 7719 | cg.body.writeOperand(Id, ge); |
| 7720 | cg.body.writeOperand(Id, le); |
| 7721 | case_cond = if (case_cond) |prev| blk: { |
| 7722 | const combined = cg.module.allocId(); |
| 7723 | try cg.body.emitRaw(gpa, .OpLogicalOr, 4); |
| 7724 | cg.body.writeOperand(Id, bool_ty_id); |
| 7725 | cg.body.writeOperand(Id, combined); |
| 7726 | cg.body.writeOperand(Id, prev); |
| 7727 | cg.body.writeOperand(Id, in_range); |
| 7728 | break :blk combined; |
| 7729 | } else in_range; |
| 7730 | } |
| 7731 | |
| 7732 | const case_label = case_labels.at(case.idx); |
| 7733 | const is_last = case.idx == last_range_case.?; |
| 7734 | const next_check = if (is_last) default_label else cg.module.allocId(); |
| 7735 | |
| 7736 | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 7737 | .merge_block = next_check, |
| 7738 | .selection_control = .{}, |
| 7739 | }); |
| 7740 | |
| 7741 | try cg.body.emit(gpa, .OpBranchConditional, .{ |
| 7742 | .condition = case_cond.?, |
| 7743 | .true_label = case_label, |
| 7744 | .false_label = next_check, |
| 7745 | }); |
| 7746 | |
| 7747 | if (!is_last) { |
| 7748 | try cg.beginSpvBlock(next_check); |
| 7749 | } |
| 7750 | } |
| 7751 | } |
| 7752 | |
| 7753 | { |
| 7754 | var it = switch_br.iterateCases(); |
| 7755 | while (it.next()) |case| { |
| 7756 | const label = case_labels.at(case.idx); |
| 7757 | try cg.beginSpvBlock(label); |
| 7758 | |
| 7759 | const next_block = try cg.genStructuredBody(.selection, case.body); |
| 7760 | incoming_structured_blocks.appendAssumeCapacity(.{ |
| 7761 | .src_label = cg.block_label, |
| 7762 | .next_block = next_block, |
| 7763 | }); |
| 7764 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = switch_merge }); |
| 7765 | } |
| 7766 | } |
| 7767 | |
| 7768 | const else_body = blk: { |
| 7769 | var it_else = switch_br.iterateCases(); |
| 7770 | while (it_else.next()) |_| {} |
| 7771 | break :blk it_else.elseBody(); |
| 7772 | }; |
| 7773 | try cg.beginSpvBlock(default_label); |
| 7774 | if (else_body.len != 0) { |
| 7775 | const next_block = try cg.genStructuredBody(.selection, else_body); |
| 7776 | incoming_structured_blocks.appendAssumeCapacity(.{ |
| 7777 | .src_label = cg.block_label, |
| 7778 | .next_block = next_block, |
| 7779 | }); |
| 7780 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = switch_merge }); |
| 7781 | } else { |
| 7782 | try cg.body.emit(gpa, .OpUnreachable, {}); |
| 7783 | } |
| 7784 | |
| 7785 | try cg.beginSpvBlock(switch_merge); |
| 7786 | const next_block = try cg.structuredNextBlock(incoming_structured_blocks.items); |
| 7787 | |
| 7788 | const is_dispatch = cg.module.allocId(); |
| 7789 | const bool_ty_id = try cg.resolveType(.bool, .direct); |
| 7790 | try cg.body.emit(gpa, .OpIEqual, .{ |
| 7791 | .id_result_type = bool_ty_id, |
| 7792 | .id_result = is_dispatch, |
| 7793 | .operand_1 = next_block, |
| 7794 | .operand_2 = dispatch_sentinel, |
| 7795 | }); |
| 7796 | |
| 7797 | const dispatch_check_merge = cg.module.allocId(); |
| 7798 | try cg.body.emit(gpa, .OpSelectionMerge, .{ |
| 7799 | .merge_block = dispatch_check_merge, |
| 7800 | .selection_control = .{}, |
| 7801 | }); |
| 7802 | const exit_block = cg.module.allocId(); |
| 7803 | try cg.body.emit(gpa, .OpBranchConditional, .{ |
| 7804 | .condition = is_dispatch, |
| 7805 | .true_label = dispatch_check_merge, |
| 7806 | .false_label = exit_block, |
| 7807 | }); |
| 7808 | |
| 7809 | try cg.beginSpvBlock(exit_block); |
| 7810 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = loop_merge }); |
| 7811 | |
| 7812 | try cg.beginSpvBlock(dispatch_check_merge); |
| 7813 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = continue_label }); |
| 7814 | |
| 7815 | try cg.beginSpvBlock(continue_label); |
| 7816 | try cg.body.emit(gpa, .OpBranch, .{ .target_label = header_label }); |
| 7817 | |
| 7818 | try cg.beginSpvBlock(loop_merge); |
| 7819 | try cg.structuredBreak(next_block); |
| 7820 | } |
| 7821 | |
| 7822 | fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 7823 | const br = cg.air.instructions.items(.data)[@intFromEnum(inst)].br; |
| 7824 | const loop_switch = cg.loop_switches.get(br.block_inst).?; |
| 7825 | const cond_ty = cg.typeOf(br.operand); |
| 7826 | const operand = try cg.resolve(br.operand); |
| 7827 | const operand_indirect = try cg.convertToIndirect(cond_ty, operand); |
| 7828 | |
| 7829 | try cg.store(cond_ty, loop_switch.cond_var, operand_indirect, .{}); |
| 7830 | try cg.structuredBreak(loop_switch.continue_label); |
| 7539 | 7831 | } |
| 7540 | 7832 | |
| 7541 | 7833 | fn airUnreach(cg: *CodeGen) !void { |
| ... | ... | @@ -7743,9 +8035,19 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 7743 | 8035 | // temporary params buffer. |
| 7744 | 8036 | const arg_ty = cg.typeOf(arg); |
| 7745 | 8037 | if (!arg_ty.hasRuntimeBits(zcu)) continue; |
| 7746 | | const arg_id = try cg.resolve(arg); |
| 7747 | 8038 | |
| 7748 | | params[n_params] = arg_id; |
| 8039 | if (arg_ty.zigTypeTag(zcu) == .pointer and !arg_ty.isSlice(zcu) and |
| 8040 | !arg_ty.childType(zcu).hasRuntimeBits(zcu)) |
| 8041 | { |
| 8042 | // in logical addressing, pointer arguments to function calls |
| 8043 | // must be memory object declarations (OpVariable). for pointers to |
| 8044 | // zero-sized types, the source value may not be a variable, so just |
| 8045 | // allocate a dummy one. |
| 8046 | const child_ty_id = try cg.resolveType(arg_ty.childType(zcu), .indirect); |
| 8047 | params[n_params] = try cg.alloc(child_ty_id, null); |
| 8048 | } else { |
| 8049 | params[n_params] = try cg.resolve(arg); |
| 8050 | } |
| 7749 | 8051 | n_params += 1; |
| 7750 | 8052 | } |
| 7751 | 8053 | |