authorgravatar for william@sengir.comWilliam Sengir <william@sengir.com> 2022-03-26 15:55:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-16 13:55:26-07:00
logeb06c78a8ac472b3406075dcbddf7fce63e98597
tree0880c45c0724d5d42168716785293db3122cf319
parent86a928ce61ae7df52d3d54fa5c653195a7a4cbef

Sema: vectorize overflow arithmetic


1 files changed, 73 insertions(+), 45 deletions(-)

src/Sema.zig+73-45
......@@ -9417,32 +9417,29 @@ fn zirOverflowArithmetic(
94179417 const ptr = sema.resolveInst(extra.ptr);
94189418
94199419 const lhs_ty = sema.typeOf(lhs);
9420 const rhs_ty = sema.typeOf(rhs);
94209421 const mod = sema.mod;
94219422 const target = mod.getTarget();
94229423
94239424 // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen.
9425 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
94249426 const dest_ty = lhs_ty;
9425 if (dest_ty.zigTypeTag() != .Int) {
9426 return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(mod)});
9427 if (dest_ty.scalarType().zigTypeTag() != .Int) {
9428 return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)});
94279429 }
94289430
94299431 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
94309432 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
94319433
9432 const types = try sema.arena.alloc(Type, 2);
9433 const values = try sema.arena.alloc(Value, 2);
9434 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
9435 .types = types,
9436 .values = values,
9437 });
9438
9439 types[0] = dest_ty;
9440 types[1] = Type.initTag(.u1);
9441 values[0] = Value.initTag(.unreachable_value);
9442 values[1] = Value.initTag(.unreachable_value);
9434 const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
9435 const ov_ty = tuple_ty.tupleFields().types[1];
9436 // TODO: Remove and use `ov_ty` instead.
9437 // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`.
9438 const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool";
94439439
94449440 const result: struct {
9445 overflowed: enum { yes, no, undef },
9441 /// TODO: Rename to `overflow_bit` and make of type `u1`.
9442 overflowed: Air.Inst.Ref,
94469443 wrapped: Air.Inst.Ref,
94479444 } = result: {
94489445 switch (zir_tag) {
......@@ -9452,23 +9449,24 @@ fn zirOverflowArithmetic(
94529449 // Otherwise, if either of the argument is undefined, undefined is returned.
94539450 if (maybe_lhs_val) |lhs_val| {
94549451 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
9455 break :result .{ .overflowed = .no, .wrapped = rhs };
9452 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
94569453 }
94579454 }
94589455 if (maybe_rhs_val) |rhs_val| {
94599456 if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {
9460 break :result .{ .overflowed = .no, .wrapped = lhs };
9457 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
94619458 }
94629459 }
94639460 if (maybe_lhs_val) |lhs_val| {
94649461 if (maybe_rhs_val) |rhs_val| {
94659462 if (lhs_val.isUndef() or rhs_val.isUndef()) {
9466 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
9463 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
94679464 }
94689465
94699466 const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target);
9470 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
9471 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
9467 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9468 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9469 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
94729470 }
94739471 }
94749472 },
......@@ -9477,17 +9475,18 @@ fn zirOverflowArithmetic(
94779475 // Otherwise, if either result is undefined, both results are undefined.
94789476 if (maybe_rhs_val) |rhs_val| {
94799477 if (rhs_val.isUndef()) {
9480 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
9478 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
94819479 } else if (rhs_val.compareWithZero(.eq)) {
9482 break :result .{ .overflowed = .no, .wrapped = lhs };
9480 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
94839481 } else if (maybe_lhs_val) |lhs_val| {
94849482 if (lhs_val.isUndef()) {
9485 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
9483 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
94869484 }
94879485
94889486 const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target);
9489 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
9490 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
9487 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9488 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9489 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
94919490 }
94929491 }
94939492 },
......@@ -9498,9 +9497,9 @@ fn zirOverflowArithmetic(
94989497 if (maybe_lhs_val) |lhs_val| {
94999498 if (!lhs_val.isUndef()) {
95009499 if (lhs_val.compareWithZero(.eq)) {
9501 break :result .{ .overflowed = .no, .wrapped = lhs };
9500 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
95029501 } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) {
9503 break :result .{ .overflowed = .no, .wrapped = rhs };
9502 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
95049503 }
95059504 }
95069505 }
......@@ -9508,9 +9507,9 @@ fn zirOverflowArithmetic(
95089507 if (maybe_rhs_val) |rhs_val| {
95099508 if (!rhs_val.isUndef()) {
95109509 if (rhs_val.compareWithZero(.eq)) {
9511 break :result .{ .overflowed = .no, .wrapped = rhs };
9510 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
95129511 } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) {
9513 break :result .{ .overflowed = .no, .wrapped = lhs };
9512 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
95149513 }
95159514 }
95169515 }
......@@ -9518,12 +9517,13 @@ fn zirOverflowArithmetic(
95189517 if (maybe_lhs_val) |lhs_val| {
95199518 if (maybe_rhs_val) |rhs_val| {
95209519 if (lhs_val.isUndef() or rhs_val.isUndef()) {
9521 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
9520 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
95229521 }
95239522
95249523 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);
9525 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
9526 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
9524 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9525 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9526 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
95279527 }
95289528 }
95299529 },
......@@ -9533,23 +9533,24 @@ fn zirOverflowArithmetic(
95339533 // Oterhwise if either of the arguments is undefined, both results are undefined.
95349534 if (maybe_lhs_val) |lhs_val| {
95359535 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
9536 break :result .{ .overflowed = .no, .wrapped = lhs };
9536 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
95379537 }
95389538 }
95399539 if (maybe_rhs_val) |rhs_val| {
95409540 if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {
9541 break :result .{ .overflowed = .no, .wrapped = lhs };
9541 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
95429542 }
95439543 }
95449544 if (maybe_lhs_val) |lhs_val| {
95459545 if (maybe_rhs_val) |rhs_val| {
95469546 if (lhs_val.isUndef() or rhs_val.isUndef()) {
9547 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
9547 break :result .{ .overflowed = try sema.addConstUndef(overflowed_ty), .wrapped = try sema.addConstUndef(dest_ty) };
95489548 }
95499549
95509550 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);
9551 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
9552 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
9551 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9552 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9553 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
95539554 }
95549555 }
95559556 },
......@@ -9577,21 +9578,40 @@ fn zirOverflowArithmetic(
95779578 } },
95789579 });
95799580
9580 const wrapped = try block.addStructFieldVal(tuple, 0, dest_ty);
9581 const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty);
95819582 try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store);
95829583
9583 const overflow_bit = try block.addStructFieldVal(tuple, 1, Type.initTag(.u1));
9584 const zero_u1 = try sema.addConstant(Type.initTag(.u1), Value.zero);
9585 return try block.addBinOp(.cmp_neq, overflow_bit, zero_u1);
9584 const overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty);
9585 const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero;
9586 const zero_ov = try sema.addConstant(ov_ty, zero_ov_val);
9587
9588 const overflowed_inst = if (dest_ty.zigTypeTag() == .Vector)
9589 block.addCmpVector(overflow_bit, .zero, .neq, try sema.addType(ov_ty))
9590 else
9591 block.addBinOp(.cmp_neq, overflow_bit, zero_ov);
9592 return overflowed_inst;
95869593 };
95879594
95889595 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
9596 return result.overflowed;
9597}
95899598
9590 return switch (result.overflowed) {
9591 .yes => Air.Inst.Ref.bool_true,
9592 .no => Air.Inst.Ref.bool_false,
9593 .undef => try sema.addConstUndef(Type.bool),
9594 };
9599fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
9600 const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.@"u1") else Type.@"u1";
9601
9602 const types = try sema.arena.alloc(Type, 2);
9603 const values = try sema.arena.alloc(Value, 2);
9604 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{
9605 .types = types,
9606 .values = values,
9607 });
9608
9609 types[0] = ty;
9610 types[1] = ov_ty;
9611 values[0] = Value.initTag(.unreachable_value);
9612 values[1] = Value.initTag(.unreachable_value);
9613
9614 return tuple_ty;
95959615}
95969616
95979617fn analyzeArithmetic(
......@@ -23093,6 +23113,14 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
2309323113 return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int));
2309423114}
2309523115
23116fn addBool(sema: *Sema, ty: Type, boolean: bool) CompileError!Air.Inst.Ref {
23117 return switch (ty.zigTypeTag()) {
23118 .Vector => sema.addConstant(ty, try Value.Tag.repeated.create(sema.arena, Value.makeBool(boolean))),
23119 .Bool => sema.resolveInst(if (boolean) .bool_true else .bool_false),
23120 else => unreachable,
23121 };
23122}
23123
2309623124fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
2309723125 return sema.addConstant(ty, Value.undef);
2309823126}