From 3d775dc09c4f19458a79fb7fbb8535cb27bf1e0c Mon Sep 17 00:00:00 2001 From: Daniele Cocca Date: Sun, 13 Mar 2022 21:37:36 +0000 Subject: [PATCH] CBE: implement mod, divFloor, divTrunc --- src/codegen/c.zig | 51 +++++++++++++++++++++++-- src/link/C/zig.h | 78 +++++++++++++++++++++++++++++++++++++++ test/behavior/int_div.zig | 1 - test/behavior/math.zig | 37 ++++++++++++++----- 4 files changed, 153 insertions(+), 14 deletions(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index b814be44453c..1b11ae9952b9 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1663,10 +1663,20 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul => try airBinOp (f, inst, " * "), // TODO use a different strategy for div that communicates to the optimizer // that wrapping is UB. - .div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "), - .div_floor => try airBinOp( f, inst, " divfloor "), + .div_float, .div_exact => try airBinOp( f, inst, " / "), + .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, " % "), - .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division + .mod => try airBinOpBuiltinCall(f, inst, "mod"), .addwrap => try airWrapOp(f, inst, " + ", "addw_"), .subwrap => try airWrapOp(f, inst, " - ", "subw_"), @@ -3463,6 +3473,41 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C 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 { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data; diff --git a/src/link/C/zig.h b/src/link/C/zig.h index 7259ba19cee8..85c7856d2bde 100644 --- a/src/link/C/zig.h +++ b/src/link/C/zig.h @@ -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 + +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) diff --git a/test/behavior/int_div.zig b/test/behavior/int_div.zig index 8ab8ad9e42c3..0c50ce89096b 100644 --- a/test/behavior/int_div.zig +++ b/test/behavior/int_div.zig @@ -4,7 +4,6 @@ const expect = std.testing.expect; test "integer division" { 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_arm) return error.SkipZigTest; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 0d10113fb04c..67f7fd101de6 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -385,7 +385,6 @@ fn testBinaryNot(x: u16) !void { } 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_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -394,22 +393,18 @@ test "division" { try testDivision(); comptime try testDivision(); } + fn testDivision() !void { 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(divExact(u32, 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(divFloor(i32, 5, 3) == 1); 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) == -2.0); 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, 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, 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 { return a / b; }