-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
compiler-rt: add aeabi_fcmp, comparesf2
- Loading branch information
1 parent
22f5e5f
commit bb25f21
Showing
5 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
// Ported from: | ||
// | ||
// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/arm/aeabi_fcmp.S | ||
|
||
const compiler_rt_armhf_target = false; // TODO | ||
|
||
const ConditionalOperator = enum { | ||
Eq, | ||
Lt, | ||
Le, | ||
Ge, | ||
Gt, | ||
}; | ||
|
||
pub nakedcc fn __aeabi_fcmpeq() noreturn { | ||
@setRuntimeSafety(false); | ||
aeabi_fcmp(.Eq); | ||
unreachable; | ||
} | ||
|
||
pub nakedcc fn __aeabi_fcmplt() noreturn { | ||
@setRuntimeSafety(false); | ||
aeabi_fcmp(.Lt); | ||
unreachable; | ||
} | ||
|
||
pub nakedcc fn __aeabi_fcmple() noreturn { | ||
@setRuntimeSafety(false); | ||
aeabi_fcmp(.Le); | ||
unreachable; | ||
} | ||
|
||
pub nakedcc fn __aeabi_fcmpge() noreturn { | ||
@setRuntimeSafety(false); | ||
aeabi_fcmp(.Ge); | ||
unreachable; | ||
} | ||
|
||
pub nakedcc fn __aeabi_fcmpgt() noreturn { | ||
@setRuntimeSafety(false); | ||
aeabi_fcmp(.Gt); | ||
unreachable; | ||
} | ||
|
||
inline fn convert_fcmp_args_to_sf2_args() void { | ||
asm volatile ( | ||
\\ vmov s0, r0 | ||
\\ vmov s1, r1 | ||
); | ||
} | ||
|
||
inline fn aeabi_fcmp(comptime cond: ConditionalOperator) void { | ||
@setRuntimeSafety(false); | ||
asm volatile ( | ||
\\ push { r4, lr } | ||
); | ||
|
||
if (compiler_rt_armhf_target) { | ||
convert_fcmp_args_to_sf2_args(); | ||
} | ||
|
||
switch (cond) { | ||
.Eq => asm volatile ( | ||
\\ bl __eqsf2 | ||
\\ cmp r0, #0 | ||
\\ beq 1f | ||
\\ movs r0, #0 | ||
\\ pop { r4, pc } | ||
\\ 1: | ||
), | ||
.Lt => asm volatile ( | ||
\\ bl __ltsf2 | ||
\\ cmp r0, #0 | ||
\\ blt 1f | ||
\\ movs r0, #0 | ||
\\ pop { r4, pc } | ||
\\ 1: | ||
), | ||
.Le => asm volatile ( | ||
\\ bl __lesf2 | ||
\\ cmp r0, #0 | ||
\\ ble 1f | ||
\\ movs r0, #0 | ||
\\ pop { r4, pc } | ||
\\ 1: | ||
), | ||
.Ge => asm volatile ( | ||
\\ bl __ltsf2 | ||
\\ cmp r0, #0 | ||
\\ blt 1f | ||
\\ movs r0, #0 | ||
\\ pop { r4, pc } | ||
\\ 1: | ||
), | ||
.Gt => asm volatile ( | ||
\\ bl __gtsf2 | ||
\\ cmp r0, #0 | ||
\\ bgt 1f | ||
\\ movs r0, #0 | ||
\\ pop { r4, pc } | ||
\\ 1: | ||
), | ||
} | ||
asm volatile ( | ||
\\ movs r0, #1 | ||
\\ pop { r4, pc } | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Ported from: | ||
// | ||
// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/comparesf2.c | ||
|
||
const std = @import("std"); | ||
const builtin = @import("builtin"); | ||
const is_test = builtin.is_test; | ||
|
||
const fp_t = f32; | ||
const rep_t = u32; | ||
const srep_t = i32; | ||
|
||
const typeWidth = rep_t.bit_count; | ||
const significandBits = std.math.floatMantissaBits(fp_t); | ||
const exponentBits = std.math.floatExponentBits(fp_t); | ||
const signBit = (rep_t(1) << (significandBits + exponentBits)); | ||
const absMask = signBit - 1; | ||
const implicitBit = rep_t(1) << significandBits; | ||
const significandMask = implicitBit - 1; | ||
const exponentMask = absMask ^ significandMask; | ||
const infRep = @bitCast(rep_t, std.math.inf(fp_t)); | ||
|
||
// TODO https://github.com/ziglang/zig/issues/641 | ||
// and then make the return types of some of these functions the enum instead of c_int | ||
const LE_LESS = c_int(-1); | ||
const LE_EQUAL = c_int(0); | ||
const LE_GREATER = c_int(1); | ||
const LE_UNORDERED = c_int(1); | ||
|
||
pub extern fn __lesf2(a: fp_t, b: fp_t) c_int { | ||
@setRuntimeSafety(is_test); | ||
const aInt: srep_t = @bitCast(srep_t, a); | ||
const bInt: srep_t = @bitCast(srep_t, b); | ||
const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask; | ||
const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask; | ||
|
||
// If either a or b is NaN, they are unordered. | ||
if (aAbs > infRep or bAbs > infRep) return LE_UNORDERED; | ||
|
||
// If a and b are both zeros, they are equal. | ||
if ((aAbs | bAbs) == 0) return LE_EQUAL; | ||
|
||
// If at least one of a and b is positive, we get the same result comparing | ||
// a and b as signed integers as we would with a fp_ting-point compare. | ||
if ((aInt & bInt) >= 0) { | ||
if (aInt < bInt) { | ||
return LE_LESS; | ||
} else if (aInt == bInt) { | ||
return LE_EQUAL; | ||
} else return LE_GREATER; | ||
} | ||
|
||
// Otherwise, both are negative, so we need to flip the sense of the | ||
// comparison to get the correct result. (This assumes a twos- or ones- | ||
// complement integer representation; if integers are represented in a | ||
// sign-magnitude representation, then this flip is incorrect). | ||
else { | ||
if (aInt > bInt) { | ||
return LE_LESS; | ||
} else if (aInt == bInt) { | ||
return LE_EQUAL; | ||
} else return LE_GREATER; | ||
} | ||
} | ||
|
||
// TODO https://github.com/ziglang/zig/issues/641 | ||
// and then make the return types of some of these functions the enum instead of c_int | ||
const GE_LESS = c_int(-1); | ||
const GE_EQUAL = c_int(0); | ||
const GE_GREATER = c_int(1); | ||
const GE_UNORDERED = c_int(-1); // Note: different from LE_UNORDERED | ||
|
||
pub extern fn __gesf2(a: fp_t, b: fp_t) c_int { | ||
@setRuntimeSafety(is_test); | ||
const aInt: srep_t = @bitCast(srep_t, a); | ||
const bInt: srep_t = @bitCast(srep_t, b); | ||
const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask; | ||
const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask; | ||
|
||
if (aAbs > infRep or bAbs > infRep) return GE_UNORDERED; | ||
if ((aAbs | bAbs) == 0) return GE_EQUAL; | ||
if ((aInt & bInt) >= 0) { | ||
if (aInt < bInt) { | ||
return GE_LESS; | ||
} else if (aInt == bInt) { | ||
return GE_EQUAL; | ||
} else return GE_GREATER; | ||
} else { | ||
if (aInt > bInt) { | ||
return GE_LESS; | ||
} else if (aInt == bInt) { | ||
return GE_EQUAL; | ||
} else return GE_GREATER; | ||
} | ||
} | ||
|
||
pub extern fn __unordsf2(a: fp_t, b: fp_t) c_int { | ||
@setRuntimeSafety(is_test); | ||
const aAbs: rep_t = @bitCast(rep_t, a) & absMask; | ||
const bAbs: rep_t = @bitCast(rep_t, b) & absMask; | ||
return @boolToInt(aAbs > infRep or bAbs > infRep); | ||
} | ||
|
||
pub extern fn __eqsf2(a: fp_t, b: fp_t) c_int { | ||
return __lesf2(a, b); | ||
} | ||
|
||
pub extern fn __ltsf2(a: fp_t, b: fp_t) c_int { | ||
return __lesf2(a, b); | ||
} | ||
|
||
pub extern fn __nesf2(a: fp_t, b: fp_t) c_int { | ||
return __lesf2(a, b); | ||
} | ||
|
||
pub extern fn __gtsf2(a: fp_t, b: fp_t) c_int { | ||
return __gesf2(a, b); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Ported from: | ||
// | ||
// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/comparesf2_test.c | ||
|
||
const std = @import("std"); | ||
const builtin = @import("builtin"); | ||
const is_test = builtin.is_test; | ||
|
||
const comparesf2 = @import("comparesf2.zig"); | ||
|
||
const TestVector = struct { | ||
a: f32, | ||
b: f32, | ||
eqReference: c_int, | ||
geReference: c_int, | ||
gtReference: c_int, | ||
leReference: c_int, | ||
ltReference: c_int, | ||
neReference: c_int, | ||
unReference: c_int, | ||
}; | ||
|
||
fn test__cmpsf2(vector: TestVector) bool { | ||
if (comparesf2.__eqsf2(vector.a, vector.b) != vector.eqReference) { | ||
return false; | ||
} | ||
if (comparesf2.__gesf2(vector.a, vector.b) != vector.geReference) { | ||
return false; | ||
} | ||
if (comparesf2.__gtsf2(vector.a, vector.b) != vector.gtReference) { | ||
return false; | ||
} | ||
if (comparesf2.__lesf2(vector.a, vector.b) != vector.leReference) { | ||
return false; | ||
} | ||
if (comparesf2.__ltsf2(vector.a, vector.b) != vector.ltReference) { | ||
return false; | ||
} | ||
if (comparesf2.__nesf2(vector.a, vector.b) != vector.neReference) { | ||
return false; | ||
} | ||
if (comparesf2.__unordsf2(vector.a, vector.b) != vector.unReference) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
const arguments = []f32{ | ||
std.math.nan(f32), | ||
-std.math.inf(f32), | ||
-0x1.fffffep127, | ||
-0x1.000002p0 - 0x1.000000p0, | ||
-0x1.fffffep-1, | ||
-0x1.000000p-126, | ||
-0x0.fffffep-126, | ||
-0x0.000002p-126, | ||
-0.0, | ||
0.0, | ||
0x0.000002p-126, | ||
0x0.fffffep-126, | ||
0x1.000000p-126, | ||
0x1.fffffep-1, | ||
0x1.000000p0, | ||
0x1.000002p0, | ||
0x1.fffffep127, | ||
std.math.inf(f32), | ||
}; | ||
|
||
fn generateVector(comptime a: f32, comptime b: f32) TestVector { | ||
const leResult = if (a < b) -1 else if (a == b) 0 else 1; | ||
const geResult = if (a > b) 1 else if (a == b) 0 else -1; | ||
const unResult = if (a != a or b != b) 1 else 0; | ||
return TestVector{ | ||
.a = a, | ||
.b = b, | ||
.eqReference = leResult, | ||
.geReference = geResult, | ||
.gtReference = geResult, | ||
.leReference = leResult, | ||
.ltReference = leResult, | ||
.neReference = leResult, | ||
.unReference = unResult, | ||
}; | ||
} | ||
|
||
const test_vectors = init: { | ||
@setEvalBranchQuota(10000); | ||
var vectors: [arguments.len * arguments.len]TestVector = undefined; | ||
for (arguments[0..]) |arg_i, i| { | ||
for (arguments[0..]) |arg_j, j| { | ||
vectors[(i * arguments.len) + j] = generateVector(arg_i, arg_j); | ||
} | ||
} | ||
break :init vectors; | ||
}; | ||
|
||
test "compare f32" { | ||
for (test_vectors) |vector, i| { | ||
std.testing.expect(test__cmpsf2(vector)); | ||
} | ||
} |