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
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
be05442
Works for 32->32
dschuff Mar 12, 2024
90bd6d5
Templatize
dschuff Mar 12, 2024
ea01454
hacks to run it
dschuff Mar 29, 2024
7df947b
unreachable
dschuff Oct 17, 2024
f3eff06
remove debug code
dschuff Oct 17, 2024
9810d17
update help checks
dschuff Oct 17, 2024
d1db88e
tee into a local
dschuff Oct 17, 2024
925d34a
add more explanation
dschuff Oct 17, 2024
07dd028
remove debugging code
dschuff Oct 17, 2024
ab7431a
formatting
dschuff Oct 17, 2024
d3ae179
clang-format
dschuff Oct 17, 2024
0ed5b59
Lower memory.copy
dschuff Oct 18, 2024
9bbd758
update help tests
dschuff Oct 18, 2024
16285bb
reverse loop condition
dschuff Oct 18, 2024
47dc98f
add memory fill
dschuff Oct 18, 2024
c373c13
copy in both directions, add var names
dschuff Oct 21, 2024
46aaa06
Fix bugs
dschuff Oct 30, 2024
8c74874
use Fatal instead of throw; error on other bulk-memory instructions; …
dschuff Nov 7, 2024
d0f3c87
review
dschuff Nov 7, 2024
914def0
clang-format
dschuff Nov 7, 2024
c758b5f
fix func names
dschuff Nov 7, 2024
02201ef
improve formatting
dschuff Nov 8, 2024
2854ff3
suppress clang-format
dschuff Nov 8, 2024
3ccb64f
revert formatting changes; not worth it
dschuff Nov 8, 2024
573275f
Merge branch 'copy-fill-lowering' into nontrapping-lower
dschuff Nov 8, 2024
8eaebf6
add test
dschuff Nov 14, 2024
6f29023
Merge branch 'main' into nontrapping-lower
dschuff Nov 15, 2024
d735093
update test
dschuff Nov 15, 2024
f6483d1
convert to function-parallel pass
dschuff Nov 15, 2024
e7c50cf
clang-format
dschuff Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/passes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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

OnceReduction.cpp
OptimizeAddedConstants.cpp
OptimizeCasts.cpp
Expand Down
150 changes: 150 additions & 0 deletions src/passes/LLVMNontrappingFPToIntLowering.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#include "pass.h"
#include "wasm-builder.h"
#include "wasm.h"
#include <limits>

// By default LLVM emits nontrapping float-to-int instructions to implement its
// fptoui/fptosi conversion instructions. This pass replaces these instructions
// with code sequences which also implement LLVM's fptoui/fptosi, but which are
// not semantically equivalent in wasm. This is because out-of-range inputs to
// these instructions produce poison values. So we need only ensure that there
// is no trap, but need not ensure any particular result.
dschuff marked this conversation as resolved.
Show resolved Hide resolved

