| author | |
| committer | |
| log | 3b9ec4e4df634b17268034a6a5527c11cf67e54b |
| tree | b2234a4d097f1586cf32540fa0ae967c33906874 |
| parent | d522f925b7f2f7f9d4782bb42eed95d5da4f3e0f |
| parent | cf9684ce75d4f9a4dc576d9c2cd490edcb8002df |
| signature |
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 | }; |
| 5824 | 5824 | ||
| 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 | } |
| 5834 | 5834 | ||
| 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 | }; |
| 5845 | 5855 | ||
| 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 | } |
| 7303 | 7313 | ||
| 7304 | fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 7314 | fn 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 | } |
| 7309 | 7317 | ||
| 7310 | fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 7318 | fn 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), | ||
| 812 | 813 | ||
| 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 | } |
| 1268 | 1269 | ||
| 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 optimizer | 858 | // 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, " % "), | ||
| 861 | 862 | ||
| 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), |
| 984 | 985 | ||
| ... | @@ -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 | } |
| 1723 | 1724 | ||
| 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; |
| 388 | 388 | ||
| 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; |
| 391 | 400 |
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 | } |
| 918 | 918 | ||
| 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 | } |