Skip to content

Commit 19a616d

Browse files
committed
Support indirect calls with omitted arguments
1 parent 664f2a1 commit 19a616d

18 files changed

+1080
-527
lines changed

dist/assemblyscript.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/assemblyscript.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/compiler.ts

Lines changed: 101 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import {
2323
NativeType,
2424
FunctionRef,
2525
ExpressionId,
26-
FunctionTypeRef
26+
FunctionTypeRef,
27+
GlobalRef
2728
} from "./module";
2829

2930
import {
@@ -194,29 +195,26 @@ export class Compiler extends DiagnosticEmitter {
194195
options: Options;
195196
/** Module instance being compiled. */
196197
module: Module;
197-
198-
/** Start function being compiled. */
199-
startFunction: Function;
200-
/** Start function statements. */
201-
startFunctionBody: ExpressionRef[] = [];
202-
203198
/** Current function in compilation. */
204199
currentFunction: Function;
205200
/** Current enum in compilation. */
206201
currentEnum: Enum | null = null;
207202
/** Current type in compilation. */
208203
currentType: Type = Type.void;
209-
204+
/** Start function being compiled. */
205+
startFunction: Function;
206+
/** Start function statements. */
207+
startFunctionBody: ExpressionRef[] = [];
210208
/** Counting memory offset. */
211209
memoryOffset: I64;
212210
/** Memory segments being compiled. */
213211
memorySegments: MemorySegment[] = new Array();
214212
/** Map of already compiled static string segments. */
215213
stringSegments: Map<string,MemorySegment> = new Map();
216-
217214
/** Function table being compiled. */
218215
functionTable: Function[] = new Array();
219-
216+
/** Argument count helper global. */
217+
argumentCountRef: GlobalRef = 0;
220218
/** Already processed file names. */
221219
files: Set<string> = new Set();
222220

@@ -1192,7 +1190,7 @@ export class Compiler extends DiagnosticEmitter {
11921190
}
11931191
var functionTable = this.functionTable;
11941192
var index = functionTable.length;
1195-
if (func.signature.requiredParameters < func.signature.parameterTypes.length) {
1193+
if (!func.is(CommonFlags.TRAMPOLINE) && func.signature.requiredParameters < func.signature.parameterTypes.length) {
11961194
// insert the trampoline if the function has optional parameters
11971195
func = this.ensureTrampoline(func);
11981196
}
@@ -4314,8 +4312,7 @@ export class Compiler extends DiagnosticEmitter {
43144312
++minOperands;
43154313
++maxOperands;
43164314
}
4317-
var numOptional = maxOperands - minOperands;
4318-
assert(numOptional);
4315+
var numOptional = assert(maxOperands - minOperands);
43194316

43204317
var forwardedOperands = new Array<ExpressionRef>(minOperands);
43214318
var operandIndex = 0;
@@ -4333,21 +4330,13 @@ export class Compiler extends DiagnosticEmitter {
43334330
}
43344331
assert(operandIndex == minOperands);
43354332

4336-
// append an additional parameter taking the number of optional arguments provided
4337-
var trampolineParameterTypes = new Array<Type>(maxArguments + 1);
4338-
for (let i = 0; i < maxArguments; ++i) {
4339-
trampolineParameterTypes[i] = originalParameterTypes[i];
4340-
}
4341-
trampolineParameterTypes[maxArguments] = Type.i32;
4342-
43434333
// create the trampoline element
4344-
var trampolineSignature = new Signature(trampolineParameterTypes, commonReturnType, commonThisType);
4334+
var trampolineSignature = new Signature(originalParameterTypes, commonReturnType, commonThisType);
43454335
var trampolineName = originalName + "|trampoline";
4346-
trampolineSignature.requiredParameters = maxArguments + 1;
4336+
trampolineSignature.requiredParameters = maxArguments;
43474337
trampoline = new Function(original.prototype, trampolineName, trampolineSignature, original.memberOf);
4348-
trampoline.flags = original.flags;
4338+
trampoline.set(original.flags | CommonFlags.TRAMPOLINE | CommonFlags.COMPILED);
43494339
trampoline.contextualTypeArguments = original.contextualTypeArguments;
4350-
trampoline.set(CommonFlags.COMPILED);
43514340
original.trampoline = trampoline;
43524341

43534342
// compile initializers of omitted arguments in scope of the trampoline function
@@ -4356,23 +4345,24 @@ export class Compiler extends DiagnosticEmitter {
43564345
this.currentFunction = trampoline;
43574346

43584347
// create a br_table switching over the number of optional parameters provided
4359-
var numNames = numOptional + 1; // incl. 'with0'
4348+
var numNames = numOptional + 1; // incl. outer block
43604349
var names = new Array<string>(numNames);
4350+
var ofN = "of" + numOptional.toString(10);
43614351
for (let i = 0; i < numNames; ++i) {
4362-
let label = "N=" + i.toString();
4352+
let label = i.toString(10) + ofN;
43634353
names[i] = label;
43644354
}
43654355
var body = module.createBlock(names[0], [
4366-
module.createBlock("N=invalid", [
4367-
module.createSwitch(names, "N=invalid",
4368-
// condition is number of provided optional operands, so subtract required operands
4369-
minOperands
4356+
module.createBlock("oob", [
4357+
module.createSwitch(names, "oob",
4358+
// condition is number of provided optional arguments, so subtract required arguments
4359+
minArguments
43704360
? module.createBinary(
43714361
BinaryOp.SubI32,
4372-
module.createGetLocal(maxOperands, NativeType.I32),
4373-
module.createI32(minOperands)
4362+
module.createGetGlobal("argumentCount", NativeType.I32),
4363+
module.createI32(minArguments)
43744364
)
4375-
: module.createGetLocal(maxOperands, NativeType.I32)
4365+
: module.createGetGlobal("argumentCount", NativeType.I32)
43764366
)
43774367
]),
43784368
module.createUnreachable()
@@ -4409,7 +4399,10 @@ export class Compiler extends DiagnosticEmitter {
44094399
}
44104400

44114401
/** Creates a direct call to the specified function. */
4412-
makeCallDirect(instance: Function, operands: ExpressionRef[] | null = null): ExpressionRef {
4402+
makeCallDirect(
4403+
instance: Function,
4404+
operands: ExpressionRef[] | null = null
4405+
): ExpressionRef {
44134406
var numOperands = operands ? operands.length : 0;
44144407
var numArguments = numOperands;
44154408
var minArguments = instance.signature.requiredParameters;
@@ -4422,27 +4415,39 @@ export class Compiler extends DiagnosticEmitter {
44224415
--numArguments;
44234416
}
44244417
assert(numOperands >= minOperands);
4418+
44254419
var module = this.module;
44264420
if (!this.compileFunction(instance)) return module.createUnreachable();
4421+
var returnType = instance.signature.returnType;
4422+
var isCallImport = instance.is(CommonFlags.MODULE_IMPORT);
4423+
4424+
// fill up omitted arguments with zeroes
44274425
if (numOperands < maxOperands) {
4428-
instance = this.ensureTrampoline(instance);
4429-
if (!this.compileFunction(instance)) return module.createUnreachable();
44304426
if (!operands) {
4431-
operands = new Array(maxOperands + 1);
4427+
operands = new Array(maxOperands);
44324428
operands.length = 0;
44334429
}
4430+
let parameterTypes = instance.signature.parameterTypes;
44344431
for (let i = numArguments; i < maxArguments; ++i) {
4435-
operands.push(instance.signature.parameterTypes[i].toNativeZero(module));
4432+
operands.push(parameterTypes[i].toNativeZero(module));
4433+
}
4434+
if (!isCallImport) { // call the trampoline
4435+
instance = this.ensureTrampoline(instance);
4436+
if (!this.compileFunction(instance)) return module.createUnreachable();
4437+
let nativeReturnType = returnType.toNativeType();
4438+
this.currentType = returnType;
4439+
return module.createBlock(null, [
4440+
this.ensureArgumentCount(numArguments),
4441+
module.createCall(instance.internalName, operands, nativeReturnType)
4442+
], nativeReturnType);
44364443
}
4437-
operands.push(module.createI32(numOperands)); // actual number of provided operands
44384444
}
4439-
var returnType = instance.signature.returnType;
4445+
4446+
// otherwise just call through
44404447
this.currentType = returnType;
4441-
if (instance.is(CommonFlags.MODULE_IMPORT)) {
4442-
return module.createCallImport(instance.internalName, operands, returnType.toNativeType());
4443-
} else {
4444-
return module.createCall(instance.internalName, operands, returnType.toNativeType());
4445-
}
4448+
return isCallImport
4449+
? module.createCallImport(instance.internalName, operands, returnType.toNativeType())
4450+
: module.createCall(instance.internalName, operands, returnType.toNativeType());
44464451
}
44474452

44484453
/** Compiles an indirect call using an index argument and a signature. */
@@ -4483,11 +4488,59 @@ export class Compiler extends DiagnosticEmitter {
44834488
}
44844489

44854490
/** Creates an indirect call to the function at `indexArg` in the function table. */
4486-
makeCallIndirect(signature: Signature, indexArg: ExpressionRef, operands: ExpressionRef[]): ExpressionRef {
4491+
makeCallIndirect(
4492+
signature: Signature,
4493+
indexArg: ExpressionRef,
4494+
operands: ExpressionRef[] | null = null
4495+
): ExpressionRef {
4496+
var numOperands = operands ? operands.length : 0;
4497+
var numArguments = numOperands;
4498+
var minArguments = signature.requiredParameters;
4499+
var minOperands = minArguments;
4500+
var maxArguments = signature.parameterTypes.length;
4501+
var maxOperands = maxArguments;
4502+
if (signature.thisType) {
4503+
++minOperands;
4504+
++maxOperands;
4505+
--numArguments;
4506+
}
4507+
assert(numOperands >= minOperands);
4508+
4509+
this.ensureFunctionType(signature);
4510+
var module = this.module;
4511+
4512+
// fill up omitted arguments with zeroes
4513+
if (numOperands < maxOperands) {
4514+
if (!operands) {
4515+
operands = new Array(maxOperands);
4516+
operands.length = 0;
4517+
}
4518+
let parameterTypes = signature.parameterTypes;
4519+
for (let i = numArguments; i < maxArguments; ++i) {
4520+
operands.push(parameterTypes[i].toNativeZero(module));
4521+
}
4522+
}
4523+
44874524
var returnType = signature.returnType;
44884525
this.currentType = returnType;
4489-
this.ensureFunctionType(signature);
4490-
return this.module.createCallIndirect(indexArg, operands, signature.toSignatureString());
4526+
return module.createBlock(null, [
4527+
this.ensureArgumentCount(numArguments), // might still be calling a trampoline
4528+
module.createCallIndirect(indexArg, operands, signature.toSignatureString())
4529+
], returnType.toNativeType());
4530+
}
4531+
4532+
/** Makes sure that the `argumentCount` helper global is present and returns an expression that sets it. */
4533+
private ensureArgumentCount(argumentCount: i32): ExpressionRef {
4534+
var module = this.module;
4535+
if (!this.argumentCountRef) {
4536+
this.argumentCountRef = module.addGlobal(
4537+
"argumentCount",
4538+
NativeType.I32,
4539+
true,
4540+
module.createI32(0)
4541+
);
4542+
}
4543+
return module.createSetGlobal("argumentCount", module.createI32(argumentCount));
44914544
}
44924545

44934546
compileCommaExpression(expression: CommaExpression, contextualType: Type): ExpressionRef {

src/program.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2070,7 +2070,9 @@ export enum CommonFlags {
20702070
/** Has a constant value and is therefore inlined. */
20712071
INLINED = 1 << 26,
20722072
/** Is scoped. */
2073-
SCOPED = 1 << 27
2073+
SCOPED = 1 << 27,
2074+
/** Is a trampoline. */
2075+
TRAMPOLINE = 1 << 28
20742076
}
20752077

20762078
/** Base class of all program elements. */

0 commit comments

Comments
 (0)