-
Notifications
You must be signed in to change notification settings - Fork 821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for non-trapping float-to-int conversions. #1071
Conversation
bors try |
tryBuild succeeded
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good to me as far as code structure goes. The biggest code smell to me is the large number of repeated large magic numbers, sometimes with subtle differences. I didn't comment on all them, but I think it'd be good to give them names maybe put them somewhere shared if we're going to need them in every backend
lower_bound: u64, // Exclusive (lowest representable value) | ||
upper_bound: u64, // Exclusive (greatest representable value) | ||
int_min_value: u64, | ||
int_max_value: u64, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment in the abstract, I don't expect you to fix this in this PR or necessarily soon, but on first glance, I'd prefer a struct with methods over numbers in a case like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reviewing the PR I think it's not as bad as I first thought. I think it's probably possible to clean it up a bit though. Low priority all things considered though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand the proposed cleanup here. Create a struct with lower/upper/min/max and a single method on it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just being more semantic where possible. It's a minor thing but a number is a very open ended thing. If it was a bunch of 0-sized structs with a method that returned an upper and lower bound, then mix ups would be less likely.
I don't think it's worth the effort here though -- I thought this was more complex than it actually is
std::i32::MIN as u64, | ||
2147483520u64, // bits as f32: 0x4effffff | ||
std::i32::MIN as u32 as u64, | ||
std::i32::MAX as u32 as u64, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⛳️ i32::max_value()
doesn't need std::
🤔 either way is fine though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think std::i32::MIN
is shorter than i32::max_value()
? I tried i32::MIN
and the compiler told me I need a std::
prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm fine with either -- the only reason I mention it is that I haven't seen the MIN and MAX constants in a while. Now that I see them again I do remember having seen them in the past.
&mut self.machine, | ||
tmp_in, | ||
-2147483904.0, | ||
2147483648.0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Names and/or explanations for numbers like these are always appreciated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a section of constants to the bottom of the file. (Same in the LLVM backend too.) Done.
&mut self.machine, | ||
tmp_in, | ||
-9223373136366403584.0, | ||
9223372036854775808.0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GEF32_LT_I64_MIN
and LEF32_GT_I64_MAX
.
&mut self.machine, | ||
tmp_in, | ||
-1.0, | ||
18446744073709551616.0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here too, if these numbers are shared between the backends it might even make sense to put them in a shared location
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These aren't quite shared between backends, though they absolutely could be. Note that these are f32/f64 while the llvm-backend uses u64, also llvm-backend uses values that are inside the valid range while singlepass uses values outside the valid range. I don't think there's any underlying need for these differences, but it might be that these are expressed in the way most convenient for lowering code in each backend. I'm going to punt that investigation and possible cleanup to a future PR.
a.emit_cvttss2si_64(XMMOrMemory::XMM(tmp_in), tmp_out); | ||
a.emit_mov( | ||
Size::S64, | ||
Location::Imm64(0x8000000000000000u64), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be i64::min_value() as u64
or 1 << 63
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, though I don't know what about this value to emphasize, I merely copied this code from I64TruncUF32
. I'm going to request that I save that change for a future PR. This constant appears in numerous other places, including those not related to truncation at all.
&mut self.machine, | ||
tmp_in, | ||
-1.0, | ||
4294967296.0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This number is appearing often
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
&mut self.machine, | ||
tmp_in, | ||
-1.0, | ||
4294967296.0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps a named constant is in order
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup.
&mut self.machine, | ||
real_in, | ||
-2147483649.0, | ||
2147483648.0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mhmm.
&mut self.machine, | ||
tmp_in, | ||
-9223372036854777856.0, | ||
9223372036854775808.0, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bottom number is the same but the top number is slightly different:
from
-9223373136366403584.0,
9223372036854775808.0,
names would make that more obvious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GEF64_LT_I64_MIN
and LEF64_GT_I64_MAX
.
if a.arch_has_itruncf() { | ||
a.arch_emit_i64_trunc_uf32(tmp_in, tmp_out); | ||
} else { | ||
let tmp = m.acquire_temp_gpr().unwrap(); // r15 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems that these specific register indexes (r15, xmm1, xmm3) are not depended on from anywhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copied from I64TruncUF32
. Agreed. I also never checked what registers are really chosen, so the comments could very well be wrong. Removed the comments.
self.value_stack.push(ret); | ||
|
||
let tmp_out = self.machine.acquire_temp_gpr().unwrap(); | ||
let tmp_in = self.machine.acquire_temp_xmm().unwrap(); // xmm2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
if a.arch_has_itruncf() { | ||
a.arch_emit_i64_trunc_uf64(tmp_in, tmp_out); | ||
} else { | ||
let tmp = m.acquire_temp_gpr().unwrap(); // r15 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
258c4f3
to
4f1c53f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks reasonable from my side -- I didn't check that Heyang's issues were resolved, this approval is just from my side.
Though I do think sharing the constants should happen before this PR merged, unless there's a good reason why putting them in runtime-core or a new shared backend crate isn't a good idea
edit: for the record: the constants in singlepass and llvm are entirely different so it doesn't make sense to share them. I didn't review the PR closely enough 😄
/// Greatest Exact Float (64 bits) less-than u64::MIN when rounding towards zero. | ||
const GEF64_LT_U64_MIN: f64 = -1.0; | ||
/// Least Exact Float (64 bits) greater-than u64::MAX when rounding towards zero. | ||
const LEF64_GT_U64_MAX: f64 = 18446744073709551616.0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer if these weren't duplicated; if they don't seem to fit into runtime-core
, we could add backend-common
crate for this kind of thing. I'd like to encourage code reuse among the backends in general
This breaks all of conversions.wast on singlepass. LLVM and Cranelift pass.
… there's been a release.
29d65c0
to
bba0129
Compare
bors r+ |
1071: Add support for non-trapping float-to-int conversions. r=nlewycky a=nlewycky # Description Rewrites LLVM implementation of non-trapping float-to-int conversions. LLVM's conversion operations produce unspecified output when the input floats are out of range for the conversion. Add implementation to singlepass for both x86-64 and AArch64. # Review - [x] Add a short description of the the change to the CHANGELOG.md file Co-authored-by: Nick Lewycky <nick@wasmer.io>
Build succeeded
|
Description
Rewrites LLVM implementation of non-trapping float-to-int conversions. LLVM's conversion operations produce unspecified output when the input floats are out of range for the conversion.
Add implementation to singlepass for both x86-64 and AArch64.
Review