@@ -210,6 +210,8 @@ export class Options {
210210 features : Feature = Feature . MUTABLE_GLOBALS ;
211211 /** If true, disallows unsafe features in user code. */
212212 noUnsafe : bool = false ;
213+ /** If true, enables pedantic diagnostics. */
214+ pedantic : bool = false ;
213215
214216 /** Hinted optimize level. Not applied by the compiler itself. */
215217 optimizeLevelHint : i32 = 0 ;
@@ -236,6 +238,11 @@ export class Options {
236238 return this . target == Target . WASM64 ? NativeType . I64 : NativeType . I32 ;
237239 }
238240
241+ /** Gets if any optimizations will be performed. */
242+ get willOptimize ( ) : bool {
243+ return this . optimizeLevelHint > 0 || this . shrinkLevelHint > 0 ;
244+ }
245+
239246 /** Tests if a specific feature is activated. */
240247 hasFeature ( feature : Feature ) : bool {
241248 return ( this . features & feature ) != 0 ;
@@ -322,6 +329,8 @@ export class Compiler extends DiagnosticEmitter {
322329 skippedAutoreleases : Set < ExpressionRef > = new Set ( ) ;
323330 /** Current inline functions stack. */
324331 inlineStack : Function [ ] = [ ] ;
332+ /** Lazily compiled library functions. */
333+ lazyLibraryFunctions : Set < Function > = new Set ( ) ;
325334
326335 /** Compiles a {@link Program} to a {@link Module} using the specified options. */
327336 static compile ( program : Program ) : Module {
@@ -387,7 +396,7 @@ export class Compiler extends DiagnosticEmitter {
387396 }
388397 }
389398
390- // compile the start function if not empty or explicitly requested
399+ // compile the start function if not empty or if explicitly requested
391400 var startIsEmpty = ! startFunctionBody . length ;
392401 var explicitStart = options . explicitStart ;
393402 if ( ! startIsEmpty || explicitStart ) {
@@ -414,11 +423,39 @@ export class Compiler extends DiagnosticEmitter {
414423 else module . addFunctionExport ( startFunctionInstance . internalName , ExportNames . start ) ;
415424 }
416425
417- // compile runtime features
418- if ( this . runtimeFeatures & RuntimeFeatures . visitGlobals ) compileVisitGlobals ( this ) ;
419- if ( this . runtimeFeatures & RuntimeFeatures . visitMembers ) compileVisitMembers ( this ) ;
426+ // check if the entire program is acyclic
427+ var cyclicClasses = program . findCyclicClasses ( ) ;
428+ if ( cyclicClasses . size ) {
429+ if ( options . pedantic ) {
430+ for ( let classInstance of cyclicClasses ) {
431+ this . info (
432+ DiagnosticCode . Type_0_is_cyclic_Module_will_include_deferred_garbage_collection ,
433+ classInstance . identifierNode . range , classInstance . internalName
434+ ) ;
435+ }
436+ }
437+ } else {
438+ program . registerConstantInteger ( "__GC_ALL_ACYCLIC" , Type . bool , i64_new ( 1 , 0 ) ) ;
439+ }
440+
441+ // compile lazy library functions
442+ var lazyLibraryFunctions = this . lazyLibraryFunctions ;
443+ do {
444+ let functionsToCompile = new Array < Function > ( ) ;
445+ for ( let instance of lazyLibraryFunctions ) {
446+ functionsToCompile . push ( instance ) ;
447+ }
448+ lazyLibraryFunctions . clear ( ) ;
449+ for ( let i = 0 , k = functionsToCompile . length ; i < k ; ++ i ) {
450+ this . compileFunction ( unchecked ( functionsToCompile [ i ] ) , true ) ;
451+ }
452+ } while ( lazyLibraryFunctions . size ) ;
453+
454+ // finalize runtime features
420455 module . removeGlobal ( BuiltinNames . rtti_base ) ;
421456 if ( this . runtimeFeatures & RuntimeFeatures . RTTI ) compileRTTI ( this ) ;
457+ if ( this . runtimeFeatures & RuntimeFeatures . visitGlobals ) compileVisitGlobals ( this ) ;
458+ if ( this . runtimeFeatures & RuntimeFeatures . visitMembers ) compileVisitMembers ( this ) ;
422459
423460 // update the heap base pointer
424461 var memoryOffset = this . memoryOffset ;
@@ -464,8 +501,24 @@ export class Compiler extends DiagnosticEmitter {
464501 module . setFunctionTable ( 1 + functionTable . length , Module . UNLIMITED_TABLE , functionTable , module . i32 ( 1 ) ) ;
465502
466503 // import and/or export table if requested (default table is named '0' by Binaryen)
467- if ( options . importTable ) module . addTableImport ( "0" , "env" , "table" ) ;
468- if ( options . exportTable ) module . addTableExport ( "0" , ExportNames . table ) ;
504+ if ( options . importTable ) {
505+ module . addTableImport ( "0" , "env" , "table" ) ;
506+ if ( options . pedantic && options . willOptimize ) {
507+ this . warning (
508+ DiagnosticCode . Importing_the_table_disables_some_indirect_call_optimizations ,
509+ null
510+ ) ;
511+ }
512+ }
513+ if ( options . exportTable ) {
514+ module . addTableExport ( "0" , ExportNames . table ) ;
515+ if ( options . pedantic && options . willOptimize ) {
516+ this . warning (
517+ DiagnosticCode . Exporting_the_table_disables_some_indirect_call_optimizations ,
518+ null
519+ ) ;
520+ }
521+ }
469522
470523 // set up module exports
471524 for ( let file of this . program . filesByName . values ( ) ) {
@@ -1101,11 +1154,15 @@ export class Compiler extends DiagnosticEmitter {
11011154 forceStdAlternative : bool = false
11021155 ) : bool {
11031156 if ( instance . is ( CommonFlags . COMPILED ) ) return true ;
1104- if ( instance . hasDecorator ( DecoratorFlags . BUILTIN ) ) {
1105- if ( ! forceStdAlternative ) return true ;
1157+ if ( ! forceStdAlternative ) {
1158+ if ( instance . hasDecorator ( DecoratorFlags . BUILTIN ) ) return true ;
1159+ if ( instance . hasDecorator ( DecoratorFlags . LAZY ) ) {
1160+ this . lazyLibraryFunctions . add ( instance ) ;
1161+ return true ;
1162+ }
11061163 }
11071164
1108- var previousType = this . currentType ; // remember to retain it if compiling a function lazily
1165+ var previousType = this . currentType ;
11091166 instance . set ( CommonFlags . COMPILED ) ;
11101167
11111168 var module = this . module ;
0 commit comments