Always compile globals because initializers might have side effects

This commit is contained in:
dcodeIO 2018-01-04 06:00:42 +01:00
parent 50dea3b1df
commit 7e9b58428b
14 changed files with 60 additions and 69 deletions

View File

@ -151,10 +151,9 @@ export function initialize(program: Program): void {
/** Adds a built-in constant to the specified program. */ /** Adds a built-in constant to the specified program. */
function addConstant(program: Program, name: string, type: Type): Global { 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.isBuiltIn = true;
global.isConstant = true; global.isConstant = true;
global.type = type;
program.elements.set(name, global); program.elements.set(name, global);
return global; return global;
} }

View File

@ -21,7 +21,8 @@ import {
NativeType, NativeType,
FunctionTypeRef, FunctionTypeRef,
FunctionRef, FunctionRef,
ExpressionId ExpressionId,
readString
} from "./module"; } from "./module";
import { import {
@ -295,9 +296,8 @@ export class Compiler extends DiagnosticEmitter {
this.compileNamespaceDeclaration(<NamespaceDeclaration>statement); this.compileNamespaceDeclaration(<NamespaceDeclaration>statement);
break; break;
case NodeKind.VARIABLE: case NodeKind.VARIABLE: // global
if (noTreeShaking || source.isEntry && hasModifier(ModifierKind.EXPORT, (<VariableStatement>statement).modifiers)) this.compileVariableStatement(<VariableStatement>statement); // always because initializers might have side effects
this.compileVariableStatement(<VariableStatement>statement);
break; break;
case NodeKind.EXPORT: case NodeKind.EXPORT:
@ -344,12 +344,17 @@ export class Compiler extends DiagnosticEmitter {
var declaration = global.declaration; var declaration = global.declaration;
var initExpr: ExpressionRef = 0; var initExpr: ExpressionRef = 0;
if (!global.type) { // infer type if (global.type == Type.void) { // infer type
if (declaration) { if (declaration) {
if (declaration.type) { if (declaration.type) {
global.type = this.program.resolveType(declaration.type); // reports var resolvedType = this.program.resolveType(declaration.type); // reports
if (!global.type) if (!resolvedType)
return false; 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) { } else if (declaration.initializer) {
initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable
if (this.currentType == Type.void) { if (this.currentType == Type.void) {
@ -2119,18 +2124,9 @@ export class Compiler extends DiagnosticEmitter {
switch (element.kind) { switch (element.kind) {
case ElementKind.LOCAL: case ElementKind.LOCAL:
elementType = (<Local>element).type;
break;
case ElementKind.GLOBAL: case ElementKind.GLOBAL:
if (!this.compileGlobal(<Global>element)) // reports
return this.module.createUnreachable();
assert((<Global>element).type != null);
elementType = <Type>(<Global>element).type;
break;
case ElementKind.FIELD: case ElementKind.FIELD:
elementType = (<Field>element).type; elementType = (<VariableLikeElement>element).type;
break; break;
case ElementKind.PROPERTY: case ElementKind.PROPERTY:
@ -2174,17 +2170,17 @@ export class Compiler extends DiagnosticEmitter {
: this.module.createSetLocal((<Local>element).index, valueWithCorrectType); : this.module.createSetLocal((<Local>element).index, valueWithCorrectType);
case ElementKind.GLOBAL: case ElementKind.GLOBAL:
if (!this.compileGlobal(<Global>element)) if (!this.compileGlobal(<Global>element)) // reports; not yet compiled if a static field compiled as a global
return this.module.createUnreachable(); return this.module.createUnreachable();
assert((<Global>element).type != null); assert((<Global>element).type != Type.void);
this.currentType = select<Type>(<Type>(<Global>element).type, Type.void, tee); this.currentType = select<Type>((<Global>element).type, Type.void, tee);
if ((<Local>element).isConstant) { if ((<Local>element).isConstant) {
this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (<Local>element).internalName); this.error(DiagnosticCode.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, expression.range, (<Local>element).internalName);
return this.module.createUnreachable(); return this.module.createUnreachable();
} }
if (!tee) if (!tee)
return this.module.createSetGlobal((<Global>element).internalName, valueWithCorrectType); return this.module.createSetGlobal((<Global>element).internalName, valueWithCorrectType);
var globalNativeType = (<Type>(<Global>element).type).toNativeType(); var globalNativeType = (<Global>element).type.toNativeType();
return this.module.createBlock(null, [ // emulated teeGlobal return this.module.createBlock(null, [ // emulated teeGlobal
this.module.createSetGlobal((<Global>element).internalName, valueWithCorrectType), this.module.createSetGlobal((<Global>element).internalName, valueWithCorrectType),
this.module.createGetGlobal((<Global>element).internalName, globalNativeType) this.module.createGetGlobal((<Global>element).internalName, globalNativeType)
@ -2431,10 +2427,10 @@ export class Compiler extends DiagnosticEmitter {
case ElementKind.GLOBAL: case ElementKind.GLOBAL:
if (element.isBuiltIn) if (element.isBuiltIn)
return compileBuiltinGetConstant(this, <Global>element); return compileBuiltinGetConstant(this, <Global>element);
if (!this.compileGlobal(<Global>element)) // reports if (!this.compileGlobal(<Global>element)) // reports; not yet compiled if a static field compiled as a global
return this.module.createUnreachable(); return this.module.createUnreachable();
assert((<Global>element).type != null); assert((<Global>element).type != Type.void);
this.currentType = <Type>(<Global>element).type; this.currentType = (<Global>element).type;
if ((<Global>element).hasConstantValue) if ((<Global>element).hasConstantValue)
return makeInlineConstant(<Global>element, this.module); return makeInlineConstant(<Global>element, this.module);
return this.module.createGetGlobal((<Global>element).internalName, this.currentType.toNativeType()); return this.module.createGetGlobal((<Global>element).internalName, this.currentType.toNativeType());
@ -2504,10 +2500,10 @@ export class Compiler extends DiagnosticEmitter {
switch (element.kind) { switch (element.kind) {
case ElementKind.GLOBAL: // static property case ElementKind.GLOBAL: // static property
if (!this.compileGlobal(<Global>element)) if (!this.compileGlobal(<Global>element)) // reports; not yet compiled if a static field compiled as a global
return this.module.createUnreachable(); return this.module.createUnreachable();
assert((<Global>element).type != null); assert((<Global>element).type != Type.void);
this.currentType = <Type>(<Global>element).type; this.currentType = (<Global>element).type;
if ((<Global>element).hasConstantValue) if ((<Global>element).hasConstantValue)
return makeInlineConstant(<Global>element, this.module); return makeInlineConstant(<Global>element, this.module);
return this.module.createGetGlobal((<Global>element).internalName, this.currentType.toNativeType()); return this.module.createGetGlobal((<Global>element).internalName, this.currentType.toNativeType());

View File

@ -333,7 +333,7 @@ export class Program extends DiagnosticEmitter {
} }
} else } else
classPrototype.members = new Map(); 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); classPrototype.members.set(name, staticField);
this.elements.set(internalName, staticField); this.elements.set(internalName, staticField);
@ -801,7 +801,7 @@ export class Program extends DiagnosticEmitter {
continue; 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; global.namespace = namespace;
this.elements.set(internalName, global); 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 // at this point we know exactly what the target is, so look up the element within
var propertyName = propertyAccess.property.name; var propertyName = propertyAccess.property.name;
var targetType: Type | null; var targetType: Type;
switch (target.kind) { switch (target.kind) {
case ElementKind.GLOBAL: case ElementKind.GLOBAL:
case ElementKind.LOCAL: case ElementKind.LOCAL:
targetType = (<VariableLikeElement>target).type; case ElementKind.FIELD:
assert(targetType != null); // FIXME: this is a problem because auto-globals might not be if (!(targetType = (<VariableLikeElement>target).type).classType)
// resolved (and should not be attempted to be resolved) here
if ((<Type>targetType).classType)
target = <Class>(<Type>targetType).classType;
// fall-through
else
break; break;
target = <Class>targetType.classType;
// fall-through
default: default:
if (target.members) { if (target.members) {
@ -1265,8 +1262,8 @@ export class VariableLikeElement extends Element {
/** Declaration reference. */ /** Declaration reference. */
declaration: VariableLikeDeclarationStatement | null; declaration: VariableLikeDeclarationStatement | null;
/** Resolved type, if resolved. */ /** Variable type. Is {@link Type.void} for type-inferred {@link Global}s before compilation. */
type: Type | null; type: Type;
/** Constant integer value, if applicable. */ /** Constant integer value, if applicable. */
constantIntegerValue: I64 | null = null; constantIntegerValue: I64 | null = null;
/** Constant float value, if applicable. */ /** Constant float value, if applicable. */
@ -1292,7 +1289,7 @@ export class Global extends VariableLikeElement {
kind = ElementKind.GLOBAL; 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); super(program, simpleName, internalName);
if (this.declaration = declaration) { if (this.declaration = declaration) {
if (this.declaration.modifiers) { if (this.declaration.modifiers) {
@ -1310,7 +1307,7 @@ export class Global extends VariableLikeElement {
} else { } else {
this.hasConstantValue = true; // built-ins have constant values 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; kind = ElementKind.LOCAL;
type: Type; // more specific
/** Local index. */ /** Local index. */
index: i32; index: i32;
@ -1679,12 +1675,10 @@ export class Function extends Element {
} }
/** A yet unresolved instance field prototype. */ /** A yet unresolved instance field prototype. */
export class FieldPrototype extends Element { export class FieldPrototype extends VariableLikeElement {
kind = ElementKind.FIELD_PROTOTYPE; kind = ElementKind.FIELD_PROTOTYPE;
/** Declaration reference. */
declaration: FieldDeclaration | null;
/** Parent class prototype. */ /** Parent class prototype. */
classPrototype: ClassPrototype; classPrototype: ClassPrototype;
@ -1710,12 +1704,6 @@ export class FieldPrototype extends Element {
/** Whether the field is read-only or not. */ /** Whether the field is read-only or not. */
get isReadonly(): bool { return (this.flags & ElementFlags.READONLY) != 0; } get isReadonly(): bool { return (this.flags & ElementFlags.READONLY) != 0; }
set isReadonly(is: bool) { if (is) this.flags |= ElementFlags.READONLY; else this.flags &= ~ElementFlags.READONLY; } set isReadonly(is: bool) { if (is) this.flags |= ElementFlags.READONLY; else this.flags &= ~ElementFlags.READONLY; }
// resolve(contextualTypeArguments: Map<string,Type> | null = null): Field {
// if (!this.declaration)
// throw new Error("declaration expected");
// this.declaration.type
// }
} }
/** A resolved instance field. */ /** A resolved instance field. */
@ -1871,7 +1859,7 @@ export class ClassPrototype extends Element {
var fieldType = this.program.resolveType(fieldDeclaration.type, instance.contextualTypeArguments); // reports var fieldType = this.program.resolveType(fieldDeclaration.type, instance.contextualTypeArguments); // reports
if (fieldType) { if (fieldType) {
var fieldInstance = new Field(<FieldPrototype>member, (<FieldPrototype>member).internalName, fieldType); var fieldInstance = new Field(<FieldPrototype>member, (<FieldPrototype>member).internalName, fieldType);
switch (fieldType.byteSize) { // align switch (fieldType.size >> 3) { // align (byteSize might vary if a class type)
case 1: break; case 1: break;
case 2: if (memoryOffset & 1) ++memoryOffset; break; case 2: if (memoryOffset & 1) ++memoryOffset; break;
case 4: if (memoryOffset & 3) memoryOffset = (memoryOffset | 3) + 1; break; case 4: if (memoryOffset & 3) memoryOffset = (memoryOffset | 3) + 1; break;

View File

@ -1,7 +1,7 @@
(module (module
(type $v (func)) (type $v (func))
(global $binary/i (mut i32) (i32.const 0))
(global $binary/b (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/I (mut i64) (i64.const 0))
(global $binary/f (mut f32) (f32.const 0)) (global $binary/f (mut f32) (f32.const 0))
(global $binary/F (mut f64) (f64.const 0)) (global $binary/F (mut f64) (f64.const 0))

View File

@ -1,7 +1,7 @@
(module (module
(type $v (func)) (type $v (func))
(global $binary/i (mut i32) (i32.const 0))
(global $binary/b (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/I (mut i64) (i64.const 0))
(global $binary/f (mut f32) (f32.const 0)) (global $binary/f (mut f32) (f32.const 0))
(global $binary/F (mut f64) (f64.const 0)) (global $binary/F (mut f64) (f64.const 0))

View File

@ -1,9 +1,9 @@
(module (module
(type $v (func)) (type $v (func))
(global $builtins/b (mut i32) (i32.const 0))
(global $builtins/i (mut i32) (i32.const 0)) (global $builtins/i (mut i32) (i32.const 0))
(global $builtins/I (mut i64) (i64.const 0)) (global $builtins/I (mut i64) (i64.const 0))
(global $builtins/f (mut f32) (f32.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/F (mut f64) (f64.const 0))
(global $builtins/s (mut i32) (i32.const 0)) (global $builtins/s (mut i32) (i32.const 0))
(memory $0 1) (memory $0 1)

View File

@ -1,9 +1,9 @@
(module (module
(type $v (func)) (type $v (func))
(global $builtins/b (mut i32) (i32.const 0))
(global $builtins/i (mut i32) (i32.const 0)) (global $builtins/i (mut i32) (i32.const 0))
(global $builtins/I (mut i64) (i64.const 0)) (global $builtins/I (mut i64) (i64.const 0))
(global $builtins/f (mut f32) (f32.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/F (mut f64) (f64.const 0))
(global $builtins/s (mut i32) (i32.const 0)) (global $builtins/s (mut i32) (i32.const 0))
(global $i8.MIN_VALUE i32 (i32.const -128)) (global $i8.MIN_VALUE i32 (i32.const -128))

View File

@ -1,7 +1,7 @@
(module (module
(type $v (func)) (type $v (func))
(import "env" "externalFunc" (func $declare/externalFunc))
(import "env" "externalConst" (global $declare/externalConst i32)) (import "env" "externalConst" (global $declare/externalConst i32))
(import "env" "externalFunc" (func $declare/externalFunc))
(import "external" "externalFunc" (func $declare/external.externalFunc)) (import "external" "externalFunc" (func $declare/external.externalFunc))
(import "external" "externalConst" (global $declare/external.externalConst i32)) (import "external" "externalConst" (global $declare/external.externalConst i32))
(global $HEAP_BASE i32 (i32.const 4)) (global $HEAP_BASE i32 (i32.const 4))

View File

@ -2,8 +2,8 @@
(type $i (func (result i32))) (type $i (func (result i32)))
(type $iiv (func (param i32 i32))) (type $iiv (func (param i32 i32)))
(type $iiiiv (func (param i32 i32 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/lo (mut i32) (i32.const 0))
(global $../../examples/i64-polyfill/assembly/i64/hi (mut i32) (i32.const 0))
(memory $0 1) (memory $0 1)
(export "getHi" (func $../../examples/i64-polyfill/assembly/i64/getHi)) (export "getHi" (func $../../examples/i64-polyfill/assembly/i64/getHi))
(export "getLo" (func $../../examples/i64-polyfill/assembly/i64/getLo)) (export "getLo" (func $../../examples/i64-polyfill/assembly/i64/getLo))

View File

@ -2,8 +2,8 @@
(type $i (func (result i32))) (type $i (func (result i32)))
(type $iiv (func (param i32 i32))) (type $iiv (func (param i32 i32)))
(type $iiiiv (func (param i32 i32 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/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)) (global $HEAP_BASE i32 (i32.const 4))
(memory $0 1) (memory $0 1)
(export "getHi" (func $../../examples/i64-polyfill/assembly/i64/getHi)) (export "getHi" (func $../../examples/i64-polyfill/assembly/i64/getHi))

View File

@ -1,8 +1,8 @@
(module (module
(type $iii (func (param i32 i32) (result i32))) (type $iii (func (param i32 i32) (result i32)))
(type $v (func)) (type $v (func))
(global $export/b i32 (i32.const 2))
(global $export/a i32 (i32.const 1)) (global $export/a i32 (i32.const 1))
(global $export/b i32 (i32.const 2))
(global $HEAP_BASE i32 (i32.const 4)) (global $HEAP_BASE i32 (i32.const 4))
(memory $0 1) (memory $0 1)
(export "memory" (memory $0)) (export "memory" (memory $0))

View File

@ -1,8 +1,8 @@
(module (module
(type $iii (func (param i32 i32) (result i32))) (type $iii (func (param i32 i32) (result i32)))
(type $v (func)) (type $v (func))
(global $export/b i32 (i32.const 2))
(global $export/a i32 (i32.const 1)) (global $export/a i32 (i32.const 1))
(global $export/b i32 (i32.const 2))
(memory $0 1) (memory $0 1)
(export "add" (func $export/add)) (export "add" (func $export/add))
(export "renamed_sub" (func $export/sub)) (export "renamed_sub" (func $export/sub))

View File

@ -1,8 +1,8 @@
(module (module
(type $iii (func (param i32 i32) (result i32))) (type $iii (func (param i32 i32) (result i32)))
(type $v (func)) (type $v (func))
(global $export/b i32 (i32.const 2))
(global $export/a i32 (i32.const 1)) (global $export/a i32 (i32.const 1))
(global $export/b i32 (i32.const 2))
(global $HEAP_BASE i32 (i32.const 4)) (global $HEAP_BASE i32 (i32.const 4))
(memory $0 1) (memory $0 1)
(export "add" (func $export/add)) (export "add" (func $export/add))

View File

@ -8,19 +8,27 @@
(type $v (func)) (type $v (func))
(global $tlsf/ALIGN_SIZE_LOG2 i32 (i32.const 3)) (global $tlsf/ALIGN_SIZE_LOG2 i32 (i32.const 3))
(global $tlsf/ALIGN_SIZE i32 (i32.const 8)) (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$PREV_PHYS_BLOCK_OFFSET i32 (i32.const 0))
(global $tlsf/BLOCK$TAGGED_SIZE_OFFSET i32 (i32.const 4)) (global $tlsf/BLOCK$TAGGED_SIZE_OFFSET i32 (i32.const 4))
(global $tlsf/BLOCK$NEXT_FREE_OFFSET i32 (i32.const 8)) (global $tlsf/BLOCK$NEXT_FREE_OFFSET i32 (i32.const 8))
(global $tlsf/BLOCK$PREV_FREE_OFFSET i32 (i32.const 12)) (global $tlsf/BLOCK$PREV_FREE_OFFSET i32 (i32.const 12))
(global $tlsf/BLOCK$SIZE i32 (i32.const 16)) (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/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/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$BLOCKS_OFFSET i32 (i32.const 112))
(global $tlsf/CONTROL$SIZE i32 (i32.const 3056))
(global $HEAP_BASE i32 (i32.const 4)) (global $HEAP_BASE i32 (i32.const 4))
(memory $0 1) (memory $0 1)
(export "control$construct" (func $tlsf/control$construct)) (export "control$construct" (func $tlsf/control$construct))