001/*
002 * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package jdk.internal.jvmci.code;
024
025import static jdk.internal.jvmci.meta.MetaUtil.*;
026
027import java.nio.*;
028import java.util.*;
029import java.util.function.*;
030
031import jdk.internal.jvmci.code.CompilationResult.*;
032import jdk.internal.jvmci.code.DataSection.*;
033import jdk.internal.jvmci.meta.*;
034
035public final class DataSection implements Iterable<Data> {
036
037    @FunctionalInterface
038    public interface DataBuilder {
039
040        void emit(ByteBuffer buffer, Consumer<DataPatch> patch);
041
042        static DataBuilder raw(byte[] data) {
043            return (buffer, patch) -> buffer.put(data);
044        }
045
046        static DataBuilder serializable(SerializableConstant c) {
047            return (buffer, patch) -> c.serialize(buffer);
048        }
049
050        static DataBuilder zero(int size) {
051            switch (size) {
052                case 1:
053                    return (buffer, patch) -> buffer.put((byte) 0);
054                case 2:
055                    return (buffer, patch) -> buffer.putShort((short) 0);
056                case 4:
057                    return (buffer, patch) -> buffer.putInt(0);
058                case 8:
059                    return (buffer, patch) -> buffer.putLong(0L);
060                default:
061                    return (buffer, patch) -> {
062                        int rest = size;
063                        while (rest > 8) {
064                            buffer.putLong(0L);
065                            rest -= 8;
066                        }
067                        while (rest > 0) {
068                            buffer.put((byte) 0);
069                            rest--;
070                        }
071                    };
072            }
073        }
074    }
075
076    public static final class Data {
077
078        private int alignment;
079        private final int size;
080        private final DataBuilder builder;
081
082        private DataSectionReference ref;
083
084        public Data(int alignment, int size, DataBuilder builder) {
085            this.alignment = alignment;
086            this.size = size;
087            this.builder = builder;
088
089            // initialized in DataSection.insertData(Data)
090            ref = null;
091        }
092
093        public void updateAlignment(int newAlignment) {
094            if (newAlignment == alignment) {
095                return;
096            }
097            alignment = lcm(alignment, newAlignment);
098        }
099
100        public int getAlignment() {
101            return alignment;
102        }
103
104        public int getSize() {
105            return size;
106        }
107
108        public DataBuilder getBuilder() {
109            return builder;
110        }
111
112        @Override
113        public int hashCode() {
114            // Data instances should not be used as hash map keys
115            throw new UnsupportedOperationException("hashCode");
116        }
117
118        @Override
119        public String toString() {
120            return identityHashCodeString(this);
121        }
122
123        @Override
124        public boolean equals(Object obj) {
125            assert ref != null;
126            if (obj == this) {
127                return true;
128            }
129            if (obj instanceof Data) {
130                Data that = (Data) obj;
131                if (this.alignment == that.alignment && this.size == that.size && this.ref.equals(that.ref)) {
132                    return true;
133                }
134            }
135            return false;
136        }
137    }
138
139    private final ArrayList<Data> dataItems = new ArrayList<>();
140
141    private boolean finalLayout;
142    private int sectionAlignment;
143    private int sectionSize;
144
145    @Override
146    public int hashCode() {
147        // DataSection instances should not be used as hash map keys
148        throw new UnsupportedOperationException("hashCode");
149    }
150
151    @Override
152    public String toString() {
153        return identityHashCodeString(this);
154    }
155
156    @Override
157    public boolean equals(Object obj) {
158        if (this == obj) {
159            return true;
160        }
161        if (obj instanceof DataSection) {
162            DataSection that = (DataSection) obj;
163            if (this.finalLayout == that.finalLayout && this.sectionAlignment == that.sectionAlignment && this.sectionSize == that.sectionSize && Objects.equals(this.dataItems, that.dataItems)) {
164                return true;
165            }
166        }
167        return false;
168    }
169
170    /**
171     * Insert a {@link Data} item into the data section. If the item is already in the data section,
172     * the same {@link DataSectionReference} is returned.
173     *
174     * @param data the {@link Data} item to be inserted
175     * @return a unique {@link DataSectionReference} identifying the {@link Data} item
176     */
177    public DataSectionReference insertData(Data data) {
178        assert !finalLayout;
179        if (data.ref == null) {
180            data.ref = new DataSectionReference();
181            dataItems.add(data);
182        }
183        return data.ref;
184    }
185
186    /**
187     * Compute the layout of the data section. This can be called only once, and after it has been
188     * called, the data section can no longer be modified.
189     */
190    public void finalizeLayout() {
191        assert !finalLayout;
192        finalLayout = true;
193
194        // simple heuristic: put items with larger alignment requirement first
195        dataItems.sort((a, b) -> a.alignment - b.alignment);
196
197        int position = 0;
198        for (Data d : dataItems) {
199            sectionAlignment = lcm(sectionAlignment, d.alignment);
200            position = align(position, d.alignment);
201
202            d.ref.setOffset(position);
203            position += d.size;
204        }
205
206        sectionSize = position;
207    }
208
209    /**
210     * Get the size of the data section. Can only be called after {@link #finalizeLayout}.
211     */
212    public int getSectionSize() {
213        assert finalLayout;
214        return sectionSize;
215    }
216
217    /**
218     * Get the minimum alignment requirement of the data section. Can only be called after
219     * {@link #finalizeLayout}.
220     */
221    public int getSectionAlignment() {
222        assert finalLayout;
223        return sectionAlignment;
224    }
225
226    /**
227     * Build the data section. Can only be called after {@link #finalizeLayout}.
228     *
229     * @param buffer The {@link ByteBuffer} where the data section should be built. The buffer must
230     *            hold at least {@link #getSectionSize()} bytes.
231     * @param patch A {@link Consumer} to receive {@link DataPatch data patches} for relocations in
232     *            the data section.
233     */
234    public void buildDataSection(ByteBuffer buffer, Consumer<DataPatch> patch) {
235        assert finalLayout;
236        for (Data d : dataItems) {
237            buffer.position(d.ref.getOffset());
238            d.builder.emit(buffer, patch);
239        }
240    }
241
242    public Data findData(DataSectionReference ref) {
243        for (Data d : dataItems) {
244            if (d.ref == ref) {
245                return d;
246            }
247        }
248        return null;
249    }
250
251    public Iterator<Data> iterator() {
252        return dataItems.iterator();
253    }
254
255    private static int lcm(int x, int y) {
256        if (x == 0) {
257            return y;
258        } else if (y == 0) {
259            return x;
260        }
261
262        int a = Math.max(x, y);
263        int b = Math.min(x, y);
264        while (b > 0) {
265            int tmp = a % b;
266            a = b;
267            b = tmp;
268        }
269
270        int gcd = a;
271        return x * y / gcd;
272    }
273
274    private static int align(int position, int alignment) {
275        return ((position + alignment - 1) / alignment) * alignment;
276    }
277
278    public void clear() {
279        assert !finalLayout;
280        this.dataItems.clear();
281        this.sectionAlignment = 0;
282        this.sectionSize = 0;
283    }
284}