| ... | @@ -32,12 +32,28 @@ const IncomingBlock = struct { | ... | @@ -32,12 +32,28 @@ const IncomingBlock = struct { |
| 32 | break_value_id: IdRef, | 32 | break_value_id: IdRef, |
| 33 | }; | 33 | }; |
| 34 | | 34 | |
| 35 | pub const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct { | 35 | const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, struct { |
| 36 | label_id: IdRef, | 36 | label_id: IdRef, |
| 37 | incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock), | 37 | incoming_blocks: *std.ArrayListUnmanaged(IncomingBlock), |
| 38 | }); | 38 | }); |
| 39 | | 39 | |
| 40 | pub const DeclMap = std.AutoHashMap(Module.Decl.Index, IdResult); | 40 | /// Linking information about a particular decl. |
| | 41 | /// The active field of this enum depends on the type of the corresponding decl. |
| | 42 | const DeclLink = union { |
| | 43 | /// Linking information about a function. |
| | 44 | /// Active when the decl is a function. |
| | 45 | func: struct { |
| | 46 | /// Result-id of the OpFunction instruction. |
| | 47 | result_id: IdResult, |
| | 48 | }, |
| | 49 | /// Linking information about a global. This index points into the |
| | 50 | /// SPIR-V module's `globals` array. |
| | 51 | /// Active when the decl is a variable. |
| | 52 | global: SpvModule.Global.Index, |
| | 53 | }; |
| | 54 | |
| | 55 | /// Maps Zig decl indices to linking SPIR-V linking information. |
| | 56 | pub const DeclLinkMap = std.AutoHashMap(Module.Decl.Index, DeclLink); |
| 41 | | 57 | |
| 42 | /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. | 58 | /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that. |
| 43 | pub const DeclGen = struct { | 59 | pub const DeclGen = struct { |
| ... | @@ -61,8 +77,8 @@ pub const DeclGen = struct { | ... | @@ -61,8 +77,8 @@ pub const DeclGen = struct { |
| 61 | /// Note: If the declaration is not a function, this value will be undefined! | 77 | /// Note: If the declaration is not a function, this value will be undefined! |
| 62 | liveness: Liveness, | 78 | liveness: Liveness, |
| 63 | | 79 | |
| 64 | /// Maps Zig Decl indices to SPIR-V result indices. | 80 | /// Maps Zig Decl indices to SPIR-V globals. |
| 65 | decl_ids: *DeclMap, | 81 | decl_link: *DeclLinkMap, |
| 66 | | 82 | |
| 67 | /// An array of function argument result-ids. Each index corresponds with the | 83 | /// An array of function argument result-ids. Each index corresponds with the |
| 68 | /// function argument of the same index. | 84 | /// function argument of the same index. |
| ... | @@ -152,7 +168,7 @@ pub const DeclGen = struct { | ... | @@ -152,7 +168,7 @@ pub const DeclGen = struct { |
| 152 | allocator: Allocator, | 168 | allocator: Allocator, |
| 153 | module: *Module, | 169 | module: *Module, |
| 154 | spv: *SpvModule, | 170 | spv: *SpvModule, |
| 155 | decl_ids: *DeclMap, | 171 | decl_link: *DeclLinkMap, |
| 156 | ) DeclGen { | 172 | ) DeclGen { |
| 157 | return .{ | 173 | return .{ |
| 158 | .gpa = allocator, | 174 | .gpa = allocator, |
| ... | @@ -161,7 +177,7 @@ pub const DeclGen = struct { | ... | @@ -161,7 +177,7 @@ pub const DeclGen = struct { |
| 161 | .decl_index = undefined, | 177 | .decl_index = undefined, |
| 162 | .air = undefined, | 178 | .air = undefined, |
| 163 | .liveness = undefined, | 179 | .liveness = undefined, |
| 164 | .decl_ids = decl_ids, | 180 | .decl_link = decl_link, |
| 165 | .next_arg_index = undefined, | 181 | .next_arg_index = undefined, |
| 166 | .current_block_label_id = undefined, | 182 | .current_block_label_id = undefined, |
| 167 | .error_msg = undefined, | 183 | .error_msg = undefined, |
| ... | @@ -235,7 +251,8 @@ pub const DeclGen = struct { | ... | @@ -235,7 +251,8 @@ pub const DeclGen = struct { |
| 235 | .function => val.castTag(.function).?.data.owner_decl, | 251 | .function => val.castTag(.function).?.data.owner_decl, |
| 236 | else => unreachable, | 252 | else => unreachable, |
| 237 | }; | 253 | }; |
| 238 | return try self.resolveDecl(fn_decl_index); | 254 | const link = try self.resolveDecl(fn_decl_index); |
| | 255 | return link.func.result_id; |
| 239 | } | 256 | } |
| 240 | | 257 | |
| 241 | return try self.constant(ty, val); | 258 | return try self.constant(ty, val); |
| ... | @@ -246,17 +263,22 @@ pub const DeclGen = struct { | ... | @@ -246,17 +263,22 @@ pub const DeclGen = struct { |
| 246 | | 263 | |
| 247 | /// Fetch or allocate a result id for decl index. This function also marks the decl as alive. | 264 | /// Fetch or allocate a result id for decl index. This function also marks the decl as alive. |
| 248 | /// Note: Function does not actually generate the decl. | 265 | /// Note: Function does not actually generate the decl. |
| 249 | fn resolveDecl(self: *DeclGen, decl_index: Module.Decl.Index) !IdResult { | 266 | fn resolveDecl(self: *DeclGen, decl_index: Module.Decl.Index) !DeclLink { |
| 250 | const decl = self.module.declPtr(decl_index); | 267 | const decl = self.module.declPtr(decl_index); |
| 251 | self.module.markDeclAlive(decl); | 268 | self.module.markDeclAlive(decl); |
| 252 | | 269 | |
| 253 | const entry = try self.decl_ids.getOrPut(decl_index); | 270 | const entry = try self.decl_link.getOrPut(decl_index); |
| 254 | if (entry.found_existing) { | | |
| 255 | return entry.value_ptr.*; | | |
| 256 | } | | |
| 257 | const result_id = self.spv.allocId(); | 271 | const result_id = self.spv.allocId(); |
| 258 | entry.value_ptr.* = result_id; | 272 | |
| 259 | return result_id; | 273 | if (!entry.found_existing) { |
| | 274 | if (decl.val.castTag(.function)) |_| { |
| | 275 | entry.value_ptr.* = .{.func = .{ .result_id = result_id }}; |
| | 276 | } else { |
| | 277 | entry.value_ptr.* = .{ .global = try self.spv.allocGlobal() }; |
| | 278 | } |
| | 279 | } |
| | 280 | |
| | 281 | return entry.value_ptr.*; |
| 260 | } | 282 | } |
| 261 | | 283 | |
| 262 | /// Start a new SPIR-V block, Emits the label of the new block, and stores which | 284 | /// Start a new SPIR-V block, Emits the label of the new block, and stores which |
| ... | @@ -363,7 +385,7 @@ pub const DeclGen = struct { | ... | @@ -363,7 +385,7 @@ pub const DeclGen = struct { |
| 363 | // As of yet, there is no vector support in the self-hosted compiler. | 385 | // As of yet, there is no vector support in the self-hosted compiler. |
| 364 | .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}), | 386 | .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}), |
| 365 | // TODO: For which types is this the case? | 387 | // TODO: For which types is this the case? |
| 366 | else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmtDebug()}), | 388 | else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmt(self.module)}), |
| 367 | }; | 389 | }; |
| 368 | } | 390 | } |
| 369 | | 391 | |
| ... | @@ -399,7 +421,7 @@ pub const DeclGen = struct { | ... | @@ -399,7 +421,7 @@ pub const DeclGen = struct { |
| 399 | try self.spv.sections.types_globals_constants.emit( | 421 | try self.spv.sections.types_globals_constants.emit( |
| 400 | self.spv.gpa, | 422 | self.spv.gpa, |
| 401 | .OpUndef, | 423 | .OpUndef, |
| 402 | .{ .id_result_type = self.typeId(ty_ref), .id_result = result_id }, | 424 | .{ .id_result_type = self.typeId(ty_ref), .id_result = result_id } |
| 403 | ); | 425 | ); |
| 404 | return result_id; | 426 | return result_id; |
| 405 | } | 427 | } |
| ... | @@ -423,6 +445,11 @@ pub const DeclGen = struct { | ... | @@ -423,6 +445,11 @@ pub const DeclGen = struct { |
| 423 | /// If full, its flushed. | 445 | /// If full, its flushed. |
| 424 | partial_word: std.BoundedArray(u8, @sizeOf(Word)) = .{}, | 446 | partial_word: std.BoundedArray(u8, @sizeOf(Word)) = .{}, |
| 425 | | 447 | |
| | 448 | /// Utility function to get the section that instructions should be lowered to. |
| | 449 | fn section(self: *@This()) *SpvSection { |
| | 450 | return &self.dg.spv.globals.section; |
| | 451 | } |
| | 452 | |
| 426 | /// Flush the partial_word to the members. If the partial_word is not | 453 | /// Flush the partial_word to the members. If the partial_word is not |
| 427 | /// filled, this adds padding bytes (which are undefined). | 454 | /// filled, this adds padding bytes (which are undefined). |
| 428 | fn flush(self: *@This()) !void { | 455 | fn flush(self: *@This()) !void { |
| ... | @@ -438,6 +465,7 @@ pub const DeclGen = struct { | ... | @@ -438,6 +465,7 @@ pub const DeclGen = struct { |
| 438 | | 465 | |
| 439 | const word = @bitCast(Word, self.partial_word.buffer); | 466 | const word = @bitCast(Word, self.partial_word.buffer); |
| 440 | const result_id = self.dg.spv.allocId(); | 467 | const result_id = self.dg.spv.allocId(); |
| | 468 | // TODO: Integrate with caching mechanism |
| 441 | try self.dg.spv.emitConstant(self.u32_ty_id, result_id, .{ .uint32 = word }); | 469 | try self.dg.spv.emitConstant(self.u32_ty_id, result_id, .{ .uint32 = word }); |
| 442 | try self.members.append(.{ .ty = self.u32_ty_ref }); | 470 | try self.members.append(.{ .ty = self.u32_ty_ref }); |
| 443 | try self.initializers.append(result_id); | 471 | try self.initializers.append(result_id); |
| ... | @@ -523,10 +551,52 @@ pub const DeclGen = struct { | ... | @@ -523,10 +551,52 @@ pub const DeclGen = struct { |
| 523 | try self.addBytes(std.mem.asBytes(&int_bits)[0..@intCast(usize, len)]); | 551 | try self.addBytes(std.mem.asBytes(&int_bits)[0..@intCast(usize, len)]); |
| 524 | } | 552 | } |
| 525 | | 553 | |
| | 554 | fn addDeclRef(self: *@This(), ty: Type, decl_index: Decl.Index) !void { |
| | 555 | const dg = self.dg; |
| | 556 | |
| | 557 | const ty_ref = try self.dg.resolveType(ty, .indirect); |
| | 558 | const ty_id = dg.typeId(ty_ref); |
| | 559 | |
| | 560 | const decl = dg.module.declPtr(decl_index); |
| | 561 | const link = try dg.resolveDecl(decl_index); |
| | 562 | |
| | 563 | switch (decl.val.tag()) { |
| | 564 | .function => { |
| | 565 | // TODO: Properly lower function pointers. For now we are going to hack around it and |
| | 566 | // just generate an empty pointer. Function pointers are represented by usize for now, |
| | 567 | // though. |
| | 568 | try self.addInt(Type.usize, Value.initTag(.zero)); |
| | 569 | return; |
| | 570 | }, |
| | 571 | .extern_fn => unreachable, // TODO |
| | 572 | else => { |
| | 573 | const result_id = dg.spv.allocId(); |
| | 574 | log.debug("addDeclRef {s} = {}", .{ decl.name, result_id.id }); |
| | 575 | |
| | 576 | const global = dg.spv.globalPtr(link.global); |
| | 577 | try dg.spv.addGlobalDependency(link.global); |
| | 578 | // TODO: Do we need a storage class cast here? |
| | 579 | // TODO: We can probably eliminate these casts |
| | 580 | try dg.spv.globals.section.emitSpecConstantOp(dg.spv.gpa, .OpBitcast, .{ |
| | 581 | .id_result_type = ty_id, |
| | 582 | .id_result = result_id, |
| | 583 | .operand = global.result_id, |
| | 584 | }); |
| | 585 | |
| | 586 | try self.addPtr(ty_ref, result_id); |
| | 587 | }, |
| | 588 | } |
| | 589 | } |
| | 590 | |
| 526 | fn lower(self: *@This(), ty: Type, val: Value) !void { | 591 | fn lower(self: *@This(), ty: Type, val: Value) !void { |
| 527 | const target = self.dg.getTarget(); | 592 | const target = self.dg.getTarget(); |
| 528 | const dg = self.dg; | 593 | const dg = self.dg; |
| 529 | | 594 | |
| | 595 | if (val.isUndef()) { |
| | 596 | const size = ty.abiSize(target); |
| | 597 | return try self.addUndef(size); |
| | 598 | } |
| | 599 | |
| 530 | switch (ty.zigTypeTag()) { | 600 | switch (ty.zigTypeTag()) { |
| 531 | .Int => try self.addInt(ty, val), | 601 | .Int => try self.addInt(ty, val), |
| 532 | .Bool => try self.addConstBool(val.toBool()), | 602 | .Bool => try self.addConstBool(val.toBool()), |
| ... | @@ -558,22 +628,20 @@ pub const DeclGen = struct { | ... | @@ -558,22 +628,20 @@ pub const DeclGen = struct { |
| 558 | try self.addByte(@intCast(u8, sentinel.toUnsignedInt(target))); | 628 | try self.addByte(@intCast(u8, sentinel.toUnsignedInt(target))); |
| 559 | } | 629 | } |
| 560 | }, | 630 | }, |
| | 631 | .bytes => { |
| | 632 | const bytes = val.castTag(.bytes).?.data; |
| | 633 | try self.addBytes(bytes); |
| | 634 | }, |
| 561 | else => |tag| return dg.todo("indirect array constant with tag {s}", .{@tagName(tag)}), | 635 | else => |tag| return dg.todo("indirect array constant with tag {s}", .{@tagName(tag)}), |
| 562 | }, | 636 | }, |
| 563 | .Pointer => switch (val.tag()) { | 637 | .Pointer => switch (val.tag()) { |
| 564 | .decl_ref_mut => { | 638 | .decl_ref_mut => { |
| 565 | const ptr_ty_ref = try dg.resolveType(ty, .indirect); | | |
| 566 | const ptr_id = dg.spv.allocId(); | | |
| 567 | const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index; | 639 | const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index; |
| 568 | try dg.genDeclRef(ptr_ty_ref, ptr_id, decl_index); | 640 | try self.addDeclRef(ty, decl_index); |
| 569 | try self.addPtr(ptr_ty_ref, ptr_id); | | |
| 570 | }, | 641 | }, |
| 571 | .decl_ref => { | 642 | .decl_ref => { |
| 572 | const ptr_ty_ref = try dg.resolveType(ty, .indirect); | | |
| 573 | const ptr_id = dg.spv.allocId(); | | |
| 574 | const decl_index = val.castTag(.decl_ref).?.data; | 643 | const decl_index = val.castTag(.decl_ref).?.data; |
| 575 | try dg.genDeclRef(ptr_ty_ref, ptr_id, decl_index); | 644 | try self.addDeclRef(ty, decl_index); |
| 576 | try self.addPtr(ptr_ty_ref, ptr_id); | | |
| 577 | }, | 645 | }, |
| 578 | .slice => { | 646 | .slice => { |
| 579 | const slice = val.castTag(.slice).?.data; | 647 | const slice = val.castTag(.slice).?.data; |
| ... | @@ -730,22 +798,31 @@ pub const DeclGen = struct { | ... | @@ -730,22 +798,31 @@ pub const DeclGen = struct { |
| 730 | // - Underaligned pointers. These need to be packed into the word array by using a mixture of | 798 | // - Underaligned pointers. These need to be packed into the word array by using a mixture of |
| 731 | // OpSpecConstantOp instructions such as OpConvertPtrToU, OpBitcast, OpShift, etc. | 799 | // OpSpecConstantOp instructions such as OpConvertPtrToU, OpBitcast, OpShift, etc. |
| 732 | | 800 | |
| 733 | log.debug("lowerIndirectConstant: ty = {}, val = {}", .{ ty.fmtDebug(), val.fmtDebug() }); | 801 | assert(storage_class != .Generic and storage_class != .Function); |
| 734 | | 802 | |
| 735 | const constant_section = &self.spv.sections.types_globals_constants; | 803 | log.debug("lowerIndirectConstant: ty = {}, val = {}", .{ ty.fmt(self.module), val.fmtDebug() }); |
| | 804 | |
| | 805 | const section = &self.spv.globals.section; |
| 736 | | 806 | |
| 737 | const ty_ref = try self.resolveType(ty, .indirect); | 807 | const ty_ref = try self.resolveType(ty, .indirect); |
| 738 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, alignment); | 808 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, alignment); |
| 739 | | 809 | |
| | 810 | const target = self.getTarget(); |
| | 811 | |
| 740 | if (val.isUndef()) { | 812 | if (val.isUndef()) { |
| 741 | // Special case: the entire value is undefined. In this case, we can just | 813 | // Special case: the entire value is undefined. In this case, we can just |
| 742 | // generate an OpVariable with no initializer. | 814 | // generate an OpVariable with no initializer. |
| 743 | try constant_section.emit(self.spv.gpa, .OpVariable, .{ | 815 | return try section.emit(self.spv.gpa, .OpVariable, .{ |
| 744 | .id_result_type = self.typeId(ptr_ty_ref), | 816 | .id_result_type = self.typeId(ptr_ty_ref), |
| 745 | .id_result = result_id, | 817 | .id_result = result_id, |
| 746 | .storage_class = storage_class, | 818 | .storage_class = storage_class, |
| 747 | }); | 819 | }); |
| 748 | return; | 820 | } else if (ty.abiSize(target) == 0) { |
| | 821 | // Special case: if the type has no size, then return an undefined pointer. |
| | 822 | return try section.emit(self.spv.gpa, .OpUndef, .{ |
| | 823 | .id_result_type = self.typeId(ptr_ty_ref), |
| | 824 | .id_result = result_id, |
| | 825 | }); |
| 749 | } | 826 | } |
| 750 | | 827 | |
| 751 | const u32_ty_ref = try self.intType(.unsigned, 32); | 828 | const u32_ty_ref = try self.intType(.unsigned, 32); |
| ... | @@ -757,62 +834,42 @@ pub const DeclGen = struct { | ... | @@ -757,62 +834,42 @@ pub const DeclGen = struct { |
| 757 | .initializers = std.ArrayList(IdRef).init(self.gpa), | 834 | .initializers = std.ArrayList(IdRef).init(self.gpa), |
| 758 | }; | 835 | }; |
| 759 | | 836 | |
| 760 | try icl.lower(ty, val); | | |
| 761 | try icl.flush(); | | |
| 762 | | | |
| 763 | defer icl.members.deinit(); | 837 | defer icl.members.deinit(); |
| 764 | defer icl.initializers.deinit(); | 838 | defer icl.initializers.deinit(); |
| 765 | | 839 | |
| | 840 | try icl.lower(ty, val); |
| | 841 | try icl.flush(); |
| | 842 | |
| 766 | const constant_struct_ty_ref = try self.spv.simpleStructType(icl.members.items); | 843 | const constant_struct_ty_ref = try self.spv.simpleStructType(icl.members.items); |
| 767 | const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class, alignment); | 844 | const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class, alignment); |
| 768 | | 845 | |
| 769 | const constant_struct_id = self.spv.allocId(); | 846 | const constant_struct_id = self.spv.allocId(); |
| 770 | try constant_section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | 847 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ |
| 771 | .id_result_type = self.typeId(constant_struct_ty_ref), | 848 | .id_result_type = self.typeId(constant_struct_ty_ref), |
| 772 | .id_result = constant_struct_id, | 849 | .id_result = constant_struct_id, |
| 773 | .constituents = icl.initializers.items, | 850 | .constituents = icl.initializers.items, |
| 774 | }); | 851 | }); |
| 775 | | 852 | |
| 776 | const var_id = self.spv.allocId(); | 853 | const var_id = self.spv.allocId(); |
| 777 | switch (storage_class) { | 854 | try section.emit(self.spv.gpa, .OpVariable, .{ |
| 778 | .Generic => unreachable, | 855 | .id_result_type = self.typeId(ptr_constant_struct_ty_ref), |
| 779 | .Function => { | 856 | .id_result = var_id, |
| 780 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ | 857 | .storage_class = storage_class, |
| 781 | .id_result_type = self.typeId(ptr_constant_struct_ty_ref), | 858 | .initializer = constant_struct_id, |
| 782 | .id_result = var_id, | 859 | }); |
| 783 | .storage_class = storage_class, | 860 | // TODO: Set alignment of OpVariable. |
| 784 | .initializer = constant_struct_id, | 861 | // TODO: We may be able to eliminate this cast. |
| 785 | }); | 862 | try section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{ |
| 786 | // TODO: Set alignment of OpVariable. | 863 | .id_result_type = self.typeId(ptr_ty_ref), |
| 787 | | 864 | .id_result = result_id, |
| 788 | try self.func.body.emit(self.spv.gpa, .OpBitcast, .{ | 865 | .operand = var_id, |
| 789 | .id_result_type = self.typeId(ptr_ty_ref), | 866 | }); |
| 790 | .id_result = result_id, | | |
| 791 | .operand = var_id, | | |
| 792 | }); | | |
| 793 | }, | | |
| 794 | else => { | | |
| 795 | try constant_section.emit(self.spv.gpa, .OpVariable, .{ | | |
| 796 | .id_result_type = self.typeId(ptr_constant_struct_ty_ref), | | |
| 797 | .id_result = var_id, | | |
| 798 | .storage_class = storage_class, | | |
| 799 | .initializer = constant_struct_id, | | |
| 800 | }); | | |
| 801 | // TODO: Set alignment of OpVariable. | | |
| 802 | | | |
| 803 | try constant_section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{ | | |
| 804 | .id_result_type = self.typeId(ptr_ty_ref), | | |
| 805 | .id_result = result_id, | | |
| 806 | .operand = var_id, | | |
| 807 | }); | | |
| 808 | }, | | |
| 809 | } | | |
| 810 | } | 867 | } |
| 811 | | 868 | |
| 812 | /// This function generates a load for a constant in direct (ie, non-memory) representation. | 869 | /// This function generates a load for a constant in direct (ie, non-memory) representation. |
| 813 | /// When the constant is simple, it can be generated directly using OpConstant instructions. When | 870 | /// When the constant is simple, it can be generated directly using OpConstant instructions. When |
| 814 | /// the constant is more complicated however, it needs to be lowered to an indirect constant, which | 871 | /// the constant is more complicated however, it needs to be lowered to an indirect constant, which |
| 815 | /// is then loaded using OpLoad. Such values are loaded into the Function address space by default. | 872 | /// is then loaded using OpLoad. Such values are loaded into the UniformConstant storage class by default. |
| 816 | /// This function should only be called during function code generation. | 873 | /// This function should only be called during function code generation. |
| 817 | fn constant(self: *DeclGen, ty: Type, val: Value) !IdRef { | 874 | fn constant(self: *DeclGen, ty: Type, val: Value) !IdRef { |
| 818 | const target = self.getTarget(); | 875 | const target = self.getTarget(); |
| ... | @@ -846,53 +903,27 @@ pub const DeclGen = struct { | ... | @@ -846,53 +903,27 @@ pub const DeclGen = struct { |
| 846 | } | 903 | } |
| 847 | }, | 904 | }, |
| 848 | else => { | 905 | else => { |
| 849 | // The value cannot be generated directly, so generate it as an indirect function-local | 906 | // The value cannot be generated directly, so generate it as an indirect constant, |
| 850 | // constant, and then perform an OpLoad. | 907 | // and then perform an OpLoad. |
| 851 | const ptr_id = self.spv.allocId(); | | |
| 852 | const alignment = ty.abiAlignment(target); | 908 | const alignment = ty.abiAlignment(target); |
| 853 | try self.lowerIndirectConstant(ptr_id, ty, val, .Function, alignment); | 909 | const global_index = try self.spv.allocGlobal(); |
| | 910 | log.debug("constant {}", .{global_index}); |
| | 911 | const ptr_id = self.spv.beginGlobal(global_index); |
| | 912 | defer self.spv.endGlobal(); |
| | 913 | try self.lowerIndirectConstant(ptr_id, ty, val, .UniformConstant, alignment); |
| 854 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ | 914 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| 855 | .id_result_type = result_ty_id, | 915 | .id_result_type = result_ty_id, |
| 856 | .id_result = result_id, | 916 | .id_result = result_id, |
| 857 | .pointer = ptr_id, | 917 | .pointer = ptr_id, |
| 858 | }); | 918 | }); |
| 859 | // TODO: Convert bools? This logic should hook into `load`. | 919 | // TODO: Convert bools? This logic should hook into `load`. It should be a dead |
| | 920 | // path though considering .Bool is handled above. |
| 860 | }, | 921 | }, |
| 861 | } | 922 | } |
| 862 | | 923 | |
| 863 | return result_id; | 924 | return result_id; |
| 864 | } | 925 | } |
| 865 | | 926 | |
| 866 | fn genDeclRef(self: *DeclGen, result_ty_ref: SpvType.Ref, result_id: IdRef, decl_index: Decl.Index) Error!void { | | |
| 867 | // TODO: Clean up | | |
| 868 | const decl = self.module.declPtr(decl_index); | | |
| 869 | self.module.markDeclAlive(decl); | | |
| 870 | // _ = result_ty_ref; | | |
| 871 | // const decl_id = try self.constant(decl.ty, decl.val, .indirect); | | |
| 872 | // try self.variable(.global, result_id, result_ty_ref, decl_id); | | |
| 873 | const result_storage_class = self.spv.typeRefType(result_ty_ref).payload(.pointer).storage_class; | | |
| 874 | const indirect_result_id = if (result_storage_class != .CrossWorkgroup) | | |
| 875 | self.spv.allocId() | | |
| 876 | else | | |
| 877 | result_id; | | |
| 878 | | | |
| 879 | try self.lowerIndirectConstant( | | |
| 880 | indirect_result_id, | | |
| 881 | decl.ty, | | |
| 882 | decl.val, | | |
| 883 | .CrossWorkgroup, // TODO: Make this .Function if required | | |
| 884 | decl.@"align", | | |
| 885 | ); | | |
| 886 | const section = &self.spv.sections.types_globals_constants; | | |
| 887 | if (result_storage_class != .CrossWorkgroup) { | | |
| 888 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ | | |
| 889 | .id_result_type = self.typeId(result_ty_ref), | | |
| 890 | .id_result = result_id, | | |
| 891 | .pointer = indirect_result_id, | | |
| 892 | }); | | |
| 893 | } | | |
| 894 | } | | |
| 895 | | | |
| 896 | /// Turn a Zig type into a SPIR-V Type, and return its type result-id. | 927 | /// Turn a Zig type into a SPIR-V Type, and return its type result-id. |
| 897 | fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType { | 928 | fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType { |
| 898 | const type_ref = try self.resolveType(ty, .direct); | 929 | const type_ref = try self.resolveType(ty, .direct); |
| ... | @@ -996,7 +1027,7 @@ pub const DeclGen = struct { | ... | @@ -996,7 +1027,7 @@ pub const DeclGen = struct { |
| 996 | | 1027 | |
| 997 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. | 1028 | /// Turn a Zig type into a SPIR-V Type, and return a reference to it. |
| 998 | fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!SpvType.Ref { | 1029 | fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!SpvType.Ref { |
| 999 | log.debug("resolveType: ty = {}", .{ty.fmtDebug()}); | 1030 | log.debug("resolveType: ty = {}", .{ty.fmt(self.module)}); |
| 1000 | const target = self.getTarget(); | 1031 | const target = self.getTarget(); |
| 1001 | switch (ty.zigTypeTag()) { | 1032 | switch (ty.zigTypeTag()) { |
| 1002 | .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)), | 1033 | .Void, .NoReturn => return try self.spv.resolveType(SpvType.initTag(.void)), |
| ... | @@ -1042,23 +1073,30 @@ pub const DeclGen = struct { | ... | @@ -1042,23 +1073,30 @@ pub const DeclGen = struct { |
| 1042 | }; | 1073 | }; |
| 1043 | return try self.spv.arrayType(total_len, elem_ty_ref); | 1074 | return try self.spv.arrayType(total_len, elem_ty_ref); |
| 1044 | }, | 1075 | }, |
| 1045 | .Fn => { | 1076 | .Fn => switch (repr) { |
| 1046 | // TODO: Put this somewhere in Sema.zig | 1077 | .direct => { |
| 1047 | if (ty.fnIsVarArgs()) | 1078 | // TODO: Put this somewhere in Sema.zig |
| 1048 | return self.fail("VarArgs functions are unsupported for SPIR-V", .{}); | 1079 | if (ty.fnIsVarArgs()) |
| | 1080 | return self.fail("VarArgs functions are unsupported for SPIR-V", .{}); |
| 1049 | | 1081 | |
| 1050 | // TODO: Parameter passing convention etc. | 1082 | // TODO: Parameter passing convention etc. |
| 1051 | | 1083 | |
| 1052 | const param_types = try self.spv.arena.alloc(SpvType.Ref, ty.fnParamLen()); | 1084 | const param_types = try self.spv.arena.alloc(SpvType.Ref, ty.fnParamLen()); |
| 1053 | for (param_types, 0..) |*param, i| { | 1085 | for (param_types, 0..) |*param, i| { |
| 1054 | param.* = try self.resolveType(ty.fnParamType(i), .direct); | 1086 | param.* = try self.resolveType(ty.fnParamType(i), .direct); |
| 1055 | } | 1087 | } |
| 1056 | | 1088 | |
| 1057 | const return_type = try self.resolveType(ty.fnReturnType(), .direct); | 1089 | const return_type = try self.resolveType(ty.fnReturnType(), .direct); |
| 1058 | | 1090 | |
| 1059 | const payload = try self.spv.arena.create(SpvType.Payload.Function); | 1091 | const payload = try self.spv.arena.create(SpvType.Payload.Function); |
| 1060 | payload.* = .{ .return_type = return_type, .parameters = param_types }; | 1092 | payload.* = .{ .return_type = return_type, .parameters = param_types }; |
| 1061 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); | 1093 | return try self.spv.resolveType(SpvType.initPayload(&payload.base)); |
| | 1094 | }, |
| | 1095 | .indirect => { |
| | 1096 | // TODO: Represent function pointers properly. |
| | 1097 | // For now, just use an usize type. |
| | 1098 | return try self.sizeType(); |
| | 1099 | }, |
| 1062 | }, | 1100 | }, |
| 1063 | .Pointer => { | 1101 | .Pointer => { |
| 1064 | const ptr_info = ty.ptrInfo().data; | 1102 | const ptr_info = ty.ptrInfo().data; |
| ... | @@ -1196,14 +1234,16 @@ pub const DeclGen = struct { | ... | @@ -1196,14 +1234,16 @@ pub const DeclGen = struct { |
| 1196 | | 1234 | |
| 1197 | fn genDecl(self: *DeclGen) !void { | 1235 | fn genDecl(self: *DeclGen) !void { |
| 1198 | const decl = self.module.declPtr(self.decl_index); | 1236 | const decl = self.module.declPtr(self.decl_index); |
| 1199 | const result_id = try self.resolveDecl(self.decl_index); | 1237 | const link = try self.resolveDecl(self.decl_index); |
| 1200 | | 1238 | |
| 1201 | if (decl.val.castTag(.function)) |_| { | 1239 | if (decl.val.castTag(.function)) |_| { |
| | 1240 | log.debug("genDecl function {s} = {}", .{decl.name, link.func.result_id.id}); |
| | 1241 | |
| 1202 | assert(decl.ty.zigTypeTag() == .Fn); | 1242 | assert(decl.ty.zigTypeTag() == .Fn); |
| 1203 | const prototype_id = try self.resolveTypeId(decl.ty); | 1243 | const prototype_id = try self.resolveTypeId(decl.ty); |
| 1204 | try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ | 1244 | try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ |
| 1205 | .id_result_type = try self.resolveTypeId(decl.ty.fnReturnType()), | 1245 | .id_result_type = try self.resolveTypeId(decl.ty.fnReturnType()), |
| 1206 | .id_result = result_id, | 1246 | .id_result = link.func.result_id, |
| 1207 | .function_control = .{}, // TODO: We can set inline here if the type requires it. | 1247 | .function_control = .{}, // TODO: We can set inline here if the type requires it. |
| 1208 | .function_type = prototype_id, | 1248 | .function_type = prototype_id, |
| 1209 | }); | 1249 | }); |
| ... | @@ -1243,7 +1283,7 @@ pub const DeclGen = struct { | ... | @@ -1243,7 +1283,7 @@ pub const DeclGen = struct { |
| 1243 | defer self.module.gpa.free(fqn); | 1283 | defer self.module.gpa.free(fqn); |
| 1244 | | 1284 | |
| 1245 | try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{ | 1285 | try self.spv.sections.debug_names.emit(self.gpa, .OpName, .{ |
| 1246 | .target = result_id, | 1286 | .target = link.func.result_id, |
| 1247 | .name = fqn, | 1287 | .name = fqn, |
| 1248 | }); | 1288 | }); |
| 1249 | } else { | 1289 | } else { |
| ... | @@ -1264,9 +1304,13 @@ pub const DeclGen = struct { | ... | @@ -1264,9 +1304,13 @@ pub const DeclGen = struct { |
| 1264 | else => storage_class, | 1304 | else => storage_class, |
| 1265 | }; | 1305 | }; |
| 1266 | | 1306 | |
| | 1307 | const global_result_id = self.spv.beginGlobal(link.global); |
| | 1308 | defer self.spv.endGlobal(); |
| | 1309 | log.debug("genDecl {}", .{link.global}); |
| | 1310 | |
| 1267 | const var_result_id = switch (storage_class) { | 1311 | const var_result_id = switch (storage_class) { |
| 1268 | .Generic => self.spv.allocId(), | 1312 | .Generic => self.spv.allocId(), |
| 1269 | else => result_id, | 1313 | else => global_result_id, |
| 1270 | }; | 1314 | }; |
| 1271 | | 1315 | |
| 1272 | try self.lowerIndirectConstant( | 1316 | try self.lowerIndirectConstant( |
| ... | @@ -1278,12 +1322,13 @@ pub const DeclGen = struct { | ... | @@ -1278,12 +1322,13 @@ pub const DeclGen = struct { |
| 1278 | ); | 1322 | ); |
| 1279 | | 1323 | |
| 1280 | if (storage_class == .Generic) { | 1324 | if (storage_class == .Generic) { |
| 1281 | const section = &self.spv.sections.types_globals_constants; | 1325 | const section = &self.spv.globals.section; |
| 1282 | const ty_ref = try self.resolveType(decl.ty, .indirect); | 1326 | const ty_ref = try self.resolveType(decl.ty, .indirect); |
| 1283 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, decl.@"align"); | 1327 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class, decl.@"align"); |
| | 1328 | // TODO: Can we eliminate this cast? |
| 1284 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ | 1329 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1285 | .id_result_type = self.typeId(ptr_ty_ref), | 1330 | .id_result_type = self.typeId(ptr_ty_ref), |
| 1286 | .id_result = result_id, | 1331 | .id_result = global_result_id, |
| 1287 | .pointer = var_result_id, | 1332 | .pointer = var_result_id, |
| 1288 | }); | 1333 | }); |
| 1289 | } | 1334 | } |
| ... | @@ -1972,6 +2017,7 @@ pub const DeclGen = struct { | ... | @@ -1972,6 +2017,7 @@ pub const DeclGen = struct { |
| 1972 | .id_result = result_id, | 2017 | .id_result = result_id, |
| 1973 | .pointer = alloc_result_id, | 2018 | .pointer = alloc_result_id, |
| 1974 | }), | 2019 | }), |
| | 2020 | // TODO: Can we do without this cast or move it to runtime? |
| 1975 | else => try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ | 2021 | else => try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1976 | .id_result_type = self.typeId(ptr_ty_ref), | 2022 | .id_result_type = self.typeId(ptr_ty_ref), |
| 1977 | .id_result = result_id, | 2023 | .id_result = result_id, |