Skip to content

stage2: Change semantics of AIR arithmetic overflow instructions #11311

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 16 additions & 22 deletions src/Air.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
}
}

Expand Down
11 changes: 8 additions & 3 deletions src/Liveness.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 24 additions & 4 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
27 changes: 9 additions & 18 deletions src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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();

Expand All @@ -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 {
Expand Down
10 changes: 4 additions & 6 deletions src/print_air.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down