| author | |
| committer | |
| log | 8660661af4626c4bd589e6c6331a6ac786143f12 |
| tree | 0c771125a45adab3a9d80cbc6b101cad2bed0fd1 |
| parent | e252f92b9947e82f2473a37a74d5f9dc278a1c1d |
| parent | 9031cc54f27b4483c62ab904dd94063a9499c842 |
| signature |
stage2: bug fixes towards more behavior tests passing20 files changed, 623 insertions(+), 388 deletions(-)
lib/std/Thread.zig+6-5| ... | ... | @@ -893,6 +893,7 @@ const LinuxThreadImpl = struct { |
| 893 | 893 | }; |
| 894 | 894 | |
| 895 | 895 | fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl { |
| 896 | const page_size = std.mem.page_size; | |
| 896 | 897 | const Args = @TypeOf(args); |
| 897 | 898 | const Instance = struct { |
| 898 | 899 | fn_args: Args, |
| ... | ... | @@ -915,11 +916,11 @@ const LinuxThreadImpl = struct { |
| 915 | 916 | var instance_offset: usize = undefined; |
| 916 | 917 | |
| 917 | 918 | const map_bytes = blk: { |
| 918 | var bytes: usize = std.mem.page_size; | |
| 919 | var bytes: usize = page_size; | |
| 919 | 920 | guard_offset = bytes; |
| 920 | 921 | |
| 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); | |
| 923 | 924 | stack_offset = bytes; |
| 924 | 925 | |
| 925 | 926 | bytes = std.mem.alignForward(bytes, linux.tls.tls_image.alloc_align); |
| ... | ... | @@ -930,7 +931,7 @@ const LinuxThreadImpl = struct { |
| 930 | 931 | instance_offset = bytes; |
| 931 | 932 | bytes += @sizeOf(Instance); |
| 932 | 933 | |
| 933 | bytes = std.mem.alignForward(bytes, std.mem.page_size); | |
| 934 | bytes = std.mem.alignForward(bytes, page_size); | |
| 934 | 935 | break :blk bytes; |
| 935 | 936 | }; |
| 936 | 937 | |
| ... | ... | @@ -954,7 +955,7 @@ const LinuxThreadImpl = struct { |
| 954 | 955 | |
| 955 | 956 | // map everything but the guard page as read/write |
| 956 | 957 | os.mprotect( |
| 957 | mapped[guard_offset..], | |
| 958 | @alignCast(page_size, mapped[guard_offset..]), | |
| 958 | 959 | os.PROT.READ | os.PROT.WRITE, |
| 959 | 960 | ) catch |err| switch (err) { |
| 960 | 961 | error.AccessDenied => unreachable, |
lib/std/heap.zig+1-1| ... | ... | @@ -345,7 +345,7 @@ const PageAllocator = struct { |
| 345 | 345 | // Unmap extra pages |
| 346 | 346 | const aligned_buffer_len = alloc_len - drop_len; |
| 347 | 347 | 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])); | |
| 349 | 349 | } |
| 350 | 350 | |
| 351 | 351 | const new_hint = @alignCast(mem.page_size, result_ptr + aligned_len); |
src/Air.zig+4-4| ... | ... | @@ -113,13 +113,13 @@ pub const Inst = struct { |
| 113 | 113 | /// The offset is in element type units, not bytes. |
| 114 | 114 | /// Wrapping is undefined behavior. |
| 115 | 115 | /// 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`. | |
| 117 | 117 | ptr_add, |
| 118 | 118 | /// Subtract an offset from a pointer, returning a new pointer. |
| 119 | 119 | /// The offset is in element type units, not bytes. |
| 120 | 120 | /// Wrapping is undefined behavior. |
| 121 | 121 | /// 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`. | |
| 123 | 123 | ptr_sub, |
| 124 | 124 | /// Given two operands which can be floats, integers, or vectors, returns the |
| 125 | 125 | /// greater of the operands. For vectors it operates element-wise. |
| ... | ... | @@ -916,8 +916,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 916 | 916 | .bit_and, |
| 917 | 917 | .bit_or, |
| 918 | 918 | .xor, |
| 919 | .ptr_add, | |
| 920 | .ptr_sub, | |
| 921 | 919 | .shr, |
| 922 | 920 | .shr_exact, |
| 923 | 921 | .shl, |
| ... | ... | @@ -989,6 +987,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 989 | 987 | .sub_with_overflow, |
| 990 | 988 | .mul_with_overflow, |
| 991 | 989 | .shl_with_overflow, |
| 990 | .ptr_add, | |
| 991 | .ptr_sub, | |
| 992 | 992 | => return air.getRefType(datas[inst].ty_pl.ty), |
| 993 | 993 | |
| 994 | 994 | .not, |
src/Liveness.zig+16-15| ... | ... | @@ -312,8 +312,6 @@ fn analyzeInst( |
| 312 | 312 | .div_exact, |
| 313 | 313 | .rem, |
| 314 | 314 | .mod, |
| 315 | .ptr_add, | |
| 316 | .ptr_sub, | |
| 317 | 315 | .bit_and, |
| 318 | 316 | .bit_or, |
| 319 | 317 | .xor, |
| ... | ... | @@ -441,6 +439,21 @@ fn analyzeInst( |
| 441 | 439 | return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); |
| 442 | 440 | }, |
| 443 | 441 | |
| 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 | ||
| 444 | 457 | .dbg_var_ptr, |
| 445 | 458 | .dbg_var_val, |
| 446 | 459 | => { |
| ... | ... | @@ -529,10 +542,6 @@ fn analyzeInst( |
| 529 | 542 | const extra = a.air.extraData(Air.FieldParentPtr, inst_datas[inst].ty_pl.payload).data; |
| 530 | 543 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.field_ptr, .none, .none }); |
| 531 | 544 | }, |
| 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 | }, | |
| 536 | 545 | .cmpxchg_strong, .cmpxchg_weak => { |
| 537 | 546 | const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data; |
| 538 | 547 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value }); |
| ... | ... | @@ -558,15 +567,7 @@ fn analyzeInst( |
| 558 | 567 | const extra = a.air.extraData(Air.Bin, pl_op.payload).data; |
| 559 | 568 | return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); |
| 560 | 569 | }, |
| 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 | ||
| 570 | 571 | .br => { |
| 571 | 572 | const br = inst_datas[inst].br; |
| 572 | 573 | return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); |
src/Sema.zig+123-42| ... | ... | @@ -7020,22 +7020,25 @@ fn intCast( |
| 7020 | 7020 | operand_src: LazySrcLoc, |
| 7021 | 7021 | runtime_safety: bool, |
| 7022 | 7022 | ) 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); | |
| 7026 | 7026 | |
| 7027 | 7027 | if (try sema.isComptimeKnown(block, operand_src, operand)) { |
| 7028 | 7028 | return sema.coerce(block, dest_ty, operand, operand_src); |
| 7029 | } else if (dest_is_comptime_int) { | |
| 7029 | } else if (dest_scalar_ty.zigTypeTag() == .ComptimeInt) { | |
| 7030 | 7030 | return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); |
| 7031 | 7031 | } |
| 7032 | 7032 | |
| 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 | ||
| 7033 | 7036 | if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| { |
| 7034 | 7037 | // requirement: intCast(u0, input) iff input == 0 |
| 7035 | 7038 | if (runtime_safety and block.wantSafety()) { |
| 7036 | 7039 | try sema.requireRuntimeBlock(block, operand_src); |
| 7037 | 7040 | const target = sema.mod.getTarget(); |
| 7038 | const wanted_info = dest_ty.intInfo(target); | |
| 7041 | const wanted_info = dest_scalar_ty.intInfo(target); | |
| 7039 | 7042 | const wanted_bits = wanted_info.bits; |
| 7040 | 7043 | |
| 7041 | 7044 | if (wanted_bits == 0) { |
| ... | ... | @@ -7051,9 +7054,8 @@ fn intCast( |
| 7051 | 7054 | try sema.requireRuntimeBlock(block, operand_src); |
| 7052 | 7055 | if (runtime_safety and block.wantSafety()) { |
| 7053 | 7056 | 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); | |
| 7057 | 7059 | const actual_bits = actual_info.bits; |
| 7058 | 7060 | const wanted_bits = wanted_info.bits; |
| 7059 | 7061 | const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed); |
| ... | ... | @@ -7062,7 +7064,11 @@ fn intCast( |
| 7062 | 7064 | // range shrinkage |
| 7063 | 7065 | // requirement: int value fits into target type |
| 7064 | 7066 | 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; | |
| 7066 | 7072 | const dest_max = try sema.addConstant(operand_ty, dest_max_val); |
| 7067 | 7073 | const diff = try block.addBinOp(.subwrap, dest_max, operand); |
| 7068 | 7074 | |
| ... | ... | @@ -7080,19 +7086,59 @@ fn intCast( |
| 7080 | 7086 | } else dest_max_val; |
| 7081 | 7087 | const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); |
| 7082 | 7088 | |
| 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); | |
| 7085 | 7104 | } 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); | |
| 7088 | 7120 | } |
| 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); | |
| 7096 | 7142 | } |
| 7097 | 7143 | } |
| 7098 | 7144 | return block.addTyOp(.intcast, dest_ty, operand); |
| ... | ... | @@ -10610,28 +10656,55 @@ fn analyzePtrArithmetic( |
| 10610 | 10656 | // TODO if the operand is comptime-known to be negative, or is a negative int, |
| 10611 | 10657 | // coerce to isize instead of usize. |
| 10612 | 10658 | const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); |
| 10613 | // TODO adjust the return type according to alignment and other factors | |
| 10614 | 10659 | 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 | }; | |
| 10620 | 10699 | |
| 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); | |
| 10624 | 10704 | |
| 10625 | 10705 | 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 | |
| 10627 | 10706 | if (offset_int == 0) return ptr; |
| 10628 | 10707 | 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 | ||
| 10635 | 10708 | const elem_size = elem_ty.abiSize(target); |
| 10636 | 10709 | const new_addr = switch (air_tag) { |
| 10637 | 10710 | .ptr_add => addr + elem_size * offset_int, |
| ... | ... | @@ -10651,7 +10724,16 @@ fn analyzePtrArithmetic( |
| 10651 | 10724 | }; |
| 10652 | 10725 | |
| 10653 | 10726 | 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 | }); | |
| 10655 | 10737 | } |
| 10656 | 10738 | |
| 10657 | 10739 | fn 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 |
| 14481 | 14563 | const dest_scalar_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); |
| 14482 | 14564 | const operand = sema.resolveInst(extra.rhs); |
| 14483 | 14565 | 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); | |
| 14485 | 14566 | const operand_ty = sema.typeOf(operand); |
| 14567 | const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); | |
| 14486 | 14568 | const is_vector = operand_ty.zigTypeTag() == .Vector; |
| 14487 | 14569 | const dest_ty = if (is_vector) |
| 14488 | 14570 | 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 |
| 14650 | 14732 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 14651 | 14733 | const operand = sema.resolveInst(inst_data.operand); |
| 14652 | 14734 | 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); | |
| 14654 | 14736 | const target = sema.mod.getTarget(); |
| 14655 | 14737 | const bits = scalar_ty.intInfo(target).bits; |
| 14656 | 14738 | if (bits % 8 != 0) { |
| ... | ... | @@ -14707,7 +14789,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 14707 | 14789 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 14708 | 14790 | const operand = sema.resolveInst(inst_data.operand); |
| 14709 | 14791 | const operand_ty = sema.typeOf(operand); |
| 14710 | _ = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src); | |
| 14792 | _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); | |
| 14711 | 14793 | |
| 14712 | 14794 | if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| { |
| 14713 | 14795 | return sema.addConstant(operand_ty, val); |
| ... | ... | @@ -15059,10 +15141,9 @@ fn checkIntOrVector( |
| 15059 | 15141 | fn checkIntOrVectorAllowComptime( |
| 15060 | 15142 | sema: *Sema, |
| 15061 | 15143 | block: *Block, |
| 15062 | operand: Air.Inst.Ref, | |
| 15144 | operand_ty: Type, | |
| 15063 | 15145 | operand_src: LazySrcLoc, |
| 15064 | 15146 | ) CompileError!Type { |
| 15065 | const operand_ty = sema.typeOf(operand); | |
| 15066 | 15147 | switch (try operand_ty.zigTypeTagOrPoison()) { |
| 15067 | 15148 | .Int, .ComptimeInt => return operand_ty, |
| 15068 | 15149 | .Vector => { |
src/arch/aarch64/CodeGen.zig+98-68| ... | ... | @@ -545,18 +545,30 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 545 | 545 | |
| 546 | 546 | switch (air_tags[inst]) { |
| 547 | 547 | // 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 | ||
| 550 | 567 | .add_sat => try self.airAddSat(inst), |
| 551 | .sub, .ptr_sub => try self.airBinOp(inst), | |
| 552 | .subwrap => try self.airBinOp(inst), | |
| 553 | 568 | .sub_sat => try self.airSubSat(inst), |
| 554 | .mul => try self.airBinOp(inst), | |
| 555 | .mulwrap => try self.airBinOp(inst), | |
| 556 | 569 | .mul_sat => try self.airMulSat(inst), |
| 557 | 570 | .rem => try self.airRem(inst), |
| 558 | 571 | .mod => try self.airMod(inst), |
| 559 | .shl, .shl_exact => try self.airBinOp(inst), | |
| 560 | 572 | .shl_sat => try self.airShlSat(inst), |
| 561 | 573 | .min => try self.airMin(inst), |
| 562 | 574 | .max => try self.airMax(inst), |
| ... | ... | @@ -595,13 +607,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 595 | 607 | .cmp_vector => try self.airCmpVector(inst), |
| 596 | 608 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 597 | 609 | |
| 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 | ||
| 605 | 610 | .alloc => try self.airAlloc(inst), |
| 606 | 611 | .ret_ptr => try self.airRetPtr(inst), |
| 607 | 612 | .arg => try self.airArg(inst), |
| ... | ... | @@ -1260,11 +1265,11 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1260 | 1265 | fn binOpRegister( |
| 1261 | 1266 | self: *Self, |
| 1262 | 1267 | mir_tag: Mir.Inst.Tag, |
| 1263 | maybe_inst: ?Air.Inst.Index, | |
| 1264 | 1268 | lhs: MCValue, |
| 1265 | 1269 | rhs: MCValue, |
| 1266 | 1270 | lhs_ty: Type, |
| 1267 | 1271 | rhs_ty: Type, |
| 1272 | metadata: ?BinOpMetadata, | |
| 1268 | 1273 | ) !MCValue { |
| 1269 | 1274 | const lhs_is_register = lhs == .register; |
| 1270 | 1275 | const rhs_is_register = rhs == .register; |
| ... | ... | @@ -1284,9 +1289,8 @@ fn binOpRegister( |
| 1284 | 1289 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1285 | 1290 | |
| 1286 | 1291 | 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).?; | |
| 1290 | 1294 | } else null; |
| 1291 | 1295 | |
| 1292 | 1296 | const raw_reg = try self.register_manager.allocReg(track_inst); |
| ... | ... | @@ -1300,9 +1304,8 @@ fn binOpRegister( |
| 1300 | 1304 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1301 | 1305 | |
| 1302 | 1306 | 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).?; | |
| 1306 | 1309 | } else null; |
| 1307 | 1310 | |
| 1308 | 1311 | const raw_reg = try self.register_manager.allocReg(track_inst); |
| ... | ... | @@ -1317,15 +1320,13 @@ fn binOpRegister( |
| 1317 | 1320 | |
| 1318 | 1321 | const dest_reg = switch (mir_tag) { |
| 1319 | 1322 | .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)) { | |
| 1324 | 1325 | 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)) { | |
| 1326 | 1327 | break :blk rhs_reg; |
| 1327 | 1328 | } else { |
| 1328 | const raw_reg = try self.register_manager.allocReg(inst); | |
| 1329 | const raw_reg = try self.register_manager.allocReg(md.inst); | |
| 1329 | 1330 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1330 | 1331 | } |
| 1331 | 1332 | } else blk: { |
| ... | ... | @@ -1407,11 +1408,11 @@ fn binOpRegister( |
| 1407 | 1408 | fn binOpImmediate( |
| 1408 | 1409 | self: *Self, |
| 1409 | 1410 | mir_tag: Mir.Inst.Tag, |
| 1410 | maybe_inst: ?Air.Inst.Index, | |
| 1411 | 1411 | lhs: MCValue, |
| 1412 | 1412 | rhs: MCValue, |
| 1413 | 1413 | lhs_ty: Type, |
| 1414 | 1414 | lhs_and_rhs_swapped: bool, |
| 1415 | metadata: ?BinOpMetadata, | |
| 1415 | 1416 | ) !MCValue { |
| 1416 | 1417 | const lhs_is_register = lhs == .register; |
| 1417 | 1418 | |
| ... | ... | @@ -1424,10 +1425,9 @@ fn binOpImmediate( |
| 1424 | 1425 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1425 | 1426 | |
| 1426 | 1427 | 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: { | |
| 1429 | 1429 | 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, | |
| 1431 | 1431 | ).?; |
| 1432 | 1432 | } else null; |
| 1433 | 1433 | |
| ... | ... | @@ -1443,18 +1443,16 @@ fn binOpImmediate( |
| 1443 | 1443 | |
| 1444 | 1444 | const dest_reg = switch (mir_tag) { |
| 1445 | 1445 | .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: { | |
| 1449 | 1447 | 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, | |
| 1452 | 1450 | if (lhs_and_rhs_swapped) 1 else 0, |
| 1453 | 1451 | lhs, |
| 1454 | 1452 | )) { |
| 1455 | 1453 | break :blk lhs_reg; |
| 1456 | 1454 | } else { |
| 1457 | const raw_reg = try self.register_manager.allocReg(inst); | |
| 1455 | const raw_reg = try self.register_manager.allocReg(md.inst); | |
| 1458 | 1456 | break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); |
| 1459 | 1457 | } |
| 1460 | 1458 | } else blk: { |
| ... | ... | @@ -1498,6 +1496,12 @@ fn binOpImmediate( |
| 1498 | 1496 | return MCValue{ .register = dest_reg }; |
| 1499 | 1497 | } |
| 1500 | 1498 | |
| 1499 | const BinOpMetadata = struct { | |
| 1500 | inst: Air.Inst.Index, | |
| 1501 | lhs: Air.Inst.Ref, | |
| 1502 | rhs: Air.Inst.Ref, | |
| 1503 | }; | |
| 1504 | ||
| 1501 | 1505 | /// For all your binary operation needs, this function will generate |
| 1502 | 1506 | /// the corresponding Mir instruction(s). Returns the location of the |
| 1503 | 1507 | /// result. |
| ... | ... | @@ -1513,11 +1517,11 @@ fn binOpImmediate( |
| 1513 | 1517 | fn binOp( |
| 1514 | 1518 | self: *Self, |
| 1515 | 1519 | tag: Air.Inst.Tag, |
| 1516 | maybe_inst: ?Air.Inst.Index, | |
| 1517 | 1520 | lhs: MCValue, |
| 1518 | 1521 | rhs: MCValue, |
| 1519 | 1522 | lhs_ty: Type, |
| 1520 | 1523 | rhs_ty: Type, |
| 1524 | metadata: ?BinOpMetadata, | |
| 1521 | 1525 | ) InnerError!MCValue { |
| 1522 | 1526 | const mod = self.bin_file.options.module.?; |
| 1523 | 1527 | switch (tag) { |
| ... | ... | @@ -1562,12 +1566,12 @@ fn binOp( |
| 1562 | 1566 | }; |
| 1563 | 1567 | |
| 1564 | 1568 | 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); | |
| 1566 | 1570 | } else if (lhs_immediate_ok) { |
| 1567 | 1571 | // 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); | |
| 1569 | 1573 | } 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); | |
| 1571 | 1575 | } |
| 1572 | 1576 | } else { |
| 1573 | 1577 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| ... | ... | @@ -1586,7 +1590,7 @@ fn binOp( |
| 1586 | 1590 | // TODO add optimisations for multiplication |
| 1587 | 1591 | // with immediates, for example a * 2 can be |
| 1588 | 1592 | // 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); | |
| 1590 | 1594 | } else { |
| 1591 | 1595 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1592 | 1596 | } |
| ... | ... | @@ -1606,7 +1610,7 @@ fn binOp( |
| 1606 | 1610 | }; |
| 1607 | 1611 | |
| 1608 | 1612 | // 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); | |
| 1610 | 1614 | |
| 1611 | 1615 | // Truncate if necessary |
| 1612 | 1616 | switch (lhs_ty.zigTypeTag()) { |
| ... | ... | @@ -1642,7 +1646,7 @@ fn binOp( |
| 1642 | 1646 | else => unreachable, |
| 1643 | 1647 | }; |
| 1644 | 1648 | |
| 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); | |
| 1646 | 1650 | } else { |
| 1647 | 1651 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1648 | 1652 | } |
| ... | ... | @@ -1678,9 +1682,9 @@ fn binOp( |
| 1678 | 1682 | }; |
| 1679 | 1683 | |
| 1680 | 1684 | 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); | |
| 1682 | 1686 | } 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); | |
| 1684 | 1688 | } |
| 1685 | 1689 | } else { |
| 1686 | 1690 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| ... | ... | @@ -1699,7 +1703,7 @@ fn binOp( |
| 1699 | 1703 | }; |
| 1700 | 1704 | |
| 1701 | 1705 | // 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); | |
| 1703 | 1707 | |
| 1704 | 1708 | // Truncate if necessary |
| 1705 | 1709 | switch (tag) { |
| ... | ... | @@ -1735,7 +1739,7 @@ fn binOp( |
| 1735 | 1739 | else => unreachable, |
| 1736 | 1740 | }; |
| 1737 | 1741 | |
| 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); | |
| 1739 | 1743 | }, |
| 1740 | 1744 | else => unreachable, |
| 1741 | 1745 | } |
| ... | ... | @@ -1759,12 +1763,12 @@ fn binOp( |
| 1759 | 1763 | else => unreachable, |
| 1760 | 1764 | }; |
| 1761 | 1765 | |
| 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); | |
| 1763 | 1767 | } else { |
| 1764 | 1768 | // convert the offset into a byte offset by |
| 1765 | 1769 | // 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); | |
| 1768 | 1772 | return addr; |
| 1769 | 1773 | } |
| 1770 | 1774 | }, |
| ... | ... | @@ -1775,8 +1779,7 @@ fn binOp( |
| 1775 | 1779 | } |
| 1776 | 1780 | } |
| 1777 | 1781 | |
| 1778 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { | |
| 1779 | const tag = self.air.instructions.items(.tag)[inst]; | |
| 1782 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | |
| 1780 | 1783 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1781 | 1784 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1782 | 1785 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | ... | @@ -1786,7 +1789,30 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1786 | 1789 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 1787 | 1790 | .dead |
| 1788 | 1791 | 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 | ||
| 1800 | fn 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 | }); | |
| 1790 | 1816 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1791 | 1817 | } |
| 1792 | 1818 | |
| ... | ... | @@ -1841,7 +1867,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1841 | 1867 | .sub_with_overflow => .sub, |
| 1842 | 1868 | else => unreachable, |
| 1843 | 1869 | }; |
| 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); | |
| 1845 | 1871 | const dest_reg = dest.register; |
| 1846 | 1872 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 1847 | 1873 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | ... | @@ -1855,7 +1881,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1855 | 1881 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 1856 | 1882 | |
| 1857 | 1883 | // 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); | |
| 1859 | 1885 | |
| 1860 | 1886 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1861 | 1887 | 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 { |
| 1894 | 1920 | |
| 1895 | 1921 | const dest = blk: { |
| 1896 | 1922 | 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); | |
| 1898 | 1924 | } else if (lhs_immediate_ok) { |
| 1899 | 1925 | // 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); | |
| 1901 | 1927 | } 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); | |
| 1903 | 1929 | } |
| 1904 | 1930 | }; |
| 1905 | 1931 | |
| ... | ... | @@ -1952,7 +1978,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1952 | 1978 | .unsigned => .umull, |
| 1953 | 1979 | }; |
| 1954 | 1980 | |
| 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); | |
| 1956 | 1982 | const dest_reg = dest.register; |
| 1957 | 1983 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 1958 | 1984 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | ... | @@ -2136,11 +2162,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2136 | 2162 | |
| 2137 | 2163 | _ = try self.binOp( |
| 2138 | 2164 | .cmp_eq, |
| 2139 | null, | |
| 2140 | 2165 | .{ .register = dest_high_reg }, |
| 2141 | 2166 | .{ .immediate = 0 }, |
| 2142 | 2167 | Type.usize, |
| 2143 | 2168 | Type.usize, |
| 2169 | null, | |
| 2144 | 2170 | ); |
| 2145 | 2171 | |
| 2146 | 2172 | if (int_info.bits < 64) { |
| ... | ... | @@ -2156,11 +2182,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2156 | 2182 | |
| 2157 | 2183 | _ = try self.binOp( |
| 2158 | 2184 | .cmp_eq, |
| 2159 | null, | |
| 2160 | 2185 | .{ .register = dest_high_reg }, |
| 2161 | 2186 | .{ .immediate = 0 }, |
| 2162 | 2187 | Type.usize, |
| 2163 | 2188 | Type.usize, |
| 2189 | null, | |
| 2164 | 2190 | ); |
| 2165 | 2191 | } |
| 2166 | 2192 | }, |
| ... | ... | @@ -2218,16 +2244,16 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2218 | 2244 | self.compare_flags_inst = null; |
| 2219 | 2245 | |
| 2220 | 2246 | // 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); | |
| 2222 | 2248 | const dest_reg = dest.register; |
| 2223 | 2249 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 2224 | 2250 | defer self.register_manager.unlockReg(dest_reg_lock); |
| 2225 | 2251 | |
| 2226 | 2252 | // 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); | |
| 2228 | 2254 | |
| 2229 | 2255 | // 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); | |
| 2231 | 2257 | |
| 2232 | 2258 | try self.genSetStack(lhs_ty, stack_offset, dest); |
| 2233 | 2259 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ |
| ... | ... | @@ -2489,7 +2515,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2489 | 2515 | switch (elem_size) { |
| 2490 | 2516 | else => { |
| 2491 | 2517 | 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); | |
| 2493 | 2519 | try self.load(dest, addr, slice_ptr_field_type); |
| 2494 | 2520 | |
| 2495 | 2521 | break :result dest; |
| ... | ... | @@ -2933,11 +2959,11 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 2933 | 2959 | |
| 2934 | 2960 | const dest = try self.binOp( |
| 2935 | 2961 | .add, |
| 2936 | null, | |
| 2937 | 2962 | .{ .register = addr_reg }, |
| 2938 | 2963 | .{ .register = offset_reg }, |
| 2939 | 2964 | Type.usize, |
| 2940 | 2965 | Type.usize, |
| 2966 | null, | |
| 2941 | 2967 | ); |
| 2942 | 2968 | |
| 2943 | 2969 | break :result dest; |
| ... | ... | @@ -3302,7 +3328,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3302 | 3328 | |
| 3303 | 3329 | const int_info = int_ty.intInfo(self.target.*); |
| 3304 | 3330 | 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 | }); | |
| 3306 | 3336 | |
| 3307 | 3337 | try self.spillCompareFlagsIfOccupied(); |
| 3308 | 3338 | 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 { |
| 552 | 552 | |
| 553 | 553 | switch (air_tags[inst]) { |
| 554 | 554 | // 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 | ||
| 557 | 577 | .add_sat => try self.airAddSat(inst), |
| 558 | .sub, .ptr_sub => try self.airBinOp(inst), | |
| 559 | .subwrap => try self.airBinOp(inst), | |
| 560 | 578 | .sub_sat => try self.airSubSat(inst), |
| 561 | .mul => try self.airBinOp(inst), | |
| 562 | .mulwrap => try self.airBinOp(inst), | |
| 563 | 579 | .mul_sat => try self.airMulSat(inst), |
| 564 | 580 | .rem => try self.airRem(inst), |
| 565 | 581 | .mod => try self.airMod(inst), |
| 566 | .shl, .shl_exact => try self.airBinOp(inst), | |
| 567 | 582 | .shl_sat => try self.airShlSat(inst), |
| 568 | .min => try self.airMinMax(inst), | |
| 569 | .max => try self.airMinMax(inst), | |
| 570 | 583 | .slice => try self.airSlice(inst), |
| 571 | 584 | |
| 572 | 585 | .sqrt, |
| ... | ... | @@ -602,13 +615,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 602 | 615 | .cmp_vector => try self.airCmpVector(inst), |
| 603 | 616 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 604 | 617 | |
| 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 | ||
| 612 | 618 | .alloc => try self.airAlloc(inst), |
| 613 | 619 | .ret_ptr => try self.airRetPtr(inst), |
| 614 | 620 | .arg => try self.airArg(inst), |
| ... | ... | @@ -1260,7 +1266,7 @@ fn minMax( |
| 1260 | 1266 | // register. |
| 1261 | 1267 | assert(lhs_reg != rhs_reg); // see note above |
| 1262 | 1268 | |
| 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); | |
| 1264 | 1270 | |
| 1265 | 1271 | const cond_choose_lhs: Condition = switch (tag) { |
| 1266 | 1272 | .max => switch (int_info.signedness) { |
| ... | ... | @@ -1340,15 +1346,40 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1340 | 1346 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1341 | 1347 | } |
| 1342 | 1348 | |
| 1343 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { | |
| 1344 | const tag = self.air.instructions.items(.tag)[inst]; | |
| 1349 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | |
| 1345 | 1350 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1346 | 1351 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1347 | 1352 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1348 | 1353 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1349 | 1354 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1350 | 1355 | |
| 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 | ||
| 1367 | fn 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 | }); | |
| 1352 | 1383 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1353 | 1384 | } |
| 1354 | 1385 | |
| ... | ... | @@ -1402,7 +1433,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1402 | 1433 | .sub_with_overflow => .sub, |
| 1403 | 1434 | else => unreachable, |
| 1404 | 1435 | }; |
| 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); | |
| 1406 | 1437 | const dest_reg = dest.register; |
| 1407 | 1438 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 1408 | 1439 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | ... | @@ -1415,7 +1446,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1415 | 1446 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 1416 | 1447 | |
| 1417 | 1448 | // 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); | |
| 1419 | 1450 | |
| 1420 | 1451 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1421 | 1452 | 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 { |
| 1448 | 1479 | |
| 1449 | 1480 | const dest = blk: { |
| 1450 | 1481 | 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); | |
| 1452 | 1483 | } else if (lhs_immediate_ok) { |
| 1453 | 1484 | // 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); | |
| 1455 | 1486 | } 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); | |
| 1457 | 1488 | } |
| 1458 | 1489 | }; |
| 1459 | 1490 | |
| ... | ... | @@ -1507,7 +1538,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1507 | 1538 | .unsigned => .mul, |
| 1508 | 1539 | }; |
| 1509 | 1540 | |
| 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); | |
| 1511 | 1542 | const dest_reg = dest.register; |
| 1512 | 1543 | const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 1513 | 1544 | defer self.register_manager.unlockReg(dest_reg_lock); |
| ... | ... | @@ -1520,7 +1551,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1520 | 1551 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 1521 | 1552 | |
| 1522 | 1553 | // 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); | |
| 1524 | 1555 | |
| 1525 | 1556 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1526 | 1557 | 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 { |
| 1594 | 1625 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 1595 | 1626 | |
| 1596 | 1627 | // 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); | |
| 1598 | 1629 | |
| 1599 | 1630 | // mov rdlo, #0 |
| 1600 | 1631 | _ = try self.addInst(.{ |
| ... | ... | @@ -1618,7 +1649,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1618 | 1649 | }); |
| 1619 | 1650 | |
| 1620 | 1651 | // 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); | |
| 1622 | 1653 | |
| 1623 | 1654 | // movne rdlo, #1 |
| 1624 | 1655 | _ = try self.addInst(.{ |
| ... | ... | @@ -1677,16 +1708,16 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1677 | 1708 | self.compare_flags_inst = null; |
| 1678 | 1709 | |
| 1679 | 1710 | // 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); | |
| 1681 | 1712 | const dest_reg = dest.register; |
| 1682 | 1713 | const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg); |
| 1683 | 1714 | defer self.register_manager.unlockReg(dest_lock); |
| 1684 | 1715 | |
| 1685 | 1716 | // 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); | |
| 1687 | 1718 | |
| 1688 | 1719 | // 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); | |
| 1690 | 1721 | |
| 1691 | 1722 | try self.genSetStack(lhs_ty, stack_offset, dest); |
| 1692 | 1723 | 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 { |
| 2031 | 2062 | }, |
| 2032 | 2063 | else => { |
| 2033 | 2064 | 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); | |
| 2035 | 2066 | try self.load(dest, addr, slice_ptr_field_type); |
| 2036 | 2067 | |
| 2037 | 2068 | break :result dest; |
| ... | ... | @@ -2051,7 +2082,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2051 | 2082 | |
| 2052 | 2083 | const slice_ty = self.air.typeOf(extra.lhs); |
| 2053 | 2084 | |
| 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); | |
| 2055 | 2086 | break :result addr; |
| 2056 | 2087 | }; |
| 2057 | 2088 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| ... | ... | @@ -2079,7 +2110,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2079 | 2110 | |
| 2080 | 2111 | const ptr_ty = self.air.typeOf(extra.lhs); |
| 2081 | 2112 | |
| 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); | |
| 2083 | 2114 | break :result addr; |
| 2084 | 2115 | }; |
| 2085 | 2116 | 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 |
| 2411 | 2442 | |
| 2412 | 2443 | const dest = try self.binOp( |
| 2413 | 2444 | .add, |
| 2414 | null, | |
| 2415 | 2445 | .{ .register = addr_reg }, |
| 2416 | 2446 | .{ .register = offset_reg }, |
| 2417 | 2447 | Type.usize, |
| 2418 | 2448 | Type.usize, |
| 2449 | null, | |
| 2419 | 2450 | ); |
| 2420 | 2451 | |
| 2421 | 2452 | break :result dest; |
| ... | ... | @@ -2514,11 +2545,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2514 | 2545 | fn binOpRegister( |
| 2515 | 2546 | self: *Self, |
| 2516 | 2547 | mir_tag: Mir.Inst.Tag, |
| 2517 | maybe_inst: ?Air.Inst.Index, | |
| 2518 | 2548 | lhs: MCValue, |
| 2519 | 2549 | rhs: MCValue, |
| 2520 | 2550 | lhs_ty: Type, |
| 2521 | 2551 | rhs_ty: Type, |
| 2552 | metadata: ?BinOpMetadata, | |
| 2522 | 2553 | ) !MCValue { |
| 2523 | 2554 | const lhs_is_register = lhs == .register; |
| 2524 | 2555 | const rhs_is_register = rhs == .register; |
| ... | ... | @@ -2532,9 +2563,8 @@ fn binOpRegister( |
| 2532 | 2563 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 2533 | 2564 | |
| 2534 | 2565 | 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).?; | |
| 2538 | 2568 | } else null; |
| 2539 | 2569 | |
| 2540 | 2570 | const reg = try self.register_manager.allocReg(track_inst); |
| ... | ... | @@ -2547,9 +2577,8 @@ fn binOpRegister( |
| 2547 | 2577 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 2548 | 2578 | |
| 2549 | 2579 | 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).?; | |
| 2553 | 2582 | } else null; |
| 2554 | 2583 | |
| 2555 | 2584 | const reg = try self.register_manager.allocReg(track_inst); |
| ... | ... | @@ -2563,15 +2592,13 @@ fn binOpRegister( |
| 2563 | 2592 | |
| 2564 | 2593 | const dest_reg = switch (mir_tag) { |
| 2565 | 2594 | .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)) { | |
| 2570 | 2597 | 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)) { | |
| 2572 | 2599 | break :blk rhs_reg; |
| 2573 | 2600 | } else { |
| 2574 | break :blk try self.register_manager.allocReg(inst); | |
| 2601 | break :blk try self.register_manager.allocReg(md.inst); | |
| 2575 | 2602 | } |
| 2576 | 2603 | } else try self.register_manager.allocReg(null), |
| 2577 | 2604 | }; |
| ... | ... | @@ -2634,11 +2661,11 @@ fn binOpRegister( |
| 2634 | 2661 | fn binOpImmediate( |
| 2635 | 2662 | self: *Self, |
| 2636 | 2663 | mir_tag: Mir.Inst.Tag, |
| 2637 | maybe_inst: ?Air.Inst.Index, | |
| 2638 | 2664 | lhs: MCValue, |
| 2639 | 2665 | rhs: MCValue, |
| 2640 | 2666 | lhs_ty: Type, |
| 2641 | 2667 | lhs_and_rhs_swapped: bool, |
| 2668 | metadata: ?BinOpMetadata, | |
| 2642 | 2669 | ) !MCValue { |
| 2643 | 2670 | const lhs_is_register = lhs == .register; |
| 2644 | 2671 | |
| ... | ... | @@ -2651,10 +2678,9 @@ fn binOpImmediate( |
| 2651 | 2678 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 2652 | 2679 | |
| 2653 | 2680 | 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: { | |
| 2656 | 2682 | 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, | |
| 2658 | 2684 | ).?; |
| 2659 | 2685 | } else null; |
| 2660 | 2686 | |
| ... | ... | @@ -2669,18 +2695,16 @@ fn binOpImmediate( |
| 2669 | 2695 | |
| 2670 | 2696 | const dest_reg = switch (mir_tag) { |
| 2671 | 2697 | .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: { | |
| 2675 | 2699 | 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, | |
| 2678 | 2702 | if (lhs_and_rhs_swapped) 1 else 0, |
| 2679 | 2703 | lhs, |
| 2680 | 2704 | )) { |
| 2681 | 2705 | break :blk lhs_reg; |
| 2682 | 2706 | } else { |
| 2683 | break :blk try self.register_manager.allocReg(inst); | |
| 2707 | break :blk try self.register_manager.allocReg(md.inst); | |
| 2684 | 2708 | } |
| 2685 | 2709 | } else try self.register_manager.allocReg(null), |
| 2686 | 2710 | }; |
| ... | ... | @@ -2720,6 +2744,12 @@ fn binOpImmediate( |
| 2720 | 2744 | return MCValue{ .register = dest_reg }; |
| 2721 | 2745 | } |
| 2722 | 2746 | |
| 2747 | const BinOpMetadata = struct { | |
| 2748 | inst: Air.Inst.Index, | |
| 2749 | lhs: Air.Inst.Ref, | |
| 2750 | rhs: Air.Inst.Ref, | |
| 2751 | }; | |
| 2752 | ||
| 2723 | 2753 | /// For all your binary operation needs, this function will generate |
| 2724 | 2754 | /// the corresponding Mir instruction(s). Returns the location of the |
| 2725 | 2755 | /// result. |
| ... | ... | @@ -2735,11 +2765,11 @@ fn binOpImmediate( |
| 2735 | 2765 | fn binOp( |
| 2736 | 2766 | self: *Self, |
| 2737 | 2767 | tag: Air.Inst.Tag, |
| 2738 | maybe_inst: ?Air.Inst.Index, | |
| 2739 | 2768 | lhs: MCValue, |
| 2740 | 2769 | rhs: MCValue, |
| 2741 | 2770 | lhs_ty: Type, |
| 2742 | 2771 | rhs_ty: Type, |
| 2772 | metadata: ?BinOpMetadata, | |
| 2743 | 2773 | ) InnerError!MCValue { |
| 2744 | 2774 | switch (tag) { |
| 2745 | 2775 | .add, |
| ... | ... | @@ -2780,12 +2810,12 @@ fn binOp( |
| 2780 | 2810 | }; |
| 2781 | 2811 | |
| 2782 | 2812 | 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); | |
| 2784 | 2814 | } else if (lhs_immediate_ok) { |
| 2785 | 2815 | // 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); | |
| 2787 | 2817 | } 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); | |
| 2789 | 2819 | } |
| 2790 | 2820 | } else { |
| 2791 | 2821 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | ... | @@ -2806,7 +2836,7 @@ fn binOp( |
| 2806 | 2836 | // TODO add optimisations for multiplication |
| 2807 | 2837 | // with immediates, for example a * 2 can be |
| 2808 | 2838 | // 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); | |
| 2810 | 2840 | } else { |
| 2811 | 2841 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| 2812 | 2842 | } |
| ... | ... | @@ -2826,7 +2856,7 @@ fn binOp( |
| 2826 | 2856 | }; |
| 2827 | 2857 | |
| 2828 | 2858 | // 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); | |
| 2830 | 2860 | |
| 2831 | 2861 | // Truncate if necessary |
| 2832 | 2862 | switch (lhs_ty.zigTypeTag()) { |
| ... | ... | @@ -2869,12 +2899,12 @@ fn binOp( |
| 2869 | 2899 | }; |
| 2870 | 2900 | |
| 2871 | 2901 | 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); | |
| 2873 | 2903 | } else if (lhs_immediate_ok) { |
| 2874 | 2904 | // 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); | |
| 2876 | 2906 | } 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); | |
| 2878 | 2908 | } |
| 2879 | 2909 | } else { |
| 2880 | 2910 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | ... | @@ -2903,9 +2933,9 @@ fn binOp( |
| 2903 | 2933 | }; |
| 2904 | 2934 | |
| 2905 | 2935 | 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); | |
| 2907 | 2937 | } 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); | |
| 2909 | 2939 | } |
| 2910 | 2940 | } else { |
| 2911 | 2941 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| ... | ... | @@ -2924,7 +2954,7 @@ fn binOp( |
| 2924 | 2954 | }; |
| 2925 | 2955 | |
| 2926 | 2956 | // 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); | |
| 2928 | 2958 | |
| 2929 | 2959 | // Truncate if necessary |
| 2930 | 2960 | switch (tag) { |
| ... | ... | @@ -2964,12 +2994,12 @@ fn binOp( |
| 2964 | 2994 | }; |
| 2965 | 2995 | |
| 2966 | 2996 | 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); | |
| 2968 | 2998 | } else if (lhs_immediate_ok) { |
| 2969 | 2999 | // 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); | |
| 2971 | 3001 | } 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); | |
| 2973 | 3003 | } |
| 2974 | 3004 | }, |
| 2975 | 3005 | else => unreachable, |
| ... | ... | @@ -2994,12 +3024,12 @@ fn binOp( |
| 2994 | 3024 | else => unreachable, |
| 2995 | 3025 | }; |
| 2996 | 3026 | |
| 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); | |
| 2998 | 3028 | } else { |
| 2999 | 3029 | // convert the offset into a byte offset by |
| 3000 | 3030 | // 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); | |
| 3003 | 3033 | return addr; |
| 3004 | 3034 | } |
| 3005 | 3035 | }, |
| ... | ... | @@ -3575,7 +3605,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 3575 | 3605 | try self.spillCompareFlagsIfOccupied(); |
| 3576 | 3606 | self.compare_flags_inst = inst; |
| 3577 | 3607 | |
| 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 | }); | |
| 3579 | 3613 | |
| 3580 | 3614 | break :result switch (int_info.signedness) { |
| 3581 | 3615 | .signed => MCValue{ .compare_flags_signed = op }, |
| ... | ... | @@ -3865,7 +3899,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3865 | 3899 | } |
| 3866 | 3900 | |
| 3867 | 3901 | 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); | |
| 3869 | 3903 | return MCValue{ .compare_flags_unsigned = .gt }; |
| 3870 | 3904 | } |
| 3871 | 3905 |
src/arch/riscv64/CodeGen.zig+19-4| ... | ... | @@ -481,10 +481,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 481 | 481 | |
| 482 | 482 | switch (air_tags[inst]) { |
| 483 | 483 | // 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 | ||
| 485 | 490 | .addwrap => try self.airAddWrap(inst), |
| 486 | 491 | .add_sat => try self.airAddSat(inst), |
| 487 | .sub, .ptr_sub => try self.airBinOp(inst), | |
| 488 | 492 | .subwrap => try self.airSubWrap(inst), |
| 489 | 493 | .sub_sat => try self.airSubSat(inst), |
| 490 | 494 | .mul => try self.airMul(inst), |
| ... | ... | @@ -1091,8 +1095,7 @@ fn binOp( |
| 1091 | 1095 | } |
| 1092 | 1096 | } |
| 1093 | 1097 | |
| 1094 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { | |
| 1095 | const tag = self.air.instructions.items(.tag)[inst]; | |
| 1098 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | |
| 1096 | 1099 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1097 | 1100 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1098 | 1101 | const rhs = try self.resolveInst(bin_op.rhs); |
| ... | ... | @@ -1103,6 +1106,18 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1103 | 1106 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1104 | 1107 | } |
| 1105 | 1108 | |
| 1109 | fn 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 | ||
| 1106 | 1121 | fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void { |
| 1107 | 1122 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1108 | 1123 | 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 { |
| 483 | 483 | |
| 484 | 484 | switch (air_tags[inst]) { |
| 485 | 485 | // 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), | |
| 487 | 490 | .addwrap => @panic("TODO try self.airAddWrap(inst)"), |
| 488 | 491 | .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)"), | |
| 490 | 493 | .subwrap => @panic("TODO try self.airSubWrap(inst)"), |
| 491 | 494 | .sub_sat => @panic("TODO try self.airSubSat(inst)"), |
| 492 | 495 | .mul => @panic("TODO try self.airMul(inst)"), |
| ... | ... | @@ -827,18 +830,38 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 827 | 830 | return self.finishAir(inst, mcv, .{ .none, .none, .none }); |
| 828 | 831 | } |
| 829 | 832 | |
| 830 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { | |
| 831 | const tag = self.air.instructions.items(.tag)[inst]; | |
| 833 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | |
| 832 | 834 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 833 | 835 | const lhs = try self.resolveInst(bin_op.lhs); |
| 834 | 836 | const rhs = try self.resolveInst(bin_op.rhs); |
| 835 | 837 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 836 | 838 | 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 | } | |
| 837 | 849 | |
| 850 | fn 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); | |
| 838 | 857 | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 839 | 858 | .dead |
| 840 | 859 | 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 | }); | |
| 842 | 865 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 843 | 866 | } |
| 844 | 867 | |
| ... | ... | @@ -1030,7 +1053,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1030 | 1053 | |
| 1031 | 1054 | var int_buffer: Type.Payload.Bits = undefined; |
| 1032 | 1055 | const int_ty = switch (lhs_ty.zigTypeTag()) { |
| 1033 | .Vector => unreachable, // Should be handled by cmp_vector? | |
| 1056 | .Vector => unreachable, // Handled by cmp_vector. | |
| 1034 | 1057 | .Enum => lhs_ty.intTagType(&int_buffer), |
| 1035 | 1058 | .Int => lhs_ty, |
| 1036 | 1059 | .Bool => Type.initTag(.u1), |
| ... | ... | @@ -1053,7 +1076,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1053 | 1076 | |
| 1054 | 1077 | const int_info = int_ty.intInfo(self.target.*); |
| 1055 | 1078 | 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 | }); | |
| 1057 | 1084 | |
| 1058 | 1085 | try self.spillCompareFlagsIfOccupied(); |
| 1059 | 1086 | self.compare_flags_inst = inst; |
| ... | ... | @@ -1426,7 +1453,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1426 | 1453 | // TODO skip the ptr_add emission entirely and use native addressing modes |
| 1427 | 1454 | // i.e sllx/mulx then R+R or scale immediate then R+I |
| 1428 | 1455 | 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); | |
| 1430 | 1457 | try self.load(dest, addr, slice_ptr_field_type); |
| 1431 | 1458 | |
| 1432 | 1459 | break :result dest; |
| ... | ... | @@ -1595,6 +1622,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1595 | 1622 | return MCValue{ .stack_offset = stack_offset }; |
| 1596 | 1623 | } |
| 1597 | 1624 | |
| 1625 | const BinOpMetadata = struct { | |
| 1626 | inst: Air.Inst.Index, | |
| 1627 | lhs: Air.Inst.Ref, | |
| 1628 | rhs: Air.Inst.Ref, | |
| 1629 | }; | |
| 1630 | ||
| 1598 | 1631 | /// For all your binary operation needs, this function will generate |
| 1599 | 1632 | /// the corresponding Mir instruction(s). Returns the location of the |
| 1600 | 1633 | /// result. |
| ... | ... | @@ -1610,11 +1643,11 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1610 | 1643 | fn binOp( |
| 1611 | 1644 | self: *Self, |
| 1612 | 1645 | tag: Air.Inst.Tag, |
| 1613 | maybe_inst: ?Air.Inst.Index, | |
| 1614 | 1646 | lhs: MCValue, |
| 1615 | 1647 | rhs: MCValue, |
| 1616 | 1648 | lhs_ty: Type, |
| 1617 | 1649 | rhs_ty: Type, |
| 1650 | metadata: ?BinOpMetadata, | |
| 1618 | 1651 | ) InnerError!MCValue { |
| 1619 | 1652 | const mod = self.bin_file.options.module.?; |
| 1620 | 1653 | switch (tag) { |
| ... | ... | @@ -1649,13 +1682,13 @@ fn binOp( |
| 1649 | 1682 | }; |
| 1650 | 1683 | |
| 1651 | 1684 | 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); | |
| 1653 | 1686 | } else if (lhs_immediate_ok) { |
| 1654 | 1687 | // 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); | |
| 1656 | 1689 | } else { |
| 1657 | 1690 | // 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); | |
| 1659 | 1692 | } |
| 1660 | 1693 | } else { |
| 1661 | 1694 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| ... | ... | @@ -1683,10 +1716,10 @@ fn binOp( |
| 1683 | 1716 | // If it's a power of two immediate then we emit an shl instead |
| 1684 | 1717 | // TODO add similar checks for LHS |
| 1685 | 1718 | 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); | |
| 1687 | 1720 | } |
| 1688 | 1721 | |
| 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); | |
| 1690 | 1723 | } else { |
| 1691 | 1724 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| 1692 | 1725 | } |
| ... | ... | @@ -1711,13 +1744,13 @@ fn binOp( |
| 1711 | 1744 | else => unreachable, |
| 1712 | 1745 | }; |
| 1713 | 1746 | |
| 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); | |
| 1715 | 1748 | } else { |
| 1716 | 1749 | // convert the offset into a byte offset by |
| 1717 | 1750 | // multiplying it with elem_size |
| 1718 | 1751 | |
| 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); | |
| 1721 | 1754 | return addr; |
| 1722 | 1755 | } |
| 1723 | 1756 | }, |
| ... | ... | @@ -1732,7 +1765,7 @@ fn binOp( |
| 1732 | 1765 | }; |
| 1733 | 1766 | |
| 1734 | 1767 | // 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); | |
| 1736 | 1769 | |
| 1737 | 1770 | // Truncate if necessary |
| 1738 | 1771 | switch (tag) { |
| ... | ... | @@ -1768,9 +1801,9 @@ fn binOp( |
| 1768 | 1801 | }; |
| 1769 | 1802 | |
| 1770 | 1803 | 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); | |
| 1772 | 1805 | } 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); | |
| 1774 | 1807 | } |
| 1775 | 1808 | } else { |
| 1776 | 1809 | return self.fail("TODO binary operations on int with bits > 64", .{}); |
| ... | ... | @@ -1792,18 +1825,17 @@ fn binOp( |
| 1792 | 1825 | /// op dest, lhs, #rhs_imm |
| 1793 | 1826 | /// |
| 1794 | 1827 | /// 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. | |
| 1797 | 1829 | /// |
| 1798 | 1830 | /// Asserts that generating an instruction of that form is possible. |
| 1799 | 1831 | fn binOpImmediate( |
| 1800 | 1832 | self: *Self, |
| 1801 | 1833 | mir_tag: Mir.Inst.Tag, |
| 1802 | maybe_inst: ?Air.Inst.Index, | |
| 1803 | 1834 | lhs: MCValue, |
| 1804 | 1835 | rhs: MCValue, |
| 1805 | 1836 | lhs_ty: Type, |
| 1806 | 1837 | lhs_and_rhs_swapped: bool, |
| 1838 | metadata: ?BinOpMetadata, | |
| 1807 | 1839 | ) !MCValue { |
| 1808 | 1840 | const lhs_is_register = lhs == .register; |
| 1809 | 1841 | |
| ... | ... | @@ -1816,10 +1848,9 @@ fn binOpImmediate( |
| 1816 | 1848 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1817 | 1849 | |
| 1818 | 1850 | 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: { | |
| 1821 | 1852 | 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, | |
| 1823 | 1854 | ).?; |
| 1824 | 1855 | } else null; |
| 1825 | 1856 | |
| ... | ... | @@ -1833,18 +1864,16 @@ fn binOpImmediate( |
| 1833 | 1864 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1834 | 1865 | |
| 1835 | 1866 | 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: { | |
| 1839 | 1868 | 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, | |
| 1842 | 1871 | if (lhs_and_rhs_swapped) 1 else 0, |
| 1843 | 1872 | lhs, |
| 1844 | 1873 | )) { |
| 1845 | 1874 | break :blk lhs_reg; |
| 1846 | 1875 | } else { |
| 1847 | break :blk try self.register_manager.allocReg(inst); | |
| 1876 | break :blk try self.register_manager.allocReg(md.inst); | |
| 1848 | 1877 | } |
| 1849 | 1878 | } else blk: { |
| 1850 | 1879 | break :blk try self.register_manager.allocReg(null); |
| ... | ... | @@ -1896,11 +1925,11 @@ fn binOpImmediate( |
| 1896 | 1925 | fn binOpRegister( |
| 1897 | 1926 | self: *Self, |
| 1898 | 1927 | mir_tag: Mir.Inst.Tag, |
| 1899 | maybe_inst: ?Air.Inst.Index, | |
| 1900 | 1928 | lhs: MCValue, |
| 1901 | 1929 | rhs: MCValue, |
| 1902 | 1930 | lhs_ty: Type, |
| 1903 | 1931 | rhs_ty: Type, |
| 1932 | metadata: ?BinOpMetadata, | |
| 1904 | 1933 | ) !MCValue { |
| 1905 | 1934 | const lhs_is_register = lhs == .register; |
| 1906 | 1935 | const rhs_is_register = rhs == .register; |
| ... | ... | @@ -1920,9 +1949,8 @@ fn binOpRegister( |
| 1920 | 1949 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 1921 | 1950 | |
| 1922 | 1951 | 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).?; | |
| 1926 | 1954 | } else null; |
| 1927 | 1955 | |
| 1928 | 1956 | const reg = try self.register_manager.allocReg(track_inst); |
| ... | ... | @@ -1934,9 +1962,8 @@ fn binOpRegister( |
| 1934 | 1962 | defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1935 | 1963 | |
| 1936 | 1964 | 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).?; | |
| 1940 | 1967 | } else null; |
| 1941 | 1968 | |
| 1942 | 1969 | const reg = try self.register_manager.allocReg(track_inst); |
| ... | ... | @@ -1948,15 +1975,13 @@ fn binOpRegister( |
| 1948 | 1975 | defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); |
| 1949 | 1976 | |
| 1950 | 1977 | 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)) { | |
| 1955 | 1980 | 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)) { | |
| 1957 | 1982 | break :blk rhs_reg; |
| 1958 | 1983 | } else { |
| 1959 | break :blk try self.register_manager.allocReg(inst); | |
| 1984 | break :blk try self.register_manager.allocReg(md.inst); | |
| 1960 | 1985 | } |
| 1961 | 1986 | } else blk: { |
| 1962 | 1987 | 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 |
| 3069 | 3094 | |
| 3070 | 3095 | const dest = try self.binOp( |
| 3071 | 3096 | .add, |
| 3072 | null, | |
| 3073 | 3097 | .{ .register = addr_reg }, |
| 3074 | 3098 | .{ .register = offset_reg }, |
| 3075 | 3099 | Type.usize, |
| 3076 | 3100 | Type.usize, |
| 3101 | null, | |
| 3077 | 3102 | ); |
| 3078 | 3103 | |
| 3079 | 3104 | break :result dest; |
src/arch/wasm/CodeGen.zig+2-1| ... | ... | @@ -3397,7 +3397,8 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3397 | 3397 | |
| 3398 | 3398 | fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 3399 | 3399 | 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; | |
| 3401 | 3402 | const ptr = try self.resolveInst(bin_op.lhs); |
| 3402 | 3403 | const offset = try self.resolveInst(bin_op.rhs); |
| 3403 | 3404 | 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 { |
| 574 | 574 | |
| 575 | 575 | switch (air_tags[inst]) { |
| 576 | 576 | // 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 | ||
| 583 | 593 | .mul => try self.airMulDivBinOp(inst), |
| 584 | 594 | .mulwrap => try self.airMulDivBinOp(inst), |
| 585 | .mul_sat => try self.airMulSat(inst), | |
| 586 | 595 | .rem => try self.airMulDivBinOp(inst), |
| 587 | 596 | .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), | |
| 589 | 601 | .shl_sat => try self.airShlSat(inst), |
| 590 | 602 | .min => try self.airMin(inst), |
| 591 | 603 | .max => try self.airMax(inst), |
| 592 | .ptr_add => try self.airBinOp(inst), | |
| 593 | .ptr_sub => try self.airBinOp(inst), | |
| 594 | 604 | .slice => try self.airSlice(inst), |
| 595 | 605 | |
| 596 | 606 | .sqrt, |
| ... | ... | @@ -626,13 +636,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 626 | 636 | .cmp_vector => try self.airCmpVector(inst), |
| 627 | 637 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| 628 | 638 | |
| 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 | ||
| 636 | 639 | .alloc => try self.airAlloc(inst), |
| 637 | 640 | .ret_ptr => try self.airRetPtr(inst), |
| 638 | 641 | .arg => try self.airArg(inst), |
| ... | ... | @@ -1231,21 +1234,26 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 1231 | 1234 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1232 | 1235 | } |
| 1233 | 1236 | |
| 1234 | fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { | |
| 1237 | fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { | |
| 1235 | 1238 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1236 | 1239 | |
| 1237 | 1240 | if (self.liveness.isUnused(inst)) { |
| 1238 | 1241 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1239 | 1242 | } |
| 1240 | 1243 | |
| 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 | } | |
| 1246 | 1247 | |
| 1247 | const result = try self.genBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); | |
| 1248 | fn 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 | } | |
| 1248 | 1255 | |
| 1256 | const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); | |
| 1249 | 1257 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1250 | 1258 | } |
| 1251 | 1259 | |
| ... | ... | @@ -1316,13 +1324,12 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1316 | 1324 | try self.spillRegisters(1, .{.rcx}); |
| 1317 | 1325 | } |
| 1318 | 1326 | |
| 1319 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1320 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 1321 | ||
| 1322 | 1327 | 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), | |
| 1325 | 1330 | .shl_with_overflow => blk: { |
| 1331 | const lhs = try self.resolveInst(bin_op.lhs); | |
| 1332 | const rhs = try self.resolveInst(bin_op.rhs); | |
| 1326 | 1333 | const shift_ty = self.air.typeOf(bin_op.rhs); |
| 1327 | 1334 | break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty); |
| 1328 | 1335 | }, |
| ... | ... | @@ -3310,13 +3317,15 @@ fn genMulDivBinOp( |
| 3310 | 3317 | /// Result is always a register. |
| 3311 | 3318 | fn genBinOp( |
| 3312 | 3319 | self: *Self, |
| 3313 | tag: Air.Inst.Tag, | |
| 3314 | 3320 | 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, | |
| 3319 | 3324 | ) !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); | |
| 3320 | 3329 | if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { |
| 3321 | 3330 | return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); |
| 3322 | 3331 | } |
| ... | ... | @@ -3352,11 +3361,10 @@ fn genBinOp( |
| 3352 | 3361 | var flipped: bool = false; |
| 3353 | 3362 | const dst_mcv: MCValue = blk: { |
| 3354 | 3363 | 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()) { | |
| 3357 | 3365 | break :blk lhs; |
| 3358 | 3366 | } |
| 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()) { | |
| 3360 | 3368 | flipped = true; |
| 3361 | 3369 | break :blk rhs; |
| 3362 | 3370 | } |
src/codegen/c.zig+16-20| ... | ... | @@ -1711,21 +1711,18 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1711 | 1711 | .unreach => try airUnreach(f), |
| 1712 | 1712 | .fence => try airFence(f, inst), |
| 1713 | 1713 | |
| 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, " * "), | |
| 1727 | 1722 | .div_float, .div_exact => try airBinOp( f, inst, " / "), |
| 1728 | .div_trunc => blk: { | |
| 1723 | .rem => try airBinOp( f, inst, " % "), | |
| 1724 | ||
| 1725 | .div_trunc => blk: { | |
| 1729 | 1726 | const bin_op = f.air.instructions.items(.data)[inst].bin_op; |
| 1730 | 1727 | const lhs_ty = f.air.typeOf(bin_op.lhs); |
| 1731 | 1728 | // For binary operations @TypeOf(lhs)==@TypeOf(rhs), |
| ... | ... | @@ -1735,9 +1732,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1735 | 1732 | else |
| 1736 | 1733 | try airBinOpBuiltinCall(f, inst, "div_trunc"); |
| 1737 | 1734 | }, |
| 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"), | |
| 1741 | 1737 | |
| 1742 | 1738 | .addwrap => try airWrapOp(f, inst, " + ", "addw_"), |
| 1743 | 1739 | .subwrap => try airWrapOp(f, inst, " - ", "subw_"), |
| ... | ... | @@ -2617,10 +2613,10 @@ fn airEquality( |
| 2617 | 2613 | } |
| 2618 | 2614 | |
| 2619 | 2615 | fn 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; | |
| 2622 | 2617 | |
| 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; | |
| 2624 | 2620 | const lhs = try f.resolveInst(bin_op.lhs); |
| 2625 | 2621 | const rhs = try f.resolveInst(bin_op.rhs); |
| 2626 | 2622 |
src/codegen/llvm.zig+63-14| ... | ... | @@ -395,11 +395,11 @@ pub const Object = struct { |
| 395 | 395 | return slice.ptr; |
| 396 | 396 | } |
| 397 | 397 | |
| 398 | fn genErrorNameTable(self: *Object, comp: *Compilation) !void { | |
| 398 | fn genErrorNameTable(self: *Object) !void { | |
| 399 | 399 | // If self.error_name_table is null, there was no instruction that actually referenced the error table. |
| 400 | 400 | const error_name_table_ptr_global = self.error_name_table orelse return; |
| 401 | 401 | |
| 402 | const mod = comp.bin_file.options.module.?; | |
| 402 | const mod = self.module; | |
| 403 | 403 | const target = mod.getTarget(); |
| 404 | 404 | |
| 405 | 405 | const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space |
| ... | ... | @@ -413,8 +413,8 @@ pub const Object = struct { |
| 413 | 413 | const slice_alignment = slice_ty.abiAlignment(target); |
| 414 | 414 | |
| 415 | 415 | 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); | |
| 418 | 418 | |
| 419 | 419 | llvm_errors[0] = llvm_slice_ty.getUndef(); |
| 420 | 420 | for (llvm_errors[1..]) |*llvm_error, i| { |
| ... | ... | @@ -447,10 +447,10 @@ pub const Object = struct { |
| 447 | 447 | error_name_table_ptr_global.setInitializer(error_name_table_ptr); |
| 448 | 448 | } |
| 449 | 449 | |
| 450 | fn genCmpLtErrorsLenFunction(object: *Object, comp: *Compilation) !void { | |
| 450 | fn genCmpLtErrorsLenFunction(object: *Object) !void { | |
| 451 | 451 | // If there is no such function in the module, it means the source code does not need it. |
| 452 | 452 | 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; | |
| 454 | 454 | const errors_len = mod.global_error_set.count(); |
| 455 | 455 | |
| 456 | 456 | // Delete previous implementation. We replace it with every flush() because the |
| ... | ... | @@ -476,10 +476,10 @@ pub const Object = struct { |
| 476 | 476 | _ = builder.buildRet(is_lt); |
| 477 | 477 | } |
| 478 | 478 | |
| 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; | |
| 481 | 481 | 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); | |
| 483 | 483 | defer buffer.deinit(); |
| 484 | 484 | var it = mod.global_assembly.iterator(); |
| 485 | 485 | while (it.next()) |kv| { |
| ... | ... | @@ -489,15 +489,53 @@ pub const Object = struct { |
| 489 | 489 | object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1); |
| 490 | 490 | } |
| 491 | 491 | |
| 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 | ||
| 492 | 529 | pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| 493 | 530 | var sub_prog_node = prog_node.start("LLVM Emit Object", 0); |
| 494 | 531 | sub_prog_node.activate(); |
| 495 | 532 | sub_prog_node.context.refresh(); |
| 496 | 533 | defer sub_prog_node.end(); |
| 497 | 534 | |
| 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(); | |
| 501 | 539 | |
| 502 | 540 | if (self.di_builder) |dib| { |
| 503 | 541 | // When lowering debug info for pointers, we emitted the element types as |
| ... | ... | @@ -761,6 +799,14 @@ pub const Object = struct { |
| 761 | 799 | try self.updateDeclExports(module, decl_index, decl_exports); |
| 762 | 800 | } |
| 763 | 801 | |
| 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 | ||
| 764 | 810 | pub fn updateDeclExports( |
| 765 | 811 | self: *Object, |
| 766 | 812 | module: *Module, |
| ... | ... | @@ -827,6 +873,7 @@ pub const Object = struct { |
| 827 | 873 | llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); |
| 828 | 874 | } |
| 829 | 875 | } |
| 876 | ||
| 830 | 877 | // If a Decl is exported more than one time (which is rare), |
| 831 | 878 | // we add aliases for all but the first export. |
| 832 | 879 | // TODO LLVM C API does not support deleting aliases. We need to |
| ... | ... | @@ -5632,7 +5679,8 @@ pub const FuncGen = struct { |
| 5632 | 5679 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 5633 | 5680 | if (self.liveness.isUnused(inst)) return null; |
| 5634 | 5681 | |
| 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; | |
| 5636 | 5684 | const base_ptr = try self.resolveInst(bin_op.lhs); |
| 5637 | 5685 | const offset = try self.resolveInst(bin_op.rhs); |
| 5638 | 5686 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| ... | ... | @@ -5651,7 +5699,8 @@ pub const FuncGen = struct { |
| 5651 | 5699 | fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 5652 | 5700 | if (self.liveness.isUnused(inst)) return null; |
| 5653 | 5701 | |
| 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; | |
| 5655 | 5704 | const base_ptr = try self.resolveInst(bin_op.lhs); |
| 5656 | 5705 | const offset = try self.resolveInst(bin_op.rhs); |
| 5657 | 5706 | const negative_offset = self.builder.buildNeg(offset, ""); |
src/print_air.zig+6-17| ... | ... | @@ -114,8 +114,6 @@ const Writer = struct { |
| 114 | 114 | .div_exact, |
| 115 | 115 | .rem, |
| 116 | 116 | .mod, |
| 117 | .ptr_add, | |
| 118 | .ptr_sub, | |
| 119 | 117 | .bit_and, |
| 120 | 118 | .bit_or, |
| 121 | 119 | .xor, |
| ... | ... | @@ -231,6 +229,12 @@ const Writer = struct { |
| 231 | 229 | .slice, |
| 232 | 230 | .slice_elem_ptr, |
| 233 | 231 | .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, | |
| 234 | 238 | => try w.writeTyPlBin(s, inst), |
| 235 | 239 | |
| 236 | 240 | .call, |
| ... | ... | @@ -275,12 +279,6 @@ const Writer = struct { |
| 275 | 279 | .reduce => try w.writeReduce(s, inst), |
| 276 | 280 | .cmp_vector => try w.writeCmpVector(s, inst), |
| 277 | 281 | |
| 278 | .add_with_overflow, | |
| 279 | .sub_with_overflow, | |
| 280 | .mul_with_overflow, | |
| 281 | .shl_with_overflow, | |
| 282 | => try w.writeOverflow(s, inst), | |
| 283 | ||
| 284 | 282 | .dbg_block_begin, .dbg_block_end => {}, |
| 285 | 283 | } |
| 286 | 284 | } |
| ... | ... | @@ -478,15 +476,6 @@ const Writer = struct { |
| 478 | 476 | try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); |
| 479 | 477 | } |
| 480 | 478 | |
| 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 | ||
| 490 | 479 | fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 491 | 480 | const pl_op = w.air.instructions.items(.data)[inst].pl_op; |
| 492 | 481 | const extra = w.air.extraData(Air.Bin, pl_op.payload).data; |
src/value.zig-22| ... | ... | @@ -1813,27 +1813,6 @@ pub const Value = extern union { |
| 1813 | 1813 | }; |
| 1814 | 1814 | } |
| 1815 | 1815 | |
| 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 | ||
| 1837 | 1816 | pub fn orderAgainstZero(lhs: Value) std.math.Order { |
| 1838 | 1817 | return orderAgainstZeroAdvanced(lhs, null) catch unreachable; |
| 1839 | 1818 | } |
| ... | ... | @@ -3442,7 +3421,6 @@ pub const Value = extern union { |
| 3442 | 3421 | const info = ty.intInfo(target); |
| 3443 | 3422 | |
| 3444 | 3423 | if (info.bits == 0) { |
| 3445 | assert(val.isZero()); // Sema should guarantee | |
| 3446 | 3424 | return val; |
| 3447 | 3425 | } |
| 3448 | 3426 |
test/behavior/align.zig+16-5| ... | ... | @@ -16,11 +16,22 @@ test "global variable alignment" { |
| 16 | 16 | const slice = @as(*align(4) [1]u8, &foo)[0..]; |
| 17 | 17 | comptime try expect(@TypeOf(slice) == *align(4) [1]u8); |
| 18 | 18 | } |
| 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 | ||
| 21 | test "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); | |
| 24 | 35 | } |
| 25 | 36 | |
| 26 | 37 | test "default alignment allows unspecified in type syntax" { |
test/behavior/basic.zig+5-1| ... | ... | @@ -797,7 +797,11 @@ test "auto created variables have correct alignment" { |
| 797 | 797 | } |
| 798 | 798 | |
| 799 | 799 | test "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 | |
| 801 | 805 | |
| 802 | 806 | @export(var_to_export, .{ .name = "opaque_extern_var" }); |
| 803 | 807 | 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" { |
| 326 | 326 | |
| 327 | 327 | @setRuntimeSafety(true); |
| 328 | 328 | |
| 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) { | |
| 331 | 330 | return error.SkipZigTest; |
| 332 | 331 | } |
| 333 | 332 | |
| ... | ... | @@ -588,7 +587,11 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { |
| 588 | 587 | } |
| 589 | 588 | |
| 590 | 589 | test "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 | |
| 592 | 595 | |
| 593 | 596 | const S = struct { |
| 594 | 597 | fn doTheTest() !void { |
test/behavior/eval.zig+7| ... | ... | @@ -1110,3 +1110,10 @@ test "no dependency loop for alignment of self tagged union" { |
| 1110 | 1110 | }; |
| 1111 | 1111 | try S.doTheTest(); |
| 1112 | 1112 | } |
| 1113 | ||
| 1114 | test "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" { |
| 214 | 214 | } |
| 215 | 215 | |
| 216 | 216 | test "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 | |
| 218 | 221 | |
| 219 | 222 | var x: [*c]i32 = null; |
| 220 | 223 | try expect(x == null); |
| ... | ... | @@ -238,7 +241,8 @@ test "assign null directly to C pointer and test null equality" { |
| 238 | 241 | @panic("fail"); |
| 239 | 242 | } |
| 240 | 243 | 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); | |
| 242 | 246 | |
| 243 | 247 | var n: i32 = 1234; |
| 244 | 248 | var x1: [*c]i32 = &n; |
| ... | ... | @@ -373,8 +377,6 @@ test "pointer to array at fixed address" { |
| 373 | 377 | } |
| 374 | 378 | |
| 375 | 379 | test "pointer arithmetic affects the alignment" { |
| 376 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 377 | ||
| 378 | 380 | { |
| 379 | 381 | var ptr: [*]align(8) u32 = undefined; |
| 380 | 382 | var x: usize = 1; |