mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
CBE: implement mod, divFloor, divTrunc
This commit is contained in:
parent
1bd595ceea
commit
b6203b89d6
4 changed files with 153 additions and 14 deletions
|
|
@ -1663,10 +1663,20 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
|
||||||
.mul => try airBinOp (f, inst, " * "),
|
.mul => try airBinOp (f, inst, " * "),
|
||||||
// TODO use a different strategy for div that communicates to the optimizer
|
// TODO use a different strategy for div that communicates to the optimizer
|
||||||
// that wrapping is UB.
|
// that wrapping is UB.
|
||||||
.div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "),
|
.div_float, .div_exact => try airBinOp( f, inst, " / "),
|
||||||
.div_floor => try airBinOp( f, inst, " divfloor "),
|
.div_trunc => blk: {
|
||||||
|
const bin_op = f.air.instructions.items(.data)[inst].bin_op;
|
||||||
|
const lhs_ty = f.air.typeOf(bin_op.lhs);
|
||||||
|
// For binary operations @TypeOf(lhs)==@TypeOf(rhs),
|
||||||
|
// so we only check one.
|
||||||
|
break :blk if (lhs_ty.isInt())
|
||||||
|
try airBinOp(f, inst, " / ")
|
||||||
|
else
|
||||||
|
try airBinOpBuiltinCall(f, inst, "div_trunc");
|
||||||
|
},
|
||||||
|
.div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"),
|
||||||
.rem => try airBinOp( f, inst, " % "),
|
.rem => try airBinOp( f, inst, " % "),
|
||||||
.mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
|
.mod => try airBinOpBuiltinCall(f, inst, "mod"),
|
||||||
|
|
||||||
.addwrap => try airWrapOp(f, inst, " + ", "addw_"),
|
.addwrap => try airWrapOp(f, inst, " + ", "addw_"),
|
||||||
.subwrap => try airWrapOp(f, inst, " - ", "subw_"),
|
.subwrap => try airWrapOp(f, inst, " - ", "subw_"),
|
||||||
|
|
@ -3467,6 +3477,41 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C
|
||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue {
|
||||||
|
if (f.liveness.isUnused(inst)) return CValue.none;
|
||||||
|
|
||||||
|
const inst_ty = f.air.typeOfIndex(inst);
|
||||||
|
const local = try f.allocLocal(inst_ty, .Const);
|
||||||
|
const bin_op = f.air.instructions.items(.data)[inst].bin_op;
|
||||||
|
const lhs_ty = f.air.typeOf(bin_op.lhs);
|
||||||
|
const target = f.object.dg.module.getTarget();
|
||||||
|
const writer = f.object.writer();
|
||||||
|
|
||||||
|
// For binary operations @TypeOf(lhs)==@TypeOf(rhs), so we only check one.
|
||||||
|
if (lhs_ty.isInt()) {
|
||||||
|
const int_info = lhs_ty.intInfo(target);
|
||||||
|
const c_bits = toCIntBits(int_info.bits) orelse
|
||||||
|
return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
|
||||||
|
const prefix_byte: u8 = switch (int_info.signedness) {
|
||||||
|
.signed => 'i',
|
||||||
|
.unsigned => 'u',
|
||||||
|
};
|
||||||
|
try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, prefix_byte, c_bits });
|
||||||
|
} else if (lhs_ty.isRuntimeFloat()) {
|
||||||
|
const c_bits = lhs_ty.floatBits(target);
|
||||||
|
try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits });
|
||||||
|
} else {
|
||||||
|
return f.fail("TODO: C backend: implement airBinOpBuiltinCall for type {s}", .{@tagName(lhs_ty.tag())});
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeByte('(');
|
||||||
|
try f.writeCValue(writer, try f.resolveInst(bin_op.lhs));
|
||||||
|
try writer.writeAll(", ");
|
||||||
|
try f.writeCValue(writer, try f.resolveInst(bin_op.rhs));
|
||||||
|
try writer.writeAll(");\n");
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
|
||||||
fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
|
fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
|
||||||
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
|
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
|
||||||
const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
|
const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
|
||||||
|
|
|
||||||
|
|
@ -750,3 +750,81 @@ static inline uint128_t zig_bit_reverse_u128(uint128_t value, uint8_t zig_type_b
|
||||||
}
|
}
|
||||||
|
|
||||||
#define zig_bit_reverse_i128 zig_bit_reverse_u128
|
#define zig_bit_reverse_i128 zig_bit_reverse_u128
|
||||||
|
|
||||||
|
static inline float zig_div_truncf(float numerator, float denominator) {
|
||||||
|
return __builtin_truncf(numerator / denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double zig_div_trunc(double numerator, double denominator) {
|
||||||
|
return __builtin_trunc(numerator / denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long double zig_div_truncl(long double numerator, long double denominator) {
|
||||||
|
return __builtin_truncf(numerator / denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define zig_div_trunc_f16 zig_div_truncf
|
||||||
|
#define zig_div_trunc_f32 zig_div_truncf
|
||||||
|
#define zig_div_trunc_f64 zig_div_trunc
|
||||||
|
#define zig_div_trunc_f80 zig_div_truncl
|
||||||
|
#define zig_div_trunc_f128 zig_div_truncl
|
||||||
|
|
||||||
|
#define zig_div_floorf(numerator, denominator) \
|
||||||
|
__builtin_floorf((float)(numerator) / (float)(denominator))
|
||||||
|
|
||||||
|
#define zig_div_floor(numerator, denominator) \
|
||||||
|
__builtin_floor((double)(numerator) / (double)(denominator))
|
||||||
|
|
||||||
|
#define zig_div_floorl(numerator, denominator) \
|
||||||
|
__builtin_floorl((long double)(numerator) / (long double)(denominator))
|
||||||
|
|
||||||
|
#define zig_div_floor_f16 zig_div_floorf
|
||||||
|
#define zig_div_floor_f32 zig_div_floorf
|
||||||
|
#define zig_div_floor_f64 zig_div_floor
|
||||||
|
#define zig_div_floor_f80 zig_div_floorl
|
||||||
|
#define zig_div_floor_f128 zig_div_floorl
|
||||||
|
|
||||||
|
#define zig_div_floor_u8 zig_div_floorf
|
||||||
|
#define zig_div_floor_i8 zig_div_floorf
|
||||||
|
#define zig_div_floor_u16 zig_div_floorf
|
||||||
|
#define zig_div_floor_i16 zig_div_floorf
|
||||||
|
#define zig_div_floor_u32 zig_div_floor
|
||||||
|
#define zig_div_floor_i32 zig_div_floor
|
||||||
|
#define zig_div_floor_u64 zig_div_floor
|
||||||
|
#define zig_div_floor_i64 zig_div_floor
|
||||||
|
#define zig_div_floor_u128 zig_div_floorl
|
||||||
|
#define zig_div_floor_i128 zig_div_floorl
|
||||||
|
|
||||||
|
static inline float zig_modf(float numerator, float denominator) {
|
||||||
|
return (numerator - (zig_div_floorf(numerator, denominator) * denominator));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline double zig_mod(double numerator, double denominator) {
|
||||||
|
return (numerator - (zig_div_floor(numerator, denominator) * denominator));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long double zig_modl(long double numerator, long double denominator) {
|
||||||
|
return (numerator - (zig_div_floorl(numerator, denominator) * denominator));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define zig_mod_f16 zig_modf
|
||||||
|
#define zig_mod_f32 zig_modf
|
||||||
|
#define zig_mod_f64 zig_mod
|
||||||
|
#define zig_mod_f80 zig_modl
|
||||||
|
#define zig_mod_f128 zig_modl
|
||||||
|
|
||||||
|
#define zig_mod_int(ZigType, CType) \
|
||||||
|
static inline CType zig_mod_##ZigType(CType numerator, CType denominator) { \
|
||||||
|
return (numerator - (zig_div_floor_##ZigType(numerator, denominator) * denominator)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
zig_mod_int( u8, uint8_t)
|
||||||
|
zig_mod_int( i8, int8_t)
|
||||||
|
zig_mod_int( u16, uint16_t)
|
||||||
|
zig_mod_int( i16, int16_t)
|
||||||
|
zig_mod_int( u32, uint32_t)
|
||||||
|
zig_mod_int( i32, int32_t)
|
||||||
|
zig_mod_int( u64, uint64_t)
|
||||||
|
zig_mod_int( i64, int64_t)
|
||||||
|
zig_mod_int(u128, uint128_t)
|
||||||
|
zig_mod_int(i128, int128_t)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ const expect = std.testing.expect;
|
||||||
|
|
||||||
test "integer division" {
|
test "integer division" {
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
|
||||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -385,7 +385,6 @@ fn testBinaryNot(x: u16) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "division" {
|
test "division" {
|
||||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
|
||||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||||
|
|
@ -394,22 +393,18 @@ test "division" {
|
||||||
try testDivision();
|
try testDivision();
|
||||||
comptime try testDivision();
|
comptime try testDivision();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn testDivision() !void {
|
fn testDivision() !void {
|
||||||
try expect(div(u32, 13, 3) == 4);
|
try expect(div(u32, 13, 3) == 4);
|
||||||
try expect(div(f16, 1.0, 2.0) == 0.5);
|
|
||||||
try expect(div(f32, 1.0, 2.0) == 0.5);
|
try expect(div(f32, 1.0, 2.0) == 0.5);
|
||||||
|
|
||||||
try expect(divExact(u32, 55, 11) == 5);
|
try expect(divExact(u32, 55, 11) == 5);
|
||||||
try expect(divExact(i32, -55, 11) == -5);
|
try expect(divExact(i32, -55, 11) == -5);
|
||||||
try expect(divExact(f16, 55.0, 11.0) == 5.0);
|
|
||||||
try expect(divExact(f16, -55.0, 11.0) == -5.0);
|
|
||||||
try expect(divExact(f32, 55.0, 11.0) == 5.0);
|
try expect(divExact(f32, 55.0, 11.0) == 5.0);
|
||||||
try expect(divExact(f32, -55.0, 11.0) == -5.0);
|
try expect(divExact(f32, -55.0, 11.0) == -5.0);
|
||||||
|
|
||||||
try expect(divFloor(i32, 5, 3) == 1);
|
try expect(divFloor(i32, 5, 3) == 1);
|
||||||
try expect(divFloor(i32, -5, 3) == -2);
|
try expect(divFloor(i32, -5, 3) == -2);
|
||||||
try expect(divFloor(f16, 5.0, 3.0) == 1.0);
|
|
||||||
try expect(divFloor(f16, -5.0, 3.0) == -2.0);
|
|
||||||
try expect(divFloor(f32, 5.0, 3.0) == 1.0);
|
try expect(divFloor(f32, 5.0, 3.0) == 1.0);
|
||||||
try expect(divFloor(f32, -5.0, 3.0) == -2.0);
|
try expect(divFloor(f32, -5.0, 3.0) == -2.0);
|
||||||
try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
|
try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
|
||||||
|
|
@ -424,10 +419,6 @@ fn testDivision() !void {
|
||||||
try expect(divTrunc(i32, -5, 3) == -1);
|
try expect(divTrunc(i32, -5, 3) == -1);
|
||||||
try expect(divTrunc(i32, 9, -10) == 0);
|
try expect(divTrunc(i32, 9, -10) == 0);
|
||||||
try expect(divTrunc(i32, -9, 10) == 0);
|
try expect(divTrunc(i32, -9, 10) == 0);
|
||||||
try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
|
|
||||||
try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
|
|
||||||
try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
|
|
||||||
try expect(divTrunc(f16, -9.0, 10.0) == 0.0);
|
|
||||||
try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
|
try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
|
||||||
try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
|
try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
|
||||||
try expect(divTrunc(f32, 9.0, -10.0) == 0.0);
|
try expect(divTrunc(f32, 9.0, -10.0) == 0.0);
|
||||||
|
|
@ -468,6 +459,32 @@ fn testDivision() !void {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "division half-precision floats" {
|
||||||
|
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||||
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||||
|
|
||||||
|
try testDivisionFP16();
|
||||||
|
comptime try testDivisionFP16();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testDivisionFP16() !void {
|
||||||
|
try expect(div(f16, 1.0, 2.0) == 0.5);
|
||||||
|
|
||||||
|
try expect(divExact(f16, 55.0, 11.0) == 5.0);
|
||||||
|
try expect(divExact(f16, -55.0, 11.0) == -5.0);
|
||||||
|
|
||||||
|
try expect(divFloor(f16, 5.0, 3.0) == 1.0);
|
||||||
|
try expect(divFloor(f16, -5.0, 3.0) == -2.0);
|
||||||
|
try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
|
||||||
|
try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
|
||||||
|
try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
|
||||||
|
try expect(divTrunc(f16, -9.0, 10.0) == 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
fn div(comptime T: type, a: T, b: T) T {
|
fn div(comptime T: type, a: T, b: T) T {
|
||||||
return a / b;
|
return a / b;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue