| ... | ... | @@ -105,9 +105,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 105 | 105 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 106 | 106 | |
| 107 | 107 | const MCValue = union(enum) { |
| 108 | | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. |
| 109 | | /// TODO Look into deleting this tag and using `dead` instead, since every use |
| 110 | | /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. |
| 108 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 |
| 109 | /// tag, etc. |
| 110 | /// |
| 111 | /// TODO Look into deleting this tag and using `dead` instead, |
| 112 | /// since every use of MCValue.none should be instead looking at |
| 113 | /// the type and noticing it is 0 bits. |
| 111 | 114 | none, |
| 112 | 115 | /// Control flow will not allow this value to be observed. |
| 113 | 116 | unreach, |
| ... | ... | @@ -116,20 +119,41 @@ const MCValue = union(enum) { |
| 116 | 119 | /// The value is undefined. |
| 117 | 120 | undef, |
| 118 | 121 | /// A pointer-sized integer that fits in a register. |
| 119 | | /// If the type is a pointer, this is the pointer address in virtual address space. |
| 122 | /// |
| 123 | /// If the type is a pointer, this is the pointer address in |
| 124 | /// virtual address space. |
| 120 | 125 | immediate: u32, |
| 121 | 126 | /// The value is in a target-specific register. |
| 122 | 127 | register: Register, |
| 128 | /// The value is a tuple { wrapped: u32, overflow: u1 } where |
| 129 | /// wrapped is stored in the register and the overflow bit is |
| 130 | /// stored in the C flag of the CPSR. |
| 131 | /// |
| 132 | /// This MCValue is only generated by a add_with_overflow or |
| 133 | /// sub_with_overflow instruction operating on u32. |
| 134 | register_c_flag: Register, |
| 135 | /// The value is a tuple { wrapped: i32, overflow: u1 } where |
| 136 | /// wrapped is stored in the register and the overflow bit is |
| 137 | /// stored in the V flag of the CPSR. |
| 138 | /// |
| 139 | /// This MCValue is only generated by a add_with_overflow or |
| 140 | /// sub_with_overflow instruction operating on i32. |
| 141 | register_v_flag: Register, |
| 123 | 142 | /// The value is in memory at a hard-coded address. |
| 124 | | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 143 | /// |
| 144 | /// If the type is a pointer, it means the pointer address is at |
| 145 | /// this memory location. |
| 125 | 146 | memory: u64, |
| 126 | 147 | /// The value is one of the stack variables. |
| 127 | | /// If the type is a pointer, it means the pointer address is in the stack at this offset. |
| 148 | /// |
| 149 | /// If the type is a pointer, it means the pointer address is in |
| 150 | /// the stack at this offset. |
| 128 | 151 | stack_offset: u32, |
| 129 | | /// The value is a pointer to one of the stack variables (payload is stack offset). |
| 152 | /// The value is a pointer to one of the stack variables (payload |
| 153 | /// is stack offset). |
| 130 | 154 | ptr_stack_offset: u32, |
| 131 | | /// The value is in the compare flags assuming an unsigned operation, |
| 132 | | /// with this operator applied on top of it. |
| 155 | /// The value is in the compare flags assuming an unsigned |
| 156 | /// operation, with this operator applied on top of it. |
| 133 | 157 | compare_flags_unsigned: math.CompareOperator, |
| 134 | 158 | /// The value is in the compare flags assuming a signed operation, |
| 135 | 159 | /// with this operator applied on top of it. |
| ... | ... | @@ -554,8 +578,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 554 | 578 | .trunc_float, |
| 555 | 579 | => try self.airUnaryMath(inst), |
| 556 | 580 | |
| 557 | | .add_with_overflow => try self.airAddWithOverflow(inst), |
| 558 | | .sub_with_overflow => try self.airSubWithOverflow(inst), |
| 581 | .add_with_overflow => try self.airOverflow(inst), |
| 582 | .sub_with_overflow => try self.airOverflow(inst), |
| 559 | 583 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| 560 | 584 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 561 | 585 | |
| ... | ... | @@ -726,6 +750,12 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 726 | 750 | .register => |reg| { |
| 727 | 751 | self.register_manager.freeReg(reg); |
| 728 | 752 | }, |
| 753 | .register_c_flag, |
| 754 | .register_v_flag, |
| 755 | => |reg| { |
| 756 | self.register_manager.freeReg(reg); |
| 757 | self.compare_flags_inst = null; |
| 758 | }, |
| 729 | 759 | .compare_flags_signed, .compare_flags_unsigned => { |
| 730 | 760 | self.compare_flags_inst = null; |
| 731 | 761 | }, |
| ... | ... | @@ -841,8 +871,16 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 841 | 871 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 842 | 872 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 843 | 873 | log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv }); |
| 874 | |
| 844 | 875 | const reg_mcv = self.getResolvedInstValue(inst); |
| 845 | | assert(reg == reg_mcv.register); |
| 876 | switch (reg_mcv) { |
| 877 | .register, |
| 878 | .register_c_flag, |
| 879 | .register_v_flag, |
| 880 | => |r| assert(r == reg), |
| 881 | else => unreachable, // not a register |
| 882 | } |
| 883 | |
| 846 | 884 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 847 | 885 | try branch.inst_table.put(self.gpa, inst, stack_mcv); |
| 848 | 886 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); |
| ... | ... | @@ -853,7 +891,14 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 853 | 891 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 854 | 892 | if (self.compare_flags_inst) |inst_to_save| { |
| 855 | 893 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 856 | | assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); |
| 894 | switch (mcv) { |
| 895 | .compare_flags_signed, |
| 896 | .compare_flags_unsigned, |
| 897 | .register_c_flag, |
| 898 | .register_v_flag, |
| 899 | => {}, |
| 900 | else => unreachable, // mcv doesn't occupy the compare flags |
| 901 | } |
| 857 | 902 | |
| 858 | 903 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); |
| 859 | 904 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); |
| ... | ... | @@ -1268,7 +1313,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1268 | 1313 | const len = try self.resolveInst(bin_op.rhs); |
| 1269 | 1314 | const len_ty = self.air.typeOf(bin_op.rhs); |
| 1270 | 1315 | |
| 1271 | | const stack_offset = try self.allocMem(inst, 8, 8); |
| 1316 | const stack_offset = try self.allocMem(inst, 8, 4); |
| 1272 | 1317 | try self.genSetStack(ptr_ty, stack_offset, ptr); |
| 1273 | 1318 | try self.genSetStack(len_ty, stack_offset - 4, len); |
| 1274 | 1319 | break :result MCValue{ .stack_offset = stack_offset }; |
| ... | ... | @@ -1306,14 +1351,71 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1306 | 1351 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1307 | 1352 | } |
| 1308 | 1353 | |
| 1309 | | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1310 | | _ = inst; |
| 1311 | | return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); |
| 1312 | | } |
| 1354 | fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1355 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1356 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1357 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1358 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1359 | const lhs = try self.resolveInst(extra.lhs); |
| 1360 | const rhs = try self.resolveInst(extra.rhs); |
| 1361 | const lhs_ty = self.air.typeOf(extra.lhs); |
| 1362 | const rhs_ty = self.air.typeOf(extra.rhs); |
| 1363 | |
| 1364 | switch (lhs_ty.zigTypeTag()) { |
| 1365 | .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), |
| 1366 | .Int => { |
| 1367 | assert(lhs_ty.eql(rhs_ty, self.target.*)); |
| 1368 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1369 | if (int_info.bits < 32) { |
| 1370 | return self.fail("TODO ARM overflow operations on integers < u32/i32", .{}); |
| 1371 | } else if (int_info.bits == 32) { |
| 1372 | // Only say yes if the operation is |
| 1373 | // commutative, i.e. we can swap both of the |
| 1374 | // operands |
| 1375 | const lhs_immediate_ok = switch (tag) { |
| 1376 | .add_with_overflow => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null, |
| 1377 | .sub_with_overflow => false, |
| 1378 | else => unreachable, |
| 1379 | }; |
| 1380 | const rhs_immediate_ok = switch (tag) { |
| 1381 | .add_with_overflow, |
| 1382 | .sub_with_overflow, |
| 1383 | => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null, |
| 1384 | else => unreachable, |
| 1385 | }; |
| 1313 | 1386 | |
| 1314 | | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1315 | | _ = inst; |
| 1316 | | return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); |
| 1387 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1388 | .add_with_overflow => .adds, |
| 1389 | .sub_with_overflow => .subs, |
| 1390 | else => unreachable, |
| 1391 | }; |
| 1392 | |
| 1393 | try self.spillCompareFlagsIfOccupied(); |
| 1394 | self.compare_flags_inst = inst; |
| 1395 | |
| 1396 | const dest = blk: { |
| 1397 | if (rhs_immediate_ok) { |
| 1398 | break :blk try self.binOpImmediate(mir_tag, null, lhs, rhs, lhs_ty, false); |
| 1399 | } else if (lhs_immediate_ok) { |
| 1400 | // swap lhs and rhs |
| 1401 | break :blk try self.binOpImmediate(mir_tag, null, rhs, lhs, rhs_ty, true); |
| 1402 | } else { |
| 1403 | break :blk try self.binOpRegister(mir_tag, null, lhs, rhs, lhs_ty, rhs_ty); |
| 1404 | } |
| 1405 | }; |
| 1406 | |
| 1407 | switch (int_info.signedness) { |
| 1408 | .unsigned => break :result MCValue{ .register_c_flag = dest.register }, |
| 1409 | .signed => break :result MCValue{ .register_v_flag = dest.register }, |
| 1410 | } |
| 1411 | } else { |
| 1412 | return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); |
| 1413 | } |
| 1414 | }, |
| 1415 | else => unreachable, |
| 1416 | } |
| 1417 | }; |
| 1418 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1317 | 1419 | } |
| 1318 | 1420 | |
| 1319 | 1421 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1424,7 +1526,6 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) |
| 1424 | 1526 | const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); |
| 1425 | 1527 | const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); |
| 1426 | 1528 | |
| 1427 | | // TODO optimization for small error unions: put into register |
| 1428 | 1529 | switch (error_union_mcv) { |
| 1429 | 1530 | .register => return self.fail("TODO errUnionPayload for registers", .{}), |
| 1430 | 1531 | .stack_argument_offset => |off| { |
| ... | ... | @@ -1791,8 +1892,11 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1791 | 1892 | .undef => unreachable, |
| 1792 | 1893 | .unreach => unreachable, |
| 1793 | 1894 | .dead => unreachable, |
| 1794 | | .compare_flags_unsigned => unreachable, |
| 1795 | | .compare_flags_signed => unreachable, |
| 1895 | .compare_flags_unsigned, |
| 1896 | .compare_flags_signed, |
| 1897 | .register_c_flag, |
| 1898 | .register_v_flag, |
| 1899 | => unreachable, // cannot hold an address |
| 1796 | 1900 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 1797 | 1901 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 1798 | 1902 | .register => |reg| { |
| ... | ... | @@ -1887,8 +1991,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 1887 | 1991 | .undef => unreachable, |
| 1888 | 1992 | .unreach => unreachable, |
| 1889 | 1993 | .dead => unreachable, |
| 1890 | | .compare_flags_unsigned => unreachable, |
| 1891 | | .compare_flags_signed => unreachable, |
| 1994 | .compare_flags_unsigned, |
| 1995 | .compare_flags_signed, |
| 1996 | .register_c_flag, |
| 1997 | .register_v_flag, |
| 1998 | => unreachable, // cannot hold an address |
| 1892 | 1999 | .immediate => |imm| { |
| 1893 | 2000 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); |
| 1894 | 2001 | }, |
| ... | ... | @@ -2043,6 +2150,50 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2043 | 2150 | .memory => |addr| { |
| 2044 | 2151 | break :result MCValue{ .memory = addr + struct_field_offset }; |
| 2045 | 2152 | }, |
| 2153 | .register_c_flag, |
| 2154 | .register_v_flag, |
| 2155 | => |reg| { |
| 2156 | switch (index) { |
| 2157 | 0 => { |
| 2158 | // get wrapped value: return register |
| 2159 | break :result MCValue{ .register = reg }; |
| 2160 | }, |
| 2161 | 1 => { |
| 2162 | // get overflow bit: set register to C flag |
| 2163 | // resp. V flag |
| 2164 | const dest_reg = try self.register_manager.allocReg(null); |
| 2165 | |
| 2166 | // mov reg, #0 |
| 2167 | _ = try self.addInst(.{ |
| 2168 | .tag = .mov, |
| 2169 | .data = .{ .rr_op = .{ |
| 2170 | .rd = dest_reg, |
| 2171 | .rn = .r0, |
| 2172 | .op = Instruction.Operand.fromU32(0).?, |
| 2173 | } }, |
| 2174 | }); |
| 2175 | |
| 2176 | // C flag: movcs reg, #1 |
| 2177 | // V flag: movvs reg, #1 |
| 2178 | _ = try self.addInst(.{ |
| 2179 | .tag = .mov, |
| 2180 | .cond = switch (mcv) { |
| 2181 | .register_c_flag => .cs, |
| 2182 | .register_v_flag => .vs, |
| 2183 | else => unreachable, |
| 2184 | }, |
| 2185 | .data = .{ .rr_op = .{ |
| 2186 | .rd = dest_reg, |
| 2187 | .rn = .r0, |
| 2188 | .op = Instruction.Operand.fromU32(1).?, |
| 2189 | } }, |
| 2190 | }); |
| 2191 | |
| 2192 | break :result MCValue{ .register = dest_reg }; |
| 2193 | }, |
| 2194 | else => unreachable, |
| 2195 | } |
| 2196 | }, |
| 2046 | 2197 | else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), |
| 2047 | 2198 | } |
| 2048 | 2199 | }; |
| ... | ... | @@ -2132,7 +2283,9 @@ fn binOpRegister( |
| 2132 | 2283 | |
| 2133 | 2284 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2134 | 2285 | .add, |
| 2286 | .adds, |
| 2135 | 2287 | .sub, |
| 2288 | .subs, |
| 2136 | 2289 | .cmp, |
| 2137 | 2290 | .@"and", |
| 2138 | 2291 | .orr, |
| ... | ... | @@ -2232,7 +2385,9 @@ fn binOpImmediate( |
| 2232 | 2385 | |
| 2233 | 2386 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2234 | 2387 | .add, |
| 2388 | .adds, |
| 2235 | 2389 | .sub, |
| 2390 | .subs, |
| 2236 | 2391 | .cmp, |
| 2237 | 2392 | .@"and", |
| 2238 | 2393 | .orr, |
| ... | ... | @@ -2814,14 +2969,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2814 | 2969 | |
| 2815 | 2970 | switch (mc_arg) { |
| 2816 | 2971 | .none => continue, |
| 2817 | | .undef => unreachable, |
| 2818 | | .immediate => unreachable, |
| 2819 | | .unreach => unreachable, |
| 2820 | | .dead => unreachable, |
| 2821 | | .memory => unreachable, |
| 2822 | | .compare_flags_signed => unreachable, |
| 2823 | | .compare_flags_unsigned => unreachable, |
| 2824 | | .ptr_stack_offset => unreachable, |
| 2825 | 2972 | .register => |reg| { |
| 2826 | 2973 | try self.register_manager.getReg(reg, null); |
| 2827 | 2974 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| ... | ... | @@ -2832,6 +2979,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2832 | 2979 | info.stack_byte_count - offset, |
| 2833 | 2980 | arg_mcv, |
| 2834 | 2981 | ), |
| 2982 | else => unreachable, |
| 2835 | 2983 | } |
| 2836 | 2984 | } |
| 2837 | 2985 | |
| ... | ... | @@ -3774,6 +3922,11 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3774 | 3922 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 3775 | 3923 | } |
| 3776 | 3924 | }, |
| 3925 | .register_c_flag, |
| 3926 | .register_v_flag, |
| 3927 | => { |
| 3928 | return self.fail("TODO implement genSetStack {}", .{mcv}); |
| 3929 | }, |
| 3777 | 3930 | .memory, |
| 3778 | 3931 | .stack_argument_offset, |
| 3779 | 3932 | .stack_offset, |
| ... | ... | @@ -4015,6 +4168,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4015 | 4168 | } }, |
| 4016 | 4169 | }); |
| 4017 | 4170 | }, |
| 4171 | .register_c_flag => unreachable, // doesn't fit into a register |
| 4172 | .register_v_flag => unreachable, // doesn't fit into a register |
| 4018 | 4173 | .memory => |addr| { |
| 4019 | 4174 | // The value is in memory at a hard-coded address. |
| 4020 | 4175 | // If the type is a pointer, it means the pointer address is at this memory location. |
| ... | ... | @@ -4149,6 +4304,11 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4149 | 4304 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 4150 | 4305 | } |
| 4151 | 4306 | }, |
| 4307 | .register_c_flag, |
| 4308 | .register_v_flag, |
| 4309 | => { |
| 4310 | return self.fail("TODO implement genSetStack {}", .{mcv}); |
| 4311 | }, |
| 4152 | 4312 | .stack_offset, |
| 4153 | 4313 | .memory, |
| 4154 | 4314 | .stack_argument_offset, |