mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-12 06:21:29 +00:00
Statically eliminate unnecessary branches in generic contexts
In order to use the new compile time type checks in generics, untaken branches must be skipped because these might be invalid.
This commit is contained in:
@ -1331,19 +1331,21 @@ export class Compiler extends DiagnosticEmitter {
|
||||
flow.continueLabel = previousContinueLabel;
|
||||
|
||||
var module = this.module;
|
||||
var condition = makeIsTrueish(
|
||||
var condExpr = makeIsTrueish(
|
||||
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
|
||||
this.currentType,
|
||||
module
|
||||
);
|
||||
|
||||
// No need to eliminate the condition in generic contexts as the statement is executed anyway.
|
||||
|
||||
this.currentFunction.leaveBreakContext();
|
||||
|
||||
return module.createBlock(breakLabel, [
|
||||
module.createLoop(continueLabel,
|
||||
module.createBlock(null, [
|
||||
body,
|
||||
module.createBreak(continueLabel, condition)
|
||||
module.createBreak(continueLabel, condExpr)
|
||||
], NativeType.None))
|
||||
], NativeType.None);
|
||||
}
|
||||
@ -1418,26 +1420,44 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
compileIfStatement(statement: IfStatement): ExpressionRef {
|
||||
var module = this.module;
|
||||
var currentFunction = this.currentFunction;
|
||||
var ifTrue = statement.ifTrue;
|
||||
var ifFalse = statement.ifFalse;
|
||||
|
||||
// The condition doesn't initiate a branch yet
|
||||
var condition = makeIsTrueish(
|
||||
var condExpr = makeIsTrueish(
|
||||
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
|
||||
this.currentType,
|
||||
module
|
||||
);
|
||||
|
||||
// Eliminate unnecesssary branches in generic contexts if the condition is constant
|
||||
if (
|
||||
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT) &&
|
||||
_BinaryenExpressionGetId(condExpr = this.precomputeExpressionRef(condExpr)) == ExpressionId.Const &&
|
||||
_BinaryenExpressionGetType(condExpr) == NativeType.I32
|
||||
) {
|
||||
let ret: ExpressionRef;
|
||||
if (_BinaryenConstGetValueI32(condExpr)) {
|
||||
ret = this.compileStatement(ifTrue);
|
||||
} else if (ifFalse) {
|
||||
ret = this.compileStatement(ifFalse);
|
||||
} else {
|
||||
ret = module.createNop();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Each arm initiates a branch
|
||||
var currentFunction = this.currentFunction;
|
||||
var flow = currentFunction.flow.enterBranchOrScope();
|
||||
currentFunction.flow = flow;
|
||||
var ifTrueExpr = this.compileStatement(statement.ifTrue);
|
||||
var ifTrueExpr = this.compileStatement(ifTrue);
|
||||
var ifTrueReturns = flow.is(FlowFlags.RETURNS);
|
||||
flow = flow.leaveBranchOrScope();
|
||||
currentFunction.flow = flow;
|
||||
|
||||
var ifFalseExpr: ExpressionRef = 0;
|
||||
var ifFalseReturns = false;
|
||||
var ifFalse = statement.ifFalse;
|
||||
if (ifFalse) {
|
||||
flow = flow.enterBranchOrScope();
|
||||
currentFunction.flow = flow;
|
||||
@ -1449,7 +1469,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (ifTrueReturns && ifFalseReturns) { // not necessary to append a hint
|
||||
flow.set(FlowFlags.RETURNS);
|
||||
}
|
||||
return module.createIf(condition, ifTrueExpr, ifFalseExpr);
|
||||
return module.createIf(condExpr, ifTrueExpr, ifFalseExpr);
|
||||
}
|
||||
|
||||
compileReturnStatement(statement: ReturnStatement): ExpressionRef {
|
||||
@ -1724,12 +1744,23 @@ export class Compiler extends DiagnosticEmitter {
|
||||
var module = this.module;
|
||||
|
||||
// The condition does not yet initialize a branch
|
||||
var condition = makeIsTrueish(
|
||||
var condExpr = makeIsTrueish(
|
||||
this.compileExpression(statement.condition, Type.i32, ConversionKind.NONE),
|
||||
this.currentType,
|
||||
module
|
||||
);
|
||||
|
||||
// Eliminate unnecesssary loops in generic contexts if the condition is constant
|
||||
if (
|
||||
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT) &&
|
||||
_BinaryenExpressionGetId(condExpr = this.precomputeExpressionRef(condExpr)) == ExpressionId.Const &&
|
||||
_BinaryenExpressionGetType(condExpr) == NativeType.I32
|
||||
) {
|
||||
if (!_BinaryenConstGetValueI32(condExpr)) {
|
||||
return module.createNop();
|
||||
}
|
||||
}
|
||||
|
||||
// Statements initiate a new branch with its own break context
|
||||
var currentFunction = this.currentFunction;
|
||||
var label = currentFunction.enterBreakContext();
|
||||
@ -1750,7 +1781,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
var expr = module.createBlock(breakLabel, [
|
||||
module.createLoop(continueLabel,
|
||||
module.createIf(condition, module.createBlock(null, [
|
||||
module.createIf(condExpr, module.createBlock(null, [
|
||||
body,
|
||||
module.createBreak(continueLabel)
|
||||
], NativeType.None))
|
||||
@ -4952,14 +4983,29 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
compileTernaryExpression(expression: TernaryExpression, contextualType: Type): ExpressionRef {
|
||||
var condition = makeIsTrueish(
|
||||
var ifThen = expression.ifThen;
|
||||
var ifElse = expression.ifElse;
|
||||
|
||||
var condExpr = makeIsTrueish(
|
||||
this.compileExpression(expression.condition, Type.u32, ConversionKind.NONE),
|
||||
this.currentType,
|
||||
this.module
|
||||
);
|
||||
var ifThen = this.compileExpression(expression.ifThen, contextualType);
|
||||
var ifElse = this.compileExpression(expression.ifElse, contextualType);
|
||||
return this.module.createIf(condition, ifThen, ifElse);
|
||||
|
||||
// Eliminate unnecesssary branches in generic contexts if the condition is constant
|
||||
if (
|
||||
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT) &&
|
||||
_BinaryenExpressionGetId(condExpr = this.precomputeExpressionRef(condExpr)) == ExpressionId.Const &&
|
||||
_BinaryenExpressionGetType(condExpr) == NativeType.I32
|
||||
) {
|
||||
return _BinaryenConstGetValueI32(condExpr)
|
||||
? this.compileExpression(ifThen, contextualType)
|
||||
: this.compileExpression(ifElse, contextualType);
|
||||
}
|
||||
|
||||
var ifThenExpr = this.compileExpression(ifThen, contextualType);
|
||||
var ifElseExpr = this.compileExpression(ifElse, contextualType);
|
||||
return this.module.createIf(condExpr, ifThenExpr, ifElseExpr);
|
||||
}
|
||||
|
||||
compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): ExpressionRef {
|
||||
|
@ -202,7 +202,6 @@ export function formatDiagnosticContext(range: Range, useColors: bool = false):
|
||||
export abstract class DiagnosticEmitter {
|
||||
|
||||
diagnostics: DiagnosticMessage[];
|
||||
// silentDiagnostics: bool = false;
|
||||
|
||||
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
||||
this.diagnostics = diagnostics ? <DiagnosticMessage[]>diagnostics : new Array();
|
||||
|
@ -1459,12 +1459,17 @@ export class Parser extends DiagnosticEmitter {
|
||||
flags |= CommonFlags.STATIC;
|
||||
staticStart = tn.tokenPos;
|
||||
staticEnd = tn.pos;
|
||||
} else if (tn.skip(Token.ABSTRACT)) {
|
||||
flags |= (CommonFlags.ABSTRACT | CommonFlags.INSTANCE);
|
||||
abstractStart = tn.tokenPos;
|
||||
abstractEnd = tn.pos;
|
||||
} else {
|
||||
flags |= CommonFlags.INSTANCE;
|
||||
if (tn.skip(Token.ABSTRACT)) {
|
||||
flags |= (CommonFlags.ABSTRACT | CommonFlags.INSTANCE);
|
||||
abstractStart = tn.tokenPos;
|
||||
abstractEnd = tn.pos;
|
||||
} else {
|
||||
flags |= CommonFlags.INSTANCE;
|
||||
}
|
||||
if (parentFlags & CommonFlags.GENERIC) {
|
||||
flags |= CommonFlags.GENERIC_CONTEXT;
|
||||
}
|
||||
}
|
||||
|
||||
var readonlyStart: i32 = 0;
|
||||
@ -2737,12 +2742,12 @@ export class Parser extends DiagnosticEmitter {
|
||||
// fall-through
|
||||
}
|
||||
// function expression
|
||||
case Token.QUESTION: // optional parameter
|
||||
case Token.COLON: { // type annotation
|
||||
tn.reset(state);
|
||||
return this.parseFunctionExpression(tn);
|
||||
}
|
||||
// can be both
|
||||
case Token.QUESTION: // optional parameter or ternary
|
||||
case Token.COMMA: {
|
||||
break; // continue
|
||||
}
|
||||
|
@ -2036,25 +2036,27 @@ export enum CommonFlags {
|
||||
AMBIENT = 1 << 16,
|
||||
/** Is generic. */
|
||||
GENERIC = 1 << 17,
|
||||
/** Is part of a generic context. */
|
||||
GENERIC_CONTEXT = 1 << 18,
|
||||
/** Is an instance member. */
|
||||
INSTANCE = 1 << 18,
|
||||
INSTANCE = 1 << 19,
|
||||
/** Is a constructor. */
|
||||
CONSTRUCTOR = 1 << 19,
|
||||
CONSTRUCTOR = 1 << 20,
|
||||
/** Is an arrow function. */
|
||||
ARROW = 1 << 20,
|
||||
ARROW = 1 << 21,
|
||||
/** Is a module export. */
|
||||
MODULE_EXPORT = 1 << 21,
|
||||
MODULE_EXPORT = 1 << 22,
|
||||
/** Is a module import. */
|
||||
MODULE_IMPORT = 1 << 22,
|
||||
MODULE_IMPORT = 1 << 23,
|
||||
|
||||
// Compilation states
|
||||
|
||||
/** Is compiled. */
|
||||
COMPILED = 1 << 23,
|
||||
COMPILED = 1 << 24,
|
||||
/** Has a constant value and is therefore inlined. */
|
||||
INLINED = 1 << 24,
|
||||
INLINED = 1 << 25,
|
||||
/** Is scoped. */
|
||||
SCOPED = 1 << 25
|
||||
SCOPED = 1 << 26
|
||||
}
|
||||
|
||||
/** Base class of all program elements. */
|
||||
@ -2084,7 +2086,8 @@ export abstract class Element {
|
||||
|
||||
/** Tests if this element has a specific flag or flags. */
|
||||
is(flag: CommonFlags): bool { return (this.flags & flag) == flag; }
|
||||
|
||||
/** Tests if this element has any of the specified flags. */
|
||||
isAny(flags: CommonFlags): bool { return (this.flags & flags) != 0; }
|
||||
/** Sets a specific flag or flags. */
|
||||
set(flag: CommonFlags): void { this.flags |= flag; }
|
||||
}
|
||||
|
Reference in New Issue
Block a user