mirror of
https://github.com/ziglang/zig.git
synced 2026-01-06 05:25:10 +00:00
Merge pull request #9655 from nektro/stage2-rem
stage2: implement runtime `%` and `@rem`
This commit is contained in:
commit
3b9ec4e4df
@ -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,
|
||||
|
||||
@ -231,6 +231,7 @@ fn analyzeInst(
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.div,
|
||||
.rem,
|
||||
.ptr_add,
|
||||
.ptr_sub,
|
||||
.bit_and,
|
||||
|
||||
18
src/Sema.zig
18
src/Sema.zig
@ -5819,7 +5819,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 });
|
||||
@ -5832,6 +5832,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,
|
||||
@ -5840,7 +5848,9 @@ 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)}),
|
||||
.mod_rem => .rem,
|
||||
.rem => .rem,
|
||||
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);
|
||||
@ -7302,9 +7312,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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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, " > "),
|
||||
|
||||
@ -979,6 +979,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),
|
||||
|
||||
@ -1721,6 +1722,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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -109,6 +109,7 @@ const Writer = struct {
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.div,
|
||||
.rem,
|
||||
.ptr_add,
|
||||
.ptr_sub,
|
||||
.bit_and,
|
||||
|
||||
@ -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{}
|
||||
,
|
||||
|
||||
@ -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;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user