From 1803167db2d5045e71015498b8e5b184c53edf07 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 25 Mar 2022 09:30:53 +0100 Subject: [PATCH 1/3] AIR: change signature of overflow arithmetic instructions add_with_overflow and similar functions now have the ty_pl data attached. The Payload will now be a binary operation and the inst is expected to return a tuple consisting of the destination integer type and an overflow bit (u1). Co-authored-by: Jan Philipp Hafer --- src/Air.zig | 38 ++++++++++++++++---------------------- src/print_air.zig | 10 ++++------ 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 5120e0fd677d..a0fb51293466 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -134,28 +134,24 @@ pub const Inst = struct { /// Uses the `bin_op` field. min, /// Integer addition with overflow. Both operands are guaranteed to be the same type, - /// and the result is bool. The wrapped value is written to the pointer given by the in - /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types - /// of the operation. - /// Uses the `pl_op` field with payload `Bin`. + /// and the result is a tuple with .{res, ov}. The wrapped value is written to res + /// and if an overflow happens, ov is 1. Otherwise ov is 0. + /// Uses the `ty_pl` field. Payload is `Bin`. add_with_overflow, /// Integer subtraction with overflow. Both operands are guaranteed to be the same type, - /// and the result is bool. The wrapped value is written to the pointer given by the in - /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types - /// of the operation. - /// Uses the `pl_op` field with payload `Bin`. + /// and the result is a tuple with .{res, ov}. The wrapped value is written to res + /// and if an overflow happens, ov is 1. Otherwise ov is 0. + /// Uses the `ty_pl` field. Payload is `Bin`. sub_with_overflow, /// Integer multiplication with overflow. Both operands are guaranteed to be the same type, - /// and the result is bool. The wrapped value is written to the pointer given by the in - /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types - /// of the operation. - /// Uses the `pl_op` field with payload `Bin`. + /// and the result is a tuple with .{res, ov}. The wrapped value is written to res + /// and if an overflow happens, ov is 1. Otherwise ov is 0. + /// Uses the `ty_pl` field. Payload is `Bin`. mul_with_overflow, /// Integer left-shift with overflow. Both operands are guaranteed to be the same type, - /// and the result is bool. The wrapped value is written to the pointer given by the in - /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types - /// of the operation. - /// Uses the `pl_op` field with payload `Bin`. + /// and the result is a tuple with .{res, ov}. The wrapped value is written to res + /// and if an overflow happens, ov is 1. Otherwise ov is 0. + /// Uses the `ty_pl` field. Payload is `Bin`. shl_with_overflow, /// Allocates stack local memory. /// Uses the `ty` field. @@ -964,6 +960,10 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .union_init, .field_parent_ptr, .cmp_vector, + .add_with_overflow, + .sub_with_overflow, + .mul_with_overflow, + .shl_with_overflow, => return air.getRefType(datas[inst].ty_pl.ty), .not, @@ -1074,12 +1074,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { const extra = air.extraData(Air.Bin, datas[inst].pl_op.payload).data; return air.typeOf(extra.lhs); }, - - .add_with_overflow, - .sub_with_overflow, - .mul_with_overflow, - .shl_with_overflow, - => return Type.bool, } } diff --git a/src/print_air.zig b/src/print_air.zig index f1e51150a601..6552b54faf47 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -473,14 +473,12 @@ const Writer = struct { } fn writeOverflow(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; + 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, pl_op.operand); - try s.writeAll(", "); - try w.writeOperand(s, inst, 1, extra.lhs); + try w.writeOperand(s, inst, 0, extra.lhs); try s.writeAll(", "); - try w.writeOperand(s, inst, 2, extra.rhs); + try w.writeOperand(s, inst, 1, extra.rhs); } fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { From e2468e3f2732a7a301f4174eb4d99bca5315a643 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 25 Mar 2022 21:35:28 +0100 Subject: [PATCH 2/3] Sema: change zirOverflowArithmetic to use new version of AIR insts Also applies the change to Liveness --- src/Liveness.zig | 11 ++++++++--- src/Sema.zig | 28 ++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Liveness.zig b/src/Liveness.zig index b9f4e6b33afb..4b099baf6dad 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -508,14 +508,19 @@ fn analyzeInst( }, .memset, .memcpy, + => { + const pl_op = inst_datas[inst].pl_op; + 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 pl_op = inst_datas[inst].pl_op; - 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 }); + 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; diff --git a/src/Sema.zig b/src/Sema.zig index f41528eca460..d2295a01a864 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9064,6 +9064,18 @@ fn zirOverflowArithmetic( const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); + const types = try sema.arena.alloc(Type, 2); + const values = try sema.arena.alloc(Value, 2); + const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ + .types = types, + .values = values, + }); + + types[0] = dest_ty; + types[1] = Type.initTag(.u1); + values[0] = Value.initTag(.unreachable_value); + values[1] = Value.initTag(.unreachable_value); + const result: struct { overflowed: enum { yes, no, undef }, wrapped: Air.Inst.Ref, @@ -9188,16 +9200,24 @@ fn zirOverflowArithmetic( }; try sema.requireRuntimeBlock(block, src); - return block.addInst(.{ + + const tuple = try block.addInst(.{ .tag = air_tag, - .data = .{ .pl_op = .{ - .operand = ptr, - .payload = try sema.addExtra(Air.Bin{ + .data = .{ .ty_pl = .{ + .ty = try block.sema.addType(tuple_ty), + .payload = try block.sema.addExtra(Air.Bin{ .lhs = lhs, .rhs = rhs, }), } }, }); + + const wrapped = try block.addStructFieldVal(tuple, 0, dest_ty); + try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store); + + const overflow_bit = try block.addStructFieldVal(tuple, 1, Type.initTag(.u1)); + const zero_u1 = try sema.addConstant(Type.initTag(.u1), Value.zero); + return try block.addBinOp(.cmp_neq, overflow_bit, zero_u1); }; try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store); From 9070ad77740477f7f3806bad884b4070d476a68c Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sat, 26 Mar 2022 12:04:52 +0100 Subject: [PATCH 3/3] stage2 LLVM: Adjust to new AIR arithmetic overflow instructions --- src/codegen/llvm.zig | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 00074d69d170..df7f05948db6 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5189,14 +5189,12 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; - const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const ptr = try self.resolveInst(pl_op.operand); const lhs = try self.resolveInst(extra.lhs); const rhs = try self.resolveInst(extra.rhs); - const ptr_ty = self.air.typeOf(pl_op.operand); const lhs_ty = self.air.typeOf(extra.lhs); const intrinsic_name = if (lhs_ty.isSignedInt()) signed_intrinsic else unsigned_intrinsic; @@ -5205,13 +5203,7 @@ pub const FuncGen = struct { const llvm_fn = self.getIntrinsic(intrinsic_name, &.{llvm_lhs_ty}); const result_struct = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{ lhs, rhs }, 2, .Fast, .Auto, ""); - - const result = self.builder.buildExtractValue(result_struct, 0, ""); - const overflow_bit = self.builder.buildExtractValue(result_struct, 1, ""); - - self.store(ptr, ptr_ty, result, .NotAtomic); - - return overflow_bit; + return result_struct; } fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -5293,16 +5285,16 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; - const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const ptr = try self.resolveInst(pl_op.operand); const lhs = try self.resolveInst(extra.lhs); const rhs = try self.resolveInst(extra.rhs); - const ptr_ty = self.air.typeOf(pl_op.operand); const lhs_ty = self.air.typeOf(extra.lhs); const rhs_ty = self.air.typeOf(extra.rhs); + const dest_ty = self.air.typeOfIndex(inst); + const llvm_dest_ty = try self.dg.llvmType(dest_ty); const tg = self.dg.module.getTarget(); @@ -5319,9 +5311,8 @@ pub const FuncGen = struct { const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, ""); - self.store(ptr, ptr_ty, result, .NotAtomic); - - return overflow_bit; + const partial = self.builder.buildInsertValue(llvm_dest_ty.getUndef(), result, 0, ""); + return self.builder.buildInsertValue(partial, overflow_bit, 1, ""); } fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {