@@ -68,8 +68,6 @@ import {
6868 getConstValueI32 ,
6969 getConstValueF32 ,
7070 getConstValueF64 ,
71- Relooper ,
72- RelooperBlockRef ,
7371 SIMDLoadOp ,
7472 getLocalGetIndex ,
7573 createType ,
@@ -82,7 +80,6 @@ import {
8280 Field ,
8381 Global ,
8482 DecoratorFlags ,
85- Element ,
8683 ClassPrototype ,
8784 Class
8885} from "./program" ;
@@ -8691,134 +8688,184 @@ export function compileVisitGlobals(compiler: Compiler): void {
86918688 ) ;
86928689}
86938690
8694- /** Compiles the `visit_members` function. */
8695- export function compileVisitMembers ( compiler : Compiler ) : void {
8691+ /** Ensures that the visitor function of the specified class is compiled. */
8692+ function ensureVisitMembersOf ( compiler : Compiler , instance : Class ) : void {
8693+ assert ( instance . type . isManaged ) ;
8694+ if ( instance . visitRef ) return ;
8695+
86968696 var program = compiler . program ;
86978697 var module = compiler . module ;
86988698 var usizeType = program . options . usizeType ;
86998699 var nativeSizeType = usizeType . toNativeType ( ) ;
87008700 var nativeSizeSize = usizeType . byteSize ;
8701- var managedClasses = program . managedClasses ;
87028701 var visitInstance = assert ( program . visitInstance ) ;
8703- var blocks = new Array < RelooperBlockRef > ( ) ;
8704- var relooper = Relooper . create ( module ) ;
8705-
8706- // this function is @lazy : make sure it exists
8707- compiler . compileFunction ( visitInstance , true ) ;
8708-
8709- var outer = relooper . addBlockWithSwitch (
8710- module . nop ( ) ,
8711- module . load ( nativeSizeSize , false ,
8712- nativeSizeType == NativeType . I64
8713- ? module . binary ( BinaryOp . SubI64 ,
8714- module . local_get ( 0 , nativeSizeType ) ,
8715- module . i64 ( 8 )
8716- )
8717- : module . binary ( BinaryOp . SubI32 ,
8718- module . local_get ( 0 , nativeSizeType ) ,
8719- module . i32 ( 8 ) // rtId is at -8
8720- ) ,
8721- NativeType . I32 ,
8722- 0
8723- )
8724- ) ;
8725-
8726- var lastId = 0 ;
8727- // TODO: for (let [instanceId, instance] of managedClasses) {
8728- for ( let _keys = Map_keys ( managedClasses ) , i = 0 , k = _keys . length ; i < k ; ++ i ) {
8729- let instanceId = _keys [ i ] ;
8730- let instance = assert ( managedClasses . get ( instanceId ) ) ;
8731- assert ( instance . type . isManaged ) ;
8732- assert ( instanceId == lastId ++ ) ;
8733-
8734- let visitImpl : Element | null ;
8735- let code = new Array < ExpressionRef > ( ) ;
8702+ var body = new Array < ExpressionRef > ( ) ;
8703+
8704+ // If the class has a base class, call its visitor first
8705+ var base = instance . base ;
8706+ if ( base ) {
8707+ body . push (
8708+ module . call ( base . internalName + "~visit" , [
8709+ module . local_get ( 0 , nativeSizeType ) , // this
8710+ module . local_get ( 1 , NativeType . I32 ) // cookie
8711+ ] , NativeType . None )
8712+ ) ;
8713+ }
87368714
8737- // if a library element, check if it implements a custom traversal function
8738- if ( instance . isDeclaredInLibrary && ( visitImpl = instance . lookupInSelf ( "__visit_impl" ) ) !== null ) {
8739- assert ( visitImpl . kind == ElementKind . FUNCTION_PROTOTYPE ) ;
8740- let visitFunc = program . resolver . resolveFunction ( < FunctionPrototype > visitImpl , null ) ;
8741- if ( ! visitFunc || ! compiler . compileFunction ( visitFunc ) ) {
8742- code . push (
8715+ // Some standard library components provide a custom visitor implementation,
8716+ // for example to visit all members of a collection, e.g. arrays and maps.
8717+ var hasVisitImpl = false ;
8718+ if ( instance . isDeclaredInLibrary ) {
8719+ let visitPrototype = instance . lookupInSelf ( "__visit" ) ;
8720+ if ( visitPrototype ) {
8721+ assert ( visitPrototype . kind == ElementKind . FUNCTION_PROTOTYPE ) ;
8722+ let visitInstance = program . resolver . resolveFunction ( < FunctionPrototype > visitPrototype , null ) ;
8723+ if ( ! visitInstance || ! compiler . compileFunction ( visitInstance ) ) {
8724+ body . push (
87438725 module . unreachable ( )
87448726 ) ;
87458727 } else {
8746- let visitSig = visitFunc . signature ;
8747- let visitThisType = assert ( visitSig . thisType ) ;
8728+ let visitSignature = visitInstance . signature ;
8729+ let visitThisType = assert ( visitSignature . thisType ) ;
87488730 assert (
8749- visitSig . parameterTypes . length == 1 &&
8750- visitSig . parameterTypes [ 0 ] == Type . u32 &&
8751- visitSig . returnType == Type . void &&
8731+ visitSignature . parameterTypes . length == 1 &&
8732+ visitSignature . parameterTypes [ 0 ] == Type . u32 &&
8733+ visitSignature . returnType == Type . void &&
87528734 instance . type . isStrictlyAssignableTo ( visitThisType ) // incl. implemented on super
87538735 ) ;
8754- code . push (
8755- module . call ( visitFunc . internalName , [
8756- module . local_get ( 0 , nativeSizeType ) , // ref
8736+ body . push (
8737+ module . call ( visitInstance . internalName , [
8738+ module . local_get ( 0 , nativeSizeType ) , // this
87578739 module . local_get ( 1 , NativeType . I32 ) // cookie
87588740 ] , NativeType . None )
87598741 ) ;
87608742 }
8743+ hasVisitImpl = true ;
8744+ }
8745+ }
87618746
8762- // otherwise generate traversal logic for own fields
8763- } else {
8764- let members = instance . members ;
8765- if ( members ) {
8766- // TODO: for (let member of members.values()) {
8767- for ( let _values = Map_values ( members ) , j = 0 , l = _values . length ; j < l ; ++ j ) {
8768- let member = unchecked ( _values [ j ] ) ;
8769- if ( member . kind == ElementKind . FIELD ) {
8770- if ( ( < Field > member ) . parent === instance ) {
8771- let fieldType = ( < Field > member ) . type ;
8772- if ( fieldType . isManaged ) {
8773- let fieldOffset = ( < Field > member ) . memoryOffset ;
8774- assert ( fieldOffset >= 0 ) ;
8775- code . push (
8776- // if ($2 = value) FIELDCLASS~traverse($2)
8777- module . if (
8778- module . local_tee ( 2 ,
8779- module . load ( nativeSizeSize , false ,
8780- module . local_get ( 0 , nativeSizeType ) ,
8781- nativeSizeType , fieldOffset
8782- )
8783- ) ,
8784- module . call ( visitInstance . internalName , [
8785- module . local_get ( 2 , nativeSizeType ) , // ref
8786- module . local_get ( 1 , NativeType . I32 ) // cookie
8787- ] , NativeType . None )
8788- )
8789- ) ;
8790- }
8747+ // Otherwise, if there is no custom visitor, generate a visitor function
8748+ // according to class layout, visiting all _own_ managed members.
8749+ var needsTempValue = false ;
8750+ if ( ! hasVisitImpl ) {
8751+ let members = instance . members ;
8752+ if ( members ) {
8753+ // TODO: for (let member of members.values()) {
8754+ for ( let _values = Map_values ( members ) , j = 0 , l = _values . length ; j < l ; ++ j ) {
8755+ let member = unchecked ( _values [ j ] ) ;
8756+ if ( member . kind == ElementKind . FIELD ) {
8757+ if ( ( < Field > member ) . parent === instance ) {
8758+ let fieldType = ( < Field > member ) . type ;
8759+ if ( fieldType . isManaged ) {
8760+ let fieldOffset = ( < Field > member ) . memoryOffset ;
8761+ assert ( fieldOffset >= 0 ) ;
8762+ needsTempValue = true ;
8763+ body . push (
8764+ // if ($2 = value) __visit($2, $1)
8765+ module . if (
8766+ module . local_tee ( 2 ,
8767+ module . load ( nativeSizeSize , false ,
8768+ module . local_get ( 0 , nativeSizeType ) ,
8769+ nativeSizeType , fieldOffset
8770+ )
8771+ ) ,
8772+ module . call ( visitInstance . internalName , [
8773+ module . local_get ( 2 , nativeSizeType ) , // value
8774+ module . local_get ( 1 , NativeType . I32 ) // cookie
8775+ ] , NativeType . None )
8776+ )
8777+ ) ;
87918778 }
87928779 }
87938780 }
87948781 }
87958782 }
8796- if ( ! instance . base ) code . push ( module . return ( ) ) ;
8797- let block = relooper . addBlock (
8798- module . flatten ( code )
8799- ) ;
8800- relooper . addBranchForSwitch ( outer , block , [ instanceId ] ) ;
8801- blocks . push ( block ) ;
88028783 }
8803- // TODO: for (let [instanceId, instance] of managedClasses) {
8784+
8785+ // Create the visitor function
8786+ instance . visitRef = module . addFunction ( instance . internalName + "~visit" ,
8787+ createType ( [ nativeSizeType , NativeType . I32 ] ) ,
8788+ NativeType . None ,
8789+ needsTempValue ? [ nativeSizeType ] : null ,
8790+ module . flatten ( body , NativeType . None )
8791+ ) ;
8792+
8793+ // And make sure the base visitor function exists
8794+ if ( base ) ensureVisitMembersOf ( compiler , base ) ;
8795+ }
8796+
8797+ /** Compiles the `__visit_members` function. */
8798+ export function compileVisitMembers ( compiler : Compiler ) : void {
8799+ var program = compiler . program ;
8800+ var module = compiler . module ;
8801+ var usizeType = program . options . usizeType ;
8802+ var nativeSizeType = usizeType . toNativeType ( ) ;
8803+ var managedClasses = program . managedClasses ;
8804+ var visitInstance = assert ( program . visitInstance ) ;
8805+ compiler . compileFunction ( visitInstance , true ) ; // is lazy, make sure it is compiled
8806+
8807+ // Prepare a mapping of class names to visitor calls. Each name corresponds to
8808+ // the respective sequential (0..N) class id.
8809+ var names = new Array < string > ( ) ;
8810+ var cases = new Array < ExpressionRef > ( ) ;
8811+ var nextId = 0 ;
88048812 for ( let _keys = Map_keys ( managedClasses ) , i = 0 , k = _keys . length ; i < k ; ++ i ) {
8805- let instanceId = unchecked ( _keys [ i ] ) ;
8813+ let instanceId = _keys [ i ] ;
8814+ assert ( instanceId == nextId ++ ) ;
88068815 let instance = assert ( managedClasses . get ( instanceId ) ) ;
8807- let base = instance . base ;
8808- if ( base ) relooper . addBranch ( blocks [ instanceId ] , blocks [ base . id ] ) ;
8809- }
8810- blocks . push (
8811- relooper . addBlock (
8812- module . unreachable ( )
8816+ names [ i ] = instance . internalName ;
8817+ cases [ i ] = module . block ( null , [
8818+ module . call ( instance . internalName + "~visit" , [
8819+ module . local_get ( 0 , nativeSizeType ) , // this
8820+ module . local_get ( 1 , NativeType . I32 ) // cookie
8821+ ] , NativeType . None ) ,
8822+ module . return ( )
8823+ ] , NativeType . None ) ;
8824+ ensureVisitMembersOf ( compiler , instance ) ;
8825+ }
8826+
8827+ // Make a br_table of the mapping, calling visitor functions by unique class id
8828+ var current = module . block ( names [ 0 ] , [
8829+ module . switch ( names , "invalid" ,
8830+ // load<u32>(changetype<usize>(this) - 8)
8831+ module . load ( 4 , false ,
8832+ nativeSizeType == NativeType . I64
8833+ ? module . binary ( BinaryOp . SubI64 ,
8834+ module . local_get ( 0 , nativeSizeType ) ,
8835+ module . i64 ( 8 )
8836+ )
8837+ : module . binary ( BinaryOp . SubI32 ,
8838+ module . local_get ( 0 , nativeSizeType ) ,
8839+ module . i32 ( 8 ) // rtId is at -8
8840+ ) ,
8841+ NativeType . I32 , 0
8842+ )
88138843 )
8814- ) ;
8815- relooper . addBranchForSwitch ( outer , blocks [ blocks . length - 1 ] , [ ] ) ; // default
8816- compiler . compileFunction ( visitInstance ) ;
8844+ ] , NativeType . None ) ;
8845+
8846+ // Wrap blocks in order
8847+ for ( let i = 0 , k = names . length - 1 ; i < k ; ++ i ) {
8848+ current = module . block ( names [ i + 1 ] , [
8849+ current ,
8850+ cases [ i ]
8851+ ] , NativeType . None ) ;
8852+ }
8853+
8854+ // Wrap the last id in an 'invalid' block to break out of on invalid ids
8855+ current = module . block ( "invalid" , [
8856+ current ,
8857+ cases [ names . length - 1 ]
8858+ ] , NativeType . None ) ;
8859+
8860+ // Add the function, executing an unreachable if breaking to 'invalid'
88178861 module . addFunction ( BuiltinNames . visit_members ,
8818- createType ( [ usizeType . toNativeType ( ) , NativeType . I32 ] ) , // ref , cookie
8862+ createType ( [ nativeSizeType , NativeType . I32 ] ) , // this , cookie
88198863 NativeType . None , // => void
8820- [ nativeSizeType ] ,
8821- relooper . renderAndDispose ( outer , 2 )
8864+ null ,
8865+ module . flatten ( [
8866+ current ,
8867+ module . unreachable ( )
8868+ ] )
88228869 ) ;
88238870}
88248871
0 commit comments