| ... | ... | @@ -453,7 +453,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 453 | 453 | .bitcast => @panic("TODO try self.airBitCast(inst)"), |
| 454 | 454 | .block => try self.airBlock(inst), |
| 455 | 455 | .br => @panic("TODO try self.airBr(inst)"), |
| 456 | | .breakpoint => @panic("TODO try self.airBreakpoint()"), |
| 456 | .breakpoint => try self.airBreakpoint(), |
| 457 | 457 | .ret_addr => @panic("TODO try self.airRetAddr(inst)"), |
| 458 | 458 | .frame_addr => @panic("TODO try self.airFrameAddress(inst)"), |
| 459 | 459 | .fence => @panic("TODO try self.airFence()"), |
| ... | ... | @@ -476,7 +476,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 476 | 476 | .loop => @panic("TODO try self.airLoop(inst)"), |
| 477 | 477 | .not => @panic("TODO try self.airNot(inst)"), |
| 478 | 478 | .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"), |
| 479 | | .ret => @panic("TODO try self.airRet(inst)"), |
| 479 | .ret => try self.airRet(inst), |
| 480 | 480 | .ret_load => try self.airRetLoad(inst), |
| 481 | 481 | .store => try self.airStore(inst), |
| 482 | 482 | .struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"), |
| ... | ... | @@ -667,6 +667,21 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 667 | 667 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 668 | 668 | } |
| 669 | 669 | |
| 670 | fn airBreakpoint(self: *Self) !void { |
| 671 | // ta 0x01 |
| 672 | _ = try self.addInst(.{ |
| 673 | .tag = .tcc, |
| 674 | .data = .{ |
| 675 | .trap = .{ |
| 676 | .is_imm = true, |
| 677 | .cond = 0b1000, // TODO need to look into changing this into an enum |
| 678 | .rs2_or_imm = .{ .imm = 0x01 }, |
| 679 | }, |
| 680 | }, |
| 681 | }); |
| 682 | return self.finishAirBookkeeping(); |
| 683 | } |
| 684 | |
| 670 | 685 | fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void { |
| 671 | 686 | if (modifier == .always_tail) return self.fail("TODO implement tail calls for {}", .{self.target.cpu.arch}); |
| 672 | 687 | |
| ... | ... | @@ -695,10 +710,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 695 | 710 | .unreach => unreachable, |
| 696 | 711 | .dead => unreachable, |
| 697 | 712 | .memory => unreachable, |
| 698 | | .compare_flags_signed => unreachable, |
| 699 | | .compare_flags_unsigned => unreachable, |
| 700 | | .got_load => unreachable, |
| 701 | | .direct_load => unreachable, |
| 702 | 713 | .register => |reg| { |
| 703 | 714 | try self.register_manager.getReg(reg, null); |
| 704 | 715 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| ... | ... | @@ -712,6 +723,44 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 712 | 723 | } |
| 713 | 724 | } |
| 714 | 725 | |
| 726 | // Due to incremental compilation, how function calls are generated depends |
| 727 | // on linking. |
| 728 | if (self.air.value(callee)) |func_value| { |
| 729 | if (self.bin_file.tag == link.File.Elf.base_tag) { |
| 730 | if (func_value.castTag(.function)) |func_payload| { |
| 731 | const func = func_payload.data; |
| 732 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 733 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 734 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { |
| 735 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; |
| 736 | break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); |
| 737 | } else unreachable; |
| 738 | |
| 739 | try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr }); |
| 740 | |
| 741 | _ = try self.addInst(.{ |
| 742 | .tag = .jmpl, |
| 743 | .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, |
| 744 | }); |
| 745 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 746 | return self.fail("TODO implement calling extern functions", .{}); |
| 747 | } else { |
| 748 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 749 | } |
| 750 | } else @panic("TODO SPARCv9 currently does not support non-ELF binaries"); |
| 751 | } else { |
| 752 | assert(ty.zigTypeTag() == .Pointer); |
| 753 | const mcv = try self.resolveInst(callee); |
| 754 | try self.genSetReg(ty, .o7, mcv); |
| 755 | |
| 756 | _ = try self.addInst(.{ |
| 757 | .tag = .jmpl, |
| 758 | .data = .{ .branch_link_indirect = .{ .reg = .o7 } }, |
| 759 | }); |
| 760 | } |
| 761 | |
| 762 | // TODO handle return value |
| 763 | |
| 715 | 764 | return self.fail("TODO implement call for {}", .{self.target.cpu.arch}); |
| 716 | 765 | } |
| 717 | 766 | |
| ... | ... | @@ -759,8 +808,17 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { |
| 759 | 808 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 760 | 809 | } |
| 761 | 810 | |
| 811 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 812 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 813 | const operand = try self.resolveInst(un_op); |
| 814 | try self.ret(operand); |
| 815 | return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 816 | } |
| 817 | |
| 762 | 818 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 763 | | _ = inst; |
| 819 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 820 | const ptr = try self.resolveInst(un_op); |
| 821 | _ = ptr; |
| 764 | 822 | return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch}); |
| 765 | 823 | //return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 766 | 824 | } |
| ... | ... | @@ -832,6 +890,37 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 832 | 890 | return self.allocMem(inst, abi_size, abi_align); |
| 833 | 891 | } |
| 834 | 892 | |
| 893 | fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 894 | const elem_ty = self.air.typeOfIndex(inst); |
| 895 | const target = self.target.*; |
| 896 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { |
| 897 | return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)}); |
| 898 | }; |
| 899 | const abi_align = elem_ty.abiAlignment(self.target.*); |
| 900 | if (abi_align > self.stack_align) |
| 901 | self.stack_align = abi_align; |
| 902 | |
| 903 | if (reg_ok) { |
| 904 | // Make sure the type can fit in a register before we try to allocate one. |
| 905 | if (abi_size <= 8) { |
| 906 | if (self.register_manager.tryAllocReg(inst)) |reg| { |
| 907 | return MCValue{ .register = reg }; |
| 908 | } |
| 909 | } |
| 910 | } |
| 911 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 912 | return MCValue{ .stack_offset = stack_offset }; |
| 913 | } |
| 914 | |
| 915 | /// Copies a value to a register without tracking the register. The register is not considered |
| 916 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 917 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 918 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 919 | const reg = try self.register_manager.allocReg(null); |
| 920 | try self.genSetReg(ty, reg, mcv); |
| 921 | return reg; |
| 922 | } |
| 923 | |
| 835 | 924 | fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { |
| 836 | 925 | const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; |
| 837 | 926 | try table.ensureUnusedCapacity(self.gpa, additional_count); |
| ... | ... | @@ -885,37 +974,216 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 885 | 974 | self.finishAirBookkeeping(); |
| 886 | 975 | } |
| 887 | 976 | |
| 977 | fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, off: i13, abi_size: u64) !void { |
| 978 | _ = value_reg; |
| 979 | _ = addr_reg; |
| 980 | _ = off; |
| 981 | |
| 982 | switch (abi_size) { |
| 983 | 1, 2, 4, 8 => return self.fail("TODO: A.27 Load Integer", .{}), |
| 984 | 3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}), |
| 985 | else => unreachable, |
| 986 | } |
| 987 | } |
| 988 | |
| 989 | fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { |
| 990 | switch (mcv) { |
| 991 | .dead => unreachable, |
| 992 | .unreach, .none => return, // Nothing to do. |
| 993 | .undef => { |
| 994 | if (!self.wantSafety()) |
| 995 | return; // The already existing value will do just fine. |
| 996 | // Write the debug undefined value. |
| 997 | return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); |
| 998 | }, |
| 999 | .ptr_stack_offset => |off| { |
| 1000 | const simm13 = math.cast(u12, off) catch |
| 1001 | return self.fail("TODO larger stack offsets", .{}); |
| 1002 | |
| 1003 | _ = try self.addInst(.{ |
| 1004 | .tag = .add, |
| 1005 | .data = .{ |
| 1006 | .arithmetic_3op = .{ |
| 1007 | .is_imm = true, |
| 1008 | .rd = reg, |
| 1009 | .rs1 = .sp, |
| 1010 | .rs2_or_imm = .{ .imm = simm13 }, |
| 1011 | }, |
| 1012 | }, |
| 1013 | }); |
| 1014 | }, |
| 1015 | .immediate => |x| { |
| 1016 | if (x <= math.maxInt(u12)) { |
| 1017 | _ = try self.addInst(.{ |
| 1018 | .tag = .@"or", |
| 1019 | .data = .{ |
| 1020 | .arithmetic_3op = .{ |
| 1021 | .is_imm = true, |
| 1022 | .rd = reg, |
| 1023 | .rs1 = .g0, |
| 1024 | .rs2_or_imm = .{ .imm = @truncate(u12, x) }, |
| 1025 | }, |
| 1026 | }, |
| 1027 | }); |
| 1028 | } else if (x <= math.maxInt(u32)) { |
| 1029 | _ = try self.addInst(.{ |
| 1030 | .tag = .sethi, |
| 1031 | .data = .{ |
| 1032 | .sethi = .{ |
| 1033 | .rd = reg, |
| 1034 | .imm = @truncate(u22, x >> 10), |
| 1035 | }, |
| 1036 | }, |
| 1037 | }); |
| 1038 | |
| 1039 | _ = try self.addInst(.{ |
| 1040 | .tag = .@"or", |
| 1041 | .data = .{ |
| 1042 | .arithmetic_3op = .{ |
| 1043 | .is_imm = true, |
| 1044 | .rd = reg, |
| 1045 | .rs1 = reg, |
| 1046 | .rs2_or_imm = .{ .imm = @truncate(u10, x) }, |
| 1047 | }, |
| 1048 | }, |
| 1049 | }); |
| 1050 | } else if (x <= math.maxInt(u44)) { |
| 1051 | try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 12) }); |
| 1052 | |
| 1053 | _ = try self.addInst(.{ |
| 1054 | .tag = .sllx, |
| 1055 | .data = .{ |
| 1056 | .shift = .{ |
| 1057 | .is_imm = true, |
| 1058 | .width = .shift64, |
| 1059 | .rd = reg, |
| 1060 | .rs1 = reg, |
| 1061 | .rs2_or_imm = .{ .imm = 12 }, |
| 1062 | }, |
| 1063 | }, |
| 1064 | }); |
| 1065 | |
| 1066 | _ = try self.addInst(.{ |
| 1067 | .tag = .@"or", |
| 1068 | .data = .{ |
| 1069 | .arithmetic_3op = .{ |
| 1070 | .is_imm = true, |
| 1071 | .rd = reg, |
| 1072 | .rs1 = reg, |
| 1073 | .rs2_or_imm = .{ .imm = @truncate(u12, x) }, |
| 1074 | }, |
| 1075 | }, |
| 1076 | }); |
| 1077 | } else { |
| 1078 | // Need to allocate a temporary register to load 64-bit immediates. |
| 1079 | const tmp_reg = try self.register_manager.allocReg(null); |
| 1080 | |
| 1081 | try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) }); |
| 1082 | try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) }); |
| 1083 | |
| 1084 | _ = try self.addInst(.{ |
| 1085 | .tag = .sllx, |
| 1086 | .data = .{ |
| 1087 | .shift = .{ |
| 1088 | .is_imm = true, |
| 1089 | .width = .shift64, |
| 1090 | .rd = reg, |
| 1091 | .rs1 = reg, |
| 1092 | .rs2_or_imm = .{ .imm = 32 }, |
| 1093 | }, |
| 1094 | }, |
| 1095 | }); |
| 1096 | |
| 1097 | _ = try self.addInst(.{ |
| 1098 | .tag = .@"or", |
| 1099 | .data = .{ |
| 1100 | .arithmetic_3op = .{ |
| 1101 | .is_imm = false, |
| 1102 | .rd = reg, |
| 1103 | .rs1 = reg, |
| 1104 | .rs2_or_imm = .{ .rs2 = tmp_reg }, |
| 1105 | }, |
| 1106 | }, |
| 1107 | }); |
| 1108 | } |
| 1109 | }, |
| 1110 | .register => |src_reg| { |
| 1111 | // If the registers are the same, nothing to do. |
| 1112 | if (src_reg.id() == reg.id()) |
| 1113 | return; |
| 1114 | |
| 1115 | // or %g0, src, dst (aka mov src, dst) |
| 1116 | _ = try self.addInst(.{ |
| 1117 | .tag = .@"or", |
| 1118 | .data = .{ |
| 1119 | .arithmetic_3op = .{ |
| 1120 | .is_imm = false, |
| 1121 | .rd = reg, |
| 1122 | .rs1 = .g0, |
| 1123 | .rs2_or_imm = .{ .rs2 = src_reg }, |
| 1124 | }, |
| 1125 | }, |
| 1126 | }); |
| 1127 | }, |
| 1128 | .memory => |addr| { |
| 1129 | // The value is in memory at a hard-coded address. |
| 1130 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 1131 | try self.genSetReg(ty, reg, .{ .immediate = addr }); |
| 1132 | try self.genLoad(reg, reg, 0, ty.abiSize(self.target.*)); |
| 1133 | }, |
| 1134 | .stack_offset => |off| { |
| 1135 | const simm13 = math.cast(u12, off) catch |
| 1136 | return self.fail("TODO larger stack offsets", .{}); |
| 1137 | try self.genLoad(reg, .sp, simm13, ty.abiSize(self.target.*)); |
| 1138 | }, |
| 1139 | } |
| 1140 | } |
| 1141 | |
| 1142 | fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { |
| 1143 | const abi_size = ty.abiSize(self.target.*); |
| 1144 | switch (mcv) { |
| 1145 | .dead => unreachable, |
| 1146 | .unreach, .none => return, // Nothing to do. |
| 1147 | .undef => { |
| 1148 | if (!self.wantSafety()) |
| 1149 | return; // The already existing value will do just fine. |
| 1150 | // TODO Upgrade this to a memset call when we have that available. |
| 1151 | switch (ty.abiSize(self.target.*)) { |
| 1152 | 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), |
| 1153 | 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), |
| 1154 | 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), |
| 1155 | 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), |
| 1156 | else => return self.fail("TODO implement memset", .{}), |
| 1157 | } |
| 1158 | }, |
| 1159 | .immediate, |
| 1160 | .ptr_stack_offset, |
| 1161 | => { |
| 1162 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 1163 | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 1164 | }, |
| 1165 | .register => return self.fail("TODO implement storing types abi_size={}", .{abi_size}), |
| 1166 | .memory, .stack_offset => return self.fail("TODO implement memcpy", .{}), |
| 1167 | } |
| 1168 | } |
| 1169 | |
| 888 | 1170 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 889 | 1171 | if (typed_value.val.isUndef()) |
| 890 | 1172 | return MCValue{ .undef = {} }; |
| 891 | 1173 | |
| 892 | 1174 | if (typed_value.val.castTag(.decl_ref)) |payload| { |
| 893 | | return self.lowerDeclRef(typed_value, payload.data); |
| 1175 | _ = payload; |
| 1176 | return self.fail("TODO implement lowerDeclRef", .{}); |
| 1177 | // return self.lowerDeclRef(typed_value, payload.data); |
| 894 | 1178 | } |
| 895 | 1179 | if (typed_value.val.castTag(.decl_ref_mut)) |payload| { |
| 896 | | return self.lowerDeclRef(typed_value, payload.data.decl); |
| 1180 | _ = payload; |
| 1181 | return self.fail("TODO implement lowerDeclRef", .{}); |
| 1182 | // return self.lowerDeclRef(typed_value, payload.data.decl); |
| 897 | 1183 | } |
| 898 | 1184 | const target = self.target.*; |
| 899 | 1185 | |
| 900 | 1186 | switch (typed_value.ty.zigTypeTag()) { |
| 901 | | .Pointer => switch (typed_value.ty.ptrSize()) { |
| 902 | | .Slice => { |
| 903 | | return self.lowerUnnamedConst(typed_value); |
| 904 | | }, |
| 905 | | else => { |
| 906 | | switch (typed_value.val.tag()) { |
| 907 | | .int_u64 => { |
| 908 | | return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; |
| 909 | | }, |
| 910 | | .slice => { |
| 911 | | return self.lowerUnnamedConst(typed_value); |
| 912 | | }, |
| 913 | | else => { |
| 914 | | return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); |
| 915 | | }, |
| 916 | | } |
| 917 | | }, |
| 918 | | }, |
| 919 | 1187 | .Int => { |
| 920 | 1188 | const info = typed_value.ty.intInfo(self.target.*); |
| 921 | 1189 | if (info.bits <= 64) { |
| ... | ... | @@ -929,83 +1197,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 929 | 1197 | |
| 930 | 1198 | return MCValue{ .immediate = unsigned }; |
| 931 | 1199 | } else { |
| 932 | | return self.lowerUnnamedConst(typed_value); |
| 1200 | return self.fail("TODO implement int genTypedValue of > 64 bits", .{}); |
| 933 | 1201 | } |
| 934 | 1202 | }, |
| 935 | | .Bool => { |
| 936 | | return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; |
| 937 | | }, |
| 938 | 1203 | .ComptimeInt => unreachable, // semantic analysis prevents this |
| 939 | 1204 | .ComptimeFloat => unreachable, // semantic analysis prevents this |
| 940 | | .Optional => { |
| 941 | | if (typed_value.ty.isPtrLikeOptional()) { |
| 942 | | if (typed_value.val.isNull()) |
| 943 | | return MCValue{ .immediate = 0 }; |
| 944 | | |
| 945 | | var buf: Type.Payload.ElemType = undefined; |
| 946 | | return self.genTypedValue(.{ |
| 947 | | .ty = typed_value.ty.optionalChild(&buf), |
| 948 | | .val = typed_value.val, |
| 949 | | }); |
| 950 | | } else if (typed_value.ty.abiSize(self.target.*) == 1) { |
| 951 | | return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; |
| 952 | | } |
| 953 | | return self.fail("TODO non pointer optionals", .{}); |
| 954 | | }, |
| 955 | | .Enum => { |
| 956 | | if (typed_value.val.castTag(.enum_field_index)) |field_index| { |
| 957 | | switch (typed_value.ty.tag()) { |
| 958 | | .enum_simple => { |
| 959 | | return MCValue{ .immediate = field_index.data }; |
| 960 | | }, |
| 961 | | .enum_full, .enum_nonexhaustive => { |
| 962 | | const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; |
| 963 | | if (enum_full.values.count() != 0) { |
| 964 | | const tag_val = enum_full.values.keys()[field_index.data]; |
| 965 | | return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); |
| 966 | | } else { |
| 967 | | return MCValue{ .immediate = field_index.data }; |
| 968 | | } |
| 969 | | }, |
| 970 | | else => unreachable, |
| 971 | | } |
| 972 | | } else { |
| 973 | | var int_tag_buffer: Type.Payload.Bits = undefined; |
| 974 | | const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); |
| 975 | | return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); |
| 976 | | } |
| 977 | | }, |
| 978 | | .ErrorSet => { |
| 979 | | const err_name = typed_value.val.castTag(.@"error").?.data.name; |
| 980 | | const module = self.bin_file.options.module.?; |
| 981 | | const global_error_set = module.global_error_set; |
| 982 | | const error_index = global_error_set.get(err_name).?; |
| 983 | | return MCValue{ .immediate = error_index }; |
| 984 | | }, |
| 985 | | .ErrorUnion => { |
| 986 | | const error_type = typed_value.ty.errorUnionSet(); |
| 987 | | const payload_type = typed_value.ty.errorUnionPayload(); |
| 988 | | |
| 989 | | if (typed_value.val.castTag(.eu_payload)) |pl| { |
| 990 | | if (!payload_type.hasRuntimeBits()) { |
| 991 | | // We use the error type directly as the type. |
| 992 | | return MCValue{ .immediate = 0 }; |
| 993 | | } |
| 994 | | |
| 995 | | _ = pl; |
| 996 | | return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); |
| 997 | | } else { |
| 998 | | if (!payload_type.hasRuntimeBits()) { |
| 999 | | // We use the error type directly as the type. |
| 1000 | | return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); |
| 1001 | | } |
| 1002 | | |
| 1003 | | return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); |
| 1004 | | } |
| 1005 | | }, |
| 1006 | | .Struct => { |
| 1007 | | return self.lowerUnnamedConst(typed_value); |
| 1008 | | }, |
| 1009 | 1205 | else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), |
| 1010 | 1206 | } |
| 1011 | 1207 | } |
| ... | ... | @@ -1171,6 +1367,18 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 1171 | 1367 | } |
| 1172 | 1368 | } |
| 1173 | 1369 | |
| 1370 | fn ret(self: *Self, mcv: MCValue) !void { |
| 1371 | const ret_ty = self.fn_type.fnReturnType(); |
| 1372 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); |
| 1373 | |
| 1374 | // Just add space for an instruction, patch this later |
| 1375 | const index = try self.addInst(.{ |
| 1376 | .tag = .nop, |
| 1377 | .data = .{ .nop = {} }, |
| 1378 | }); |
| 1379 | try self.exitlude_jump_relocs.append(self.gpa, index); |
| 1380 | } |
| 1381 | |
| 1174 | 1382 | fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { |
| 1175 | 1383 | if (!self.liveness.operandDies(inst, op_index)) |
| 1176 | 1384 | return false; |
| ... | ... | @@ -1201,3 +1409,36 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind |
| 1201 | 1409 | |
| 1202 | 1410 | return true; |
| 1203 | 1411 | } |
| 1412 | |
| 1413 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. |
| 1414 | fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { |
| 1415 | switch (loc) { |
| 1416 | .none => return, |
| 1417 | .register => |reg| return self.genSetReg(ty, reg, val), |
| 1418 | .stack_offset => |off| return self.genSetStack(ty, off, val), |
| 1419 | .memory => { |
| 1420 | return self.fail("TODO implement setRegOrMem for memory", .{}); |
| 1421 | }, |
| 1422 | else => unreachable, |
| 1423 | } |
| 1424 | } |
| 1425 | |
| 1426 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 1427 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 1428 | log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 1429 | const reg_mcv = self.getResolvedInstValue(inst); |
| 1430 | assert(reg == reg_mcv.register); |
| 1431 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1432 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 1433 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); |
| 1434 | } |
| 1435 | |
| 1436 | /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. |
| 1437 | fn wantSafety(self: *Self) bool { |
| 1438 | return switch (self.bin_file.options.optimize_mode) { |
| 1439 | .Debug => true, |
| 1440 | .ReleaseSafe => true, |
| 1441 | .ReleaseFast => false, |
| 1442 | .ReleaseSmall => false, |
| 1443 | }; |
| 1444 | } |