From 7e9b58428b048d8cb37d91e3cd53b098d24b0073 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Thu, 4 Jan 2018 06:00:42 +0100 Subject: [PATCH] Always compile globals because initializers might have side effects --- src/builtins.ts | 3 +- src/compiler.ts | 50 ++++++++++------------ src/program.ts | 38 ++++++---------- tests/compiler/binary.optimized.wast | 2 +- tests/compiler/binary.wast | 2 +- tests/compiler/builtins.optimized.wast | 2 +- tests/compiler/builtins.wast | 2 +- tests/compiler/declare.wast | 2 +- tests/compiler/i64-polyfill.optimized.wast | 2 +- tests/compiler/i64-polyfill.wast | 2 +- tests/compiler/import.wast | 2 +- tests/compiler/reexport.optimized.wast | 2 +- tests/compiler/reexport.wast | 2 +- tests/compiler/tlsf.wast | 18 +++++--- 14 files changed, 60 insertions(+), 69 deletions(-) diff --git a/src/builtins.ts b/src/builtins.ts index 12db13a1..0a5bb7b8 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -151,10 +151,9 @@ export function initialize(program: Program): void { /** Adds a built-in constant to the specified program. */ function addConstant(program: Program, name: string, type: Type): Global { - var global = new Global(program, name, name, null); + var global = new Global(program, name, name, null, type); global.isBuiltIn = true; global.isConstant = true; - global.type = type; program.elements.set(name, global); return global; } diff --git a/src/compiler.ts b/src/compiler.ts index b591c145..cb735a66 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -21,7 +21,8 @@ import { NativeType, FunctionTypeRef, FunctionRef, - ExpressionId + ExpressionId, + readString } from "./module"; import { @@ -295,9 +296,8 @@ export class Compiler extends DiagnosticEmitter { this.compileNamespaceDeclaration(statement); break; - case NodeKind.VARIABLE: - if (noTreeShaking || source.isEntry && hasModifier(ModifierKind.EXPORT, (statement).modifiers)) - this.compileVariableStatement(statement); + case NodeKind.VARIABLE: // global + this.compileVariableStatement(statement); // always because initializers might have side effects break; case NodeKind.EXPORT: @@ -344,12 +344,17 @@ export class Compiler extends DiagnosticEmitter { var declaration = global.declaration; var initExpr: ExpressionRef = 0; - if (!global.type) { // infer type + if (global.type == Type.void) { // infer type if (declaration) { if (declaration.type) { - global.type = this.program.resolveType(declaration.type); // reports - if (!global.type) + var resolvedType = this.program.resolveType(declaration.type); // reports + if (!resolvedType) return false; + if (resolvedType == Type.void) { + this.error(DiagnosticCode.Type_0_is_not_assignable_to_type_1, declaration.range, "*", resolvedType.toString()); + return false; + } + global.type = resolvedType; } else if (declaration.initializer) { initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable if (this.currentType == Type.void) { @@ -2119,18 +2124,9 @@ export class Compiler extends DiagnosticEmitter { switch (element.kind) { case ElementKind.LOCAL: - elementType = (element).type; - break; - case ElementKind.GLOBAL: - if (!this.compileGlobal(element)) // reports - return this.module.createUnreachable(); - assert((element).type != null); - elementType = (element).type; - break; - case ElementKind.FIELD: - elementType = (element).type; + elementType = (element).type; break; case ElementKind.PROPERTY: @@ -2174,17 +2170,17 @@ export class Compiler extends DiagnosticEmitter { : this.module.createSetLocal((element).index, valueWithCorrectType); case ElementKind.GLOBAL: - if (!this.compileGlobal(element)) + if (!this.compileGlobal(element)) // reports; not yet compiled if a static field compiled as a global return this.module.createUnreachable(); - assert((element).type != null); - this.currentType = select((element).type, Type.void, tee); + assert((element).type != Type.void); + this.currentType = select((element).type, Type.void, tee); if ((element).isConstant) { this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (element).internalName); return this.module.createUnreachable(); } if (!tee) return this.module.createSetGlobal((element).internalName, valueWithCorrectType); - var globalNativeType = ((element).type).toNativeType(); + var globalNativeType = (element).type.toNativeType(); return this.module.createBlock(null, [ // emulated teeGlobal this.module.createSetGlobal((element).internalName, valueWithCorrectType), this.module.createGetGlobal((element).internalName, globalNativeType) @@ -2431,10 +2427,10 @@ export class Compiler extends DiagnosticEmitter { case ElementKind.GLOBAL: if (element.isBuiltIn) return compileBuiltinGetConstant(this, element); - if (!this.compileGlobal(element)) // reports + if (!this.compileGlobal(element)) // reports; not yet compiled if a static field compiled as a global return this.module.createUnreachable(); - assert((element).type != null); - this.currentType = (element).type; + assert((element).type != Type.void); + this.currentType = (element).type; if ((element).hasConstantValue) return makeInlineConstant(element, this.module); return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); @@ -2504,10 +2500,10 @@ export class Compiler extends DiagnosticEmitter { switch (element.kind) { case ElementKind.GLOBAL: // static property - if (!this.compileGlobal(element)) + if (!this.compileGlobal(element)) // reports; not yet compiled if a static field compiled as a global return this.module.createUnreachable(); - assert((element).type != null); - this.currentType = (element).type; + assert((element).type != Type.void); + this.currentType = (element).type; if ((element).hasConstantValue) return makeInlineConstant(element, this.module); return this.module.createGetGlobal((element).internalName, this.currentType.toNativeType()); diff --git a/src/program.ts b/src/program.ts index d8e0278c..a35d8f73 100644 --- a/src/program.ts +++ b/src/program.ts @@ -333,7 +333,7 @@ export class Program extends DiagnosticEmitter { } } else classPrototype.members = new Map(); - var staticField = new Global(this, name, internalName, declaration, null); + var staticField = new Global(this, name, internalName, declaration, Type.void); classPrototype.members.set(name, staticField); this.elements.set(internalName, staticField); @@ -801,7 +801,7 @@ export class Program extends DiagnosticEmitter { continue; } - var global = new Global(this, declaration.name.name, internalName, declaration, null); + var global = new Global(this, declaration.name.name, internalName, declaration, /* resolved later */ Type.void); global.namespace = namespace; this.elements.set(internalName, global); @@ -949,19 +949,16 @@ export class Program extends DiagnosticEmitter { // at this point we know exactly what the target is, so look up the element within var propertyName = propertyAccess.property.name; - var targetType: Type | null; + var targetType: Type; switch (target.kind) { case ElementKind.GLOBAL: case ElementKind.LOCAL: - targetType = (target).type; - assert(targetType != null); // FIXME: this is a problem because auto-globals might not be - // resolved (and should not be attempted to be resolved) here - if ((targetType).classType) - target = (targetType).classType; - // fall-through - else + case ElementKind.FIELD: + if (!(targetType = (target).type).classType) break; + target = targetType.classType; + // fall-through default: if (target.members) { @@ -1265,8 +1262,8 @@ export class VariableLikeElement extends Element { /** Declaration reference. */ declaration: VariableLikeDeclarationStatement | null; - /** Resolved type, if resolved. */ - type: Type | null; + /** Variable type. Is {@link Type.void} for type-inferred {@link Global}s before compilation. */ + type: Type; /** Constant integer value, if applicable. */ constantIntegerValue: I64 | null = null; /** Constant float value, if applicable. */ @@ -1292,7 +1289,7 @@ export class Global extends VariableLikeElement { kind = ElementKind.GLOBAL; - constructor(program: Program, simpleName: string, internalName: string, declaration: VariableLikeDeclarationStatement | null = null, type: Type | null = null) { + constructor(program: Program, simpleName: string, internalName: string, declaration: VariableLikeDeclarationStatement | null = null, type: Type) { super(program, simpleName, internalName); if (this.declaration = declaration) { if (this.declaration.modifiers) { @@ -1310,7 +1307,7 @@ export class Global extends VariableLikeElement { } else { this.hasConstantValue = true; // built-ins have constant values } - this.type = type; // resolved later if `null` + this.type = type; // resolved later if `void` } } @@ -1339,7 +1336,6 @@ export class Local extends VariableLikeElement { kind = ElementKind.LOCAL; - type: Type; // more specific /** Local index. */ index: i32; @@ -1679,12 +1675,10 @@ export class Function extends Element { } /** A yet unresolved instance field prototype. */ -export class FieldPrototype extends Element { +export class FieldPrototype extends VariableLikeElement { kind = ElementKind.FIELD_PROTOTYPE; - /** Declaration reference. */ - declaration: FieldDeclaration | null; /** Parent class prototype. */ classPrototype: ClassPrototype; @@ -1710,12 +1704,6 @@ export class FieldPrototype extends Element { /** Whether the field is read-only or not. */ get isReadonly(): bool { return (this.flags & ElementFlags.READONLY) != 0; } set isReadonly(is: bool) { if (is) this.flags |= ElementFlags.READONLY; else this.flags &= ~ElementFlags.READONLY; } - - // resolve(contextualTypeArguments: Map | null = null): Field { - // if (!this.declaration) - // throw new Error("declaration expected"); - // this.declaration.type - // } } /** A resolved instance field. */ @@ -1871,7 +1859,7 @@ export class ClassPrototype extends Element { var fieldType = this.program.resolveType(fieldDeclaration.type, instance.contextualTypeArguments); // reports if (fieldType) { var fieldInstance = new Field(member, (member).internalName, fieldType); - switch (fieldType.byteSize) { // align + switch (fieldType.size >> 3) { // align (byteSize might vary if a class type) case 1: break; case 2: if (memoryOffset & 1) ++memoryOffset; break; case 4: if (memoryOffset & 3) memoryOffset = (memoryOffset | 3) + 1; break; diff --git a/tests/compiler/binary.optimized.wast b/tests/compiler/binary.optimized.wast index 3af1279d..17505639 100644 --- a/tests/compiler/binary.optimized.wast +++ b/tests/compiler/binary.optimized.wast @@ -1,7 +1,7 @@ (module (type $v (func)) - (global $binary/i (mut i32) (i32.const 0)) (global $binary/b (mut i32) (i32.const 0)) + (global $binary/i (mut i32) (i32.const 0)) (global $binary/I (mut i64) (i64.const 0)) (global $binary/f (mut f32) (f32.const 0)) (global $binary/F (mut f64) (f64.const 0)) diff --git a/tests/compiler/binary.wast b/tests/compiler/binary.wast index 33840cdc..0019a23e 100644 --- a/tests/compiler/binary.wast +++ b/tests/compiler/binary.wast @@ -1,7 +1,7 @@ (module (type $v (func)) - (global $binary/i (mut i32) (i32.const 0)) (global $binary/b (mut i32) (i32.const 0)) + (global $binary/i (mut i32) (i32.const 0)) (global $binary/I (mut i64) (i64.const 0)) (global $binary/f (mut f32) (f32.const 0)) (global $binary/F (mut f64) (f64.const 0)) diff --git a/tests/compiler/builtins.optimized.wast b/tests/compiler/builtins.optimized.wast index b9262be6..62604dd7 100644 --- a/tests/compiler/builtins.optimized.wast +++ b/tests/compiler/builtins.optimized.wast @@ -1,9 +1,9 @@ (module (type $v (func)) + (global $builtins/b (mut i32) (i32.const 0)) (global $builtins/i (mut i32) (i32.const 0)) (global $builtins/I (mut i64) (i64.const 0)) (global $builtins/f (mut f32) (f32.const 0)) - (global $builtins/b (mut i32) (i32.const 0)) (global $builtins/F (mut f64) (f64.const 0)) (global $builtins/s (mut i32) (i32.const 0)) (memory $0 1) diff --git a/tests/compiler/builtins.wast b/tests/compiler/builtins.wast index 275496d8..c9aac83e 100644 --- a/tests/compiler/builtins.wast +++ b/tests/compiler/builtins.wast @@ -1,9 +1,9 @@ (module (type $v (func)) + (global $builtins/b (mut i32) (i32.const 0)) (global $builtins/i (mut i32) (i32.const 0)) (global $builtins/I (mut i64) (i64.const 0)) (global $builtins/f (mut f32) (f32.const 0)) - (global $builtins/b (mut i32) (i32.const 0)) (global $builtins/F (mut f64) (f64.const 0)) (global $builtins/s (mut i32) (i32.const 0)) (global $i8.MIN_VALUE i32 (i32.const -128)) diff --git a/tests/compiler/declare.wast b/tests/compiler/declare.wast index ad329a7e..482ddb6f 100644 --- a/tests/compiler/declare.wast +++ b/tests/compiler/declare.wast @@ -1,7 +1,7 @@ (module (type $v (func)) - (import "env" "externalFunc" (func $declare/externalFunc)) (import "env" "externalConst" (global $declare/externalConst i32)) + (import "env" "externalFunc" (func $declare/externalFunc)) (import "external" "externalFunc" (func $declare/external.externalFunc)) (import "external" "externalConst" (global $declare/external.externalConst i32)) (global $HEAP_BASE i32 (i32.const 4)) diff --git a/tests/compiler/i64-polyfill.optimized.wast b/tests/compiler/i64-polyfill.optimized.wast index d4b3b0b6..02594bb0 100644 --- a/tests/compiler/i64-polyfill.optimized.wast +++ b/tests/compiler/i64-polyfill.optimized.wast @@ -2,8 +2,8 @@ (type $i (func (result i32))) (type $iiv (func (param i32 i32))) (type $iiiiv (func (param i32 i32 i32 i32))) - (global $../../examples/i64-polyfill/assembly/i64/hi (mut i32) (i32.const 0)) (global $../../examples/i64-polyfill/assembly/i64/lo (mut i32) (i32.const 0)) + (global $../../examples/i64-polyfill/assembly/i64/hi (mut i32) (i32.const 0)) (memory $0 1) (export "getHi" (func $../../examples/i64-polyfill/assembly/i64/getHi)) (export "getLo" (func $../../examples/i64-polyfill/assembly/i64/getLo)) diff --git a/tests/compiler/i64-polyfill.wast b/tests/compiler/i64-polyfill.wast index 48ced19a..f59995d6 100644 --- a/tests/compiler/i64-polyfill.wast +++ b/tests/compiler/i64-polyfill.wast @@ -2,8 +2,8 @@ (type $i (func (result i32))) (type $iiv (func (param i32 i32))) (type $iiiiv (func (param i32 i32 i32 i32))) - (global $../../examples/i64-polyfill/assembly/i64/hi (mut i32) (i32.const 0)) (global $../../examples/i64-polyfill/assembly/i64/lo (mut i32) (i32.const 0)) + (global $../../examples/i64-polyfill/assembly/i64/hi (mut i32) (i32.const 0)) (global $HEAP_BASE i32 (i32.const 4)) (memory $0 1) (export "getHi" (func $../../examples/i64-polyfill/assembly/i64/getHi)) diff --git a/tests/compiler/import.wast b/tests/compiler/import.wast index c88c0dec..36941e8a 100644 --- a/tests/compiler/import.wast +++ b/tests/compiler/import.wast @@ -1,8 +1,8 @@ (module (type $iii (func (param i32 i32) (result i32))) (type $v (func)) - (global $export/b i32 (i32.const 2)) (global $export/a i32 (i32.const 1)) + (global $export/b i32 (i32.const 2)) (global $HEAP_BASE i32 (i32.const 4)) (memory $0 1) (export "memory" (memory $0)) diff --git a/tests/compiler/reexport.optimized.wast b/tests/compiler/reexport.optimized.wast index 157cceed..f3fc9b36 100644 --- a/tests/compiler/reexport.optimized.wast +++ b/tests/compiler/reexport.optimized.wast @@ -1,8 +1,8 @@ (module (type $iii (func (param i32 i32) (result i32))) (type $v (func)) - (global $export/b i32 (i32.const 2)) (global $export/a i32 (i32.const 1)) + (global $export/b i32 (i32.const 2)) (memory $0 1) (export "add" (func $export/add)) (export "renamed_sub" (func $export/sub)) diff --git a/tests/compiler/reexport.wast b/tests/compiler/reexport.wast index aca3b6a8..132b7600 100644 --- a/tests/compiler/reexport.wast +++ b/tests/compiler/reexport.wast @@ -1,8 +1,8 @@ (module (type $iii (func (param i32 i32) (result i32))) (type $v (func)) - (global $export/b i32 (i32.const 2)) (global $export/a i32 (i32.const 1)) + (global $export/b i32 (i32.const 2)) (global $HEAP_BASE i32 (i32.const 4)) (memory $0 1) (export "add" (func $export/add)) diff --git a/tests/compiler/tlsf.wast b/tests/compiler/tlsf.wast index ae3f0737..b0fa1119 100644 --- a/tests/compiler/tlsf.wast +++ b/tests/compiler/tlsf.wast @@ -8,19 +8,27 @@ (type $v (func)) (global $tlsf/ALIGN_SIZE_LOG2 i32 (i32.const 3)) (global $tlsf/ALIGN_SIZE i32 (i32.const 8)) + (global $tlsf/SL_INDEX_COUNT_LOG2 i32 (i32.const 5)) + (global $tlsf/FL_INDEX_MAX i32 (i32.const 30)) + (global $tlsf/SL_INDEX_COUNT i32 (i32.const 32)) + (global $tlsf/FL_INDEX_SHIFT i32 (i32.const 8)) + (global $tlsf/FL_INDEX_COUNT i32 (i32.const 23)) + (global $tlsf/SMALL_BLOCK_SIZE i32 (i32.const 256)) (global $tlsf/BLOCK$PREV_PHYS_BLOCK_OFFSET i32 (i32.const 0)) (global $tlsf/BLOCK$TAGGED_SIZE_OFFSET i32 (i32.const 4)) (global $tlsf/BLOCK$NEXT_FREE_OFFSET i32 (i32.const 8)) (global $tlsf/BLOCK$PREV_FREE_OFFSET i32 (i32.const 12)) (global $tlsf/BLOCK$SIZE i32 (i32.const 16)) + (global $tlsf/BLOCK_HEADER_FREE_BIT i32 (i32.const 1)) + (global $tlsf/BLOCK_HEADER_PREV_FREE_BIT i32 (i32.const 2)) + (global $tlsf/BLOCK_OVERHEAD i32 (i32.const 4)) + (global $tlsf/BLOCK_START_OFFSET i32 (i32.const 8)) + (global $tlsf/BLOCK_SIZE_MIN i32 (i32.const 12)) + (global $tlsf/BLOCK_SIZE_MAX i32 (i32.const 1073741824)) (global $tlsf/CONTROL$FL_BITMAP_OFFSET i32 (i32.const 16)) - (global $tlsf/FL_INDEX_MAX i32 (i32.const 30)) - (global $tlsf/SL_INDEX_COUNT_LOG2 i32 (i32.const 5)) - (global $tlsf/FL_INDEX_SHIFT i32 (i32.const 8)) - (global $tlsf/FL_INDEX_COUNT i32 (i32.const 23)) (global $tlsf/CONTROL$SL_BITMAP_OFFSET i32 (i32.const 20)) - (global $tlsf/SL_INDEX_COUNT i32 (i32.const 32)) (global $tlsf/CONTROL$BLOCKS_OFFSET i32 (i32.const 112)) + (global $tlsf/CONTROL$SIZE i32 (i32.const 3056)) (global $HEAP_BASE i32 (i32.const 4)) (memory $0 1) (export "control$construct" (func $tlsf/control$construct))