| author | |
| committer | |
| log | b6203b89d6b41f17e44f7806f2244309fa3bd86c |
| tree | 3528624c5803ba60c2797378ef4f2fb70cc4fe78 |
| parent | 1bd595ceea7a0d888d09cd54e3ee39a548beb0db |
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 |
| 1663 | 1663 | .mul => try airBinOp (f, inst, " * "), |
| 1664 | 1664 | // TODO use a different strategy for div that communicates to the optimizer |
| 1665 | 1665 | // 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"), | |
| 1668 | 1678 | .rem => try airBinOp( f, inst, " % "), |
| 1669 | .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division | |
| 1679 | .mod => try airBinOpBuiltinCall(f, inst, "mod"), | |
| 1670 | 1680 | |
| 1671 | 1681 | .addwrap => try airWrapOp(f, inst, " + ", "addw_"), |
| 1672 | 1682 | .subwrap => try airWrapOp(f, inst, " - ", "subw_"), |
| ... | ... | @@ -3467,6 +3477,41 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C |
| 3467 | 3477 | return local; |
| 3468 | 3478 | } |
| 3469 | 3479 | |
| 3480 | fn 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 | ||
| 3470 | 3515 | fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue { |
| 3471 | 3516 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; |
| 3472 | 3517 | 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 |
| 750 | 750 | } |
| 751 | 751 | |
| 752 | 752 | #define zig_bit_reverse_i128 zig_bit_reverse_u128 |
| 753 | ||
| 754 | static inline float zig_div_truncf(float numerator, float denominator) { | |
| 755 | return __builtin_truncf(numerator / denominator); | |
| 756 | } | |
| 757 | ||
| 758 | static inline double zig_div_trunc(double numerator, double denominator) { | |
| 759 | return __builtin_trunc(numerator / denominator); | |
| 760 | } | |
| 761 | ||
| 762 | static 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 | ||
| 798 | static inline float zig_modf(float numerator, float denominator) { | |
| 799 | return (numerator - (zig_div_floorf(numerator, denominator) * denominator)); | |
| 800 | } | |
| 801 | ||
| 802 | static inline double zig_mod(double numerator, double denominator) { | |
| 803 | return (numerator - (zig_div_floor(numerator, denominator) * denominator)); | |
| 804 | } | |
| 805 | ||
| 806 | static 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 | ||
| 821 | zig_mod_int( u8, uint8_t) | |
| 822 | zig_mod_int( i8, int8_t) | |
| 823 | zig_mod_int( u16, uint16_t) | |
| 824 | zig_mod_int( i16, int16_t) | |
| 825 | zig_mod_int( u32, uint32_t) | |
| 826 | zig_mod_int( i32, int32_t) | |
| 827 | zig_mod_int( u64, uint64_t) | |
| 828 | zig_mod_int( i64, int64_t) | |
| 829 | zig_mod_int(u128, uint128_t) | |
| 830 | zig_mod_int(i128, int128_t) |
test/behavior/int_div.zig-1| ... | ... | @@ -4,7 +4,6 @@ const expect = std.testing.expect; |
| 4 | 4 | |
| 5 | 5 | test "integer division" { |
| 6 | 6 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 7 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 8 | 7 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; |
| 9 | 8 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 10 | 9 |
test/behavior/math.zig+27-10| ... | ... | @@ -385,7 +385,6 @@ fn testBinaryNot(x: u16) !void { |
| 385 | 385 | } |
| 386 | 386 | |
| 387 | 387 | test "division" { |
| 388 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 389 | 388 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 390 | 389 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 391 | 390 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | ... | @@ -394,22 +393,18 @@ test "division" { |
| 394 | 393 | try testDivision(); |
| 395 | 394 | comptime try testDivision(); |
| 396 | 395 | } |
| 396 | ||
| 397 | 397 | fn testDivision() !void { |
| 398 | 398 | try expect(div(u32, 13, 3) == 4); |
| 399 | try expect(div(f16, 1.0, 2.0) == 0.5); | |
| 400 | 399 | try expect(div(f32, 1.0, 2.0) == 0.5); |
| 401 | 400 | |
| 402 | 401 | try expect(divExact(u32, 55, 11) == 5); |
| 403 | 402 | 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); | |
| 406 | 403 | try expect(divExact(f32, 55.0, 11.0) == 5.0); |
| 407 | 404 | try expect(divExact(f32, -55.0, 11.0) == -5.0); |
| 408 | 405 | |
| 409 | 406 | try expect(divFloor(i32, 5, 3) == 1); |
| 410 | 407 | 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); | |
| 413 | 408 | try expect(divFloor(f32, 5.0, 3.0) == 1.0); |
| 414 | 409 | try expect(divFloor(f32, -5.0, 3.0) == -2.0); |
| 415 | 410 | try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); |
| ... | ... | @@ -424,10 +419,6 @@ fn testDivision() !void { |
| 424 | 419 | try expect(divTrunc(i32, -5, 3) == -1); |
| 425 | 420 | try expect(divTrunc(i32, 9, -10) == 0); |
| 426 | 421 | 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); | |
| 431 | 422 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); |
| 432 | 423 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); |
| 433 | 424 | try expect(divTrunc(f32, 9.0, -10.0) == 0.0); |
| ... | ... | @@ -468,6 +459,32 @@ fn testDivision() !void { |
| 468 | 459 | ); |
| 469 | 460 | } |
| 470 | 461 | } |
| 462 | ||
| 463 | test "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 | ||
| 474 | fn 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 | ||
| 471 | 488 | fn div(comptime T: type, a: T, b: T) T { |
| 472 | 489 | return a / b; |
| 473 | 490 | } |