From 00f3d84f38c54e70716cf8e2908c899b49de1d88 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 May 2022 20:26:20 -0700 Subject: [PATCH 1/6] LLVM: support mixing extern and export with the same symbol name --- src/codegen/llvm.zig | 71 ++++++++++++++++++++++++++++++++++------- test/behavior/basic.zig | 6 +++- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index dfb0f8f03b..fb7a2c39bc 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -395,11 +395,11 @@ pub const Object = struct { return slice.ptr; } - fn genErrorNameTable(self: *Object, comp: *Compilation) !void { + fn genErrorNameTable(self: *Object) !void { // If self.error_name_table is null, there was no instruction that actually referenced the error table. const error_name_table_ptr_global = self.error_name_table orelse return; - const mod = comp.bin_file.options.module.?; + const mod = self.module; const target = mod.getTarget(); const llvm_ptr_ty = self.context.intType(8).pointerType(0); // TODO: Address space @@ -413,8 +413,8 @@ pub const Object = struct { const slice_alignment = slice_ty.abiAlignment(target); const error_name_list = mod.error_name_list.items; - const llvm_errors = try comp.gpa.alloc(*const llvm.Value, error_name_list.len); - defer comp.gpa.free(llvm_errors); + const llvm_errors = try mod.gpa.alloc(*const llvm.Value, error_name_list.len); + defer mod.gpa.free(llvm_errors); llvm_errors[0] = llvm_slice_ty.getUndef(); for (llvm_errors[1..]) |*llvm_error, i| { @@ -447,10 +447,10 @@ pub const Object = struct { error_name_table_ptr_global.setInitializer(error_name_table_ptr); } - fn genCmpLtErrorsLenFunction(object: *Object, comp: *Compilation) !void { + fn genCmpLtErrorsLenFunction(object: *Object) !void { // If there is no such function in the module, it means the source code does not need it. const llvm_fn = object.llvm_module.getNamedFunction(lt_errors_fn_name) orelse return; - const mod = comp.bin_file.options.module.?; + const mod = object.module; const errors_len = mod.global_error_set.count(); // Delete previous implementation. We replace it with every flush() because the @@ -476,10 +476,10 @@ pub const Object = struct { _ = builder.buildRet(is_lt); } - fn genModuleLevelAssembly(object: *Object, comp: *Compilation) !void { - const mod = comp.bin_file.options.module.?; + fn genModuleLevelAssembly(object: *Object) !void { + const mod = object.module; if (mod.global_assembly.count() == 0) return; - var buffer = std.ArrayList(u8).init(comp.gpa); + var buffer = std.ArrayList(u8).init(mod.gpa); defer buffer.deinit(); var it = mod.global_assembly.iterator(); while (it.next()) |kv| { @@ -489,15 +489,53 @@ pub const Object = struct { object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1); } + fn resolveExportExternCollisions(object: *Object) !void { + const mod = object.module; + + const export_keys = mod.decl_exports.keys(); + for (mod.decl_exports.values()) |export_list, i| { + const decl_index = export_keys[i]; + const llvm_global = object.decl_map.get(decl_index) orelse continue; + for (export_list) |exp| { + // Detect if the LLVM global has already been created as an extern. In such + // case, we need to replace all uses of it with this exported global. + // TODO update std.builtin.ExportOptions to have the name be a + // null-terminated slice. + const exp_name_z = try mod.gpa.dupeZ(u8, exp.options.name); + defer mod.gpa.free(exp_name_z); + + const other_global = object.getLlvmGlobal(exp_name_z.ptr) orelse continue; + if (other_global == llvm_global) continue; + + // replaceAllUsesWith requires the type to be unchanged. So we bitcast + // the new global to the old type and use that as the thing to replace + // old uses. + const new_global_ptr = llvm_global.constBitCast(other_global.typeOf()); + other_global.replaceAllUsesWith(new_global_ptr); + llvm_global.takeName(other_global); + other_global.deleteGlobal(); + // Problem: now we need to replace in the decl_map that + // the extern decl index points to this new global. However we don't + // know the decl index. + // Even if we did, a future incremental update to the extern would then + // treat the LLVM global as an extern rather than an export, so it would + // need a way to check that. + // This is a TODO that needs to be solved when making + // the LLVM backend support incremental compilation. + } + } + } + pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { var sub_prog_node = prog_node.start("LLVM Emit Object", 0); sub_prog_node.activate(); sub_prog_node.context.refresh(); defer sub_prog_node.end(); - try self.genErrorNameTable(comp); - try self.genCmpLtErrorsLenFunction(comp); - try self.genModuleLevelAssembly(comp); + try self.resolveExportExternCollisions(); + try self.genErrorNameTable(); + try self.genCmpLtErrorsLenFunction(); + try self.genModuleLevelAssembly(); if (self.di_builder) |dib| { // When lowering debug info for pointers, we emitted the element types as @@ -761,6 +799,14 @@ pub const Object = struct { try self.updateDeclExports(module, decl_index, decl_exports); } + /// TODO replace this with a call to `Module::getNamedValue`. This will require adding + /// a new wrapper in zig_llvm.h/zig_llvm.cpp. + fn getLlvmGlobal(o: Object, name: [*:0]const u8) ?*const llvm.Value { + if (o.llvm_module.getNamedFunction(name)) |x| return x; + if (o.llvm_module.getNamedGlobal(name)) |x| return x; + return null; + } + pub fn updateDeclExports( self: *Object, module: *Module, @@ -827,6 +873,7 @@ pub const Object = struct { llvm_global.setThreadLocalMode(.GeneralDynamicTLSModel); } } + // If a Decl is exported more than one time (which is rare), // we add aliases for all but the first export. // TODO LLVM C API does not support deleting aliases. We need to diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 12ead179f4..10e48c6c7b 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -797,7 +797,11 @@ test "auto created variables have correct alignment" { } test "extern variable with non-pointer opaque type" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @export(var_to_export, .{ .name = "opaque_extern_var" }); try expect(@ptrCast(*align(1) u32, &opaque_extern_var).* == 42); From 95f5e17e49d32a301d6a9d6f9948719d65469b09 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 May 2022 20:44:40 -0700 Subject: [PATCH 2/6] behavior tests: correction of C pointer test This test was also covering this behavior: ```zig test "equality of pointers to comptime const" { const a: i32 = undefined; comptime assert(&a == &a); } ``` This check belongs in its own behavior test which isolates this behavior; not bundled along with a C pointer test. --- test/behavior/eval.zig | 7 +++++++ test/behavior/pointers.zig | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 007ea48d87..88be755b61 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1110,3 +1110,10 @@ test "no dependency loop for alignment of self tagged union" { }; try S.doTheTest(); } + +test "equality of pointers to comptime const" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + const a: i32 = undefined; + comptime assert(&a == &a); +} diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 5b9c3c8cf0..42939986d4 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -214,7 +214,10 @@ test "allowzero pointer and slice" { } test "assign null directly to C pointer and test null equality" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO var x: [*c]i32 = null; try expect(x == null); @@ -238,7 +241,8 @@ test "assign null directly to C pointer and test null equality" { @panic("fail"); } const othery: i32 = undefined; - comptime try expect((y orelse &othery) == &othery); + const ptr_othery = &othery; + comptime try expect((y orelse ptr_othery) == ptr_othery); var n: i32 = 1234; var x1: [*c]i32 = &n; From b6798c26efc4689cf35c5f4ac0436b4510a1f813 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 May 2022 01:52:02 -0700 Subject: [PATCH 3/6] stage2: fix pointer arithmetic result type This makes it so the result of doing pointer arithmetic creates a new pointer type that has adjusted alignment. --- lib/std/heap.zig | 2 +- src/Air.zig | 8 +- src/Liveness.zig | 31 +++--- src/Sema.zig | 70 +++++++++---- src/arch/aarch64/CodeGen.zig | 166 +++++++++++++++++------------- src/arch/arm/CodeGen.zig | 192 +++++++++++++++++++++-------------- src/arch/riscv64/CodeGen.zig | 23 ++++- src/arch/sparc64/CodeGen.zig | 119 +++++++++++++--------- src/arch/wasm/CodeGen.zig | 3 +- src/arch/x86_64/CodeGen.zig | 82 ++++++++------- src/codegen/c.zig | 36 +++---- src/codegen/llvm.zig | 6 +- src/print_air.zig | 23 ++--- src/value.zig | 22 ---- test/behavior/align.zig | 21 +++- test/behavior/pointers.zig | 2 - 16 files changed, 465 insertions(+), 341 deletions(-) diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 65952f6c50..cbbe111a26 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -345,7 +345,7 @@ const PageAllocator = struct { // Unmap extra pages const aligned_buffer_len = alloc_len - drop_len; if (aligned_buffer_len > aligned_len) { - os.munmap(result_ptr[aligned_len..aligned_buffer_len]); + os.munmap(@alignCast(mem.page_size, result_ptr[aligned_len..aligned_buffer_len])); } const new_hint = @alignCast(mem.page_size, result_ptr + aligned_len); diff --git a/src/Air.zig b/src/Air.zig index 169449a066..8211e39454 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -113,13 +113,13 @@ pub const Inst = struct { /// The offset is in element type units, not bytes. /// Wrapping is undefined behavior. /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs. - /// Uses the `bin_op` field. + /// Uses the `ty_pl` field. Payload is `Bin`. ptr_add, /// Subtract an offset from a pointer, returning a new pointer. /// The offset is in element type units, not bytes. /// Wrapping is undefined behavior. /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs. - /// Uses the `bin_op` field. + /// Uses the `ty_pl` field. Payload is `Bin`. ptr_sub, /// Given two operands which can be floats, integers, or vectors, returns the /// greater of the operands. For vectors it operates element-wise. @@ -916,8 +916,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .bit_and, .bit_or, .xor, - .ptr_add, - .ptr_sub, .shr, .shr_exact, .shl, @@ -989,6 +987,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .sub_with_overflow, .mul_with_overflow, .shl_with_overflow, + .ptr_add, + .ptr_sub, => return air.getRefType(datas[inst].ty_pl.ty), .not, diff --git a/src/Liveness.zig b/src/Liveness.zig index c14c460d67..b37e220086 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -312,8 +312,6 @@ fn analyzeInst( .div_exact, .rem, .mod, - .ptr_add, - .ptr_sub, .bit_and, .bit_or, .xor, @@ -441,6 +439,21 @@ fn analyzeInst( return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none }); }, + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, + .ptr_add, + .ptr_sub, + .ptr_elem_ptr, + .slice_elem_ptr, + .slice, + => { + const ty_pl = inst_datas[inst].ty_pl; + const extra = a.air.extraData(Air.Bin, ty_pl.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); + }, + .dbg_var_ptr, .dbg_var_val, => { @@ -529,10 +542,6 @@ fn analyzeInst( const extra = a.air.extraData(Air.FieldParentPtr, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.field_ptr, .none, .none }); }, - .ptr_elem_ptr, .slice_elem_ptr, .slice => { - const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data; - return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); - }, .cmpxchg_strong, .cmpxchg_weak => { const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value }); @@ -558,15 +567,7 @@ fn analyzeInst( const extra = a.air.extraData(Air.Bin, pl_op.payload).data; return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs }); }, - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - => { - const ty_pl = inst_datas[inst].ty_pl; - const extra = a.air.extraData(Air.Bin, ty_pl.payload).data; - return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); - }, + .br => { const br = inst_datas[inst].br; return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index c45079eaa7..7086bb3f13 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -10610,28 +10610,55 @@ fn analyzePtrArithmetic( // TODO if the operand is comptime-known to be negative, or is a negative int, // coerce to isize instead of usize. const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src); - // TODO adjust the return type according to alignment and other factors const target = sema.mod.getTarget(); - const runtime_src = rs: { - if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { - if (try sema.resolveMaybeUndefVal(block, offset_src, offset)) |offset_val| { - const ptr_ty = sema.typeOf(ptr); - const new_ptr_ty = ptr_ty; // TODO modify alignment + const opt_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, ptr); + const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset); + const ptr_ty = sema.typeOf(ptr); + const ptr_info = ptr_ty.ptrInfo().data; + const elem_ty = if (ptr_info.size == .One and ptr_info.pointee_type.zigTypeTag() == .Array) + ptr_info.pointee_type.childType() + else + ptr_info.pointee_type; - if (ptr_val.isUndef() or offset_val.isUndef()) { - return sema.addConstUndef(new_ptr_ty); - } + const new_ptr_ty = t: { + // Calculate the new pointer alignment. + if (ptr_info.@"align" == 0) { + // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness. + break :t ptr_ty; + } + // If the addend is not a comptime-known value we can still count on + // it being a multiple of the type size. + const elem_size = elem_ty.abiSize(target); + const addend = if (opt_off_val) |off_val| a: { + const off_int = try sema.usizeCast(block, offset_src, off_val.toUnsignedInt(target)); + break :a elem_size * off_int; + } else elem_size; + + // The resulting pointer is aligned to the lcd between the offset (an + // arbitrary number) and the alignment factor (always a power of two, + // non zero). + const new_align = @as(u32, 1) << @intCast(u5, @ctz(u64, addend | ptr_info.@"align")); + + break :t try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = ptr_info.pointee_type, + .sentinel = ptr_info.sentinel, + .@"align" = new_align, + .@"addrspace" = ptr_info.@"addrspace", + .mutable = ptr_info.mutable, + .@"allowzero" = ptr_info.@"allowzero", + .@"volatile" = ptr_info.@"volatile", + .size = ptr_info.size, + }); + }; + + const runtime_src = rs: { + if (opt_ptr_val) |ptr_val| { + if (opt_off_val) |offset_val| { + if (ptr_val.isUndef()) return sema.addConstUndef(new_ptr_ty); const offset_int = try sema.usizeCast(block, offset_src, offset_val.toUnsignedInt(target)); - // TODO I tried to put this check earlier but it the LLVM backend generate invalid instructinons if (offset_int == 0) return ptr; if (try ptr_val.getUnsignedIntAdvanced(target, sema.kit(block, ptr_src))) |addr| { - const ptr_child_ty = ptr_ty.childType(); - const elem_ty = if (ptr_ty.isSinglePointer() and ptr_child_ty.zigTypeTag() == .Array) - ptr_child_ty.childType() - else - ptr_child_ty; - const elem_size = elem_ty.abiSize(target); const new_addr = switch (air_tag) { .ptr_add => addr + elem_size * offset_int, @@ -10651,7 +10678,16 @@ fn analyzePtrArithmetic( }; try sema.requireRuntimeBlock(block, runtime_src); - return block.addBinOp(air_tag, ptr, offset); + return block.addInst(.{ + .tag = air_tag, + .data = .{ .ty_pl = .{ + .ty = try sema.addType(new_ptr_ty), + .payload = try sema.addExtra(Air.Bin{ + .lhs = ptr, + .rhs = offset, + }), + } }, + }); } fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 95a29b840e..0993d352d7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -545,18 +545,30 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airBinOp(inst), - .addwrap => try self.airBinOp(inst), + .add => try self.airBinOp(inst, .add), + .addwrap => try self.airBinOp(inst, .addwrap), + .sub => try self.airBinOp(inst, .sub), + .subwrap => try self.airBinOp(inst, .subwrap), + .mul => try self.airBinOp(inst, .mul), + .mulwrap => try self.airBinOp(inst, .mulwrap), + .shl => try self.airBinOp(inst, .shl), + .shl_exact => try self.airBinOp(inst, .shl_exact), + .bool_and => try self.airBinOp(inst, .bool_and), + .bool_or => try self.airBinOp(inst, .bool_or), + .bit_and => try self.airBinOp(inst, .bit_and), + .bit_or => try self.airBinOp(inst, .bit_or), + .xor => try self.airBinOp(inst, .xor), + .shr => try self.airBinOp(inst, .shr), + .shr_exact => try self.airBinOp(inst, .shr_exact), + + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airBinOp(inst), - .subwrap => try self.airBinOp(inst), .sub_sat => try self.airSubSat(inst), - .mul => try self.airBinOp(inst), - .mulwrap => try self.airBinOp(inst), .mul_sat => try self.airMulSat(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), - .shl, .shl_exact => try self.airBinOp(inst), .shl_sat => try self.airShlSat(inst), .min => try self.airMin(inst), .max => try self.airMax(inst), @@ -595,13 +607,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_vector => try self.airCmpVector(inst), .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - .bool_and => try self.airBinOp(inst), - .bool_or => try self.airBinOp(inst), - .bit_and => try self.airBinOp(inst), - .bit_or => try self.airBinOp(inst), - .xor => try self.airBinOp(inst), - .shr, .shr_exact => try self.airBinOp(inst), - .alloc => try self.airAlloc(inst), .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), @@ -1260,11 +1265,11 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { fn binOpRegister( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; @@ -1284,9 +1289,8 @@ fn binOpRegister( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.lhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.lhs).?; } else null; const raw_reg = try self.register_manager.allocReg(track_inst); @@ -1300,9 +1304,8 @@ fn binOpRegister( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.rhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.rhs).?; } else null; const raw_reg = try self.register_manager.allocReg(track_inst); @@ -1317,15 +1320,13 @@ fn binOpRegister( const dest_reg = switch (mir_tag) { .cmp_shifted_register => undefined, // cmp has no destination register - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + else => if (metadata) |md| blk: { + if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - const raw_reg = try self.register_manager.allocReg(inst); + const raw_reg = try self.register_manager.allocReg(md.inst); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else blk: { @@ -1407,11 +1408,11 @@ fn binOpRegister( fn binOpImmediate( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, lhs_and_rhs_swapped: bool, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; @@ -1424,10 +1425,9 @@ fn binOpImmediate( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex( - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, ).?; } else null; @@ -1443,18 +1443,16 @@ fn binOpImmediate( const dest_reg = switch (mir_tag) { .cmp_immediate => undefined, // cmp has no destination register - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - + else => if (metadata) |md| blk: { if (lhs_is_register and self.reuseOperand( - inst, - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + md.inst, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, if (lhs_and_rhs_swapped) 1 else 0, lhs, )) { break :blk lhs_reg; } else { - const raw_reg = try self.register_manager.allocReg(inst); + const raw_reg = try self.register_manager.allocReg(md.inst); break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); } } else blk: { @@ -1498,6 +1496,12 @@ fn binOpImmediate( return MCValue{ .register = dest_reg }; } +const BinOpMetadata = struct { + inst: Air.Inst.Index, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, +}; + /// For all your binary operation needs, this function will generate /// the corresponding Mir instruction(s). Returns the location of the /// result. @@ -1513,11 +1517,11 @@ fn binOpImmediate( fn binOp( self: *Self, tag: Air.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) InnerError!MCValue { const mod = self.bin_file.options.module.?; switch (tag) { @@ -1562,12 +1566,12 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag_immediate, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, metadata); } else { - return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1586,7 +1590,7 @@ fn binOp( // TODO add optimisations for multiplication // with immediates, for example a * 2 can be // lowered to a << 1 - return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1606,7 +1610,7 @@ fn binOp( }; // Generate an add/sub/mul - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (lhs_ty.zigTypeTag()) { @@ -1642,7 +1646,7 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1678,9 +1682,9 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, metadata); } else { - return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1699,7 +1703,7 @@ fn binOp( }; // Generate a shl_exact/shr_exact - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (tag) { @@ -1735,7 +1739,7 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, metadata); }, else => unreachable, } @@ -1759,12 +1763,12 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { // convert the offset into a byte offset by // multiplying it with elem_size - const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); - const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); + const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); + const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); return addr; } }, @@ -1775,8 +1779,7 @@ fn binOp( } } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[inst]; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1786,7 +1789,30 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .inst = inst, + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + }); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .inst = inst, + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + }); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1841,7 +1867,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { .sub_with_overflow => .sub, else => unreachable, }; - const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); @@ -1855,7 +1881,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); // cmp dest, truncated - _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); 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 { const dest = blk: { if (rhs_immediate_ok) { - break :blk try self.binOpImmediate(mir_tag_immediate, null, lhs, rhs, lhs_ty, false); + break :blk try self.binOpImmediate(mir_tag_immediate, lhs, rhs, lhs_ty, false, null); } else if (lhs_immediate_ok) { // swap lhs and rhs - break :blk try self.binOpImmediate(mir_tag_immediate, null, rhs, lhs, rhs_ty, true); + break :blk try self.binOpImmediate(mir_tag_immediate, rhs, lhs, rhs_ty, true, null); } else { - break :blk try self.binOpRegister(mir_tag_register, null, lhs, rhs, lhs_ty, rhs_ty); + break :blk try self.binOpRegister(mir_tag_register, lhs, rhs, lhs_ty, rhs_ty, null); } }; @@ -1952,7 +1978,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { .unsigned => .umull, }; - const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); @@ -2136,11 +2162,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp( .cmp_eq, - null, .{ .register = dest_high_reg }, .{ .immediate = 0 }, Type.usize, Type.usize, + null, ); if (int_info.bits < 64) { @@ -2156,11 +2182,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { _ = try self.binOp( .cmp_eq, - null, .{ .register = dest_high_reg }, .{ .immediate = 0 }, Type.usize, Type.usize, + null, ); } }, @@ -2218,16 +2244,16 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { self.compare_flags_inst = null; // lsl dest, lhs, rhs - const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); // asr/lsr reconstructed, dest, rhs - const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); + const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null); // cmp lhs, reconstructed - _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); + _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); try self.genSetStack(lhs_ty, stack_offset, dest); try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ @@ -2489,7 +2515,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { switch (elem_size) { else => { const dest = try self.allocRegOrMem(inst, true); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); + const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); try self.load(dest, addr, slice_ptr_field_type); break :result dest; @@ -2933,11 +2959,11 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde const dest = try self.binOp( .add, - null, .{ .register = addr_reg }, .{ .register = offset_reg }, Type.usize, Type.usize, + null, ); break :result dest; @@ -3302,7 +3328,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const int_info = int_ty.intInfo(self.target.*); if (int_info.bits <= 64) { - _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ + .inst = inst, + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + }); try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 0154548911..bfc7f687fa 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -552,21 +552,34 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airBinOp(inst), - .addwrap => try self.airBinOp(inst), + .add, => try self.airBinOp(inst, .add), + .addwrap => try self.airBinOp(inst, .addwrap), + .sub, => try self.airBinOp(inst, .sub), + .subwrap => try self.airBinOp(inst, .subwrap), + .mul => try self.airBinOp(inst, .mul), + .mulwrap => try self.airBinOp(inst, .mulwrap), + .shl => try self.airBinOp(inst, .shl), + .shl_exact => try self.airBinOp(inst, .shl_exact), + .bool_and => try self.airBinOp(inst, .bool_and), + .bool_or => try self.airBinOp(inst, .bool_or), + .bit_and => try self.airBinOp(inst, .bit_and), + .bit_or => try self.airBinOp(inst, .bit_or), + .xor => try self.airBinOp(inst, .xor), + .shr => try self.airBinOp(inst, .shr), + .shr_exact => try self.airBinOp(inst, .shr_exact), + + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + + .min => try self.airMinMax(inst), + .max => try self.airMinMax(inst), + .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airBinOp(inst), - .subwrap => try self.airBinOp(inst), .sub_sat => try self.airSubSat(inst), - .mul => try self.airBinOp(inst), - .mulwrap => try self.airBinOp(inst), .mul_sat => try self.airMulSat(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), - .shl, .shl_exact => try self.airBinOp(inst), .shl_sat => try self.airShlSat(inst), - .min => try self.airMinMax(inst), - .max => try self.airMinMax(inst), .slice => try self.airSlice(inst), .sqrt, @@ -602,13 +615,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_vector => try self.airCmpVector(inst), .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - .bool_and => try self.airBinOp(inst), - .bool_or => try self.airBinOp(inst), - .bit_and => try self.airBinOp(inst), - .bit_or => try self.airBinOp(inst), - .xor => try self.airBinOp(inst), - .shr, .shr_exact => try self.airBinOp(inst), - .alloc => try self.airAlloc(inst), .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), @@ -1260,7 +1266,7 @@ fn minMax( // register. assert(lhs_reg != rhs_reg); // see note above - _ = try self.binOpRegister(.cmp, null, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty); + _ = try self.binOpRegister(.cmp, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty, null); const cond_choose_lhs: Condition = switch (tag) { .max => switch (int_info.signedness) { @@ -1340,15 +1346,40 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[inst]; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const lhs_ty = self.air.typeOf(bin_op.lhs); const rhs_ty = self.air.typeOf(bin_op.rhs); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1402,7 +1433,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { .sub_with_overflow => .sub, else => unreachable, }; - const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); @@ -1415,7 +1446,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); // cmp dest, truncated - _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); 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 { const dest = blk: { if (rhs_immediate_ok) { - break :blk try self.binOpImmediate(mir_tag, null, lhs, rhs, lhs_ty, false); + break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null); } else if (lhs_immediate_ok) { // swap lhs and rhs - break :blk try self.binOpImmediate(mir_tag, null, rhs, lhs, rhs_ty, true); + break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null); } else { - break :blk try self.binOpRegister(mir_tag, null, lhs, rhs, lhs_ty, rhs_ty); + break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null); } }; @@ -1507,7 +1538,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { .unsigned => .mul, }; - const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_reg_lock); @@ -1520,7 +1551,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); // cmp dest, truncated - _ = try self.binOp(.cmp_eq, null, dest, .{ .register = truncated_reg }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null); try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); 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 { try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); // cmp truncated, rdlo - _ = try self.binOp(.cmp_eq, null, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize, null); // mov rdlo, #0 _ = try self.addInst(.{ @@ -1618,7 +1649,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { }); // cmp rdhi, #0 - _ = try self.binOp(.cmp_eq, null, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize); + _ = try self.binOp(.cmp_eq, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize, null); // movne rdlo, #1 _ = try self.addInst(.{ @@ -1677,16 +1708,16 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { self.compare_flags_inst = null; // lsl dest, lhs, rhs - const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); + const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null); const dest_reg = dest.register; const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg); defer self.register_manager.unlockReg(dest_lock); // asr/lsr reconstructed, dest, rhs - const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty); + const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null); // cmp lhs, reconstructed - _ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty); + _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null); try self.genSetStack(lhs_ty, stack_offset, dest); 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 { }, else => { const dest = try self.allocRegOrMem(inst, true); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); + const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); try self.load(dest, addr, slice_ptr_field_type); break :result dest; @@ -2051,7 +2082,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { const slice_ty = self.air.typeOf(extra.lhs); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize); + const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null); break :result addr; }; return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); @@ -2079,7 +2110,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { const ptr_ty = self.air.typeOf(extra.lhs); - const addr = try self.binOp(.ptr_add, null, ptr_mcv, index_mcv, ptr_ty, Type.usize); + const addr = try self.binOp(.ptr_add, ptr_mcv, index_mcv, ptr_ty, Type.usize, null); break :result addr; }; 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 const dest = try self.binOp( .add, - null, .{ .register = addr_reg }, .{ .register = offset_reg }, Type.usize, Type.usize, + null, ); break :result dest; @@ -2514,11 +2545,11 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { fn binOpRegister( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; @@ -2532,9 +2563,8 @@ fn binOpRegister( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.lhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.lhs).?; } else null; const reg = try self.register_manager.allocReg(track_inst); @@ -2547,9 +2577,8 @@ fn binOpRegister( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.rhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.rhs).?; } else null; const reg = try self.register_manager.allocReg(track_inst); @@ -2563,15 +2592,13 @@ fn binOpRegister( const dest_reg = switch (mir_tag) { .cmp => .r0, // cmp has no destination regardless - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + else => if (metadata) |md| blk: { + if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(md.inst); } } else try self.register_manager.allocReg(null), }; @@ -2634,11 +2661,11 @@ fn binOpRegister( fn binOpImmediate( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, lhs_and_rhs_swapped: bool, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; @@ -2651,10 +2678,9 @@ fn binOpImmediate( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex( - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, ).?; } else null; @@ -2669,18 +2695,16 @@ fn binOpImmediate( const dest_reg = switch (mir_tag) { .cmp => .r0, // cmp has no destination reg - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - + else => if (metadata) |md| blk: { if (lhs_is_register and self.reuseOperand( - inst, - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + md.inst, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, if (lhs_and_rhs_swapped) 1 else 0, lhs, )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(md.inst); } } else try self.register_manager.allocReg(null), }; @@ -2720,6 +2744,12 @@ fn binOpImmediate( return MCValue{ .register = dest_reg }; } +const BinOpMetadata = struct { + inst: Air.Inst.Index, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, +}; + /// For all your binary operation needs, this function will generate /// the corresponding Mir instruction(s). Returns the location of the /// result. @@ -2735,11 +2765,11 @@ fn binOpImmediate( fn binOp( self: *Self, tag: Air.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) InnerError!MCValue { switch (tag) { .add, @@ -2780,12 +2810,12 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2806,7 +2836,7 @@ fn binOp( // TODO add optimisations for multiplication // with immediates, for example a * 2 can be // lowered to a << 1 - return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); } @@ -2826,7 +2856,7 @@ fn binOp( }; // Generate an add/sub/mul - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (lhs_ty.zigTypeTag()) { @@ -2869,12 +2899,12 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2903,9 +2933,9 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); @@ -2924,7 +2954,7 @@ fn binOp( }; // Generate a shl_exact/shr_exact - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (tag) { @@ -2964,12 +2994,12 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } }, else => unreachable, @@ -2994,12 +3024,12 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { // convert the offset into a byte offset by // multiplying it with elem_size - const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); - const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); + const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); + const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); return addr; } }, @@ -3575,7 +3605,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; - _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); break :result switch (int_info.signedness) { .signed => MCValue{ .compare_flags_signed = op }, @@ -3865,7 +3899,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { } const error_mcv = try self.errUnionErr(operand, ty); - _ = try self.binOp(.cmp_eq, null, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type); + _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null); return MCValue{ .compare_flags_unsigned = .gt }; } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 22c97b9aec..c2f9b2e36d 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -481,10 +481,14 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airBinOp(inst), + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + + .add => try self.airBinOp(inst, .add), + .sub => try self.airBinOp(inst, .sub), + .addwrap => try self.airAddWrap(inst), .add_sat => try self.airAddSat(inst), - .sub, .ptr_sub => try self.airBinOp(inst), .subwrap => try self.airSubWrap(inst), .sub_sat => try self.airSubSat(inst), .mul => try self.airMul(inst), @@ -1091,8 +1095,7 @@ fn binOp( } } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[inst]; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1103,6 +1106,18 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}); diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 2ec8587273..aa679cac6d 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -483,10 +483,13 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add, .ptr_add => try self.airBinOp(inst), + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + + .add => try self.airBinOp(inst, .add), .addwrap => @panic("TODO try self.airAddWrap(inst)"), .add_sat => @panic("TODO try self.airAddSat(inst)"), - .sub, .ptr_sub => @panic("TODO try self.airBinOp(inst)"), + .sub => @panic("TODO try self.airBinOp(inst)"), .subwrap => @panic("TODO try self.airSubWrap(inst)"), .sub_sat => @panic("TODO try self.airSubSat(inst)"), .mul => @panic("TODO try self.airMul(inst)"), @@ -827,18 +830,38 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, mcv, .{ .none, .none, .none }); } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { - const tag = self.air.instructions.items(.tag)[inst]; +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const lhs_ty = self.air.typeOf(bin_op.lhs); const rhs_ty = self.air.typeOf(bin_op.rhs); - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else - try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} + +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1030,7 +1053,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { var int_buffer: Type.Payload.Bits = undefined; const int_ty = switch (lhs_ty.zigTypeTag()) { - .Vector => unreachable, // Should be handled by cmp_vector? + .Vector => unreachable, // Handled by cmp_vector. .Enum => lhs_ty.intTagType(&int_buffer), .Int => lhs_ty, .Bool => Type.initTag(.u1), @@ -1053,7 +1076,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const int_info = int_ty.intInfo(self.target.*); if (int_info.bits <= 64) { - _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); + _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, BinOpMetadata{ + .lhs = bin_op.lhs, + .rhs = bin_op.rhs, + .inst = inst, + }); try self.spillCompareFlagsIfOccupied(); self.compare_flags_inst = inst; @@ -1426,7 +1453,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { // TODO skip the ptr_add emission entirely and use native addressing modes // i.e sllx/mulx then R+R or scale immediate then R+I const dest = try self.allocRegOrMem(inst, true); - const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ptr_field_type, Type.usize); + const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); try self.load(dest, addr, slice_ptr_field_type); break :result dest; @@ -1595,6 +1622,12 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { return MCValue{ .stack_offset = stack_offset }; } +const BinOpMetadata = struct { + inst: Air.Inst.Index, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, +}; + /// For all your binary operation needs, this function will generate /// the corresponding Mir instruction(s). Returns the location of the /// result. @@ -1610,11 +1643,11 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { fn binOp( self: *Self, tag: Air.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) InnerError!MCValue { const mod = self.bin_file.options.module.?; switch (tag) { @@ -1649,13 +1682,13 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else if (lhs_immediate_ok) { // swap lhs and rhs - return try self.binOpImmediate(mir_tag, maybe_inst, rhs, lhs, rhs_ty, true); + return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata); } else { // TODO convert large immediates to register before adding - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1683,10 +1716,10 @@ fn binOp( // If it's a power of two immediate then we emit an shl instead // TODO add similar checks for LHS if (new_rhs == .immediate and math.isPowerOfTwo(new_rhs.immediate)) { - return try self.binOp(.shl, maybe_inst, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize); + return try self.binOp(.shl, new_lhs, .{ .immediate = math.log2(new_rhs.immediate) }, new_lhs_ty, Type.usize, metadata); } - return try self.binOpRegister(.mulx, maybe_inst, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty); + return try self.binOpRegister(.mulx, new_lhs, new_rhs, new_lhs_ty, new_rhs_ty, metadata); } else { return self.fail("TODO binary operations on int with bits > 64", .{}); } @@ -1711,13 +1744,13 @@ fn binOp( else => unreachable, }; - return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } else { // convert the offset into a byte offset by // multiplying it with elem_size - const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); - const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); + const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null); + const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null); return addr; } }, @@ -1732,7 +1765,7 @@ fn binOp( }; // Generate a shl_exact/shr_exact - const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); // Truncate if necessary switch (tag) { @@ -1768,9 +1801,9 @@ fn binOp( }; if (rhs_immediate_ok) { - return try self.binOpImmediate(mir_tag, maybe_inst, lhs, rhs, lhs_ty, false); + return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata); } else { - return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); + return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata); } } else { return self.fail("TODO binary operations on int with bits > 64", .{}); @@ -1792,18 +1825,17 @@ fn binOp( /// op dest, lhs, #rhs_imm /// /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to -/// rhs and vice versa. This parameter is only used when maybe_inst != -/// null. +/// rhs and vice versa. This parameter is only used when metadata != null. /// /// Asserts that generating an instruction of that form is possible. fn binOpImmediate( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, lhs_and_rhs_swapped: bool, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; @@ -1816,10 +1848,9 @@ fn binOpImmediate( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { break :inst Air.refToIndex( - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, ).?; } else null; @@ -1833,18 +1864,16 @@ fn binOpImmediate( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - + else => if (metadata) |md| blk: { if (lhs_is_register and self.reuseOperand( - inst, - if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, + md.inst, + if (lhs_and_rhs_swapped) md.rhs else md.lhs, if (lhs_and_rhs_swapped) 1 else 0, lhs, )) { break :blk lhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(md.inst); } } else blk: { break :blk try self.register_manager.allocReg(null); @@ -1896,11 +1925,11 @@ fn binOpImmediate( fn binOpRegister( self: *Self, mir_tag: Mir.Inst.Tag, - maybe_inst: ?Air.Inst.Index, lhs: MCValue, rhs: MCValue, lhs_ty: Type, rhs_ty: Type, + metadata: ?BinOpMetadata, ) !MCValue { const lhs_is_register = lhs == .register; const rhs_is_register = rhs == .register; @@ -1920,9 +1949,8 @@ fn binOpRegister( const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; const lhs_reg = if (lhs_is_register) lhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.lhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.lhs).?; } else null; const reg = try self.register_manager.allocReg(track_inst); @@ -1934,9 +1962,8 @@ fn binOpRegister( defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); const rhs_reg = if (rhs_is_register) rhs.register else blk: { - const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - break :inst Air.refToIndex(bin_op.rhs).?; + const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: { + break :inst Air.refToIndex(md.rhs).?; } else null; const reg = try self.register_manager.allocReg(track_inst); @@ -1948,15 +1975,13 @@ fn binOpRegister( defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); const dest_reg = switch (mir_tag) { - else => if (maybe_inst) |inst| blk: { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - - if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { + else => if (metadata) |md| blk: { + if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) { break :blk lhs_reg; - } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { + } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) { break :blk rhs_reg; } else { - break :blk try self.register_manager.allocReg(inst); + break :blk try self.register_manager.allocReg(md.inst); } } else blk: { 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 const dest = try self.binOp( .add, - null, .{ .register = addr_reg }, .{ .register = offset_reg }, Type.usize, Type.usize, + null, ); break :result dest; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 60d03ccafd..a35589f043 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3397,7 +3397,8 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { fn airPtrBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr = try self.resolveInst(bin_op.lhs); const offset = try self.resolveInst(bin_op.rhs); const ptr_ty = self.air.typeOf(bin_op.lhs); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bb3ebec5db..f1455b0591 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -574,23 +574,33 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { switch (air_tags[inst]) { // zig fmt: off - .add => try self.airBinOp(inst), - .addwrap => try self.airBinOp(inst), - .add_sat => try self.airAddSat(inst), - .sub => try self.airBinOp(inst), - .subwrap => try self.airBinOp(inst), - .sub_sat => try self.airSubSat(inst), + .add => try self.airBinOp(inst, .add), + .addwrap => try self.airBinOp(inst, .addwrap), + .sub => try self.airBinOp(inst, .sub), + .subwrap => try self.airBinOp(inst, .subwrap), + .bool_and => try self.airBinOp(inst, .bool_and), + .bool_or => try self.airBinOp(inst, .bool_or), + .bit_and => try self.airBinOp(inst, .bit_and), + .bit_or => try self.airBinOp(inst, .bit_or), + .xor => try self.airBinOp(inst, .xor), + + .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), + .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), + + .shr, .shr_exact => try self.airShlShrBinOp(inst), + .shl, .shl_exact => try self.airShlShrBinOp(inst), + .mul => try self.airMulDivBinOp(inst), .mulwrap => try self.airMulDivBinOp(inst), - .mul_sat => try self.airMulSat(inst), .rem => try self.airMulDivBinOp(inst), .mod => try self.airMulDivBinOp(inst), - .shl, .shl_exact => try self.airShlShrBinOp(inst), + + .add_sat => try self.airAddSat(inst), + .sub_sat => try self.airSubSat(inst), + .mul_sat => try self.airMulSat(inst), .shl_sat => try self.airShlSat(inst), .min => try self.airMin(inst), .max => try self.airMax(inst), - .ptr_add => try self.airBinOp(inst), - .ptr_sub => try self.airBinOp(inst), .slice => try self.airSlice(inst), .sqrt, @@ -626,13 +636,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_vector => try self.airCmpVector(inst), .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), - .bool_and => try self.airBinOp(inst), - .bool_or => try self.airBinOp(inst), - .bit_and => try self.airBinOp(inst), - .bit_or => try self.airBinOp(inst), - .xor => try self.airBinOp(inst), - .shr, .shr_exact => try self.airShlShrBinOp(inst), - .alloc => try self.airAlloc(inst), .ret_ptr => try self.airRetPtr(inst), .arg => try self.airArg(inst), @@ -1231,21 +1234,26 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } -fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { +fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; if (self.liveness.isUnused(inst)) { return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); } - const tag = self.air.instructions.items(.tag)[inst]; - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const lhs_ty = self.air.typeOf(bin_op.lhs); - const rhs_ty = self.air.typeOf(bin_op.rhs); + const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +} - const result = try self.genBinOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); +fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const result = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1316,13 +1324,12 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { try self.spillRegisters(1, .{.rcx}); } - const lhs = try self.resolveInst(bin_op.lhs); - const rhs = try self.resolveInst(bin_op.rhs); - const partial: MCValue = switch (tag) { - .add_with_overflow => try self.genBinOp(.add, null, lhs, rhs, ty, ty), - .sub_with_overflow => try self.genBinOp(.sub, null, lhs, rhs, ty, ty), + .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs), + .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs), .shl_with_overflow => blk: { + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); const shift_ty = self.air.typeOf(bin_op.rhs); break :blk try self.genShiftBinOp(.shl, null, lhs, rhs, ty, shift_ty); }, @@ -3310,13 +3317,15 @@ fn genMulDivBinOp( /// Result is always a register. fn genBinOp( self: *Self, - tag: Air.Inst.Tag, maybe_inst: ?Air.Inst.Index, - lhs: MCValue, - rhs: MCValue, - lhs_ty: Type, - rhs_ty: Type, + tag: Air.Inst.Tag, + lhs_air: Air.Inst.Ref, + rhs_air: Air.Inst.Ref, ) !MCValue { + const lhs = try self.resolveInst(lhs_air); + const rhs = try self.resolveInst(rhs_air); + const lhs_ty = self.air.typeOf(lhs_air); + const rhs_ty = self.air.typeOf(rhs_air); if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmtDebug()}); } @@ -3352,11 +3361,10 @@ fn genBinOp( var flipped: bool = false; const dst_mcv: MCValue = blk: { if (maybe_inst) |inst| { - const bin_op = self.air.instructions.items(.data)[inst].bin_op; - if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) { + if (self.reuseOperand(inst, lhs_air, 0, lhs) and lhs.isRegister()) { break :blk lhs; } - if (is_commutative and self.reuseOperand(inst, bin_op.rhs, 1, rhs) and rhs.isRegister()) { + if (is_commutative and self.reuseOperand(inst, rhs_air, 1, rhs) and rhs.isRegister()) { flipped = true; break :blk rhs; } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 92770168f4..5f61f8586e 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1711,21 +1711,18 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .unreach => try airUnreach(f), .fence => try airFence(f, inst), - // TODO use a different strategy for add that communicates to the optimizer - // that wrapping is UB. - .add => try airBinOp (f, inst, " + "), - .ptr_add => try airPtrAddSub (f, inst, " + "), - // TODO use a different strategy for sub that communicates to the optimizer - // that wrapping is UB. - .sub => try airBinOp (f, inst, " - "), - .ptr_sub => try airPtrAddSub (f, inst, " - "), - // TODO use a different strategy for mul that communicates to the optimizer - // that wrapping is UB. - .mul => try airBinOp (f, inst, " * "), - // TODO use a different strategy for div that communicates to the optimizer - // that wrapping is UB. + .ptr_add => try airPtrAddSub(f, inst, " + "), + .ptr_sub => try airPtrAddSub(f, inst, " - "), + + // TODO use a different strategy for add, sub, mul, div + // that communicates to the optimizer that wrapping is UB. + .add => try airBinOp (f, inst, " + "), + .sub => try airBinOp (f, inst, " - "), + .mul => try airBinOp (f, inst, " * "), .div_float, .div_exact => try airBinOp( f, inst, " / "), - .div_trunc => blk: { + .rem => try airBinOp( f, inst, " % "), + + .div_trunc => blk: { const bin_op = f.air.instructions.items(.data)[inst].bin_op; const lhs_ty = f.air.typeOf(bin_op.lhs); // For binary operations @TypeOf(lhs)==@TypeOf(rhs), @@ -1735,9 +1732,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO else try airBinOpBuiltinCall(f, inst, "div_trunc"); }, - .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"), - .rem => try airBinOp( f, inst, " % "), - .mod => try airBinOpBuiltinCall(f, inst, "mod"), + .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"), + .mod => try airBinOpBuiltinCall(f, inst, "mod"), .addwrap => try airWrapOp(f, inst, " + ", "addw_"), .subwrap => try airWrapOp(f, inst, " - ", "subw_"), @@ -2617,10 +2613,10 @@ fn airEquality( } fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue { - if (f.liveness.isUnused(inst)) - return CValue.none; + if (f.liveness.isUnused(inst)) return CValue.none; - const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; + const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data; const lhs = try f.resolveInst(bin_op.lhs); const rhs = try f.resolveInst(bin_op.rhs); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index fb7a2c39bc..aba290060a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5679,7 +5679,8 @@ pub const FuncGen = struct { fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const base_ptr = try self.resolveInst(bin_op.lhs); const offset = try self.resolveInst(bin_op.rhs); const ptr_ty = self.air.typeOf(bin_op.lhs); @@ -5698,7 +5699,8 @@ pub const FuncGen = struct { fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; - const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const base_ptr = try self.resolveInst(bin_op.lhs); const offset = try self.resolveInst(bin_op.rhs); const negative_offset = self.builder.buildNeg(offset, ""); diff --git a/src/print_air.zig b/src/print_air.zig index 0524356fa7..e62ca806b7 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -114,8 +114,6 @@ const Writer = struct { .div_exact, .rem, .mod, - .ptr_add, - .ptr_sub, .bit_and, .bit_or, .xor, @@ -231,6 +229,12 @@ const Writer = struct { .slice, .slice_elem_ptr, .ptr_elem_ptr, + .ptr_add, + .ptr_sub, + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, => try w.writeTyPlBin(s, inst), .call, @@ -275,12 +279,6 @@ const Writer = struct { .reduce => try w.writeReduce(s, inst), .cmp_vector => try w.writeCmpVector(s, inst), - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - => try w.writeOverflow(s, inst), - .dbg_block_begin, .dbg_block_end => {}, } } @@ -478,15 +476,6 @@ const Writer = struct { try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); } - fn writeOverflow(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; - const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; - - try w.writeOperand(s, inst, 0, extra.lhs); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.rhs); - } - fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const pl_op = w.air.instructions.items(.data)[inst].pl_op; const extra = w.air.extraData(Air.Bin, pl_op.payload).data; diff --git a/src/value.zig b/src/value.zig index b7764327c0..588c7d2832 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1813,27 +1813,6 @@ pub const Value = extern union { }; } - /// Asserts the value is numeric - pub fn isZero(self: Value) bool { - return switch (self.tag()) { - .zero, .the_only_possible_value => true, - .one => false, - - .int_u64 => self.castTag(.int_u64).?.data == 0, - .int_i64 => self.castTag(.int_i64).?.data == 0, - - .float_16 => self.castTag(.float_16).?.data == 0, - .float_32 => self.castTag(.float_32).?.data == 0, - .float_64 => self.castTag(.float_64).?.data == 0, - .float_80 => self.castTag(.float_80).?.data == 0, - .float_128 => self.castTag(.float_128).?.data == 0, - - .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().eqZero(), - .int_big_negative => self.castTag(.int_big_negative).?.asBigInt().eqZero(), - else => unreachable, - }; - } - pub fn orderAgainstZero(lhs: Value) std.math.Order { return orderAgainstZeroAdvanced(lhs, null) catch unreachable; } @@ -3442,7 +3421,6 @@ pub const Value = extern union { const info = ty.intInfo(target); if (info.bits == 0) { - assert(val.isZero()); // Sema should guarantee return val; } diff --git a/test/behavior/align.zig b/test/behavior/align.zig index ffe53f9088..5b753aa737 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -16,11 +16,22 @@ test "global variable alignment" { const slice = @as(*align(4) [1]u8, &foo)[0..]; comptime try expect(@TypeOf(slice) == *align(4) [1]u8); } - { - var runtime_zero: usize = 0; - const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..]; - comptime try expect(@TypeOf(slice) == []align(4) u8); - } +} + +test "slicing array of length 1 can assume runtime index is always zero" { + if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + + // TODO reevaluate this test case, because notice that you can + // change `runtime_zero` to be `1` and the test still passes for stage1. + // Reconsider also this code: + // var array: [4]u8 = undefined; + // var runtime: usize = 4; + // var ptr = array[runtime..]; + // _ = ptr; + + var runtime_zero: usize = 0; + const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..]; + comptime try expect(@TypeOf(slice) == []align(4) u8); } test "default alignment allows unspecified in type syntax" { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 42939986d4..07bab40135 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -377,8 +377,6 @@ test "pointer to array at fixed address" { } test "pointer arithmetic affects the alignment" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - { var ptr: [*]align(8) u32 = undefined; var x: usize = 1; From 0fafc8cc4409b35649c19af19f7c259ff570c0b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 May 2022 15:13:20 -0700 Subject: [PATCH 4/6] std.Thread: insert a missing `@alignCast` stage1 has a missing compile error for this situation. --- lib/std/Thread.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index 9e29c82672..06903a9e91 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -893,6 +893,7 @@ const LinuxThreadImpl = struct { }; fn spawn(config: SpawnConfig, comptime f: anytype, args: anytype) !Impl { + const page_size = std.mem.page_size; const Args = @TypeOf(args); const Instance = struct { fn_args: Args, @@ -915,11 +916,11 @@ const LinuxThreadImpl = struct { var instance_offset: usize = undefined; const map_bytes = blk: { - var bytes: usize = std.mem.page_size; + var bytes: usize = page_size; guard_offset = bytes; - bytes += std.math.max(std.mem.page_size, config.stack_size); - bytes = std.mem.alignForward(bytes, std.mem.page_size); + bytes += std.math.max(page_size, config.stack_size); + bytes = std.mem.alignForward(bytes, page_size); stack_offset = bytes; bytes = std.mem.alignForward(bytes, linux.tls.tls_image.alloc_align); @@ -930,7 +931,7 @@ const LinuxThreadImpl = struct { instance_offset = bytes; bytes += @sizeOf(Instance); - bytes = std.mem.alignForward(bytes, std.mem.page_size); + bytes = std.mem.alignForward(bytes, page_size); break :blk bytes; }; @@ -954,7 +955,7 @@ const LinuxThreadImpl = struct { // map everything but the guard page as read/write os.mprotect( - mapped[guard_offset..], + @alignCast(page_size, mapped[guard_offset..]), os.PROT.READ | os.PROT.WRITE, ) catch |err| switch (err) { error.AccessDenied => unreachable, From 691fba38d8cf28a32e6d4a4cd2111c401e763d08 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 May 2022 15:21:33 -0700 Subject: [PATCH 5/6] enable passing behavior test --- test/behavior/cast.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 231fa25b2d..b98eaea37a 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -326,8 +326,7 @@ test "array coersion to undefined at runtime" { @setRuntimeSafety(true); - // TODO implement @setRuntimeSafety in stage2 - if (builtin.zig_backend != .stage1 and builtin.mode != .Debug and builtin.mode != .ReleaseSafe) { + if (builtin.mode != .Debug and builtin.mode != .ReleaseSafe) { return error.SkipZigTest; } From 9031cc54f27b4483c62ab904dd94063a9499c842 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 17 May 2022 16:51:35 -0700 Subject: [PATCH 6/6] Sema: implement `@intCast` for vectors --- src/Sema.zig | 95 +++++++++++++++++++++++++++++++----------- test/behavior/cast.zig | 6 ++- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7086bb3f13..5866cc21c9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7020,22 +7020,25 @@ fn intCast( operand_src: LazySrcLoc, runtime_safety: bool, ) CompileError!Air.Inst.Ref { - // TODO: Add support for vectors - const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty); - _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand)); + const operand_ty = sema.typeOf(operand); + const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src); + const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); if (try sema.isComptimeKnown(block, operand_src, operand)) { return sema.coerce(block, dest_ty, operand, operand_src); - } else if (dest_is_comptime_int) { + } else if (dest_scalar_ty.zigTypeTag() == .ComptimeInt) { return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); } + try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); + const is_vector = dest_ty.zigTypeTag() == .Vector; + if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| { // requirement: intCast(u0, input) iff input == 0 if (runtime_safety and block.wantSafety()) { try sema.requireRuntimeBlock(block, operand_src); const target = sema.mod.getTarget(); - const wanted_info = dest_ty.intInfo(target); + const wanted_info = dest_scalar_ty.intInfo(target); const wanted_bits = wanted_info.bits; if (wanted_bits == 0) { @@ -7051,9 +7054,8 @@ fn intCast( try sema.requireRuntimeBlock(block, operand_src); if (runtime_safety and block.wantSafety()) { const target = sema.mod.getTarget(); - const operand_ty = sema.typeOf(operand); - const actual_info = operand_ty.intInfo(target); - const wanted_info = dest_ty.intInfo(target); + const actual_info = operand_scalar_ty.intInfo(target); + const wanted_info = dest_scalar_ty.intInfo(target); const actual_bits = actual_info.bits; const wanted_bits = wanted_info.bits; const actual_value_bits = actual_bits - @boolToInt(actual_info.signedness == .signed); @@ -7062,7 +7064,11 @@ fn intCast( // range shrinkage // requirement: int value fits into target type if (wanted_value_bits < actual_value_bits) { - const dest_max_val = try dest_ty.maxInt(sema.arena, target); + const dest_max_val_scalar = try dest_scalar_ty.maxInt(sema.arena, target); + const dest_max_val = if (is_vector) + try Value.Tag.repeated.create(sema.arena, dest_max_val_scalar) + else + dest_max_val_scalar; const dest_max = try sema.addConstant(operand_ty, dest_max_val); const diff = try block.addBinOp(.subwrap, dest_max, operand); @@ -7080,19 +7086,59 @@ fn intCast( } else dest_max_val; const dest_range = try sema.addConstant(unsigned_operand_ty, dest_range_val); - const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range); - try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + const ok = if (is_vector) ok: { + const is_in_range = try block.addCmpVector(diff_unsigned, dest_range, .lte, try sema.addType(operand_ty)); + const all_in_range = try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = is_in_range, + .operation = .And, + } }, + }); + break :ok all_in_range; + } else ok: { + const is_in_range = try block.addBinOp(.cmp_lte, diff_unsigned, dest_range); + break :ok is_in_range; + }; + try sema.addSafetyCheck(block, ok, .cast_truncated_data); } else { - const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max); - try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + const ok = if (is_vector) ok: { + const is_in_range = try block.addCmpVector(diff, dest_max, .lte, try sema.addType(operand_ty)); + const all_in_range = try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = is_in_range, + .operation = .And, + } }, + }); + break :ok all_in_range; + } else ok: { + const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max); + break :ok is_in_range; + }; + try sema.addSafetyCheck(block, ok, .cast_truncated_data); } - } - // no shrinkage, yes sign loss - // requirement: signed to unsigned >= 0 - else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) { - const zero_inst = try sema.addConstant(operand_ty, Value.zero); - const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); - try sema.addSafetyCheck(block, is_in_range, .cast_truncated_data); + } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) { + // no shrinkage, yes sign loss + // requirement: signed to unsigned >= 0 + const ok = if (is_vector) ok: { + const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero); + const zero_inst = try sema.addConstant(operand_ty, zero_val); + const is_in_range = try block.addCmpVector(operand, zero_inst, .lte, try sema.addType(operand_ty)); + const all_in_range = try block.addInst(.{ + .tag = .reduce, + .data = .{ .reduce = .{ + .operand = is_in_range, + .operation = .And, + } }, + }); + break :ok all_in_range; + } else ok: { + const zero_inst = try sema.addConstant(operand_ty, Value.zero); + const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst); + break :ok is_in_range; + }; + try sema.addSafetyCheck(block, ok, .cast_truncated_data); } } return block.addTyOp(.intcast, dest_ty, operand); @@ -14517,8 +14563,8 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const dest_scalar_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = sema.resolveInst(extra.rhs); const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_scalar_ty); - const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src); const operand_ty = sema.typeOf(operand); + const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); const is_vector = operand_ty.zigTypeTag() == .Vector; const dest_ty = if (is_vector) try Type.vector(sema.arena, operand_ty.vectorLen(), dest_scalar_ty) @@ -14686,7 +14732,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src); + const scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); const target = sema.mod.getTarget(); const bits = scalar_ty.intInfo(target).bits; if (bits % 8 != 0) { @@ -14743,7 +14789,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); - _ = try sema.checkIntOrVectorAllowComptime(block, operand, operand_src); + _ = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); if (try sema.typeHasOnePossibleValue(block, operand_src, operand_ty)) |val| { return sema.addConstant(operand_ty, val); @@ -15095,10 +15141,9 @@ fn checkIntOrVector( fn checkIntOrVectorAllowComptime( sema: *Sema, block: *Block, - operand: Air.Inst.Ref, + operand_ty: Type, operand_src: LazySrcLoc, ) CompileError!Type { - const operand_ty = sema.typeOf(operand); switch (try operand_ty.zigTypeTagOrPoison()) { .Int, .ComptimeInt => return operand_ty, .Vector => { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index b98eaea37a..6753b731f0 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -587,7 +587,11 @@ test "cast *[1][*]const u8 to [*]const ?[*]const u8" { } test "vector casts" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void {