authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-31 21:59:48-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-08-31 21:59:48-04:00
log3b9ec4e4df634b17268034a6a5527c11cf67e54b
treeb2234a4d097f1586cf32540fa0ae967c33906874
parentd522f925b7f2f7f9d4782bb42eed95d5da4f3e0f
parentcf9684ce75d4f9a4dc576d9c2cd490edcb8002df
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9655 from nektro/stage2-rem

stage2: implement runtime `%` and `@rem`

10 files changed, 87 insertions(+), 5 deletions(-)

src/Air.zig+5
...@@ -69,6 +69,10 @@ pub const Inst = struct {...@@ -69,6 +69,10 @@ pub const Inst = struct {
69 /// is the same as both operands.69 /// is the same as both operands.
70 /// Uses the `bin_op` field.70 /// Uses the `bin_op` field.
71 div,71 div,
72 /// Integer or float remainder.
73 /// Both operands are guaranteed to be the same type, and the result type is the same as both operands.
74 /// Uses the `bin_op` field.
75 rem,
72 /// Add an offset to a pointer, returning a new pointer.76 /// Add an offset to a pointer, returning a new pointer.
73 /// The offset is in element type units, not bytes.77 /// The offset is in element type units, not bytes.
74 /// Wrapping is undefined behavior.78 /// Wrapping is undefined behavior.
...@@ -462,6 +466,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -462,6 +466,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
462 .mul,466 .mul,
463 .mulwrap,467 .mulwrap,
464 .div,468 .div,
469 .rem,
465 .bit_and,470 .bit_and,
466 .bit_or,471 .bit_or,
467 .xor,472 .xor,
src/Liveness.zig+1
...@@ -231,6 +231,7 @@ fn analyzeInst(...@@ -231,6 +231,7 @@ fn analyzeInst(
231 .mul,231 .mul,
232 .mulwrap,232 .mulwrap,
233 .div,233 .div,
234 .rem,
234 .ptr_add,235 .ptr_add,
235 .ptr_sub,236 .ptr_sub,
236 .bit_and,237 .bit_and,
src/Sema.zig+13-5
...@@ -5819,7 +5819,7 @@ fn analyzeArithmetic(...@@ -5819,7 +5819,7 @@ fn analyzeArithmetic(
5819 try lhs_val.floatMul(rhs_val, scalar_type, sema.arena);5819 try lhs_val.floatMul(rhs_val, scalar_type, sema.arena);
5820 break :blk val;5820 break :blk val;
5821 },5821 },
5822 else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}),5822 else => return sema.mod.fail(&block.base, src, "TODO implement comptime arithmetic for operand '{s}'", .{@tagName(zir_tag)}),
5823 };5823 };
58245824
5825 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });5825 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
...@@ -5832,6 +5832,14 @@ fn analyzeArithmetic(...@@ -5832,6 +5832,14 @@ fn analyzeArithmetic(
5832 try sema.requireRuntimeBlock(block, lhs_src);5832 try sema.requireRuntimeBlock(block, lhs_src);
5833 }5833 }
58345834
5835 if (zir_tag == .mod_rem) {
5836 const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat();
5837 const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat();
5838 if (dirty_lhs or dirty_rhs) {
5839 return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
5840 }
5841 }
5842
5835 const air_tag: Air.Inst.Tag = switch (zir_tag) {5843 const air_tag: Air.Inst.Tag = switch (zir_tag) {
5836 .add => .add,5844 .add => .add,
5837 .addwrap => .addwrap,5845 .addwrap => .addwrap,
...@@ -5840,7 +5848,9 @@ fn analyzeArithmetic(...@@ -5840,7 +5848,9 @@ fn analyzeArithmetic(
5840 .mul => .mul,5848 .mul => .mul,
5841 .mulwrap => .mulwrap,5849 .mulwrap => .mulwrap,
5842 .div => .div,5850 .div => .div,
5843 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}),5851 .mod_rem => .rem,
5852 .rem => .rem,
5853 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}'", .{@tagName(zir_tag)}),
5844 };5854 };
58455855
5846 return block.addBinOp(air_tag, casted_lhs, casted_rhs);5856 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
...@@ -7302,9 +7312,7 @@ fn zirMod(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A...@@ -7302,9 +7312,7 @@ fn zirMod(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
7302}7312}
73037313
7304fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7314fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7305 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;7315 return sema.zirArithmetic(block, inst);
7306 const src = inst_data.src();
7307 return sema.mod.fail(&block.base, src, "TODO: Sema.zirRem", .{});
7308}7316}
73097317
7310fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {7318fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/codegen.zig+9
...@@ -809,6 +809,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -809,6 +809,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
809 .mul => try self.airMul(inst),809 .mul => try self.airMul(inst),
810 .mulwrap => try self.airMulWrap(inst),810 .mulwrap => try self.airMulWrap(inst),
811 .div => try self.airDiv(inst),811 .div => try self.airDiv(inst),
812 .rem => try self.airRem(inst),
812813
813 .cmp_lt => try self.airCmp(inst, .lt),814 .cmp_lt => try self.airCmp(inst, .lt),
814 .cmp_lte => try self.airCmp(inst, .lte),815 .cmp_lte => try self.airCmp(inst, .lte),
...@@ -1266,6 +1267,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1266,6 +1267,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1266 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1267 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1267 }1268 }
12681269
1270 fn airRem(self: *Self, inst: Air.Inst.Index) !void {
1271 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1272 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1273 else => return self.fail("TODO implement rem for {}", .{self.target.cpu.arch}),
1274 };
1275 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1276 }
1277
1269 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {1278 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
1270 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1279 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1271 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1280 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
src/codegen/c.zig+1
...@@ -858,6 +858,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM...@@ -858,6 +858,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
858 // TODO use a different strategy for div that communicates to the optimizer858 // TODO use a different strategy for div that communicates to the optimizer
859 // that wrapping is UB.859 // that wrapping is UB.
860 .div => try airBinOp( o, inst, " / "),860 .div => try airBinOp( o, inst, " / "),
861 .rem => try airBinOp( o, inst, " % "),
861862
862 .cmp_eq => try airBinOp(o, inst, " == "),863 .cmp_eq => try airBinOp(o, inst, " == "),
863 .cmp_gt => try airBinOp(o, inst, " > "),864 .cmp_gt => try airBinOp(o, inst, " > "),
src/codegen/llvm.zig+14
...@@ -979,6 +979,7 @@ pub const FuncGen = struct {...@@ -979,6 +979,7 @@ pub const FuncGen = struct {
979 .mul => try self.airMul(inst, false),979 .mul => try self.airMul(inst, false),
980 .mulwrap => try self.airMul(inst, true),980 .mulwrap => try self.airMul(inst, true),
981 .div => try self.airDiv(inst),981 .div => try self.airDiv(inst),
982 .rem => try self.airRem(inst),
982 .ptr_add => try self.airPtrAdd(inst),983 .ptr_add => try self.airPtrAdd(inst),
983 .ptr_sub => try self.airPtrSub(inst),984 .ptr_sub => try self.airPtrSub(inst),
984985
...@@ -1721,6 +1722,19 @@ pub const FuncGen = struct {...@@ -1721,6 +1722,19 @@ pub const FuncGen = struct {
1721 return self.builder.buildUDiv(lhs, rhs, "");1722 return self.builder.buildUDiv(lhs, rhs, "");
1722 }1723 }
17231724
1725 fn airRem(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1726 if (self.liveness.isUnused(inst)) return null;
1727
1728 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1729 const lhs = try self.resolveInst(bin_op.lhs);
1730 const rhs = try self.resolveInst(bin_op.rhs);
1731 const inst_ty = self.air.typeOfIndex(inst);
1732
1733 if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, "");
1734 if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
1735 return self.builder.buildURem(lhs, rhs, "");
1736 }
1737
1724 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1738 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
1725 if (self.liveness.isUnused(inst))1739 if (self.liveness.isUnused(inst))
1726 return null;1740 return null;
src/codegen/llvm/bindings.zig+9
...@@ -386,6 +386,15 @@ pub const Builder = opaque {...@@ -386,6 +386,15 @@ pub const Builder = opaque {
386 pub const buildFDiv = LLVMBuildFDiv;386 pub const buildFDiv = LLVMBuildFDiv;
387 extern fn LLVMBuildFDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;387 extern fn LLVMBuildFDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
388388
389 pub const buildURem = LLVMBuildURem;
390 extern fn LLVMBuildURem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
391
392 pub const buildSRem = LLVMBuildSRem;
393 extern fn LLVMBuildSRem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
394
395 pub const buildFRem = LLVMBuildFRem;
396 extern fn LLVMBuildFRem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
397
389 pub const buildAnd = LLVMBuildAnd;398 pub const buildAnd = LLVMBuildAnd;
390 extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;399 extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
391400
src/print_air.zig+1
...@@ -109,6 +109,7 @@ const Writer = struct {...@@ -109,6 +109,7 @@ const Writer = struct {
109 .mul,109 .mul,
110 .mulwrap,110 .mulwrap,
111 .div,111 .div,
112 .rem,
112 .ptr_add,113 .ptr_add,
113 .ptr_sub,114 .ptr_sub,
114 .bit_and,115 .bit_and,
test/stage2/cbe.zig+17
...@@ -916,6 +916,23 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -916,6 +916,23 @@ pub fn addCases(ctx: *TestContext) !void {
916 , "");916 , "");
917 }917 }
918918
919 {
920 var case = ctx.exeFromCompiledC("@rem", linux_x64);
921 case.addCompareOutput(
922 \\fn assert(ok: bool) void {
923 \\ if (!ok) unreachable;
924 \\}
925 \\fn rem(lhs: i32, rhs: i32, expected: i32) bool {
926 \\ return @rem(lhs, rhs) == expected;
927 \\}
928 \\pub export fn main() c_int {
929 \\ assert(rem(-5, 3, -2));
930 \\ assert(rem(5, 3, 2));
931 \\ return 0;
932 \\}
933 , "");
934 }
935
919 ctx.h("simple header", linux_x64,936 ctx.h("simple header", linux_x64,
920 \\export fn start() void{}937 \\export fn start() void{}
921 ,938 ,
test/stage2/llvm.zig+17
...@@ -225,4 +225,21 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -225,4 +225,21 @@ pub fn addCases(ctx: *TestContext) !void {
225 \\}225 \\}
226 , "");226 , "");
227 }227 }
228
229 {
230 var case = ctx.exeUsingLlvmBackend("@rem", linux_x64);
231 case.addCompareOutput(
232 \\fn assert(ok: bool) void {
233 \\ if (!ok) unreachable;
234 \\}
235 \\fn rem(lhs: i32, rhs: i32, expected: i32) bool {
236 \\ return @rem(lhs, rhs) == expected;
237 \\}
238 \\pub export fn main() c_int {
239 \\ assert(rem(-5, 3, -2));
240 \\ assert(rem(5, 3, 2));
241 \\ return 0;
242 \\}
243 , "");
244 }
228}245}