Skip to content

Commit f6ca382

Browse files
authored
Tail call C/JS API (#2223)
1 parent c7e9271 commit f6ca382

10 files changed

Lines changed: 261 additions & 73 deletions

File tree

build-js.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,8 @@ export_function "_BinaryenBreak"
542542
export_function "_BinaryenSwitch"
543543
export_function "_BinaryenCall"
544544
export_function "_BinaryenCallIndirect"
545+
export_function "_BinaryenReturnCall"
546+
export_function "_BinaryenReturnCallIndirect"
545547
export_function "_BinaryenLocalGet"
546548
export_function "_BinaryenLocalSet"
547549
export_function "_BinaryenLocalTee"

scripts/fuzz_opt.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@
3333
# simd: known issues with d8
3434
# atomics, bulk memory: doesn't work in wasm2js
3535
# truncsat: https://github.com/WebAssembly/binaryen/issues/2198
36+
# tail-call: WIP
3637
CONSTANT_FEATURE_OPTS = ['--all-features']
3738

3839
# possible feature options that are sometimes passed to the tools.
39-
POSSIBLE_FEATURE_OPTS = ['--disable-exception-handling', '--disable-simd', '--disable-threads', '--disable-bulk-memory', '--disable-nontrapping-float-to-int']
40+
POSSIBLE_FEATURE_OPTS = ['--disable-exception-handling', '--disable-simd', '--disable-threads', '--disable-bulk-memory', '--disable-nontrapping-float-to-int', '--disable-tail-call']
4041

4142
FUZZ_OPTS = []
4243

src/binaryen-c.cpp

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -977,11 +977,12 @@ BinaryenExpressionRef BinaryenSwitch(BinaryenModuleRef module,
977977
ret->finalize();
978978
return static_cast<Expression*>(ret);
979979
}
980-
BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module,
981-
const char* target,
982-
BinaryenExpressionRef* operands,
983-
BinaryenIndex numOperands,
984-
BinaryenType returnType) {
980+
static BinaryenExpressionRef makeBinaryenCall(BinaryenModuleRef module,
981+
const char* target,
982+
BinaryenExpressionRef* operands,
983+
BinaryenIndex numOperands,
984+
BinaryenType returnType,
985+
bool isReturn) {
985986
auto* ret = ((Module*)module)->allocator.alloc<Call>();
986987

987988
if (tracing) {
@@ -999,7 +1000,7 @@ BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module,
9991000
}
10001001
std::cout << " };\n ";
10011002
traceExpression(ret,
1002-
"BinaryenCall",
1003+
(isReturn ? "BinaryenReturnCall" : "BinaryenCall"),
10031004
StringLit(target),
10041005
"operands",
10051006
numOperands,
@@ -1012,14 +1013,33 @@ BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module,
10121013
ret->operands.push_back((Expression*)operands[i]);
10131014
}
10141015
ret->type = Type(returnType);
1016+
ret->isReturn = isReturn;
10151017
ret->finalize();
10161018
return static_cast<Expression*>(ret);
10171019
}
1018-
BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module,
1019-
BinaryenExpressionRef target,
1020-
BinaryenExpressionRef* operands,
1021-
BinaryenIndex numOperands,
1022-
const char* type) {
1020+
BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module,
1021+
const char* target,
1022+
BinaryenExpressionRef* operands,
1023+
BinaryenIndex numOperands,
1024+
BinaryenType returnType) {
1025+
return makeBinaryenCall(
1026+
module, target, operands, numOperands, returnType, false);
1027+
}
1028+
BinaryenExpressionRef BinaryenReturnCall(BinaryenModuleRef module,
1029+
const char* target,
1030+
BinaryenExpressionRef* operands,
1031+
BinaryenIndex numOperands,
1032+
BinaryenType returnType) {
1033+
return makeBinaryenCall(
1034+
module, target, operands, numOperands, returnType, true);
1035+
}
1036+
static BinaryenExpressionRef
1037+
makeBinaryenCallIndirect(BinaryenModuleRef module,
1038+
BinaryenExpressionRef target,
1039+
BinaryenExpressionRef* operands,
1040+
BinaryenIndex numOperands,
1041+
const char* type,
1042+
bool isReturn) {
10231043
auto* wasm = (Module*)module;
10241044
auto* ret = wasm->allocator.alloc<CallIndirect>();
10251045

@@ -1037,12 +1057,13 @@ BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module,
10371057
std::cout << "0";
10381058
}
10391059
std::cout << " };\n ";
1040-
traceExpression(ret,
1041-
"BinaryenCallIndirect",
1042-
target,
1043-
"operands",
1044-
numOperands,
1045-
StringLit(type));
1060+
traceExpression(
1061+
ret,
1062+
(isReturn ? "BinaryenReturnCallIndirect" : "BinaryenCallIndirect"),
1063+
target,
1064+
"operands",
1065+
numOperands,
1066+
StringLit(type));
10461067
std::cout << " }\n";
10471068
}
10481069

