Skip to content

Commit 7579063

Browse files
committed
skip final alloc in any terminating scenario
1 parent 53e0787 commit 7579063

File tree

1 file changed

+141
-2
lines changed

1 file changed

+141
-2
lines changed

src/compiler.ts

+141-2
Original file line numberDiff line numberDiff line change
@@ -1111,8 +1111,7 @@ export class Compiler extends DiagnosticEmitter {
11111111
assert(instance.is(CommonFlags.INSTANCE));
11121112
let classInstance = assert(instance.parent); assert(classInstance.kind == ElementKind.CLASS);
11131113

1114-
// implicitly return `this` if the constructor doesn't always return on its own
1115-
if (!flow.is(FlowFlags.RETURNS)) {
1114+
if (!flow.isAny(FlowFlags.ANY_TERMINATING)) {
11161115

11171116
// if `this` wasn't accessed before, allocate if necessary and initialize `this`
11181117
if (!flow.is(FlowFlags.ALLOCATES)) {
@@ -1133,6 +1132,8 @@ export class Compiler extends DiagnosticEmitter {
11331132
);
11341133
this.makeFieldInitialization(<Class>classInstance, stmts);
11351134
}
1135+
1136+
// implicitly return `this`
11361137
stmts.push(
11371138
module.createGetLocal(0, nativeSizeType)
11381139
);
@@ -5631,6 +5632,144 @@ export class Compiler extends DiagnosticEmitter {
56315632
: module.createNop();
56325633
}
56335634

5635+
makeCallInlineUnchecked(
5636+
instance: Function,
5637+
argumentExpressions: Expression[],
5638+
argumentLocals: Local[],
5639+
thisArg: ExpressionRef = 0
5640+
): ExpressionRef {
5641+
var numArguments = argumentExpressions.length;
5642+
var signature = instance.signature;
5643+
var currentFunction = this.currentFunction;
5644+
var module = this.module;
5645+
var declaration = instance.prototype.declaration;
5646+
5647+
// Create a new flow with its own scope and mark it for inlining
5648+
var previousFlow = currentFunction.flow;
5649+
var returnLabel = instance.internalName + "|inlined." + (instance.nextInlineId++).toString(10);
5650+
var returnType = instance.signature.returnType;
5651+
var flow = Flow.create(currentFunction);
5652+
flow.set(FlowFlags.INLINE_CONTEXT);
5653+
flow.returnLabel = returnLabel;
5654+
flow.returnType = returnType;
5655+
flow.contextualTypeArguments = instance.contextualTypeArguments;
5656+
5657+
// Convert provided call arguments to temporary locals. It is important that these are compiled
5658+
// here, with their respective locals being blocked. There is no 'makeCallInline'.
5659+
var body = [];
5660+
if (thisArg) {
5661+
let classInstance = assert(instance.parent); assert(classInstance.kind == ElementKind.CLASS);
5662+
let thisType = assert(instance.signature.thisType);
5663+
let classType = thisType.classReference;
5664+
let superType = classType
5665+
? classType.base
5666+
? classType.base.type
5667+
: null
5668+
: null;
5669+
if (getExpressionId(thisArg) == ExpressionId.GetLocal) { // reuse this var
5670+
flow.addScopedLocalAlias(getGetLocalIndex(thisArg), thisType, "this");
5671+
if (superType) flow.addScopedLocalAlias(getGetLocalIndex(thisArg), superType, "super");
5672+
} else { // use a temp var
5673+
let thisLocal = flow.addScopedLocal(thisType, "this", false);
5674+
body.push(
5675+
module.createSetLocal(thisLocal.index, thisArg)
5676+
);
5677+
if (superType) flow.addScopedLocalAlias(thisLocal.index, superType, "super");
5678+
}
5679+
}
5680+
var parameterTypes = signature.parameterTypes;
5681+
for (let i = 0; i < numArguments; ++i) {
5682+
let paramExpr = this.compileExpression(
5683+
argumentExpressions[i],
5684+
parameterTypes[i],
5685+
ConversionKind.IMPLICIT,
5686+
WrapMode.NONE
5687+
);
5688+
if (getExpressionId(paramExpr) == ExpressionId.GetLocal) {
5689+
flow.addScopedLocalAlias(
5690+
getGetLocalIndex(paramExpr),
5691+
parameterTypes[i],
5692+
signature.getParameterName(i)
5693+
);
5694+
// inherits wrap status
5695+
} else {
5696+
let argumentLocal = flow.addScopedLocal(
5697+
parameterTypes[i],
5698+
signature.getParameterName(i),
5699+
!flow.canOverflow(paramExpr, parameterTypes[i])
5700+
);
5701+
body.push(
5702+
module.createSetLocal(argumentLocal.index, paramExpr)
5703+
);
5704+
}
5705+
}
5706+
5707+
// Compile optional parameter initializers in the scope of the inlined flow
5708+
currentFunction.flow = flow;
5709+
var numParameters = signature.parameterTypes.length;
5710+
for (let i = numArguments; i < numParameters; ++i) {
5711+
let initExpr = this.compileExpression(
5712+
assert(declaration.signature.parameters[i].initializer),
5713+
parameterTypes[i],
5714+
ConversionKind.IMPLICIT,
5715+
WrapMode.WRAP
5716+
);
5717+
let argumentLocal = flow.addScopedLocal(
5718+
parameterTypes[i],
5719+
signature.getParameterName(i),
5720+
!flow.canOverflow(initExpr, parameterTypes[i])
5721+
);
5722+
body.push(
5723+
module.createSetLocal(argumentLocal.index, initExpr)
5724+
);
5725+
}
5726+
5727+
// Compile the called function's body in the scope of the inlined flow
5728+
var bodyStatement = assert(declaration.body);
5729+
if (bodyStatement.kind == NodeKind.BLOCK) {
5730+
let statements = (<BlockStatement>bodyStatement).statements;
5731+
for (let i = 0, k = statements.length; i < k; ++i) {
5732+
let stmt = this.compileStatement(statements[i]);
5733+
if (getExpressionId(stmt) != ExpressionId.Nop) {
5734+
body.push(stmt);
5735+
if (flow.isAny(FlowFlags.ANY_TERMINATING)) break;
5736+
}
5737+
}
5738+
} else {
5739+
body.push(this.compileStatement(bodyStatement));
5740+
}
5741+
5742+
// Free any new scoped locals and reset to the original flow
5743+
var scopedLocals = flow.scopedLocals;
5744+
if (scopedLocals) {
5745+
for (let scopedLocal of scopedLocals.values()) {
5746+
if (scopedLocal.is(CommonFlags.SCOPED)) { // otherwise an alias
5747+
currentFunction.freeTempLocal(scopedLocal);
5748+
}
5749+
}
5750+
flow.scopedLocals = null;
5751+
}
5752+
flow.finalize();
5753+
this.currentFunction.flow = previousFlow;
5754+
this.currentType = returnType;
5755+
5756+
// Check that all branches are terminated
5757+
if (returnType != Type.void && !flow.isAny(FlowFlags.ANY_TERMINATING)) {
5758+
this.error(
5759+
DiagnosticCode.A_function_whose_declared_type_is_not_void_must_return_a_value,
5760+
declaration.signature.returnType.range
5761+
);
5762+
return module.createUnreachable();
5763+
}
5764+
return flow.is(FlowFlags.RETURNS)
5765+
? module.createBlock(returnLabel, body, returnType.toNativeType())
5766+
: body.length > 1
5767+
? module.createBlock(null, body, returnType.toNativeType())
5768+
: body.length
5769+
? body[0]
5770+
: module.createNop();
5771+
}
5772+
56345773
/** Gets the trampoline for the specified function. */
56355774
ensureTrampoline(original: Function): Function {
56365775
// A trampoline is a function that takes a fixed amount of operands with some of them possibly

0 commit comments

Comments
 (0)