| ... | ... | @@ -45,10 +45,12 @@ const IncomingBlock = struct { |
| 45 | 45 | break_value_id: IdRef, |
| 46 | 46 | }; |
| 47 | 47 | |
| 48 | | const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct { |
| 49 | | label_id: IdRef, |
| 50 | | incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock), |
| 51 | | }); |
| 48 | const Block = struct { |
| 49 | label_id: ?IdRef, |
| 50 | incoming_blocks: std.ArrayListUnmanaged(IncomingBlock), |
| 51 | }; |
| 52 | |
| 53 | const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block); |
| 52 | 54 | |
| 53 | 55 | /// Maps Zig decl indices to linking SPIR-V linking information. |
| 54 | 56 | pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, SpvModule.Decl.Index); |
| ... | ... | @@ -2970,50 +2972,50 @@ pub const DeclGen = struct { |
| 2970 | 2972 | } |
| 2971 | 2973 | |
| 2972 | 2974 | fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |
| 2973 | | // In AIR, a block doesn't really define an entry point like a block, but more like a scope that breaks can jump out of and |
| 2974 | | // "return" a value from. This cannot be directly modelled in SPIR-V, so in a block instruction, we're going to split up |
| 2975 | | // the current block by first generating the code of the block, then a label, and then generate the rest of the current |
| 2975 | // In AIR, a block doesn't really define an entry point like a block, but |
| 2976 | // more like a scope that breaks can jump out of and "return" a value from. |
| 2977 | // This cannot be directly modelled in SPIR-V, so in a block instruction, |
| 2978 | // we're going to split up the current block by first generating the code |
| 2979 | // of the block, then a label, and then generate the rest of the current |
| 2976 | 2980 | // ir.Block in a different SPIR-V block. |
| 2977 | 2981 | |
| 2978 | 2982 | const mod = self.module; |
| 2979 | | const label_id = self.spv.allocId(); |
| 2980 | | |
| 2981 | | // 4 chosen as arbitrary initial capacity. |
| 2982 | | var incoming_blocks = try std.ArrayListUnmanaged(IncomingBlock).initCapacity(self.gpa, 4); |
| 2983 | | |
| 2984 | | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| 2985 | | .label_id = label_id, |
| 2986 | | .incoming_blocks = &incoming_blocks, |
| 2987 | | }); |
| 2988 | | defer { |
| 2989 | | assert(self.blocks.remove(inst)); |
| 2990 | | incoming_blocks.deinit(self.gpa); |
| 2991 | | } |
| 2992 | | |
| 2993 | 2983 | const ty = self.typeOfIndex(inst); |
| 2994 | 2984 | const inst_datas = self.air.instructions.items(.data); |
| 2995 | 2985 | const extra = self.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload); |
| 2996 | 2986 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 2987 | const have_block_result = ty.isFnOrHasRuntimeBitsIgnoreComptime(mod); |
| 2988 | |
| 2989 | // 4 chosen as arbitrary initial capacity. |
| 2990 | var block = Block{ |
| 2991 | // Label id is lazily allocated if needed. |
| 2992 | .label_id = null, |
| 2993 | .incoming_blocks = try std.ArrayListUnmanaged(IncomingBlock).initCapacity(self.gpa, 4), |
| 2994 | }; |
| 2995 | defer block.incoming_blocks.deinit(self.gpa); |
| 2996 | |
| 2997 | try self.blocks.putNoClobber(self.gpa, inst, &block); |
| 2998 | defer assert(self.blocks.remove(inst)); |
| 2997 | 2999 | |
| 2998 | 3000 | try self.genBody(body); |
| 2999 | | try self.beginSpvBlock(label_id); |
| 3000 | 3001 | |
| 3001 | | // If this block didn't produce a value, simply return here. |
| 3002 | | if (!ty.hasRuntimeBitsIgnoreComptime(mod)) |
| 3002 | // Only begin a new block if there were actually any breaks towards it. |
| 3003 | if (block.label_id) |label_id| { |
| 3004 | try self.beginSpvBlock(label_id); |
| 3005 | } |
| 3006 | |
| 3007 | if (!have_block_result) |
| 3003 | 3008 | return null; |
| 3004 | 3009 | |
| 3005 | | // Combine the result from the blocks using the Phi instruction. |
| 3010 | assert(block.label_id != null); |
| 3006 | 3011 | const result_id = self.spv.allocId(); |
| 3007 | | |
| 3008 | | // TODO: OpPhi is limited in the types that it may produce, such as pointers. Figure out which other types |
| 3009 | | // are not allowed to be created from a phi node, and throw an error for those. |
| 3010 | 3012 | const result_type_id = try self.resolveTypeId(ty); |
| 3011 | 3013 | |
| 3012 | | try self.func.body.emitRaw(self.spv.gpa, .OpPhi, 2 + @as(u16, @intCast(incoming_blocks.items.len * 2))); // result type + result + variable/parent... |
| 3014 | try self.func.body.emitRaw(self.spv.gpa, .OpPhi, 2 + @as(u16, @intCast(block.incoming_blocks.items.len * 2))); // result type + result + variable/parent... |
| 3013 | 3015 | self.func.body.writeOperand(spec.IdResultType, result_type_id); |
| 3014 | 3016 | self.func.body.writeOperand(spec.IdRef, result_id); |
| 3015 | 3017 | |
| 3016 | | for (incoming_blocks.items) |incoming| { |
| 3018 | for (block.incoming_blocks.items) |incoming| { |
| 3017 | 3019 | self.func.body.writeOperand(spec.PairIdRefIdRef, .{ incoming.break_value_id, incoming.src_label_id }); |
| 3018 | 3020 | } |
| 3019 | 3021 | |
| ... | ... | @@ -3022,17 +3024,24 @@ pub const DeclGen = struct { |
| 3022 | 3024 | |
| 3023 | 3025 | fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 3024 | 3026 | const br = self.air.instructions.items(.data)[inst].br; |
| 3025 | | const block = self.blocks.get(br.block_inst).?; |
| 3026 | 3027 | const operand_ty = self.typeOf(br.operand); |
| 3028 | const block = self.blocks.get(br.block_inst).?; |
| 3027 | 3029 | |
| 3028 | 3030 | const mod = self.module; |
| 3029 | | if (operand_ty.hasRuntimeBits(mod)) { |
| 3031 | if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { |
| 3030 | 3032 | const operand_id = try self.resolve(br.operand); |
| 3031 | 3033 | // current_block_label_id should not be undefined here, lest there is a br or br_void in the function's body. |
| 3032 | | try block.incoming_blocks.append(self.gpa, .{ .src_label_id = self.current_block_label_id, .break_value_id = operand_id }); |
| 3034 | try block.incoming_blocks.append(self.gpa, .{ |
| 3035 | .src_label_id = self.current_block_label_id, |
| 3036 | .break_value_id = operand_id, |
| 3037 | }); |
| 3038 | } |
| 3039 | |
| 3040 | if (block.label_id == null) { |
| 3041 | block.label_id = self.spv.allocId(); |
| 3033 | 3042 | } |
| 3034 | 3043 | |
| 3035 | | try self.func.body.emit(self.spv.gpa, .OpBranch, .{ .target_label = block.label_id }); |
| 3044 | try self.func.body.emit(self.spv.gpa, .OpBranch, .{ .target_label = block.label_id.? }); |
| 3036 | 3045 | } |
| 3037 | 3046 | |
| 3038 | 3047 | fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void { |