@@ -1052,9 +1073,27 @@ BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module,
10521073
}
10531074
ret->fullType = type;
10541075
ret->type = wasm->getFunctionType(ret->fullType)->result;
1076+
ret->isReturn = isReturn;
10551077
ret->finalize();
10561078
return static_cast<Expression*>(ret);
10571079
}
1080+
BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module,
1081+
BinaryenExpressionRef target,
1082+
BinaryenExpressionRef* operands,
1083+
BinaryenIndex numOperands,
1084+
const char* type) {
1085+
return makeBinaryenCallIndirect(
1086+
module, target, operands, numOperands, type, false);
1087+
}
1088+
BinaryenExpressionRef
1089+
BinaryenReturnCallIndirect(BinaryenModuleRef module,
1090+
BinaryenExpressionRef target,
1091+
BinaryenExpressionRef* operands,
1092+
BinaryenIndex numOperands,
1093+
const char* type) {
1094+
return makeBinaryenCallIndirect(
1095+
module, target, operands, numOperands, type, true);
1096+
}
10581097
BinaryenExpressionRef BinaryenLocalGet(BinaryenModuleRef module,
10591098
BinaryenIndex index,
10601099
BinaryenType type) {

src/binaryen-c.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,18 @@ BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module,
545545
BinaryenExpressionRef* operands,
546546
BinaryenIndex numOperands,
547547
const char* type);
548+
BinaryenExpressionRef BinaryenReturnCall(BinaryenModuleRef module,
549+
const char* target,
550+
BinaryenExpressionRef* operands,
551+
BinaryenIndex numOperands,
552+
BinaryenType returnType);
553+
BinaryenExpressionRef
554+
BinaryenReturnCallIndirect(BinaryenModuleRef module,
555+
BinaryenExpressionRef target,
556+
BinaryenExpressionRef* operands,
557+
BinaryenIndex numOperands,
558+
const char* type);
559+
548560
// LocalGet: Note the 'type' parameter. It might seem redundant, since the
549561
// local at that index must have a type. However, this API lets you
550562
// build code "top-down": create a node, then its parents, and so

src/js/binaryen.js-post.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,16 @@ function wrapModule(module, self) {
450450
return Module['_BinaryenCallIndirect'](module, target, i32sToStack(operands), operands.length, strToStack(type));
451451
});
452452
};
453+
self['returnCall'] = function(name, operands, type) {
454+
return preserveStack(function() {
455+
return Module['_BinaryenReturnCall'](module, strToStack(name), i32sToStack(operands), operands.length, type);
456+
});
457+
};
458+
self['returnCallIndirect'] = function(target, operands, type) {
459+
return preserveStack(function() {
460+
return Module['_BinaryenReturnCallIndirect'](module, target, i32sToStack(operands), operands.length, strToStack(type));
461+
});
462+
};
453463

