authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-18 15:29:19-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-18 15:29:19-04:00
log8660661af4626c4bd589e6c6331a6ac786143f12
tree0c771125a45adab3a9d80cbc6b101cad2bed0fd1
parente252f92b9947e82f2473a37a74d5f9dc278a1c1d
parent9031cc54f27b4483c62ab904dd94063a9499c842
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11660 from ziglang/stage2-behavior

stage2: bug fixes towards more behavior tests passing

20 files changed, 623 insertions(+), 388 deletions(-)

lib/std/Thread.zig+6-5
......@@ -893,6 +893,7 @@ const LinuxThreadImpl = struct {
893893 };
894894
895895 fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl {
896 const page_size = std.mem.page_size;
896897 const Args = @TypeOf(args);
897898 const Instance = struct {
898899 fn_args: Args,
......@@ -915,11 +916,11 @@ const LinuxThreadImpl = struct {
915916 var instance_offset: usize = undefined;
916917
917918 const map_bytes = blk: {
918 var bytes: usize = std.mem.page_size;
919 var bytes: usize = page_size;
919920 guard_offset = bytes;
920921
921 bytes += std.math.max(std.mem.page_size, config.stack_size);
922 bytes = std.mem.alignForward(bytes, std.mem.page_size);
922 bytes += std.math.max(page_size, config.stack_size);
923 bytes = std.mem.alignForward(bytes, page_size);
923924 stack_offset = bytes;
924925
925926 bytes = std.mem.alignForward(bytes, linux.tls.tls_image.alloc_align);
......@@ -930,7 +931,7 @@ const LinuxThreadImpl = struct {
930931 instance_offset = bytes;
931932 bytes += @sizeOf(Instance);
932933
933 bytes = std.mem.alignForward(bytes, std.mem.page_size);
934 bytes = std.mem.alignForward(bytes, page_size);
934935 break :blk bytes;
935936 };
936937
......@@ -954,7 +955,7 @@ const LinuxThreadImpl = struct {
954955
955956 // map everything but the guard page as read/write
956957 os.mprotect(
957 mapped[guard_offset..],
958 @alignCast(page_size, mapped[guard_offset..]),
958959 os.PROT.READ | os.PROT.WRITE,
959960 ) catch |err| switch (err) {
960961 error.AccessDenied => unreachable,
lib/std/heap.zig+1-1
......@@ -345,7 +345,7 @@ const PageAllocator = struct {
345345 // Unmap extra pages
346346 const aligned_buffer_len = alloc_len - drop_len;
347347 if (aligned_buffer_len > aligned_len) {
348 os.munmap(result_ptr[aligned_len..aligned_buffer_len]);
348 os.munmap(@alignCast(mem.page_size, result_ptr[aligned_len..aligned_buffer_len]));
349349 }
350350
351351 const new_hint = @alignCast(mem.page_size, result_ptr + aligned_len);
src/Air.zig+4-4
......@@ -113,13 +113,13 @@ pub const Inst = struct {
113113 /// The offset is in element type units, not bytes.
114114 /// Wrapping is undefined behavior.
115115 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
116 /// Uses the `bin_op` field.
116 /// Uses the `ty_pl` field. Payload is `Bin`.
117117 ptr_add,
118118 /// Subtract an offset from a pointer, returning a new pointer.
119119 /// The offset is in element type units, not bytes.
120120 /// Wrapping is undefined behavior.
121121 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
122 /// Uses the `bin_op` field.
122 /// Uses the `ty_pl` field. Payload is `Bin`.
123123 ptr_sub,
124124 /// Given two operands which can be floats, integers, or vectors, returns the
125125 /// greater of the operands. For vectors it operates element-wise.
......@@ -916,8 +916,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
916916 .bit_and,
917917 .bit_or,
918918 .xor,
919 .ptr_add,
920 .ptr_sub,
921919 .shr,
922920 .shr_exact,
923921 .shl,
......@@ -989,6 +987,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
989987 .sub_with_overflow,
990988 .mul_with_overflow,
991989 .shl_with_overflow,
990 .ptr_add,
991 .ptr_sub,
992992 => return air.getRefType(datas[inst].ty_pl.ty),
993993
994994 .not,
src/Liveness.zig+16-15
......@@ -312,8 +312,6 @@ fn analyzeInst(
312312 .div_exact,
313313 .rem,
314314 .mod,
315 .ptr_add,
316 .ptr_sub,
317315 .bit_and,
318316 .bit_or,
319317 .xor,
......@@ -441,6 +439,21 @@ fn analyzeInst(
441439 return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
442440 },
443441
442 .add_with_overflow,
443 .sub_with_overflow,
444 .mul_with_overflow,
445 .shl_with_overflow,
446 .ptr_add,
447 .ptr_sub,
448 .ptr_elem_ptr,
449 .slice_elem_ptr,
450 .slice,
451 => {
452 const ty_pl = inst_datas[inst].ty_pl;
453 const extra = a.air.extraData(Air.Bin, ty_pl.payload).data;
454 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
455 },
456
444457 .dbg_var_ptr,
445458 .dbg_var_val,
446459 => {
......@@ -529,10 +542,6 @@ fn analyzeInst(
529542 const extra = a.air.extraData(Air.FieldParentPtr, inst_datas[inst].ty_pl.payload).data;
530543 return trackOperands(a, new_set, inst, main_tomb, .{ extra.field_ptr, .none, .none });
531544 },
532 .ptr_elem_ptr, .slice_elem_ptr, .slice => {
533 const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data;
534 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
535 },
536545 .cmpxchg_strong, .cmpxchg_weak => {
537546 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;
538547 return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value });
......@@ -558,15 +567,7 @@ fn analyzeInst(
558567 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
559568 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
560569 },
561 .add_with_overflow,
562 .sub_with_overflow,
563 .mul_with_overflow,
564 .shl_with_overflow,
565 => {
566 const ty_pl = inst_datas[inst].ty_pl;
567 const extra = a.air.extraData(Air.Bin, ty_pl.payload).data;
568 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
569 },
570
570571 .br => {
571572 const br = inst_datas[inst].br;
572573 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
src/Sema.zig+123-42
......@@ -7020,22 +7020,25 @@ fn intCast(
70207020 operand_src: LazySrcLoc,
70217021 runtime_safety: bool,
70227022) CompileError!Air.Inst.Ref {
7023 // TODO: Add support for vectors
7024 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
7025 _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
7023 const operand_ty = sema.typeOf(operand);
7024 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src);
7025 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
70267026
70277027 if (try sema.isComptimeKnown(block, operand_src, operand)) {
70287028 return sema.coerce(block, dest_ty, operand, operand_src);
7029 } else if (dest_is_comptime_int) {
7029 } else if (dest_scalar_ty.zigTypeTag() == .ComptimeInt) {
70307030 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
70317031 }
70327032
7033 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
7034 const is_vector = dest_ty.zigTypeTag() == .Vector;
7035
70337036 if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| {
70347037 // requirement: intCast(u0, input) iff input == 0
70357038 if (runtime_safety and block.wantSafety()) {
70367039 try sema.requireRuntimeBlock(block, operand_src);
70377040 const target = sema.mod.getTarget();
7038 const wanted_info = dest_ty.intInfo(target);
7041 const wanted_info = dest_scalar_ty.intInfo(target);
70397042 const wanted_bits = wanted_info.bits;
70407043
70417044 if (wanted_bits == 0) {
......@@ -7051,9 +7054,8 @@ fn intCast(
70517054 try sema.requireRuntimeBlock(block, operand_src);
70527055 if (runtime_safety and block.wantSafety()) {
70537056 const target = sema.mod.getTarget();
7054 const operand_ty = sema.typeOf(operand);
7055 const actual_info = operand_ty.intInfo(target);
7056 const wanted_info = dest_ty.intInfo(target);
7057 const actual_info = operand_scalar_ty.intInfo(target);
7058 const wanted_info = dest_scalar_ty.intInfo(target);
70577059 const actual_bits = actual_info.bits;
70587060 const wanted_bits = wanted_info.bits;
70597061 const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed);
......@@ -7062,7 +7064,11 @@ fn intCast(
70627064 // range shrinkage
70637065 // requirement: int value fits into target type
70647066 if (wanted_value_bits < actual_value_bits) {
7065 const dest_max_val = try dest_ty.maxInt(sema.arena, target);
7067 const dest_max_val_scalar = try dest_scalar_ty.maxInt(sema.arena, target);
7068 const dest_max_val = if (is_vector)
7069 try Value.Tag.repeated.create(sema.arena, dest_max_val_scalar)
7070 else
7071 dest_max_val_scalar;
70667072 const dest_max = try sema.addConstant(operand_ty, dest_max_val);
70677073 const diff = try block.addBinOp(.subwrap, dest_max, operand);
70687074
......@@ -7080,19 +7086,59 @@ fn intCast(
70807086 } else dest_max_val;
70817087 const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val);
70827088
7083 const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);
7084 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7089 const ok = if (is_vector) ok: {
7090 const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte, try sema.addType(operand_ty));
7091 const all_in_range = try block.addInst(.{
7092 .tag = .reduce,
7093 .data = .{ .reduce = .{
7094 .operand = is_in_range,
7095 .operation = .And,
7096 } },
7097 });
7098 break :ok all_in_range;
7099 } else ok: {
7100 const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range);
7101 break :ok is_in_range;
7102 };
7103 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
70857104 } else {
7086 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
7087 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7105 const ok = if (is_vector) ok: {
7106 const is_in_range = try block.addCmpVector(diff, dest_max, .lte, try sema.addType(operand_ty));
7107 const all_in_range = try block.addInst(.{
7108 .tag = .reduce,
7109 .data = .{ .reduce = .{
7110 .operand = is_in_range,
7111 .operation = .And,
7112 } },
7113 });
7114 break :ok all_in_range;
7115 } else ok: {
7116 const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
7117 break :ok is_in_range;
7118 };
7119 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
70887120 }
7089 }
7090 // no shrinkage, yes sign loss
7091 // requirement: signed to unsigned >= 0
7092 else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
7093 const zero_inst = try sema.addConstant(operand_ty, Value.zero);
7094 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
7095 try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data);
7121 } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
7122 // no shrinkage, yes sign loss
7123 // requirement: signed to unsigned >= 0
7124 const ok = if (is_vector) ok: {
7125 const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
7126 const zero_inst = try sema.addConstant(operand_ty, zero_val);
7127 const is_in_range = try block.addCmpVector(operand, zero_inst, .lte, try sema.addType(operand_ty));
7128 const all_in_range = try block.addInst(.{
7129 .tag = .reduce,
7130 .data = .{ .reduce = .{
7131 .operand = is_in_range,
7132 .operation = .And,
7133 } },
7134 });
7135 break :ok all_in_range;
7136 } else ok: {
7137 const zero_inst = try sema.addConstant(operand_ty, Value.zero);
7138 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
7139 break :ok is_in_range;
7140 };
7141 try sema.addSafetyCheck(block, ok, .cast_truncated_data);
70967142 }
70977143 }
70987144 return block.addTyOp(.intcast, dest_ty, operand);
......@@ -10610,28 +10656,55 @@ fn analyzePtrArithmetic(
1061010656 // TODO if the operand is comptime-known to be negative, or is a negative int,
1061110657 // coerce to isize instead of usize.
1061210658 const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);
10613 // TODO adjust the return type according to alignment and other factors
1061410659 const target = sema.mod.getTarget();
10615 const runtime_src = rs: {
10616 if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| {
10617 if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| {
10618 const ptr_ty = sema.typeOf(ptr);
10619 const new_ptr_ty = ptr_ty; // TODO modify alignment
10660 const opt_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, ptr);
10661 const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset);
10662 const ptr_ty = sema.typeOf(ptr);
10663 const ptr_info = ptr_ty.ptrInfo().data;
10664 const elem_ty = if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Array)
10665 ptr_info.pointee_type.childType()
10666 else
10667 ptr_info.pointee_type;
10668
10669 const new_ptr_ty = t: {
10670 // Calculate the new pointer alignment.
10671 if (ptr_info.@"align" == 0) {
10672 // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
10673 break :t ptr_ty;
10674 }
10675 // If the addend is not a comptime-known value we can still count on
10676 // it being a multiple of the type size.
10677 const elem_size = elem_ty.abiSize(target);
10678 const addend = if (opt_off_val) |off_val| a: {
10679 const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(target));
10680 break :a elem_size * off_int;
10681 } else elem_size;
10682
10683 // The resulting pointer is aligned to the lcd between the offset (an
10684 // arbitrary number) and the alignment factor (always a power of two,
10685 // non zero).
10686 const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, addend | ptr_info.@"align"));
10687
10688 break :t try Type.ptr(sema.arena, sema.mod, .{
10689 .pointee_type = ptr_info.pointee_type,
10690 .sentinel = ptr_info.sentinel,
10691 .@"align" = new_align,
10692 .@"addrspace" = ptr_info.@"addrspace",
10693 .mutable = ptr_info.mutable,
10694 .@"allowzero" = ptr_info.@"allowzero",
10695 .@"volatile" = ptr_info.@"volatile",
10696 .size = ptr_info.size,
10697 });
10698 };
1062010699
10621 if (ptr_val.isUndef() or offset_val.isUndef()) {
10622 return sema.addConstUndef(new_ptr_ty);
10623 }
10700 const runtime_src = rs: {
10701 if (opt_ptr_val) |ptr_val| {
10702 if (opt_off_val) |offset_val| {
10703 if (ptr_val.isUndef()) return sema.addConstUndef(new_ptr_ty);
1062410704
1062510705 const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target));
10626 // TODO I tried to put this check earlier but it the LLVM backend generate invalid instructinons
1062710706 if (offset_int == 0) return ptr;
1062810707 if (try ptr_val.getUnsignedIntAdvanced(target, sema.kit(block, ptr_src))) |addr| {
10629 const ptr_child_ty = ptr_ty.childType();
10630 const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array)
10631 ptr_child_ty.childType()
10632 else
10633 ptr_child_ty;
10634
1063510708 const elem_size = elem_ty.abiSize(target);
1063610709 const new_addr = switch (air_tag) {
1063710710 .ptr_add => addr + elem_size * offset_int,
......@@ -10651,7 +10724,16 @@ fn analyzePtrArithmetic(
1065110724 };
1065210725
1065310726 try sema.requireRuntimeBlock(block, runtime_src);
10654 return block.addBinOp(air_tag, ptr, offset);
10727 return block.addInst(.{
10728 .tag = air_tag,
10729 .data = .{ .ty_pl = .{
10730 .ty = try sema.addType(new_ptr_ty),
10731 .payload = try sema.addExtra(Air.Bin{
10732 .lhs = ptr,
10733 .rhs = offset,
10734 }),
10735 } },
10736 });
1065510737}
1065610738
1065710739fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -14481,8 +14563,8 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1448114563 const dest_scalar_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
1448214564 const operand = sema.resolveInst(extra.rhs);
1448314565 const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_scalar_ty);
14484 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src);
1448514566 const operand_ty = sema.typeOf(operand);
14567 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
1448614568 const is_vector = operand_ty.zigTypeTag() == .Vector;
1448714569 const dest_ty = if (is_vector)
1448814570 try Type.vector(sema.arena, operand_ty.vectorLen(), dest_scalar_ty)
......@@ -14650,7 +14732,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1465014732 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1465114733 const operand = sema.resolveInst(inst_data.operand);
1465214734 const operand_ty = sema.typeOf(operand);
14653 const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src);
14735 const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
1465414736 const target = sema.mod.getTarget();
1465514737 const bits = scalar_ty.intInfo(target).bits;
1465614738 if (bits % 8 != 0) {
......@@ -14707,7 +14789,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1470714789 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
1470814790 const operand = sema.resolveInst(inst_data.operand);
1470914791 const operand_ty = sema.typeOf(operand);
14710 _ = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src);
14792 _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
1471114793
1471214794 if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| {
1471314795 return sema.addConstant(operand_ty, val);
......@@ -15059,10 +15141,9 @@ fn checkIntOrVector(
1505915141fn checkIntOrVectorAllowComptime(
1506015142 sema: *Sema,
1506115143 block: *Block,
15062 operand: Air.Inst.Ref,
15144 operand_ty: Type,
1506315145 operand_src: LazySrcLoc,
1506415146) CompileError!Type {
15065 const operand_ty = sema.typeOf(operand);
1506615147 switch (try operand_ty.zigTypeTagOrPoison()) {
1506715148 .Int, .ComptimeInt => return operand_ty,
1506815149 .Vector => {
src/arch/aarch64/CodeGen.zig+98-68
......@@ -545,18 +545,30 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
545545
546546 switch (air_tags[inst]) {
547547 // zig fmt: off
548 .add, .ptr_add => try self.airBinOp(inst),
549 .addwrap => try self.airBinOp(inst),
548 .add => try self.airBinOp(inst, .add),
549 .addwrap => try self.airBinOp(inst, .addwrap),
550 .sub => try self.airBinOp(inst, .sub),
551 .subwrap => try self.airBinOp(inst, .subwrap),
552 .mul => try self.airBinOp(inst, .mul),
553 .mulwrap => try self.airBinOp(inst, .mulwrap),
554 .shl => try self.airBinOp(inst, .shl),
555 .shl_exact => try self.airBinOp(inst, .shl_exact),
556 .bool_and => try self.airBinOp(inst, .bool_and),
557 .bool_or => try self.airBinOp(inst, .bool_or),
558 .bit_and => try self.airBinOp(inst, .bit_and),
559 .bit_or => try self.airBinOp(inst, .bit_or),
560 .xor => try self.airBinOp(inst, .xor),
561 .shr => try self.airBinOp(inst, .shr),
562 .shr_exact => try self.airBinOp(inst, .shr_exact),
563
564 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
565 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
566
550567 .add_sat => try self.airAddSat(inst),
551 .sub, .ptr_sub => try self.airBinOp(inst),
552 .subwrap => try self.airBinOp(inst),
553568 .sub_sat => try self.airSubSat(inst),
554 .mul => try self.airBinOp(inst),
555 .mulwrap => try self.airBinOp(inst),
556569 .mul_sat => try self.airMulSat(inst),
557570 .rem => try self.airRem(inst),
558571 .mod => try self.airMod(inst),
559 .shl, .shl_exact => try self.airBinOp(inst),
560572 .shl_sat => try self.airShlSat(inst),
561573 .min => try self.airMin(inst),
562574 .max => try self.airMax(inst),
......@@ -595,13 +607,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
595607 .cmp_vector => try self.airCmpVector(inst),
596608 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
597609
598 .bool_and => try self.airBinOp(inst),
599 .bool_or => try self.airBinOp(inst),
600 .bit_and => try self.airBinOp(inst),
601 .bit_or => try self.airBinOp(inst),
602 .xor => try self.airBinOp(inst),
603 .shr, .shr_exact => try self.airBinOp(inst),
604
605610 .alloc => try self.airAlloc(inst),
606611 .ret_ptr => try self.airRetPtr(inst),
607612 .arg => try self.airArg(inst),
......@@ -1260,11 +1265,11 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
12601265fn binOpRegister(
12611266 self: *Self,
12621267 mir_tag: Mir.Inst.Tag,
1263 maybe_inst: ?Air.Inst.Index,
12641268 lhs: MCValue,
12651269 rhs: MCValue,
12661270 lhs_ty: Type,
12671271 rhs_ty: Type,
1272 metadata: ?BinOpMetadata,
12681273) !MCValue {
12691274 const lhs_is_register = lhs == .register;
12701275 const rhs_is_register = rhs == .register;
......@@ -1284,9 +1289,8 @@ fn binOpRegister(
12841289 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
12851290
12861291 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1287 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1288 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1289 break :inst Air.refToIndex(bin_op.lhs).?;
1292 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1293 break :inst Air.refToIndex(md.lhs).?;
12901294 } else null;
12911295
12921296 const raw_reg = try self.register_manager.allocReg(track_inst);
......@@ -1300,9 +1304,8 @@ fn binOpRegister(
13001304 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
13011305
13021306 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
1303 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1304 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1305 break :inst Air.refToIndex(bin_op.rhs).?;
1307 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1308 break :inst Air.refToIndex(md.rhs).?;
13061309 } else null;
13071310
13081311 const raw_reg = try self.register_manager.allocReg(track_inst);
......@@ -1317,15 +1320,13 @@ fn binOpRegister(
13171320
13181321 const dest_reg = switch (mir_tag) {
13191322 .cmp_shifted_register => undefined, // cmp has no destination register
1320 else => if (maybe_inst) |inst| blk: {
1321 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1322
1323 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
1323 else => if (metadata) |md| blk: {
1324 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {
13241325 break :blk lhs_reg;
1325 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
1326 } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {
13261327 break :blk rhs_reg;
13271328 } else {
1328 const raw_reg = try self.register_manager.allocReg(inst);
1329 const raw_reg = try self.register_manager.allocReg(md.inst);
13291330 break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
13301331 }
13311332 } else blk: {
......@@ -1407,11 +1408,11 @@ fn binOpRegister(
14071408fn binOpImmediate(
14081409 self: *Self,
14091410 mir_tag: Mir.Inst.Tag,
1410 maybe_inst: ?Air.Inst.Index,
14111411 lhs: MCValue,
14121412 rhs: MCValue,
14131413 lhs_ty: Type,
14141414 lhs_and_rhs_swapped: bool,
1415 metadata: ?BinOpMetadata,
14151416) !MCValue {
14161417 const lhs_is_register = lhs == .register;
14171418
......@@ -1424,10 +1425,9 @@ fn binOpImmediate(
14241425 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
14251426
14261427 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1427 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1428 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1428 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
14291429 break :inst Air.refToIndex(
1430 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
1430 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
14311431 ).?;
14321432 } else null;
14331433
......@@ -1443,18 +1443,16 @@ fn binOpImmediate(
14431443
14441444 const dest_reg = switch (mir_tag) {
14451445 .cmp_immediate => undefined, // cmp has no destination register
1446 else => if (maybe_inst) |inst| blk: {
1447 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1448
1446 else => if (metadata) |md| blk: {
14491447 if (lhs_is_register and self.reuseOperand(
1450 inst,
1451 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
1448 md.inst,
1449 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
14521450 if (lhs_and_rhs_swapped) 1 else 0,
14531451 lhs,
14541452 )) {
14551453 break :blk lhs_reg;
14561454 } else {
1457 const raw_reg = try self.register_manager.allocReg(inst);
1455 const raw_reg = try self.register_manager.allocReg(md.inst);
14581456 break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
14591457 }
14601458 } else blk: {
......@@ -1498,6 +1496,12 @@ fn binOpImmediate(
14981496 return MCValue{ .register = dest_reg };
14991497}
15001498
1499const BinOpMetadata = struct {
1500 inst: Air.Inst.Index,
1501 lhs: Air.Inst.Ref,
1502 rhs: Air.Inst.Ref,
1503};
1504
15011505/// For all your binary operation needs, this function will generate
15021506/// the corresponding Mir instruction(s). Returns the location of the
15031507/// result.
......@@ -1513,11 +1517,11 @@ fn binOpImmediate(
15131517fn binOp(
15141518 self: *Self,
15151519 tag: Air.Inst.Tag,
1516 maybe_inst: ?Air.Inst.Index,
15171520 lhs: MCValue,
15181521 rhs: MCValue,
15191522 lhs_ty: Type,
15201523 rhs_ty: Type,
1524 metadata: ?BinOpMetadata,
15211525) InnerError!MCValue {
15221526 const mod = self.bin_file.options.module.?;
15231527 switch (tag) {
......@@ -1562,12 +1566,12 @@ fn binOp(
15621566 };
15631567
15641568 if (rhs_immediate_ok) {
1565 return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false);
1569 return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata);
15661570 } else if (lhs_immediate_ok) {
15671571 // swap lhs and rhs
1568 return try self.binOpImmediate(mir_tag_immediate, maybe_inst, rhs, lhs, rhs_ty, true);
1572 return try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, metadata);
15691573 } else {
1570 return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1574 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);
15711575 }
15721576 } else {
15731577 return self.fail("TODO binary operations on int with bits > 64", .{});
......@@ -1586,7 +1590,7 @@ fn binOp(
15861590 // TODO add optimisations for multiplication
15871591 // with immediates, for example a * 2 can be
15881592 // lowered to a << 1
1589 return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1593 return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata);
15901594 } else {
15911595 return self.fail("TODO binary operations on int with bits > 64", .{});
15921596 }
......@@ -1606,7 +1610,7 @@ fn binOp(
16061610 };
16071611
16081612 // Generate an add/sub/mul
1609 const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1613 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
16101614
16111615 // Truncate if necessary
16121616 switch (lhs_ty.zigTypeTag()) {
......@@ -1642,7 +1646,7 @@ fn binOp(
16421646 else => unreachable,
16431647 };
16441648
1645 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1649 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
16461650 } else {
16471651 return self.fail("TODO binary operations on int with bits > 64", .{});
16481652 }
......@@ -1678,9 +1682,9 @@ fn binOp(
16781682 };
16791683
16801684 if (rhs_immediate_ok) {
1681 return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false);
1685 return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata);
16821686 } else {
1683 return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1687 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);
16841688 }
16851689 } else {
16861690 return self.fail("TODO binary operations on int with bits > 64", .{});
......@@ -1699,7 +1703,7 @@ fn binOp(
16991703 };
17001704
17011705 // Generate a shl_exact/shr_exact
1702 const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1706 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
17031707
17041708 // Truncate if necessary
17051709 switch (tag) {
......@@ -1735,7 +1739,7 @@ fn binOp(
17351739 else => unreachable,
17361740 };
17371741
1738 return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1742 return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata);
17391743 },
17401744 else => unreachable,
17411745 }
......@@ -1759,12 +1763,12 @@ fn binOp(
17591763 else => unreachable,
17601764 };
17611765
1762 return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1766 return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
17631767 } else {
17641768 // convert the offset into a byte offset by
17651769 // multiplying it with elem_size
1766 const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize);
1767 const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize);
1770 const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null);
1771 const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null);
17681772 return addr;
17691773 }
17701774 },
......@@ -1775,8 +1779,7 @@ fn binOp(
17751779 }
17761780}
17771781
1778fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
1779 const tag = self.air.instructions.items(.tag)[inst];
1782fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
17801783 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
17811784 const lhs = try self.resolveInst(bin_op.lhs);
17821785 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -1786,7 +1789,30 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
17861789 const result: MCValue = if (self.liveness.isUnused(inst))
17871790 .dead
17881791 else
1789 try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
1792 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
1793 .inst = inst,
1794 .lhs = bin_op.lhs,
1795 .rhs = bin_op.rhs,
1796 });
1797 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1798}
1799
1800fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1801 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1802 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1803 const lhs = try self.resolveInst(bin_op.lhs);
1804 const rhs = try self.resolveInst(bin_op.rhs);
1805 const lhs_ty = self.air.typeOf(bin_op.lhs);
1806 const rhs_ty = self.air.typeOf(bin_op.rhs);
1807
1808 const result: MCValue = if (self.liveness.isUnused(inst))
1809 .dead
1810 else
1811 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
1812 .inst = inst,
1813 .lhs = bin_op.lhs,
1814 .rhs = bin_op.rhs,
1815 });
17901816 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
17911817}
17921818
......@@ -1841,7 +1867,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
18411867 .sub_with_overflow => .sub,
18421868 else => unreachable,
18431869 };
1844 const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1870 const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null);
18451871 const dest_reg = dest.register;
18461872 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
18471873 defer self.register_manager.unlockReg(dest_reg_lock);
......@@ -1855,7 +1881,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
18551881 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
18561882
18571883 // cmp dest, truncated
1858 _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize);
1884 _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null);
18591885
18601886 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
18611887 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
......@@ -1894,12 +1920,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
18941920
18951921 const dest = blk: {
18961922 if (rhs_immediate_ok) {
1897 break :blk try self.binOpImmediate(mir_tag_immediate, null, lhs, rhs, lhs_ty, false);
1923 break :blk try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, null);
18981924 } else if (lhs_immediate_ok) {
18991925 // swap lhs and rhs
1900 break :blk try self.binOpImmediate(mir_tag_immediate, null, rhs, lhs, rhs_ty, true);
1926 break :blk try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, null);
19011927 } else {
1902 break :blk try self.binOpRegister(mir_tag_register, null, lhs, rhs, lhs_ty, rhs_ty);
1928 break :blk try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, null);
19031929 }
19041930 };
19051931
......@@ -1952,7 +1978,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
19521978 .unsigned => .umull,
19531979 };
19541980
1955 const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1981 const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null);
19561982 const dest_reg = dest.register;
19571983 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
19581984 defer self.register_manager.unlockReg(dest_reg_lock);
......@@ -2136,11 +2162,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
21362162
21372163 _ = try self.binOp(
21382164 .cmp_eq,
2139 null,
21402165 .{ .register = dest_high_reg },
21412166 .{ .immediate = 0 },
21422167 Type.usize,
21432168 Type.usize,
2169 null,
21442170 );
21452171
21462172 if (int_info.bits < 64) {
......@@ -2156,11 +2182,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
21562182
21572183 _ = try self.binOp(
21582184 .cmp_eq,
2159 null,
21602185 .{ .register = dest_high_reg },
21612186 .{ .immediate = 0 },
21622187 Type.usize,
21632188 Type.usize,
2189 null,
21642190 );
21652191 }
21662192 },
......@@ -2218,16 +2244,16 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
22182244 self.compare_flags_inst = null;
22192245
22202246 // lsl dest, lhs, rhs
2221 const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty);
2247 const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null);
22222248 const dest_reg = dest.register;
22232249 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
22242250 defer self.register_manager.unlockReg(dest_reg_lock);
22252251
22262252 // asr/lsr reconstructed, dest, rhs
2227 const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty);
2253 const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null);
22282254
22292255 // cmp lhs, reconstructed
2230 _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty);
2256 _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null);
22312257
22322258 try self.genSetStack(lhs_ty, stack_offset, dest);
22332259 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{
......@@ -2489,7 +2515,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
24892515 switch (elem_size) {
24902516 else => {
24912517 const dest = try self.allocRegOrMem(inst, true);
2492 const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize);
2518 const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null);
24932519 try self.load(dest, addr, slice_ptr_field_type);
24942520
24952521 break :result dest;
......@@ -2933,11 +2959,11 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
29332959
29342960 const dest = try self.binOp(
29352961 .add,
2936 null,
29372962 .{ .register = addr_reg },
29382963 .{ .register = offset_reg },
29392964 Type.usize,
29402965 Type.usize,
2966 null,
29412967 );
29422968
29432969 break :result dest;
......@@ -3302,7 +3328,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
33023328
33033329 const int_info = int_ty.intInfo(self.target.*);
33043330 if (int_info.bits <= 64) {
3305 _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty);
3331 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{
3332 .inst = inst,
3333 .lhs = bin_op.lhs,
3334 .rhs = bin_op.rhs,
3335 });
33063336
33073337 try self.spillCompareFlagsIfOccupied();
33083338 self.compare_flags_inst = inst;
src/arch/arm/CodeGen.zig+113-79
......@@ -552,21 +552,34 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
552552
553553 switch (air_tags[inst]) {
554554 // zig fmt: off
555 .add, .ptr_add => try self.airBinOp(inst),
556 .addwrap => try self.airBinOp(inst),
555 .add, => try self.airBinOp(inst, .add),
556 .addwrap => try self.airBinOp(inst, .addwrap),
557 .sub, => try self.airBinOp(inst, .sub),
558 .subwrap => try self.airBinOp(inst, .subwrap),
559 .mul => try self.airBinOp(inst, .mul),
560 .mulwrap => try self.airBinOp(inst, .mulwrap),
561 .shl => try self.airBinOp(inst, .shl),
562 .shl_exact => try self.airBinOp(inst, .shl_exact),
563 .bool_and => try self.airBinOp(inst, .bool_and),
564 .bool_or => try self.airBinOp(inst, .bool_or),
565 .bit_and => try self.airBinOp(inst, .bit_and),
566 .bit_or => try self.airBinOp(inst, .bit_or),
567 .xor => try self.airBinOp(inst, .xor),
568 .shr => try self.airBinOp(inst, .shr),
569 .shr_exact => try self.airBinOp(inst, .shr_exact),
570
571 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
572 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
573
574 .min => try self.airMinMax(inst),
575 .max => try self.airMinMax(inst),
576
557577 .add_sat => try self.airAddSat(inst),
558 .sub, .ptr_sub => try self.airBinOp(inst),
559 .subwrap => try self.airBinOp(inst),
560578 .sub_sat => try self.airSubSat(inst),
561 .mul => try self.airBinOp(inst),
562 .mulwrap => try self.airBinOp(inst),
563579 .mul_sat => try self.airMulSat(inst),
564580 .rem => try self.airRem(inst),
565581 .mod => try self.airMod(inst),
566 .shl, .shl_exact => try self.airBinOp(inst),
567582 .shl_sat => try self.airShlSat(inst),
568 .min => try self.airMinMax(inst),
569 .max => try self.airMinMax(inst),
570583 .slice => try self.airSlice(inst),
571584
572585 .sqrt,
......@@ -602,13 +615,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
602615 .cmp_vector => try self.airCmpVector(inst),
603616 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
604617
605 .bool_and => try self.airBinOp(inst),
606 .bool_or => try self.airBinOp(inst),
607 .bit_and => try self.airBinOp(inst),
608 .bit_or => try self.airBinOp(inst),
609 .xor => try self.airBinOp(inst),
610 .shr, .shr_exact => try self.airBinOp(inst),
611
612618 .alloc => try self.airAlloc(inst),
613619 .ret_ptr => try self.airRetPtr(inst),
614620 .arg => try self.airArg(inst),
......@@ -1260,7 +1266,7 @@ fn minMax(
12601266 // register.
12611267 assert(lhs_reg != rhs_reg); // see note above
12621268
1263 _ = try self.binOpRegister(.cmp, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty);
1269 _ = try self.binOpRegister(.cmp, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty, null);
12641270
12651271 const cond_choose_lhs: Condition = switch (tag) {
12661272 .max => switch (int_info.signedness) {
......@@ -1340,15 +1346,40 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
13401346 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13411347}
13421348
1343fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
1344 const tag = self.air.instructions.items(.tag)[inst];
1349fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
13451350 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13461351 const lhs = try self.resolveInst(bin_op.lhs);
13471352 const rhs = try self.resolveInst(bin_op.rhs);
13481353 const lhs_ty = self.air.typeOf(bin_op.lhs);
13491354 const rhs_ty = self.air.typeOf(bin_op.rhs);
13501355
1351 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
1356 const result: MCValue = if (self.liveness.isUnused(inst))
1357 .dead
1358 else
1359 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
1360 .lhs = bin_op.lhs,
1361 .rhs = bin_op.rhs,
1362 .inst = inst,
1363 });
1364 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1365}
1366
1367fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1368 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1369 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1370 const lhs = try self.resolveInst(bin_op.lhs);
1371 const rhs = try self.resolveInst(bin_op.rhs);
1372 const lhs_ty = self.air.typeOf(bin_op.lhs);
1373 const rhs_ty = self.air.typeOf(bin_op.rhs);
1374
1375 const result: MCValue = if (self.liveness.isUnused(inst))
1376 .dead
1377 else
1378 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
1379 .lhs = bin_op.lhs,
1380 .rhs = bin_op.rhs,
1381 .inst = inst,
1382 });
13521383 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13531384}
13541385
......@@ -1402,7 +1433,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
14021433 .sub_with_overflow => .sub,
14031434 else => unreachable,
14041435 };
1405 const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1436 const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null);
14061437 const dest_reg = dest.register;
14071438 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
14081439 defer self.register_manager.unlockReg(dest_reg_lock);
......@@ -1415,7 +1446,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
14151446 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
14161447
14171448 // cmp dest, truncated
1418 _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize);
1449 _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null);
14191450
14201451 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
14211452 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
......@@ -1448,12 +1479,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
14481479
14491480 const dest = blk: {
14501481 if (rhs_immediate_ok) {
1451 break :blk try self.binOpImmediate(mir_tag, null, lhs, rhs, lhs_ty, false);
1482 break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null);
14521483 } else if (lhs_immediate_ok) {
14531484 // swap lhs and rhs
1454 break :blk try self.binOpImmediate(mir_tag, null, rhs, lhs, rhs_ty, true);
1485 break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null);
14551486 } else {
1456 break :blk try self.binOpRegister(mir_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1487 break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null);
14571488 }
14581489 };
14591490
......@@ -1507,7 +1538,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
15071538 .unsigned => .mul,
15081539 };
15091540
1510 const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1541 const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null);
15111542 const dest_reg = dest.register;
15121543 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
15131544 defer self.register_manager.unlockReg(dest_reg_lock);
......@@ -1520,7 +1551,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
15201551 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
15211552
15221553 // cmp dest, truncated
1523 _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize);
1554 _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null);
15241555
15251556 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
15261557 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
......@@ -1594,7 +1625,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
15941625 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
15951626
15961627 // cmp truncated, rdlo
1597 _ = try self.binOp(.cmp_eq, null, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize);
1628 _ = try self.binOp(.cmp_eq, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize, null);
15981629
15991630 // mov rdlo, #0
16001631 _ = try self.addInst(.{
......@@ -1618,7 +1649,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16181649 });
16191650
16201651 // cmp rdhi, #0
1621 _ = try self.binOp(.cmp_eq, null, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize);
1652 _ = try self.binOp(.cmp_eq, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize, null);
16221653
16231654 // movne rdlo, #1
16241655 _ = try self.addInst(.{
......@@ -1677,16 +1708,16 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16771708 self.compare_flags_inst = null;
16781709
16791710 // lsl dest, lhs, rhs
1680 const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty);
1711 const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null);
16811712 const dest_reg = dest.register;
16821713 const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
16831714 defer self.register_manager.unlockReg(dest_lock);
16841715
16851716 // asr/lsr reconstructed, dest, rhs
1686 const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty);
1717 const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null);
16871718
16881719 // cmp lhs, reconstructed
1689 _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty);
1720 _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null);
16901721
16911722 try self.genSetStack(lhs_ty, stack_offset, dest);
16921723 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
......@@ -2031,7 +2062,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
20312062 },
20322063 else => {
20332064 const dest = try self.allocRegOrMem(inst, true);
2034 const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize);
2065 const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null);
20352066 try self.load(dest, addr, slice_ptr_field_type);
20362067
20372068 break :result dest;
......@@ -2051,7 +2082,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
20512082
20522083 const slice_ty = self.air.typeOf(extra.lhs);
20532084
2054 const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize);
2085 const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null);
20552086 break :result addr;
20562087 };
20572088 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
......@@ -2079,7 +2110,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
20792110
20802111 const ptr_ty = self.air.typeOf(extra.lhs);
20812112
2082 const addr = try self.binOp(.ptr_add, null, ptr_mcv, index_mcv, ptr_ty, Type.usize);
2113 const addr = try self.binOp(.ptr_add, ptr_mcv, index_mcv, ptr_ty, Type.usize, null);
20832114 break :result addr;
20842115 };
20852116 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
......@@ -2411,11 +2442,11 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
24112442
24122443 const dest = try self.binOp(
24132444 .add,
2414 null,
24152445 .{ .register = addr_reg },
24162446 .{ .register = offset_reg },
24172447 Type.usize,
24182448 Type.usize,
2449 null,
24192450 );
24202451
24212452 break :result dest;
......@@ -2514,11 +2545,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
25142545fn binOpRegister(
25152546 self: *Self,
25162547 mir_tag: Mir.Inst.Tag,
2517 maybe_inst: ?Air.Inst.Index,
25182548 lhs: MCValue,
25192549 rhs: MCValue,
25202550 lhs_ty: Type,
25212551 rhs_ty: Type,
2552 metadata: ?BinOpMetadata,
25222553) !MCValue {
25232554 const lhs_is_register = lhs == .register;
25242555 const rhs_is_register = rhs == .register;
......@@ -2532,9 +2563,8 @@ fn binOpRegister(
25322563 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
25332564
25342565 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2535 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
2536 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2537 break :inst Air.refToIndex(bin_op.lhs).?;
2566 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2567 break :inst Air.refToIndex(md.lhs).?;
25382568 } else null;
25392569
25402570 const reg = try self.register_manager.allocReg(track_inst);
......@@ -2547,9 +2577,8 @@ fn binOpRegister(
25472577 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
25482578
25492579 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
2550 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
2551 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2552 break :inst Air.refToIndex(bin_op.rhs).?;
2580 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2581 break :inst Air.refToIndex(md.rhs).?;
25532582 } else null;
25542583
25552584 const reg = try self.register_manager.allocReg(track_inst);
......@@ -2563,15 +2592,13 @@ fn binOpRegister(
25632592
25642593 const dest_reg = switch (mir_tag) {
25652594 .cmp => .r0, // cmp has no destination regardless
2566 else => if (maybe_inst) |inst| blk: {
2567 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2568
2569 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
2595 else => if (metadata) |md| blk: {
2596 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {
25702597 break :blk lhs_reg;
2571 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
2598 } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {
25722599 break :blk rhs_reg;
25732600 } else {
2574 break :blk try self.register_manager.allocReg(inst);
2601 break :blk try self.register_manager.allocReg(md.inst);
25752602 }
25762603 } else try self.register_manager.allocReg(null),
25772604 };
......@@ -2634,11 +2661,11 @@ fn binOpRegister(
26342661fn binOpImmediate(
26352662 self: *Self,
26362663 mir_tag: Mir.Inst.Tag,
2637 maybe_inst: ?Air.Inst.Index,
26382664 lhs: MCValue,
26392665 rhs: MCValue,
26402666 lhs_ty: Type,
26412667 lhs_and_rhs_swapped: bool,
2668 metadata: ?BinOpMetadata,
26422669) !MCValue {
26432670 const lhs_is_register = lhs == .register;
26442671
......@@ -2651,10 +2678,9 @@ fn binOpImmediate(
26512678 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
26522679
26532680 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2654 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
2655 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2681 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
26562682 break :inst Air.refToIndex(
2657 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
2683 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
26582684 ).?;
26592685 } else null;
26602686
......@@ -2669,18 +2695,16 @@ fn binOpImmediate(
26692695
26702696 const dest_reg = switch (mir_tag) {
26712697 .cmp => .r0, // cmp has no destination reg
2672 else => if (maybe_inst) |inst| blk: {
2673 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2674
2698 else => if (metadata) |md| blk: {
26752699 if (lhs_is_register and self.reuseOperand(
2676 inst,
2677 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
2700 md.inst,
2701 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
26782702 if (lhs_and_rhs_swapped) 1 else 0,
26792703 lhs,
26802704 )) {
26812705 break :blk lhs_reg;
26822706 } else {
2683 break :blk try self.register_manager.allocReg(inst);
2707 break :blk try self.register_manager.allocReg(md.inst);
26842708 }
26852709 } else try self.register_manager.allocReg(null),
26862710 };
......@@ -2720,6 +2744,12 @@ fn binOpImmediate(
27202744 return MCValue{ .register = dest_reg };
27212745}
27222746
2747const BinOpMetadata = struct {
2748 inst: Air.Inst.Index,
2749 lhs: Air.Inst.Ref,
2750 rhs: Air.Inst.Ref,
2751};
2752
27232753/// For all your binary operation needs, this function will generate
27242754/// the corresponding Mir instruction(s). Returns the location of the
27252755/// result.
......@@ -2735,11 +2765,11 @@ fn binOpImmediate(
27352765fn binOp(
27362766 self: *Self,
27372767 tag: Air.Inst.Tag,
2738 maybe_inst: ?Air.Inst.Index,
27392768 lhs: MCValue,
27402769 rhs: MCValue,
27412770 lhs_ty: Type,
27422771 rhs_ty: Type,
2772 metadata: ?BinOpMetadata,
27432773) InnerError!MCValue {
27442774 switch (tag) {
27452775 .add,
......@@ -2780,12 +2810,12 @@ fn binOp(
27802810 };
27812811
27822812 if (rhs_immediate_ok) {
2783 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
2813 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
27842814 } else if (lhs_immediate_ok) {
27852815 // swap lhs and rhs
2786 return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true);
2816 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
27872817 } else {
2788 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2818 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
27892819 }
27902820 } else {
27912821 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
......@@ -2806,7 +2836,7 @@ fn binOp(
28062836 // TODO add optimisations for multiplication
28072837 // with immediates, for example a * 2 can be
28082838 // lowered to a << 1
2809 return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2839 return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata);
28102840 } else {
28112841 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
28122842 }
......@@ -2826,7 +2856,7 @@ fn binOp(
28262856 };
28272857
28282858 // Generate an add/sub/mul
2829 const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2859 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
28302860
28312861 // Truncate if necessary
28322862 switch (lhs_ty.zigTypeTag()) {
......@@ -2869,12 +2899,12 @@ fn binOp(
28692899 };
28702900
28712901 if (rhs_immediate_ok) {
2872 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
2902 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
28732903 } else if (lhs_immediate_ok) {
28742904 // swap lhs and rhs
2875 return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true);
2905 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
28762906 } else {
2877 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2907 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
28782908 }
28792909 } else {
28802910 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
......@@ -2903,9 +2933,9 @@ fn binOp(
29032933 };
29042934
29052935 if (rhs_immediate_ok) {
2906 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
2936 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
29072937 } else {
2908 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2938 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
29092939 }
29102940 } else {
29112941 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
......@@ -2924,7 +2954,7 @@ fn binOp(
29242954 };
29252955
29262956 // Generate a shl_exact/shr_exact
2927 const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
2957 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
29282958
29292959 // Truncate if necessary
29302960 switch (tag) {
......@@ -2964,12 +2994,12 @@ fn binOp(
29642994 };
29652995
29662996 if (rhs_immediate_ok) {
2967 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
2997 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
29682998 } else if (lhs_immediate_ok) {
29692999 // swap lhs and rhs
2970 return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true);
3000 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
29713001 } else {
2972 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
3002 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
29733003 }
29743004 },
29753005 else => unreachable,
......@@ -2994,12 +3024,12 @@ fn binOp(
29943024 else => unreachable,
29953025 };
29963026
2997 return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
3027 return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
29983028 } else {
29993029 // convert the offset into a byte offset by
30003030 // multiplying it with elem_size
3001 const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize);
3002 const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize);
3031 const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null);
3032 const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null);
30033033 return addr;
30043034 }
30053035 },
......@@ -3575,7 +3605,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
35753605 try self.spillCompareFlagsIfOccupied();
35763606 self.compare_flags_inst = inst;
35773607
3578 _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty);
3608 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{
3609 .lhs = bin_op.lhs,
3610 .rhs = bin_op.rhs,
3611 .inst = inst,
3612 });
35793613
35803614 break :result switch (int_info.signedness) {
35813615 .signed => MCValue{ .compare_flags_signed = op },
......@@ -3865,7 +3899,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
38653899 }
38663900
38673901 const error_mcv = try self.errUnionErr(operand, ty);
3868 _ = try self.binOp(.cmp_eq, null, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type);
3902 _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null);
38693903 return MCValue{ .compare_flags_unsigned = .gt };
38703904}
38713905
src/arch/riscv64/CodeGen.zig+19-4
......@@ -481,10 +481,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
481481
482482 switch (air_tags[inst]) {
483483 // zig fmt: off
484 .add, .ptr_add => try self.airBinOp(inst),
484 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
485 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
486
487 .add => try self.airBinOp(inst, .add),
488 .sub => try self.airBinOp(inst, .sub),
489
485490 .addwrap => try self.airAddWrap(inst),
486491 .add_sat => try self.airAddSat(inst),
487 .sub, .ptr_sub => try self.airBinOp(inst),
488492 .subwrap => try self.airSubWrap(inst),
489493 .sub_sat => try self.airSubSat(inst),
490494 .mul => try self.airMul(inst),
......@@ -1091,8 +1095,7 @@ fn binOp(
10911095 }
10921096}
10931097
1094fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
1095 const tag = self.air.instructions.items(.tag)[inst];
1098fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
10961099 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
10971100 const lhs = try self.resolveInst(bin_op.lhs);
10981101 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -1103,6 +1106,18 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
11031106 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
11041107}
11051108
1109fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1110 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1111 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1112 const lhs = try self.resolveInst(bin_op.lhs);
1113 const rhs = try self.resolveInst(bin_op.rhs);
1114 const lhs_ty = self.air.typeOf(bin_op.lhs);
1115 const rhs_ty = self.air.typeOf(bin_op.rhs);
1116
1117 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
1118 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1119}
1120
11061121fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
11071122 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
11081123 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
src/arch/sparc64/CodeGen.zig+71-46
......@@ -483,10 +483,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
483483
484484 switch (air_tags[inst]) {
485485 // zig fmt: off
486 .add, .ptr_add => try self.airBinOp(inst),
486 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
487 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
488
489 .add => try self.airBinOp(inst, .add),
487490 .addwrap => @panic("TODO try self.airAddWrap(inst)"),
488491 .add_sat => @panic("TODO try self.airAddSat(inst)"),
489 .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"),
492 .sub => @panic("TODO try self.airBinOp(inst)"),
490493 .subwrap => @panic("TODO try self.airSubWrap(inst)"),
491494 .sub_sat => @panic("TODO try self.airSubSat(inst)"),
492495 .mul => @panic("TODO try self.airMul(inst)"),
......@@ -827,18 +830,38 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
827830 return self.finishAir(inst, mcv, .{ .none, .none, .none });
828831}
829832
830fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
831 const tag = self.air.instructions.items(.tag)[inst];
833fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
832834 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
833835 const lhs = try self.resolveInst(bin_op.lhs);
834836 const rhs = try self.resolveInst(bin_op.rhs);
835837 const lhs_ty = self.air.typeOf(bin_op.lhs);
836838 const rhs_ty = self.air.typeOf(bin_op.rhs);
839 const result: MCValue = if (self.liveness.isUnused(inst))
840 .dead
841 else
842 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
843 .lhs = bin_op.lhs,
844 .rhs = bin_op.rhs,
845 .inst = inst,
846 });
847 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
848}
837849
850fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
851 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
852 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
853 const lhs = try self.resolveInst(bin_op.lhs);
854 const rhs = try self.resolveInst(bin_op.rhs);
855 const lhs_ty = self.air.typeOf(bin_op.lhs);
856 const rhs_ty = self.air.typeOf(bin_op.rhs);
838857 const result: MCValue = if (self.liveness.isUnused(inst))
839858 .dead
840859 else
841 try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
860 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
861 .lhs = bin_op.lhs,
862 .rhs = bin_op.rhs,
863 .inst = inst,
864 });
842865 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
843866}
844867
......@@ -1030,7 +1053,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
10301053
10311054 var int_buffer: Type.Payload.Bits = undefined;
10321055 const int_ty = switch (lhs_ty.zigTypeTag()) {
1033 .Vector => unreachable, // Should be handled by cmp_vector?
1056 .Vector => unreachable, // Handled by cmp_vector.
10341057 .Enum => lhs_ty.intTagType(&int_buffer),
10351058 .Int => lhs_ty,
10361059 .Bool => Type.initTag(.u1),
......@@ -1053,7 +1076,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
10531076
10541077 const int_info = int_ty.intInfo(self.target.*);
10551078 if (int_info.bits <= 64) {
1056 _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty);
1079 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{
1080 .lhs = bin_op.lhs,
1081 .rhs = bin_op.rhs,
1082 .inst = inst,
1083 });
10571084
10581085 try self.spillCompareFlagsIfOccupied();
10591086 self.compare_flags_inst = inst;
......@@ -1426,7 +1453,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
14261453 // TODO skip the ptr_add emission entirely and use native addressing modes
14271454 // i.e sllx/mulx then R+R or scale immediate then R+I
14281455 const dest = try self.allocRegOrMem(inst, true);
1429 const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize);
1456 const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null);
14301457 try self.load(dest, addr, slice_ptr_field_type);
14311458
14321459 break :result dest;
......@@ -1595,6 +1622,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
15951622 return MCValue{ .stack_offset = stack_offset };
15961623}
15971624
1625const BinOpMetadata = struct {
1626 inst: Air.Inst.Index,
1627 lhs: Air.Inst.Ref,
1628 rhs: Air.Inst.Ref,
1629};
1630
15981631/// For all your binary operation needs, this function will generate
15991632/// the corresponding Mir instruction(s). Returns the location of the
16001633/// result.
......@@ -1610,11 +1643,11 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
16101643fn binOp(
16111644 self: *Self,
16121645 tag: Air.Inst.Tag,
1613 maybe_inst: ?Air.Inst.Index,
16141646 lhs: MCValue,
16151647 rhs: MCValue,
16161648 lhs_ty: Type,
16171649 rhs_ty: Type,
1650 metadata: ?BinOpMetadata,
16181651) InnerError!MCValue {
16191652 const mod = self.bin_file.options.module.?;
16201653 switch (tag) {
......@@ -1649,13 +1682,13 @@ fn binOp(
16491682 };
16501683
16511684 if (rhs_immediate_ok) {
1652 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
1685 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
16531686 } else if (lhs_immediate_ok) {
16541687 // swap lhs and rhs
1655 return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true);
1688 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
16561689 } else {
16571690 // TODO convert large immediates to register before adding
1658 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1691 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
16591692 }
16601693 } else {
16611694 return self.fail("TODO binary operations on int with bits > 64", .{});
......@@ -1683,10 +1716,10 @@ fn binOp(
16831716 // If it's a power of two immediate then we emit an shl instead
16841717 // TODO add similar checks for LHS
16851718 if (new_rhs == .immediate and math.isPowerOfTwo(new_rhs.immediate)) {
1686 return try self.binOp(.shl, maybe_inst, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize);
1719 return try self.binOp(.shl, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize, metadata);
16871720 }
16881721
1689 return try self.binOpRegister(.mulx, maybe_inst, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty);
1722 return try self.binOpRegister(.mulx, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty, metadata);
16901723 } else {
16911724 return self.fail("TODO binary operations on int with bits > 64", .{});
16921725 }
......@@ -1711,13 +1744,13 @@ fn binOp(
17111744 else => unreachable,
17121745 };
17131746
1714 return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1747 return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
17151748 } else {
17161749 // convert the offset into a byte offset by
17171750 // multiplying it with elem_size
17181751
1719 const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize);
1720 const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize);
1752 const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null);
1753 const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null);
17211754 return addr;
17221755 }
17231756 },
......@@ -1732,7 +1765,7 @@ fn binOp(
17321765 };
17331766
17341767 // Generate a shl_exact/shr_exact
1735 const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1768 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
17361769
17371770 // Truncate if necessary
17381771 switch (tag) {
......@@ -1768,9 +1801,9 @@ fn binOp(
17681801 };
17691802
17701803 if (rhs_immediate_ok) {
1771 return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false);
1804 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
17721805 } else {
1773 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty);
1806 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
17741807 }
17751808 } else {
17761809 return self.fail("TODO binary operations on int with bits > 64", .{});
......@@ -1792,18 +1825,17 @@ fn binOp(
17921825/// op dest, lhs, #rhs_imm
17931826///
17941827/// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to
1795/// rhs and vice versa. This parameter is only used when maybe_inst !=
1796/// null.
1828/// rhs and vice versa. This parameter is only used when metadata != null.
17971829///
17981830/// Asserts that generating an instruction of that form is possible.
17991831fn binOpImmediate(
18001832 self: *Self,
18011833 mir_tag: Mir.Inst.Tag,
1802 maybe_inst: ?Air.Inst.Index,
18031834 lhs: MCValue,
18041835 rhs: MCValue,
18051836 lhs_ty: Type,
18061837 lhs_and_rhs_swapped: bool,
1838 metadata: ?BinOpMetadata,
18071839) !MCValue {
18081840 const lhs_is_register = lhs == .register;
18091841
......@@ -1816,10 +1848,9 @@ fn binOpImmediate(
18161848 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
18171849
18181850 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1819 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1820 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1851 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
18211852 break :inst Air.refToIndex(
1822 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
1853 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
18231854 ).?;
18241855 } else null;
18251856
......@@ -1833,18 +1864,16 @@ fn binOpImmediate(
18331864 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
18341865
18351866 const dest_reg = switch (mir_tag) {
1836 else => if (maybe_inst) |inst| blk: {
1837 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1838
1867 else => if (metadata) |md| blk: {
18391868 if (lhs_is_register and self.reuseOperand(
1840 inst,
1841 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs,
1869 md.inst,
1870 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
18421871 if (lhs_and_rhs_swapped) 1 else 0,
18431872 lhs,
18441873 )) {
18451874 break :blk lhs_reg;
18461875 } else {
1847 break :blk try self.register_manager.allocReg(inst);
1876 break :blk try self.register_manager.allocReg(md.inst);
18481877 }
18491878 } else blk: {
18501879 break :blk try self.register_manager.allocReg(null);
......@@ -1896,11 +1925,11 @@ fn binOpImmediate(
18961925fn binOpRegister(
18971926 self: *Self,
18981927 mir_tag: Mir.Inst.Tag,
1899 maybe_inst: ?Air.Inst.Index,
19001928 lhs: MCValue,
19011929 rhs: MCValue,
19021930 lhs_ty: Type,
19031931 rhs_ty: Type,
1932 metadata: ?BinOpMetadata,
19041933) !MCValue {
19051934 const lhs_is_register = lhs == .register;
19061935 const rhs_is_register = rhs == .register;
......@@ -1920,9 +1949,8 @@ fn binOpRegister(
19201949 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
19211950
19221951 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
1923 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1924 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1925 break :inst Air.refToIndex(bin_op.lhs).?;
1952 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1953 break :inst Air.refToIndex(md.lhs).?;
19261954 } else null;
19271955
19281956 const reg = try self.register_manager.allocReg(track_inst);
......@@ -1934,9 +1962,8 @@ fn binOpRegister(
19341962 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
19351963
19361964 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
1937 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
1938 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1939 break :inst Air.refToIndex(bin_op.rhs).?;
1965 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
1966 break :inst Air.refToIndex(md.rhs).?;
19401967 } else null;
19411968
19421969 const reg = try self.register_manager.allocReg(track_inst);
......@@ -1948,15 +1975,13 @@ fn binOpRegister(
19481975 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
19491976
19501977 const dest_reg = switch (mir_tag) {
1951 else => if (maybe_inst) |inst| blk: {
1952 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1953
1954 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
1978 else => if (metadata) |md| blk: {
1979 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {
19551980 break :blk lhs_reg;
1956 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
1981 } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {
19571982 break :blk rhs_reg;
19581983 } else {
1959 break :blk try self.register_manager.allocReg(inst);
1984 break :blk try self.register_manager.allocReg(md.inst);
19601985 }
19611986 } else blk: {
19621987 break :blk try self.register_manager.allocReg(null);
......@@ -3069,11 +3094,11 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
30693094
30703095 const dest = try self.binOp(
30713096 .add,
3072 null,
30733097 .{ .register = addr_reg },
30743098 .{ .register = offset_reg },
30753099 Type.usize,
30763100 Type.usize,
3101 null,
30773102 );
30783103
30793104 break :result dest;
src/arch/wasm/CodeGen.zig+2-1
......@@ -3397,7 +3397,8 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
33973397
33983398fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
33993399 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
3400 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3400 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3401 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
34013402 const ptr = try self.resolveInst(bin_op.lhs);
34023403 const offset = try self.resolveInst(bin_op.rhs);
34033404 const ptr_ty = self.air.typeOf(bin_op.lhs);
src/arch/x86_64/CodeGen.zig+45-37
......@@ -574,23 +574,33 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
574574
575575 switch (air_tags[inst]) {
576576 // zig fmt: off
577 .add => try self.airBinOp(inst),
578 .addwrap => try self.airBinOp(inst),
579 .add_sat => try self.airAddSat(inst),
580 .sub => try self.airBinOp(inst),
581 .subwrap => try self.airBinOp(inst),
582 .sub_sat => try self.airSubSat(inst),
577 .add => try self.airBinOp(inst, .add),
578 .addwrap => try self.airBinOp(inst, .addwrap),
579 .sub => try self.airBinOp(inst, .sub),
580 .subwrap => try self.airBinOp(inst, .subwrap),
581 .bool_and => try self.airBinOp(inst, .bool_and),
582 .bool_or => try self.airBinOp(inst, .bool_or),
583 .bit_and => try self.airBinOp(inst, .bit_and),
584 .bit_or => try self.airBinOp(inst, .bit_or),
585 .xor => try self.airBinOp(inst, .xor),
586
587 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add),
588 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
589
590 .shr, .shr_exact => try self.airShlShrBinOp(inst),
591 .shl, .shl_exact => try self.airShlShrBinOp(inst),
592
583593 .mul => try self.airMulDivBinOp(inst),
584594 .mulwrap => try self.airMulDivBinOp(inst),
585 .mul_sat => try self.airMulSat(inst),
586595 .rem => try self.airMulDivBinOp(inst),
587596 .mod => try self.airMulDivBinOp(inst),
588 .shl, .shl_exact => try self.airShlShrBinOp(inst),
597
598 .add_sat => try self.airAddSat(inst),
599 .sub_sat => try self.airSubSat(inst),
600 .mul_sat => try self.airMulSat(inst),
589601 .shl_sat => try self.airShlSat(inst),
590602 .min => try self.airMin(inst),
591603 .max => try self.airMax(inst),
592 .ptr_add => try self.airBinOp(inst),
593 .ptr_sub => try self.airBinOp(inst),
594604 .slice => try self.airSlice(inst),
595605
596606 .sqrt,
......@@ -626,13 +636,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
626636 .cmp_vector => try self.airCmpVector(inst),
627637 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
628638
629 .bool_and => try self.airBinOp(inst),
630 .bool_or => try self.airBinOp(inst),
631 .bit_and => try self.airBinOp(inst),
632 .bit_or => try self.airBinOp(inst),
633 .xor => try self.airBinOp(inst),
634 .shr, .shr_exact => try self.airShlShrBinOp(inst),
635
636639 .alloc => try self.airAlloc(inst),
637640 .ret_ptr => try self.airRetPtr(inst),
638641 .arg => try self.airArg(inst),
......@@ -1231,21 +1234,26 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
12311234 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12321235}
12331236
1234fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
1237fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
12351238 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
12361239
12371240 if (self.liveness.isUnused(inst)) {
12381241 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
12391242 }
12401243
1241 const tag = self.air.instructions.items(.tag)[inst];
1242 const lhs = try self.resolveInst(bin_op.lhs);
1243 const rhs = try self.resolveInst(bin_op.rhs);
1244 const lhs_ty = self.air.typeOf(bin_op.lhs);
1245 const rhs_ty = self.air.typeOf(bin_op.rhs);
1244 const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
1245 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1246}
12461247
1247 const result = try self.genBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
1248fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1249 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1250 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1251
1252 if (self.liveness.isUnused(inst)) {
1253 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1254 }
12481255
1256 const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs);
12491257 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12501258}
12511259
......@@ -1316,13 +1324,12 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
13161324 try self.spillRegisters(1, .{.rcx});
13171325 }
13181326
1319 const lhs = try self.resolveInst(bin_op.lhs);
1320 const rhs = try self.resolveInst(bin_op.rhs);
1321
13221327 const partial: MCValue = switch (tag) {
1323 .add_with_overflow => try self.genBinOp(.add, null, lhs, rhs, ty, ty),
1324 .sub_with_overflow => try self.genBinOp(.sub, null, lhs, rhs, ty, ty),
1328 .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs),
1329 .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs),
13251330 .shl_with_overflow => blk: {
1331 const lhs = try self.resolveInst(bin_op.lhs);
1332 const rhs = try self.resolveInst(bin_op.rhs);
13261333 const shift_ty = self.air.typeOf(bin_op.rhs);
13271334 break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty);
13281335 },
......@@ -3310,13 +3317,15 @@ fn genMulDivBinOp(
33103317/// Result is always a register.
33113318fn genBinOp(
33123319 self: *Self,
3313 tag: Air.Inst.Tag,
33143320 maybe_inst: ?Air.Inst.Index,
3315 lhs: MCValue,
3316 rhs: MCValue,
3317 lhs_ty: Type,
3318 rhs_ty: Type,
3321 tag: Air.Inst.Tag,
3322 lhs_air: Air.Inst.Ref,
3323 rhs_air: Air.Inst.Ref,
33193324) !MCValue {
3325 const lhs = try self.resolveInst(lhs_air);
3326 const rhs = try self.resolveInst(rhs_air);
3327 const lhs_ty = self.air.typeOf(lhs_air);
3328 const rhs_ty = self.air.typeOf(rhs_air);
33203329 if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) {
33213330 return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()});
33223331 }
......@@ -3352,11 +3361,10 @@ fn genBinOp(
33523361 var flipped: bool = false;
33533362 const dst_mcv: MCValue = blk: {
33543363 if (maybe_inst) |inst| {
3355 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3356 if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) {
3364 if (self.reuseOperand(inst, lhs_air, 0, lhs) and lhs.isRegister()) {
33573365 break :blk lhs;
33583366 }
3359 if (is_commutative and self.reuseOperand(inst, bin_op.rhs, 1, rhs) and rhs.isRegister()) {
3367 if (is_commutative and self.reuseOperand(inst, rhs_air, 1, rhs) and rhs.isRegister()) {
33603368 flipped = true;
33613369 break :blk rhs;
33623370 }
src/codegen/c.zig+16-20
......@@ -1711,21 +1711,18 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
17111711 .unreach => try airUnreach(f),
17121712 .fence => try airFence(f, inst),
17131713
1714 // TODO use a different strategy for add that communicates to the optimizer
1715 // that wrapping is UB.
1716 .add => try airBinOp (f, inst, " + "),
1717 .ptr_add => try airPtrAddSub (f, inst, " + "),
1718 // TODO use a different strategy for sub that communicates to the optimizer
1719 // that wrapping is UB.
1720 .sub => try airBinOp (f, inst, " - "),
1721 .ptr_sub => try airPtrAddSub (f, inst, " - "),
1722 // TODO use a different strategy for mul that communicates to the optimizer
1723 // that wrapping is UB.
1724 .mul => try airBinOp (f, inst, " * "),
1725 // TODO use a different strategy for div that communicates to the optimizer
1726 // that wrapping is UB.
1714 .ptr_add => try airPtrAddSub(f, inst, " + "),
1715 .ptr_sub => try airPtrAddSub(f, inst, " - "),
1716
1717 // TODO use a different strategy for add, sub, mul, div
1718 // that communicates to the optimizer that wrapping is UB.
1719 .add => try airBinOp (f, inst, " + "),
1720 .sub => try airBinOp (f, inst, " - "),
1721 .mul => try airBinOp (f, inst, " * "),
17271722 .div_float, .div_exact => try airBinOp( f, inst, " / "),
1728 .div_trunc => blk: {
1723 .rem => try airBinOp( f, inst, " % "),
1724
1725 .div_trunc => blk: {
17291726 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
17301727 const lhs_ty = f.air.typeOf(bin_op.lhs);
17311728 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),
......@@ -1735,9 +1732,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
17351732 else
17361733 try airBinOpBuiltinCall(f, inst, "div_trunc");
17371734 },
1738 .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"),
1739 .rem => try airBinOp( f, inst, " % "),
1740 .mod => try airBinOpBuiltinCall(f, inst, "mod"),
1735 .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"),
1736 .mod => try airBinOpBuiltinCall(f, inst, "mod"),
17411737
17421738 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
17431739 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
......@@ -2617,10 +2613,10 @@ fn airEquality(
26172613}
26182614
26192615fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
2620 if (f.liveness.isUnused(inst))
2621 return CValue.none;
2616 if (f.liveness.isUnused(inst)) return CValue.none;
26222617
2623 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
2618 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
2619 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
26242620 const lhs = try f.resolveInst(bin_op.lhs);
26252621 const rhs = try f.resolveInst(bin_op.rhs);
26262622
src/codegen/llvm.zig+63-14
......@@ -395,11 +395,11 @@ pub const Object = struct {
395395 return slice.ptr;
396396 }
397397
398 fn genErrorNameTable(self: *Object, comp: *Compilation) !void {
398 fn genErrorNameTable(self: *Object) !void {
399399 // If self.error_name_table is null, there was no instruction that actually referenced the error table.
400400 const error_name_table_ptr_global = self.error_name_table orelse return;
401401
402 const mod = comp.bin_file.options.module.?;
402 const mod = self.module;
403403 const target = mod.getTarget();
404404
405405 const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space
......@@ -413,8 +413,8 @@ pub const Object = struct {
413413 const slice_alignment = slice_ty.abiAlignment(target);
414414
415415 const error_name_list = mod.error_name_list.items;
416 const llvm_errors = try comp.gpa.alloc(*const llvm.Value, error_name_list.len);
417 defer comp.gpa.free(llvm_errors);
416 const llvm_errors = try mod.gpa.alloc(*const llvm.Value, error_name_list.len);
417 defer mod.gpa.free(llvm_errors);
418418
419419 llvm_errors[0] = llvm_slice_ty.getUndef();
420420 for (llvm_errors[1..]) |*llvm_error, i| {
......@@ -447,10 +447,10 @@ pub const Object = struct {
447447 error_name_table_ptr_global.setInitializer(error_name_table_ptr);
448448 }
449449
450 fn genCmpLtErrorsLenFunction(object: *Object, comp: *Compilation) !void {
450 fn genCmpLtErrorsLenFunction(object: *Object) !void {
451451 // If there is no such function in the module, it means the source code does not need it.
452452 const llvm_fn = object.llvm_module.getNamedFunction(lt_errors_fn_name) orelse return;
453 const mod = comp.bin_file.options.module.?;
453 const mod = object.module;
454454 const errors_len = mod.global_error_set.count();
455455
456456 // Delete previous implementation. We replace it with every flush() because the
......@@ -476,10 +476,10 @@ pub const Object = struct {
476476 _ = builder.buildRet(is_lt);
477477 }
478478
479 fn genModuleLevelAssembly(object: *Object, comp: *Compilation) !void {
480 const mod = comp.bin_file.options.module.?;
479 fn genModuleLevelAssembly(object: *Object) !void {
480 const mod = object.module;
481481 if (mod.global_assembly.count() == 0) return;
482 var buffer = std.ArrayList(u8).init(comp.gpa);
482 var buffer = std.ArrayList(u8).init(mod.gpa);
483483 defer buffer.deinit();
484484 var it = mod.global_assembly.iterator();
485485 while (it.next()) |kv| {
......@@ -489,15 +489,53 @@ pub const Object = struct {
489489 object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1);
490490 }
491491
492 fn resolveExportExternCollisions(object: *Object) !void {
493 const mod = object.module;
494
495 const export_keys = mod.decl_exports.keys();
496 for (mod.decl_exports.values()) |export_list, i| {
497 const decl_index = export_keys[i];
498 const llvm_global = object.decl_map.get(decl_index) orelse continue;
499 for (export_list) |exp| {
500 // Detect if the LLVM global has already been created as an extern. In such
501 // case, we need to replace all uses of it with this exported global.
502 // TODO update std.builtin.ExportOptions to have the name be a
503 // null-terminated slice.
504 const exp_name_z = try mod.gpa.dupeZ(u8, exp.options.name);
505 defer mod.gpa.free(exp_name_z);
506
507 const other_global = object.getLlvmGlobal(exp_name_z.ptr) orelse continue;
508 if (other_global == llvm_global) continue;
509
510 // replaceAllUsesWith requires the type to be unchanged. So we bitcast
511 // the new global to the old type and use that as the thing to replace
512 // old uses.
513 const new_global_ptr = llvm_global.constBitCast(other_global.typeOf());
514 other_global.replaceAllUsesWith(new_global_ptr);
515 llvm_global.takeName(other_global);
516 other_global.deleteGlobal();
517 // Problem: now we need to replace in the decl_map that
518 // the extern decl index points to this new global. However we don't
519 // know the decl index.
520 // Even if we did, a future incremental update to the extern would then
521 // treat the LLVM global as an extern rather than an export, so it would
522 // need a way to check that.
523 // This is a TODO that needs to be solved when making
524 // the LLVM backend support incremental compilation.
525 }
526 }
527 }
528
492529 pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void {
493530 var sub_prog_node = prog_node.start("LLVM Emit Object", 0);
494531 sub_prog_node.activate();
495532 sub_prog_node.context.refresh();
496533 defer sub_prog_node.end();
497534
498 try self.genErrorNameTable(comp);
499 try self.genCmpLtErrorsLenFunction(comp);
500 try self.genModuleLevelAssembly(comp);
535 try self.resolveExportExternCollisions();
536 try self.genErrorNameTable();
537 try self.genCmpLtErrorsLenFunction();
538 try self.genModuleLevelAssembly();
501539
502540 if (self.di_builder) |dib| {
503541 // When lowering debug info for pointers, we emitted the element types as
......@@ -761,6 +799,14 @@ pub const Object = struct {
761799 try self.updateDeclExports(module, decl_index, decl_exports);
762800 }
763801
802 /// TODO replace this with a call to `Module::getNamedValue`. This will require adding
803 /// a new wrapper in zig_llvm.h/zig_llvm.cpp.
804 fn getLlvmGlobal(o: Object, name: [*:0]const u8) ?*const llvm.Value {
805 if (o.llvm_module.getNamedFunction(name)) |x| return x;
806 if (o.llvm_module.getNamedGlobal(name)) |x| return x;
807 return null;
808 }
809
764810 pub fn updateDeclExports(
765811 self: *Object,
766812 module: *Module,
......@@ -827,6 +873,7 @@ pub const Object = struct {
827873 llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel);
828874 }
829875 }
876
830877 // If a Decl is exported more than one time (which is rare),
831878 // we add aliases for all but the first export.
832879 // TODO LLVM C API does not support deleting aliases. We need to
......@@ -5632,7 +5679,8 @@ pub const FuncGen = struct {
56325679 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
56335680 if (self.liveness.isUnused(inst)) return null;
56345681
5635 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5682 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5683 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
56365684 const base_ptr = try self.resolveInst(bin_op.lhs);
56375685 const offset = try self.resolveInst(bin_op.rhs);
56385686 const ptr_ty = self.air.typeOf(bin_op.lhs);
......@@ -5651,7 +5699,8 @@ pub const FuncGen = struct {
56515699 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
56525700 if (self.liveness.isUnused(inst)) return null;
56535701
5654 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
5702 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
5703 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
56555704 const base_ptr = try self.resolveInst(bin_op.lhs);
56565705 const offset = try self.resolveInst(bin_op.rhs);
56575706 const negative_offset = self.builder.buildNeg(offset, "");
src/print_air.zig+6-17
......@@ -114,8 +114,6 @@ const Writer = struct {
114114 .div_exact,
115115 .rem,
116116 .mod,
117 .ptr_add,
118 .ptr_sub,
119117 .bit_and,
120118 .bit_or,
121119 .xor,
......@@ -231,6 +229,12 @@ const Writer = struct {
231229 .slice,
232230 .slice_elem_ptr,
233231 .ptr_elem_ptr,
232 .ptr_add,
233 .ptr_sub,
234 .add_with_overflow,
235 .sub_with_overflow,
236 .mul_with_overflow,
237 .shl_with_overflow,
234238 => try w.writeTyPlBin(s, inst),
235239
236240 .call,
......@@ -275,12 +279,6 @@ const Writer = struct {
275279 .reduce => try w.writeReduce(s, inst),
276280 .cmp_vector => try w.writeCmpVector(s, inst),
277281
278 .add_with_overflow,
279 .sub_with_overflow,
280 .mul_with_overflow,
281 .shl_with_overflow,
282 => try w.writeOverflow(s, inst),
283
284282 .dbg_block_begin, .dbg_block_end => {},
285283 }
286284 }
......@@ -478,15 +476,6 @@ const Writer = struct {
478476 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
479477 }
480478
481 fn writeOverflow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
482 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
483 const extra = w.air.extraData(Air.Bin, ty_pl.payload).data;
484
485 try w.writeOperand(s, inst, 0, extra.lhs);
486 try s.writeAll(", ");
487 try w.writeOperand(s, inst, 1, extra.rhs);
488 }
489
490479 fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
491480 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
492481 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
src/value.zig-22
......@@ -1813,27 +1813,6 @@ pub const Value = extern union {
18131813 };
18141814 }
18151815
1816 /// Asserts the value is numeric
1817 pub fn isZero(self: Value) bool {
1818 return switch (self.tag()) {
1819 .zero, .the_only_possible_value => true,
1820 .one => false,
1821
1822 .int_u64 => self.castTag(.int_u64).?.data == 0,
1823 .int_i64 => self.castTag(.int_i64).?.data == 0,
1824
1825 .float_16 => self.castTag(.float_16).?.data == 0,
1826 .float_32 => self.castTag(.float_32).?.data == 0,
1827 .float_64 => self.castTag(.float_64).?.data == 0,
1828 .float_80 => self.castTag(.float_80).?.data == 0,
1829 .float_128 => self.castTag(.float_128).?.data == 0,
1830
1831 .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().eqZero(),
1832 .int_big_negative => self.castTag(.int_big_negative).?.asBigInt().eqZero(),
1833 else => unreachable,
1834 };
1835 }
1836
18371816 pub fn orderAgainstZero(lhs: Value) std.math.Order {
18381817 return orderAgainstZeroAdvanced(lhs, null) catch unreachable;
18391818 }
......@@ -3442,7 +3421,6 @@ pub const Value = extern union {
34423421 const info = ty.intInfo(target);
34433422
34443423 if (info.bits == 0) {
3445 assert(val.isZero()); // Sema should guarantee
34463424 return val;
34473425 }
34483426
test/behavior/align.zig+16-5
......@@ -16,11 +16,22 @@ test "global variable alignment" {
1616 const slice = @as(*align(4) [1]u8, &foo)[0..];
1717 comptime try expect(@TypeOf(slice) == *align(4) [1]u8);
1818 }
19 {
20 var runtime_zero: usize = 0;
21 const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..];
22 comptime try expect(@TypeOf(slice) == []align(4) u8);
23 }
19}
20
21test "slicing array of length 1 can assume runtime index is always zero" {
22 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
23
24 // TODO reevaluate this test case, because notice that you can
25 // change `runtime_zero` to be `1` and the test still passes for stage1.
26 // Reconsider also this code:
27 // var array: [4]u8 = undefined;
28 // var runtime: usize = 4;
29 // var ptr = array[runtime..];
30 // _ = ptr;
31
32 var runtime_zero: usize = 0;
33 const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..];
34 comptime try expect(@TypeOf(slice) == []align(4) u8);
2435}
2536
2637test "default alignment allows unspecified in type syntax" {
test/behavior/basic.zig+5-1
......@@ -797,7 +797,11 @@ test "auto created variables have correct alignment" {
797797}
798798
799799test "extern variable with non-pointer opaque type" {
800 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
800 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
801 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
802 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
803 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
804 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
801805
802806 @export(var_to_export, .{ .name = "opaque_extern_var" });
803807 try expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42);
test/behavior/cast.zig+6-3
......@@ -326,8 +326,7 @@ test "array coersion to undefined at runtime" {
326326
327327 @setRuntimeSafety(true);
328328
329 // TODO implement @setRuntimeSafety in stage2
330 if (builtin.zig_backend != .stage1 and builtin.mode != .Debug and builtin.mode != .ReleaseSafe) {
329 if (builtin.mode != .Debug and builtin.mode != .ReleaseSafe) {
331330 return error.SkipZigTest;
332331 }
333332
......@@ -588,7 +587,11 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
588587}
589588
590589test "vector casts" {
591 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
590 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
591 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
592 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
593 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
594 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
592595
593596 const S = struct {
594597 fn doTheTest() !void {
test/behavior/eval.zig+7
......@@ -1110,3 +1110,10 @@ test "no dependency loop for alignment of self tagged union" {
11101110 };
11111111 try S.doTheTest();
11121112}
1113
1114test "equality of pointers to comptime const" {
1115 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1116
1117 const a: i32 = undefined;
1118 comptime assert(&a == &a);
1119}
test/behavior/pointers.zig+6-4
......@@ -214,7 +214,10 @@ test "allowzero pointer and slice" {
214214}
215215
216216test "assign null directly to C pointer and test null equality" {
217 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
217 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
218 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
219 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
220 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
218221
219222 var x: [*c]i32 = null;
220223 try expect(x == null);
......@@ -238,7 +241,8 @@ test "assign null directly to C pointer and test null equality" {
238241 @panic("fail");
239242 }
240243 const othery: i32 = undefined;
241 comptime try expect((y orelse &othery) == &othery);
244 const ptr_othery = &othery;
245 comptime try expect((y orelse ptr_othery) == ptr_othery);
242246
243247 var n: i32 = 1234;
244248 var x1: [*c]i32 = &n;
......@@ -373,8 +377,6 @@ test "pointer to array at fixed address" {
373377}
374378
375379test "pointer arithmetic affects the alignment" {
376 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
377
378380 {
379381 var ptr: [*]align(8) u32 = undefined;
380382 var x: usize = 1;