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 nontrapping-fptoint lowering pass #7016

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open

Add nontrapping-fptoint lowering pass #7016

wants to merge 30 commits into from

Conversation

dschuff
Copy link
Member

@dschuff dschuff commented Oct 17, 2024

This pass lowers nontrapping FP to int instructions to implement LLVM's conversion behavior.
This means that they are not fully complete lowerings according to the wasm spec, but have the same
undefined behavior that LLM does. This keeps the pass simpler and preserves existing behavior when
compiling without nontrapping-ft.
This will be used in emscripten, so that we can build libraries with nontrapping-fp and lower them away after link if desired.

@dschuff dschuff requested a review from kripken October 17, 2024 18:50
@dschuff
Copy link
Member Author

dschuff commented Nov 15, 2024

OK, I added an exec test similar to what we have for memcpy. I think this should be good to review now.

@@ -76,6 +76,7 @@ set(passes_SOURCES
NameList.cpp
NameTypes.cpp
NoInline.cpp
LLVMNontrappingFPToIntLowering.cpp
Copy link
Member

Choose a reason for hiding this comment

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

We didn't put LLVM in the name of the bulk memory pass IIRC - should we match the two? I don't feel strongly either way.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah yeah my original intention was that it should lower just memory.copy and memory.fill (not all of bulk memory) but that part wouldn't need to be LLVM-specific. But then I made it have the same undefined behavior LLVM does, so I think it does make sense to put LLVM in the name. I'll do that in a separate PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in #7082

src/passes/LLVMNontrappingFPToIntLowering.cpp Show resolved Hide resolved
(func $i64.trunc_sat_f64_s (param $x f64) (result i64) (i64.trunc_sat_f64_s (local.get $x)))
(func $i64.trunc_sat_f64_u (param $x f64) (result i64) (i64.trunc_sat_f64_u (local.get $x)))

(func $f32_i32 (export "f32_i32")
Copy link
Member

Choose a reason for hiding this comment

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

The CHECK lines don't seem to be here? May need to run ./scripts/update_lit_checks.py test/lit/exec/*

Copy link
Member Author

Choose a reason for hiding this comment

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

ah oops, yes, the fuzz comparison runs, but the test run was written not to generate output. I think I pasted that from somewhere, I guess some tests want to check the output and some don't.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, does it make more sense just to not print the wast output in this test? test/lit/passes/nontrapping-fptoint-lowering.wast prints the output, and the output from this test is the same.

Copy link
Member

Choose a reason for hiding this comment

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

The wast isn't really needed, I agree. But we should still have the execution output, even if just to report that the function was called and returned nothing.

Actually, I see --fuzz-exec is missing from line 3, is that the issue maybe?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that was why the tests didn't get auto-updated. Fixed now, using just the fuzz-exec output.

@dschuff dschuff requested a review from aheejin November 15, 2024 17:42
@dschuff
Copy link
Member Author

dschuff commented Nov 15, 2024

@aheejin can you PTAL, @kripken is out today.

Comment on lines +35 to +36
case TruncSatSFloat32ToInt32:
return UnaryOp::TruncSFloat32ToInt32;
Copy link
Member

Choose a reason for hiding this comment

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

Nit: It'd be better to be consistent whether or not we use UnaryOp::? The same for other parts of the code.

Comment on lines +93 to +108
case TruncSatUFloat32ToInt32:
ltOp = BinaryOp::LtFloat32;
geOp = BinaryOp::GeFloat32;
break;
case TruncSatUFloat64ToInt32:
ltOp = BinaryOp::LtFloat64;
geOp = BinaryOp::GeFloat64;
break;
case TruncSatUFloat32ToInt64:
ltOp = BinaryOp::LtFloat32;
geOp = BinaryOp::GeFloat32;
break;
case TruncSatUFloat64ToInt64:
ltOp = BinaryOp::LtFloat64;
geOp = BinaryOp::GeFloat64;
break;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
case TruncSatUFloat32ToInt32:
ltOp = BinaryOp::LtFloat32;
geOp = BinaryOp::GeFloat32;
break;
case TruncSatUFloat64ToInt32:
ltOp = BinaryOp::LtFloat64;
geOp = BinaryOp::GeFloat64;
break;
case TruncSatUFloat32ToInt64:
ltOp = BinaryOp::LtFloat32;
geOp = BinaryOp::GeFloat32;
break;
case TruncSatUFloat64ToInt64:
ltOp = BinaryOp::LtFloat64;
geOp = BinaryOp::GeFloat64;
break;
case TruncSatUFloat32ToInt32:
case TruncSatUFloat32ToInt64:
ltOp = BinaryOp::LtFloat32;
geOp = BinaryOp::GeFloat32;
break;
case TruncSatUFloat64ToInt32:
ltOp = BinaryOp::LtFloat64;
geOp = BinaryOp::GeFloat64;
break;

@@ -334,6 +334,10 @@ void PassRegistry::registerPasses() {
registerPass("no-partial-inline",
"mark functions as no-inline (for partial inlining only)",
createNoPartialInlinePass);
registerPass("llvm-nontrapping-fptoint-lowering",
"lower nontrapping float-to-int operations to wasm mvp and"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"lower nontrapping float-to-int operations to wasm mvp and"
"lower nontrapping float-to-int operations to wasm mvp and "

@@ -230,6 +230,11 @@
;; CHECK-NEXT: --limit-segments attempt to merge segments to fit
;; CHECK-NEXT: within web limits
;; CHECK-NEXT:
;; CHECK-NEXT: --llvm-nontrapping-fptoint-lowering lower nontrapping float-to-int
;; CHECK-NEXT: operations to wasm mvp
;; CHECK-NEXT: anddisable the nontrapping
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
;; CHECK-NEXT: anddisable the nontrapping
;; CHECK-NEXT: and disable the nontrapping

@@ -239,6 +239,11 @@
;; CHECK-NEXT: --limit-segments attempt to merge segments to fit
;; CHECK-NEXT: within web limits
;; CHECK-NEXT:
;; CHECK-NEXT: --llvm-nontrapping-fptoint-lowering lower nontrapping float-to-int
;; CHECK-NEXT: operations to wasm mvp
;; CHECK-NEXT: anddisable the nontrapping
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
;; CHECK-NEXT: anddisable the nontrapping
;; CHECK-NEXT: and disable the nontrapping

@@ -193,6 +193,11 @@
;; CHECK-NEXT: --limit-segments attempt to merge segments to fit
;; CHECK-NEXT: within web limits
;; CHECK-NEXT:
;; CHECK-NEXT: --llvm-nontrapping-fptoint-lowering lower nontrapping float-to-int
;; CHECK-NEXT: operations to wasm mvp
;; CHECK-NEXT: anddisable the nontrapping
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
;; CHECK-NEXT: anddisable the nontrapping
;; CHECK-NEXT: and disable the nontrapping

builder.makeConst(static_cast<From>(std::numeric_limits<To>::max()))),
builder.makeUnary(getReplacementOp(curr->op),
builder.makeLocalGet(v, curr->value->type)),
builder.makeConst(std::numeric_limits<To>::min())));
Copy link
Member

Choose a reason for hiding this comment

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

Are this value and '0' in replaceUnsigned arbitrarily chosen?
(LLVM's choices are different (but it looks they are arbitrarily chosen too): https://github.com/llvm/llvm-project/blob/2906fcadb8563a02949f852867cebc63e0539cb7/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp#L492)

And these values don't seem to conform to the real "saturating" spec, because we don't check whether the value overflows or underflows and just use a single substitute value. Is this the reason why this pass is LLVM-specific? Then what is the reason for not making pass a generic conversion pass by implementing the real saturation behavior? (I guess performance?)

ltOp,
builder.makeUnary(
absOp, builder.makeLocalTee(v, curr->value, curr->value->type)),
builder.makeConst(static_cast<From>(std::numeric_limits<To>::max()))),
Copy link
Member

Choose a reason for hiding this comment

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

Separate question: Do you know why LLVM's code adds -(minus)?
https://github.com/llvm/llvm-project/blob/2906fcadb8563a02949f852867cebc63e0539cb7/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp#L493

Also, does this handle when the input value is greater than INT_MIN and less than or equal to INT_MIN+1? INT_MIN's abs value would be greater than INT_MAX but it's still in the int range. (in case of int)

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.

3 participants