Skip to content

Commit e415377

Browse files
committed
Avoid trampolines where optional arguments are constant literals, see #102; Fix temporary local flags not being cleared; Fix inlined temporary locals not being free'd; Fix inlined flows not breaking after returns; Allow changetype of u32s, i.e. function pointers
1 parent ef9b437 commit e415377

35 files changed

+3012
-3647
lines changed

dist/asc.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/asc.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/assemblyscript.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/assemblyscript.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/builtins.ts

+2-9
Original file line numberDiff line numberDiff line change
@@ -1945,13 +1945,6 @@ export function compileCall(
19451945
reportNode.range, "1", typeArguments ? typeArguments.length.toString(10) : "0"
19461946
);
19471947
return module.createUnreachable();
1948-
} else if (typeArguments[0].kind != TypeKind.USIZE) { // any usize
1949-
compiler.error(
1950-
DiagnosticCode.Operation_not_supported,
1951-
reportNode.range
1952-
);
1953-
compiler.currentType = typeArguments[0];
1954-
return module.createUnreachable();
19551948
}
19561949
if (operands.length != 1) {
19571950
compiler.error(
@@ -1963,11 +1956,11 @@ export function compileCall(
19631956
}
19641957
arg0 = compiler.compileExpressionRetainType(
19651958
operands[0],
1966-
compiler.options.usizeType,
1959+
typeArguments[0],
19671960
WrapMode.NONE
19681961
);
19691962
compiler.currentType = typeArguments[0];
1970-
if (compiler.currentType.kind != TypeKind.USIZE) {
1963+
if (compiler.currentType.size != typeArguments[0].size) {
19711964
compiler.error(
19721965
DiagnosticCode.Operation_not_supported,
19731966
reportNode.range

src/compiler.ts

+45-18
Original file line numberDiff line numberDiff line change
@@ -5073,10 +5073,14 @@ export class Compiler extends DiagnosticEmitter {
50735073

50745074
// Compile the called function's body in the scope of the inlined flow
50755075
var bodyStatement = assert(declaration.body);
5076-
if (bodyStatement.kind == NodeKind.BLOCK) { // it's ok to unwrap the block here
5076+
if (bodyStatement.kind == NodeKind.BLOCK) {
50775077
let statements = (<BlockStatement>bodyStatement).statements;
50785078
for (let i = 0, k = statements.length; i < k; ++i) {
5079-
body.push(this.compileStatement(statements[i]));
5079+
let stmt = this.compileStatement(statements[i]);
5080+
if (getExpressionId(stmt) != ExpressionId.Nop) {
5081+
body.push(stmt);
5082+
if (flow.isAny(FlowFlags.BREAKS | FlowFlags.CONTINUES | FlowFlags.RETURNS)) break;
5083+
}
50805084
}
50815085
} else {
50825086
body.push(this.compileStatement(bodyStatement));
@@ -5290,28 +5294,51 @@ export class Compiler extends DiagnosticEmitter {
52905294
var returnType = instance.signature.returnType;
52915295
var isCallImport = instance.is(CommonFlags.MODULE_IMPORT);
52925296

5293-
// fill up omitted arguments with zeroes
5297+
// fill up omitted arguments with their initializers, if constant, otherwise with zeroes.
52945298
if (numOperands < maxOperands) {
52955299
if (!operands) {
52965300
operands = new Array(maxOperands);
52975301
operands.length = 0;
52985302
}
52995303
let parameterTypes = instance.signature.parameterTypes;
5304+
let parameterNodes = instance.prototype.declaration.signature.parameterTypes;
5305+
let allOptionalsAreConstant = true;
53005306
for (let i = numArguments; i < maxArguments; ++i) {
5301-
operands.push(parameterTypes[i].toNativeZero(module));
5307+
let initializer = assert(parameterNodes[i].initializer);
5308+
if (initializer.kind != NodeKind.LITERAL) {
5309+
// TODO: other kinds might be constant as well
5310+
allOptionalsAreConstant = false;
5311+
break;
5312+
}
53025313
}
5303-
if (!isCallImport) { // call the trampoline
5304-
let original = instance;
5305-
instance = this.ensureTrampoline(instance);
5306-
if (!this.compileFunction(instance)) return module.createUnreachable();
5307-
instance.flow.flags = original.flow.flags;
5308-
this.program.instancesLookup.set(instance.internalName, instance); // so canOverflow can find it
5309-
let nativeReturnType = returnType.toNativeType();
5310-
this.currentType = returnType;
5311-
return module.createBlock(null, [
5312-
module.createSetGlobal(this.ensureArgcVar(), module.createI32(numArguments)),
5313-
module.createCall(instance.internalName, operands, nativeReturnType)
5314-
], nativeReturnType);
5314+
if (allOptionalsAreConstant) { // inline into the call
5315+
for (let i = numArguments; i < maxArguments; ++i) {
5316+
operands.push(
5317+
this.compileExpression(
5318+
<Expression>parameterNodes[i].initializer,
5319+
parameterTypes[i],
5320+
ConversionKind.IMPLICIT,
5321+
WrapMode.NONE
5322+
)
5323+
);
5324+
}
5325+
} else { // otherwise fill up with zeroes and call the trampoline
5326+
for (let i = numArguments; i < maxArguments; ++i) {
5327+
operands.push(parameterTypes[i].toNativeZero(module));
5328+
}
5329+
if (!isCallImport) {
5330+
let original = instance;
5331+
instance = this.ensureTrampoline(instance);
5332+
if (!this.compileFunction(instance)) return module.createUnreachable();
5333+
instance.flow.flags = original.flow.flags;
5334+
this.program.instancesLookup.set(instance.internalName, instance); // so canOverflow can find it
5335+
let nativeReturnType = returnType.toNativeType();
5336+
this.currentType = returnType;
5337+
return module.createBlock(null, [
5338+
module.createSetGlobal(this.ensureArgcVar(), module.createI32(numArguments)),
5339+
module.createCall(instance.internalName, operands, nativeReturnType)
5340+
], nativeReturnType);
5341+
}
53155342
}
53165343
}
53175344

@@ -6211,8 +6238,8 @@ export class Compiler extends DiagnosticEmitter {
62116238
getExpressionType(condExprPrecomp) == NativeType.I32
62126239
) {
62136240
return getConstValueI32(condExprPrecomp)
6214-
? this.compileExpression(ifThen, contextualType, ConversionKind.IMPLICIT, WrapMode.NONE)
6215-
: this.compileExpression(ifElse, contextualType, ConversionKind.IMPLICIT, WrapMode.NONE);
6241+
? this.compileExpressionRetainType(ifThen, contextualType, WrapMode.NONE)
6242+
: this.compileExpressionRetainType(ifElse, contextualType, WrapMode.NONE);
62166243

62176244
// Otherwise recompile to the original and let the optimizer decide
62186245
} else /* if (condExpr != condExprPrecomp) <- not guaranteed */ {

src/definitions.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@ abstract class ExportsWalker {
3737
/** Program reference. */
3838
program: Program;
3939
/** Whether to include private members */
40-
private includePrivate: bool;
40+
includePrivate: bool;
41+
/** Elements still to do. */
42+
todo: Element[] = [];
4143
/** Already seen elements. */
42-
private seen: Set<Element> = new Set();
44+
seen: Set<Element> = new Set();
4345

4446
/** Constructs a new Element walker. */
4547
constructor(program: Program, includePrivate: bool = false) {
@@ -50,6 +52,8 @@ abstract class ExportsWalker {
5052
/** Walks all exports and calls the respective handlers. */
5153
walk(): void {
5254
for (let element of this.program.moduleLevelExports.values()) this.visitElement(element);
55+
var todo = this.todo;
56+
for (let i = 0; i < todo.length; ) this.visitElement(todo[i]);
5357
}
5458

5559
/** Visits an element.*/

src/module.ts

+37-25
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ export class MemorySegment {
239239
export class Module {
240240

241241
ref: ModuleRef;
242-
out: usize;
242+
243+
private cachedByValue: usize;
243244

244245
/** Maximum number of pages when targeting WASM32. */
245246
static readonly MAX_MEMORY_WASM32: Index = 0xffff;
@@ -250,7 +251,7 @@ export class Module {
250251
static create(): Module {
251252
var module = new Module();
252253
module.ref = _BinaryenModuleCreate();
253-
module.out = allocate_memory(16);
254+
module.cachedByValue = allocate_memory(16);
254255
return module;
255256
}
256257

@@ -259,7 +260,7 @@ export class Module {
259260
try {
260261
let module = new Module();
261262
module.ref = _BinaryenModuleRead(cArr, buffer.length);
262-
module.out = allocate_memory(3 * 8); // LLVM C-ABI, max used is 3 * usize
263+
module.cachedByValue = allocate_memory(3 * 8); // LLVM C-ABI, max used is 3 * usize
263264
return module;
264265
} finally {
265266
free_memory(changetype<usize>(cArr));
@@ -309,25 +310,25 @@ export class Module {
309310
// constants
310311

311312
createI32(value: i32): ExpressionRef {
312-
var out = this.out;
313+
var out = this.cachedByValue;
313314
_BinaryenLiteralInt32(out, value);
314315
return _BinaryenConst(this.ref, out);
315316
}
316317

317318
createI64(valueLow: i32, valueHigh: i32 = 0): ExpressionRef {
318-
var out = this.out;
319+
var out = this.cachedByValue;
319320
_BinaryenLiteralInt64(out, valueLow, valueHigh);
320321
return _BinaryenConst(this.ref, out);
321322
}
322323

323324
createF32(value: f32): ExpressionRef {
324-
var out = this.out;
325+
var out = this.cachedByValue;
325326
_BinaryenLiteralFloat32(out, value);
326327
return _BinaryenConst(this.ref, out);
327328
}
328329

329330
createF64(value: f64): ExpressionRef {
330-
var out = this.out;
331+
var out = this.cachedByValue;
331332
_BinaryenLiteralFloat64(out, value);
332333
return _BinaryenConst(this.ref, out);
333334
}
@@ -672,24 +673,25 @@ export class Module {
672673
}
673674
}
674675

675-
private tempName: usize = 0;
676-
private hasTempFunc: bool = false;
676+
private cachedTemporaryName: usize = 0;
677+
private hasTemporaryFunction: bool = false;
677678

678679
addTemporaryFunction(result: NativeType, paramTypes: NativeType[] | null, body: ExpressionRef): FunctionRef {
679-
this.hasTempFunc = assert(!this.hasTempFunc);
680-
if (!this.tempName) this.tempName = allocString(""); // works because strings are interned
680+
this.hasTemporaryFunction = assert(!this.hasTemporaryFunction);
681+
var tempName = this.cachedTemporaryName;
682+
if (!tempName) this.cachedTemporaryName = tempName = allocString(""); // works because strings are interned
681683
var cArr = allocI32Array(paramTypes);
682684
try {
683-
let typeRef = _BinaryenAddFunctionType(this.ref, this.tempName, result, cArr, paramTypes ? paramTypes.length : 0);
684-
return _BinaryenAddFunction(this.ref, this.tempName, typeRef, 0, 0, body);
685+
let typeRef = _BinaryenAddFunctionType(this.ref, tempName, result, cArr, paramTypes ? paramTypes.length : 0);
686+
return _BinaryenAddFunction(this.ref, tempName, typeRef, 0, 0, body);
685687
} finally {
686688
free_memory(cArr);
687689
}
688690
}
689691

690692
removeTemporaryFunction(): void {
691-
this.hasTempFunc = !assert(this.hasTempFunc);
692-
var tempName = assert(this.tempName);
693+
this.hasTemporaryFunction = !assert(this.hasTemporaryFunction);
694+
var tempName = assert(this.cachedTemporaryName);
693695
_BinaryenRemoveFunction(this.ref, tempName);
694696
_BinaryenRemoveFunctionType(this.ref, tempName);
695697
}
@@ -927,6 +929,19 @@ export class Module {
927929
}
928930
}
929931

932+
private cachedPrecomputeName: usize = 0;
933+
private cachedPrecomputeNames: usize = 0;
934+
935+
precomputeFunction(func: FunctionRef): void {
936+
var names = this.cachedPrecomputeNames;
937+
if (!names) {
938+
let name = allocString("precompute");
939+
this.cachedPrecomputeName = name;
940+
this.cachedPrecomputeNames = names = allocI32Array([ name ]);
941+
}
942+
_BinaryenFunctionRunPasses(func, this.ref, names, 1);
943+
}
944+
930945
validate(): bool {
931946
return _BinaryenModuleValidate(this.ref) == 1;
932947
}
@@ -936,7 +951,7 @@ export class Module {
936951
}
937952

938953
toBinary(sourceMapUrl: string | null): BinaryModule {
939-
var out = this.out;
954+
var out = this.cachedByValue;
940955
var cStr = allocString(sourceMapUrl);
941956
var binaryPtr: usize = 0;
942957
var sourceMapPtr: usize = 0;
@@ -965,9 +980,13 @@ export class Module {
965980
}
966981

967982
dispose(): void {
968-
if (!this.ref) return; // sic
983+
assert(this.ref);
984+
free_memory(this.cachedByValue);
985+
free_memory(this.cachedTemporaryName);
986+
free_memory(this.cachedPrecomputeName);
987+
free_memory(this.cachedPrecomputeNames);
969988
_BinaryenModuleDispose(this.ref);
970-
free_memory(this.out);
989+
this.ref = 0;
971990
}
972991

973992
createRelooper(): Relooper {
@@ -1295,13 +1314,6 @@ export class Relooper {
12951314
var relooper = new Relooper();
12961315
relooper.module = module;
12971316
relooper.ref = _RelooperCreate();
1298-
return relooper;
1299-
}
1300-
1301-
static createStub(module: Module): Relooper {
1302-
var relooper = new Relooper();
1303-
relooper.module = module;
1304-
relooper.ref = 0;
13051317
return relooper;
13061318
}
13071319

src/program.ts

+2
Original file line numberDiff line numberDiff line change
@@ -2923,6 +2923,7 @@ export class Function extends Element {
29232923
if (temps && temps.length) {
29242924
local = temps.pop();
29252925
local.type = type;
2926+
local.flags = CommonFlags.NONE;
29262927
} else {
29272928
local = this.addLocal(type);
29282929
}
@@ -3704,6 +3705,7 @@ export class Flow {
37043705
return existingLocal;
37053706
}
37063707
}
3708+
scopedLocal.set(CommonFlags.SCOPED);
37073709
this.scopedLocals.set(name, scopedLocal);
37083710
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
37093711
this.setLocalWrapped(scopedLocal.index, wrapped);

0 commit comments

Comments
 (0)