| author | |
| committer | |
| log | bb859a0be7860a544aca1543cfd724435a24e380 |
| tree | 0904f7a6332232ea8970e8412063243737424365 |
| parent | 76bceb240d837d859378e01cc5a40b00cb6aacdb |
| parent | b74cd902c6abf46644329409dae493335a1708bc |
| signature |
stage2 AArch64: misc improvements13 files changed, 348 insertions(+), 106 deletions(-)
src/arch/aarch64/CodeGen.zig+137-75| ... | ... | @@ -85,6 +85,8 @@ blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, |
| 85 | 85 | register_manager: RegisterManager = .{}, |
| 86 | 86 | /// Maps offset to what is stored there. |
| 87 | 87 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 88 | /// Tracks the current instruction allocated to the compare flags | |
| 89 | compare_flags_inst: ?Air.Inst.Index = null, | |
| 88 | 90 | |
| 89 | 91 | /// Offset from the stack base, representing the end of the stack frame. |
| 90 | 92 | max_end_stack: u32 = 0, |
| ... | ... | @@ -536,12 +538,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 536 | 538 | .sub, .ptr_sub => try self.airBinOp(inst), |
| 537 | 539 | .subwrap => try self.airSubWrap(inst), |
| 538 | 540 | .sub_sat => try self.airSubSat(inst), |
| 539 | .mul => try self.airMul(inst), | |
| 541 | .mul => try self.airBinOp(inst), | |
| 540 | 542 | .mulwrap => try self.airMulWrap(inst), |
| 541 | 543 | .mul_sat => try self.airMulSat(inst), |
| 542 | 544 | .rem => try self.airRem(inst), |
| 543 | 545 | .mod => try self.airMod(inst), |
| 544 | .shl, .shl_exact => try self.airShl(inst), | |
| 546 | .shl, .shl_exact => try self.airBinOp(inst), | |
| 545 | 547 | .shl_sat => try self.airShlSat(inst), |
| 546 | 548 | .min => try self.airMin(inst), |
| 547 | 549 | .max => try self.airMax(inst), |
| ... | ... | @@ -581,7 +583,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 581 | 583 | .bit_and => try self.airBinOp(inst), |
| 582 | 584 | .bit_or => try self.airBinOp(inst), |
| 583 | 585 | .xor => try self.airBinOp(inst), |
| 584 | .shr, .shr_exact => try self.airShr(inst), | |
| 586 | .shr, .shr_exact => try self.airBinOp(inst), | |
| 585 | 587 | |
| 586 | 588 | .alloc => try self.airAlloc(inst), |
| 587 | 589 | .ret_ptr => try self.airRetPtr(inst), |
| ... | ... | @@ -722,6 +724,9 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 722 | 724 | const canon_reg = toCanonicalReg(reg); |
| 723 | 725 | self.register_manager.freeReg(canon_reg); |
| 724 | 726 | }, |
| 727 | .compare_flags_signed, .compare_flags_unsigned => { | |
| 728 | self.compare_flags_inst = null; | |
| 729 | }, | |
| 725 | 730 | else => {}, // TODO process stack allocation death |
| 726 | 731 | } |
| 727 | 732 | } |
| ... | ... | @@ -815,7 +820,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { |
| 815 | 820 | const elem_ty = self.air.typeOfIndex(inst).elemType(); |
| 816 | 821 | |
| 817 | 822 | if (!elem_ty.hasRuntimeBits()) { |
| 818 | return self.allocMem(inst, @sizeOf(usize), @alignOf(usize)); | |
| 823 | // As this stack item will never be dereferenced at runtime, | |
| 824 | // return the current stack offset | |
| 825 | return self.next_stack_offset; | |
| 819 | 826 | } |
| 820 | 827 | |
| 821 | 828 | const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { |
| ... | ... | @@ -857,6 +864,24 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 857 | 864 | try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); |
| 858 | 865 | } |
| 859 | 866 | |
| 867 | /// Save the current instruction stored in the compare flags if | |
| 868 | /// occupied | |
| 869 | fn spillCompareFlagsIfOccupied(self: *Self) !void { | |
| 870 | if (self.compare_flags_inst) |inst_to_save| { | |
| 871 | const mcv = self.getResolvedInstValue(inst_to_save); | |
| 872 | assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); | |
| 873 | ||
| 874 | const new_mcv = try self.allocRegOrMem(inst_to_save, true); | |
| 875 | try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); | |
| 876 | log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); | |
| 877 | ||
| 878 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | |
| 879 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); | |
| 880 | ||
| 881 | self.compare_flags_inst = null; | |
| 882 | } | |
| 883 | } | |
| 884 | ||
| 860 | 885 | /// Copies a value to a register without tracking the register. The register is not considered |
| 861 | 886 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 862 | 887 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| ... | ... | @@ -1156,6 +1181,15 @@ fn binOpRegister( |
| 1156 | 1181 | .bit_or, |
| 1157 | 1182 | .bool_or, |
| 1158 | 1183 | => .orr_shifted_register, |
| 1184 | .shl, | |
| 1185 | .shl_exact, | |
| 1186 | => .lsl_register, | |
| 1187 | .shr, | |
| 1188 | .shr_exact, | |
| 1189 | => switch (lhs_ty.intInfo(self.target.*).signedness) { | |
| 1190 | .signed => Mir.Inst.Tag.asr_register, | |
| 1191 | .unsigned => Mir.Inst.Tag.lsr_register, | |
| 1192 | }, | |
| 1159 | 1193 | .xor => .eor_shifted_register, |
| 1160 | 1194 | else => unreachable, |
| 1161 | 1195 | }; |
| ... | ... | @@ -1171,7 +1205,12 @@ fn binOpRegister( |
| 1171 | 1205 | .imm6 = 0, |
| 1172 | 1206 | .shift = .lsl, |
| 1173 | 1207 | } }, |
| 1174 | .mul => .{ .rrr = .{ | |
| 1208 | .mul, | |
| 1209 | .shl, | |
| 1210 | .shl_exact, | |
| 1211 | .shr, | |
| 1212 | .shr_exact, | |
| 1213 | => .{ .rrr = .{ | |
| 1175 | 1214 | .rd = dest_reg, |
| 1176 | 1215 | .rn = lhs_reg, |
| 1177 | 1216 | .rm = rhs_reg, |
| ... | ... | @@ -1263,6 +1302,15 @@ fn binOpImmediate( |
| 1263 | 1302 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1264 | 1303 | .add => .add_immediate, |
| 1265 | 1304 | .sub => .sub_immediate, |
| 1305 | .shl, | |
| 1306 | .shl_exact, | |
| 1307 | => .lsl_immediate, | |
| 1308 | .shr, | |
| 1309 | .shr_exact, | |
| 1310 | => switch (lhs_ty.intInfo(self.target.*).signedness) { | |
| 1311 | .signed => Mir.Inst.Tag.asr_immediate, | |
| 1312 | .unsigned => Mir.Inst.Tag.lsr_immediate, | |
| 1313 | }, | |
| 1266 | 1314 | else => unreachable, |
| 1267 | 1315 | }; |
| 1268 | 1316 | const mir_data: Mir.Inst.Data = switch (tag) { |
| ... | ... | @@ -1273,6 +1321,15 @@ fn binOpImmediate( |
| 1273 | 1321 | .rn = lhs_reg, |
| 1274 | 1322 | .imm12 = @intCast(u12, rhs.immediate), |
| 1275 | 1323 | } }, |
| 1324 | .shl, | |
| 1325 | .shl_exact, | |
| 1326 | .shr, | |
| 1327 | .shr_exact, | |
| 1328 | => .{ .rr_shift = .{ | |
| 1329 | .rd = dest_reg, | |
| 1330 | .rn = lhs_reg, | |
| 1331 | .shift = @intCast(u6, rhs.immediate), | |
| 1332 | } }, | |
| 1276 | 1333 | else => unreachable, |
| 1277 | 1334 | }; |
| 1278 | 1335 | |
| ... | ... | @@ -1304,7 +1361,7 @@ fn binOp( |
| 1304 | 1361 | rhs: MCValue, |
| 1305 | 1362 | lhs_ty: Type, |
| 1306 | 1363 | rhs_ty: Type, |
| 1307 | ) !MCValue { | |
| 1364 | ) InnerError!MCValue { | |
| 1308 | 1365 | switch (tag) { |
| 1309 | 1366 | // Arithmetic operations on integers and floats |
| 1310 | 1367 | .add, |
| ... | ... | @@ -1385,6 +1442,28 @@ fn binOp( |
| 1385 | 1442 | else => unreachable, |
| 1386 | 1443 | } |
| 1387 | 1444 | }, |
| 1445 | .shl, | |
| 1446 | .shr, | |
| 1447 | => { | |
| 1448 | switch (lhs_ty.zigTypeTag()) { | |
| 1449 | .Vector => return self.fail("TODO binary operations on vectors", .{}), | |
| 1450 | .Int => { | |
| 1451 | const int_info = lhs_ty.intInfo(self.target.*); | |
| 1452 | if (int_info.bits <= 64) { | |
| 1453 | const rhs_immediate_ok = rhs == .immediate; | |
| 1454 | ||
| 1455 | if (rhs_immediate_ok) { | |
| 1456 | return try self.binOpImmediate(tag, maybe_inst, lhs, rhs, lhs_ty, false); | |
| 1457 | } else { | |
| 1458 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | |
| 1459 | } | |
| 1460 | } else { | |
| 1461 | return self.fail("TODO binary operations on int with bits > 64", .{}); | |
| 1462 | } | |
| 1463 | }, | |
| 1464 | else => unreachable, | |
| 1465 | } | |
| 1466 | }, | |
| 1388 | 1467 | .bool_and, |
| 1389 | 1468 | .bool_or, |
| 1390 | 1469 | => { |
| ... | ... | @@ -1404,16 +1483,21 @@ fn binOp( |
| 1404 | 1483 | switch (lhs_ty.zigTypeTag()) { |
| 1405 | 1484 | .Pointer => { |
| 1406 | 1485 | const ptr_ty = lhs_ty; |
| 1407 | const pointee_ty = switch (ptr_ty.ptrSize()) { | |
| 1486 | const elem_ty = switch (ptr_ty.ptrSize()) { | |
| 1408 | 1487 | .One => ptr_ty.childType().childType(), // ptr to array, so get array element type |
| 1409 | 1488 | else => ptr_ty.childType(), |
| 1410 | 1489 | }; |
| 1490 | const elem_size = elem_ty.abiSize(self.target.*); | |
| 1411 | 1491 | |
| 1412 | if (pointee_ty.abiSize(self.target.*) > 1) { | |
| 1413 | return self.fail("TODO ptr_add, ptr_sub with more element sizes", .{}); | |
| 1492 | if (elem_size == 1) { | |
| 1493 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | |
| 1494 | } else { | |
| 1495 | // convert the offset into a byte offset by | |
| 1496 | // multiplying it with elem_size | |
| 1497 | const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); | |
| 1498 | const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); | |
| 1499 | return addr; | |
| 1414 | 1500 | } |
| 1415 | ||
| 1416 | return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); | |
| 1417 | 1501 | }, |
| 1418 | 1502 | else => unreachable, |
| 1419 | 1503 | } |
| ... | ... | @@ -1458,12 +1542,6 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1458 | 1542 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1459 | 1543 | } |
| 1460 | 1544 | |
| 1461 | fn airMul(self: *Self, inst: Air.Inst.Index) !void { | |
| 1462 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1463 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch}); | |
| 1464 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1465 | } | |
| 1466 | ||
| 1467 | 1545 | fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 1468 | 1546 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1469 | 1547 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}); |
| ... | ... | @@ -1514,24 +1592,12 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { |
| 1514 | 1592 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1515 | 1593 | } |
| 1516 | 1594 | |
| 1517 | fn airShl(self: *Self, inst: Air.Inst.Index) !void { | |
| 1518 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1519 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch}); | |
| 1520 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1521 | } | |
| 1522 | ||
| 1523 | 1595 | fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1524 | 1596 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1525 | 1597 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); |
| 1526 | 1598 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1527 | 1599 | } |
| 1528 | 1600 | |
| 1529 | fn airShr(self: *Self, inst: Air.Inst.Index) !void { | |
| 1530 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | |
| 1531 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch}); | |
| 1532 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | |
| 1533 | } | |
| 1534 | ||
| 1535 | 1601 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 1536 | 1602 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1537 | 1603 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); |
| ... | ... | @@ -1735,29 +1801,11 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1735 | 1801 | |
| 1736 | 1802 | switch (elem_size) { |
| 1737 | 1803 | else => { |
| 1738 | const dst_mcv = try self.allocRegOrMem(inst, true); | |
| 1739 | ||
| 1740 | const offset_mcv = try self.binOp( | |
| 1741 | .mul, | |
| 1742 | null, | |
| 1743 | index_mcv, | |
| 1744 | .{ .immediate = elem_size }, | |
| 1745 | Type.usize, | |
| 1746 | Type.usize, | |
| 1747 | ); | |
| 1748 | assert(offset_mcv == .register); // result of multiplication should always be register | |
| 1749 | self.register_manager.freezeRegs(&.{offset_mcv.register}); | |
| 1750 | ||
| 1751 | const addr_mcv = try self.binOp(.add, null, base_mcv, offset_mcv, Type.usize, Type.usize); | |
| 1804 | const dest = try self.allocRegOrMem(inst, true); | |
| 1805 | const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize); | |
| 1806 | try self.load(dest, addr, slice_ptr_field_type); | |
| 1752 | 1807 | |
| 1753 | // At this point in time, neither the base register | |
| 1754 | // nor the offset register contains any valuable data | |
| 1755 | // anymore. | |
| 1756 | self.register_manager.unfreezeRegs(&.{ base_mcv.register, offset_mcv.register }); | |
| 1757 | ||
| 1758 | try self.load(dst_mcv, addr_mcv, slice_ptr_field_type); | |
| 1759 | ||
| 1760 | break :result dst_mcv; | |
| 1808 | break :result dest; | |
| 1761 | 1809 | }, |
| 1762 | 1810 | } |
| 1763 | 1811 | }; |
| ... | ... | @@ -2360,6 +2408,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 2360 | 2408 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 2361 | 2409 | defer info.deinit(self); |
| 2362 | 2410 | |
| 2411 | // According to the Procedure Call Standard for the ARM | |
| 2412 | // Architecture, compare flags are not preserved across | |
| 2413 | // calls. Therefore, if some value is currently stored there, we | |
| 2414 | // need to save it. | |
| 2415 | // | |
| 2416 | // TODO once caller-saved registers are implemented, save them | |
| 2417 | // here too, but crucially *after* we save the compare flags as | |
| 2418 | // saving compare flags may require a new caller-saved register | |
| 2419 | try self.spillCompareFlagsIfOccupied(); | |
| 2420 | ||
| 2363 | 2421 | for (info.args) |mc_arg, arg_i| { |
| 2364 | 2422 | const arg = args[arg_i]; |
| 2365 | 2423 | const arg_ty = self.air.typeOf(arg); |
| ... | ... | @@ -2551,6 +2609,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2551 | 2609 | return self.fail("TODO cmp for types with size > 8", .{}); |
| 2552 | 2610 | } |
| 2553 | 2611 | |
| 2612 | try self.spillCompareFlagsIfOccupied(); | |
| 2613 | self.compare_flags_inst = inst; | |
| 2614 | ||
| 2554 | 2615 | const signedness: std.builtin.Signedness = blk: { |
| 2555 | 2616 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 2556 | 2617 | if (ty.zigTypeTag() != .Int) break :blk .unsigned; |
| ... | ... | @@ -2713,12 +2774,24 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2713 | 2774 | }, |
| 2714 | 2775 | }; |
| 2715 | 2776 | |
| 2777 | // If the condition dies here in this condbr instruction, process | |
| 2778 | // that death now instead of later as this has an effect on | |
| 2779 | // whether it needs to be spilled in the branches | |
| 2780 | if (self.liveness.operandDies(inst, 0)) { | |
| 2781 | const op_int = @enumToInt(pl_op.operand); | |
| 2782 | if (op_int >= Air.Inst.Ref.typed_value_map.len) { | |
| 2783 | const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); | |
| 2784 | self.processDeath(op_index); | |
| 2785 | } | |
| 2786 | } | |
| 2787 | ||
| 2716 | 2788 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 2717 | 2789 | const parent_next_stack_offset = self.next_stack_offset; |
| 2718 | 2790 | const parent_free_registers = self.register_manager.free_registers; |
| 2719 | 2791 | var parent_stack = try self.stack.clone(self.gpa); |
| 2720 | 2792 | defer parent_stack.deinit(self.gpa); |
| 2721 | 2793 | const parent_registers = self.register_manager.registers; |
| 2794 | const parent_compare_flags_inst = self.compare_flags_inst; | |
| 2722 | 2795 | |
| 2723 | 2796 | try self.branch_stack.append(.{}); |
| 2724 | 2797 | |
| ... | ... | @@ -2734,6 +2807,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2734 | 2807 | defer saved_then_branch.deinit(self.gpa); |
| 2735 | 2808 | |
| 2736 | 2809 | self.register_manager.registers = parent_registers; |
| 2810 | self.compare_flags_inst = parent_compare_flags_inst; | |
| 2737 | 2811 | |
| 2738 | 2812 | self.stack.deinit(self.gpa); |
| 2739 | 2813 | self.stack = parent_stack; |
| ... | ... | @@ -2825,7 +2899,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 2825 | 2899 | |
| 2826 | 2900 | self.branch_stack.pop().deinit(self.gpa); |
| 2827 | 2901 | |
| 2828 | return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); | |
| 2902 | // We already took care of pl_op.operand earlier, so we're going | |
| 2903 | // to pass .none here | |
| 2904 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); | |
| 2829 | 2905 | } |
| 2830 | 2906 | |
| 2831 | 2907 | fn isNull(self: *Self, operand: MCValue) !MCValue { |
| ... | ... | @@ -2843,8 +2919,6 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 2843 | 2919 | } |
| 2844 | 2920 | |
| 2845 | 2921 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 2846 | _ = operand; | |
| 2847 | ||
| 2848 | 2922 | const error_type = ty.errorUnionSet(); |
| 2849 | 2923 | const payload_type = ty.errorUnionPayload(); |
| 2850 | 2924 | |
| ... | ... | @@ -3323,7 +3397,13 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3323 | 3397 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3324 | 3398 | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 3325 | 3399 | } else { |
| 3326 | // TODO optimize the register allocation | |
| 3400 | var ptr_ty_payload: Type.Payload.ElemType = .{ | |
| 3401 | .base = .{ .tag = .single_mut_pointer }, | |
| 3402 | .data = ty, | |
| 3403 | }; | |
| 3404 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); | |
| 3405 | ||
| 3406 | // TODO call extern memcpy | |
| 3327 | 3407 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); |
| 3328 | 3408 | self.register_manager.freezeRegs(&regs); |
| 3329 | 3409 | defer self.register_manager.unfreezeRegs(&regs); |
| ... | ... | @@ -3337,16 +3417,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3337 | 3417 | switch (mcv) { |
| 3338 | 3418 | .stack_offset => |off| { |
| 3339 | 3419 | // sub src_reg, fp, #off |
| 3340 | const adj_src_offset = off + abi_size; | |
| 3341 | const src_offset = math.cast(u12, adj_src_offset) catch return self.fail("TODO load: larger stack offsets", .{}); | |
| 3342 | _ = try self.addInst(.{ | |
| 3343 | .tag = .sub_immediate, | |
| 3344 | .data = .{ .rr_imm12_sh = .{ | |
| 3345 | .rd = src_reg, | |
| 3346 | .rn = .x29, | |
| 3347 | .imm12 = src_offset, | |
| 3348 | } }, | |
| 3349 | }); | |
| 3420 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); | |
| 3350 | 3421 | }, |
| 3351 | 3422 | .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }), |
| 3352 | 3423 | .got_load, |
| ... | ... | @@ -3372,16 +3443,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3372 | 3443 | } |
| 3373 | 3444 | |
| 3374 | 3445 | // sub dst_reg, fp, #stack_offset |
| 3375 | const adj_dst_off = stack_offset + abi_size; | |
| 3376 | const dst_offset = math.cast(u12, adj_dst_off) catch return self.fail("TODO load: larger stack offsets", .{}); | |
| 3377 | _ = try self.addInst(.{ | |
| 3378 | .tag = .sub_immediate, | |
| 3379 | .data = .{ .rr_imm12_sh = .{ | |
| 3380 | .rd = dst_reg, | |
| 3381 | .rn = .x29, | |
| 3382 | .imm12 = dst_offset, | |
| 3383 | } }, | |
| 3384 | }); | |
| 3446 | try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); | |
| 3385 | 3447 | |
| 3386 | 3448 | // mov len, #abi_size |
| 3387 | 3449 | try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); |
src/arch/aarch64/Emit.zig+38-14| ... | ... | @@ -80,6 +80,14 @@ pub fn emitMir( |
| 80 | 80 | .cmp_immediate => try emit.mirAddSubtractImmediate(inst), |
| 81 | 81 | .sub_immediate => try emit.mirAddSubtractImmediate(inst), |
| 82 | 82 | |
| 83 | .asr_register => try emit.mirShiftRegister(inst), | |
| 84 | .lsl_register => try emit.mirShiftRegister(inst), | |
| 85 | .lsr_register => try emit.mirShiftRegister(inst), | |
| 86 | ||
| 87 | .asr_immediate => try emit.mirShiftImmediate(inst), | |
| 88 | .lsl_immediate => try emit.mirShiftImmediate(inst), | |
| 89 | .lsr_immediate => try emit.mirShiftImmediate(inst), | |
| 90 | ||
| 83 | 91 | .b_cond => try emit.mirConditionalBranchImmediate(inst), |
| 84 | 92 | |
| 85 | 93 | .b => try emit.mirBranch(inst), |
| ... | ... | @@ -374,20 +382,6 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { |
| 374 | 382 | return error.EmitFail; |
| 375 | 383 | } |
| 376 | 384 | |
| 377 | fn moveImmediate(emit: *Emit, reg: Register, imm64: u64) !void { | |
| 378 | try emit.writeInstruction(Instruction.movz(reg, @truncate(u16, imm64), 0)); | |
| 379 | ||
| 380 | if (imm64 > math.maxInt(u16)) { | |
| 381 | try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 16), 16)); | |
| 382 | } | |
| 383 | if (imm64 > math.maxInt(u32)) { | |
| 384 | try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 32), 32)); | |
| 385 | } | |
| 386 | if (imm64 > math.maxInt(u48)) { | |
| 387 | try emit.writeInstruction(Instruction.movk(reg, @truncate(u16, imm64 >> 48), 48)); | |
| 388 | } | |
| 389 | } | |
| 390 | ||
| 391 | 385 | fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void { |
| 392 | 386 | const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); |
| 393 | 387 | const delta_pc: usize = self.code.items.len - self.prev_di_pc; |
| ... | ... | @@ -469,6 +463,36 @@ fn mirAddSubtractImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 469 | 463 | } |
| 470 | 464 | } |
| 471 | 465 | |
| 466 | fn mirShiftRegister(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 467 | const tag = emit.mir.instructions.items(.tag)[inst]; | |
| 468 | const rrr = emit.mir.instructions.items(.data)[inst].rrr; | |
| 469 | const rd = rrr.rd; | |
| 470 | const rn = rrr.rn; | |
| 471 | const rm = rrr.rm; | |
| 472 | ||
| 473 | switch (tag) { | |
| 474 | .asr_register => try emit.writeInstruction(Instruction.asrRegister(rd, rn, rm)), | |
| 475 | .lsl_register => try emit.writeInstruction(Instruction.lslRegister(rd, rn, rm)), | |
| 476 | .lsr_register => try emit.writeInstruction(Instruction.lsrRegister(rd, rn, rm)), | |
| 477 | else => unreachable, | |
| 478 | } | |
| 479 | } | |
| 480 | ||
| 481 | fn mirShiftImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { | |
| 482 | const tag = emit.mir.instructions.items(.tag)[inst]; | |
| 483 | const rr_shift = emit.mir.instructions.items(.data)[inst].rr_shift; | |
| 484 | const rd = rr_shift.rd; | |
| 485 | const rn = rr_shift.rn; | |
| 486 | const shift = rr_shift.shift; | |
| 487 | ||
| 488 | switch (tag) { | |
| 489 | .asr_immediate => try emit.writeInstruction(Instruction.asrImmediate(rd, rn, shift)), | |
| 490 | .lsl_immediate => try emit.writeInstruction(Instruction.lslImmediate(rd, rn, shift)), | |
| 491 | .lsr_immediate => try emit.writeInstruction(Instruction.lsrImmediate(rd, rn, shift)), | |
| 492 | else => unreachable, | |
| 493 | } | |
| 494 | } | |
| 495 | ||
| 472 | 496 | fn mirConditionalBranchImmediate(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 473 | 497 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 474 | 498 | const inst_cond = emit.mir.instructions.items(.data)[inst].inst_cond; |
src/arch/aarch64/Mir.zig+21-1| ... | ... | @@ -30,6 +30,10 @@ pub const Inst = struct { |
| 30 | 30 | add_shifted_register, |
| 31 | 31 | /// Bitwise AND (shifted register) |
| 32 | 32 | and_shifted_register, |
| 33 | /// Arithmetic Shift Right (immediate) | |
| 34 | asr_immediate, | |
| 35 | /// Arithmetic Shift Right (register) | |
| 36 | asr_register, | |
| 33 | 37 | /// Branch conditionally |
| 34 | 38 | b_cond, |
| 35 | 39 | /// Branch |
| ... | ... | @@ -96,6 +100,14 @@ pub const Inst = struct { |
| 96 | 100 | ldrh_immediate, |
| 97 | 101 | /// Load Register Halfword (register) |
| 98 | 102 | ldrh_register, |
| 103 | /// Logical Shift Left (immediate) | |
| 104 | lsl_immediate, | |
| 105 | /// Logical Shift Left (register) | |
| 106 | lsl_register, | |
| 107 | /// Logical Shift Right (immediate) | |
| 108 | lsr_immediate, | |
| 109 | /// Logical Shift Right (register) | |
| 110 | lsr_register, | |
| 99 | 111 | /// Move (to/from SP) |
| 100 | 112 | mov_to_from_sp, |
| 101 | 113 | /// Move (register) |
| ... | ... | @@ -257,7 +269,15 @@ pub const Inst = struct { |
| 257 | 269 | immr: u6, |
| 258 | 270 | n: u1, |
| 259 | 271 | }, |
| 260 | /// Two registers | |
| 272 | /// Two registers and a 6-bit unsigned shift | |
| 273 | /// | |
| 274 | /// Used by e.g. lsl_immediate | |
| 275 | rr_shift: struct { | |
| 276 | rd: Register, | |
| 277 | rn: Register, | |
| 278 | shift: u6, | |
| 279 | }, | |
| 280 | /// Three registers | |
| 261 | 281 | /// |
| 262 | 282 | /// Used by e.g. mul |
| 263 | 283 | rrr: struct { |
src/arch/aarch64/bits.zig+152| ... | ... | @@ -308,6 +308,16 @@ pub const Instruction = union(enum) { |
| 308 | 308 | opc: u2, |
| 309 | 309 | sf: u1, |
| 310 | 310 | }, |
| 311 | bitfield: packed struct { | |
| 312 | rd: u5, | |
| 313 | rn: u5, | |
| 314 | imms: u6, | |
| 315 | immr: u6, | |
| 316 | n: u1, | |
| 317 | fixed: u6 = 0b100110, | |
| 318 | opc: u2, | |
| 319 | sf: u1, | |
| 320 | }, | |
| 311 | 321 | add_subtract_shifted_register: packed struct { |
| 312 | 322 | rd: u5, |
| 313 | 323 | rn: u5, |
| ... | ... | @@ -356,6 +366,16 @@ pub const Instruction = union(enum) { |
| 356 | 366 | op54: u2, |
| 357 | 367 | sf: u1, |
| 358 | 368 | }, |
| 369 | data_processing_2_source: packed struct { | |
| 370 | rd: u5, | |
| 371 | rn: u5, | |
| 372 | opcode: u6, | |
| 373 | rm: u5, | |
| 374 | fixed_1: u8 = 0b11010110, | |
| 375 | s: u1, | |
| 376 | fixed_2: u1 = 0b0, | |
| 377 | sf: u1, | |
| 378 | }, | |
| 359 | 379 | |
| 360 | 380 | pub const Condition = enum(u4) { |
| 361 | 381 | /// Integer: Equal |
| ... | ... | @@ -473,12 +493,14 @@ pub const Instruction = union(enum) { |
| 473 | 493 | .logical_shifted_register => |v| @bitCast(u32, v), |
| 474 | 494 | .add_subtract_immediate => |v| @bitCast(u32, v), |
| 475 | 495 | .logical_immediate => |v| @bitCast(u32, v), |
| 496 | .bitfield => |v| @bitCast(u32, v), | |
| 476 | 497 | .add_subtract_shifted_register => |v| @bitCast(u32, v), |
| 477 | 498 | // TODO once packed structs work, this can be refactored |
| 478 | 499 | .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), |
| 479 | 500 | .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), |
| 480 | 501 | .conditional_select => |v| @as(u32, v.rd) | @as(u32, v.rn) << 5 | @as(u32, v.op2) << 10 | @as(u32, v.cond) << 12 | @as(u32, v.rm) << 16 | @as(u32, v.fixed) << 21 | @as(u32, v.s) << 29 | @as(u32, v.op) << 30 | @as(u32, v.sf) << 31, |
| 481 | 502 | .data_processing_3_source => |v| @bitCast(u32, v), |
| 503 | .data_processing_2_source => |v| @bitCast(u32, v), | |
| 482 | 504 | }; |
| 483 | 505 | } |
| 484 | 506 | |
| ... | ... | @@ -911,6 +933,31 @@ pub const Instruction = union(enum) { |
| 911 | 933 | }; |
| 912 | 934 | } |
| 913 | 935 | |
| 936 | fn bitfield( | |
| 937 | opc: u2, | |
| 938 | n: u1, | |
| 939 | rd: Register, | |
| 940 | rn: Register, | |
| 941 | immr: u6, | |
| 942 | imms: u6, | |
| 943 | ) Instruction { | |
| 944 | return Instruction{ | |
| 945 | .bitfield = .{ | |
| 946 | .rd = rd.enc(), | |
| 947 | .rn = rn.enc(), | |
| 948 | .imms = imms, | |
| 949 | .immr = immr, | |
| 950 | .n = n, | |
| 951 | .opc = opc, | |
| 952 | .sf = switch (rd.size()) { | |
| 953 | 32 => 0b0, | |
| 954 | 64 => 0b1, | |
| 955 | else => unreachable, // unexpected register size | |
| 956 | }, | |
| 957 | }, | |
| 958 | }; | |
| 959 | } | |
| 960 | ||
| 914 | 961 | pub const AddSubtractShiftedRegisterShift = enum(u2) { lsl, lsr, asr, _ }; |
| 915 | 962 | |
| 916 | 963 | fn addSubtractShiftedRegister( |
| ... | ... | @@ -1031,6 +1078,29 @@ pub const Instruction = union(enum) { |
| 1031 | 1078 | }; |
| 1032 | 1079 | } |
| 1033 | 1080 | |
| 1081 | fn dataProcessing2Source( | |
| 1082 | s: u1, | |
| 1083 | opcode: u6, | |
| 1084 | rd: Register, | |
| 1085 | rn: Register, | |
| 1086 | rm: Register, | |
| 1087 | ) Instruction { | |
| 1088 | return Instruction{ | |
| 1089 | .data_processing_2_source = .{ | |
| 1090 | .rd = rd.enc(), | |
| 1091 | .rn = rn.enc(), | |
| 1092 | .opcode = opcode, | |
| 1093 | .rm = rm.enc(), | |
| 1094 | .s = s, | |
| 1095 | .sf = switch (rd.size()) { | |
| 1096 | 32 => 0b0, | |
| 1097 | 64 => 0b1, | |
| 1098 | else => unreachable, // unexpected register size | |
| 1099 | }, | |
| 1100 | }, | |
| 1101 | }; | |
| 1102 | } | |
| 1103 | ||
| 1034 | 1104 | // Helper functions for assembly syntax functions |
| 1035 | 1105 | |
| 1036 | 1106 | // Move wide (immediate) |
| ... | ... | @@ -1300,6 +1370,50 @@ pub const Instruction = union(enum) { |
| 1300 | 1370 | return logicalImmediate(0b11, rd, rn, imms, immr, n); |
| 1301 | 1371 | } |
| 1302 | 1372 | |
| 1373 | // Bitfield | |
| 1374 | ||
| 1375 | pub fn sbfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { | |
| 1376 | const n: u1 = switch (rd.size()) { | |
| 1377 | 32 => 0b0, | |
| 1378 | 64 => 0b1, | |
| 1379 | else => unreachable, // unexpected register size | |
| 1380 | }; | |
| 1381 | return bitfield(0b00, n, rd, rn, immr, imms); | |
| 1382 | } | |
| 1383 | ||
| 1384 | pub fn bfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { | |
| 1385 | const n: u1 = switch (rd.size()) { | |
| 1386 | 32 => 0b0, | |
| 1387 | 64 => 0b1, | |
| 1388 | else => unreachable, // unexpected register size | |
| 1389 | }; | |
| 1390 | return bitfield(0b01, n, rd, rn, immr, imms); | |
| 1391 | } | |
| 1392 | ||
| 1393 | pub fn ubfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction { | |
| 1394 | const n: u1 = switch (rd.size()) { | |
| 1395 | 32 => 0b0, | |
| 1396 | 64 => 0b1, | |
| 1397 | else => unreachable, // unexpected register size | |
| 1398 | }; | |
| 1399 | return bitfield(0b10, n, rd, rn, immr, imms); | |
| 1400 | } | |
| 1401 | ||
| 1402 | pub fn asrImmediate(rd: Register, rn: Register, shift: u6) Instruction { | |
| 1403 | const imms = @intCast(u6, rd.size() - 1); | |
| 1404 | return sbfm(rd, rn, shift, imms); | |
| 1405 | } | |
| 1406 | ||
| 1407 | pub fn lslImmediate(rd: Register, rn: Register, shift: u6) Instruction { | |
| 1408 | const size = @intCast(u6, rd.size() - 1); | |
| 1409 | return ubfm(rd, rn, size - shift + 1, size - shift); | |
| 1410 | } | |
| 1411 | ||
| 1412 | pub fn lsrImmediate(rd: Register, rn: Register, shift: u6) Instruction { | |
| 1413 | const imms = @intCast(u6, rd.size() - 1); | |
| 1414 | return ubfm(rd, rn, shift, imms); | |
| 1415 | } | |
| 1416 | ||
| 1303 | 1417 | // Add/subtract (shifted register) |
| 1304 | 1418 | |
| 1305 | 1419 | pub fn addShiftedRegister( |
| ... | ... | @@ -1393,6 +1507,24 @@ pub const Instruction = union(enum) { |
| 1393 | 1507 | pub fn mneg(rd: Register, rn: Register, rm: Register) Instruction { |
| 1394 | 1508 | return msub(rd, rn, rm, .xzr); |
| 1395 | 1509 | } |
| 1510 | ||
| 1511 | // Data processing (2 source) | |
| 1512 | ||
| 1513 | pub fn lslv(rd: Register, rn: Register, rm: Register) Instruction { | |
| 1514 | return dataProcessing2Source(0b0, 0b001000, rd, rn, rm); | |
| 1515 | } | |
| 1516 | ||
| 1517 | pub fn lsrv(rd: Register, rn: Register, rm: Register) Instruction { | |
| 1518 | return dataProcessing2Source(0b0, 0b001001, rd, rn, rm); | |
| 1519 | } | |
| 1520 | ||
| 1521 | pub fn asrv(rd: Register, rn: Register, rm: Register) Instruction { | |
| 1522 | return dataProcessing2Source(0b0, 0b001010, rd, rn, rm); | |
| 1523 | } | |
| 1524 | ||
| 1525 | pub const asrRegister = asrv; | |
| 1526 | pub const lslRegister = lslv; | |
| 1527 | pub const lsrRegister = lsrv; | |
| 1396 | 1528 | }; |
| 1397 | 1529 | |
| 1398 | 1530 | test { |
| ... | ... | @@ -1570,6 +1702,26 @@ test "serialize instructions" { |
| 1570 | 1702 | .inst = Instruction.eorImmediate(.x3, .x5, 0b000000, 0b000000, 0b1), |
| 1571 | 1703 | .expected = 0b1_10_100100_1_000000_000000_00101_00011, |
| 1572 | 1704 | }, |
| 1705 | .{ // lslv x6, x9, x10 | |
| 1706 | .inst = Instruction.lslv(.x6, .x9, .x10), | |
| 1707 | .expected = 0b1_0_0_11010110_01010_0010_00_01001_00110, | |
| 1708 | }, | |
| 1709 | .{ // lsl x4, x2, #42 | |
| 1710 | .inst = Instruction.lslImmediate(.x4, .x2, 42), | |
| 1711 | .expected = 0b1_10_100110_1_010110_010101_00010_00100, | |
| 1712 | }, | |
| 1713 | .{ // lsl x4, x2, #63 | |
| 1714 | .inst = Instruction.lslImmediate(.x4, .x2, 63), | |
| 1715 | .expected = 0b1_10_100110_1_000001_000000_00010_00100, | |
| 1716 | }, | |
| 1717 | .{ // lsr x4, x2, #42 | |
| 1718 | .inst = Instruction.lsrImmediate(.x4, .x2, 42), | |
| 1719 | .expected = 0b1_10_100110_1_101010_111111_00010_00100, | |
| 1720 | }, | |
| 1721 | .{ // lsr x4, x2, #63 | |
| 1722 | .inst = Instruction.lsrImmediate(.x4, .x2, 63), | |
| 1723 | .expected = 0b1_10_100110_1_111111_111111_00010_00100, | |
| 1724 | }, | |
| 1573 | 1725 | }; |
| 1574 | 1726 | |
| 1575 | 1727 | for (testcases) |case| { |
test/behavior/align.zig-1| ... | ... | @@ -351,7 +351,6 @@ test "read 128-bit field from default aligned struct in global memory" { |
| 351 | 351 | } |
| 352 | 352 | |
| 353 | 353 | test "struct field explicit alignment" { |
| 354 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 355 | 354 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 356 | 355 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 357 | 356 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
test/behavior/array.zig-1| ... | ... | @@ -7,7 +7,6 @@ const expectEqual = testing.expectEqual; |
| 7 | 7 | |
| 8 | 8 | test "array to slice" { |
| 9 | 9 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 10 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 11 | 10 | |
| 12 | 11 | const a: u32 align(4) = 3; |
| 13 | 12 | const b: u32 align(8) = 4; |
test/behavior/bitcast.zig-1| ... | ... | @@ -269,7 +269,6 @@ test "bitcast passed as tuple element" { |
| 269 | 269 | |
| 270 | 270 | test "triple level result location with bitcast sandwich passed as tuple element" { |
| 271 | 271 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 272 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 273 | 272 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 274 | 273 | |
| 275 | 274 | const S = struct { |
test/behavior/bugs/5474.zig-2| ... | ... | @@ -50,7 +50,6 @@ fn constant() !void { |
| 50 | 50 | |
| 51 | 51 | test "pointer-to-array constness for zero-size elements, var" { |
| 52 | 52 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 53 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 54 | 53 | |
| 55 | 54 | try mutable(); |
| 56 | 55 | comptime try mutable(); |
| ... | ... | @@ -58,7 +57,6 @@ test "pointer-to-array constness for zero-size elements, var" { |
| 58 | 57 | |
| 59 | 58 | test "pointer-to-array constness for zero-size elements, const" { |
| 60 | 59 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 61 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 62 | 60 | |
| 63 | 61 | try constant(); |
| 64 | 62 | comptime try constant(); |
test/behavior/math.zig-1| ... | ... | @@ -573,7 +573,6 @@ test "bit shift a u1" { |
| 573 | 573 | |
| 574 | 574 | test "truncating shift right" { |
| 575 | 575 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 576 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 577 | 576 | |
| 578 | 577 | try testShrTrunc(maxInt(u16)); |
| 579 | 578 | comptime try testShrTrunc(maxInt(u16)); |
test/behavior/sizeof_and_typeof.zig-2| ... | ... | @@ -187,7 +187,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" { |
| 187 | 187 | |
| 188 | 188 | test "@TypeOf() has no runtime side effects" { |
| 189 | 189 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 190 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 191 | 190 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 192 | 191 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 193 | 192 | const S = struct { |
| ... | ... | @@ -204,7 +203,6 @@ test "@TypeOf() has no runtime side effects" { |
| 204 | 203 | |
| 205 | 204 | test "branching logic inside @TypeOf" { |
| 206 | 205 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 207 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 208 | 206 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 209 | 207 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; |
| 210 | 208 | const S = struct { |
test/behavior/slice.zig-1| ... | ... | @@ -204,7 +204,6 @@ test "slicing zero length array" { |
| 204 | 204 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; |
| 205 | 205 | const y = x[0x100..]; |
| 206 | 206 | test "compile time slice of pointer to hard coded address" { |
| 207 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 208 | 207 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 209 | 208 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 210 | 209 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
test/behavior/struct.zig-3| ... | ... | @@ -927,7 +927,6 @@ test "anonymous struct literal syntax" { |
| 927 | 927 | test "fully anonymous struct" { |
| 928 | 928 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 929 | 929 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 930 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 931 | 930 | |
| 932 | 931 | const S = struct { |
| 933 | 932 | fn doTheTest() !void { |
| ... | ... | @@ -953,7 +952,6 @@ test "fully anonymous struct" { |
| 953 | 952 | test "fully anonymous list literal" { |
| 954 | 953 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 955 | 954 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 956 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 957 | 955 | |
| 958 | 956 | const S = struct { |
| 959 | 957 | fn doTheTest() !void { |
| ... | ... | @@ -983,7 +981,6 @@ test "tuple assigned to variable" { |
| 983 | 981 | |
| 984 | 982 | test "comptime struct field" { |
| 985 | 983 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 986 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 987 | 984 | |
| 988 | 985 | const T = struct { |
| 989 | 986 | a: i32, |
test/behavior/var_args.zig-4| ... | ... | @@ -15,7 +15,6 @@ fn add(args: anytype) i32 { |
| 15 | 15 | test "add arbitrary args" { |
| 16 | 16 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 17 | 17 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 18 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 19 | 18 | |
| 20 | 19 | try expect(add(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10); |
| 21 | 20 | try expect(add(.{@as(i32, 1234)}) == 1234); |
| ... | ... | @@ -27,7 +26,6 @@ fn readFirstVarArg(args: anytype) void { |
| 27 | 26 | } |
| 28 | 27 | |
| 29 | 28 | test "send void arg to var args" { |
| 30 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 31 | 29 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 32 | 30 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 33 | 31 | |
| ... | ... | @@ -90,7 +88,6 @@ fn foo2(args: anytype) bool { |
| 90 | 88 | } |
| 91 | 89 | |
| 92 | 90 | test "array of var args functions" { |
| 93 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 94 | 91 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 95 | 92 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 96 | 93 | |
| ... | ... | @@ -99,7 +96,6 @@ test "array of var args functions" { |
| 99 | 96 | } |
| 100 | 97 | |
| 101 | 98 | test "pass zero length array to var args param" { |
| 102 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 103 | 99 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 104 | 100 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 105 | 101 |