namespace wasm {
struct LLVMNonTrappingFPToIntLowering
: public WalkerPass<PostWalker<LLVMNonTrappingFPToIntLowering>> {

dschuff marked this conversation as resolved.
Show resolved Hide resolved
UnaryOp getReplacementOp(UnaryOp op) {
switch (op) {
case TruncSatSFloat32ToInt32:
return UnaryOp::TruncSFloat32ToInt32;
Comment on lines +35 to +36
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.

case TruncSatUFloat32ToInt32:
return UnaryOp::TruncUFloat32ToInt32;
case TruncSatSFloat64ToInt32:
return UnaryOp::TruncSFloat64ToInt32;
case TruncSatUFloat64ToInt32:
return UnaryOp::TruncUFloat64ToInt32;
case TruncSatSFloat32ToInt64:
return UnaryOp::TruncSFloat32ToInt64;
case TruncSatUFloat32ToInt64:
return UnaryOp::TruncUFloat32ToInt64;
case TruncSatSFloat64ToInt64:
return UnaryOp::TruncSFloat64ToInt64;
case TruncSatUFloat64ToInt64:
return UnaryOp::TruncUFloat64ToInt64;
default:
WASM_UNREACHABLE("Unexpected opcode");
}
}

template<typename From, typename To> void ReplaceSigned(Unary* curr) {
dschuff marked this conversation as resolved.
Show resolved Hide resolved
BinaryOp ltOp;
UnaryOp absOp;
switch (curr->op) {
case TruncSatSFloat32ToInt32:
case TruncSatSFloat32ToInt64:
ltOp = BinaryOp::LtFloat32;
absOp = UnaryOp::AbsFloat32;
break;
case TruncSatSFloat64ToInt32:
case TruncSatSFloat64ToInt64:
ltOp = BinaryOp::LtFloat64;
absOp = UnaryOp::AbsFloat64;
break;
default:
WASM_UNREACHABLE("Unexpected opcode");
}

Builder builder(*getModule());
replaceCurrent(builder.makeIf(
builder.makeBinary(
ltOp,
builder.makeUnary(absOp, curr->value),
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)

builder.makeUnary(getReplacementOp(curr->op), curr->value),
dschuff marked this conversation as resolved.
Show resolved Hide resolved
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?)

}

template<typename From, typename To> void ReplaceUnsigned(Unary* curr) {
dschuff marked this conversation as resolved.
Show resolved Hide resolved
BinaryOp ltOp, geOp;

switch (curr->op) {
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;
Comment on lines +93 to +108
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;

default:
WASM_UNREACHABLE("Unexpected opcode");
}

Builder builder(*getModule());
replaceCurrent(builder.makeIf(
builder.makeBinary(
BinaryOp::AndInt32,
builder.makeBinary(
ltOp,
curr->value,
builder.makeConst(static_cast<From>(std::numeric_limits<To>::max()))),
builder.makeBinary(
geOp, curr->value, builder.makeConst(static_cast<From>(0.0)))),
builder.makeUnary(getReplacementOp(curr->op), curr->value),
builder.makeConst(static_cast<To>(0))));
}

void visitUnary(Unary* curr) {
switch (curr->op) {
case TruncSatSFloat32ToInt32:
ReplaceSigned<float, int32_t>(curr);
break;
case TruncSatSFloat64ToInt32:
ReplaceSigned<double, int32_t>(curr);
break;
case TruncSatSFloat32ToInt64:
ReplaceSigned<float, int64_t>(curr);
break;
case TruncSatSFloat64ToInt64:
ReplaceSigned<double, int64_t>(curr);
break;
case TruncSatUFloat32ToInt32:
ReplaceUnsigned<float, uint32_t>(curr);
break;
case TruncSatUFloat64ToInt32:
ReplaceUnsigned<double, uint32_t>(curr);
break;
case TruncSatUFloat32ToInt64:
ReplaceUnsigned<float, uint64_t>(curr);
break;
case TruncSatUFloat64ToInt64:
ReplaceUnsigned<double, uint64_t>(curr);
break;
default:
WASM_UNREACHABLE("Unexpected opcode");
}
}

void run(Module* module) override {
if (!module->features.hasTruncSat()) {
return;
}
Super::run(module);
module->features.disable(FeatureSet::TruncSat);
}
};

Pass* createLLVMNonTrappingFPToIntLoweringPass() {
return new LLVMNonTrappingFPToIntLowering();
}

} // namespace wasm
4 changes: 4 additions & 0 deletions src/passes/pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,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 "

"disable the nontrapping fptoint feature",
createLLVMNonTrappingFPToIntLoweringPass);
registerPass("once-reduction",
"reduces calls to code that only runs once",
createOnceReductionPass);
Expand Down
1 change: 1 addition & 0 deletions src/passes/passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Pass* createOutliningPass();
Pass* createPickLoadSignsPass();
Pass* createModAsyncifyAlwaysOnlyUnwindPass();
Pass* createModAsyncifyNeverUnwindPass();
Pass* createLLVMNonTrappingFPToIntLoweringPass();
Pass* createPoppifyPass();
Pass* createPostEmscriptenPass();
Pass* createPrecomputePass();
Expand Down
3 changes: 3 additions & 0 deletions src/tools/wasm-opt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,9 @@ int main(int argc, const char* argv[]) {
}
}
};
PassRunner runner(&wasm, options.passOptions);
runner.add("llvm-nontrapping-fptoint-lowering");
//runner.run();
runPasses();
if (converge) {
// Keep on running passes to convergence, defined as binary
Expand Down
5 changes: 5 additions & 0 deletions test/lit/help/wasm-metadce.test
Original file line number Diff line number Diff line change
Expand Up @@ -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

;; CHECK-NEXT: fptoint feature
;; CHECK-NEXT:
;; CHECK-NEXT: --local-cse common subexpression elimination
;; CHECK-NEXT: inside basic blocks
;; CHECK-NEXT:
Expand Down
5 changes: 5 additions & 0 deletions test/lit/help/wasm-opt.test
Original file line number Diff line number Diff line change
Expand Up @@ -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

;; CHECK-NEXT: fptoint feature
;; CHECK-NEXT:
;; CHECK-NEXT: --local-cse common subexpression elimination
;; CHECK-NEXT: inside basic blocks
;; CHECK-NEXT:
Expand Down
5 changes: 5 additions & 0 deletions test/lit/help/wasm2js.test
Original file line number Diff line number Diff line change
Expand Up @@ -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

;; CHECK-NEXT: fptoint feature
;; CHECK-NEXT:
;; CHECK-NEXT: --local-cse common subexpression elimination
;; CHECK-NEXT: inside basic blocks
;; CHECK-NEXT:
Expand Down
Loading
Loading