authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-09-21 22:50:13+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-23 12:36:56-07:00
log68c7fc5c595b4a48f95b3f2f8c4d0a6c3a388667
treec8bddb4cf96e69ff12a2dc606fa8dcf656fd2768
parent63512192deeb2ba42e51a4dc7e1ba24eefcee7db

spirv: fix blocks that return no value


2 files changed, 43 insertions(+), 35 deletions(-)

src/codegen/spirv.zig+43-34
......@@ -45,10 +45,12 @@ const IncomingBlock = struct {
4545 break_value_id: IdRef,
4646};
4747
48const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
49 label_id: IdRef,
50 incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock),
51});
48const Block = struct {
49 label_id: ?IdRef,
50 incoming_blocks: std.ArrayListUnmanaged(IncomingBlock),
51};
52
53const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block);
5254
5355/// Maps Zig decl indices to linking SPIR-V linking information.
5456pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, SpvModule.Decl.Index);
......@@ -2970,50 +2972,50 @@ pub const DeclGen = struct {
29702972 }
29712973
29722974 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
29762980 // ir.Block in a different SPIR-V block.
29772981
29782982 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
29932983 const ty = self.typeOfIndex(inst);
29942984 const inst_datas = self.air.instructions.items(.data);
29952985 const extra = self.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
29962986 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));
29972999
29983000 try self.genBody(body);
2999 try self.beginSpvBlock(label_id);
30003001
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)
30033008 return null;
30043009
3005 // Combine the result from the blocks using the Phi instruction.
3010 assert(block.label_id != null);
30063011 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.
30103012 const result_type_id = try self.resolveTypeId(ty);
30113013
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...
30133015 self.func.body.writeOperand(spec.IdResultType, result_type_id);
30143016 self.func.body.writeOperand(spec.IdRef, result_id);
30153017
3016 for (incoming_blocks.items) |incoming| {
3018 for (block.incoming_blocks.items) |incoming| {
30173019 self.func.body.writeOperand(spec.PairIdRefIdRef, .{ incoming.break_value_id, incoming.src_label_id });
30183020 }
30193021
......@@ -3022,17 +3024,24 @@ pub const DeclGen = struct {
30223024
30233025 fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
30243026 const br = self.air.instructions.items(.data)[inst].br;
3025 const block = self.blocks.get(br.block_inst).?;
30263027 const operand_ty = self.typeOf(br.operand);
3028 const block = self.blocks.get(br.block_inst).?;
30273029
30283030 const mod = self.module;
3029 if (operand_ty.hasRuntimeBits(mod)) {
3031 if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) {
30303032 const operand_id = try self.resolve(br.operand);
30313033 // 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();
30333042 }
30343043
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.? });
30363045 }
30373046
30383047 fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void {
test/behavior/pointers.zig-1
......@@ -125,7 +125,6 @@ fn testDerefPtrOneVal() !void {
125125}
126126
127127test "peer type resolution with C pointers" {
128 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
129128 var ptr_one: *u8 = undefined;
130129 var ptr_many: [*]u8 = undefined;
131130 var ptr_c: [*c]u8 = undefined;