Commit e878a6633f

Meghan Denny <hello@nektro.net>
2021-08-30 09:31:40
stage2: implement runtime `%` and `@rem`
1 parent 5684508
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;
 
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, " > "),
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;
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,
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) {
src/Liveness.zig
@@ -231,6 +231,7 @@ fn analyzeInst(
         .mul,
         .mulwrap,
         .div,
+        .rem,
         .ptr_add,
         .ptr_sub,
         .bit_and,
src/print_air.zig
@@ -109,6 +109,7 @@ const Writer = struct {
             .mul,
             .mulwrap,
             .div,
+            .rem,
             .ptr_add,
             .ptr_sub,
             .bit_and,
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 {