Skip to content
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

Merged
merged 12 commits into from
Dec 21, 2019
Merged

Conversation

nlewycky
Copy link
Contributor

@nlewycky nlewycky commented Dec 16, 2019

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

  • Add a short description of the the change to the CHANGELOG.md file

@syrusakbary
Copy link
Member

bors try

bors bot added a commit that referenced this pull request Dec 16, 2019
@bors
Copy link
Contributor

bors bot commented Dec 17, 2019

try

Build succeeded

Copy link
Contributor

@MarkMcCaskey MarkMcCaskey left a 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,
Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Contributor

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,
Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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,
Copy link
Contributor

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

Copy link
Contributor Author

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

Copy link
Contributor Author

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,
Copy link
Contributor

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

Copy link
Contributor Author

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),
Copy link
Contributor

@MarkMcCaskey MarkMcCaskey Dec 18, 2019

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?

Copy link
Contributor Author

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,
Copy link
Contributor

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

Copy link
Contributor Author

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,
Copy link
Contributor

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

Copy link
Contributor Author

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these too

Copy link
Contributor Author

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,
Copy link
Contributor

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

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Contributor Author

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

Copy link
Contributor Author

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@MarkMcCaskey MarkMcCaskey left a 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;
Copy link
Contributor

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

@nlewycky
Copy link
Contributor Author

bors r+

bors bot added a commit that referenced this pull request Dec 21, 2019
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 <[email protected]>
@bors
Copy link
Contributor

bors bot commented Dec 21, 2019

Build succeeded

@bors bors bot merged commit bba0129 into master Dec 21, 2019
@bors bors bot deleted the feature/trunc-sat branch December 21, 2019 01:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants