@@ -210,6 +210,8 @@ export class Options {
210
210
features : Feature = Feature . MUTABLE_GLOBALS ;
211
211
/** If true, disallows unsafe features in user code. */
212
212
noUnsafe : bool = false ;
213
+ /** If true, enables pedantic diagnostics. */
214
+ pedantic : bool = false ;
213
215
214
216
/** Hinted optimize level. Not applied by the compiler itself. */
215
217
optimizeLevelHint : i32 = 0 ;
@@ -236,6 +238,11 @@ export class Options {
236
238
return this . target == Target . WASM64 ? NativeType . I64 : NativeType . I32 ;
237
239
}
238
240
241
+ /** Gets if any optimizations will be performed. */
242
+ get willOptimize ( ) : bool {
243
+ return this . optimizeLevelHint > 0 || this . shrinkLevelHint > 0 ;
244
+ }
245
+
239
246
/** Tests if a specific feature is activated. */
240
247
hasFeature ( feature : Feature ) : bool {
241
248
return ( this . features & feature ) != 0 ;
@@ -322,6 +329,8 @@ export class Compiler extends DiagnosticEmitter {
322
329
skippedAutoreleases : Set < ExpressionRef > = new Set ( ) ;
323
330
/** Current inline functions stack. */
324
331
inlineStack : Function [ ] = [ ] ;
332
+ /** Lazily compiled library functions. */
333
+ lazyLibraryFunctions : Set < Function > = new Set ( ) ;
325
334
326
335
/** Compiles a {@link Program} to a {@link Module} using the specified options. */
327
336
static compile ( program : Program ) : Module {
@@ -387,7 +396,7 @@ export class Compiler extends DiagnosticEmitter {
387
396
}
388
397
}
389
398
390
- // compile the start function if not empty or explicitly requested
399
+ // compile the start function if not empty or if explicitly requested
391
400
var startIsEmpty = ! startFunctionBody . length ;
392
401
var explicitStart = options . explicitStart ;
393
402
if ( ! startIsEmpty || explicitStart ) {
@@ -414,11 +423,39 @@ export class Compiler extends DiagnosticEmitter {
414
423
else module . addFunctionExport ( startFunctionInstance . internalName , ExportNames . start ) ;
415
424
}
416
425
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
420
455
module . removeGlobal ( BuiltinNames . rtti_base ) ;
421
456
if ( this . runtimeFeatures & RuntimeFeatures . RTTI ) compileRTTI ( this ) ;
457
+ if ( this . runtimeFeatures & RuntimeFeatures . visitGlobals ) compileVisitGlobals ( this ) ;
458
+ if ( this . runtimeFeatures & RuntimeFeatures . visitMembers ) compileVisitMembers ( this ) ;
422
459
423
460
// update the heap base pointer
424
461
var memoryOffset = this . memoryOffset ;
@@ -464,8 +501,24 @@ export class Compiler extends DiagnosticEmitter {
464
501
module . setFunctionTable ( 1 + functionTable . length , Module . UNLIMITED_TABLE , functionTable , module . i32 ( 1 ) ) ;
465
502
466
503
// 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
+ }
469
522
470
523
// set up module exports
471
524
for ( let file of this . program . filesByName . values ( ) ) {
@@ -1101,11 +1154,15 @@ export class Compiler extends DiagnosticEmitter {
1101
1154
forceStdAlternative : bool = false
1102
1155
) : bool {
1103
1156
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
+ }
1106
1163
}
1107
1164
1108
- var previousType = this . currentType ; // remember to retain it if compiling a function lazily
1165
+ var previousType = this . currentType ;
1109
1166
instance . set ( CommonFlags . COMPILED ) ;
1110
1167
1111
1168
var module = this . module ;
0 commit comments