Add a 'call_indirect' builtin to emit arbitrary calls (might trap at runtime); Optimize 'for' loop compilation a bit

This commit is contained in:
dcodeIO
2018-05-25 15:59:17 +02:00
parent 51ede113dd
commit 7ad13f9d65
47 changed files with 3311 additions and 22111 deletions

View File

@ -2203,6 +2203,65 @@ export function compileCall(
flow.unset(FlowFlags.UNCHECKED_CONTEXT);
return ret;
}
case "call_indirect": { // call_indirect<T?>(target: Function | u32, ...args: *[]) -> T
if (operands.length < 1) {
if (typeArguments) {
if (typeArguments.length) compiler.currentType = typeArguments[0];
if (typeArguments.length != 1) {
compiler.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
reportNode.range, "1", typeArguments.length.toString(10)
);
}
}
compiler.error(
DiagnosticCode.Expected_at_least_0_arguments_but_got_1,
reportNode.range, "1", operands.length.toString(10)
);
return module.createUnreachable();
}
let returnType: Type;
if (typeArguments) {
if (typeArguments.length != 1) {
if (typeArguments.length) compiler.currentType = typeArguments[0];
compiler.error(
DiagnosticCode.Expected_0_type_arguments_but_got_1,
reportNode.range, "1", typeArguments.length.toString(10)
);
return module.createUnreachable();
}
returnType = typeArguments[0];
} else {
returnType = contextualType;
}
arg0 = compiler.compileExpressionRetainType(operands[0], Type.u32, WrapMode.NONE);
if (compiler.currentType.kind != TypeKind.U32) {
compiler.error(
DiagnosticCode.Operation_not_supported,
operands[0].range
);
return module.createUnreachable();
}
let numOperands = operands.length - 1;
let operandExprs = new Array<ExpressionRef>(numOperands);
let signatureParts = new Array<string>(numOperands + 1);
let nativeReturnType = returnType.toNativeType();
let nativeParamTypes = new Array<NativeType>(numOperands);
for (let i = 0; i < numOperands; ++i) {
operandExprs[i] = compiler.compileExpressionRetainType(operands[1 + i], Type.i32, WrapMode.NONE);
let operandType = compiler.currentType;
signatureParts[i] = operandType.toSignatureString();
nativeParamTypes[i] = operandType.toNativeType();
}
signatureParts[numOperands] = returnType.toSignatureString();
let typeName = signatureParts.join("");
let typeRef = module.getFunctionTypeBySignature(nativeReturnType, nativeParamTypes);
if (!typeRef) typeRef = module.addFunctionType(typeName, nativeReturnType, nativeParamTypes);
compiler.currentType = returnType;
// of course this can easily result in a 'RuntimeError: function signature mismatch' trap and
// thus must be used with care. it exists because it *might* be useful in specific scenarios.
return module.createCallIndirect(arg0, operandExprs, typeName);
}
// conversions

View File

@ -859,10 +859,10 @@ export class Compiler extends DiagnosticEmitter {
/** Compiles a readily resolved function instance. */
compileFunction(instance: Function): bool {
if (instance.is(CommonFlags.COMPILED)) return true;
assert(!instance.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN) || instance.internalName == "abort");
assert(!instance.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN));
instance.set(CommonFlags.COMPILED);
// check that modifiers are matching but still compile as-is
// check that modifiers are matching
var declaration = instance.prototype.declaration;
var body = declaration.body;
if (body) {
@ -1601,13 +1601,13 @@ export class Compiler extends DiagnosticEmitter {
flow.breakLabel = breakLabel;
var continueLabel = "continue|" + label;
flow.continueLabel = continueLabel;
var loopLabel = "loop|" + label;
var repeatLabel = "repeat|" + label;
// Compile in correct order
var module = this.module;
var initExpr = statement.initializer
? this.compileStatement(<Statement>statement.initializer)
: module.createNop();
: 0;
var condExpr: ExpressionRef = 0;
var alwaysTrue = true;
if (statement.condition) {
@ -1635,7 +1635,7 @@ export class Compiler extends DiagnosticEmitter {
}
var incrExpr = statement.incrementor
? this.compileExpression(<Expression>statement.incrementor, Type.void, ConversionKind.IMPLICIT, WrapMode.NONE)
: module.createNop();
: 0;
var bodyExpr = this.compileStatement(statement.statement);
// Switch back to the parent flow
@ -1644,19 +1644,35 @@ export class Compiler extends DiagnosticEmitter {
currentFunction.flow = parentFlow;
currentFunction.leaveBreakContext();
var expr = module.createBlock(breakLabel, [
initExpr,
module.createLoop(loopLabel,
module.createBlock(null, [
module.createBlock(continueLabel, [
module.createBreak(breakLabel, module.createUnary(UnaryOp.EqzI32, condExpr)),
bodyExpr
], NativeType.None),
incrExpr,
module.createBreak(loopLabel)
var breakBlock = new Array<ExpressionRef>(); // outer 'break' block
if (initExpr) breakBlock.push(initExpr);
var repeatBlock = new Array<ExpressionRef>(); // block repeating the loop
if (parentFlow.isAny(FlowFlags.CONTINUES | FlowFlags.CONDITIONALLY_CONTINUES)) {
repeatBlock.push(
module.createBlock(continueLabel, [ // inner 'continue' block
module.createBreak(breakLabel, module.createUnary(UnaryOp.EqzI32, condExpr)),
bodyExpr
], NativeType.None)
);
} else { // can omit the 'continue' block
repeatBlock.push(
module.createBreak(breakLabel, module.createUnary(UnaryOp.EqzI32, condExpr))
);
repeatBlock.push(bodyExpr);
}
if (incrExpr) repeatBlock.push(incrExpr);
repeatBlock.push(
module.createBreak(repeatLabel)
);
breakBlock.push(
module.createLoop(repeatLabel,
module.createBlock(null, repeatBlock, NativeType.None)
)
], NativeType.None);
);
var expr = module.createBlock(breakLabel, breakBlock, NativeType.None);
// If the loop is guaranteed to run and return, append a hint for Binaryen
if (flow.isAny(FlowFlags.RETURNS | FlowFlags.THROWS)) {

View File

@ -364,13 +364,6 @@ export class Parser extends DiagnosticEmitter {
var token = tn.next();
var startPos = tn.tokenPos;
// 'void'
if (token == Token.VOID) {
return Node.createType(
Node.createIdentifierExpression("void", tn.range()), [], false, tn.range(startPos, tn.pos)
);
}
var type: CommonTypeNode;
// '(' ...
@ -437,6 +430,12 @@ export class Parser extends DiagnosticEmitter {
return null;
}
// 'void'
} else if (token == Token.VOID) {
type = Node.createType(
Node.createIdentifierExpression("void", tn.range()), [], false, tn.range(startPos, tn.pos)
);
// 'this'
} else if (token == Token.THIS) {
type = Node.createType(

View File

@ -2318,7 +2318,9 @@ export enum CommonFlags {
/** Is scoped. */
SCOPED = 1 << 24,
/** Is a trampoline. */
TRAMPOLINE = 1 << 25
TRAMPOLINE = 1 << 25,
/** Is a virtual method. */
VIRTUAL = 1 << 26
}
export enum DecoratorFlags {