| ... | @@ -520,576 +520,6 @@ pub const DeclGen = struct { | ... | @@ -520,576 +520,6 @@ pub const DeclGen = struct { |
| 520 | } | 520 | } |
| 521 | } | 521 | } |
| 522 | | 522 | |
| 523 | const IndirectConstantLowering = struct { | | |
| 524 | const undef = 0xAA; | | |
| 525 | | | |
| 526 | dg: *DeclGen, | | |
| 527 | /// Cached reference of the u32 type. | | |
| 528 | u32_ty_ref: CacheRef, | | |
| 529 | /// The members of the resulting structure type | | |
| 530 | members: std.ArrayList(CacheRef), | | |
| 531 | /// The initializers of each of the members. | | |
| 532 | initializers: std.ArrayList(IdRef), | | |
| 533 | /// The current size of the structure. Includes | | |
| 534 | /// the bytes in partial_word. | | |
| 535 | size: u32 = 0, | | |
| 536 | /// The partially filled last constant. | | |
| 537 | /// If full, its flushed. | | |
| 538 | partial_word: std.BoundedArray(u8, @sizeOf(Word)) = .{}, | | |
| 539 | /// The declaration dependencies of the constant we are lowering. | | |
| 540 | decl_deps: std.AutoArrayHashMap(SpvModule.Decl.Index, void), | | |
| 541 | | | |
| 542 | /// Utility function to get the section that instructions should be lowered to. | | |
| 543 | fn section(self: *@This()) *SpvSection { | | |
| 544 | return &self.dg.spv.globals.section; | | |
| 545 | } | | |
| 546 | | | |
| 547 | /// Flush the partial_word to the members. If the partial_word is not | | |
| 548 | /// filled, this adds padding bytes (which are undefined). | | |
| 549 | fn flush(self: *@This()) !void { | | |
| 550 | if (self.partial_word.len == 0) { | | |
| 551 | // No need to add it there. | | |
| 552 | return; | | |
| 553 | } | | |
| 554 | | | |
| 555 | for (self.partial_word.unusedCapacitySlice()) |*unused| { | | |
| 556 | // TODO: Perhaps we should generate OpUndef for these bytes? | | |
| 557 | unused.* = undef; | | |
| 558 | } | | |
| 559 | | | |
| 560 | const word = @as(Word, @bitCast(self.partial_word.buffer)); | | |
| 561 | const result_id = try self.dg.spv.constInt(self.u32_ty_ref, word); | | |
| 562 | try self.members.append(self.u32_ty_ref); | | |
| 563 | try self.initializers.append(result_id); | | |
| 564 | | | |
| 565 | self.partial_word.len = 0; | | |
| 566 | self.size = std.mem.alignForward(u32, self.size, @sizeOf(Word)); | | |
| 567 | } | | |
| 568 | | | |
| 569 | /// Fill the buffer with undefined values until the size is aligned to `align`. | | |
| 570 | fn fillToAlign(self: *@This(), alignment: u32) !void { | | |
| 571 | const target_size = std.mem.alignForward(u32, self.size, alignment); | | |
| 572 | try self.addUndef(target_size - self.size); | | |
| 573 | } | | |
| 574 | | | |
| 575 | fn addUndef(self: *@This(), amt: u64) !void { | | |
| 576 | for (0..@as(usize, @intCast(amt))) |_| { | | |
| 577 | try self.addByte(undef); | | |
| 578 | } | | |
| 579 | } | | |
| 580 | | | |
| 581 | /// Add a single byte of data to the constant. | | |
| 582 | fn addByte(self: *@This(), data: u8) !void { | | |
| 583 | self.partial_word.append(data) catch { | | |
| 584 | try self.flush(); | | |
| 585 | self.partial_word.append(data) catch unreachable; | | |
| 586 | }; | | |
| 587 | self.size += 1; | | |
| 588 | } | | |
| 589 | | | |
| 590 | /// Add many bytes of data to the constnat. | | |
| 591 | fn addBytes(self: *@This(), data: []const u8) !void { | | |
| 592 | // TODO: Improve performance by adding in bulk, or something? | | |
| 593 | for (data) |byte| { | | |
| 594 | try self.addByte(byte); | | |
| 595 | } | | |
| 596 | } | | |
| 597 | | | |
| 598 | fn addPtr(self: *@This(), ptr_ty_ref: CacheRef, ptr_id: IdRef) !void { | | |
| 599 | // TODO: Double check pointer sizes here. | | |
| 600 | // shared pointers might be u32... | | |
| 601 | const target = self.dg.getTarget(); | | |
| 602 | const width = @divExact(target.ptrBitWidth(), 8); | | |
| 603 | if (self.size % width != 0) { | | |
| 604 | return self.dg.todo("misaligned pointer constants", .{}); | | |
| 605 | } | | |
| 606 | try self.members.append(ptr_ty_ref); | | |
| 607 | try self.initializers.append(ptr_id); | | |
| 608 | self.size += width; | | |
| 609 | } | | |
| 610 | | | |
| 611 | fn addNullPtr(self: *@This(), ptr_ty_ref: CacheRef) !void { | | |
| 612 | const result_id = try self.dg.spv.constNull(ptr_ty_ref); | | |
| 613 | try self.addPtr(ptr_ty_ref, result_id); | | |
| 614 | } | | |
| 615 | | | |
| 616 | fn addConstInt(self: *@This(), comptime T: type, value: T) !void { | | |
| 617 | if (@bitSizeOf(T) % 8 != 0) { | | |
| 618 | @compileError("todo: non byte aligned int constants"); | | |
| 619 | } | | |
| 620 | | | |
| 621 | // TODO: Swap endianness if the compiler is big endian. | | |
| 622 | try self.addBytes(std.mem.asBytes(&value)); | | |
| 623 | } | | |
| 624 | | | |
| 625 | fn addConstBool(self: *@This(), value: bool) !void { | | |
| 626 | try self.addByte(@intFromBool(value)); // TODO: Keep in sync with something? | | |
| 627 | } | | |
| 628 | | | |
| 629 | fn addInt(self: *@This(), ty: Type, val: Value) !void { | | |
| 630 | const mod = self.dg.module; | | |
| 631 | const len = ty.abiSize(mod); | | |
| 632 | if (val.isUndef(mod)) { | | |
| 633 | try self.addUndef(len); | | |
| 634 | return; | | |
| 635 | } | | |
| 636 | | | |
| 637 | const int_info = ty.intInfo(mod); | | |
| 638 | const int_bits = switch (int_info.signedness) { | | |
| 639 | .signed => @as(u64, @bitCast(val.toSignedInt(mod))), | | |
| 640 | .unsigned => val.toUnsignedInt(mod), | | |
| 641 | }; | | |
| 642 | | | |
| 643 | // TODO: Swap endianess if the compiler is big endian. | | |
| 644 | try self.addBytes(std.mem.asBytes(&int_bits)[0..@as(usize, @intCast(len))]); | | |
| 645 | } | | |
| 646 | | | |
| 647 | fn addFloat(self: *@This(), ty: Type, val: Value) !void { | | |
| 648 | const mod = self.dg.module; | | |
| 649 | const target = self.dg.getTarget(); | | |
| 650 | const len = ty.abiSize(mod); | | |
| 651 | | | |
| 652 | // TODO: Swap endianess if the compiler is big endian. | | |
| 653 | switch (ty.floatBits(target)) { | | |
| 654 | 16 => { | | |
| 655 | const float_bits = val.toFloat(f16, mod); | | |
| 656 | try self.addBytes(std.mem.asBytes(&float_bits)[0..@as(usize, @intCast(len))]); | | |
| 657 | }, | | |
| 658 | 32 => { | | |
| 659 | const float_bits = val.toFloat(f32, mod); | | |
| 660 | try self.addBytes(std.mem.asBytes(&float_bits)[0..@as(usize, @intCast(len))]); | | |
| 661 | }, | | |
| 662 | 64 => { | | |
| 663 | const float_bits = val.toFloat(f64, mod); | | |
| 664 | try self.addBytes(std.mem.asBytes(&float_bits)[0..@as(usize, @intCast(len))]); | | |
| 665 | }, | | |
| 666 | else => unreachable, | | |
| 667 | } | | |
| 668 | } | | |
| 669 | | | |
| 670 | fn addDeclRef(self: *@This(), ty: Type, decl_index: Decl.Index) !void { | | |
| 671 | const dg = self.dg; | | |
| 672 | const mod = dg.module; | | |
| 673 | | | |
| 674 | const ty_ref = try self.dg.resolveType(ty, .indirect); | | |
| 675 | const ty_id = dg.typeId(ty_ref); | | |
| 676 | | | |
| 677 | const decl = dg.module.declPtr(decl_index); | | |
| 678 | const spv_decl_index = try dg.resolveDecl(decl_index); | | |
| 679 | | | |
| 680 | switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { | | |
| 681 | .func => { | | |
| 682 | // TODO: Properly lower function pointers. For now we are going to hack around it and | | |
| 683 | // just generate an empty pointer. Function pointers are represented by usize for now, | | |
| 684 | // though. | | |
| 685 | try self.addInt(Type.usize, Value.zero_usize); | | |
| 686 | // TODO: Add dependency | | |
| 687 | return; | | |
| 688 | }, | | |
| 689 | .extern_func => unreachable, // TODO | | |
| 690 | else => { | | |
| 691 | const result_id = dg.spv.allocId(); | | |
| 692 | | | |
| 693 | try self.decl_deps.put(spv_decl_index, {}); | | |
| 694 | | | |
| 695 | const decl_id = dg.spv.declPtr(spv_decl_index).result_id; | | |
| 696 | // TODO: Do we need a storage class cast here? | | |
| 697 | // TODO: We can probably eliminate these casts | | |
| 698 | try dg.spv.globals.section.emitSpecConstantOp(dg.spv.gpa, .OpBitcast, .{ | | |
| 699 | .id_result_type = ty_id, | | |
| 700 | .id_result = result_id, | | |
| 701 | .operand = decl_id, | | |
| 702 | }); | | |
| 703 | | | |
| 704 | try self.addPtr(ty_ref, result_id); | | |
| 705 | }, | | |
| 706 | } | | |
| 707 | } | | |
| 708 | | | |
| 709 | fn lower(self: *@This(), ty: Type, arg_val: Value) !void { | | |
| 710 | const dg = self.dg; | | |
| 711 | const mod = dg.module; | | |
| 712 | const ip = &mod.intern_pool; | | |
| 713 | | | |
| 714 | var val = arg_val; | | |
| 715 | switch (ip.indexToKey(val.toIntern())) { | | |
| 716 | .runtime_value => |rt| val = rt.val.toValue(), | | |
| 717 | else => {}, | | |
| 718 | } | | |
| 719 | | | |
| 720 | if (val.isUndefDeep(mod)) { | | |
| 721 | const size = ty.abiSize(mod); | | |
| 722 | return try self.addUndef(size); | | |
| 723 | } | | |
| 724 | | | |
| 725 | switch (ip.indexToKey(val.toIntern())) { | | |
| 726 | .int_type, | | |
| 727 | .ptr_type, | | |
| 728 | .array_type, | | |
| 729 | .vector_type, | | |
| 730 | .opt_type, | | |
| 731 | .anyframe_type, | | |
| 732 | .error_union_type, | | |
| 733 | .simple_type, | | |
| 734 | .struct_type, | | |
| 735 | .anon_struct_type, | | |
| 736 | .union_type, | | |
| 737 | .opaque_type, | | |
| 738 | .enum_type, | | |
| 739 | .func_type, | | |
| 740 | .error_set_type, | | |
| 741 | .inferred_error_set_type, | | |
| 742 | => unreachable, // types, not values | | |
| 743 | | | |
| 744 | .undef, .runtime_value => unreachable, // handled above | | |
| 745 | .simple_value => |simple_value| switch (simple_value) { | | |
| 746 | .undefined, | | |
| 747 | .void, | | |
| 748 | .null, | | |
| 749 | .empty_struct, | | |
| 750 | .@"unreachable", | | |
| 751 | .generic_poison, | | |
| 752 | => unreachable, // non-runtime values | | |
| 753 | .false, .true => try self.addConstBool(val.toBool()), | | |
| 754 | }, | | |
| 755 | .variable, | | |
| 756 | .extern_func, | | |
| 757 | .func, | | |
| 758 | .enum_literal, | | |
| 759 | .empty_enum_value, | | |
| 760 | => unreachable, // non-runtime values | | |
| 761 | .int => try self.addInt(ty, val), | | |
| 762 | .err => |err| { | | |
| 763 | const int = try mod.getErrorValue(err.name); | | |
| 764 | try self.addConstInt(u16, @as(u16, @intCast(int))); | | |
| 765 | }, | | |
| 766 | .error_union => |error_union| { | | |
| 767 | const err_ty = switch (error_union.val) { | | |
| 768 | .err_name => ty.errorUnionSet(mod), | | |
| 769 | .payload => Type.err_int, | | |
| 770 | }; | | |
| 771 | const err_val = switch (error_union.val) { | | |
| 772 | .err_name => |err_name| (try mod.intern(.{ .err = .{ | | |
| 773 | .ty = ty.errorUnionSet(mod).toIntern(), | | |
| 774 | .name = err_name, | | |
| 775 | } })).toValue(), | | |
| 776 | .payload => try mod.intValue(Type.err_int, 0), | | |
| 777 | }; | | |
| 778 | const payload_ty = ty.errorUnionPayload(mod); | | |
| 779 | const eu_layout = dg.errorUnionLayout(payload_ty); | | |
| 780 | if (!eu_layout.payload_has_bits) { | | |
| 781 | // We use the error type directly as the type. | | |
| 782 | try self.lower(err_ty, err_val); | | |
| 783 | return; | | |
| 784 | } | | |
| 785 | | | |
| 786 | const payload_size = payload_ty.abiSize(mod); | | |
| 787 | const error_size = err_ty.abiSize(mod); | | |
| 788 | const ty_size = ty.abiSize(mod); | | |
| 789 | const padding = ty_size - payload_size - error_size; | | |
| 790 | | | |
| 791 | const payload_val = switch (error_union.val) { | | |
| 792 | .err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }), | | |
| 793 | .payload => |payload| payload, | | |
| 794 | }.toValue(); | | |
| 795 | | | |
| 796 | if (eu_layout.error_first) { | | |
| 797 | try self.lower(err_ty, err_val); | | |
| 798 | try self.lower(payload_ty, payload_val); | | |
| 799 | } else { | | |
| 800 | try self.lower(payload_ty, payload_val); | | |
| 801 | try self.lower(err_ty, err_val); | | |
| 802 | } | | |
| 803 | | | |
| 804 | try self.addUndef(padding); | | |
| 805 | }, | | |
| 806 | .enum_tag => { | | |
| 807 | const int_val = try val.intFromEnum(ty, mod); | | |
| 808 | | | |
| 809 | const int_ty = ty.intTagType(mod); | | |
| 810 | | | |
| 811 | try self.lower(int_ty, int_val); | | |
| 812 | }, | | |
| 813 | .float => try self.addFloat(ty, val), | | |
| 814 | .ptr => |ptr| { | | |
| 815 | const ptr_ty = switch (ptr.len) { | | |
| 816 | .none => ty, | | |
| 817 | else => ty.slicePtrFieldType(mod), | | |
| 818 | }; | | |
| 819 | switch (ptr.addr) { | | |
| 820 | .decl => |decl| try self.addDeclRef(ptr_ty, decl), | | |
| 821 | .mut_decl => |mut_decl| try self.addDeclRef(ptr_ty, mut_decl.decl), | | |
| 822 | .int => |int| try self.addInt(Type.usize, int.toValue()), | | |
| 823 | else => |tag| return dg.todo("pointer value of type {s}", .{@tagName(tag)}), | | |
| 824 | } | | |
| 825 | if (ptr.len != .none) { | | |
| 826 | try self.addInt(Type.usize, ptr.len.toValue()); | | |
| 827 | } | | |
| 828 | }, | | |
| 829 | .opt => { | | |
| 830 | const payload_ty = ty.optionalChild(mod); | | |
| 831 | const payload_val = val.optionalValue(mod); | | |
| 832 | const abi_size = ty.abiSize(mod); | | |
| 833 | | | |
| 834 | if (!payload_ty.hasRuntimeBits(mod)) { | | |
| 835 | try self.addConstBool(payload_val != null); | | |
| 836 | return; | | |
| 837 | } else if (ty.optionalReprIsPayload(mod)) { | | |
| 838 | // Optional representation is a nullable pointer or slice. | | |
| 839 | if (payload_val) |pl_val| { | | |
| 840 | try self.lower(payload_ty, pl_val); | | |
| 841 | } else { | | |
| 842 | const ptr_ty_ref = try dg.resolveType(ty, .indirect); | | |
| 843 | try self.addNullPtr(ptr_ty_ref); | | |
| 844 | } | | |
| 845 | return; | | |
| 846 | } | | |
| 847 | | | |
| 848 | // Optional representation is a structure. | | |
| 849 | // { Payload, Bool } | | |
| 850 | | | |
| 851 | // Subtract 1 for @sizeOf(bool). | | |
| 852 | // TODO: Make this not hardcoded. | | |
| 853 | const payload_size = payload_ty.abiSize(mod); | | |
| 854 | const padding = abi_size - payload_size - 1; | | |
| 855 | | | |
| 856 | if (payload_val) |pl_val| { | | |
| 857 | try self.lower(payload_ty, pl_val); | | |
| 858 | } else { | | |
| 859 | try self.addUndef(payload_size); | | |
| 860 | } | | |
| 861 | try self.addConstBool(payload_val != null); | | |
| 862 | try self.addUndef(padding); | | |
| 863 | }, | | |
| 864 | .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) { | | |
| 865 | .array_type => |array_type| { | | |
| 866 | const elem_ty = array_type.child.toType(); | | |
| 867 | switch (aggregate.storage) { | | |
| 868 | .bytes => |bytes| try self.addBytes(bytes), | | |
| 869 | .elems, .repeated_elem => { | | |
| 870 | for (0..@as(usize, @intCast(array_type.len))) |i| { | | |
| 871 | try self.lower(elem_ty, switch (aggregate.storage) { | | |
| 872 | .bytes => unreachable, | | |
| 873 | .elems => |elem_vals| elem_vals[@as(usize, @intCast(i))].toValue(), | | |
| 874 | .repeated_elem => |elem_val| elem_val.toValue(), | | |
| 875 | }); | | |
| 876 | } | | |
| 877 | }, | | |
| 878 | } | | |
| 879 | if (array_type.sentinel != .none) { | | |
| 880 | try self.lower(elem_ty, array_type.sentinel.toValue()); | | |
| 881 | } | | |
| 882 | }, | | |
| 883 | .vector_type => return dg.todo("indirect constant of type {}", .{ty.fmt(mod)}), | | |
| 884 | .struct_type => { | | |
| 885 | const struct_type = mod.typeToStruct(ty).?; | | |
| 886 | if (struct_type.layout == .Packed) { | | |
| 887 | return dg.todo("packed struct constants", .{}); | | |
| 888 | } | | |
| 889 | | | |
| 890 | // TODO iterate with runtime order instead so that struct field | | |
| 891 | // reordering can be enabled for this backend. | | |
| 892 | const struct_begin = self.size; | | |
| 893 | for (struct_type.field_types.get(ip), 0..) |field_ty, i_usize| { | | |
| 894 | const i: u32 = @intCast(i_usize); | | |
| 895 | if (struct_type.fieldIsComptime(ip, i)) continue; | | |
| 896 | if (!field_ty.toType().hasRuntimeBits(mod)) continue; | | |
| 897 | | | |
| 898 | const field_val = switch (aggregate.storage) { | | |
| 899 | .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ | | |
| 900 | .ty = field_ty, | | |
| 901 | .storage = .{ .u64 = bytes[i] }, | | |
| 902 | } }), | | |
| 903 | .elems => |elems| elems[i], | | |
| 904 | .repeated_elem => |elem| elem, | | |
| 905 | }; | | |
| 906 | try self.lower(field_ty.toType(), field_val.toValue()); | | |
| 907 | | | |
| 908 | // Add padding if required. | | |
| 909 | // TODO: Add to type generation as well? | | |
| 910 | const unpadded_field_end = self.size - struct_begin; | | |
| 911 | const padded_field_end = ty.structFieldOffset(i + 1, mod); | | |
| 912 | const padding = padded_field_end - unpadded_field_end; | | |
| 913 | try self.addUndef(padding); | | |
| 914 | } | | |
| 915 | }, | | |
| 916 | .anon_struct_type => unreachable, // TODO | | |
| 917 | else => unreachable, | | |
| 918 | }, | | |
| 919 | .un => |un| { | | |
| 920 | const layout = ty.unionGetLayout(mod); | | |
| 921 | | | |
| 922 | if (layout.payload_size == 0) { | | |
| 923 | return try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue()); | | |
| 924 | } | | |
| 925 | | | |
| 926 | const union_obj = mod.typeToUnion(ty).?; | | |
| 927 | if (union_obj.getLayout(ip) == .Packed) { | | |
| 928 | return dg.todo("packed union constants", .{}); | | |
| 929 | } | | |
| 930 | | | |
| 931 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module).?; | | |
| 932 | const active_field_ty = union_obj.field_types.get(ip)[active_field].toType(); | | |
| 933 | | | |
| 934 | const has_tag = layout.tag_size != 0; | | |
| 935 | const tag_first = layout.tag_align.compare(.gte, layout.payload_align); | | |
| 936 | | | |
| 937 | if (has_tag and tag_first) { | | |
| 938 | try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue()); | | |
| 939 | } | | |
| 940 | | | |
| 941 | const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: { | | |
| 942 | try self.lower(active_field_ty, un.val.toValue()); | | |
| 943 | break :blk active_field_ty.abiSize(mod); | | |
| 944 | } else 0; | | |
| 945 | | | |
| 946 | const payload_padding_len = layout.payload_size - active_field_size; | | |
| 947 | try self.addUndef(payload_padding_len); | | |
| 948 | | | |
| 949 | if (has_tag and !tag_first) { | | |
| 950 | try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue()); | | |
| 951 | } | | |
| 952 | | | |
| 953 | try self.addUndef(layout.padding); | | |
| 954 | }, | | |
| 955 | .memoized_call => unreachable, | | |
| 956 | } | | |
| 957 | } | | |
| 958 | }; | | |
| 959 | | | |
| 960 | /// Returns a pointer to `val`. The value is placed directly | | |
| 961 | /// into the storage class `storage_class`, and this is also where the resulting | | |
| 962 | /// pointer points to. Note: result is not necessarily an OpVariable instruction! | | |
| 963 | fn lowerIndirectConstant( | | |
| 964 | self: *DeclGen, | | |
| 965 | spv_decl_index: SpvModule.Decl.Index, | | |
| 966 | ty: Type, | | |
| 967 | val: Value, | | |
| 968 | storage_class: StorageClass, | | |
| 969 | cast_to_generic: bool, | | |
| 970 | alignment: u32, | | |
| 971 | ) Error!void { | | |
| 972 | // To simplify constant generation, we're going to generate constants as a word-array, and | | |
| 973 | // pointer cast the result to the right type. | | |
| 974 | // This means that the final constant will be generated as follows: | | |
| 975 | // %T = OpTypeStruct %members... | | |
| 976 | // %P = OpTypePointer %T | | |
| 977 | // %U = OpTypePointer %ty | | |
| 978 | // %1 = OpConstantComposite %T %initializers... | | |
| 979 | // %2 = OpVariable %P %1 | | |
| 980 | // %result_id = OpSpecConstantOp OpBitcast %U %2 | | |
| 981 | // | | |
| 982 | // The members consist of two options: | | |
| 983 | // - Literal values: ints, strings, etc. These are generated as u32 words. | | |
| 984 | // - Relocations, such as pointers: These are generated by embedding the pointer into the | | |
| 985 | // to-be-generated structure. There are two options here, depending on the alignment of the | | |
| 986 | // pointer value itself (not the alignment of the pointee). | | |
| 987 | // - Natively or over-aligned values. These can just be generated directly. | | |
| 988 | // - Underaligned pointers. These need to be packed into the word array by using a mixture of | | |
| 989 | // OpSpecConstantOp instructions such as OpConvertPtrToU, OpBitcast, OpShift, etc. | | |
| 990 | | | |
| 991 | // TODO: Implement alignment here. | | |
| 992 | // This is hoing to require some hacks because there is no real way to | | |
| 993 | // set an OpVariable's alignment. | | |
| 994 | _ = alignment; | | |
| 995 | | | |
| 996 | assert(storage_class != .Generic and storage_class != .Function); | | |
| 997 | | | |
| 998 | const var_id = self.spv.allocId(); | | |
| 999 | log.debug("lowerIndirectConstant: id = {}, index = {}, ty = {}, val = {}", .{ var_id.id, @intFromEnum(spv_decl_index), ty.fmt(self.module), val.fmtDebug() }); | | |
| 1000 | | | |
| 1001 | const section = &self.spv.globals.section; | | |
| 1002 | | | |
| 1003 | const ty_ref = try self.resolveType(ty, .indirect); | | |
| 1004 | const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class); | | |
| 1005 | | | |
| 1006 | // const target = self.getTarget(); | | |
| 1007 | | | |
| 1008 | // TODO: Fix the resulting global linking for these paths. | | |
| 1009 | // if (val.isUndef(mod)) { | | |
| 1010 | // // Special case: the entire value is undefined. In this case, we can just | | |
| 1011 | // // generate an OpVariable with no initializer. | | |
| 1012 | // return try section.emit(self.spv.gpa, .OpVariable, .{ | | |
| 1013 | // .id_result_type = self.typeId(ptr_ty_ref), | | |
| 1014 | // .id_result = result_id, | | |
| 1015 | // .storage_class = storage_class, | | |
| 1016 | // }); | | |
| 1017 | // } else if (ty.abiSize(mod) == 0) { | | |
| 1018 | // // Special case: if the type has no size, then return an undefined pointer. | | |
| 1019 | // return try section.emit(self.spv.gpa, .OpUndef, .{ | | |
| 1020 | // .id_result_type = self.typeId(ptr_ty_ref), | | |
| 1021 | // .id_result = result_id, | | |
| 1022 | // }); | | |
| 1023 | // } | | |
| 1024 | | | |
| 1025 | // TODO: Capture the above stuff in here as well... | | |
| 1026 | const begin_inst = self.spv.beginGlobal(); | | |
| 1027 | | | |
| 1028 | const u32_ty_ref = try self.intType(.unsigned, 32); | | |
| 1029 | var icl = IndirectConstantLowering{ | | |
| 1030 | .dg = self, | | |
| 1031 | .u32_ty_ref = u32_ty_ref, | | |
| 1032 | .members = std.ArrayList(CacheRef).init(self.gpa), | | |
| 1033 | .initializers = std.ArrayList(IdRef).init(self.gpa), | | |
| 1034 | .decl_deps = std.AutoArrayHashMap(SpvModule.Decl.Index, void).init(self.gpa), | | |
| 1035 | }; | | |
| 1036 | | | |
| 1037 | defer icl.members.deinit(); | | |
| 1038 | defer icl.initializers.deinit(); | | |
| 1039 | defer icl.decl_deps.deinit(); | | |
| 1040 | | | |
| 1041 | try icl.lower(ty, val); | | |
| 1042 | try icl.flush(); | | |
| 1043 | | | |
| 1044 | const constant_struct_ty_ref = try self.spv.resolve(.{ .struct_type = .{ | | |
| 1045 | .member_types = icl.members.items, | | |
| 1046 | } }); | | |
| 1047 | const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class); | | |
| 1048 | | | |
| 1049 | const constant_struct_id = self.spv.allocId(); | | |
| 1050 | try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{ | | |
| 1051 | .id_result_type = self.typeId(constant_struct_ty_ref), | | |
| 1052 | .id_result = constant_struct_id, | | |
| 1053 | .constituents = icl.initializers.items, | | |
| 1054 | }); | | |
| 1055 | | | |
| 1056 | self.spv.globalPtr(spv_decl_index).?.result_id = var_id; | | |
| 1057 | try section.emit(self.spv.gpa, .OpVariable, .{ | | |
| 1058 | .id_result_type = self.typeId(ptr_constant_struct_ty_ref), | | |
| 1059 | .id_result = var_id, | | |
| 1060 | .storage_class = storage_class, | | |
| 1061 | .initializer = constant_struct_id, | | |
| 1062 | }); | | |
| 1063 | // TODO: Set alignment of OpVariable. | | |
| 1064 | // TODO: We may be able to eliminate these casts. | | |
| 1065 | | | |
| 1066 | const const_ptr_id = try self.makePointerConstant(section, ptr_constant_struct_ty_ref, var_id); | | |
| 1067 | const result_id = self.spv.declPtr(spv_decl_index).result_id; | | |
| 1068 | | | |
| 1069 | const bitcast_result_id = if (cast_to_generic) | | |
| 1070 | self.spv.allocId() | | |
| 1071 | else | | |
| 1072 | result_id; | | |
| 1073 | | | |
| 1074 | try section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{ | | |
| 1075 | .id_result_type = self.typeId(ptr_ty_ref), | | |
| 1076 | .id_result = bitcast_result_id, | | |
| 1077 | .operand = const_ptr_id, | | |
| 1078 | }); | | |
| 1079 | | | |
| 1080 | if (cast_to_generic) { | | |
| 1081 | const generic_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic); | | |
| 1082 | try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{ | | |
| 1083 | .id_result_type = self.typeId(generic_ptr_ty_ref), | | |
| 1084 | .id_result = result_id, | | |
| 1085 | .pointer = bitcast_result_id, | | |
| 1086 | }); | | |
| 1087 | } | | |
| 1088 | | | |
| 1089 | try self.spv.declareDeclDeps(spv_decl_index, icl.decl_deps.keys()); | | |
| 1090 | self.spv.endGlobal(spv_decl_index, begin_inst); | | |
| 1091 | } | | |
| 1092 | | | |
| 1093 | /// This function generates a load for a constant in direct (ie, non-memory) representation. | 523 | /// This function generates a load for a constant in direct (ie, non-memory) representation. |
| 1094 | /// When the constant is simple, it can be generated directly using OpConstant instructions. | 524 | /// When the constant is simple, it can be generated directly using OpConstant instructions. |
| 1095 | /// When the constant is more complicated however, it needs to be constructed using multiple values. This | 525 | /// When the constant is more complicated however, it needs to be constructed using multiple values. This |
| ... | @@ -2104,8 +1534,6 @@ pub const DeclGen = struct { | ... | @@ -2104,8 +1534,6 @@ pub const DeclGen = struct { |
| 2104 | .id_result = decl_id, | 1534 | .id_result = decl_id, |
| 2105 | .storage_class = actual_storage_class, | 1535 | .storage_class = actual_storage_class, |
| 2106 | }); | 1536 | }); |
| 2107 | // TODO: We should be able to get rid of this by now... | | |
| 2108 | self.spv.endGlobal(spv_decl_index, begin); | | |
| 2109 | | 1537 | |
| 2110 | // Now emit the instructions that initialize the variable. | 1538 | // Now emit the instructions that initialize the variable. |
| 2111 | const initializer_id = self.spv.allocId(); | 1539 | const initializer_id = self.spv.allocId(); |
| ... | @@ -2127,6 +1555,9 @@ pub const DeclGen = struct { | ... | @@ -2127,6 +1555,9 @@ pub const DeclGen = struct { |
| 2127 | .object = val_id, | 1555 | .object = val_id, |
| 2128 | }); | 1556 | }); |
| 2129 | | 1557 | |
| | 1558 | // TODO: We should be able to get rid of this by now... |
| | 1559 | self.spv.endGlobal(spv_decl_index, begin); |
| | 1560 | |
| 2130 | try self.func.body.emit(self.spv.gpa, .OpReturn, {}); | 1561 | try self.func.body.emit(self.spv.gpa, .OpReturn, {}); |
| 2131 | try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); | 1562 | try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| 2132 | try self.spv.addFunction(spv_decl_index, self.func); | 1563 | try self.spv.addFunction(spv_decl_index, self.func); |