authorgravatar for daniele.cocca@gmail.comDaniele Cocca <daniele.cocca@gmail.com> 2022-03-13 21:37:36+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-19 21:01:36-04:00
logb6203b89d6b41f17e44f7806f2244309fa3bd86c
tree3528624c5803ba60c2797378ef4f2fb70cc4fe78
parent1bd595ceea7a0d888d09cd54e3ee39a548beb0db

CBE: implement mod, divFloor, divTrunc


4 files changed, 153 insertions(+), 14 deletions(-)

src/codegen/c.zig+48-3
......@@ -1663,10 +1663,20 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
16631663 .mul => try airBinOp (f, inst, " * "),
16641664 // TODO use a different strategy for div that communicates to the optimizer
16651665 // that wrapping is UB.
1666 .div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "),
1667 .div_floor => try airBinOp( f, inst, " divfloor "),
1666 .div_float, .div_exact => try airBinOp( f, inst, " / "),
1667 .div_trunc => blk: {
1668 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1669 const lhs_ty = f.air.typeOf(bin_op.lhs);
1670 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),
1671 // so we only check one.
1672 break :blk if (lhs_ty.isInt())
1673 try airBinOp(f, inst, " / ")
1674 else
1675 try airBinOpBuiltinCall(f, inst, "div_trunc");
1676 },
1677 .div_floor => try airBinOpBuiltinCall(f, inst, "div_floor"),
16681678 .rem => try airBinOp( f, inst, " % "),
1669 .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
1679 .mod => try airBinOpBuiltinCall(f, inst, "mod"),
16701680
16711681 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
16721682 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
......@@ -3467,6 +3477,41 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C
34673477 return local;
34683478}
34693479
3480fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue {
3481 if (f.liveness.isUnused(inst)) return CValue.none;
3482
3483 const inst_ty = f.air.typeOfIndex(inst);
3484 const local = try f.allocLocal(inst_ty, .Const);
3485 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
3486 const lhs_ty = f.air.typeOf(bin_op.lhs);
3487 const target = f.object.dg.module.getTarget();
3488 const writer = f.object.writer();
3489
3490 // For binary operations @TypeOf(lhs)==@TypeOf(rhs), so we only check one.
3491 if (lhs_ty.isInt()) {
3492 const int_info = lhs_ty.intInfo(target);
3493 const c_bits = toCIntBits(int_info.bits) orelse
3494 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
3495 const prefix_byte: u8 = switch (int_info.signedness) {
3496 .signed => 'i',
3497 .unsigned => 'u',
3498 };
3499 try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, prefix_byte, c_bits });
3500 } else if (lhs_ty.isRuntimeFloat()) {
3501 const c_bits = lhs_ty.floatBits(target);
3502 try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits });
3503 } else {
3504 return f.fail("TODO: C backend: implement airBinOpBuiltinCall for type {s}", .{@tagName(lhs_ty.tag())});
3505 }
3506
3507 try writer.writeByte('(');
3508 try f.writeCValue(writer, try f.resolveInst(bin_op.lhs));
3509 try writer.writeAll(", ");
3510 try f.writeCValue(writer, try f.resolveInst(bin_op.rhs));
3511 try writer.writeAll(");\n");
3512 return local;
3513}
3514
34703515fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
34713516 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
34723517 const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
src/link/C/zig.h+78
......@@ -750,3 +750,81 @@ static inline uint128_t zig_bit_reverse_u128(uint128_t value, uint8_t zig_type_b
750750}
751751
752752#define zig_bit_reverse_i128 zig_bit_reverse_u128
753
754static inline float zig_div_truncf(float numerator, float denominator) {
755 return __builtin_truncf(numerator / denominator);
756}
757
758static inline double zig_div_trunc(double numerator, double denominator) {
759 return __builtin_trunc(numerator / denominator);
760}
761
762static inline long double zig_div_truncl(long double numerator, long double denominator) {
763 return __builtin_truncf(numerator / denominator);
764}
765
766#define zig_div_trunc_f16 zig_div_truncf
767#define zig_div_trunc_f32 zig_div_truncf
768#define zig_div_trunc_f64 zig_div_trunc
769#define zig_div_trunc_f80 zig_div_truncl
770#define zig_div_trunc_f128 zig_div_truncl
771
772#define zig_div_floorf(numerator, denominator) \
773 __builtin_floorf((float)(numerator) / (float)(denominator))
774
775#define zig_div_floor(numerator, denominator) \
776 __builtin_floor((double)(numerator) / (double)(denominator))
777
778#define zig_div_floorl(numerator, denominator) \
779 __builtin_floorl((long double)(numerator) / (long double)(denominator))
780
781#define zig_div_floor_f16 zig_div_floorf
782#define zig_div_floor_f32 zig_div_floorf
783#define zig_div_floor_f64 zig_div_floor
784#define zig_div_floor_f80 zig_div_floorl
785#define zig_div_floor_f128 zig_div_floorl
786
787#define zig_div_floor_u8 zig_div_floorf
788#define zig_div_floor_i8 zig_div_floorf
789#define zig_div_floor_u16 zig_div_floorf
790#define zig_div_floor_i16 zig_div_floorf
791#define zig_div_floor_u32 zig_div_floor
792#define zig_div_floor_i32 zig_div_floor
793#define zig_div_floor_u64 zig_div_floor
794#define zig_div_floor_i64 zig_div_floor
795#define zig_div_floor_u128 zig_div_floorl
796#define zig_div_floor_i128 zig_div_floorl
797
798static inline float zig_modf(float numerator, float denominator) {
799 return (numerator - (zig_div_floorf(numerator, denominator) * denominator));
800}
801
802static inline double zig_mod(double numerator, double denominator) {
803 return (numerator - (zig_div_floor(numerator, denominator) * denominator));
804}
805
806static inline long double zig_modl(long double numerator, long double denominator) {
807 return (numerator - (zig_div_floorl(numerator, denominator) * denominator));
808}
809
810#define zig_mod_f16 zig_modf
811#define zig_mod_f32 zig_modf
812#define zig_mod_f64 zig_mod
813#define zig_mod_f80 zig_modl
814#define zig_mod_f128 zig_modl
815
816#define zig_mod_int(ZigType, CType) \
817 static inline CType zig_mod_##ZigType(CType numerator, CType denominator) { \
818 return (numerator - (zig_div_floor_##ZigType(numerator, denominator) * denominator)); \
819 }
820
821zig_mod_int( u8, uint8_t)
822zig_mod_int( i8, int8_t)
823zig_mod_int( u16, uint16_t)
824zig_mod_int( i16, int16_t)
825zig_mod_int( u32, uint32_t)
826zig_mod_int( i32, int32_t)
827zig_mod_int( u64, uint64_t)
828zig_mod_int( i64, int64_t)
829zig_mod_int(u128, uint128_t)
830zig_mod_int(i128, int128_t)
test/behavior/int_div.zig-1
......@@ -4,7 +4,6 @@ const expect = std.testing.expect;
44
55test "integer division" {
66 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
98 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
109
test/behavior/math.zig+27-10
......@@ -385,7 +385,6 @@ fn testBinaryNot(x: u16) !void {
385385}
386386
387387test "division" {
388 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
389388 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
390389 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
391390 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
......@@ -394,22 +393,18 @@ test "division" {
394393 try testDivision();
395394 comptime try testDivision();
396395}
396
397397fn testDivision() !void {
398398 try expect(div(u32, 13, 3) == 4);
399 try expect(div(f16, 1.0, 2.0) == 0.5);
400399 try expect(div(f32, 1.0, 2.0) == 0.5);
401400
402401 try expect(divExact(u32, 55, 11) == 5);
403402 try expect(divExact(i32, -55, 11) == -5);
404 try expect(divExact(f16, 55.0, 11.0) == 5.0);
405 try expect(divExact(f16, -55.0, 11.0) == -5.0);
406403 try expect(divExact(f32, 55.0, 11.0) == 5.0);
407404 try expect(divExact(f32, -55.0, 11.0) == -5.0);
408405
409406 try expect(divFloor(i32, 5, 3) == 1);
410407 try expect(divFloor(i32, -5, 3) == -2);
411 try expect(divFloor(f16, 5.0, 3.0) == 1.0);
412 try expect(divFloor(f16, -5.0, 3.0) == -2.0);
413408 try expect(divFloor(f32, 5.0, 3.0) == 1.0);
414409 try expect(divFloor(f32, -5.0, 3.0) == -2.0);
415410 try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
......@@ -424,10 +419,6 @@ fn testDivision() !void {
424419 try expect(divTrunc(i32, -5, 3) == -1);
425420 try expect(divTrunc(i32, 9, -10) == 0);
426421 try expect(divTrunc(i32, -9, 10) == 0);
427 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
428 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
429 try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
430 try expect(divTrunc(f16, -9.0, 10.0) == 0.0);
431422 try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
432423 try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
433424 try expect(divTrunc(f32, 9.0, -10.0) == 0.0);
......@@ -468,6 +459,32 @@ fn testDivision() !void {
468459 );
469460 }
470461}
462
463test "division half-precision floats" {
464 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
465 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
466 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
467 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
468 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
469
470 try testDivisionFP16();
471 comptime try testDivisionFP16();
472}
473
474fn testDivisionFP16() !void {
475 try expect(div(f16, 1.0, 2.0) == 0.5);
476
477 try expect(divExact(f16, 55.0, 11.0) == 5.0);
478 try expect(divExact(f16, -55.0, 11.0) == -5.0);
479
480 try expect(divFloor(f16, 5.0, 3.0) == 1.0);
481 try expect(divFloor(f16, -5.0, 3.0) == -2.0);
482 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
483 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
484 try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
485 try expect(divTrunc(f16, -9.0, 10.0) == 0.0);
486}
487
471488fn div(comptime T: type, a: T, b: T) T {
472489 return a / b;
473490}