authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-21 02:45:11+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-22 16:11:56+02:00
log228f71fa0ce25a5c473496dd2b947ae05ab9bed8
tree819e251859451e7cef627ece38ccb1deb4bf776f
parent6634abfd2669a902a86f2c61dbc011310e1f31c4

SPIR-V: Generate locals at the start of a function


2 files changed, 41 insertions(+), 23 deletions(-)

src/codegen/spirv.zig+40-22
...@@ -174,6 +174,12 @@ pub const DeclGen = struct {...@@ -174,6 +174,12 @@ pub const DeclGen = struct {
174 /// The label of the SPIR-V block we are currently generating.174 /// The label of the SPIR-V block we are currently generating.
175 current_block_label_id: ResultId,175 current_block_label_id: ResultId,
176176
177 /// The actual instructions for this function. We need to declare all locals in the first block, and because we don't
178 /// know which locals there are going to be, we're just going to generate everything after the locals-section in this array.
179 /// Note: It will not contain OpFunction, OpFunctionParameter, OpVariable and the initial OpLabel. These will be generated
180 /// into spv.binary.fn_decls directly.
181 code: std.ArrayList(Word),
182
177 /// The decl we are currently generating code for.183 /// The decl we are currently generating code for.
178 decl: *Decl,184 decl: *Decl,
179185
...@@ -229,14 +235,15 @@ pub const DeclGen = struct {...@@ -229,14 +235,15 @@ pub const DeclGen = struct {
229 };235 };
230236
231 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized, only set when `gen` is called.237 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized, only set when `gen` is called.
232 pub fn init(gpa: *Allocator, spv: *SPIRVModule) DeclGen {238 pub fn init(spv: *SPIRVModule) DeclGen {
233 return .{239 return .{
234 .spv = spv,240 .spv = spv,
235 .args = std.ArrayList(ResultId).init(gpa),241 .args = std.ArrayList(ResultId).init(spv.gpa),
236 .next_arg_index = undefined,242 .next_arg_index = undefined,
237 .inst_results = InstMap.init(gpa),243 .inst_results = InstMap.init(spv.gpa),
238 .blocks = BlockMap.init(gpa),244 .blocks = BlockMap.init(spv.gpa),
239 .current_block_label_id = undefined,245 .current_block_label_id = undefined,
246 .code = std.ArrayList(Word).init(spv.gpa),
240 .decl = undefined,247 .decl = undefined,
241 .error_msg = undefined,248 .error_msg = undefined,
242 };249 };
...@@ -252,6 +259,7 @@ pub const DeclGen = struct {...@@ -252,6 +259,7 @@ pub const DeclGen = struct {
252 self.inst_results.clearRetainingCapacity();259 self.inst_results.clearRetainingCapacity();
253 self.blocks.clearRetainingCapacity();260 self.blocks.clearRetainingCapacity();
254 self.current_block_label_id = undefined;261 self.current_block_label_id = undefined;
262 self.code.items.len = 0;
255 self.decl = decl;263 self.decl = decl;
256 self.error_msg = null;264 self.error_msg = null;
257265
...@@ -264,6 +272,7 @@ pub const DeclGen = struct {...@@ -264,6 +272,7 @@ pub const DeclGen = struct {
264 self.args.deinit();272 self.args.deinit();
265 self.inst_results.deinit();273 self.inst_results.deinit();
266 self.blocks.deinit();274 self.blocks.deinit();
275 self.code.deinit();
267 }276 }
268277
269 fn getTarget(self: *DeclGen) std.Target {278 fn getTarget(self: *DeclGen) std.Target {
...@@ -286,7 +295,7 @@ pub const DeclGen = struct {...@@ -286,7 +295,7 @@ pub const DeclGen = struct {
286 }295 }
287296
288 fn beginSPIRVBlock(self: *DeclGen, label_id: ResultId) !void {297 fn beginSPIRVBlock(self: *DeclGen, label_id: ResultId) !void {
289 try writeInstruction(&self.spv.binary.fn_decls, .OpLabel, &[_]Word{label_id});298 try writeInstruction(&self.code, .OpLabel, &[_]Word{label_id});
290 self.current_block_label_id = label_id;299 self.current_block_label_id = label_id;
291 }300 }
292301
...@@ -616,9 +625,16 @@ pub const DeclGen = struct {...@@ -616,9 +625,16 @@ pub const DeclGen = struct {
616625
617 // TODO: This could probably be done in a better way...626 // TODO: This could probably be done in a better way...
618 const root_block_id = self.spv.allocResultId();627 const root_block_id = self.spv.allocResultId();
619 try self.beginSPIRVBlock(root_block_id);628
629 // We need to generate the label directly in the fn_decls here because we're going to write the local variables after
630 // here. Since we're not generating in self.code, we're just going to bypass self.beginSPIRVBlock here.
631 try writeInstruction(&self.spv.binary.fn_decls, .OpLabel, &[_]Word{root_block_id});
632 self.current_block_label_id = root_block_id;
633
620 try self.genBody(func_payload.data.body);634 try self.genBody(func_payload.data.body);
621635
636 // Append the actual code into the fn_decls section.
637 try self.spv.binary.fn_decls.appendSlice(self.code.items);
622 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]Word{});638 try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]Word{});
623 } else {639 } else {
624 return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()});640 return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: generate decl type {}", .{decl.ty.zigTypeTag()});
...@@ -723,7 +739,7 @@ pub const DeclGen = struct {...@@ -723,7 +739,7 @@ pub const DeclGen = struct {
723 else => unreachable,739 else => unreachable,
724 };740 };
725741
726 try writeInstruction(&self.spv.binary.fn_decls, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });742 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });
727743
728 // TODO: Trap on overflow? Probably going to be annoying.744 // TODO: Trap on overflow? Probably going to be annoying.
729 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.745 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
...@@ -775,7 +791,7 @@ pub const DeclGen = struct {...@@ -775,7 +791,7 @@ pub const DeclGen = struct {
775 else => unreachable,791 else => unreachable,
776 };792 };
777793
778 try writeInstruction(&self.spv.binary.fn_decls, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });794 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id });
779 return result_id;795 return result_id;
780 }796 }
781797
...@@ -793,7 +809,7 @@ pub const DeclGen = struct {...@@ -793,7 +809,7 @@ pub const DeclGen = struct {
793 else => unreachable,809 else => unreachable,
794 };810 };
795811
796 try writeInstruction(&self.spv.binary.fn_decls, opcode, &[_]Word{ result_type_id, result_id, operand_id });812 try writeInstruction(&self.code, opcode, &[_]Word{ result_type_id, result_id, operand_id });
797813
798 return result_id;814 return result_id;
799 }815 }
...@@ -803,6 +819,8 @@ pub const DeclGen = struct {...@@ -803,6 +819,8 @@ pub const DeclGen = struct {
803 const result_type_id = try self.genPointerType(inst.base.src, inst.base.ty, storage_class);819 const result_type_id = try self.genPointerType(inst.base.src, inst.base.ty, storage_class);
804 const result_id = self.spv.allocResultId();820 const result_id = self.spv.allocResultId();
805821
822 // Rather than generating into code here, we're just going to generate directly into the fn_decls section so that
823 // variable declarations appear in the first block of the function.
806 try writeInstruction(&self.spv.binary.fn_decls, .OpVariable, &[_]Word{ result_type_id, result_id, @enumToInt(storage_class) });824 try writeInstruction(&self.spv.binary.fn_decls, .OpVariable, &[_]Word{ result_type_id, result_id, @enumToInt(storage_class) });
807825
808 return result_id;826 return result_id;
...@@ -849,10 +867,10 @@ pub const DeclGen = struct {...@@ -849,10 +867,10 @@ pub const DeclGen = struct {
849 // an error for pointers.867 // an error for pointers.
850 const result_type_id = try self.genType(inst.base.src, inst.base.ty);868 const result_type_id = try self.genType(inst.base.src, inst.base.ty);
851869
852 try writeOpcode(&self.spv.binary.fn_decls, .OpPhi, 2 + @intCast(u16, incoming_blocks.items.len * 2)); // result type + result + variable/parent...870 try writeOpcode(&self.code, .OpPhi, 2 + @intCast(u16, incoming_blocks.items.len * 2)); // result type + result + variable/parent...
853871
854 for (incoming_blocks.items) |incoming| {872 for (incoming_blocks.items) |incoming| {
855 try self.spv.binary.fn_decls.appendSlice(&[_]Word{ incoming.break_value_id, incoming.src_label_id });873 try self.code.appendSlice(&[_]Word{ incoming.break_value_id, incoming.src_label_id });
856 }874 }
857875
858 return result_id;876 return result_id;
...@@ -872,7 +890,7 @@ pub const DeclGen = struct {...@@ -872,7 +890,7 @@ pub const DeclGen = struct {
872 });890 });
873 }891 }
874892
875 try writeInstruction(&self.spv.binary.fn_decls, .OpBranch, &[_]Word{target.label_id});893 try writeInstruction(&self.code, .OpBranch, &[_]Word{target.label_id});
876894
877 return null;895 return null;
878 }896 }
...@@ -881,7 +899,7 @@ pub const DeclGen = struct {...@@ -881,7 +899,7 @@ pub const DeclGen = struct {
881 // TODO: This instruction needs to be the last in a block. Is that guaranteed?899 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
882 const target = self.blocks.get(inst.block).?;900 const target = self.blocks.get(inst.block).?;
883 // Don't need to add this to the incoming block list, as there is no value to insert in the phi node anyway.901 // Don't need to add this to the incoming block list, as there is no value to insert in the phi node anyway.
884 try writeInstruction(&self.spv.binary.fn_decls, .OpBranch, &[_]Word{target.label_id});902 try writeInstruction(&self.code, .OpBranch, &[_]Word{target.label_id});
885 return null;903 return null;
886 }904 }
887905
...@@ -896,7 +914,7 @@ pub const DeclGen = struct {...@@ -896,7 +914,7 @@ pub const DeclGen = struct {
896 // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to,914 // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to,
897 // but i don't know if those will always resolve to the same block.915 // but i don't know if those will always resolve to the same block.
898916
899 try writeInstruction(&self.spv.binary.fn_decls, .OpBranchConditional, &[_]Word{917 try writeInstruction(&self.code, .OpBranchConditional, &[_]Word{
900 condition_id,918 condition_id,
901 then_label_id,919 then_label_id,
902 else_label_id,920 else_label_id,
...@@ -912,7 +930,7 @@ pub const DeclGen = struct {...@@ -912,7 +930,7 @@ pub const DeclGen = struct {
912930
913 fn genDbgStmt(self: *DeclGen, inst: *Inst.DbgStmt) !?ResultId {931 fn genDbgStmt(self: *DeclGen, inst: *Inst.DbgStmt) !?ResultId {
914 const src_fname_id = try self.spv.resolveSourceFileName(self.decl);932 const src_fname_id = try self.spv.resolveSourceFileName(self.decl);
915 try writeInstruction(&self.spv.binary.fn_decls, .OpLine, &[_]Word{ src_fname_id, inst.line, inst.column });933 try writeInstruction(&self.code, .OpLine, &[_]Word{ src_fname_id, inst.line, inst.column });
916 return null;934 return null;
917 }935 }
918936
...@@ -927,7 +945,7 @@ pub const DeclGen = struct {...@@ -927,7 +945,7 @@ pub const DeclGen = struct {
927 else945 else
928 &[_]Word{ result_type_id, result_id, operand_id};946 &[_]Word{ result_type_id, result_id, operand_id};
929947
930 try writeInstruction(&self.spv.binary.fn_decls, .OpLoad, operands);948 try writeInstruction(&self.code, .OpLoad, operands);
931949
932 return result_id;950 return result_id;
933 }951 }
...@@ -937,27 +955,27 @@ pub const DeclGen = struct {...@@ -937,27 +955,27 @@ pub const DeclGen = struct {
937 const loop_label_id = self.spv.allocResultId();955 const loop_label_id = self.spv.allocResultId();
938956
939 // Jump to the loop entry point957 // Jump to the loop entry point
940 try writeInstruction(&self.spv.binary.fn_decls, .OpBranch, &[_]Word{ loop_label_id });958 try writeInstruction(&self.code, .OpBranch, &[_]Word{ loop_label_id });
941959
942 // TODO: Look into OpLoopMerge.960 // TODO: Look into OpLoopMerge.
943961
944 try self.beginSPIRVBlock(loop_label_id);962 try self.beginSPIRVBlock(loop_label_id);
945 try self.genBody(inst.body);963 try self.genBody(inst.body);
946964
947 try writeInstruction(&self.spv.binary.fn_decls, .OpBranch, &[_]Word{ loop_label_id });965 try writeInstruction(&self.code, .OpBranch, &[_]Word{ loop_label_id });
948 return null;966 return null;
949 }967 }
950968
951 fn genRet(self: *DeclGen, inst: *Inst.UnOp) !?ResultId {969 fn genRet(self: *DeclGen, inst: *Inst.UnOp) !?ResultId {
952 const operand_id = try self.resolve(inst.operand);970 const operand_id = try self.resolve(inst.operand);
953 // TODO: This instruction needs to be the last in a block. Is that guaranteed?971 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
954 try writeInstruction(&self.spv.binary.fn_decls, .OpReturnValue, &[_]Word{operand_id});972 try writeInstruction(&self.code, .OpReturnValue, &[_]Word{operand_id});
955 return null;973 return null;
956 }974 }
957975
958 fn genRetVoid(self: *DeclGen) !?ResultId {976 fn genRetVoid(self: *DeclGen) !?ResultId {
959 // TODO: This instruction needs to be the last in a block. Is that guaranteed?977 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
960 try writeInstruction(&self.spv.binary.fn_decls, .OpReturn, &[_]Word{});978 try writeInstruction(&self.code, .OpReturn, &[_]Word{});
961 return null;979 return null;
962 }980 }
963981
...@@ -970,13 +988,13 @@ pub const DeclGen = struct {...@@ -970,13 +988,13 @@ pub const DeclGen = struct {
970 else988 else
971 &[_]Word{ dst_ptr_id, src_val_id };989 &[_]Word{ dst_ptr_id, src_val_id };
972990
973 try writeInstruction(&self.spv.binary.fn_decls, .OpStore, operands);991 try writeInstruction(&self.code, .OpStore, operands);
974 return null;992 return null;
975 }993 }
976994
977 fn genUnreach(self: *DeclGen) !?ResultId {995 fn genUnreach(self: *DeclGen) !?ResultId {
978 // TODO: This instruction needs to be the last in a block. Is that guaranteed?996 // TODO: This instruction needs to be the last in a block. Is that guaranteed?
979 try writeInstruction(&self.spv.binary.fn_decls, .OpUnreachable, &[_]Word{});997 try writeInstruction(&self.code, .OpUnreachable, &[_]Word{});
980 return null;998 return null;
981 }999 }
982};1000};
src/link/SpirV.zig+1-1
...@@ -152,7 +152,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {...@@ -152,7 +152,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation) !void {
152152
153 // Now, actually generate the code for all declarations.153 // Now, actually generate the code for all declarations.
154 {154 {
155 var decl_gen = codegen.DeclGen.init(self.base.allocator, &spv);155 var decl_gen = codegen.DeclGen.init(&spv);
156 defer decl_gen.deinit();156 defer decl_gen.deinit();
157157
158 for (self.decl_table.items()) |entry| {158 for (self.decl_table.items()) |entry| {