| ... | @@ -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, |
| 176 | | 176 | |
| | 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, |
| 179 | | 185 | |
| ... | @@ -229,14 +235,15 @@ pub const DeclGen = struct { | ... | @@ -229,14 +235,15 @@ pub const DeclGen = struct { |
| 229 | }; | 235 | }; |
| 230 | | 236 | |
| 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; |
| 257 | | 265 | |
| ... | @@ -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 | } |
| 268 | | 277 | |
| 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 | } |
| 287 | | 296 | |
| 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 | } |
| 292 | | 301 | |
| ... | @@ -616,9 +625,16 @@ pub const DeclGen = struct { | ... | @@ -616,9 +625,16 @@ pub const DeclGen = struct { |
| 616 | | 625 | |
| 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); |
| 621 | | 635 | |
| | 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 | }; |
| 725 | | 741 | |
| 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 }); |
| 727 | | 743 | |
| 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 | }; |
| 777 | | 793 | |
| 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 | } |
| 781 | | 797 | |
| ... | @@ -793,7 +809,7 @@ pub const DeclGen = struct { | ... | @@ -793,7 +809,7 @@ pub const DeclGen = struct { |
| 793 | else => unreachable, | 809 | else => unreachable, |
| 794 | }; | 810 | }; |
| 795 | | 811 | |
| 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 }); |
| 797 | | 813 | |
| 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(); |
| 805 | | 821 | |
| | 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) }); |
| 807 | | 825 | |
| 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); |
| 851 | | 869 | |
| 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... |
| 853 | | 871 | |
| 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 | } |
| 857 | | 875 | |
| 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 | } |
| 874 | | 892 | |
| 875 | try writeInstruction(&self.spv.binary.fn_decls, .OpBranch, &[_]Word{target.label_id}); | 893 | try writeInstruction(&self.code, .OpBranch, &[_]Word{target.label_id}); |
| 876 | | 894 | |
| 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 | } |
| 887 | | 905 | |
| ... | @@ -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. |
| 898 | | 916 | |
| 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 { |
| 912 | | 930 | |
| 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 | } |
| 918 | | 936 | |
| ... | @@ -927,7 +945,7 @@ pub const DeclGen = struct { | ... | @@ -927,7 +945,7 @@ pub const DeclGen = struct { |
| 927 | else | 945 | else |
| 928 | &[_]Word{ result_type_id, result_id, operand_id}; | 946 | &[_]Word{ result_type_id, result_id, operand_id}; |
| 929 | | 947 | |
| 930 | try writeInstruction(&self.spv.binary.fn_decls, .OpLoad, operands); | 948 | try writeInstruction(&self.code, .OpLoad, operands); |
| 931 | | 949 | |
| 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(); |
| 938 | | 956 | |
| 939 | // Jump to the loop entry point | 957 | // 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 }); |
| 941 | | 959 | |
| 942 | // TODO: Look into OpLoopMerge. | 960 | // TODO: Look into OpLoopMerge. |
| 943 | | 961 | |
| 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); |
| 946 | | 964 | |
| 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 | } |
| 950 | | 968 | |
| 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 | } |
| 957 | | 975 | |
| 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 | } |
| 963 | | 981 | |
| ... | @@ -970,13 +988,13 @@ pub const DeclGen = struct { | ... | @@ -970,13 +988,13 @@ pub const DeclGen = struct { |
| 970 | else | 988 | else |
| 971 | &[_]Word{ dst_ptr_id, src_val_id }; | 989 | &[_]Word{ dst_ptr_id, src_val_id }; |
| 972 | | 990 | |
| 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 | } |
| 976 | | 994 | |
| 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 | }; |