view graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiTargetMethod.java @ 4558:3706975946e4

Make graph dumping a bit more robust when there is no method, enable debug in the startCompiler method, add context and scope for snippets installation Made IGV display graphs even if some edges are problematic When schedule failed don't use it
author Gilles Duboscq <duboscq@ssw.jku.at>
date Fri, 10 Feb 2012 02:22:23 +0100
parents aaac4894175c
children 07bcee8b70a4
line wrap: on
line source

/*
 * Copyright (c) 2009, 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.oracle.max.cri.ci;

import java.io.*;
import java.util.*;

import com.oracle.max.cri.ri.*;

/**
 * Represents the output from compiling a method, including the compiled machine code, associated data and references,
 * relocation information, deoptimization information, etc. It is the essential component of a {@link CiResult}, which also includes
 * {@linkplain CiStatistics compilation statistics} and {@linkplain CiBailout failure information}.
 */
public class CiTargetMethod implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = -1319947729753702434L;

    /**
     * Represents a code position with associated additional information.
     */
    public abstract static class Site implements Serializable {
        /**
         *
         */
        private static final long serialVersionUID = -8214214947651979102L;
        /**
         * The position (or offset) of this site with respect to the start of the target method.
         */
        public final int pcOffset;

        public Site(int pos) {
            this.pcOffset = pos;
        }
    }

    /**
     * Represents a safepoint with associated debug info.
     */
    public static class Safepoint extends Site implements Comparable<Safepoint> {
        /**
         *
         */
        private static final long serialVersionUID = 2479806696381720162L;
        public final CiDebugInfo debugInfo;

        Safepoint(int pcOffset, CiDebugInfo debugInfo) {
            super(pcOffset);
            this.debugInfo = debugInfo;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(pcOffset);
            sb.append("[<safepoint>]");
            appendDebugInfo(sb, debugInfo);
            return sb.toString();
        }

        @Override
        public int compareTo(Safepoint o) {
            if (pcOffset < o.pcOffset) {
                return -1;
            } else if (pcOffset > o.pcOffset) {
                return 1;
            }
            return 0;
        }
    }

    /**
     * Represents a call in the code.
     */
    public static final class Call extends Safepoint {
        /**
         *
         */
        private static final long serialVersionUID = 1440741241631046954L;

        /**
         * The target of the call.
         */
        public final Object target;

        /**
         * The size of the call instruction.
         */
        public final int size;

        /**
         * Specifies if this call is direct or indirect. A direct call has an immediate operand encoding
         * the absolute or relative (to the call itself) address of the target. An indirect call has a
         * register or memory operand specifying the target address of the call.
         */
        public final boolean direct;

        Call(Object target, int pcOffset, int size, boolean direct, CiDebugInfo debugInfo) {
            super(pcOffset, debugInfo);
            this.size = size;
            this.target = target;
            this.direct = direct;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(pcOffset);
            sb.append('[');
            sb.append(target);
            sb.append(']');

            if (debugInfo != null) {
                appendDebugInfo(sb, debugInfo);
            }

            return sb.toString();
        }
    }

    /**
     * Represents a reference to data from the code. The associated data can be any constant.
     */
    public static final class DataPatch extends Site {
        /**
         *
         */
        private static final long serialVersionUID = 5771730331604867476L;
        public final CiConstant constant;
        public final int alignment;

        DataPatch(int pcOffset, CiConstant data, int alignment) {
            super(pcOffset);
            this.constant = data;
            this.alignment = alignment;
        }

        @Override
        public String toString() {
            return String.format("%d[<data patch referring to data %s>]", pcOffset, constant);
        }
    }

    /**
     * Provides extra information about instructions or data at specific positions in {@link CiTargetMethod#targetCode()}.
     * This is optional information that can be used to enhance a disassembly of the code.
     */
    public abstract static class CodeAnnotation implements Serializable {
        /**
         *
         */
        private static final long serialVersionUID = -7903959680749520748L;
        public final int position;

        public CodeAnnotation(int position) {
            this.position = position;
        }
    }

    /**
     * A string comment about one or more instructions at a specific position in the code.
     */
    public static final class CodeComment extends CodeAnnotation {
        /**
         *
         */
        private static final long serialVersionUID = 6802287188701961401L;
        public final String value;
        public CodeComment(int position, String comment) {
            super(position);
            this.value = comment;
        }

        @Override
        public String toString() {
            return getClass().getSimpleName() + "@" + position + ": " + value;
        }
    }

    /**
     * Labels some inline data in the code.
     */
    public static final class InlineData extends CodeAnnotation {
        /**
         *
         */
        private static final long serialVersionUID = 305997507263827108L;
        public final int size;
        public InlineData(int position, int size) {
            super(position);
            this.size = size;
        }

        @Override
        public String toString() {
            return getClass().getSimpleName() + "@" + position + ": size=" + size;
        }
    }

    /**
     * Describes a table of signed offsets embedded in the code. The offsets are relative to the starting
     * address of the table. This type of table maybe generated when translating a multi-way branch
     * based on a key value from a dense value set (e.g. the {@code tableswitch} JVM instruction).
     *
     * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high} inclusive.
     */
    public static final class JumpTable extends CodeAnnotation {
        /**
         *
         */
        private static final long serialVersionUID = 2222194398353801831L;

        /**
         * The low value in the key range (inclusive).
         */
        public final int low;

        /**
         * The high value in the key range (inclusive).
         */
        public final int high;

        /**
         * The size (in bytes) of each table entry.
         */
        public final int entrySize;

        public JumpTable(int position, int low, int high, int entrySize) {
            super(position);
            this.low = low;
            this.high = high;
            this.entrySize = entrySize;
        }

        @Override
        public String toString() {
            return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]";
        }
    }

    /**
     * Describes a table of key and offset pairs. The offset in each table entry is relative to the address of
     * the table. This type of table maybe generated when translating a multi-way branch
     * based on a key value from a sparse value set (e.g. the {@code lookupswitch} JVM instruction).
     */
    public static final class LookupTable extends CodeAnnotation {
        /**
         *
         */
        private static final long serialVersionUID = 8367952567559116160L;

        /**
         * The number of entries in the table.
         */
        public final int npairs;

        /**
         * The size (in bytes) of entry's key.
         */
        public final int keySize;

        /**
         * The size (in bytes) of entry's offset value.
         */
        public final int offsetSize;

        public LookupTable(int position, int npairs, int keySize, int offsetSize) {
            super(position);
            this.npairs = npairs;
            this.keySize = keySize;
            this.offsetSize = offsetSize;
        }

        @Override
        public String toString() {
            return getClass().getSimpleName() + "@" + position + ": [npairs=" + npairs + ", keySize=" + keySize + ", offsetSize=" + offsetSize + "]";
        }
    }

    /**
     * Represents exception handler information for a specific code position. It includes the catch code position as
     * well as the caught exception type.
     */
    public static final class ExceptionHandler extends Site {
        /**
         *
         */
        private static final long serialVersionUID = 4897339464722665281L;
        public final int bci;
        public final int scopeLevel;
        public final int handlerPos;
        public final int handlerBci;
        public final RiType exceptionType;

        ExceptionHandler(int pcOffset, int bci, int scopeLevel, int handlerPos, int handlerBci, RiType exceptionType) {
            super(pcOffset);
            this.bci = bci;
            this.scopeLevel = scopeLevel;
            this.handlerPos = handlerPos;
            this.handlerBci = handlerBci;
            this.exceptionType = exceptionType;
        }

        @Override
        public String toString() {
            return String.format("%d[<exception edge to %d with type %s>]", pcOffset, handlerPos, (exceptionType == null) ? "null" : exceptionType);
        }
    }

    public static final class Mark extends Site {
        /**
         *
         */
        private static final long serialVersionUID = 3612943150662354844L;
        public final Object id;
        public final Mark[] references;

        Mark(int pcOffset, Object id, Mark[] references) {
            super(pcOffset);
            this.id = id;
            this.references = references;
        }

        @Override
        public String toString() {
            if (id == null) {
                return String.format("%d[<mark with %d references>]", pcOffset, references.length);
            } else if (id instanceof Integer) {
                return String.format("%d[<mark with %d references and id %s>]", pcOffset, references.length, Integer.toHexString((Integer) id));
            } else {
                return String.format("%d[<mark with %d references and id %s>]", pcOffset, references.length, id.toString());
            }
        }
    }

    /**
     * List of safepoints, sorted by {@link Site#pcOffset}.
     */
    public final List<Safepoint> safepoints = new ArrayList<>();

    /**
     * List of data references.
     */
    public final List<DataPatch> dataReferences = new ArrayList<>();

    /**
     * List of exception handlers.
     */
    public final List<ExceptionHandler> exceptionHandlers = new ArrayList<>();

    /**
     * List of marks.
     */
    public final List<Mark> marks = new ArrayList<>();

    private int frameSize = -1;
    private int customStackAreaOffset = -1;
    private int registerRestoreEpilogueOffset = -1;
    /**
     * The buffer containing the emitted machine code.
     */
    private byte[] targetCode;

    /**
     * The leading number of bytes in {@link #targetCode} containing the emitted machine code.
     */
    private int targetCodeSize;

    private ArrayList<CodeAnnotation> annotations;

    private CiAssumptions assumptions;

    /**
     * Constructs a new target method.
     */
    public CiTargetMethod() {
    }

    public void setAssumptions(CiAssumptions assumptions) {
        this.assumptions = assumptions;
    }

    public CiAssumptions assumptions() {
        return assumptions;
    }

    /**
     * Sets the frame size in bytes. Does not include the return address pushed onto the
     * stack, if any.
     *
     * @param size the size of the frame in bytes
     */
    public void setFrameSize(int size) {
        frameSize = size;
    }

    /**
     * Sets the machine that has been generated by the compiler.
     *
     * @param code the machine code generated
     * @param size the size of the machine code
     */
    public void setTargetCode(byte[] code, int size) {
        targetCode = code;
        targetCodeSize = size;
    }

    /**
     * Records a reference to the data section in the code section (e.g. to load an integer or floating point constant).
     *
     * @param codePos the position in the code where the data reference occurs
     * @param data the data that is referenced
     * @param alignment the alignment requirement of the data or 0 if there is no alignment requirement
     */
    public void recordDataReference(int codePos, CiConstant data, int alignment) {
        assert codePos >= 0 && data != null;
        dataReferences.add(new DataPatch(codePos, data, alignment));
    }

    /**
     * Records a call in the code array.
     *
     * @param codePos the position of the call in the code array
     * @param size the size of the call instruction
     * @param target the {@link RiRuntime#asCallTarget(Object) target} being called
     * @param debugInfo the debug info for the call
     * @param direct specifies if this is a {@linkplain Call#direct direct} call
     */
    public void recordCall(int codePos, int size, Object target, CiDebugInfo debugInfo, boolean direct) {
        final Call call = new Call(target, codePos, size, direct, debugInfo);
        addSafepoint(call);
    }

    /**
     * Records an exception handler for this method.
     *
     * @param codePos  the position in the code that is covered by the handler
     * @param handlerPos    the position of the handler
     * @param throwableType the type of exceptions handled by the handler
     */
    public void recordExceptionHandler(int codePos, int bci, int scopeLevel, int handlerPos, int handlerBci, RiType throwableType) {
        exceptionHandlers.add(new ExceptionHandler(codePos, bci, scopeLevel, handlerPos, handlerBci, throwableType));
    }

    /**
     * Records a safepoint in the code array.
     *
     * @param codePos the position of the safepoint in the code array
     * @param debugInfo the debug info for the safepoint
     */
    public void recordSafepoint(int codePos, CiDebugInfo debugInfo) {
        addSafepoint(new Safepoint(codePos, debugInfo));
    }

    private void addSafepoint(Safepoint safepoint) {
        // The safepoints list must always be sorted
        if (!safepoints.isEmpty() && safepoints.get(safepoints.size() - 1).pcOffset >= safepoint.pcOffset) {
            // This re-sorting should be very rare
            Collections.sort(safepoints);
        }
        safepoints.add(safepoint);
    }

    /**
     * Records an instruction mark within this method.
     *
     * @param codePos the position in the code that is covered by the handler
     * @param id the identifier for this mark
     * @param references an array of other marks that this mark references
     */
    public Mark recordMark(int codePos, Object id, Mark[] references) {
        Mark mark = new Mark(codePos, id, references);
        marks.add(mark);
        return mark;
    }

    /**
     * Allows a method to specify the offset of the epilogue that restores the callee saved registers. Must be called
     * iff the method is a callee saved method and stores callee registers on the stack.
     *
     * @param registerRestoreEpilogueOffset the offset in the machine code where the epilogue begins
     */
    public void setRegisterRestoreEpilogueOffset(int registerRestoreEpilogueOffset) {
        assert this.registerRestoreEpilogueOffset == -1;
        this.registerRestoreEpilogueOffset = registerRestoreEpilogueOffset;
    }

    /**
     * The frame size of the method in bytes.
     *
     * @return the frame size
     */
    public int frameSize() {
        assert frameSize != -1 : "frame size not yet initialized!";
        return frameSize;
    }

    /**
     * @return the code offset of the start of the epilogue that restores all callee saved registers, or -1 if this is
     *         not a callee saved method
     */
    public int registerRestoreEpilogueOffset() {
        return registerRestoreEpilogueOffset;
    }

    /**
     * Offset in bytes for the custom stack area (relative to sp).
     * @return the offset in bytes
     */
    public int customStackAreaOffset() {
        return customStackAreaOffset;
    }

    /**
     * @see #customStackAreaOffset()
     * @param offset
     */
    public void setCustomStackAreaOffset(int offset) {
        customStackAreaOffset = offset;
    }

    /**
     * @return the machine code generated for this method
     */
    public byte[] targetCode() {
        return targetCode;
    }

    /**
     * @return the size of the machine code generated for this method
     */
    public int targetCodeSize() {
        return targetCodeSize;
    }

    /**
     * @return the code annotations or {@code null} if there are none
     */
    public List<CodeAnnotation> annotations() {
        return annotations;
    }

    public void addAnnotation(CodeAnnotation annotation) {
        assert annotation != null;
        if (annotations == null) {
            annotations = new ArrayList<>();
        }
        annotations.add(annotation);
    }

    private static void appendDebugInfo(StringBuilder sb, CiDebugInfo info) {
        if (info != null) {
            appendRefMap(sb, "stackMap", info.frameRefMap);
            appendRefMap(sb, "registerMap", info.registerRefMap);
            CiCodePos codePos = info.codePos;
            if (codePos != null) {
                CiUtil.appendLocation(sb.append(" "), codePos.method, codePos.bci);
                if (info.hasFrame()) {
                    sb.append(" #locals=").append(info.frame().numLocals).append(" #expr=").append(info.frame().numStack);
                    if (info.frame().numLocks > 0) {
                        sb.append(" #locks=").append(info.frame().numLocks);
                    }
                }
            }
        }
    }

    private static void appendRefMap(StringBuilder sb, String name, CiBitMap map) {
        if (map != null) {
            sb.append(' ').append(name).append('[').append(map.toBinaryString()).append(']');
        }
    }
}