diff graal/com.oracle.max.asmdis/src/com/sun/max/asm/Assembler.java @ 3733:e233f5660da4

Added Java files from Maxine project.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Sat, 17 Dec 2011 19:59:18 +0100
parents
children bc8527f3071c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.max.asmdis/src/com/sun/max/asm/Assembler.java	Sat Dec 17 19:59:18 2011 +0100
@@ -0,0 +1,607 @@
+/*
+ * Copyright (c) 2007, 2011, 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 com.sun.max.asm;
+
+import java.io.*;
+import java.util.*;
+
+import com.sun.max.lang.*;
+import com.sun.max.program.*;
+
+/**
+ * Assembler base class.
+ */
+public abstract class Assembler {
+
+    private final Directives directives;
+
+    protected Assembler(byte byteData, boolean isValidCode) {
+        this.directives = new Directives(byteData, isValidCode);
+    }
+
+    public abstract ISA isa();
+
+    public final Directives directives() {
+        return directives;
+    }
+
+    /**
+     * Gets the width of a word on machine for which the code is being assembled.
+     */
+    public abstract WordWidth wordWidth();
+
+    /**
+     * Resets any internal state in this assembler to the equivalent state it had when first constructed.
+     * <p>
+     * This method is overridden as needed to ensure the state in subclasses is reset as well. Any
+     * overriding implementation must call this method in its superclass.
+     */
+    public Assembler reset() {
+        boundLabels.clear();
+        assembledObjects.clear();
+        mutableAssembledObjects.clear();
+        padOutput = false;
+        potentialExpansionSize = 0;
+        selectingLabelInstructions = true;
+        stream.reset();
+        if (this instanceof Assembler32) {
+            final Assembler32 assembler32 = (Assembler32) this;
+            assembler32.setStartAddress(0);
+        } else if (this instanceof Assembler64) {
+            final Assembler64 assembler64 = (Assembler64) this;
+            assembler64.setStartAddress(0);
+        }
+        return this;
+    }
+
+    /**
+     * A facility for including output during assembly that may not necessarily be decoded interpreted as code.
+     *
+     */
+    public final class Directives {
+
+        private final byte padByte;
+        private final boolean isValidCode;
+
+        public Directives(byte padByte, boolean isValidCode) {
+            this.padByte = padByte;
+            this.isValidCode = isValidCode;
+        }
+        /**
+         * Inserts as many {@linkplain #padByte pad bytes} as necessary to ensure that the next assembled object starts
+         * at an address aligned by a given number.
+         *
+         * @param alignment
+         *                the next assembled object is guaranteed to start at the next highest address starting at the
+         *                current address that is divisible by this value. Note that this computed address will be the
+         *                current address if the current address is already aligned by {@code alignment}
+         */
+        private void alignIfSpaceLeftSmallerThan(int alignment, int requiredSpace, int alignmentStart) {
+            final int startPosition = currentPosition();
+
+            // We avoid sign problems with '%' below by masking off the sign bit:
+            final long unsignedAddend = (baseAddress() + startPosition + alignmentStart) & Long.MAX_VALUE;
+
+            final int misalignmentSize = (int) (unsignedAddend % alignment);
+            final int padSize = misalignmentSize > 0 ? (alignment - misalignmentSize) : 0;
+            for (int i = 0; i < padSize; i++) {
+                emitByte(padByte);
+            }
+            new AlignmentPadding(Assembler.this, startPosition, currentPosition(), alignment, alignmentStart, requiredSpace, padByte) {
+                public boolean isCode() {
+                    return isValidCode;
+                }
+            };
+        }
+
+        public void align(int alignment) {
+            alignIfSpaceLeftSmallerThan(alignment, alignment, 0);
+        }
+
+        /**
+         * Enforce that the next assembled object fits within an aligned chunk.
+         * The specified space required by the next assembled object must be smaller or equals to the specified alignment. If the object cannot fit in the current chunk,
+         * Inserts as many {@linkplain #padByte pad bytes} as necessary to ensure that the next assembled object starts
+         * at the next alignment.
+         *
+         * @param alignment
+         *                the next assembled object is guaranteed to fit in within a block of memory whose starting address is the next
+         *                 address starting at the
+         *                current address that is divisible by this value.
+         *  @param requiredSpace size of the next assembled object; it must be smaller or equals to alignment
+         */
+        public boolean align(int alignment, int requiredSpace) {
+            if (alignment < requiredSpace) {
+                return false;
+            }
+            alignIfSpaceLeftSmallerThan(alignment, requiredSpace, 0);
+            return true;
+        }
+
+        /**
+         *  Inserts as many {@linkplain #padByte pad bytes} as necessary to ensure that the nth byte within the next assembled object starts
+         *   at an address aligned by a given number.
+         * @param alignment
+         * @param alignmentStart where the alignment should start within the next assembled object.
+         */
+        public void alignAfter(int alignment, int alignmentStart) {
+            alignIfSpaceLeftSmallerThan(alignment, alignment, alignmentStart);
+        }
+
+        public void inlineByte(byte byteValue) {
+            addInlineData(currentPosition(), Bytes.SIZE);
+            emitByte(byteValue);
+        }
+
+        public void inlineByteArray(byte[] byteArrayValue) {
+            addInlineData(currentPosition(), byteArrayValue.length);
+            emitByteArray(byteArrayValue, 0, byteArrayValue.length);
+        }
+
+        public void inlineByteArray(byte[] byteArrayValue, int offset, int length) {
+            addInlineData(currentPosition(), length);
+            emitByteArray(byteArrayValue, offset, length);
+        }
+
+        public void inlineShort(short shortValue) {
+            addInlineData(currentPosition(), Shorts.SIZE);
+            emitShort(shortValue);
+        }
+
+        public void inlineInt(int intValue) {
+            addInlineData(currentPosition(), Ints.SIZE);
+            emitInt(intValue);
+        }
+
+        public void inlineLong(long longValue) {
+            addInlineData(currentPosition(), Longs.SIZE);
+            emitLong(longValue);
+        }
+
+        /**
+         * Inlines the absolute address of a position (represented by a given label) in the assembled code.
+         * The absolute address is calculated as {@code baseAddress() + label.position()}. The size
+         * of the inlined address is determined by {@link Assembler#wordWidth()}.
+         *
+         * @param label the label whose absolute address is to be inlined
+         */
+        public AddressLiteral inlineAddress(Label label) {
+            final int startPosition = currentPosition();
+            // Emit placeholder bytes
+            final WordWidth width = wordWidth();
+            for (int i = 0; i < width.numberOfBytes; i++) {
+                emitByte((byte) 0);
+            }
+            final AddressLiteral addressLiteral = new AddressLiteral(Assembler.this, startPosition, currentPosition(), label);
+            assert addressLiteral.size() == width.numberOfBytes;
+            return addressLiteral;
+        }
+
+        /**
+         * Inlines the offset between two positions (represented by given labels) in the assembled code.
+         *
+         * @param base the label whose position marks the base of the offset
+         * @param target the label whose position marks the target of the offset
+         * @param width the fixed size to be used for the offset
+         */
+        public OffsetLiteral inlineOffset(Label target, Label base, WordWidth width) {
+            final int startPosition = currentPosition();
+            for (int i = 0; i < width.numberOfBytes; i++) {
+                emitByte((byte) 0);
+            }
+            final OffsetLiteral offsetLiteral = new OffsetLiteral(Assembler.this, startPosition, currentPosition(), target, base);
+            assert offsetLiteral.size() == width.numberOfBytes;
+            return offsetLiteral;
+        }
+    }
+
+    /**
+     * Gets the number of bytes that have been written to the underlying output stream.
+     */
+    public int currentPosition() {
+        return stream.size();
+    }
+
+    /**
+     * Gets the start address of the code assembled by this assembler.
+     */
+    public abstract long baseAddress();
+
+    private ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+    protected void emitByte(int byteValue) {
+        stream.write(byteValue);
+    }
+
+    protected void emitZeroes(int count) {
+        for (int i = 0; i < count; ++i) {
+            stream.write(0);
+        }
+    }
+
+    protected abstract void emitShort(short shortValue);
+
+    protected abstract void emitInt(int intValue);
+
+    protected abstract void emitLong(long longValue);
+
+    public void emitByteArray(byte[] byteArrayValue, int off, int len) {
+        stream.write(byteArrayValue, off, len);
+    }
+
+    private boolean selectingLabelInstructions = true;
+
+    boolean selectingLabelInstructions() {
+        return selectingLabelInstructions;
+    }
+
+    private final Set<Label> boundLabels = Collections.newSetFromMap(new IdentityHashMap<Label, Boolean>());
+
+    public Set<Label> boundLabels() {
+        return boundLabels;
+    }
+
+    /**
+     * Binds a given label to the current position in the assembler's instruction stream. The assembler may update the
+     * label's position if any emitted instructions change lengths, so that this label keeps addressing the same logical
+     * position.
+     *
+     * @param label
+     *                the label that is to be bound to the current position
+     *
+     * @see Label#fix32
+     */
+    public final void bindLabel(Label label) {
+        label.bind(currentPosition());
+        boundLabels.add(label);
+    }
+
+    private final List<AssembledObject> assembledObjects = new LinkedList<AssembledObject>();
+    private final List<MutableAssembledObject> mutableAssembledObjects = new LinkedList<MutableAssembledObject>();
+
+    private int potentialExpansionSize;
+
+    /**
+     * Adds the description of an instruction that is fixed in size.
+     *
+     * @param fixedSizeAssembledObject
+     */
+    void addFixedSizeAssembledObject(AssembledObject fixedSizeAssembledObject) {
+        assembledObjects.add(fixedSizeAssembledObject);
+        if (fixedSizeAssembledObject instanceof MutableAssembledObject) {
+            mutableAssembledObjects.add((MutableAssembledObject) fixedSizeAssembledObject);
+        }
+    }
+
+    /**
+     * Adds the description of an instruction that can change in size, depending on where it is located in the
+     * instruction and/or where another object it addresses is located in the instruction stream.
+     *
+     * @param spanDependentInstruction
+     */
+    void addSpanDependentInstruction(InstructionWithOffset spanDependentInstruction) {
+        assembledObjects.add(spanDependentInstruction);
+        mutableAssembledObjects.add(spanDependentInstruction);
+        // A span-dependent instruction's offset operand can potentially grow from 8 bits to 32 bits.
+        // Also, some instructions need an extra byte for encoding when not using an 8-bit operand.
+        // Together, this might enlarge every span-dependent label instruction by maximally 4 bytes.
+        potentialExpansionSize += 4;
+    }
+
+    void addAlignmentPadding(AlignmentPadding alignmentPadding) {
+        assembledObjects.add(alignmentPadding);
+        mutableAssembledObjects.add(alignmentPadding);
+        potentialExpansionSize += alignmentPadding.alignment() - alignmentPadding.size();
+    }
+
+    void addInlineData(int startPosition, int size) {
+        assembledObjects.add(new AssembledObject(startPosition, startPosition + size) {
+            public boolean isCode() {
+                return false;
+            }
+        });
+    }
+
+    private void gatherLabels() throws AssemblyException {
+        for (AssembledObject assembledObject : assembledObjects) {
+            if (assembledObject instanceof InstructionWithLabel) {
+                final InstructionWithLabel labelInstruction = (InstructionWithLabel) assembledObject;
+                switch (labelInstruction.label().state()) {
+                    case UNASSIGNED:
+                        throw new AssemblyException("unassigned label");
+                    case BOUND:
+                        boundLabels.add(labelInstruction.label());
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+
+    private boolean updateSpanDependentInstruction(InstructionWithOffset instruction) throws AssemblyException {
+        if (!instruction.updateLabelSize()) {
+            return false;
+        }
+        final int oldSize = instruction.size();
+        final int oldEndPosition = instruction.endPosition();
+        stream.reset();
+        instruction.assemble();
+        final int newSize = stream.toByteArray().length;
+        instruction.setSize(newSize);
+        final int delta = newSize - oldSize;
+        adjustMutableAssembledObjects(delta, oldEndPosition, null);
+        return true;
+    }
+
+    private boolean updateAlignmentPadding(AlignmentPadding alignmentPadding) throws AssemblyException {
+        final int oldSize = alignmentPadding.size();
+        final int oldEndPosition = alignmentPadding.endPosition();
+        alignmentPadding.updatePadding();
+        final int newSize = alignmentPadding.size();
+        if (oldSize != newSize) {
+            // Only if the padding expanded will subsequent objects in the stream need to be adjusted
+            final int delta = newSize - oldSize;
+            adjustMutableAssembledObjects(delta, oldEndPosition, alignmentPadding);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Adjusts the position of all the mutable objects that are currently at or after a given position.
+     *
+     * @param delta the amount by which the position of each mutable object is to be adjusted
+     * @param startPosition only mutable objects whose current {@linkplain AssembledObject#startPosition() start
+     *            position} is equal to or greater than this value are adjusted
+     * @param adjustedPadding the padding object whose adjustment caused the need for this call. If this call was made
+     *            for some other reason than having adjusted a padding object's size, then this value will be null. In this
+     *            value is not null, then its position will not be adjusted by this call.
+     */
+    private void adjustMutableAssembledObjects(int delta, int startPosition, AlignmentPadding adjustedPadding) throws AssemblyException {
+        for (Label label : boundLabels) {
+            if (label.position() >= startPosition) {
+                label.adjust(delta);
+            }
+        }
+
+        for (MutableAssembledObject mutableAssembledObject : mutableAssembledObjects) {
+            if (mutableAssembledObject != adjustedPadding && mutableAssembledObject.startPosition() >= startPosition) {
+                mutableAssembledObject.adjust(delta);
+            }
+        }
+    }
+
+    private void updateSpanDependentVariableInstructions() throws AssemblyException {
+        boolean changed;
+        do {
+            changed = false;
+            for (MutableAssembledObject mutableAssembledObject : mutableAssembledObjects) {
+                if (mutableAssembledObject instanceof InstructionWithOffset) {
+                    changed |= updateSpanDependentInstruction((InstructionWithOffset) mutableAssembledObject);
+                } else if (mutableAssembledObject instanceof AlignmentPadding) {
+                    changed |= updateAlignmentPadding((AlignmentPadding) mutableAssembledObject);
+                }
+            }
+        } while (changed);
+    }
+
+    private int writeOutput(OutputStream outputStream, byte[] initialBytes, InlineDataRecorder inlineDataRecorder) throws IOException, AssemblyException {
+        selectingLabelInstructions = false;
+        int bytesWritten = 0;
+        try {
+            int initialOffset = 0;
+            for (AssembledObject assembledObject : assembledObjects) {
+                if (inlineDataRecorder != null && !assembledObject.isCode()) {
+                    inlineDataRecorder.add(new InlineDataDescriptor.ByteData(assembledObject.startPosition(), assembledObject.size()));
+                }
+
+                if (assembledObject instanceof MutableAssembledObject) {
+                    final MutableAssembledObject mutableAssembledObject = (MutableAssembledObject) assembledObject;
+
+                    // Copy the original assembler output between the end of the last mutable assembled object and the start of the current one
+                    final int length = mutableAssembledObject.initialStartPosition() - initialOffset;
+                    outputStream.write(initialBytes, initialOffset, length);
+                    bytesWritten += length;
+
+                    // Now (re)assemble the mutable assembled object
+                    stream.reset();
+                    mutableAssembledObject.assemble();
+                    stream.writeTo(outputStream);
+                    bytesWritten += stream.size();
+                    initialOffset = mutableAssembledObject.initialEndPosition();
+                } else {
+                    // Copy the original assembler output between the end of the last assembled object and the end of current one
+                    final int length = assembledObject.endPosition() - initialOffset;
+                    outputStream.write(initialBytes, initialOffset, length);
+                    bytesWritten += length;
+                    initialOffset = assembledObject.endPosition();
+                }
+            }
+
+            // Copy the original assembler output (if any) after the last mutable assembled object
+            outputStream.write(initialBytes, initialOffset, initialBytes.length - initialOffset);
+            bytesWritten += initialBytes.length - initialOffset;
+
+            if (padOutput) {
+                final int padding = (initialBytes.length + potentialExpansionSize) - bytesWritten;
+                assert padding >= 0;
+                if (padding > 0) {
+                    stream.reset();
+                    emitPadding(padding);
+                    stream.writeTo(outputStream);
+                    bytesWritten += padding;
+                }
+            }
+            return bytesWritten;
+        } finally {
+            selectingLabelInstructions = true;
+        }
+    }
+
+    /**
+     * Emits padding to the instruction stream in the form of NOP instructions.
+     *
+     * @param numberOfBytes
+     * @throws AssemblyException if exactly {@code numberOfBytes} cannot be emitted as a sequence of one or more valid NOP instructions
+     */
+    protected abstract void emitPadding(int numberOfBytes) throws AssemblyException;
+
+    /**
+     * Writes the object code assembled so far to a given output stream.
+     *
+     * @return the number of bytes written {@code outputStream}
+     * @throws AssemblyException
+     *             if there any problem with binding labels to addresses
+     */
+    public int output(OutputStream outputStream, InlineDataRecorder inlineDataRecorder) throws IOException, AssemblyException {
+        final int upperLimitForCurrentOutputSize = upperLimitForCurrentOutputSize();
+        final byte[] initialBytes = stream.toByteArray();
+        gatherLabels();
+        updateSpanDependentVariableInstructions();
+        final int bytesWritten = writeOutput(outputStream, initialBytes, inlineDataRecorder);
+        assert !padOutput || upperLimitForCurrentOutputSize == bytesWritten;
+        return bytesWritten;
+    }
+
+    /**
+     * Gets the maximum size of the code array that would be assembled by a call to {@link #output(OutputStream)} or
+     * {@link #toByteArray()}. For a variable sized instruction set (e.g. x86), the exact size may be known until the
+     * code is assembled as the size of certain instructions depends on their position in the instruction and/or the
+     * {@linkplain #baseAddress() base address} at which the code is being assembled.
+     * <p>
+     * <b>Note that any subsequent call that adds a new instruction to the instruction stream invalidates the value
+     * returned by this method.</b>
+     */
+    public int upperLimitForCurrentOutputSize() {
+        return currentPosition() + potentialExpansionSize;
+    }
+
+    private boolean padOutput;
+
+    /**
+     * Sets or unsets the flag determining if the code assembled by a call to {@link #output(OutputStream)} or
+     * {@link #toByteArray()} should be padded with NOPs at the end to ensure that the code size equals the value
+     * returned by {@link #upperLimitForCurrentOutputSize()}. This default value of the flag is {@code false}.
+     */
+    public void setPadOutput(boolean flag) {
+        padOutput = flag;
+    }
+
+    /**
+     * Returns the object code assembled so far in a byte array.
+     *
+     * @throws AssemblyException
+     *             if there any problem with binding labels to addresses
+     */
+    public byte[] toByteArray(InlineDataRecorder inlineDataRecorder) throws AssemblyException {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(upperLimitForCurrentOutputSize());
+        try {
+            output(baos, inlineDataRecorder);
+            baos.close();
+            final byte[] result = baos.toByteArray();
+            return result;
+        } catch (IOException ioException) {
+            throw ProgramError.unexpected("IOException during output to byte array", ioException);
+        }
+    }
+
+    public byte[] toByteArray() throws AssemblyException {
+        return toByteArray(null);
+    }
+
+    /**
+     * @see Label#fix32(int)
+     */
+    protected void fixLabel32(Label label, int address32) {
+        label.fix32(address32);
+    }
+
+    /**
+     * @see Label#fix64(long)
+     */
+    protected void fixLabel64(Label label, long address64) {
+        label.fix64(address64);
+    }
+
+    protected int address32(Label label) throws AssemblyException {
+        return label.address32();
+    }
+
+    protected long address64(Label label) throws AssemblyException {
+        return label.address64();
+    }
+
+    protected void checkConstraint(boolean passed, String expression) {
+        if (!passed) {
+            throw new IllegalArgumentException(expression);
+        }
+    }
+
+    /**
+     * Calculate the difference between two Labels. This works whether the labels
+     * are fixed or bound.
+     * @throws AssemblyException
+     */
+    public int labelOffsetRelative(Label label, Label relativeTo) throws AssemblyException {
+        return labelOffsetRelative(label, 0) - labelOffsetRelative(relativeTo, 0);
+    }
+
+    /**
+     * Calculate the difference between a Label and a position within the assembled code.
+     * @throws AssemblyException
+     */
+    public int labelOffsetRelative(Label label, int position) throws AssemblyException {
+        switch (label.state()) {
+            case BOUND: {
+                return label.position() - position;
+            }
+            case FIXED_32: {
+                final Assembler32 assembler32 = (Assembler32) this;
+                return assembler32.address(label) - (assembler32.startAddress() + position);
+            }
+            case FIXED_64: {
+                final Assembler64 assembler64 = (Assembler64) this;
+                final long offset64 = assembler64.address(label) - (assembler64.startAddress() + position);
+                if (Longs.numberOfEffectiveSignedBits(offset64) > 32) {
+                    throw new AssemblyException("fixed 64-bit label out of 32-bit range");
+                }
+                return (int) offset64;
+            }
+            default: {
+                throw new AssemblyException("unassigned label");
+            }
+        }
+    }
+
+    /**
+     * Calculate the difference between a Label and an assembled object.
+     * Different CPUs have different conventions for which end of an
+     * instruction to measure from.
+     * @throws AssemblyException
+     */
+    public final int offsetInstructionRelative(Label label, AssemblyObject assembledObject) throws AssemblyException {
+        final int position = (isa().relativeBranchFromStart) ? assembledObject.startPosition() : assembledObject.endPosition();
+        return labelOffsetRelative(label, position);
+    }
+}