mirror of
https://github.com/ziglang/zig.git
synced 2025-12-24 15:13:08 +00:00
Merge pull request #11311 from joachimschmidt557/builtin-with-overflow
stage2: Change semantics of AIR arithmetic overflow instructions
This commit is contained in:
commit
97a53bb8a1
38
src/Air.zig
38
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
28
src/Sema.zig
28
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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 w.writeOperand(s, inst, 0, extra.lhs);
|
||||
try s.writeAll(", ");
|
||||
try w.writeOperand(s, inst, 1, 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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user