From 56845082bc6fb88a27c18cc3403216f93dc8ba42 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 30 Aug 2021 00:30:14 -0700 Subject: [PATCH 1/3] stage2/sema: clarify todo arithmetic operator error --- src/Sema.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 90505c68066550c3e624852f4c6dfff82bcb18d5..bab7a67c28b82ab7f431ea5bcbde29dd9eb3122d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5701,7 +5701,7 @@ fn analyzeArithmetic( try lhs_val.floatMul(rhs_val, scalar_type, sema.arena); break :blk val; }, - else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tag)}), + else => return sema.mod.fail(&block.base, src, "TODO implement comptime arithmetic for operand '{s}'", .{@tagName(zir_tag)}), }; log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value }); @@ -5722,7 +5722,7 @@ fn analyzeArithmetic( .mul => .mul, .mulwrap => .mulwrap, .div => .div, - else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}), + else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}'", .{@tagName(zir_tag)}), }; return block.addBinOp(air_tag, casted_lhs, casted_rhs); -- 2.54.0 From e878a6633f2447666217a5f9247af7c34507dca0 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 30 Aug 2021 00:31:40 -0700 Subject: [PATCH 2/3] stage2: implement runtime `%` and `@rem` --- src/Air.zig | 5 +++++ src/Liveness.zig | 1 + src/Sema.zig | 14 +++++++++++--- src/codegen.zig | 9 +++++++++ src/codegen/c.zig | 1 + src/codegen/llvm.zig | 14 ++++++++++++++ src/codegen/llvm/bindings.zig | 9 +++++++++ src/print_air.zig | 1 + 8 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 6e4125be444586676a0f2730199aedc8e71b20c0..60e5fdce1cbc86a492ddd40115220c0b380f5e6d 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -69,6 +69,10 @@ pub const Inst = struct { /// is the same as both operands. /// Uses the `bin_op` field. div, + /// Integer or float remainder. + /// Both operands are guaranteed to be the same type, and the result type is the same as both operands. + /// Uses the `bin_op` field. + rem, /// Add an offset to a pointer, returning a new pointer. /// The offset is in element type units, not bytes. /// Wrapping is undefined behavior. @@ -462,6 +466,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .mul, .mulwrap, .div, + .rem, .bit_and, .bit_or, .xor, diff --git a/src/Liveness.zig b/src/Liveness.zig index 6a47bfe59732961cb86f1a44136db2ea55dc8da1..2f1ecc9c4312ba3faacc6815362bbe92eb8aaa3d 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -231,6 +231,7 @@ fn analyzeInst( .mul, .mulwrap, .div, + .rem, .ptr_add, .ptr_sub, .bit_and, diff --git a/src/Sema.zig b/src/Sema.zig index bab7a67c28b82ab7f431ea5bcbde29dd9eb3122d..00c9029721738e212ee2a3940cf9247d861d9cdf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5714,6 +5714,14 @@ fn analyzeArithmetic( try sema.requireRuntimeBlock(block, lhs_src); } + if (zir_tag == .mod_rem) { + const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isFloat(); + const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isFloat(); + if (dirty_lhs or dirty_rhs) { + return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty }); + } + } + const air_tag: Air.Inst.Tag = switch (zir_tag) { .add => .add, .addwrap => .addwrap, @@ -5722,6 +5730,8 @@ fn analyzeArithmetic( .mul => .mul, .mulwrap => .mulwrap, .div => .div, + .mod_rem => .rem, + .rem => .rem, else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}'", .{@tagName(zir_tag)}), }; @@ -7184,9 +7194,7 @@ fn zirMod(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A } fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: Sema.zirRem", .{}); + return sema.zirArithmetic(block, inst); } fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/codegen.zig b/src/codegen.zig index 9103c7ad173fa993c82a87e14fa332b58edce711..a706b3b21c8b8541d828741f484caab208c6d76d 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -809,6 +809,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .mul => try self.airMul(inst), .mulwrap => try self.airMulWrap(inst), .div => try self.airDiv(inst), + .rem => try self.airRem(inst), .cmp_lt => try self.airCmp(inst, .lt), .cmp_lte => try self.airCmp(inst, .lte), @@ -1266,6 +1267,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } + fn airRem(self: *Self, inst: Air.Inst.Index) !void { + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { + else => return self.fail("TODO implement rem for {}", .{self.target.cpu.arch}), + }; + return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); + } + fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 2084b1e1ce7a272a9f0b0353ed25522899c555ef..fd964f28295580a60d73e0157c4cb9f959c01964 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -858,6 +858,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM // TODO use a different strategy for div that communicates to the optimizer // that wrapping is UB. .div => try airBinOp( o, inst, " / "), + .rem => try airBinOp( o, inst, " % "), .cmp_eq => try airBinOp(o, inst, " == "), .cmp_gt => try airBinOp(o, inst, " > "), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d7aa2d45b338828e4bf770bc152af5f787156bd6..5d20e380b43098b661bb093bd7d8ae370b010d3e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -985,6 +985,7 @@ pub const FuncGen = struct { .mul => try self.airMul(inst, false), .mulwrap => try self.airMul(inst, true), .div => try self.airDiv(inst), + .rem => try self.airRem(inst), .ptr_add => try self.airPtrAdd(inst), .ptr_sub => try self.airPtrSub(inst), @@ -1727,6 +1728,19 @@ pub const FuncGen = struct { return self.builder.buildUDiv(lhs, rhs, ""); } + fn airRem(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const inst_ty = self.air.typeOfIndex(inst); + + if (inst_ty.isFloat()) return self.builder.buildFRem(lhs, rhs, ""); + if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); + return self.builder.buildURem(lhs, rhs, ""); + } + fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index d33ca29d4f751f4af81e6d242d021015ba150c23..3c59fbe056ce747d4dc8b4aed064ca9f594132f4 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -386,6 +386,15 @@ pub const Builder = opaque { pub const buildFDiv = LLVMBuildFDiv; extern fn LLVMBuildFDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildURem = LLVMBuildURem; + extern fn LLVMBuildURem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildSRem = LLVMBuildSRem; + extern fn LLVMBuildSRem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildFRem = LLVMBuildFRem; + extern fn LLVMBuildFRem(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + pub const buildAnd = LLVMBuildAnd; extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; diff --git a/src/print_air.zig b/src/print_air.zig index 276158f720cae212e53e881957af5a303b64dee1..0106d0a0f31329a754124a5a119fcab17204e14f 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -109,6 +109,7 @@ const Writer = struct { .mul, .mulwrap, .div, + .rem, .ptr_add, .ptr_sub, .bit_and, -- 2.54.0 From cf9684ce75d4f9a4dc576d9c2cd490edcb8002df Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Mon, 30 Aug 2021 19:48:31 -0700 Subject: [PATCH 3/3] stage2: add `@rem` tests to llvm and c backends --- test/stage2/cbe.zig | 17 +++++++++++++++++ test/stage2/llvm.zig | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 21270f130b94db95669506c87eab33d68a30b559..12bc21b741cdfbd6038c29c75062ddc1a4d39ae6 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -916,6 +916,23 @@ pub fn addCases(ctx: *TestContext) !void { , ""); } + { + var case = ctx.exeFromCompiledC("@rem", linux_x64); + case.addCompareOutput( + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + \\fn rem(lhs: i32, rhs: i32, expected: i32) bool { + \\ return @rem(lhs, rhs) == expected; + \\} + \\pub export fn main() c_int { + \\ assert(rem(-5, 3, -2)); + \\ assert(rem(5, 3, 2)); + \\ return 0; + \\} + , ""); + } + ctx.h("simple header", linux_x64, \\export fn start() void{} , diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig index 09649f518c5ae757c206f3b3c03b5ebd432d2973..34f73b01c7a5ee56179e28a0b418203e3c5a07bb 100644 --- a/test/stage2/llvm.zig +++ b/test/stage2/llvm.zig @@ -225,4 +225,21 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + + { + var case = ctx.exeUsingLlvmBackend("@rem", linux_x64); + case.addCompareOutput( + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + \\fn rem(lhs: i32, rhs: i32, expected: i32) bool { + \\ return @rem(lhs, rhs) == expected; + \\} + \\pub export fn main() c_int { + \\ assert(rem(-5, 3, -2)); + \\ assert(rem(5, 3, 2)); + \\ return 0; + \\} + , ""); + } } -- 2.54.0