// Copyright 2019 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Flags: --expose-wasm --experimental-wasm-return-call // Reduce the stack size to test that we are indeed doing return calls (instead // of standard calls which consume stack space). // Flags: --stack-size=128 load("test/mjsunit/wasm/wasm-module-builder.js"); (function TestFactorialReturnCall() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); const sig_i_iii = builder.addType(kSig_i_iii); // construct the code for the function // f_aux(N,X) where N=<1 => X // f_aux(N,X) => f_aux(N-1,X*N) let fact_aux = builder.addFunction("fact_aux",kSig_i_ii); fact_aux.addBody([ kExprLocalGet, 0, kExprI32Const, 1, kExprI32LeS, kExprIf, kWasmI32, kExprLocalGet, 1, kExprElse, kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub, kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul, kExprReturnCall, fact_aux.index, kExprEnd ]); //main(N)=>fact_aux(N,1) let main = builder.addFunction("main", kSig_i_i) .addBody([ kExprLocalGet, 0, kExprI32Const, 1, kExprReturnCall,0 ]).exportFunc(); let module = builder.instantiate(); print(" --three--"); assertEquals(6, module.exports.main(3)); print(" --four--"); assertEquals(24, module.exports.main(4)); })(); (function TestIndirectFactorialReturnCall() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); const sig_i_iii = builder.addType(kSig_i_iii); // construct the code for the function // fact(N) => f_ind(N,1,f). // // f_ind(N,X,_) where N=<1 => X // f_ind(N,X,F) => F(N-1,X*N,F). let f_ind = builder.addFunction("f_ind",kSig_i_iii). addBody([ kExprLocalGet, 0, kExprI32Const, 1, kExprI32LeS, kExprIf, kWasmI32, kExprLocalGet, 1, kExprElse, kExprLocalGet, 0, kExprI32Const, 1, kExprI32Sub, kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Mul, kExprLocalGet, 2, kExprLocalGet, 2, kExprReturnCallIndirect, sig_i_iii, kTableZero, kExprEnd ]); //main(N)=>fact_aux(N,1) let main = builder.addFunction("main", kSig_i_i) .addBody([ kExprLocalGet, 0, kExprI32Const, 1, kExprI32Const, f_ind.index, kExprReturnCall, f_ind.index ]).exportFunc(); builder.appendToTable([f_ind.index, main.index]); let module = builder.instantiate(); print(" --three--"); assertEquals(6, module.exports.main(3)); print(" --four--"); assertEquals(24, module.exports.main(4)); })(); (function TestImportReturnCall() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); const sig_i_iii = builder.addType(kSig_i_iii); let pick = builder.addImport("q", "pick", sig_i_iii); let main = builder.addFunction("main", kSig_i_iii) .addBody([ kExprLocalGet, 1, kExprLocalGet, 2, kExprLocalGet, 0, kExprReturnCall, pick ]) .exportFunc(); let module = builder.instantiate({q: { pick: function(a, b, c) { return c ? a : b; }}}); print(" --left--"); assertEquals(-2, module.exports.main(1, -2, 3)); print(" --right--"); assertEquals(3, module.exports.main(0, -2, 3)); })(); (function TestImportIndirectReturnCall() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); const sig_i_iii = builder.addType(kSig_i_iii); let pick = builder.addImport("q", "pick", sig_i_iii); builder.addTable(kWasmAnyFunc, 4); // Arbitrary location in the table. const tableIndex = 3; builder.addElementSegment(0, tableIndex,false,[pick]); let main = builder.addFunction("main", kSig_i_iii) .addBody([ kExprLocalGet, 1, kExprLocalGet, 2, kExprLocalGet, 0, kExprI32Const, tableIndex, kExprReturnCallIndirect, sig_i_iii, kTableZero ]) .exportFunc(); builder.appendToTable([pick, main.index]); let module = builder.instantiate({q: { pick: function(a, b, c) { return c ? a : b; } }}); print(" --left--"); assertEquals(-2, module.exports.main(1, -2, 3)); print(" --right--"); assertEquals(3, module.exports.main(0, -2, 3)); })(); (function TestMultiReturnCallWithLongSig() { print(arguments.callee.name); const callee_inputs = 10; // Tail call from a function with less, as many, or more parameters than the // callee. for (caller_inputs = 9; caller_inputs <= 11; ++caller_inputs) { let builder = new WasmModuleBuilder(); // f just returns its arguments in reverse order. const f_params = new Array(callee_inputs).fill(kWasmI32); const f_returns = f_params; const f_sig = builder.addType(makeSig(f_params, f_returns)); let f_body = []; for (i = 0; i < callee_inputs; ++i) { f_body.push(kExprLocalGet, callee_inputs - i - 1); } const f = builder.addFunction("f", f_sig).addBody(f_body); // Slice or pad the caller inputs to match the callee. const main_params = new Array(caller_inputs).fill(kWasmI32); const main_sig = builder.addType(makeSig(main_params, f_returns)); let main_body = []; for (i = 0; i < callee_inputs; ++i) { main_body.push(kExprLocalGet, Math.min(caller_inputs - 1, i)); } main_body.push(kExprReturnCall, f.index); builder.addFunction("main", main_sig).addBody(main_body).exportFunc(); let module = builder.instantiate(); inputs = []; for (i = 0; i < caller_inputs; ++i) { inputs.push(i); } let expect = inputs.slice(0, callee_inputs); while (expect.length < callee_inputs) { expect.push(inputs[inputs.length - 1]); } expect.reverse(); assertEquals(expect, module.exports.main(...inputs)); } })();