1- import { compileCall as compileBuiltinCall } from "./builtins" ;
1+ import { compileCall as compileBuiltinCall , initialize } from "./builtins" ;
22import { PATH_DELIMITER } from "./constants" ;
33import { DiagnosticCode , DiagnosticEmitter } from "./diagnostics" ;
4- import { Module , MemorySegment , ExpressionRef , UnaryOp , BinaryOp , NativeType , FunctionTypeRef , getExpressionId , ExpressionId } from "./module" ;
5- import { Program , ClassPrototype , Class , Element , ElementKind , Enum , FunctionPrototype , Function , Global , Local , Namespace , Parameter } from "./program" ;
4+ import { Module , MemorySegment , ExpressionRef , UnaryOp , BinaryOp , NativeType , FunctionRef , FunctionTypeRef , getExpressionId , ExpressionId , getExpressionType , getFunctionBody , getConstValueI32 , getConstValueI64Low , getConstValueI64High , getConstValueF32 , getConstValueF64 } from "./module" ;
5+ import { Program , ClassPrototype , Class , Element , ElementKind , Enum , FunctionPrototype , Function , Global , Local , Namespace , Parameter , EnumValue } from "./program" ;
66import { I64 , U64 , sb } from "./util" ;
77import { Token } from "./tokenizer" ;
88import {
@@ -298,7 +298,7 @@ export class Compiler extends DiagnosticEmitter {
298298 if ( ! this . compileGlobal ( < Global > element ) )
299299 return null ;
300300 if ( declaration . range . source . isEntry && ( < VariableStatement > declaration . parent ) . parent == declaration . range . source && hasModifier ( ModifierKind . EXPORT , declaration . modifiers ) ) {
301- if ( ! ( < Global > element ) . isCompiledMutable )
301+ if ( ( < Global > element ) . hasConstantValue )
302302 this . module . addGlobalExport ( element . internalName , declaration . identifier . name ) ;
303303 else
304304 this . warning ( DiagnosticCode . Cannot_export_a_mutable_global , declaration . range ) ;
@@ -325,7 +325,7 @@ export class Compiler extends DiagnosticEmitter {
325325 return true ;
326326 const nativeType : NativeType = typeToNativeType ( < Type > type ) ;
327327 let initializer : ExpressionRef ;
328- let initializeInStart : bool ;
328+ let initializeInStart : bool = false ;
329329 if ( element . hasConstantValue ) {
330330 if ( type . isLongInteger )
331331 initializer = element . constantIntegerValue ? this . module . createI64 ( element . constantIntegerValue . lo , element . constantIntegerValue . hi ) : this . module . createI64 ( 0 , 0 ) ;
@@ -341,25 +341,49 @@ export class Compiler extends DiagnosticEmitter {
341341 initializer = this . module . createI32 ( element . constantIntegerValue ? element . constantIntegerValue . toI32 ( ) & type . smallIntegerMask : 0 ) ;
342342 } else
343343 initializer = this . module . createI32 ( element . constantIntegerValue ? element . constantIntegerValue . toI32 ( ) : 0 ) ;
344- initializeInStart = false ;
345344 } else if ( declaration ) {
346345 if ( declaration . initializer ) {
347346 initializer = this . compileExpression ( declaration . initializer , type ) ;
348- initializeInStart = getExpressionId ( initializer ) != ExpressionId . Const ; // MVP doesn't support complex initializers
349- } else {
347+ if ( getExpressionId ( initializer ) != ExpressionId . Const ) {
348+ if ( ! element . isMutable ) {
349+ initializer = this . precomputeExpressionRef ( initializer ) ;
350+ if ( getExpressionId ( initializer ) != ExpressionId . Const ) {
351+ this . warning ( DiagnosticCode . Compiling_constant_global_with_non_constant_initializer_as_mutable , declaration . range ) ;
352+ initializeInStart = true ;
353+ }
354+ } else
355+ initializeInStart = true ;
356+ }
357+ } else
350358 initializer = typeToNativeZero ( this . module , type ) ;
351- initializeInStart = false ;
352- }
353359 } else
354360 throw new Error ( "unexpected missing declaration or constant value" ) ;
355361 const internalName : string = element . internalName ;
356362 if ( initializeInStart ) {
357363 this . module . addGlobal ( internalName , nativeType , true , typeToNativeZero ( this . module , type ) ) ;
358364 this . startFunctionBody . push ( this . module . createSetGlobal ( internalName , initializer ) ) ;
359- element . isCompiledMutable = true ;
360365 } else {
361366 this . module . addGlobal ( internalName , nativeType , element . isMutable , initializer ) ;
362- element . isCompiledMutable = element . isMutable ;
367+ if ( ! element . isMutable ) {
368+ element . hasConstantValue = true ;
369+ const exprType : NativeType = getExpressionType ( initializer ) ;
370+ switch ( exprType ) {
371+ case NativeType . I32 :
372+ element . constantIntegerValue = new I64 ( getConstValueI32 ( initializer ) , 0 ) ;
373+ break ;
374+ case NativeType . I64 :
375+ element . constantIntegerValue = new I64 ( getConstValueI64Low ( initializer ) , getConstValueI64High ( initializer ) ) ;
376+ break ;
377+ case NativeType . F32 :
378+ element . constantFloatValue = getConstValueF32 ( initializer ) ;
379+ break ;
380+ case NativeType . F64 :
381+ element . constantFloatValue = getConstValueF64 ( initializer ) ;
382+ break ;
383+ default :
384+ throw new Error ( "unexpected initializer type" ) ;
385+ }
386+ }
363387 }
364388 return element . isCompiled = true ;
365389 }
@@ -376,7 +400,7 @@ export class Compiler extends DiagnosticEmitter {
376400 compileEnum ( element : Enum ) : void {
377401 if ( element . isCompiled )
378402 return ;
379- let previousInternalName : string | null = null ;
403+ let previousValue : EnumValue | null = null ;
380404 for ( let [ key , val ] of element . members ) {
381405 if ( val . hasConstantValue ) {
382406 this . module . addGlobal ( val . internalName , NativeType . I32 , false , this . module . createI32 ( val . constantValue ) ) ;
@@ -386,27 +410,40 @@ export class Compiler extends DiagnosticEmitter {
386410 let initializeInStart : bool = false ;
387411 if ( declaration . value ) {
388412 initializer = this . compileExpression ( < Expression > declaration . value , Type . i32 ) ;
389- initializeInStart = getExpressionId ( initializer ) != ExpressionId . Const ; // MVP doesn't support complex initializers
390- } else if ( previousInternalName == null ) {
413+ if ( getExpressionId ( initializer ) != ExpressionId . Const ) {
414+ initializer = this . precomputeExpressionRef ( initializer ) ;
415+ if ( getExpressionId ( initializer ) != ExpressionId . Const ) {
416+ this . warning ( DiagnosticCode . Compiling_constant_global_with_non_constant_initializer_as_mutable , declaration . range ) ;
417+ initializeInStart = true ;
418+ }
419+ }
420+ } else if ( previousValue == null ) {
391421 initializer = this . module . createI32 ( 0 ) ;
392- initializeInStart = false ;
422+ } else if ( previousValue . hasConstantValue ) {
423+ initializer = this . module . createI32 ( previousValue . constantValue + 1 ) ;
393424 } else {
425+ // in TypeScript this errors with TS1061, but actually we can do:
394426 initializer = this . module . createBinary ( BinaryOp . AddI32 ,
395- this . module . createGetGlobal ( previousInternalName , NativeType . I32 ) ,
427+ this . module . createGetGlobal ( previousValue . internalName , NativeType . I32 ) ,
396428 this . module . createI32 ( 1 )
397429 ) ;
430+ this . warning ( DiagnosticCode . Compiling_constant_global_with_non_constant_initializer_as_mutable , val . declaration . range ) ;
398431 initializeInStart = true ;
399432 }
400433 if ( initializeInStart ) {
401434 this . module . addGlobal ( val . internalName , NativeType . I32 , true , this . module . createI32 ( 0 ) ) ;
402435 this . startFunctionBody . push ( this . module . createSetGlobal ( val . internalName , initializer ) ) ;
403436 } else {
404437 this . module . addGlobal ( val . internalName , NativeType . I32 , false , initializer ) ;
405- // TODO: check export, requires updated binaryen.js with Module#addGlobalExport
438+ if ( getExpressionType ( initializer ) == NativeType . I32 ) {
439+ val . hasConstantValue = true ;
440+ val . constantValue = getConstValueI32 ( initializer ) ;
441+ } else
442+ throw new Error ( "unexpected initializer type" ) ;
406443 }
407444 } else
408445 throw new Error ( "unexpected missing declaration or constant value" ) ;
409- previousInternalName = val . internalName ;
446+ previousValue = val ;
410447 }
411448 element . isCompiled = true ;
412449 }
@@ -588,7 +625,7 @@ export class Compiler extends DiagnosticEmitter {
588625
589626 case ElementKind . GLOBAL :
590627 if ( this . compileGlobal ( < Global > element ) && statement . range . source . isEntry ) {
591- if ( ! ( < Global > element ) . isCompiledMutable )
628+ if ( ( < Global > element ) . hasConstantValue )
592629 this . module . addGlobalExport ( element . internalName , member . externalIdentifier . name ) ;
593630 else
594631 this . warning ( DiagnosticCode . Cannot_export_a_mutable_global , member . range ) ;
@@ -988,6 +1025,24 @@ export class Compiler extends DiagnosticEmitter {
9881025 return expr ;
9891026 }
9901027
1028+ precomputeExpression ( expression : Expression , contextualType : Type , conversionKind : ConversionKind = ConversionKind . IMPLICIT ) : ExpressionRef {
1029+ const expr : ExpressionRef = this . compileExpression ( expression , contextualType , conversionKind ) ;
1030+ return this . precomputeExpressionRef ( expr ) ;
1031+ }
1032+
1033+ precomputeExpressionRef ( expr : ExpressionRef ) : ExpressionRef {
1034+ const nativeType : NativeType = typeToNativeType ( this . currentType ) ;
1035+ let typeRef : FunctionTypeRef = this . module . getFunctionTypeBySignature ( nativeType , [ ] ) ;
1036+ if ( ! typeRef )
1037+ typeRef = this . module . addFunctionType ( typeToSignatureNamePart ( this . currentType ) , nativeType , [ ] ) ;
1038+ const funcRef : FunctionRef = this . module . addFunction ( "__precompute" , typeRef , [ ] , expr ) ;
1039+ this . module . runPasses ( [ "precompute" ] , funcRef ) ;
1040+ const ret : ExpressionRef = getFunctionBody ( funcRef ) ;
1041+ this . module . removeFunction ( "__precompute" ) ;
1042+ // TODO: also remove the function type somehow if no longer used
1043+ return ret ;
1044+ }
1045+
9911046 convertExpression ( expr : ExpressionRef , fromType : Type , toType : Type , conversionKind : ConversionKind , reportNode : Node ) : ExpressionRef {
9921047 if ( conversionKind == ConversionKind . NONE )
9931048 return expr ;
@@ -1636,11 +1691,24 @@ export class Compiler extends DiagnosticEmitter {
16361691
16371692 // global
16381693 if ( element . kind == ElementKind . GLOBAL ) {
1639- if ( ( < Global > element ) . type )
1640- this . currentType = < Type > ( < Global > element ) . type ;
1641- return this . compileGlobal ( < Global > element ) // reports
1642- ? this . module . createGetGlobal ( ( < Global > element ) . internalName , typeToNativeType ( this . currentType = < Type > ( < Global > element ) . type ) )
1643- : this . module . createUnreachable ( ) ;
1694+ const global : Global = < Global > element ;
1695+ if ( global . type )
1696+ this . currentType = < Type > global . type ;
1697+ if ( ! this . compileGlobal ( global ) ) // reports
1698+ return this . module . createUnreachable ( ) ;
1699+ if ( global . hasConstantValue ) {
1700+ if ( global . type == Type . f32 )
1701+ return this . module . createF32 ( ( < Global > element ) . constantFloatValue ) ;
1702+ else if ( global . type == Type . f64 )
1703+ return this . module . createF64 ( ( < Global > element ) . constantFloatValue ) ;
1704+ else if ( ( < Type > global . type ) . isLongInteger )
1705+ return this . module . createI64 ( ( < I64 > global . constantIntegerValue ) . lo , ( < I64 > global . constantIntegerValue ) . hi ) ;
1706+ else if ( ( < Type > global . type ) . isAnyInteger )
1707+ return this . module . createI32 ( ( < I64 > global . constantIntegerValue ) . lo ) ;
1708+ else
1709+ throw new Error ( "unexpected global type" ) ;
1710+ } else
1711+ return this . module . createGetGlobal ( ( < Global > element ) . internalName , typeToNativeType ( this . currentType = < Type > ( < Global > element ) . type ) ) ;
16441712 }
16451713
16461714 // field
0 commit comments