| // 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)); |
| } |
| })(); |