@@ -68,8 +68,6 @@ import {
68
68
getConstValueI32 ,
69
69
getConstValueF32 ,
70
70
getConstValueF64 ,
71
- Relooper ,
72
- RelooperBlockRef ,
73
71
SIMDLoadOp ,
74
72
getLocalGetIndex ,
75
73
createType ,
@@ -82,7 +80,6 @@ import {
82
80
Field ,
83
81
Global ,
84
82
DecoratorFlags ,
85
- Element ,
86
83
ClassPrototype ,
87
84
Class
88
85
} from "./program" ;
@@ -8691,134 +8688,184 @@ export function compileVisitGlobals(compiler: Compiler): void {
8691
8688
) ;
8692
8689
}
8693
8690
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
+
8696
8696
var program = compiler . program ;
8697
8697
var module = compiler . module ;
8698
8698
var usizeType = program . options . usizeType ;
8699
8699
var nativeSizeType = usizeType . toNativeType ( ) ;
8700
8700
var nativeSizeSize = usizeType . byteSize ;
8701
- var managedClasses = program . managedClasses ;
8702
8701
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
+ }
8736
8714
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 (
8743
8725
module . unreachable ( )
8744
8726
) ;
8745
8727
} else {
8746
- let visitSig = visitFunc . signature ;
8747
- let visitThisType = assert ( visitSig . thisType ) ;
8728
+ let visitSignature = visitInstance . signature ;
8729
+ let visitThisType = assert ( visitSignature . thisType ) ;
8748
8730
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 &&
8752
8734
instance . type . isStrictlyAssignableTo ( visitThisType ) // incl. implemented on super
8753
8735
) ;
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
8757
8739
module . local_get ( 1 , NativeType . I32 ) // cookie
8758
8740
] , NativeType . None )
8759
8741
) ;
8760
8742
}
8743
+ hasVisitImpl = true ;
8744
+ }
8745
+ }
8761
8746
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
+ ) ;
8791
8778
}
8792
8779
}
8793
8780
}
8794
8781
}
8795
8782
}
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 ) ;
8802
8783
}
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 ;
8804
8812
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 ++ ) ;
8806
8815
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
+ )
8813
8843
)
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'
8817
8861
module . addFunction ( BuiltinNames . visit_members ,
8818
- createType ( [ usizeType . toNativeType ( ) , NativeType . I32 ] ) , // ref , cookie
8862
+ createType ( [ nativeSizeType , NativeType . I32 ] ) , // this , cookie
8819
8863
NativeType . None , // => void
8820
- [ nativeSizeType ] ,
8821
- relooper . renderAndDispose ( outer , 2 )
8864
+ null ,
8865
+ module . flatten ( [
8866
+ current ,
8867
+ module . unreachable ( )
8868
+ ] )
8822
8869
) ;
8823
8870
}
8824
8871
0 commit comments