Get rid of determineExpressionType

This commit is contained in:
dcodeIO 2017-12-23 17:49:06 +01:00
parent ca9c79185b
commit d7c069b692
5 changed files with 292 additions and 100 deletions

View File

@ -179,11 +179,13 @@ export class Compiler extends DiagnosticEmitter {
super(program.diagnostics);
this.program = program;
this.options = options ? options : new Options();
this.memoryOffset = new U64(this.options.target == Target.WASM64 ? 8 : 4, 0); // leave space for `null`
this.module = this.options.noEmit ? Module.createStub() : Module.create();
// set up start function
const startFunctionTemplate: FunctionPrototype = new FunctionPrototype(program, "start", "start", null);
const startFunctionInstance: Function = new Function(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null);
this.currentFunction = this.startFunction = startFunctionInstance;
this.memoryOffset = new U64(this.options.target == Target.WASM64 ? 8 : 4, 0); // leave space for `null`
}
/** Performs compilation of the underlying {@link Program} to a {@link Module}. */
@ -208,7 +210,9 @@ export class Compiler extends DiagnosticEmitter {
if (!typeRef)
typeRef = this.module.addFunctionType("v", NativeType.None, []);
this.module.setStart(
this.module.addFunction(this.startFunction.prototype.internalName, typeRef, typesToNativeTypes(this.startFunction.additionalLocals), this.module.createBlock(null, this.startFunctionBody))
this.module.addFunction(this.startFunction.prototype.internalName, typeRef, typesToNativeTypes(this.startFunction.additionalLocals),
this.module.createBlock(null, this.startFunctionBody)
)
);
}
@ -226,7 +230,7 @@ export class Compiler extends DiagnosticEmitter {
initial.or32(0xffff);
initial.add32(1);
}
initial.shru32(16); // initial size in 64k pages
initial.shru32(16); // now is initial size in 64k pages
this.module.setMemory(initial.toI32(), Module.MAX_MEMORY_WASM32 /* TODO: not WASM64 compatible yet */, this.memorySegments, this.options.target, "memory");
return this.module;
@ -296,7 +300,8 @@ export class Compiler extends DiagnosticEmitter {
default: {
const previousFunction: Function = this.currentFunction;
this.currentFunction = this.startFunction;
this.startFunctionBody.push(this.compileStatement(statement));
const expr: ExpressionRef = this.compileStatement(statement);
if (!this.module.noEmit) this.startFunctionBody.push(expr);
this.currentFunction = previousFunction;
break;
}
@ -309,8 +314,8 @@ export class Compiler extends DiagnosticEmitter {
compileGlobalDeclaration(declaration: VariableDeclaration, isConst: bool): Global | null {
const element: Element | null = <Element | null>this.program.elements.get(declaration.internalName);
if (!element || element.kind != ElementKind.GLOBAL)
throw new Error("unexpected missing global");
if (!this.compileGlobal(<Global>element))
throw new Error("global expected");
if (!this.compileGlobal(<Global>element)) // reports
return null;
if (isModuleExport(element, declaration)) {
if ((<Global>element).hasConstantValue)
@ -322,54 +327,58 @@ export class Compiler extends DiagnosticEmitter {
}
compileGlobal(global: Global): bool {
if (global.isCompiled)
return true;
if (global.isBuiltIn && compileBuiltinGetGlobal(this, global))
if (global.isCompiled || (global.isBuiltIn && compileBuiltinGetGlobal(this, global)))
return true;
const declaration: VariableLikeDeclarationStatement | null = global.declaration;
if (!global.type) {
if (declaration && declaration.type) {
global.type = this.program.resolveType(declaration.type); // reports
if (!global.type)
let type: Type | null = null;
let initExpr: ExpressionRef = 0;
if (!global.type) { // infer type
if (declaration) {
if (declaration.type) {
global.type = this.program.resolveType(declaration.type); // reports
if (!global.type)
return false;
} else if (declaration.initializer) {
initExpr = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable
if (this.currentType == Type.void)
return false;
global.type = this.currentType;
} else {
this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd);
return false;
} else if (declaration && declaration.initializer) {
global.type = this.determineExpressionType(declaration.initializer);
if (!global.type)
return false;
} else if (declaration) {
this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd);
return false;
}
} else
throw new Error("unable to infer type");
throw new Error("declaration expected");
}
if (this.module.noEmit)
return true;
const nativeType: NativeType = typeToNativeType(global.type);
let initializer: ExpressionRef;
let initializeInStart: bool = false;
if (global.hasConstantValue) {
if (global.type.isLongInteger)
initializer = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0);
initExpr = global.constantIntegerValue ? this.module.createI64(global.constantIntegerValue.lo, global.constantIntegerValue.hi) : this.module.createI64(0, 0);
else if (global.type.kind == TypeKind.F32)
initializer = this.module.createF32(global.constantFloatValue);
initExpr = this.module.createF32(global.constantFloatValue);
else if (global.type.kind == TypeKind.F64)
initializer = this.module.createF64(global.constantFloatValue);
initExpr = this.module.createF64(global.constantFloatValue);
else if (global.type.isSmallInteger) {
if (global.type.isSignedInteger) {
const shift: i32 = global.type.smallIntegerShift;
initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() << shift >> shift : 0);
initExpr = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() << shift >> shift : 0);
} else
initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() & global.type.smallIntegerMask: 0);
initExpr = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() & global.type.smallIntegerMask: 0);
} else
initializer = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() : 0);
initExpr = this.module.createI32(global.constantIntegerValue ? global.constantIntegerValue.toI32() : 0);
} else if (declaration) {
if (declaration.initializer) {
initializer = this.compileExpression(declaration.initializer, global.type);
if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) {
if (!initExpr)
initExpr = this.compileExpression(declaration.initializer, global.type);
if (!this.module.noEmit && _BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
if (!global.isMutable) {
initializer = this.precomputeExpressionRef(initializer);
if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) {
initExpr = this.precomputeExpressionRef(initExpr);
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range);
initializeInStart = true;
}
@ -377,33 +386,35 @@ export class Compiler extends DiagnosticEmitter {
initializeInStart = true;
}
} else
initializer = typeToNativeZero(this.module, global.type);
initExpr = typeToNativeZero(this.module, global.type);
} else
throw new Error("unexpected missing declaration or constant value");
throw new Error("declaration expected");
const internalName: string = global.internalName;
if (initializeInStart) {
this.module.addGlobal(internalName, nativeType, true, typeToNativeZero(this.module, global.type));
this.startFunctionBody.push(this.module.createSetGlobal(internalName, initializer));
const setExpr: ExpressionRef = this.module.createSetGlobal(internalName, initExpr);
if (!this.module.noEmit)
this.startFunctionBody.push(setExpr);
} else {
this.module.addGlobal(internalName, nativeType, global.isMutable, initializer);
if (!global.isMutable) {
const exprType: NativeType = _BinaryenExpressionGetType(initializer);
this.module.addGlobal(internalName, nativeType, global.isMutable, initExpr);
if (!global.isMutable && !this.module.noEmit) {
const exprType: NativeType = _BinaryenExpressionGetType(initExpr);
switch (exprType) {
case NativeType.I32:
global.constantIntegerValue = new I64(_BinaryenConstGetValueI32(initializer), 0);
global.constantIntegerValue = new I64(_BinaryenConstGetValueI32(initExpr), 0);
break;
case NativeType.I64:
global.constantIntegerValue = new I64(_BinaryenConstGetValueI64Low(initializer), _BinaryenConstGetValueI64High(initializer));
global.constantIntegerValue = new I64(_BinaryenConstGetValueI64Low(initExpr), _BinaryenConstGetValueI64High(initExpr));
break;
case NativeType.F32:
global.constantFloatValue = _BinaryenConstGetValueF32(initializer);
global.constantFloatValue = _BinaryenConstGetValueF32(initExpr);
break;
case NativeType.F64:
global.constantFloatValue = _BinaryenConstGetValueF64(initializer);
global.constantFloatValue = _BinaryenConstGetValueF64(initExpr);
break;
default:
throw new Error("unexpected initializer type");
throw new Error("concrete type expected");
}
global.hasConstantValue = true;
}
@ -417,13 +428,14 @@ export class Compiler extends DiagnosticEmitter {
compileEnumDeclaration(declaration: EnumDeclaration): void {
const element: Element | null = <Element | null>this.program.elements.get(declaration.internalName);
if (!element || element.kind != ElementKind.ENUM)
throw new Error("unexpected missing enum");
throw new Error("enum expected");
this.compileEnum(<Enum>element);
}
compileEnum(element: Enum): void {
if (element.isCompiled)
return;
let previousValue: EnumValue | null = null;
if (element.members)
for (let [key, member] of element.members) {
@ -434,45 +446,49 @@ export class Compiler extends DiagnosticEmitter {
this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue));
} else if (val.declaration) {
const declaration: EnumValueDeclaration = val.declaration;
let initializer: ExpressionRef;
let initializeInStart: bool = false;
let initExpr: ExpressionRef;
let initInStart: bool = false;
if (declaration.value) {
initializer = this.compileExpression(<Expression>declaration.value, Type.i32);
if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) {
initializer = this.precomputeExpressionRef(initializer);
if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) {
initExpr = this.compileExpression(<Expression>declaration.value, Type.i32);
if (!this.module.noEmit && _BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
initExpr = this.precomputeExpressionRef(initExpr);
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
if (element.isConstant)
this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range);
initializeInStart = true;
initInStart = true;
}
}
} else if (previousValue == null) {
initializer = this.module.createI32(0);
initExpr = this.module.createI32(0);
} else if (previousValue.hasConstantValue) {
initializer = this.module.createI32(previousValue.constantValue + 1);
initExpr = this.module.createI32(previousValue.constantValue + 1);
} else {
// in TypeScript this errors with TS1061, but actually we can do:
initializer = this.module.createBinary(BinaryOp.AddI32,
initExpr = this.module.createBinary(BinaryOp.AddI32,
this.module.createGetGlobal(previousValue.internalName, NativeType.I32),
this.module.createI32(1)
);
if (element.isConstant)
this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range);
initializeInStart = true;
initInStart = true;
}
if (initializeInStart) {
if (initInStart) {
this.module.addGlobal(val.internalName, NativeType.I32, true, this.module.createI32(0));
this.startFunctionBody.push(this.module.createSetGlobal(val.internalName, initializer));
const setExpr: ExpressionRef = this.module.createSetGlobal(val.internalName, initExpr);
if (!this.module.noEmit)
this.startFunctionBody.push(setExpr);
} else {
this.module.addGlobal(val.internalName, NativeType.I32, false, initializer);
if (_BinaryenExpressionGetType(initializer) == NativeType.I32) {
val.constantValue = _BinaryenConstGetValueI32(initializer);
val.hasConstantValue = true;
} else
throw new Error("unexpected initializer type");
this.module.addGlobal(val.internalName, NativeType.I32, false, initExpr);
if (!this.module.noEmit) {
if (_BinaryenExpressionGetType(initExpr) == NativeType.I32) {
val.constantValue = _BinaryenConstGetValueI32(initExpr);
val.hasConstantValue = true;
} else
throw new Error("i32 expected");
}
}
} else
throw new Error("unexpected missing declaration or constant value");
throw new Error("declaration expected");
previousValue = <EnumValue>val;
}
element.isCompiled = true;
@ -505,7 +521,7 @@ export class Compiler extends DiagnosticEmitter {
const declaration: FunctionDeclaration | null = instance.prototype.declaration;
if (!declaration)
throw new Error("unexpected missing declaration");
throw new Error("declaration expected"); // built-ins are not compiled here
if (instance.isDeclared) {
if (declaration.statements) {
@ -600,7 +616,9 @@ export class Compiler extends DiagnosticEmitter {
}
compileNamespace(ns: Namespace): void {
if (!ns.members) return;
if (!ns.members)
return;
const noTreeShaking: bool = this.options.noTreeShaking;
for (let [name, element] of ns.members) {
switch (element.kind) {
@ -714,20 +732,6 @@ export class Compiler extends DiagnosticEmitter {
return segment;
}
// types
// TODO: try to get rid of this
determineExpressionType(expression: Expression, contextualType: Type = Type.void): Type {
const previousType: Type = this.currentType;
const previousNoEmit: bool = this.module.noEmit;
this.module.noEmit = true;
this.compileExpression(expression, contextualType, ConversionKind.NONE); // now performs a dry run
const type: Type = this.currentType;
this.currentType = previousType;
this.module.noEmit = previousNoEmit;
return type;
}
// statements
compileStatement(statement: Statement): ExpressionRef {
@ -785,11 +789,11 @@ export class Compiler extends DiagnosticEmitter {
}
compileStatements(statements: Statement[]): ExpressionRef[] {
const k: i32 = statements.length;
let i: i32 = 0, k: i32 = statements.length;
const stmts: ExpressionRef[] = new Array(k);
for (let i: i32 = 0; i < k; ++i)
for (; i < k; ++i)
stmts[i] = this.compileStatement(statements[i]);
return stmts;
return stmts; // array of 0-es in noEmit-mode
}
compileBlockStatement(statement: BlockStatement): ExpressionRef {
@ -802,8 +806,10 @@ export class Compiler extends DiagnosticEmitter {
}
compileBreakStatement(statement: BreakStatement): ExpressionRef {
if (statement.label)
throw new Error("not implemented");
if (statement.label) {
this.error(DiagnosticCode.Operation_not_supported, statement.label.range);
return this.module.createUnreachable();
}
const context: string | null = this.currentFunction.breakContext;
if (context != null)
return this.module.createBreak("break|" + (<string>context));
@ -812,8 +818,10 @@ export class Compiler extends DiagnosticEmitter {
}
compileContinueStatement(statement: ContinueStatement): ExpressionRef {
if (statement.label)
throw new Error("not implemented");
if (statement.label) {
this.error(DiagnosticCode.Operation_not_supported, statement.label.range);
return this.module.createUnreachable();
}
const context: string | null = this.currentFunction.breakContext;
if (context != null && !this.disallowContinue)
return this.module.createBreak("continue|" + (<string>context));
@ -973,13 +981,16 @@ export class Compiler extends DiagnosticEmitter {
const declaration: VariableDeclaration = declarations[i];
const name: string = declaration.name.name;
let type: Type | null = null;
let init: ExpressionRef = 0;
if (declaration.type) {
type = this.program.resolveType(<TypeNode>declaration.type, this.currentFunction.contextualTypeArguments, true); // reports
if (!type)
continue;
} else if (declaration.initializer) {
type = this.determineExpressionType(declaration.initializer); // reports
if (!type)
if (declaration.initializer)
init = this.compileExpression(declaration.initializer, type); // reports and returns unreachable
} else if (declaration.initializer) { // infer type
init = this.compileExpression(declaration.initializer, Type.void, ConversionKind.NONE); // reports and returns unreachable
if ((type = this.currentType) == Type.void)
continue;
} else {
this.error(DiagnosticCode.Type_expected, declaration.name.range.atEnd);
@ -989,8 +1000,8 @@ export class Compiler extends DiagnosticEmitter {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, name); // recoverable
else {
this.currentFunction.addLocal(type, name);
if (declaration.initializer)
initializers.push(this.compileAssignment(declaration.name, <Expression>declaration.initializer, Type.void));
if (init)
initializers.push(this.compileAssignmentWithValue(declaration.name, init));
}
}
return initializers.length ? this.module.createBlock(null, initializers, NativeType.None) : this.module.createNop();
@ -1600,8 +1611,35 @@ export class Compiler extends DiagnosticEmitter {
}
compileAssignment(expression: Expression, valueExpression: Expression, contextualType: Type): ExpressionRef {
this.currentType = this.determineExpressionType(expression, contextualType);
return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, this.currentType, ConversionKind.IMPLICIT), contextualType != Type.void);
let element: Element | null = null;
switch (expression.kind) {
case NodeKind.IDENTIFIER:
element = this.program.resolveIdentifier(<IdentifierExpression>expression, this.currentFunction); // reports
break;
case NodeKind.PROPERTYACCESS:
element = this.program.resolvePropertyAccess(<PropertyAccessExpression>expression, this.currentFunction); // reports
break;
default:
this.error(DiagnosticCode.Operation_not_supported, expression.range);
}
if (!element)
return this.module.createUnreachable();
let type: Type | null = null;
switch (element.kind) {
case ElementKind.LOCAL:
type = (<Local>element).type;
break;
case ElementKind.GLOBAL:
if (this.compileGlobal(<Global>element))
type = (<Global>element).type;
break;
default:
this.error(DiagnosticCode.Operation_not_supported, expression.range);
}
if (!type)
return this.module.createUnreachable();
this.currentType = type;
return this.compileAssignmentWithValue(expression, this.compileExpression(valueExpression, type, ConversionKind.IMPLICIT), contextualType != Type.void);
}
compileAssignmentWithValue(expression: Expression, valueWithCorrectType: ExpressionRef, tee: bool = false): ExpressionRef {

View File

@ -1,11 +1,49 @@
(module
(type $v (func))
(type $i (func (result i32)))
(type $I (func (result i64)))
(type $f (func (result f32)))
(type $F (func (result f64)))
(global $infer-type/ri (mut i32) (i32.const 0))
(global $infer-type/rI (mut i64) (i64.const 0))
(global $infer-type/rf (mut f32) (f32.const 0))
(global $infer-type/rF (mut f64) (f64.const 0))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $start (; 0 ;) (type $v)
(block $__inlined_func$infer-type/locals
(nop)
(block
(block $__inlined_func$infer-type/locals
(nop)
)
)
(set_global $infer-type/ri
(block (result i32)
(block $__inlined_func$infer-type/reti (result i32)
(i32.const 0)
)
)
)
(set_global $infer-type/rI
(block (result i64)
(block $__inlined_func$infer-type/retI (result i64)
(i64.const 0)
)
)
)
(set_global $infer-type/rf
(block (result f32)
(block $__inlined_func$infer-type/retf (result f32)
(f32.const 0)
)
)
)
(set_global $infer-type/rF
(block (result f64)
(block $__inlined_func$infer-type/refF (result f64)
(f64.const 0)
)
)
)
)
)

View File

@ -1,12 +1,44 @@
(module
(type $v (func))
(type $i (func (result i32)))
(type $I (func (result i64)))
(type $f (func (result f32)))
(type $F (func (result f64)))
(global $infer-type/ri (mut i32) (i32.const 0))
(global $infer-type/rI (mut i64) (i64.const 0))
(global $infer-type/rf (mut f32) (f32.const 0))
(global $infer-type/rF (mut f64) (f64.const 0))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $infer-type/locals (; 0 ;) (type $v)
(nop)
)
(func $start (; 1 ;) (type $v)
(func $infer-type/reti (; 1 ;) (type $i) (result i32)
(i32.const 0)
)
(func $infer-type/retI (; 2 ;) (type $I) (result i64)
(i64.const 0)
)
(func $infer-type/retf (; 3 ;) (type $f) (result f32)
(f32.const 0)
)
(func $infer-type/refF (; 4 ;) (type $F) (result f64)
(f64.const 0)
)
(func $start (; 5 ;) (type $v)
(call $infer-type/locals)
(set_global $infer-type/ri
(call $infer-type/reti)
)
(set_global $infer-type/rI
(call $infer-type/retI)
)
(set_global $infer-type/rf
(call $infer-type/retf)
)
(set_global $infer-type/rF
(call $infer-type/refF)
)
)
)

View File

@ -16,3 +16,27 @@ function locals(): void {
let aF = F;
}
locals();
function reti(): i32 {
return 0;
}
let ri = reti();
ri;
function retI(): i64 {
return 0;
}
let rI = retI();
rI;
function retf(): f32 {
return 0;
}
let rf = retf();
rf;
function refF(): f64 {
return 0;
}
let rF = refF();
rF;

View File

@ -1,8 +1,16 @@
(module
(type $v (func))
(type $i (func (result i32)))
(type $I (func (result i64)))
(type $f (func (result f32)))
(type $F (func (result f64)))
(global $infer-type/i i32 (i32.const 10))
(global $infer-type/I i64 (i64.const 4294967296))
(global $infer-type/F f64 (f64.const 1.5))
(global $infer-type/ri (mut i32) (i32.const 0))
(global $infer-type/rI (mut i64) (i64.const 0))
(global $infer-type/rf (mut f32) (f32.const 0))
(global $infer-type/rF (mut f64) (f64.const 0))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "memory" (memory $0))
@ -45,7 +53,27 @@
)
)
)
(func $start (; 1 ;) (type $v)
(func $infer-type/reti (; 1 ;) (type $i) (result i32)
(return
(i32.const 0)
)
)
(func $infer-type/retI (; 2 ;) (type $I) (result i64)
(return
(i64.const 0)
)
)
(func $infer-type/retf (; 3 ;) (type $f) (result f32)
(return
(f32.const 0)
)
)
(func $infer-type/refF (; 4 ;) (type $F) (result f64)
(return
(f64.const 0)
)
)
(func $start (; 5 ;) (type $v)
(drop
(i32.const 10)
)
@ -56,6 +84,30 @@
(f64.const 1.5)
)
(call $infer-type/locals)
(set_global $infer-type/ri
(call $infer-type/reti)
)
(drop
(get_global $infer-type/ri)
)
(set_global $infer-type/rI
(call $infer-type/retI)
)
(drop
(get_global $infer-type/rI)
)
(set_global $infer-type/rf
(call $infer-type/retf)
)
(drop
(get_global $infer-type/rf)
)
(set_global $infer-type/rF
(call $infer-type/refF)
)
(drop
(get_global $infer-type/rF)
)
)
)
(;
@ -108,6 +160,14 @@
infer-type/I
infer-type/F
infer-type/locals
infer-type/reti
infer-type/ri
infer-type/retI
infer-type/rI
infer-type/retf
infer-type/rf
infer-type/refF
infer-type/rF
[program.exports]
;)