| 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 | 69 | /// is the same as both operands. |
| 70 | 70 | /// Uses the `bin_op` field. |
| 71 | 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 | 76 | /// Add an offset to a pointer, returning a new pointer. |
| 73 | 77 | /// The offset is in element type units, not bytes. |
| 74 | 78 | /// Wrapping is undefined behavior. |
| ... | ... | @@ -462,6 +466,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 462 | 466 | .mul, |
| 463 | 467 | .mulwrap, |
| 464 | 468 | .div, |
| 469 | .rem, | |
| 465 | 470 | .bit_and, |
| 466 | 471 | .bit_or, |
| 467 | 472 | .xor, |
src/Liveness.zig+1| ... | ... | @@ -231,6 +231,7 @@ fn analyzeInst( |
| 231 | 231 | .mul, |
| 232 | 232 | .mulwrap, |
| 233 | 233 | .div, |
| 234 | .rem, | |
| 234 | 235 | .ptr_add, |
| 235 | 236 | .ptr_sub, |
| 236 | 237 | .bit_and, |
src/Sema.zig+13-5| ... | ... | @@ -5819,7 +5819,7 @@ fn analyzeArithmetic( |
| 5819 | 5819 | try lhs_val.floatMul(rhs_val, scalar_type, sema.arena); |
| 5820 | 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 | 5825 | log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value }); |
| ... | ... | @@ -5832,6 +5832,14 @@ fn analyzeArithmetic( |
| 5832 | 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 | 5843 | const air_tag: Air.Inst.Tag = switch (zir_tag) { |
| 5836 | 5844 | .add => .add, |
| 5837 | 5845 | .addwrap => .addwrap, |
| ... | ... | @@ -5840,7 +5848,9 @@ fn analyzeArithmetic( |
| 5840 | 5848 | .mul => .mul, |
| 5841 | 5849 | .mulwrap => .mulwrap, |
| 5842 | 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 | 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 | 7312 | } |
| 7303 | 7313 | |
| 7304 | 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; | |
| 7306 | const src = inst_data.src(); | |
| 7307 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirRem", .{}); | |
| 7315 | return sema.zirArithmetic(block, inst); | |
| 7308 | 7316 | } |
| 7309 | 7317 | |
| 7310 | 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 | 809 | .mul => try self.airMul(inst), |
| 810 | 810 | .mulwrap => try self.airMulWrap(inst), |
| 811 | 811 | .div => try self.airDiv(inst), |
| 812 | .rem => try self.airRem(inst), | |
| 812 | 813 | |
| 813 | 814 | .cmp_lt => try self.airCmp(inst, .lt), |
| 814 | 815 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | ... | @@ -1266,6 +1267,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1266 | 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 | 1278 | fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { |
| 1270 | 1279 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1271 | 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 | 858 | // TODO use a different strategy for div that communicates to the optimizer |
| 859 | 859 | // that wrapping is UB. |
| 860 | 860 | .div => try airBinOp( o, inst, " / "), |
| 861 | .rem => try airBinOp( o, inst, " % "), | |
| 861 | 862 | |
| 862 | 863 | .cmp_eq => try airBinOp(o, inst, " == "), |
| 863 | 864 | .cmp_gt => try airBinOp(o, inst, " > "), |
src/codegen/llvm.zig+14| ... | ... | @@ -979,6 +979,7 @@ pub const FuncGen = struct { |
| 979 | 979 | .mul => try self.airMul(inst, false), |
| 980 | 980 | .mulwrap => try self.airMul(inst, true), |
| 981 | 981 | .div => try self.airDiv(inst), |
| 982 | .rem => try self.airRem(inst), | |
| 982 | 983 | .ptr_add => try self.airPtrAdd(inst), |
| 983 | 984 | .ptr_sub => try self.airPtrSub(inst), |
| 984 | 985 | |
| ... | ... | @@ -1721,6 +1722,19 @@ pub const FuncGen = struct { |
| 1721 | 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 | 1738 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1725 | 1739 | if (self.liveness.isUnused(inst)) |
| 1726 | 1740 | return null; |
src/codegen/llvm/bindings.zig+9| ... | ... | @@ -386,6 +386,15 @@ pub const Builder = opaque { |
| 386 | 386 | pub const buildFDiv = LLVMBuildFDiv; |
| 387 | 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 | 398 | pub const buildAnd = LLVMBuildAnd; |
| 390 | 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 | 109 | .mul, |
| 110 | 110 | .mulwrap, |
| 111 | 111 | .div, |
| 112 | .rem, | |
| 112 | 113 | .ptr_add, |
| 113 | 114 | .ptr_sub, |
| 114 | 115 | .bit_and, |
test/stage2/cbe.zig+17| ... | ... | @@ -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 | 936 | ctx.h("simple header", linux_x64, |
| 920 | 937 | \\export fn start() void{} |
| 921 | 938 | , |
test/stage2/llvm.zig+17| ... | ... | @@ -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 | } |