Unified continue/break labels with binaryen labels; Module-level global exports

This commit is contained in:
dcodeIO
2017-12-06 17:47:48 +01:00
parent f045975a4b
commit 29468846ab
23 changed files with 192 additions and 148 deletions

View File

@ -203,7 +203,7 @@ export class Compiler extends DiagnosticEmitter {
heapStartBuffer[2] = (initial.lo >>> 16) as u8;
heapStartBuffer[3] = (initial.lo >>> 24) as u8;
}
this.memorySegments.push(MemorySegment.create(heapStartBuffer, new U64(heapStartOffset, 0)));
this.memorySegments.push(MemorySegment.create(heapStartBuffer, new U64(heapStartOffset, 0))); // TODO: use a global instead?
// determine initial page size
const initialOverlaps: U64 = initial.clone();
initialOverlaps.and32(0xffff);
@ -295,9 +295,15 @@ export class Compiler extends DiagnosticEmitter {
const element: Element | null = <Element | null>this.program.elements.get(declaration.internalName);
if (!element || element.kind != ElementKind.GLOBAL)
throw new Error("unexpected missing global");
return this.compileGlobal(<Global>element)
? <Global>element
: null;
if (!this.compileGlobal(<Global>element))
return null;
if (declaration.range.source.isEntry && (<VariableStatement>declaration.parent).parent == declaration.range.source && hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (!(<Global>element).isCompiledMutable)
this.module.addGlobalExport(element.internalName, declaration.identifier.name);
else
this.warning(DiagnosticCode.Cannot_export_a_mutable_global, declaration.range);
}
return <Global>element;
}
compileGlobal(element: Global): bool {
@ -336,7 +342,6 @@ export class Compiler extends DiagnosticEmitter {
} else
initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() : 0);
initializeInStart = false;
this.module.addGlobal(element.internalName, nativeType, element.isMutable, initializer);
} else if (declaration) {
if (declaration.initializer) {
initializer = this.compileExpression(declaration.initializer, type);
@ -351,11 +356,10 @@ export class Compiler extends DiagnosticEmitter {
if (initializeInStart) {
this.module.addGlobal(internalName, nativeType, true, typeToNativeZero(this.module, type));
this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer));
element.isCompiledMutable = true;
} else {
this.module.addGlobal(internalName, nativeType, element.isMutable, initializer);
if (!element.isMutable) {
// TODO: check export
}
element.isCompiledMutable = element.isMutable;
}
return element.isCompiled = true;
}
@ -557,10 +561,9 @@ export class Compiler extends DiagnosticEmitter {
compileExportStatement(statement: ExportStatement): void {
const members: ExportMember[] = statement.members;
const internalPath: string | null = statement.path ? statement.internalPath : statement.range.source.internalPath;
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
const member: ExportMember = members[i];
const internalExportName: string = internalPath + PATH_DELIMITER + member.externalIdentifier.name;
const internalExportName: string = statement.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name;
const element: Element | null = <Element | null>this.program.exports.get(internalExportName);
if (!element) // reported in Program#initialize
continue;
@ -584,7 +587,12 @@ export class Compiler extends DiagnosticEmitter {
break;
case ElementKind.GLOBAL:
this.compileGlobal(<Global>element);
if (this.compileGlobal(<Global>element) && statement.range.source.isEntry) {
if (!(<Global>element).isCompiledMutable)
this.module.addGlobalExport(element.internalName, member.externalIdentifier.name);
else
this.warning(DiagnosticCode.Cannot_export_a_mutable_global, member.range);
}
break;
case ElementKind.NAMESPACE:
@ -713,7 +721,7 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("not implemented");
const context: string | null = this.currentFunction.breakContext;
if (context != null)
return this.module.createBreak("break$" + (<string>context));
return this.module.createBreak("break|" + (<string>context));
this.error(DiagnosticCode.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement, statement.range);
return this.module.createUnreachable();
}
@ -723,7 +731,7 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("not implemented");
const context: string | null = this.currentFunction.breakContext;
if (context != null && !this.disallowContinue)
return this.module.createBreak("continue$" + (<string>context));
return this.module.createBreak("continue|" + (<string>context));
this.error(DiagnosticCode.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement, statement.range);
return this.module.createUnreachable();
}
@ -733,8 +741,8 @@ export class Compiler extends DiagnosticEmitter {
const condition: ExpressionRef = this.compileExpression(statement.condition, Type.i32);
const body: ExpressionRef = this.compileStatement(statement.statement);
this.currentFunction.leaveBreakContext();
const breakLabel: string = "break$" + label;
const continueLabel: string = "continue$" + label;
const breakLabel: string = "break|" + label;
const continueLabel: string = "continue|" + label;
return this.module.createBlock(breakLabel, [
this.module.createLoop(continueLabel,
this.module.createBlock(null, [
@ -768,8 +776,8 @@ export class Compiler extends DiagnosticEmitter {
const incrementor: ExpressionRef = statement.incrementor ? this.compileExpression(<Expression>statement.incrementor, Type.void) : this.module.createNop();
const body: ExpressionRef = this.compileStatement(statement.statement);
this.currentFunction.leaveBreakContext();
const continueLabel: string = "continue$" + context;
const breakLabel: string = "break$" + context;
const continueLabel: string = "continue|" + context;
const breakLabel: string = "break|" + context;
return this.module.createBlock(breakLabel, [
initializer,
this.module.createLoop(continueLabel, this.module.createBlock(null, [
@ -816,7 +824,7 @@ export class Compiler extends DiagnosticEmitter {
for (i = 0; i < k; ++i) {
const case_: SwitchCase = statement.cases[i];
if (case_.label) {
breaks[breakIndex++] = this.module.createBreak("case" + i.toString(10) + "$" + context,
breaks[breakIndex++] = this.module.createBreak("case" + i.toString(10) + "|" + context,
this.module.createBinary(BinaryOp.EqI32,
this.module.createGetLocal(local.index, NativeType.I32),
this.compileExpression(case_.label, Type.i32)
@ -830,15 +838,15 @@ export class Compiler extends DiagnosticEmitter {
breaks[breakIndex] = this.module.createBreak((defaultIndex >= 0
? "case" + defaultIndex.toString(10)
: "break"
) + "$" + context);
) + "|" + context);
// nest blocks in order
let currentBlock: ExpressionRef = this.module.createBlock("case0$" + context, breaks, NativeType.None);
let currentBlock: ExpressionRef = this.module.createBlock("case0|" + context, breaks, NativeType.None);
for (i = 0; i < k; ++i) {
const case_: SwitchCase = statement.cases[i];
const nextLabel: string = i == k - 1
? "break$" + context
: "case" + (i + 1).toString(10) + "$" + context;
? "break|" + context
: "case" + (i + 1).toString(10) + "|" + context;
const l: i32 = case_.statements.length;
const body: ExpressionRef[] = new Array(1 + l);
body[0] = currentBlock;
@ -895,8 +903,8 @@ export class Compiler extends DiagnosticEmitter {
compileWhileStatement(statement: WhileStatement): ExpressionRef {
const label: string = this.currentFunction.enterBreakContext();
const condition: ExpressionRef = this.compileExpression(statement.condition, Type.i32);
const breakLabel: string = "break$" + label;
const continueLabel: string = "continue$" + label;
const breakLabel: string = "break|" + label;
const continueLabel: string = "continue|" + label;
const body: ExpressionRef = this.compileStatement(statement.statement);
this.currentFunction.leaveBreakContext();
return this.module.createBlock(breakLabel, [

View File

@ -5,6 +5,7 @@ export enum DiagnosticCode {
Basic_type_0_cannot_be_nullable = 101,
Operation_not_supported = 102,
Operation_is_unsafe = 103,
Cannot_export_a_mutable_global = 104,
Unterminated_string_literal = 1002,
Identifier_expected = 1003,
_0_expected = 1005,
@ -74,6 +75,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 101: return "Basic type '{0}' cannot be nullable.";
case 102: return "Operation not supported.";
case 103: return "Operation is unsafe.";
case 104: return "Cannot export a mutable global.";
case 1002: return "Unterminated string literal.";
case 1003: return "Identifier expected.";
case 1005: return "'{0}' expected.";

View File

@ -3,6 +3,7 @@
"Basic type '{0}' cannot be nullable.": 101,
"Operation not supported.": 102,
"Operation is unsafe.": 103,
"Cannot export a mutable global.": 104,
"Unterminated string literal.": 1002,
"Identifier expected.": 1003,

View File

@ -12,6 +12,10 @@ globalScope["select"] = function select<T>(ifTrue: T, ifFalse: T, condition: boo
return condition ? ifTrue : ifFalse;
};
globalScope["assert"] = function(isTrue: bool): void {
if (!isTrue) throw new Error("assertion failed");
};
let binaryen: any;
try {
binaryen = require("binaryen");

View File

@ -340,7 +340,7 @@ export class Program extends DiagnosticEmitter {
// export external element
} else {
referencedName = (<string>internalPath) + PATH_DELIMITER + member.externalIdentifier.name;
referencedName = (<string>internalPath) + PATH_DELIMITER + member.identifier.name;
// resolve right away if the export exists
if (this.exports.has(referencedName)) {
@ -771,6 +771,7 @@ export class Global extends Element {
hasConstantValue: bool = false;
constantIntegerValue: I64 | null = null;
constantFloatValue: f64 = 0;
isCompiledMutable: bool = false;
constructor(program: Program, internalName: string, declaration: VariableLikeDeclarationStatement | null, type: Type | null) {
super(program, internalName);