Merge branch 'long-double' into 'master'
Implement long double in printf Closes #273 See merge request redox-os/relibc!1065
This commit is contained in:
@@ -3,3 +3,11 @@ double strtod(const char *nptr, char **endptr);
|
||||
long double strtold(const char *nptr, char **endptr) {
|
||||
return (long double)strtod(nptr, endptr);
|
||||
}
|
||||
|
||||
double relibc_ldtod(const long double* val) {
|
||||
return (double)(*val);
|
||||
}
|
||||
|
||||
void relibc_dtold(double val, long double* out) {
|
||||
*out = (long double)val;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ unsafe extern "C" {
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn __relibc_unused_but_exist_to_avoid_linker_error_on_setjmp(arg: c_int) {
|
||||
/// Workaround to https://www.openwall.com/lists/musl/2023/09/07/2
|
||||
/// By keeping setjmp close to sigsetjmp in link time
|
||||
// Workaround to https://www.openwall.com/lists/musl/2023/09/07/2
|
||||
// By keeping setjmp close to sigsetjmp in link time
|
||||
let jb: *mut u64 = core::ptr::null_mut();
|
||||
unsafe {
|
||||
setjmp(jb);
|
||||
|
||||
@@ -15,13 +15,20 @@ use crate::{
|
||||
platform::{
|
||||
self,
|
||||
types::{
|
||||
c_char, c_double, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulong,
|
||||
c_ulonglong, c_ushort, c_void, intmax_t, ptrdiff_t, size_t, ssize_t, uintmax_t,
|
||||
wchar_t, wint_t,
|
||||
c_char, c_double, c_int, c_long, c_longdouble, c_longlong, c_short, c_uchar, c_uint,
|
||||
c_ulong, c_ulonglong, c_ushort, c_void, intmax_t, ptrdiff_t, size_t, ssize_t,
|
||||
uintmax_t, wchar_t, wint_t,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#[allow(unused_doc_comments)]
|
||||
/// cbindgen:ignore
|
||||
unsafe extern "C" {
|
||||
pub unsafe fn relibc_ldtod(x: *const c_longdouble) -> c_double;
|
||||
pub unsafe fn relibc_dtold(x: c_double, out: *mut c_longdouble);
|
||||
}
|
||||
|
||||
// ____ _ _ _ _
|
||||
// | __ ) ___ (_) | ___ _ __ _ __ | | __ _| |_ ___ _
|
||||
// | _ \ / _ \| | |/ _ \ '__| '_ \| |/ _` | __/ _ (_)
|
||||
@@ -76,6 +83,10 @@ impl Number {
|
||||
match arg {
|
||||
VaArg::c_char(i) => i as usize,
|
||||
VaArg::c_double(i) => i as usize,
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
VaArg::c_longdouble(_) => 0 as usize,
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
VaArg::c_longdouble(i) => i as usize,
|
||||
VaArg::c_int(i) => i as usize,
|
||||
VaArg::c_long(i) => i as usize,
|
||||
VaArg::c_longlong(i) => i as usize,
|
||||
@@ -92,6 +103,7 @@ impl Number {
|
||||
pub(crate) enum VaArg {
|
||||
c_char(c_char),
|
||||
c_double(c_double),
|
||||
c_longdouble(c_longdouble),
|
||||
c_int(c_int),
|
||||
c_long(c_long),
|
||||
c_longlong(c_longlong),
|
||||
@@ -147,6 +159,11 @@ impl VaArg {
|
||||
VaArg::ssize_t(unsafe { ap.arg::<ssize_t>() })
|
||||
}
|
||||
|
||||
(FmtKind::AnyNotation, IntKind::LongLong)
|
||||
| (FmtKind::Decimal, IntKind::LongLong)
|
||||
| (FmtKind::Scientific, IntKind::LongLong) => {
|
||||
VaArg::c_longdouble(unsafe { VaArg::extract_longdouble(ap) })
|
||||
}
|
||||
(FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => {
|
||||
VaArg::c_double(unsafe { ap.arg::<c_double>() })
|
||||
}
|
||||
@@ -156,6 +173,69 @@ impl VaArg {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_arch = "x86")]
|
||||
unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble {
|
||||
todo_skip!(0, "long double in variadic printf is not supported");
|
||||
[0, 0, 0]
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble {
|
||||
// https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf (long double)
|
||||
|
||||
// exactly same as core::ffi::VaListImpl but all variables exposed
|
||||
#[repr(C)]
|
||||
struct VaListImpl {
|
||||
gp_offset: i32,
|
||||
fp_offset: i32,
|
||||
overflow_arg_area: *mut u8,
|
||||
reg_save_area: *mut u8,
|
||||
}
|
||||
|
||||
let ap_impl = unsafe {
|
||||
// The double deconstruct is intended
|
||||
let ptr_to_struct = *(ap as *mut core::ffi::VaList as *mut *mut VaListImpl);
|
||||
&mut *ptr_to_struct
|
||||
};
|
||||
|
||||
let ptr = ap_impl.overflow_arg_area as *const c_longdouble;
|
||||
let val = unsafe { ptr.read() };
|
||||
|
||||
ap_impl.overflow_arg_area = unsafe { ap_impl.overflow_arg_area.add(16) };
|
||||
|
||||
val
|
||||
}
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble {
|
||||
// https://c9x.me/compile/bib/abi-arm64.pdf (quad precision)
|
||||
|
||||
// exactly same as core::ffi::VaListImpl but all variables exposed
|
||||
#[repr(C)]
|
||||
struct VaListImpl {
|
||||
stack: *mut u8,
|
||||
gr_top: *mut u8,
|
||||
vr_top: *mut u8,
|
||||
gr_offs: i32,
|
||||
vr_offs: i32,
|
||||
}
|
||||
|
||||
let ap_impl: &mut VaListImpl = unsafe {
|
||||
// The double deconstruct is intended
|
||||
let ptr_to_struct = *(ap as *mut core::ffi::VaList as *mut *mut VaListImpl);
|
||||
&mut *ptr_to_struct
|
||||
};
|
||||
|
||||
let ptr = unsafe { ap_impl.vr_top.offset(ap_impl.vr_offs as isize) as *const c_longdouble };
|
||||
|
||||
ap_impl.vr_offs += 16;
|
||||
|
||||
unsafe { ptr.read() }
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
unsafe fn extract_longdouble(ap: &mut core::ffi::VaList) -> c_longdouble {
|
||||
todo_skip!(0, "long double in variadic printf is not supported");
|
||||
0u128
|
||||
}
|
||||
unsafe fn transmute(&self, fmtkind: FmtKind, intkind: IntKind) -> VaArg {
|
||||
// At this point, there are conflicting printf arguments. An
|
||||
// example of this is:
|
||||
@@ -168,6 +248,7 @@ impl VaArg {
|
||||
union Untyped {
|
||||
c_char: c_char,
|
||||
c_double: c_double,
|
||||
c_longdouble: c_longdouble,
|
||||
c_int: c_int,
|
||||
c_long: c_long,
|
||||
c_longlong: c_longlong,
|
||||
@@ -181,6 +262,7 @@ impl VaArg {
|
||||
let untyped = match *self {
|
||||
VaArg::c_char(i) => Untyped { c_char: i },
|
||||
VaArg::c_double(i) => Untyped { c_double: i },
|
||||
VaArg::c_longdouble(i) => Untyped { c_longdouble: i },
|
||||
VaArg::c_int(i) => Untyped { c_int: i },
|
||||
VaArg::c_long(i) => Untyped { c_long: i },
|
||||
VaArg::c_longlong(i) => Untyped { c_longlong: i },
|
||||
@@ -223,6 +305,11 @@ impl VaArg {
|
||||
VaArg::ssize_t(unsafe { untyped.ssize_t })
|
||||
}
|
||||
|
||||
(FmtKind::AnyNotation, IntKind::LongLong)
|
||||
| (FmtKind::Decimal, IntKind::LongLong)
|
||||
| (FmtKind::Scientific, IntKind::LongLong) => {
|
||||
VaArg::c_longdouble(unsafe { untyped.c_longdouble })
|
||||
}
|
||||
(FmtKind::AnyNotation, _) | (FmtKind::Decimal, _) | (FmtKind::Scientific, _) => {
|
||||
VaArg::c_double(unsafe { untyped.c_double })
|
||||
}
|
||||
@@ -596,7 +683,7 @@ impl<'a, T: c_str::Kind> Iterator for PrintfIter<'a, T> {
|
||||
'o' | 'u' | 'x' | 'X' => FmtKind::Unsigned,
|
||||
'b' | 'B' if T::IS_THIN_NOT_WIDE => FmtKind::Unsigned,
|
||||
'e' | 'E' => FmtKind::Scientific,
|
||||
'f' | 'F' => FmtKind::Decimal,
|
||||
'f' | 'F' | 'L' => FmtKind::Decimal,
|
||||
'g' | 'G' => FmtKind::AnyNotation,
|
||||
's' => FmtKind::String,
|
||||
'c' => FmtKind::Char,
|
||||
@@ -752,6 +839,7 @@ pub(crate) unsafe fn inner_printf<T: c_str::Kind>(
|
||||
} {
|
||||
VaArg::c_char(i) => i.to_string(),
|
||||
VaArg::c_double(i) => panic!("this should not be possible"),
|
||||
VaArg::c_longdouble(i) => panic!("this should not be possible"),
|
||||
VaArg::c_int(i) => i.to_string(),
|
||||
VaArg::c_long(i) => i.to_string(),
|
||||
VaArg::c_longlong(i) => i.to_string(),
|
||||
@@ -802,6 +890,7 @@ pub(crate) unsafe fn inner_printf<T: c_str::Kind>(
|
||||
} {
|
||||
VaArg::c_char(i) => fmt_int::<_, T>(fmt, i as c_uchar),
|
||||
VaArg::c_double(i) => panic!("this should not be possible"),
|
||||
VaArg::c_longdouble(i) => panic!("this should not be possible"),
|
||||
VaArg::c_int(i) => fmt_int::<_, T>(fmt, i as c_uint),
|
||||
VaArg::c_long(i) => fmt_int::<_, T>(fmt, i as c_ulong),
|
||||
VaArg::c_longlong(i) => fmt_int::<_, T>(fmt, i as c_ulonglong),
|
||||
@@ -862,6 +951,7 @@ pub(crate) unsafe fn inner_printf<T: c_str::Kind>(
|
||||
varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
|
||||
} {
|
||||
VaArg::c_double(i) => i,
|
||||
VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&i as *const c_longdouble) },
|
||||
_ => panic!("this should not be possible"),
|
||||
};
|
||||
if float.is_finite() {
|
||||
@@ -880,6 +970,7 @@ pub(crate) unsafe fn inner_printf<T: c_str::Kind>(
|
||||
varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
|
||||
} {
|
||||
VaArg::c_double(i) => i,
|
||||
VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&i as *const c_longdouble) },
|
||||
_ => panic!("this should not be possible"),
|
||||
};
|
||||
if float.is_finite() {
|
||||
@@ -895,6 +986,7 @@ pub(crate) unsafe fn inner_printf<T: c_str::Kind>(
|
||||
varargs.get(index, &mut ap, Some((arg.fmtkind, arg.intkind)))
|
||||
} {
|
||||
VaArg::c_double(i) => i,
|
||||
VaArg::c_longdouble(i) => unsafe { relibc_ldtod(&i as *const c_longdouble) },
|
||||
_ => panic!("this should not be possible"),
|
||||
};
|
||||
if float.is_finite() {
|
||||
|
||||
@@ -78,6 +78,17 @@ pub type clock_t = c_long;
|
||||
pub type clockid_t = c_int;
|
||||
pub type timer_t = *mut c_void;
|
||||
|
||||
// A C long double is 96 bit in x86, 128 bit in other 64-bit targets
|
||||
// However, both in x86 and x86_64 is actually f80 padded which rust has no underlying support,
|
||||
// while aarch64 (and possibly riscv64) support full f128 type but behind a feature gate.
|
||||
// Until rust supporting them, relibc will lose precision to get them working, plus:
|
||||
// All read operation to this type must be converted from "relibc_ldtod".
|
||||
// All write operation to this type must be converted with "relibc_dtold".
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type c_longdouble = u128;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type c_longdouble = [u32; 3];
|
||||
|
||||
pub use crate::header::bits_pthread::*;
|
||||
|
||||
#[repr(C, align(16))]
|
||||
|
||||
@@ -52,6 +52,8 @@ Float madness:
|
||||
0.0001
|
||||
1E-05
|
||||
1.000000E-05
|
||||
001234.56
|
||||
001234.56
|
||||
|
||||
Non-finite float madness:
|
||||
%e: inf -inf nan -nan
|
||||
|
||||
@@ -52,6 +52,8 @@ Float madness:
|
||||
0.0001
|
||||
1E-05
|
||||
1.000000E-05
|
||||
001234.56
|
||||
001234.56
|
||||
|
||||
Non-finite float madness:
|
||||
%e: inf -inf nan -nan
|
||||
|
||||
@@ -65,6 +65,9 @@ int main(void) {
|
||||
printf("%G\n", 0.00001);
|
||||
printf("%E\n", 0.00001);
|
||||
|
||||
printf("%2$0*1$.*3$lf\n", 9, 1234.56, 2);
|
||||
printf("%2$0*1$.*3$Lf\n", 9, 1234.56L, 2);
|
||||
|
||||
double nonfinites[] = {INFINITY, -INFINITY, NAN, -NAN};
|
||||
char *float_formats[] = {"%e", "%E", "%f", "%F", "%g", "%G"};
|
||||
puts("\nNon-finite float madness:");
|
||||
|
||||
Reference in New Issue
Block a user