--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.code/src/org/graalvm/compiler/code/DataSection.java 2016-12-07 13:47:41.944719545 -0800 @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.code; + +import static jdk.vm.ci.meta.MetaUtil.identityHashCodeString; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Objects; +import java.util.function.BiConsumer; + +import org.graalvm.compiler.code.DataSection.Data; + +import jdk.vm.ci.code.site.DataSectionReference; +import jdk.vm.ci.meta.SerializableConstant; +import jdk.vm.ci.meta.VMConstant; + +public final class DataSection implements Iterable { + + public interface Patches { + + void registerPatch(VMConstant c); + } + + public abstract static class Data { + + private int alignment; + private final int size; + + private DataSectionReference ref; + + protected Data(int alignment, int size) { + this.alignment = alignment; + this.size = size; + + // initialized in DataSection.insertData(Data) + ref = null; + } + + protected abstract void emit(ByteBuffer buffer, Patches patches); + + public void updateAlignment(int newAlignment) { + if (newAlignment == alignment) { + return; + } + alignment = lcm(alignment, newAlignment); + } + + public int getAlignment() { + return alignment; + } + + public int getSize() { + return size; + } + + @Override + public int hashCode() { + // Data instances should not be used as hash map keys + throw new UnsupportedOperationException("hashCode"); + } + + @Override + public String toString() { + return identityHashCodeString(this); + } + + @Override + public boolean equals(Object obj) { + assert ref != null; + if (obj == this) { + return true; + } + if (obj instanceof Data) { + Data that = (Data) obj; + if (this.alignment == that.alignment && this.size == that.size && this.ref.equals(that.ref)) { + return true; + } + } + return false; + } + } + + public static final class RawData extends Data { + + private final byte[] data; + + public RawData(byte[] data, int alignment) { + super(alignment, data.length); + this.data = data; + } + + @Override + protected void emit(ByteBuffer buffer, Patches patches) { + buffer.put(data); + } + } + + public static final class SerializableData extends Data { + + private final SerializableConstant constant; + + public SerializableData(SerializableConstant constant) { + this(constant, 1); + } + + public SerializableData(SerializableConstant constant, int alignment) { + super(alignment, constant.getSerializedSize()); + this.constant = constant; + } + + @Override + protected void emit(ByteBuffer buffer, Patches patches) { + int position = buffer.position(); + constant.serialize(buffer); + assert buffer.position() - position == constant.getSerializedSize() : "wrong number of bytes written"; + } + } + + public static class ZeroData extends Data { + + protected ZeroData(int alignment, int size) { + super(alignment, size); + } + + public static ZeroData create(int alignment, int size) { + switch (size) { + case 1: + return new ZeroData(alignment, size) { + @Override + protected void emit(ByteBuffer buffer, Patches patches) { + buffer.put((byte) 0); + } + }; + + case 2: + return new ZeroData(alignment, size) { + @Override + protected void emit(ByteBuffer buffer, Patches patches) { + buffer.putShort((short) 0); + } + }; + + case 4: + return new ZeroData(alignment, size) { + @Override + protected void emit(ByteBuffer buffer, Patches patches) { + buffer.putInt(0); + } + }; + + case 8: + return new ZeroData(alignment, size) { + @Override + protected void emit(ByteBuffer buffer, Patches patches) { + buffer.putLong(0); + } + }; + + default: + return new ZeroData(alignment, size); + } + } + + @Override + protected void emit(ByteBuffer buffer, Patches patches) { + int rest = getSize(); + while (rest > 8) { + buffer.putLong(0L); + rest -= 8; + } + while (rest > 0) { + buffer.put((byte) 0); + rest--; + } + } + } + + public static final class PackedData extends Data { + + private final Data[] nested; + + private PackedData(int alignment, int size, Data[] nested) { + super(alignment, size); + this.nested = nested; + } + + public static PackedData create(Data[] nested) { + int size = 0; + int alignment = 1; + for (int i = 0; i < nested.length; i++) { + assert size % nested[i].getAlignment() == 0 : "invalid alignment in packed constants"; + alignment = DataSection.lcm(alignment, nested[i].getAlignment()); + size += nested[i].getSize(); + } + return new PackedData(alignment, size, nested); + } + + @Override + protected void emit(ByteBuffer buffer, Patches patches) { + for (Data data : nested) { + data.emit(buffer, patches); + } + } + } + + private final ArrayList dataItems = new ArrayList<>(); + + private boolean closed; + private int sectionAlignment; + private int sectionSize; + + @Override + public int hashCode() { + // DataSection instances should not be used as hash map keys + throw new UnsupportedOperationException("hashCode"); + } + + @Override + public String toString() { + return identityHashCodeString(this); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DataSection) { + DataSection that = (DataSection) obj; + if (this.closed == that.closed && this.sectionAlignment == that.sectionAlignment && this.sectionSize == that.sectionSize && Objects.equals(this.dataItems, that.dataItems)) { + return true; + } + } + return false; + } + + /** + * Inserts a {@link Data} item into the data section. If the item is already in the data + * section, the same {@link DataSectionReference} is returned. + * + * @param data the {@link Data} item to be inserted + * @return a unique {@link DataSectionReference} identifying the {@link Data} item + */ + public DataSectionReference insertData(Data data) { + checkOpen(); + synchronized (data) { + if (data.ref == null) { + data.ref = new DataSectionReference(); + dataItems.add(data); + } + return data.ref; + } + } + + /** + * Transfers all {@link Data} from the provided other {@link DataSection} to this + * {@link DataSection}, and empties the other section. + */ + public void addAll(DataSection other) { + checkOpen(); + other.checkOpen(); + + for (Data data : other.dataItems) { + assert data.ref != null; + dataItems.add(data); + } + other.dataItems.clear(); + } + + /** + * Determines if this object has been {@link #close() closed}. + */ + public boolean closed() { + return closed; + } + + /** + * Computes the layout of the data section and closes this object to further updates. + * + * This must be called exactly once. + */ + public void close() { + checkOpen(); + closed = true; + + // simple heuristic: put items with larger alignment requirement first + dataItems.sort((a, b) -> a.alignment - b.alignment); + + int position = 0; + int alignment = 1; + for (Data d : dataItems) { + alignment = lcm(alignment, d.alignment); + position = align(position, d.alignment); + + d.ref.setOffset(position); + position += d.size; + } + + sectionAlignment = alignment; + sectionSize = position; + } + + /** + * Gets the size of the data section. + * + * This must only be called once this object has been {@linkplain #closed() closed}. + */ + public int getSectionSize() { + checkClosed(); + return sectionSize; + } + + /** + * Gets the minimum alignment requirement of the data section. + * + * This must only be called once this object has been {@linkplain #closed() closed}. + */ + public int getSectionAlignment() { + checkClosed(); + return sectionAlignment; + } + + /** + * Builds the data section into a given buffer. + * + * This must only be called once this object has been {@linkplain #closed() closed}. + * + * @param buffer the {@link ByteBuffer} where the data section should be built. The buffer must + * hold at least {@link #getSectionSize()} bytes. + * @param patch a {@link Patches} instance to receive {@link VMConstant constants} for + * relocations in the data section + */ + public void buildDataSection(ByteBuffer buffer, Patches patch) { + buildDataSection(buffer, patch, (r, s) -> { + }); + } + + /** + * Builds the data section into a given buffer. + * + * This must only be called once this object has been {@linkplain #closed() closed}. When this + * method returns, the buffers' position is just after the last data item. + * + * @param buffer the {@link ByteBuffer} where the data section should be built. The buffer must + * hold at least {@link #getSectionSize()} bytes. + * @param patch a {@link Patches} instance to receive {@link VMConstant constants} for + * @param onEmit a function that is called before emitting each data item with the + * {@link DataSectionReference} and the size of the data. + */ + public void buildDataSection(ByteBuffer buffer, Patches patch, BiConsumer onEmit) { + checkClosed(); + assert buffer.remaining() >= sectionSize; + int start = buffer.position(); + for (Data d : dataItems) { + buffer.position(start + d.ref.getOffset()); + onEmit.accept(d.ref, d.getSize()); + d.emit(buffer, patch); + } + buffer.position(start + sectionSize); + } + + public Data findData(DataSectionReference ref) { + for (Data d : dataItems) { + if (d.ref == ref) { + return d; + } + } + return null; + } + + public static void emit(ByteBuffer buffer, Data data, Patches patch) { + data.emit(buffer, patch); + } + + @Override + public Iterator iterator() { + return dataItems.iterator(); + } + + private static int lcm(int x, int y) { + if (x == 0) { + return y; + } else if (y == 0) { + return x; + } + + int a = Math.max(x, y); + int b = Math.min(x, y); + while (b > 0) { + int tmp = a % b; + a = b; + b = tmp; + } + + int gcd = a; + return x * y / gcd; + } + + private static int align(int position, int alignment) { + return ((position + alignment - 1) / alignment) * alignment; + } + + private void checkClosed() { + if (!closed) { + throw new IllegalStateException(); + } + } + + private void checkOpen() { + if (closed) { + throw new IllegalStateException(); + } + } + + public void clear() { + checkOpen(); + this.dataItems.clear(); + this.sectionAlignment = 0; + this.sectionSize = 0; + } +}