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(...@@ -9417,32 +9417,29 @@ fn zirOverflowArithmetic(
9417 const ptr = sema.resolveInst(extra.ptr);9417 const ptr = sema.resolveInst(extra.ptr);
94189418
9419 const lhs_ty = sema.typeOf(lhs);9419 const lhs_ty = sema.typeOf(lhs);
9420 const rhs_ty = sema.typeOf(rhs);
9420 const mod = sema.mod;9421 const mod = sema.mod;
9421 const target = mod.getTarget();9422 const target = mod.getTarget();
94229423
9423 // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen.9424 // 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);
9424 const dest_ty = lhs_ty;9426 const dest_ty = lhs_ty;
9425 if (dest_ty.zigTypeTag() != .Int) {9427 if (dest_ty.scalarType().zigTypeTag() != .Int) {
9426 return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(mod)});9428 return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)});
9427 }9429 }
94289430
9429 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);9431 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
9430 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);9432 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
94319433
9432 const types = try sema.arena.alloc(Type, 2);9434 const tuple_ty = try sema.overflowArithmeticTupleType(dest_ty);
9433 const values = try sema.arena.alloc(Value, 2);9435 const ov_ty = tuple_ty.tupleFields().types[1];
9434 const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{9436 // TODO: Remove and use `ov_ty` instead.
9435 .types = types,9437 // This is a temporary type used until overflow arithmetic properly returns `u1` instead of `bool`.
9436 .values = values,9438 const overflowed_ty = if (dest_ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, dest_ty.vectorLen(), Type.@"bool") else Type.@"bool";
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);
94439439
9444 const result: struct {9440 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,
9446 wrapped: Air.Inst.Ref,9443 wrapped: Air.Inst.Ref,
9447 } = result: {9444 } = result: {
9448 switch (zir_tag) {9445 switch (zir_tag) {
...@@ -9452,23 +9449,24 @@ fn zirOverflowArithmetic(...@@ -9452,23 +9449,24 @@ fn zirOverflowArithmetic(
9452 // Otherwise, if either of the argument is undefined, undefined is returned.9449 // Otherwise, if either of the argument is undefined, undefined is returned.
9453 if (maybe_lhs_val) |lhs_val| {9450 if (maybe_lhs_val) |lhs_val| {
9454 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {9451 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 };
9456 }9453 }
9457 }9454 }
9458 if (maybe_rhs_val) |rhs_val| {9455 if (maybe_rhs_val) |rhs_val| {
9459 if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {9456 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 };
9461 }9458 }
9462 }9459 }
9463 if (maybe_lhs_val) |lhs_val| {9460 if (maybe_lhs_val) |lhs_val| {
9464 if (maybe_rhs_val) |rhs_val| {9461 if (maybe_rhs_val) |rhs_val| {
9465 if (lhs_val.isUndef() or rhs_val.isUndef()) {9462 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) };
9467 }9464 }
94689465
9469 const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target);9466 const result = try lhs_val.intAddWithOverflow(rhs_val, dest_ty, sema.arena, target);
9470 const inst = try sema.addConstant(dest_ty, result.wrapped_result);9467 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9471 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };9468 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9469 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
9472 }9470 }
9473 }9471 }
9474 },9472 },
...@@ -9477,17 +9475,18 @@ fn zirOverflowArithmetic(...@@ -9477,17 +9475,18 @@ fn zirOverflowArithmetic(
9477 // Otherwise, if either result is undefined, both results are undefined.9475 // Otherwise, if either result is undefined, both results are undefined.
9478 if (maybe_rhs_val) |rhs_val| {9476 if (maybe_rhs_val) |rhs_val| {
9479 if (rhs_val.isUndef()) {9477 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) };
9481 } else if (rhs_val.compareWithZero(.eq)) {9479 } 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 };
9483 } else if (maybe_lhs_val) |lhs_val| {9481 } else if (maybe_lhs_val) |lhs_val| {
9484 if (lhs_val.isUndef()) {9482 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) };
9486 }9484 }
94879485
9488 const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target);9486 const result = try lhs_val.intSubWithOverflow(rhs_val, dest_ty, sema.arena, target);
9489 const inst = try sema.addConstant(dest_ty, result.wrapped_result);9487 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9490 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };9488 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9489 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
9491 }9490 }
9492 }9491 }
9493 },9492 },
...@@ -9498,9 +9497,9 @@ fn zirOverflowArithmetic(...@@ -9498,9 +9497,9 @@ fn zirOverflowArithmetic(
9498 if (maybe_lhs_val) |lhs_val| {9497 if (maybe_lhs_val) |lhs_val| {
9499 if (!lhs_val.isUndef()) {9498 if (!lhs_val.isUndef()) {
9500 if (lhs_val.compareWithZero(.eq)) {9499 if (lhs_val.compareWithZero(.eq)) {
9501 break :result .{ .overflowed = .no, .wrapped = lhs };9500 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = lhs };
9502 } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) {9501 } 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 };
9504 }9503 }
9505 }9504 }
9506 }9505 }
...@@ -9508,9 +9507,9 @@ fn zirOverflowArithmetic(...@@ -9508,9 +9507,9 @@ fn zirOverflowArithmetic(
9508 if (maybe_rhs_val) |rhs_val| {9507 if (maybe_rhs_val) |rhs_val| {
9509 if (!rhs_val.isUndef()) {9508 if (!rhs_val.isUndef()) {
9510 if (rhs_val.compareWithZero(.eq)) {9509 if (rhs_val.compareWithZero(.eq)) {
9511 break :result .{ .overflowed = .no, .wrapped = rhs };9510 break :result .{ .overflowed = try sema.addBool(overflowed_ty, false), .wrapped = rhs };
9512 } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) {9511 } 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 };
9514 }9513 }
9515 }9514 }
9516 }9515 }
...@@ -9518,12 +9517,13 @@ fn zirOverflowArithmetic(...@@ -9518,12 +9517,13 @@ fn zirOverflowArithmetic(
9518 if (maybe_lhs_val) |lhs_val| {9517 if (maybe_lhs_val) |lhs_val| {
9519 if (maybe_rhs_val) |rhs_val| {9518 if (maybe_rhs_val) |rhs_val| {
9520 if (lhs_val.isUndef() or rhs_val.isUndef()) {9519 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) };
9522 }9521 }
95239522
9524 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);9523 const result = try lhs_val.intMulWithOverflow(rhs_val, dest_ty, sema.arena, target);
9525 const inst = try sema.addConstant(dest_ty, result.wrapped_result);9524 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9526 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };9525 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9526 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
9527 }9527 }
9528 }9528 }
9529 },9529 },
...@@ -9533,23 +9533,24 @@ fn zirOverflowArithmetic(...@@ -9533,23 +9533,24 @@ fn zirOverflowArithmetic(
9533 // Oterhwise if either of the arguments is undefined, both results are undefined.9533 // Oterhwise if either of the arguments is undefined, both results are undefined.
9534 if (maybe_lhs_val) |lhs_val| {9534 if (maybe_lhs_val) |lhs_val| {
9535 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {9535 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 };
9537 }9537 }
9538 }9538 }
9539 if (maybe_rhs_val) |rhs_val| {9539 if (maybe_rhs_val) |rhs_val| {
9540 if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {9540 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 };
9542 }9542 }
9543 }9543 }
9544 if (maybe_lhs_val) |lhs_val| {9544 if (maybe_lhs_val) |lhs_val| {
9545 if (maybe_rhs_val) |rhs_val| {9545 if (maybe_rhs_val) |rhs_val| {
9546 if (lhs_val.isUndef() or rhs_val.isUndef()) {9546 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) };
9548 }9548 }
95499549
9550 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);9550 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);
9551 const inst = try sema.addConstant(dest_ty, result.wrapped_result);9551 const overflowed = try sema.addConstant(overflowed_ty, result.overflowed);
9552 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };9552 const wrapped = try sema.addConstant(dest_ty, result.wrapped_result);
9553 break :result .{ .overflowed = overflowed, .wrapped = wrapped };
9553 }9554 }
9554 }9555 }
9555 },9556 },
...@@ -9577,21 +9578,40 @@ fn zirOverflowArithmetic(...@@ -9577,21 +9578,40 @@ fn zirOverflowArithmetic(
9577 } },9578 } },
9578 });9579 });
95799580
9580 const wrapped = try block.addStructFieldVal(tuple, 0, dest_ty);9581 const wrapped = try sema.tupleFieldValByIndex(block, src, tuple, 0, tuple_ty);
9581 try sema.storePtr2(block, src, ptr, ptr_src, wrapped, src, .store);9582 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 overflow_bit = try sema.tupleFieldValByIndex(block, src, tuple, 1, tuple_ty);
9584 const zero_u1 = try sema.addConstant(Type.initTag(.u1), Value.zero);9585 const zero_ov_val = if (dest_ty.zigTypeTag() == .Vector) try Value.Tag.repeated.create(sema.arena, Value.zero) else Value.zero;
9585 return try block.addBinOp(.cmp_neq, overflow_bit, zero_u1);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;
9586 };9593 };
95879594
9588 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);9595 try sema.storePtr2(block, src, ptr, ptr_src, result.wrapped, src, .store);
9596 return result.overflowed;
9597}
95899598
9590 return switch (result.overflowed) {9599fn overflowArithmeticTupleType(sema: *Sema, ty: Type) !Type {
9591 .yes => Air.Inst.Ref.bool_true,9600 const ov_ty = if (ty.zigTypeTag() == .Vector) try Type.vector(sema.arena, ty.vectorLen(), Type.@"u1") else Type.@"u1";
9592 .no => Air.Inst.Ref.bool_false,9601
9593 .undef => try sema.addConstUndef(Type.bool),9602 const types = try sema.arena.alloc(Type, 2);
9594 };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;
9595}9615}
95969616
9597fn analyzeArithmetic(9617fn analyzeArithmetic(
...@@ -23093,6 +23113,14 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {...@@ -23093,6 +23113,14 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
23093 return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int));23113 return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int));
23094}23114}
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
23096fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {23124fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
23097 return sema.addConstant(ty, Value.undef);23125 return sema.addConstant(ty, Value.undef);
23098}23126}