| ... | ... | @@ -174,6 +174,12 @@ pub const DeclGen = struct { |
| 174 | 174 | /// The label of the SPIR-V block we are currently generating. |
| 175 | 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 | 183 | /// The decl we are currently generating code for. |
| 178 | 184 | decl: *Decl, |
| 179 | 185 | |
| ... | ... | @@ -229,14 +235,15 @@ pub const DeclGen = struct { |
| 229 | 235 | }; |
| 230 | 236 | |
| 231 | 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 | 239 | return .{ |
| 234 | 240 | .spv = spv, |
| 235 | | .args = std.ArrayList(ResultId).init(gpa), |
| 241 | .args = std.ArrayList(ResultId).init(spv.gpa), |
| 236 | 242 | .next_arg_index = undefined, |
| 237 | | .inst_results = InstMap.init(gpa), |
| 238 | | .blocks = BlockMap.init(gpa), |
| 243 | .inst_results = InstMap.init(spv.gpa), |
| 244 | .blocks = BlockMap.init(spv.gpa), |
| 239 | 245 | .current_block_label_id = undefined, |
| 246 | .code = std.ArrayList(Word).init(spv.gpa), |
| 240 | 247 | .decl = undefined, |
| 241 | 248 | .error_msg = undefined, |
| 242 | 249 | }; |
| ... | ... | @@ -252,6 +259,7 @@ pub const DeclGen = struct { |
| 252 | 259 | self.inst_results.clearRetainingCapacity(); |
| 253 | 260 | self.blocks.clearRetainingCapacity(); |
| 254 | 261 | self.current_block_label_id = undefined; |
| 262 | self.code.items.len = 0; |
| 255 | 263 | self.decl = decl; |
| 256 | 264 | self.error_msg = null; |
| 257 | 265 | |
| ... | ... | @@ -264,6 +272,7 @@ pub const DeclGen = struct { |
| 264 | 272 | self.args.deinit(); |
| 265 | 273 | self.inst_results.deinit(); |
| 266 | 274 | self.blocks.deinit(); |
| 275 | self.code.deinit(); |
| 267 | 276 | } |
| 268 | 277 | |
| 269 | 278 | fn getTarget(self: *DeclGen) std.Target { |
| ... | ... | @@ -286,7 +295,7 @@ pub const DeclGen = struct { |
| 286 | 295 | } |
| 287 | 296 | |
| 288 | 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 | 299 | self.current_block_label_id = label_id; |
| 291 | 300 | } |
| 292 | 301 | |
| ... | ... | @@ -616,9 +625,16 @@ pub const DeclGen = struct { |
| 616 | 625 | |
| 617 | 626 | // TODO: This could probably be done in a better way... |
| 618 | 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 | 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 | 638 | try writeInstruction(&self.spv.binary.fn_decls, .OpFunctionEnd, &[_]Word{}); |
| 623 | 639 | } else { |
| 624 | 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 | 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 | 744 | // TODO: Trap on overflow? Probably going to be annoying. |
| 729 | 745 | // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. |
| ... | ... | @@ -775,7 +791,7 @@ pub const DeclGen = struct { |
| 775 | 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 | 795 | return result_id; |
| 780 | 796 | } |
| 781 | 797 | |
| ... | ... | @@ -793,7 +809,7 @@ pub const DeclGen = struct { |
| 793 | 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 | 814 | return result_id; |
| 799 | 815 | } |
| ... | ... | @@ -803,6 +819,8 @@ pub const DeclGen = struct { |
| 803 | 819 | const result_type_id = try self.genPointerType(inst.base.src, inst.base.ty, storage_class); |
| 804 | 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 | 824 | try writeInstruction(&self.spv.binary.fn_decls, .OpVariable, &[_]Word{ result_type_id, result_id, @enumToInt(storage_class) }); |
| 807 | 825 | |
| 808 | 826 | return result_id; |
| ... | ... | @@ -849,10 +867,10 @@ pub const DeclGen = struct { |
| 849 | 867 | // an error for pointers. |
| 850 | 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 | 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 | 876 | return result_id; |
| ... | ... | @@ -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 | 895 | return null; |
| 878 | 896 | } |
| ... | ... | @@ -881,7 +899,7 @@ pub const DeclGen = struct { |
| 881 | 899 | // TODO: This instruction needs to be the last in a block. Is that guaranteed? |
| 882 | 900 | const target = self.blocks.get(inst.block).?; |
| 883 | 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 | 903 | return null; |
| 886 | 904 | } |
| 887 | 905 | |
| ... | ... | @@ -896,7 +914,7 @@ pub const DeclGen = struct { |
| 896 | 914 | // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to, |
| 897 | 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 | 918 | condition_id, |
| 901 | 919 | then_label_id, |
| 902 | 920 | else_label_id, |
| ... | ... | @@ -912,7 +930,7 @@ pub const DeclGen = struct { |
| 912 | 930 | |
| 913 | 931 | fn genDbgStmt(self: *DeclGen, inst: *Inst.DbgStmt) !?ResultId { |
| 914 | 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 | 934 | return null; |
| 917 | 935 | } |
| 918 | 936 | |
| ... | ... | @@ -927,7 +945,7 @@ pub const DeclGen = struct { |
| 927 | 945 | else |
| 928 | 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 | 950 | return result_id; |
| 933 | 951 | } |
| ... | ... | @@ -937,27 +955,27 @@ pub const DeclGen = struct { |
| 937 | 955 | const loop_label_id = self.spv.allocResultId(); |
| 938 | 956 | |
| 939 | 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 | 960 | // TODO: Look into OpLoopMerge. |
| 943 | 961 | |
| 944 | 962 | try self.beginSPIRVBlock(loop_label_id); |
| 945 | 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 | 966 | return null; |
| 949 | 967 | } |
| 950 | 968 | |
| 951 | 969 | fn genRet(self: *DeclGen, inst: *Inst.UnOp) !?ResultId { |
| 952 | 970 | const operand_id = try self.resolve(inst.operand); |
| 953 | 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 | 973 | return null; |
| 956 | 974 | } |
| 957 | 975 | |
| 958 | 976 | fn genRetVoid(self: *DeclGen) !?ResultId { |
| 959 | 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 | 979 | return null; |
| 962 | 980 | } |
| 963 | 981 | |
| ... | ... | @@ -970,13 +988,13 @@ pub const DeclGen = struct { |
| 970 | 988 | else |
| 971 | 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 | 992 | return null; |
| 975 | 993 | } |
| 976 | 994 | |
| 977 | 995 | fn genUnreach(self: *DeclGen) !?ResultId { |
| 978 | 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 | 998 | return null; |
| 981 | 999 | } |
| 982 | 1000 | }; |