authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-21 01:38:46+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-21 01:47:27+01:00
loge106e18d96595bdc4bc037e0b36900992a576160
tree950e169ddf58e761fb7b90fa4580cc26ef045cbf
parent964dbeb82623515b8392c8c7cb9317246812174e

stage2: @shlWithOverflow


15 files changed, 203 insertions(+), 57 deletions(-)

src/Air.zig+7
......@@ -153,6 +153,12 @@ pub const Inst = struct {
153153 /// of the operation.
154154 /// Uses the `pl_op` field with payload `Bin`.
155155 mul_with_overflow,
156 /// Integer left-shift with overflow. Both operands are guaranteed to be the same type,
157 /// and the result is bool. The wrapped value is written to the pointer given by the in
158 /// operand of the `pl_op` field. Payload is `Bin` with `lhs` and `rhs` the relevant types
159 /// of the operation.
160 /// Uses the `pl_op` field with payload `Bin`.
161 shl_with_overflow,
156162 /// Allocates stack local memory.
157163 /// Uses the `ty` field.
158164 alloc,
......@@ -830,6 +836,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
830836 .add_with_overflow,
831837 .sub_with_overflow,
832838 .mul_with_overflow,
839 .shl_with_overflow,
833840 => return Type.initTag(.bool),
834841 }
835842}
src/Liveness.zig+2-1
......@@ -387,7 +387,8 @@ fn analyzeInst(
387387 .add_with_overflow,
388388 .sub_with_overflow,
389389 .mul_with_overflow,
390 => {
390 .shl_with_overflow,
391 => {
391392 const pl_op = inst_datas[inst].pl_op;
392393 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
393394 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
src/Sema.zig+39-8
......@@ -7425,8 +7425,32 @@ fn zirOverflowArithmetic(
74257425 }
74267426 }
74277427 },
7428 .shl_with_overflow,
7429 => return sema.fail(block, src, "TODO implement Sema.zirOverflowArithmetic for {}", .{zir_tag}),
7428 .shl_with_overflow => {
7429 // If lhs is zero, the result is zero and no overflow occurred.
7430 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred.
7431 // Oterhwise if either of the arguments is undefined, both results are undefined.
7432 if (maybe_lhs_val) |lhs_val| {
7433 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
7434 break :result .{ .overflowed = .no, .wrapped = lhs };
7435 }
7436 }
7437 if (maybe_rhs_val) |rhs_val| {
7438 if (!rhs_val.isUndef() and rhs_val.compareWithZero(.eq)) {
7439 break :result .{ .overflowed = .no, .wrapped = lhs };
7440 }
7441 }
7442 if (maybe_lhs_val) |lhs_val| {
7443 if (maybe_rhs_val) |rhs_val| {
7444 if (lhs_val.isUndef() or rhs_val.isUndef()) {
7445 break :result .{ .overflowed = .undef, .wrapped = try sema.addConstUndef(dest_ty) };
7446 }
7447
7448 const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, target);
7449 const inst = try sema.addConstant(dest_ty, result.wrapped_result);
7450 break :result .{ .overflowed = if (result.overflowed) .yes else .no, .wrapped = inst };
7451 }
7452 }
7453 },
74307454 else => unreachable,
74317455 }
74327456
......@@ -7434,7 +7458,8 @@ fn zirOverflowArithmetic(
74347458 .add_with_overflow => .add_with_overflow,
74357459 .mul_with_overflow => .mul_with_overflow,
74367460 .sub_with_overflow => .sub_with_overflow,
7437 else => return sema.fail(block, src, "TODO implement runtime Sema.zirOverflowArithmetic for {}", .{zir_tag}),
7461 .shl_with_overflow => .shl_with_overflow,
7462 else => unreachable,
74387463 };
74397464
74407465 try sema.requireRuntimeBlock(block, src);
......@@ -9041,11 +9066,17 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi
90419066 switch (operand.zigTypeTag()) {
90429067 .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
90439068 .Int => {
9044 var count: u16 = 0;
9045 var s = operand.bitSize(sema.mod.getTarget()) - 1;
9046 while (s != 0) : (s >>= 1) {
9047 count += 1;
9048 }
9069 const bits = operand.bitSize(sema.mod.getTarget());
9070 const count = if (bits == 0)
9071 0
9072 else blk: {
9073 var count: u16 = 0;
9074 var s = bits - 1;
9075 while (s != 0) : (s >>= 1) {
9076 count += 1;
9077 }
9078 break :blk count;
9079 };
90499080 const res = try Module.makeIntType(sema.arena, .unsigned, count);
90509081 return sema.addType(res);
90519082 },
src/arch/aarch64/CodeGen.zig+9-3
......@@ -524,6 +524,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
524524 .add_with_overflow => try self.airAddWithOverflow(inst),
525525 .sub_with_overflow => try self.airSubWithOverflow(inst),
526526 .mul_with_overflow => try self.airMulWithOverflow(inst),
527 .shl_with_overflow => try self.airShlWithOverflow(inst),
527528
528529 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
529530
......@@ -975,17 +976,22 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
975976
976977fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
977978 _ = inst;
978 return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
979 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
979980}
980981
981982fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
982983 _ = inst;
983 return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch});
984 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
984985}
985986
986987fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
987988 _ = inst;
988 return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
989 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
990}
991
992fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
993 _ = inst;
994 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
989995}
990996
991997fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
src/arch/arm/CodeGen.zig+9-3
......@@ -522,6 +522,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
522522 .add_with_overflow => try self.airAddWithOverflow(inst),
523523 .sub_with_overflow => try self.airSubWithOverflow(inst),
524524 .mul_with_overflow => try self.airMulWithOverflow(inst),
525 .shl_with_overflow => try self.airShlWithOverflow(inst),
525526
526527 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
527528
......@@ -1005,17 +1006,22 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
10051006
10061007fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
10071008 _ = inst;
1008 return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
1009 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
10091010}
10101011
10111012fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
10121013 _ = inst;
1013 return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch});
1014 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
10141015}
10151016
10161017fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
10171018 _ = inst;
1018 return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
1019 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
1020}
1021
1022fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1023 _ = inst;
1024 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
10191025}
10201026
10211027fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
src/arch/riscv64/CodeGen.zig+9-3
......@@ -503,6 +503,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
503503 .add_with_overflow => try self.airAddWithOverflow(inst),
504504 .sub_with_overflow => try self.airSubWithOverflow(inst),
505505 .mul_with_overflow => try self.airMulWithOverflow(inst),
506 .shl_with_overflow => try self.airShlWithOverflow(inst),
506507
507508 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
508509
......@@ -920,17 +921,22 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
920921
921922fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
922923 _ = inst;
923 return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
924 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
924925}
925926
926927fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
927928 _ = inst;
928 return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch});
929 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
929930}
930931
931932fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
932933 _ = inst;
933 return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
934 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
935}
936
937fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
938 _ = inst;
939 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
934940}
935941
936942fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
src/arch/x86_64/CodeGen.zig+9-3
......@@ -556,6 +556,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
556556 .add_with_overflow => try self.airAddWithOverflow(inst),
557557 .sub_with_overflow => try self.airSubWithOverflow(inst),
558558 .mul_with_overflow => try self.airMulWithOverflow(inst),
559 .shl_with_overflow => try self.airShlWithOverflow(inst),
559560
560561 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
561562
......@@ -1034,17 +1035,22 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
10341035
10351036fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
10361037 _ = inst;
1037 return self.fail("TODO implement airAddResultWithOverflow for {}", .{self.target.cpu.arch});
1038 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch});
10381039}
10391040
10401041fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
10411042 _ = inst;
1042 return self.fail("TODO implement airSubResultWithOverflow for {}", .{self.target.cpu.arch});
1043 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch});
10431044}
10441045
10451046fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
10461047 _ = inst;
1047 return self.fail("TODO implement airMulResultWithOverflow for {}", .{self.target.cpu.arch});
1048 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});
1049}
1050
1051fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1052 _ = inst;
1053 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
10481054}
10491055
10501056fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
src/codegen/c.zig+7
......@@ -1159,6 +1159,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
11591159 .add_with_overflow => try airAddWithOverflow(f, inst),
11601160 .sub_with_overflow => try airSubWithOverflow(f, inst),
11611161 .mul_with_overflow => try airMulWithOverflow(f, inst),
1162 .shl_with_overflow => try airShlWithOverflow(f, inst),
11621163
11631164 .min => try airMinMax(f, inst, "<"),
11641165 .max => try airMinMax(f, inst, ">"),
......@@ -1887,6 +1888,12 @@ fn airMulWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
18871888 return f.fail("TODO mul with overflow", .{});
18881889}
18891890
1891fn airShlWithOverflow(f: *Function, inst: Air.Inst.Index) !CValue {
1892 _ = f;
1893 _ = inst;
1894 return f.fail("TODO shl with overflow", .{});
1895}
1896
18901897fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
18911898 if (f.liveness.isUnused(inst))
18921899 return CValue.none;
src/codegen/llvm.zig+36
......@@ -1721,6 +1721,7 @@ pub const FuncGen = struct {
17211721 .add_with_overflow => try self.airOverflow(inst, "llvm.sadd.with.overflow", "llvm.uadd.with.overflow"),
17221722 .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"),
17231723 .mul_with_overflow => try self.airOverflow(inst, "llvm.smul.with.overflow", "llvm.umul.with.overflow"),
1724 .shl_with_overflow => try self.airShlWithOverflow(inst),
17241725
17251726 .bit_and, .bool_and => try self.airAnd(inst),
17261727 .bit_or, .bool_or => try self.airOr(inst),
......@@ -3176,6 +3177,41 @@ pub const FuncGen = struct {
31763177 return overflow_bit;
31773178 }
31783179
3180 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3181 if (self.liveness.isUnused(inst))
3182 return null;
3183
3184 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3185 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
3186
3187 const ptr = try self.resolveInst(pl_op.operand);
3188 const lhs = try self.resolveInst(extra.lhs);
3189 const rhs = try self.resolveInst(extra.rhs);
3190
3191 const ptr_ty = self.air.typeOf(pl_op.operand);
3192 const lhs_ty = self.air.typeOf(extra.lhs);
3193 const rhs_ty = self.air.typeOf(extra.rhs);
3194
3195 const tg = self.dg.module.getTarget();
3196
3197 const casted_rhs = if (rhs_ty.bitSize(tg) < lhs_ty.bitSize(tg))
3198 self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "")
3199 else
3200 rhs;
3201
3202 const result = self.builder.buildShl(lhs, casted_rhs, "");
3203 const reconstructed = if (lhs_ty.isSignedInt())
3204 self.builder.buildAShr(result, casted_rhs, "")
3205 else
3206 self.builder.buildLShr(result, casted_rhs, "");
3207
3208 const overflow_bit = self.builder.buildICmp(.NE, lhs, reconstructed, "");
3209
3210 self.store(ptr, ptr_ty, result, .NotAtomic);
3211
3212 return overflow_bit;
3213 }
3214
31793215 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
31803216 if (self.liveness.isUnused(inst))
31813217 return null;
src/print_air.zig+1
......@@ -233,6 +233,7 @@ const Writer = struct {
233233 .add_with_overflow,
234234 .sub_with_overflow,
235235 .mul_with_overflow,
236 .shl_with_overflow,
236237 => try w.writeOverflow(s, inst),
237238 }
238239 }
src/value.zig+31
......@@ -2548,6 +2548,37 @@ pub const Value = extern union {
25482548 return fromBigInt(allocator, result_bigint.toConst());
25492549 }
25502550
2551 pub fn shlWithOverflow(
2552 lhs: Value,
2553 rhs: Value,
2554 ty: Type,
2555 allocator: Allocator,
2556 target: Target,
2557 ) !OverflowArithmeticResult {
2558 const info = ty.intInfo(target);
2559 var lhs_space: Value.BigIntSpace = undefined;
2560 const lhs_bigint = lhs.toBigInt(&lhs_space);
2561 const shift = @intCast(usize, rhs.toUnsignedInt());
2562 const limbs = try allocator.alloc(
2563 std.math.big.Limb,
2564 lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
2565 );
2566 var result_bigint = BigIntMutable{
2567 .limbs = limbs,
2568 .positive = undefined,
2569 .len = undefined,
2570 };
2571 result_bigint.shiftLeft(lhs_bigint, shift);
2572 const overflowed = !result_bigint.toConst().fitsInTwosComp(info.signedness, info.bits);
2573 if (overflowed) {
2574 result_bigint.truncate(result_bigint.toConst(), info.signedness, info.bits);
2575 }
2576 return OverflowArithmeticResult{
2577 .overflowed = overflowed,
2578 .wrapped_result = try fromBigInt(allocator, result_bigint.toConst()),
2579 };
2580 }
2581
25512582 pub fn shlSat(
25522583 lhs: Value,
25532584 rhs: Value,
test/behavior/eval.zig+16
......@@ -451,3 +451,19 @@ test "comptime bitwise operators" {
451451 try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff);
452452 }
453453}
454
455test "comptime shlWithOverflow" {
456 const ct_shifted: u64 = comptime amt: {
457 var amt = @as(u64, 0);
458 _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
459 break :amt amt;
460 };
461
462 const rt_shifted: u64 = amt: {
463 var amt = @as(u64, 0);
464 _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
465 break :amt amt;
466 };
467
468 try expect(ct_shifted == rt_shifted);
469}
test/behavior/eval_stage1.zig-16
......@@ -162,22 +162,6 @@ test "const ptr to comptime mutable data is not memoized" {
162162 }
163163}
164164
165test "comptime shlWithOverflow" {
166 const ct_shifted: u64 = comptime amt: {
167 var amt = @as(u64, 0);
168 _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
169 break :amt amt;
170 };
171
172 const rt_shifted: u64 = amt: {
173 var amt = @as(u64, 0);
174 _ = @shlWithOverflow(u64, ~@as(u64, 0), 16, &amt);
175 break :amt amt;
176 };
177
178 try expect(ct_shifted == rt_shifted);
179}
180
181165test "runtime 128 bit integer division" {
182166 var a: u128 = 152313999999999991610955792383;
183167 var b: u128 = 10000000000000000000;
test/behavior/math.zig+28
......@@ -511,3 +511,31 @@ test "@subWithOverflow" {
511511 try expect(!@subWithOverflow(u8, a, b, &result));
512512 try expect(result == 0);
513513}
514
515test "@shlWithOverflow" {
516 var result: u16 = undefined;
517 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
518 try expect(result == 0b0111111111111000);
519 try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
520 try expect(result == 0b1011111111111100);
521
522 var a: u16 = 0b0000_0000_0000_0011;
523 var b: u4 = 15;
524 try expect(@shlWithOverflow(u16, a, b, &result));
525 try expect(result == 0b1000_0000_0000_0000);
526 b = 14;
527 try expect(!@shlWithOverflow(u16, a, b, &result));
528 try expect(result == 0b1100_0000_0000_0000);
529}
530
531test "overflow arithmetic with u0 values" {
532 var result: u0 = undefined;
533 try expect(!@addWithOverflow(u0, 0, 0, &result));
534 try expect(result == 0);
535 try expect(!@subWithOverflow(u0, 0, 0, &result));
536 try expect(result == 0);
537 try expect(!@mulWithOverflow(u0, 0, 0, &result));
538 try expect(result == 0);
539 try expect(!@shlWithOverflow(u0, 0, 0, &result));
540 try expect(result == 0);
541}
test/behavior/math_stage1.zig-20
......@@ -6,26 +6,6 @@ const maxInt = std.math.maxInt;
66const minInt = std.math.minInt;
77const mem = std.mem;
88
9test "@shlWithOverflow" {
10 var result: u16 = undefined;
11 try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
12 try expect(result == 0b0111111111111000);
13 try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
14 try expect(result == 0b1011111111111100);
15}
16
17test "overflow arithmetic with u0 values" {
18 var result: u0 = undefined;
19 try expect(!@addWithOverflow(u0, 0, 0, &result));
20 try expect(result == 0);
21 try expect(!@subWithOverflow(u0, 0, 0, &result));
22 try expect(result == 0);
23 try expect(!@mulWithOverflow(u0, 0, 0, &result));
24 try expect(result == 0);
25 try expect(!@shlWithOverflow(u0, 0, 0, &result));
26 try expect(result == 0);
27}
28
299test "@clz vectors" {
3010 try testClzVectors();
3111 comptime try testClzVectors();