summaryrefslogtreecommitdiff
path: root/src/main/java/com/seaofnodes/simple/codegen/GlobalBits.java
blob: bc9c9d391eb90f72af1eb9c90e44b25b1d3a0798 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package com.seaofnodes.simple.codegen;

import com.seaofnodes.simple.util.*;
import java.util.HashMap;
import java.util.Arrays;
import java.util.Objects;


// This utility 2-way maps some *global* program feature to a local dense
// index, suitable for packing in BitSets.  The mapping takes a global value -
// always a {source file/class} name and an order number counting up per
// feature in the same file.  The dense local index can be different in
// different compilations.

// The global info allows loading the same remote class info from different
// paths and aligning them.  Example: Compiling A loads pre-compiled B and C,
// both of which load pre-compiled D.  The 2 different D loads unify via this
// global info.

// These mappings are all trivial identities when compiling one file; they
// only become complex when loading seperately compiled code.

public class GlobalBits {
    public static final int RESERVED = 2;
    private final HashMap<String,int[]> _clzOrder2Local = new HashMap<>();
    private final Ary<String> _local2Clz = new Ary<>(String.class);
    private final AryInt _local2Order = new AryInt();
    // Local dense index
    private int _local;

    GlobalBits() {
        _local = RESERVED;
        // Reserved null-class bits are global identities and are never
        // remapped.
        int[] xs = new int[RESERVED];
        xs[0] = RESERVED;       // Per-class next order counter
        for( int i=0; i<RESERVED; i++ ) {
            if( i > 0 ) xs[i] = i;
            _local2Clz  .setX(i,null);
            _local2Order.setX(i,i);
        }
        _clzOrder2Local.put(null,xs);
    }

    // Get a next local index for this clz
    int next( String clz ) { return next(clz,-1); }

    // Given a clz and order, return an existing local index or make a new
    // mapping and return that.
    int next(String clz, int order ) {
        // Get the per-clz Order->Local mapping
        int[] xs = _clzOrder2Local.get(clz);
        if( xs == null )
            xs = new int[]{RESERVED,1}; // Make, if it does not exist
        // Caller does not have an order in mind, just take the next one stored
        // in xs[0]
        if( order == -1 )
            order = xs[0]++;
        // Order exists from prior compilation, but not recorded locally
        else if( order >= xs[0] )
            xs[0] = order+1;
        // Grow as needed
        while( xs.length <= order )
            xs = Arrays.copyOf(xs,xs.length*2);
        // Have a local mapping already?
        if( xs[order] != 0 )
            return xs[order];
        int local = _local++;
        bind(local,clz,order);
        return local;        // The dense local index
    }

    // Bind a specific dense local index to {clz,order}.  Used by both fresh
    // allocation and deserialization, where local indexes must not compact
    // across holes because XInt bitsets are serialized against this space.
    private void bind( int local, String clz, int order ) {
        if( _local <= local )
            _local = local+1;
        _local2Clz  .setX(local,clz  );
        _local2Order.setX(local,order);

        // Inline-only fidx holes are represented as {null,0}.  They need the
        // reverse mapping above, but no forward map because xs[0] is the
        // per-class next-order counter.
        if( clz==null && order==0 )
            return;

        int[] xs = _clzOrder2Local.get(clz);
        if( xs == null )
            xs = new int[]{RESERVED,1};
        if( order >= xs[0] )
            xs[0] = order+1;
        while( xs.length <= order )
            xs = Arrays.copyOf(xs,xs.length*2);
        _clzOrder2Local.put(clz,xs);
        xs[order] = local;
    }

    // A file-local function that will inline, and never write to disk
    public int nextInline( ) {
        int local = _local++;
        _local2Clz  .setX(local,null);
        _local2Order.setX(local,0);
        return local;
    }
    // Get a next local index for the same clz as this old index
    public int next( int old ) { return next(_local2Clz.at(old));  }

    // Map a file-local dense index to this compilation unit's local dense index.
    public int map( GlobalBits fileBits, int fileLocal ) {
        if( fileLocal >= fileBits._local2Clz.size() )
            throw new ArrayIndexOutOfBoundsException(""+fileLocal+" >= "+fileBits._local2Clz.size());
        return findOrNext(fileBits._local2Clz.at(fileLocal), fileBits._local2Order.at(fileLocal));
    }

    // Map a global {clz,order} to a local one; keep any existing mapping or
    // make a new one as needed.
    private int findOrNext(String clz, int order ) {
        // Reserved null-class identities are not represented in xs[order],
        // because xs[0] is the next-order counter.
        if( clz==null && order < _local2Clz.size() && _local2Order.at(order)==order && _local2Clz.at(order)==null )
            return order;
        int[] xs = _clzOrder2Local.get(clz);
        if( xs != null && order < xs.length ) {
            int local = xs[order];
            if( local < _local2Clz.size() && _local2Order.at(local)==order && Objects.equals(_local2Clz.at(local),clz) )
                return local;
        }
        return next(clz,order);
    }

    // Stable native symbol identity, independent of this compilation's dense IDs.
    String owner(int local) { return _local2Clz.at(local); }
    String symbol(int local) {
        return _local2Clz.at(local)+"$F$"+_local2Order.at(local);
    }

    // Write out to BAOS enough bits to unwind the local back to global index
    void packed( BAOS baos, HashMap<String,Integer> strs ) {
        // Unwind a local index to the clz.
        baos.packed2(_local2Clz.size());
        for( int i=RESERVED; i<_local2Clz.size(); i++ ) {
            String clz = _local2Clz.at(i);
            baos.packed2(clz==null ? 0 : strs.get(clz));
            baos.packed2( _local2Order.at(i) );
        }
    }

    void gather( HashMap<String,Integer> strs ) {
        for( int i=RESERVED; i<_local2Clz.size(); i++ )
            Serialize.gather(strs,_local2Clz.at(i));
    }

    // Read in the BAIS bits needed to make a file-local -> global mapping
    static GlobalBits packed( BAOS bais, String[] strs ) {
        var gb = new GlobalBits();
        int n = bais.packed2();
        for( int i=RESERVED; i<n; i++ ) {
            int sx = bais.packed2();
            String clz = strs[sx];
            if( clz.isEmpty() ) clz = null;
            int order = bais.packed2();
            gb.bind(i,clz,order);
        }
        return gb;
    }

    @Override public String toString() {
        SB sb = new SB().p('[');
        for( int i=0; i<_local2Clz.size(); i++ ) {
            if( i > 0 ) sb.p(',');
            sb.p(i).p(" <-> ").p(_local2Clz.at(i)).p('#').p(_local2Order.at(i));
        }
        return sb.p(']').toString();
    }

}