summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rwxr-xr-xbuild-js.sh2
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/FuncCastEmulation.cpp235
-rw-r--r--src/passes/pass.cpp1
-rw-r--r--src/passes/passes.h1
-rw-r--r--src/wasm-builder.h16
-rw-r--r--src/wasm-linker.cpp8
-rw-r--r--src/wasm/CMakeLists.txt2
-rw-r--r--src/wasm/wasm-binary.cpp10
-rw-r--r--src/wasm/wasm-emscripten.cpp (renamed from src/wasm-emscripten.cpp)0
-rw-r--r--src/wasm/wasm-interpreter.cpp (renamed from src/wasm-interpreter.cpp)0
-rw-r--r--src/wasm/wasm-validator.cpp6
-rw-r--r--test/passes/dce.txt6
-rw-r--r--test/passes/dce.wast6
-rw-r--r--test/passes/fpcast-emu.txt294
-rw-r--r--test/passes/fpcast-emu.wast48
17 files changed, 623 insertions, 23 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 433205f44..2c502812e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -172,7 +172,6 @@ INSTALL(FILES bin/binaryen.js DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAM
SET(wasm-shell_SOURCES
src/tools/wasm-shell.cpp
- src/wasm-interpreter.cpp
)
ADD_EXECUTABLE(wasm-shell
${wasm-shell_SOURCES})
@@ -183,11 +182,10 @@ INSTALL(TARGETS wasm-shell DESTINATION ${CMAKE_INSTALL_BINDIR})
SET(wasm-opt_SOURCES
src/tools/wasm-opt.cpp
- src/wasm-interpreter.cpp
)
ADD_EXECUTABLE(wasm-opt
${wasm-opt_SOURCES})
-TARGET_LINK_LIBRARIES(wasm-opt wasm asmjs emscripten-optimizer passes ir cfg support)
+TARGET_LINK_LIBRARIES(wasm-opt wasm asmjs emscripten-optimizer passes ir cfg support wasm)
SET_PROPERTY(TARGET wasm-opt PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET wasm-opt PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS wasm-opt DESTINATION ${CMAKE_INSTALL_BINDIR})
@@ -197,7 +195,7 @@ SET(wasm-merge_SOURCES
)
ADD_EXECUTABLE(wasm-merge
${wasm-merge_SOURCES})
-TARGET_LINK_LIBRARIES(wasm-merge wasm asmjs emscripten-optimizer passes ir cfg support)
+TARGET_LINK_LIBRARIES(wasm-merge wasm asmjs emscripten-optimizer passes ir cfg support wasm)
SET_PROPERTY(TARGET wasm-merge PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET wasm-merge PROPERTY CXX_STANDARD_REQUIRED ON)
INSTALL(TARGETS wasm-merge DESTINATION bin)
@@ -214,7 +212,6 @@ INSTALL(TARGETS wasm-metadce DESTINATION bin)
SET(asm2wasm_SOURCES
src/tools/asm2wasm.cpp
- src/wasm-emscripten.cpp
)
ADD_EXECUTABLE(asm2wasm
${asm2wasm_SOURCES})
@@ -235,7 +232,6 @@ INSTALL(TARGETS wasm2asm DESTINATION ${CMAKE_INSTALL_BINDIR})
SET(s2wasm_SOURCES
src/tools/s2wasm.cpp
- src/wasm-emscripten.cpp
src/wasm-linker.cpp
)
ADD_EXECUTABLE(s2wasm
@@ -247,7 +243,6 @@ INSTALL(TARGETS s2wasm DESTINATION ${CMAKE_INSTALL_BINDIR})
SET(wasm-emscripten-finalize_SOURCES
src/tools/wasm-emscripten-finalize.cpp
- src/wasm-emscripten.cpp
)
ADD_EXECUTABLE(wasm-emscripten-finalize
${wasm-emscripten-finalize_SOURCES})
@@ -290,7 +285,6 @@ IF (UNIX) # TODO: port to windows
SET(wasm-reduce_SOURCES
src/tools/wasm-reduce.cpp
- src/wasm-interpreter.cpp
)
ADD_EXECUTABLE(wasm-reduce
${wasm-reduce_SOURCES})
diff --git a/build-js.sh b/build-js.sh
index 11e2ebe24..2f1c4b92d 100755
--- a/build-js.sh
+++ b/build-js.sh
@@ -134,7 +134,7 @@ echo "building shared bitcode"
$BINARYEN_SRC/wasm/wasm-type.cpp \
$BINARYEN_SRC/wasm/wasm-validator.cpp \
$BINARYEN_SRC/wasm/wasm.cpp \
- $BINARYEN_SRC/wasm-emscripten.cpp \
+ $BINARYEN_SRC/wasm/wasm-emscripten.cpp \
-I$BINARYEN_SRC \
-o shared.bc
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index e5334fff1..d4fff78df 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -8,6 +8,7 @@ SET(passes_SOURCES
DuplicateFunctionElimination.cpp
ExtractFunction.cpp
Flatten.cpp
+ FuncCastEmulation.cpp
Inlining.cpp
LegalizeJSInterface.cpp
LocalCSE.cpp
diff --git a/src/passes/FuncCastEmulation.cpp b/src/passes/FuncCastEmulation.cpp
new file mode 100644
index 000000000..59a2588da
--- /dev/null
+++ b/src/passes/FuncCastEmulation.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Instruments all indirect calls so that they work even if a function
+// pointer was cast incorrectly. For example, if you cast an int (int, float)
+// to an int (int, float, int) and call it natively, on most archs it will
+// happen to work, ignoring the extra param, whereas in wasm it will trap.
+// When porting code that relies on such casts working (like e.g. Python),
+// this pass may be useful. It sets a new "ABI" for indirect calls, in which
+// they all return an i64 and they have a fixed number of i64 params, and
+// the pass converts everything to go through that.
+//
+// This should work even with dynamic linking, however, the number of
+// params must be identical, i.e., the "ABI" must match.
+
+#include <wasm.h>
+#include <wasm-builder.h>
+#include <asm_v_wasm.h>
+#include <pass.h>
+#include <wasm-emscripten.h>
+#include <ir/literal-utils.h>
+
+namespace wasm {
+
+// This should be enough for everybody. (As described above, we need this
+// to match when dynamically linking, and also dynamic linking is why we
+// can't just detect this automatically in the module we see.)
+static const int NUM_PARAMS = 15;
+
+// Converts a value to the ABI type of i64.
+static Expression* toABI(Expression* value, Module* module) {
+ Builder builder(*module);
+ switch (value->type) {
+ case i32: {
+ value = builder.makeUnary(ExtendUInt32, value);
+ break;
+ }
+ case i64: {
+ // already good
+ break;
+ }
+ case f32: {
+ value = builder.makeUnary(
+ ExtendUInt32,
+ builder.makeUnary(ReinterpretFloat32, value)
+ );
+ break;
+ }
+ case f64: {
+ value = builder.makeUnary(ReinterpretFloat64, value);
+ break;
+ }
+ case none: {
+ // the value is none, but we need a value here
+ value = builder.makeSequence(
+ value,
+ LiteralUtils::makeZero(i64, *module)
+ );
+ break;
+ }
+ case unreachable: {
+ // can leave it, the call isn't taken anyhow
+ break;
+ }
+ default: {
+ // SIMD may be interesting some day
+ WASM_UNREACHABLE();
+ }
+ }
+ return value;
+}
+
+// Converts a value from the ABI type of i64 to the expected type
+static Expression* fromABI(Expression* value, Type type, Module* module) {
+ Builder builder(*module);
+ switch (type) {
+ case i32: {
+ value = builder.makeUnary(WrapInt64, value);
+ break;
+ }
+ case i64: {
+ // already good
+ break;
+ }
+ case f32: {
+ value = builder.makeUnary(
+ ReinterpretInt32,
+ builder.makeUnary(WrapInt64, value)
+ );
+ break;
+ }
+ case f64: {
+ value = builder.makeUnary(ReinterpretInt64, value);
+ break;
+ }
+ case none: {
+ value = builder.makeDrop(value);
+ }
+ case unreachable: {
+ // can leave it, the call isn't taken anyhow
+ break;
+ }
+ default: {
+ // SIMD may be interesting some day
+ WASM_UNREACHABLE();
+ }
+ }
+ return value;
+}
+
+struct ParallelFuncCastEmulation : public WalkerPass<PostWalker<ParallelFuncCastEmulation>> {
+ bool isFunctionParallel() override { return true; }
+
+ Pass* create() override { return new ParallelFuncCastEmulation(ABIType); }
+
+ ParallelFuncCastEmulation(Name ABIType) : ABIType(ABIType) {}
+
+ void visitCallIndirect(CallIndirect* curr) {
+ if (curr->operands.size() > NUM_PARAMS) {
+ Fatal() << "FuncCastEmulation::NUM_PARAMS needs to be at least " <<
+ curr->operands.size();
+ }
+ for (Expression*& operand : curr->operands) {
+ operand = toABI(operand, getModule());
+ }
+ // Add extra operands as needed.
+ while (curr->operands.size() < NUM_PARAMS) {
+ curr->operands.push_back(LiteralUtils::makeZero(i64, *getModule()));
+ }
+ // Set the new types
+ auto oldType = curr->type;
+ curr->type = i64;
+ curr->fullType = ABIType;
+ // Fix up return value
+ replaceCurrent(fromABI(curr, oldType, getModule()));
+ }
+
+private:
+ // the name of a type for a call with the right params and return
+ Name ABIType;
+};
+
+struct FuncCastEmulation : public Pass {
+ void run(PassRunner* runner, Module* module) override {
+ // we just need the one ABI function type for all indirect calls
+ std::string sig = "j";
+ for (Index i = 0; i < NUM_PARAMS; i++) {
+ sig += 'j';
+ }
+ ABIType = ensureFunctionType(sig, module)->name;
+ // Add a way for JS to call into the table (as our i64 ABI means an i64
+ // is returned when there is a return value, which JS engines will fail on),
+ // using dynCalls
+ EmscriptenGlueGenerator generator(*module);
+ generator.generateDynCallThunks();
+ // Add a thunk for each function in the table, and do the call through it.
+ std::unordered_map<Name, Name> funcThunks;
+ for (auto& segment : module->table.segments) {
+ for (auto& name : segment.data) {
+ auto iter = funcThunks.find(name);
+ if (iter == funcThunks.end()) {
+ auto thunk = makeThunk(name, module);
+ funcThunks[name] = thunk;
+ name = thunk;
+ } else {
+ name = iter->second;
+ }
+ }
+ }
+ // update call_indirects
+ PassRunner subRunner(module, runner->options);
+ subRunner.setIsNested(true);
+ subRunner.add<ParallelFuncCastEmulation>(ABIType);
+ subRunner.run();
+ }
+
+private:
+ // the name of a type for a call with the right params and return
+ Name ABIType;
+
+ // Creates a thunk for a function, casting args and return value as needed.
+ Name makeThunk(Name name, Module* module) {
+ Name thunk = std::string("byn$fpcast-emu$") + name.str;
+ if (module->getFunctionOrNull(thunk)) {
+ Fatal() << "FuncCastEmulation::makeThunk seems a thunk name already in use. Was the pass already run on this code?";
+ }
+ // The item in the table may be a function or a function import.
+ auto* func = module->getFunctionOrNull(name);
+ Import* imp = nullptr;
+ if (!func) imp = module->getImport(name);
+ std::vector<Type>& params = func ? func->params : module->getFunctionType(imp->functionType)->params;
+ Type type = func ? func->result : module->getFunctionType(imp->functionType)->result;
+ Builder builder(*module);
+ std::vector<Expression*> callOperands;
+ for (Index i = 0; i < params.size(); i++) {
+ callOperands.push_back(fromABI(builder.makeGetLocal(i, i64), params[i], module));
+ }
+ Expression* call = func ? (Expression*)builder.makeCall(name, callOperands, type)
+ : (Expression*)builder.makeCallImport(name, callOperands, type);
+ std::vector<Type> thunkParams;
+ for (Index i = 0; i < NUM_PARAMS; i++) {
+ thunkParams.push_back(i64);
+ }
+ auto* thunkFunc = builder.makeFunction(
+ thunk,
+ std::move(thunkParams),
+ i64,
+ {}, // no vars
+ toABI(call, module)
+ );
+ thunkFunc->type = ABIType;
+ module->addFunction(thunkFunc);
+ return thunk;
+ }
+};
+
+Pass* createFuncCastEmulationPass() {
+ return new FuncCastEmulation();
+}
+
+} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 37c59f570..6518a6f3d 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -74,6 +74,7 @@ void PassRegistry::registerPasses() {
registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass);
registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass);
registerPass("flatten", "flattens out code, removing nesting", createFlattenPass);
+ registerPass("fpcast-emu", "emulates function pointer casts, allowing incorrect indirect calls to (sometimes) work", createFuncCastEmulationPass);
registerPass("func-metrics", "reports function metrics", createFunctionMetricsPass);
registerPass("inlining", "inline functions (you probably want inlining-optimizing)", createInliningPass);
registerPass("inlining-optimizing", "inline functions and optimizes where we inlined", createInliningOptimizingPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index 230cdfd86..2eaf1049e 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -31,6 +31,7 @@ Pass* createDeadCodeEliminationPass();
Pass* createDuplicateFunctionEliminationPass();
Pass* createExtractFunctionPass();
Pass* createFlattenPass();
+Pass* createFuncCastEmulationPass();
Pass* createFullPrinterPass();
Pass* createFunctionMetricsPass();
Pass* createI64ToI32LoweringPass();
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index 94699df7e..02304acec 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -43,6 +43,20 @@ public:
// make* functions, create nodes
Function* makeFunction(Name name,
+ std::vector<Type>&& params,
+ Type resultType,
+ std::vector<Type>&& vars,
+ Expression* body = nullptr) {
+ auto* func = new Function;
+ func->name = name;
+ func->result = resultType;
+ func->body = body;
+ func->params.swap(params);
+ func->vars.swap(vars);
+ return func;
+ }
+
+ Function* makeFunction(Name name,
std::vector<NameType>&& params,
Type resultType,
std::vector<NameType>&& vars,
@@ -51,7 +65,6 @@ public:
func->name = name;
func->result = resultType;
func->body = body;
-
for (auto& param : params) {
func->params.push_back(param.type);
Index index = func->localNames.size();
@@ -64,7 +77,6 @@ public:
func->localIndices[var.name] = index;
func->localNames[index] = var.name;
}
-
return func;
}
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
index c284de81f..df51d85c6 100644
--- a/src/wasm-linker.cpp
+++ b/src/wasm-linker.cpp
@@ -382,7 +382,13 @@ void Linker::makeDummyFunction() {
if (!create) return;
wasm::Builder wasmBuilder(out.wasm);
Expression *unreachable = wasmBuilder.makeUnreachable();
- Function *dummy = wasmBuilder.makeFunction(Name(dummyFunction), {}, Type::none, {}, unreachable);
+ Function *dummy = wasmBuilder.makeFunction(
+ Name(dummyFunction),
+ std::vector<Type>{},
+ Type::none,
+ std::vector<Type>{},
+ unreachable
+ );
out.wasm.addFunction(dummy);
getFunctionIndex(dummy->name);
}
diff --git a/src/wasm/CMakeLists.txt b/src/wasm/CMakeLists.txt
index 1a8a9b8ba..da876b56f 100644
--- a/src/wasm/CMakeLists.txt
+++ b/src/wasm/CMakeLists.txt
@@ -2,6 +2,8 @@ SET(wasm_SOURCES
literal.cpp
wasm.cpp
wasm-binary.cpp
+ wasm-emscripten.cpp
+ wasm-interpreter.cpp
wasm-io.cpp
wasm-s-parser.cpp
wasm-type.cpp
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 56b35b712..69e939c44 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -1730,11 +1730,11 @@ void WasmBinaryBuilder::readFunctions() {
}
}
auto func = Builder(wasm).makeFunction(
- Name::fromInt(i),
- std::move(params),
- type->result,
- std::move(vars)
- );
+ Name::fromInt(i),
+ std::move(params),
+ type->result,
+ std::move(vars)
+ );
func->type = type->name;
currFunction = func;
{
diff --git a/src/wasm-emscripten.cpp b/src/wasm/wasm-emscripten.cpp
index 9a393db43..9a393db43 100644
--- a/src/wasm-emscripten.cpp
+++ b/src/wasm/wasm-emscripten.cpp
diff --git a/src/wasm-interpreter.cpp b/src/wasm/wasm-interpreter.cpp
index e7df785ac..e7df785ac 100644
--- a/src/wasm-interpreter.cpp
+++ b/src/wasm/wasm-interpreter.cpp
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 8ff2fb9b8..d6c6e7bd0 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -806,6 +806,12 @@ void FunctionValidator::visitFunction(Function* curr) {
shouldBeTrue(breakTargets.empty(), curr->body, "all named break targets must exist");
returnType = unreachable;
labelNames.clear();
+ // if function has a named type, it must match up with the function's params and result
+ if (info.validateGlobally && curr->type.is()) {
+ auto* ft = getModule()->getFunctionType(curr->type);
+ shouldBeTrue(ft->params == curr->params, curr->name, "function params must match its declared type");
+ shouldBeTrue(ft->result == curr->result, curr->name, "function result must match its declared type");
+ }
// expressions must not be seen more than once
struct Walker : public PostWalker<Walker, UnifiedExpressionVisitor<Walker>> {
std::unordered_set<Expression*>& seen;
diff --git a/test/passes/dce.txt b/test/passes/dce.txt
index 6f2422b2a..b2976432b 100644
--- a/test/passes/dce.txt
+++ b/test/passes/dce.txt
@@ -322,7 +322,7 @@
(unreachable)
)
)
- (func $unreachable-block-ends-br_if (; 11 ;) (type $1) (result i32)
+ (func $unreachable-block-ends-br_if (; 11 ;) (type $2) (result i32)
(block $label$2
(nop)
(unreachable)
@@ -377,13 +377,13 @@
)
)
)
- (func $br-gone-means-block-type-changes-then-refinalize-at-end-is-too-late (; 15 ;) (type $1) (param $var$0 i32) (result i32)
+ (func $br-gone-means-block-type-changes-then-refinalize-at-end-is-too-late (; 15 ;) (type $3) (param $var$0 i32) (result i32)
(block $block
(nop)
(unreachable)
)
)
- (func $br-with-unreachable-value-should-not-give-a-block-a-value (; 16 ;) (type $1) (param $var$0 i32) (result i32)
+ (func $br-with-unreachable-value-should-not-give-a-block-a-value (; 16 ;) (type $3) (param $var$0 i32) (result i32)
(block $label$0 (result i32)
(block $block
(drop
diff --git a/test/passes/dce.wast b/test/passes/dce.wast
index 93c98ce4d..18f7db012 100644
--- a/test/passes/dce.wast
+++ b/test/passes/dce.wast
@@ -466,7 +466,7 @@
(i32.const 19)
)
)
- (func $unreachable-block-ends-br_if (type $1) (result i32)
+ (func $unreachable-block-ends-br_if (result i32)
(block $label$0 (result i32)
(block $label$2
(nop)
@@ -538,7 +538,7 @@
)
)
)
- (func $br-gone-means-block-type-changes-then-refinalize-at-end-is-too-late (type $1) (param $var$0 i32) (result i32)
+ (func $br-gone-means-block-type-changes-then-refinalize-at-end-is-too-late (param $var$0 i32) (result i32)
(block $label$0 (result i32)
(br $label$0
(block (result i32)
@@ -554,7 +554,7 @@
)
)
)
- (func $br-with-unreachable-value-should-not-give-a-block-a-value (type $1) (param $var$0 i32) (result i32)
+ (func $br-with-unreachable-value-should-not-give-a-block-a-value (param $var$0 i32) (result i32)
(block $label$0 (result i32)
(br $label$0
(block (result i32) ;; turns into unreachable when refinalized
diff --git a/test/passes/fpcast-emu.txt b/test/passes/fpcast-emu.txt
new file mode 100644
index 000000000..94b92ad17
--- /dev/null
+++ b/test/passes/fpcast-emu.txt
@@ -0,0 +1,294 @@
+(module
+ (type $vijfd (func (param i32 i64 f32 f64)))
+ (type $jii (func (param i32 i32) (result i64)))
+ (type $fjj (func (param i64 i64) (result f32)))
+ (type $dff (func (param f32 f32) (result f64)))
+ (type $idd (func (param f64 f64) (result i32)))
+ (type $FUNCSIG$fijfd (func (param i32 i64 f32 f64) (result f32)))
+ (type $FUNCSIG$jjjjjjjjjjjjjjjj (func (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64) (result i64)))
+ (type $FUNCSIG$vijfd (func (param i32 i64 f32 f64)))
+ (type $FUNCSIG$jii (func (param i32 i32) (result i64)))
+ (type $FUNCSIG$fjj (func (param i64 i64) (result f32)))
+ (type $FUNCSIG$dff (func (param f32 f32) (result f64)))
+ (type $FUNCSIG$idd (func (param f64 f64) (result i32)))
+ (import "env" "imported_func" (func $imported-func (param i32 i64 f32 f64) (result f32)))
+ (table 10 10 anyfunc)
+ (elem (i32.const 0) $byn$fpcast-emu$a $byn$fpcast-emu$b $byn$fpcast-emu$c $byn$fpcast-emu$d $byn$fpcast-emu$e $byn$fpcast-emu$e $byn$fpcast-emu$imported-func)
+ (export "dynCall_dff" (func $dynCall_dff))
+ (export "dynCall_idd" (func $dynCall_idd))
+ (func $a (; 1 ;) (type $vijfd) (param $x i32) (param $y i64) (param $z f32) (param $w f64)
+ (drop
+ (call_indirect (type $FUNCSIG$jjjjjjjjjjjjjjjj)
+ (i64.extend_u/i32
+ (i32.const 1)
+ )
+ (i64.const 2)
+ (i64.extend_u/i32
+ (i32.reinterpret/f32
+ (f32.const 3)
+ )
+ )
+ (i64.reinterpret/f64
+ (f64.const 4)
+ )
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i32.const 1337)
+ )
+ )
+ )
+ (func $b (; 2 ;) (type $jii) (param $x i32) (param $y i32) (result i64)
+ (call_indirect (type $FUNCSIG$jjjjjjjjjjjjjjjj)
+ (i64.extend_u/i32
+ (i32.const 1)
+ )
+ (i64.extend_u/i32
+ (i32.const 2)
+ )
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i32.const 1337)
+ )
+ )
+ (func $c (; 3 ;) (type $fjj) (param $x i64) (param $y i64) (result f32)
+ (f32.reinterpret/i32
+ (i32.wrap/i64
+ (call_indirect (type $FUNCSIG$jjjjjjjjjjjjjjjj)
+ (i64.const 1)
+ (i64.const 2)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i32.const 1337)
+ )
+ )
+ )
+ )
+ (func $d (; 4 ;) (type $dff) (param $x f32) (param $y f32) (result f64)
+ (f64.reinterpret/i64
+ (call_indirect (type $FUNCSIG$jjjjjjjjjjjjjjjj)
+ (i64.extend_u/i32
+ (i32.reinterpret/f32
+ (f32.const 1)
+ )
+ )
+ (i64.extend_u/i32
+ (i32.reinterpret/f32
+ (f32.const 2)
+ )
+ )
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i32.const 1337)
+ )
+ )
+ )
+ (func $e (; 5 ;) (type $idd) (param $x f64) (param $y f64) (result i32)
+ (i32.wrap/i64
+ (call_indirect (type $FUNCSIG$jjjjjjjjjjjjjjjj)
+ (i64.reinterpret/f64
+ (f64.const 1)
+ )
+ (i64.reinterpret/f64
+ (f64.const 2)
+ )
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i32.const 1337)
+ )
+ )
+ )
+ (func $dynCall_dff (; 6 ;) (param $fptr i32) (param $0 f32) (param $1 f32) (result f64)
+ (f64.reinterpret/i64
+ (call_indirect (type $FUNCSIG$jjjjjjjjjjjjjjjj)
+ (i64.extend_u/i32
+ (i32.reinterpret/f32
+ (get_local $0)
+ )
+ )
+ (i64.extend_u/i32
+ (i32.reinterpret/f32
+ (get_local $1)
+ )
+ )
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (get_local $fptr)
+ )
+ )
+ )
+ (func $dynCall_idd (; 7 ;) (param $fptr i32) (param $0 f64) (param $1 f64) (result i32)
+ (i32.wrap/i64
+ (call_indirect (type $FUNCSIG$jjjjjjjjjjjjjjjj)
+ (i64.reinterpret/f64
+ (get_local $0)
+ )
+ (i64.reinterpret/f64
+ (get_local $1)
+ )
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (i64.const 0)
+ (get_local $fptr)
+ )
+ )
+ )
+ (func $byn$fpcast-emu$a (; 8 ;) (type $FUNCSIG$jjjjjjjjjjjjjjjj) (param $0 i64) (param $1 i64) (param $2 i64) (param $3 i64) (param $4 i64) (param $5 i64) (param $6 i64) (param $7 i64) (param $8 i64) (param $9 i64) (param $10 i64) (param $11 i64) (param $12 i64) (param $13 i64) (param $14 i64) (result i64)
+ (call $a
+ (i32.wrap/i64
+ (get_local $0)
+ )
+ (get_local $1)
+ (f32.reinterpret/i32
+ (i32.wrap/i64
+ (get_local $2)
+ )
+ )
+ (f64.reinterpret/i64
+ (get_local $3)
+ )
+ )
+ (i64.const 0)
+ )
+ (func $byn$fpcast-emu$b (; 9 ;) (type $FUNCSIG$jjjjjjjjjjjjjjjj) (param $0 i64) (param $1 i64) (param $2 i64) (param $3 i64) (param $4 i64) (param $5 i64) (param $6 i64) (param $7 i64) (param $8 i64) (param $9 i64) (param $10 i64) (param $11 i64) (param $12 i64) (param $13 i64) (param $14 i64) (result i64)
+ (call $b
+ (i32.wrap/i64
+ (get_local $0)
+ )
+ (i32.wrap/i64
+ (get_local $1)
+ )
+ )
+ )
+ (func $byn$fpcast-emu$c (; 10 ;) (type $FUNCSIG$jjjjjjjjjjjjjjjj) (param $0 i64) (param $1 i64) (param $2 i64) (param $3 i64) (param $4 i64) (param $5 i64) (param $6 i64) (param $7 i64) (param $8 i64) (param $9 i64) (param $10 i64) (param $11 i64) (param $12 i64) (param $13 i64) (param $14 i64) (result i64)
+ (i64.extend_u/i32
+ (i32.reinterpret/f32
+ (call $c
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ )
+ (func $byn$fpcast-emu$d (; 11 ;) (type $FUNCSIG$jjjjjjjjjjjjjjjj) (param $0 i64) (param $1 i64) (param $2 i64) (param $3 i64) (param $4 i64) (param $5 i64) (param $6 i64) (param $7 i64) (param $8 i64) (param $9 i64) (param $10 i64) (param $11 i64) (param $12 i64) (param $13 i64) (param $14 i64) (result i64)
+ (i64.reinterpret/f64
+ (call $d
+ (f32.reinterpret/i32
+ (i32.wrap/i64
+ (get_local $0)
+ )
+ )
+ (f32.reinterpret/i32
+ (i32.wrap/i64
+ (get_local $1)
+ )
+ )
+ )
+ )
+ )
+ (func $byn$fpcast-emu$e (; 12 ;) (type $FUNCSIG$jjjjjjjjjjjjjjjj) (param $0 i64) (param $1 i64) (param $2 i64) (param $3 i64) (param $4 i64) (param $5 i64) (param $6 i64) (param $7 i64) (param $8 i64) (param $9 i64) (param $10 i64) (param $11 i64) (param $12 i64) (param $13 i64) (param $14 i64) (result i64)
+ (i64.extend_u/i32
+ (call $e
+ (f64.reinterpret/i64
+ (get_local $0)
+ )
+ (f64.reinterpret/i64
+ (get_local $1)
+ )
+ )
+ )
+ )
+ (func $byn$fpcast-emu$imported-func (; 13 ;) (type $FUNCSIG$jjjjjjjjjjjjjjjj) (param $0 i64) (param $1 i64) (param $2 i64) (param $3 i64) (param $4 i64) (param $5 i64) (param $6 i64) (param $7 i64) (param $8 i64) (param $9 i64) (param $10 i64) (param $11 i64) (param $12 i64) (param $13 i64) (param $14 i64) (result i64)
+ (i64.extend_u/i32
+ (i32.reinterpret/f32
+ (call $imported-func
+ (i32.wrap/i64
+ (get_local $0)
+ )
+ (get_local $1)
+ (f32.reinterpret/i32
+ (i32.wrap/i64
+ (get_local $2)
+ )
+ )
+ (f64.reinterpret/i64
+ (get_local $3)
+ )
+ )
+ )
+ )
+ )
+)
diff --git a/test/passes/fpcast-emu.wast b/test/passes/fpcast-emu.wast
new file mode 100644
index 000000000..cef0d0deb
--- /dev/null
+++ b/test/passes/fpcast-emu.wast
@@ -0,0 +1,48 @@
+(module
+ (type $vijfd (func (param i32) (param i64) (param f32) (param f64)))
+ (type $jii (func (param i32) (param i32) (result i64)))
+ (type $fjj (func (param i64) (param i64) (result f32)))
+ (type $dff (func (param f32) (param f32) (result f64)))
+ (type $idd (func (param f64) (param f64) (result i32)))
+ (import "env" "imported_func" (func $imported-func (param i32 i64 f32 f64) (result f32)))
+ (table 10 10 anyfunc)
+ (elem (i32.const 0) $a $b $c $d $e $e $imported-func)
+ (func $a (param $x i32) (param $y i64) (param $z f32) (param $w f64)
+ (call_indirect (type $vijfd)
+ (i32.const 1)
+ (i64.const 2)
+ (f32.const 3)
+ (f64.const 4)
+ (i32.const 1337)
+ )
+ )
+ (func $b (param $x i32) (param $y i32) (result i64)
+ (call_indirect (type $jii)
+ (i32.const 1)
+ (i32.const 2)
+ (i32.const 1337)
+ )
+ )
+ (func $c (param $x i64) (param $y i64) (result f32)
+ (call_indirect (type $fjj)
+ (i64.const 1)
+ (i64.const 2)
+ (i32.const 1337)
+ )
+ )
+ (func $d (param $x f32) (param $y f32) (result f64)
+ (call_indirect (type $dff)
+ (f32.const 1)
+ (f32.const 2)
+ (i32.const 1337)
+ )
+ )
+ (func $e (param $x f64) (param $y f64) (result i32)
+ (call_indirect (type $idd)
+ (f64.const 1)
+ (f64.const 2)
+ (i32.const 1337)
+ )
+ )
+)
+