| ... | ... | @@ -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 | 523 | /// This function generates a load for a constant in direct (ie, non-memory) representation. |
| 1094 | 524 | /// When the constant is simple, it can be generated directly using OpConstant instructions. |
| 1095 | 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 | 1534 | .id_result = decl_id, |
| 2105 | 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 | 1538 | // Now emit the instructions that initialize the variable. |
| 2111 | 1539 | const initializer_id = self.spv.allocId(); |
| ... | ... | @@ -2127,6 +1555,9 @@ pub const DeclGen = struct { |
| 2127 | 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 | 1561 | try self.func.body.emit(self.spv.gpa, .OpReturn, {}); |
| 2131 | 1562 | try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| 2132 | 1563 | try self.spv.addFunction(spv_decl_index, self.func); |