Skip to content

Commit 2a138fa

Browse files
authored
Initial tail call implementation (#2197)
Including parsing, printing, assembling, disassembling. TODO: - interpreting - effects - finalization and typing - fuzzing - JS/C API
1 parent 256187c commit 2a138fa

22 files changed

+182
-35
lines changed

scripts/gen-s-parser.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
("br_if", "makeBreak(s)"),
2929
("br_table", "makeBreakTable(s)"),
3030
("return", "makeReturn(s)"),
31-
("call", "makeCall(s)"),
32-
("call_indirect", "makeCallIndirect(s)"),
31+
("call", "makeCall(s, /*isReturn=*/false)"),
32+
("call_indirect", "makeCallIndirect(s, /*isReturn=*/false)"),
33+
("return_call", "makeCall(s, /*isReturn=*/true)"),
34+
("return_call_indirect", "makeCallIndirect(s, /*isReturn=*/true)"),
3335
("drop", "makeDrop(s)"),
3436
("select", "makeSelect(s)"),
3537
("local.get", "makeLocalGet(s)"),

src/gen-s-parser.inc

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ switch (op[0]) {
4040
case 'c': {
4141
switch (op[4]) {
4242
case '\0':
43-
if (strcmp(op, "call") == 0) { return makeCall(s); }
43+
if (strcmp(op, "call") == 0) { return makeCall(s, /*isReturn=*/false); }
4444
goto parse_error;
4545
case '_':
46-
if (strcmp(op, "call_indirect") == 0) { return makeCallIndirect(s); }
46+
if (strcmp(op, "call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/false); }
4747
goto parse_error;
4848
default: goto parse_error;
4949
}
@@ -2228,9 +2228,25 @@ switch (op[0]) {
22282228
case 'p':
22292229
if (strcmp(op, "push") == 0) { return makePush(s); }
22302230
goto parse_error;
2231-
case 'r':
2232-
if (strcmp(op, "return") == 0) { return makeReturn(s); }
2233-
goto parse_error;
2231+
case 'r': {
2232+
switch (op[6]) {
2233+
case '\0':
2234+
if (strcmp(op, "return") == 0) { return makeReturn(s); }
2235+
goto parse_error;
2236+
case '_': {
2237+
switch (op[11]) {
2238+
case '\0':
2239+
if (strcmp(op, "return_call") == 0) { return makeCall(s, /*isReturn=*/true); }
2240+
goto parse_error;
2241+
case '_':
2242+
if (strcmp(op, "return_call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/true); }
2243+
goto parse_error;
2244+
default: goto parse_error;
2245+
}
2246+
}
2247+
default: goto parse_error;
2248+
}
2249+
}
22342250
case 's':
22352251
if (strcmp(op, "select") == 0) { return makeSelect(s); }
22362252
goto parse_error;

src/passes/Print.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,20 @@ struct PrintExpressionContents
106106
o << ' ' << curr->default_;
107107
}
108108
void visitCall(Call* curr) {
109-
printMedium(o, "call ");
109+
if (curr->isReturn) {
110+
printMedium(o, "return_call ");
111+
} else {
112+
printMedium(o, "call ");
113+
}
110114
printName(curr->target, o);
111115
}
112116
void visitCallIndirect(CallIndirect* curr) {
113-
printMedium(o, "call_indirect (type ") << curr->fullType << ')';
117+
if (curr->isReturn) {
118+
printMedium(o, "return_call_indirect (type ");
119+
} else {
120+
printMedium(o, "call_indirect (type ");
121+
}
122+
o << curr->fullType << ')';
114123
}
115124
void visitLocalGet(LocalGet* curr) {
116125
printMedium(o, "local.get ") << printableLocal(curr->index, currFunction);

src/tools/tool-options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct ToolOptions : public Options {
7070
.addFeature(FeatureSet::BulkMemory, "bulk memory operations")
7171
.addFeature(FeatureSet::ExceptionHandling,
7272
"exception handling operations")
73+
.addFeature(FeatureSet::TailCall, "tail call operations")
7374
.add("--no-validation",
7475
"-n",
7576
"Disables validation, assumes inputs are correct",

src/wasm-binary.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ extern const char* TruncSatFeature;
405405
extern const char* SignExtFeature;
406406
extern const char* SIMD128Feature;
407407
extern const char* ExceptionHandlingFeature;
408+
extern const char* TailCallFeature;
408409

409410
enum Subsection {
410411
NameFunction = 1,
@@ -429,6 +430,8 @@ enum ASTNodes {
429430

430431
CallFunction = 0x10,
431432
CallIndirect = 0x11,
433+
RetCallFunction = 0x12,
434+
RetCallIndirect = 0x13,
432435

433436
Drop = 0x1a,
434437
Select = 0x1b,

src/wasm-builder.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -187,41 +187,45 @@ class Builder {
187187
ret->condition = condition;
188188
return ret;
189189
}
190-
Call* makeCall(Name target, const std::vector<Expression*>& args, Type type) {
190+
Call* makeCall(Name target,
191+
const std::vector<Expression*>& args,
192+
Type type,
193+
bool isReturn = false) {
191194
auto* call = allocator.alloc<Call>();
192195
// not all functions may exist yet, so type must be provided
193196
call->type = type;
194197
call->target = target;
195198
call->operands.set(args);
199+
call->isReturn = isReturn;
196200
return call;
197201
}
198-
template<typename T> Call* makeCall(Name target, const T& args, Type type) {
202+
template<typename T>
203+
Call* makeCall(Name target, const T& args, Type type, bool isReturn = false) {
199204
auto* call = allocator.alloc<Call>();
200205
// not all functions may exist yet, so type must be provided
201206
call->type = type;
202207
call->target = target;
203208
call->operands.set(args);
209+
call->isReturn = isReturn;
204210
return call;
205211
}
206212
CallIndirect* makeCallIndirect(FunctionType* type,
207213
Expression* target,
208-
const std::vector<Expression*>& args) {
209-
auto* call = allocator.alloc<CallIndirect>();
210-
call->fullType = type->name;
211-
call->type = type->result;
212-
call->target = target;
213-
call->operands.set(args);
214-
return call;
214+
const std::vector<Expression*>& args,
215+
bool isReturn = false) {
216+
return makeCallIndirect(type->name, target, args, type->result, isReturn);
215217
}
216218
CallIndirect* makeCallIndirect(Name fullType,
217219
Expression* target,
218220
const std::vector<Expression*>& args,
219-
Type type) {
221+
Type type,
222+
bool isReturn = false) {
220223
auto* call = allocator.alloc<CallIndirect>();
221224
call->fullType = fullType;
222225
call->type = type;
223226
call->target = target;
224227
call->operands.set(args);
228+
call->isReturn = isReturn;
225229
return call;
226230
}
227231
// FunctionType

src/wasm-features.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ struct FeatureSet {
3232
BulkMemory = 1 << 4,
3333
SignExt = 1 << 5,
3434
ExceptionHandling = 1 << 6,
35-
All = Atomics | MutableGlobals | TruncSat | SIMD | BulkMemory | SignExt |
36-
ExceptionHandling
35+
TailCall = 1 << 7,
36+
All = (1 << 8) - 1
3737
};
3838

3939
static std::string toString(Feature f) {
@@ -52,6 +52,8 @@ struct FeatureSet {
5252
return "sign-ext";
5353
case ExceptionHandling:
5454
return "exception-handling";
55+
case TailCall:
56+
return "tail-call";
5557
default:
5658
WASM_UNREACHABLE();
5759
}
@@ -69,6 +71,7 @@ struct FeatureSet {
6971
bool hasBulkMemory() const { return features & BulkMemory; }
7072
bool hasSignExt() const { return features & SignExt; }
7173
bool hasExceptionHandling() const { return features & ExceptionHandling; }
74+
bool hasTailCall() const { return features & TailCall; }
7275
bool hasAll() const { return features & All; }
7376

7477
void makeMVP() { features = MVP; }
@@ -82,6 +85,7 @@ struct FeatureSet {
8285
void setBulkMemory(bool v = true) { set(BulkMemory, v); }
8386
void setSignExt(bool v = true) { set(SignExt, v); }
8487
void setExceptionHandling(bool v = true) { set(ExceptionHandling, v); }
88+
void setTailCall(bool v = true) { set(TailCall, v); }
8589
void setAll(bool v = true) { features = v ? All : MVP; }
8690

8791
void enable(const FeatureSet& other) { features |= other.features; }
@@ -111,6 +115,9 @@ struct FeatureSet {
111115
if (hasSIMD()) {
112116
f(SIMD);
113117
}
118+
if (hasTailCall()) {
119+
f(TailCall);
120+
}
114121
}
115122

116123
bool operator<=(const FeatureSet& other) const {

src/wasm-s-parser.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ class SExpressionWasmBuilder {
208208
Expression* makeIf(Element& s);
209209
Expression* makeMaybeBlock(Element& s, size_t i, Type type);
210210
Expression* makeLoop(Element& s);
211-
Expression* makeCall(Element& s);
212-
Expression* makeCallIndirect(Element& s);
211+
Expression* makeCall(Element& s, bool isReturn);
212+
Expression* makeCallIndirect(Element& s, bool isReturn);
213213
template<class T>
214214
void parseCallOperands(Element& s, Index i, Index j, T* call) {
215215
while (i < j) {

src/wasm-stack.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -626,8 +626,9 @@ void StackWriter<Mode, Parent>::visitCall(Call* curr) {
626626
visitChild(operand);
627627
}
628628
if (!justAddToStack(curr)) {
629-
o << int8_t(BinaryConsts::CallFunction)
630-
<< U32LEB(parent.getFunctionIndex(curr->target));
629+
int8_t op = curr->isReturn ? BinaryConsts::RetCallFunction
630+
: BinaryConsts::CallFunction;
631+
o << op << U32LEB(parent.getFunctionIndex(curr->target));
631632
}
632633
// TODO FIXME: this and similar can be removed
633634
if (curr->type == unreachable) {
@@ -642,8 +643,9 @@ void StackWriter<Mode, Parent>::visitCallIndirect(CallIndirect* curr) {
642643
}
643644
visitChild(curr->target);
644645
if (!justAddToStack(curr)) {
645-
o << int8_t(BinaryConsts::CallIndirect)
646-
<< U32LEB(parent.getFunctionTypeIndex(curr->fullType))
646+
int8_t op = curr->isReturn ? BinaryConsts::RetCallIndirect
647+
: BinaryConsts::CallIndirect;
648+
o << op << U32LEB(parent.getFunctionTypeIndex(curr->fullType))
647649
<< U32LEB(0); // Reserved flags field
648650
}
649651
if (curr->type == unreachable) {

src/wasm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ class Call : public SpecificExpression<Expression::CallId> {
621621

622622
ExpressionList operands;
623623
Name target;
624+
bool isReturn = false;
624625

625626
void finalize();
626627
};
@@ -647,6 +648,7 @@ class CallIndirect : public SpecificExpression<Expression::CallIndirectId> {
647648
ExpressionList operands;
648649
Name fullType;
649650
Expression* target;
651+
bool isReturn = false;
650652

651653
void finalize();
652654
};

0 commit comments

Comments
 (0)