454464
self['local'] = {
455465
'get': function(index, type) {

test/binaryen.js/kitchen-sink.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,9 @@ function test_core() {
396396
module.i64.store(2, 4, temp15, temp16),
397397
module.select(temp10, temp11, temp12),
398398
module.return(makeInt32(1337)),
399+
// Tail Call
400+
module.returnCall("kitchen()sinker", [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], Binaryen.i32),
401+
module.returnCallIndirect(makeInt32(2449), [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], "iiIfF"),
399402
// TODO: Host
400403
module.nop(),
401404
module.unreachable(),

test/binaryen.js/kitchen-sink.js.txt

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,23 @@ getExpressionInfo(f64.const)={"id":14,"type":4,"value":9.5}
14161416
(return
14171417
(i32.const 1337)
14181418
)
1419+
(drop
1420+
(return_call "$kitchen()sinker"
1421+
(i32.const 13)
1422+
(i64.const 37)
1423+
(f32.const 1.2999999523162842)
1424+
(f64.const 3.7)
1425+
)
1426+
)
1427+
(drop
1428+
(return_call_indirect (type $iiIfF)
1429+
(i32.const 13)
1430+
(i64.const 37)
1431+
(f32.const 1.2999999523162842)
1432+
(f64.const 3.7)
1433+
(i32.const 2449)
1434+
)
1435+
)
14191436
(nop)
14201437
(unreachable)
14211438
)
@@ -3256,8 +3273,25 @@ int main() {
32563273
expressions[642] = BinaryenSelect(the_module, expressions[16], expressions[17], expressions[18]);
32573274
expressions[643] = BinaryenConst(the_module, BinaryenLiteralInt32(1337));
32583275
expressions[644] = BinaryenReturn(the_module, expressions[643]);
3259-
expressions[645] = BinaryenNop(the_module);
3260-
expressions[646] = BinaryenUnreachable(the_module);
3276+
expressions[645] = BinaryenConst(the_module, BinaryenLiteralInt32(13));
3277+
expressions[646] = BinaryenConst(the_module, BinaryenLiteralInt64(37));
3278+
expressions[647] = BinaryenConst(the_module, BinaryenLiteralFloat32(1.3));
3279+
expressions[648] = BinaryenConst(the_module, BinaryenLiteralFloat64(3.7));
3280+
{
3281+
BinaryenExpressionRef operands[] = { expressions[645], expressions[646], expressions[647], expressions[648] };
3282+
expressions[649] = BinaryenReturnCall(the_module, "kitchen()sinker", operands, 4, 1);
3283+
}
3284+
expressions[650] = BinaryenConst(the_module, BinaryenLiteralInt32(2449));
3285+
expressions[651] = BinaryenConst(the_module, BinaryenLiteralInt32(13));
3286+
expressions[652] = BinaryenConst(the_module, BinaryenLiteralInt64(37));
3287+
expressions[653] = BinaryenConst(the_module, BinaryenLiteralFloat32(1.3));
3288+
expressions[654] = BinaryenConst(the_module, BinaryenLiteralFloat64(3.7));
3289+
{
3290+
BinaryenExpressionRef operands[] = { expressions[651], expressions[652], expressions[653], expressions[654] };
3291+
expressions[655] = BinaryenReturnCallIndirect(the_module, expressions[650], operands, 4, "iiIfF");
3292+
}
3293+
expressions[656] = BinaryenNop(the_module);
3294+
expressions[657] = BinaryenUnreachable(the_module);
32613295
BinaryenExpressionGetId(expressions[30]);
32623296
BinaryenExpressionGetType(expressions[30]);
32633297
BinaryenUnaryGetOp(expressions[30]);
@@ -3268,26 +3302,26 @@ getExpressionInfo={"id":15,"type":3,"op":6}
32683302
(f32.const -33.61199951171875)
32693303
)
32703304

3271-
expressions[647] = BinaryenConst(the_module, BinaryenLiteralInt32(5));
3272-
BinaryenExpressionGetId(expressions[647]);
3273-
BinaryenExpressionGetType(expressions[647]);
3274-
BinaryenConstGetValueI32(expressions[647]);
3305+
expressions[658] = BinaryenConst(the_module, BinaryenLiteralInt32(5));
3306+
BinaryenExpressionGetId(expressions[658]);
3307+
BinaryenExpressionGetType(expressions[658]);
3308+
BinaryenConstGetValueI32(expressions[658]);
32753309
getExpressionInfo(i32.const)={"id":14,"type":1,"value":5}
3276-
expressions[648] = BinaryenConst(the_module, BinaryenLiteralInt64(30064771078));
3277-
BinaryenExpressionGetId(expressions[648]);
3278-
BinaryenExpressionGetType(expressions[648]);
3279-
BinaryenConstGetValueI64Low(expressions[648]);
3280-
BinaryenConstGetValueI64High(expressions[648]);
3310+
expressions[659] = BinaryenConst(the_module, BinaryenLiteralInt64(30064771078));
3311+
BinaryenExpressionGetId(expressions[659]);
3312+
BinaryenExpressionGetType(expressions[659]);
3313+
BinaryenConstGetValueI64Low(expressions[659]);
3314+
BinaryenConstGetValueI64High(expressions[659]);
32813315
getExpressionInfo(i64.const)={"id":14,"type":2,"value":{"low":6,"high":7}}
3282-
expressions[649] = BinaryenConst(the_module, BinaryenLiteralFloat32(8.5));
3283-
BinaryenExpressionGetId(expressions[649]);
3284-
BinaryenExpressionGetType(expressions[649]);
3285-
BinaryenConstGetValueF32(expressions[649]);
3316+
expressions[660] = BinaryenConst(the_module, BinaryenLiteralFloat32(8.5));
3317+
BinaryenExpressionGetId(expressions[660]);
3318+
BinaryenExpressionGetType(expressions[660]);
3319+
BinaryenConstGetValueF32(expressions[660]);
32863320
getExpressionInfo(f32.const)={"id":14,"type":3,"value":8.5}
3287-
expressions[650] = BinaryenConst(the_module, BinaryenLiteralFloat64(9.5));
3288-
BinaryenExpressionGetId(expressions[650]);
3289-
BinaryenExpressionGetType(expressions[650]);
3290-
BinaryenConstGetValueF64(expressions[650]);
3321+
expressions[661] = BinaryenConst(the_module, BinaryenLiteralFloat64(9.5));
3322+
BinaryenExpressionGetId(expressions[661]);
3323+
BinaryenExpressionGetType(expressions[661]);
3324+
BinaryenConstGetValueF64(expressions[661]);
32913325
getExpressionInfo(f64.const)={"id":14,"type":4,"value":9.5}
32923326
{
32933327
BinaryenExpressionRef children[] = { expressions[24], expressions[26], expressions[28], expressions[30], expressions[32],
@@ -3330,25 +3364,26 @@ getExpressionInfo(f64.const)={"id":14,"type":4,"value":9.5}
33303364
expressions[597], expressions[598], expressions[600], expressions[602], expressions[603], expressions[604],
33313365
expressions[606], expressions[612], expressions[617], expressions[624], expressions[626], expressions[628],
33323366
expressions[631], expressions[633], expressions[635], expressions[637], expressions[639], expressions[640],
3333-
expressions[641], expressions[642], expressions[644], expressions[645], expressions[646] };
3334-
expressions[651] = BinaryenBlock(the_module, "the-value", children, 244, 0);
3367+
expressions[641], expressions[642], expressions[644], expressions[649], expressions[655], expressions[656],
3368+
expressions[657] };
3369+
expressions[662] = BinaryenBlock(the_module, "the-value", children, 246, 0);
33353370
}
3336-
expressions[652] = BinaryenDrop(the_module, expressions[651]);
3371+
expressions[663] = BinaryenDrop(the_module, expressions[662]);
33373372
{
3338-
BinaryenExpressionRef children[] = { expressions[652] };
3339-
expressions[653] = BinaryenBlock(the_module, "the-nothing", children, 1, 0);
3373+
BinaryenExpressionRef children[] = { expressions[663] };
3374+
expressions[664] = BinaryenBlock(the_module, "the-nothing", children, 1, 0);
33403375
}
3341-
expressions[654] = BinaryenConst(the_module, BinaryenLiteralInt32(42));
3376+
expressions[665] = BinaryenConst(the_module, BinaryenLiteralInt32(42));
33423377
{
3343-
BinaryenExpressionRef children[] = { expressions[653], expressions[654] };
3344-
expressions[655] = BinaryenBlock(the_module, "the-body", children, 2, 0);
3378+
BinaryenExpressionRef children[] = { expressions[664], expressions[665] };
3379+
expressions[666] = BinaryenBlock(the_module, "the-body", children, 2, 0);
33453380
}
33463381
{
33473382
BinaryenType varTypes[] = { 1 };
3348-
functions[0] = BinaryenAddFunction(the_module, "kitchen()sinker", functionTypes[0], varTypes, 1, expressions[655]);
3383+
functions[0] = BinaryenAddFunction(the_module, "kitchen()sinker", functionTypes[0], varTypes, 1, expressions[666]);
33493384
}
3350-
expressions[656] = BinaryenConst(the_module, BinaryenLiteralInt32(1));
3351-
globals[0] = BinaryenAddGlobal(the_module, "a-global", 1, 0, expressions[656]);
3385+
expressions[667] = BinaryenConst(the_module, BinaryenLiteralInt32(1));
3386+
globals[0] = BinaryenAddGlobal(the_module, "a-global", 1, 0, expressions[667]);
33523387
{
33533388
BinaryenType paramTypes[] = { 1 };
33543389
functionTypes[1] = BinaryenAddFunctionType(the_module, "vi", 0, paramTypes, 1);
@@ -3381,24 +3416,24 @@ getExpressionInfo(f64.const)={"id":14,"type":4,"value":9.5}
33813416
const char* funcNames[] = { "kitchen()sinker" };
33823417
BinaryenSetFunctionTable(the_module, 1, 4294967295, funcNames, 1);
33833418
}
3384-
expressions[657] = BinaryenConst(the_module, BinaryenLiteralInt32(10));
3419+
expressions[668] = BinaryenConst(the_module, BinaryenLiteralInt32(10));
33853420
{
33863421
const char segment0[] = { 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100 };
33873422
const char segment1[] = { 73, 32, 97, 109, 32, 112, 97, 115, 115, 105, 118, 101 };
33883423
const char* segments[] = { segment0, segment1 };
33893424
int8_t segmentPassive[] = { 0, 1 };
3390-
BinaryenExpressionRef segmentOffsets[] = { expressions[657], expressions[0] };
3425+
BinaryenExpressionRef segmentOffsets[] = { expressions[668], expressions[0] };
33913426
BinaryenIndex segmentSizes[] = { 12, 12 };
33923427
BinaryenSetMemory(the_module, 1, 256, "mem", segments, segmentPassive, segmentOffsets, segmentSizes, 2, 0);
33933428
}
33943429
{
33953430
BinaryenType paramTypes[] = { 0 };
33963431
functionTypes[3] = BinaryenAddFunctionType(the_module, "v", 0, paramTypes, 0);
33973432
}
3398-
expressions[658] = BinaryenNop(the_module);
3433+
expressions[669] = BinaryenNop(the_module);
33993434
{
34003435
BinaryenType varTypes[] = { 0 };
3401-
functions[1] = BinaryenAddFunction(the_module, "starter", functionTypes[3], varTypes, 0, expressions[658]);
3436+
functions[1] = BinaryenAddFunction(the_module, "starter", functionTypes[3], varTypes, 0, expressions[669]);
34023437
}
34033438
BinaryenSetStart(the_module, functions[1]);
34043439
{
@@ -4765,6 +4800,23 @@ getExpressionInfo(f64.const)={"id":14,"type":4,"value":9.5}
47654800
(return
47664801
(i32.const 1337)
47674802
)
4803+
(drop
4804+
(return_call "$kitchen()sinker"
4805+
(i32.const 13)
4806+
(i64.const 37)
4807+
(f32.const 1.2999999523162842)
4808+
(f64.const 3.7)
4809+
)
4810+
)
4811+
(drop
4812+
(return_call_indirect (type $iiIfF)
4813+
(i32.const 13)
4814+
(i64.const 37)
4815+
(f32.const 1.2999999523162842)
4816+
(f64.const 3.7)
4817+
(i32.const 2449)
4818+
)
4819+
)
47684820
(nop)
47694821
(unreachable)
47704822
)

test/example/c-api-kitchen-sink.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,12 +459,20 @@ void test_core() {
459459
BinaryenDrop(module, BinaryenLocalTee(module, 0, makeInt32(module, 102))),
460460
BinaryenLoad(module, 4, 0, 0, 0, BinaryenTypeInt32(), makeInt32(module, 1)),
461461
BinaryenLoad(module, 2, 1, 2, 1, BinaryenTypeInt64(), makeInt32(module, 8)),
462-
BinaryenLoad(module, 4, 0, 0, 0, BinaryenTypeFloat32(), makeInt32(module, 2)),
463-
BinaryenLoad(module, 8, 0, 2, 8, BinaryenTypeFloat64(), makeInt32(module, 9)),
462+
BinaryenLoad(
463+
module, 4, 0, 0, 0, BinaryenTypeFloat32(), makeInt32(module, 2)),
464+
BinaryenLoad(
465+
module, 8, 0, 2, 8, BinaryenTypeFloat64(), makeInt32(module, 9)),
464466
BinaryenStore(module, 4, 0, 0, temp13, temp14, BinaryenTypeInt32()),
465467
BinaryenStore(module, 8, 2, 4, temp15, temp16, BinaryenTypeInt64()),
466468
BinaryenSelect(module, temp10, temp11, temp12),
467469
BinaryenReturn(module, makeInt32(module, 1337)),
470+
// Tail call
471+
BinaryenReturnCall(
472+
module, "kitchen()sinker", callOperands4, 4, BinaryenTypeInt32()),
473+
BinaryenReturnCallIndirect(
474+
module, makeInt32(module, 2449), callOperands4b, 4, "iiIfF"),
475+
468476
// TODO: Host
469477
BinaryenNop(module),
470478
BinaryenUnreachable(module),

0 commit comments

Comments
 (0)