/* * Copyright (c) 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.vm.ext.t1x; import static com.oracle.max.vm.ext.t1x.T1X.*; import static com.sun.max.platform.Platform.*; import static com.sun.max.vm.MaxineVM.*; import static com.sun.max.vm.compiler.target.Safepoints.*; import java.lang.annotation.*; import java.util.*; import com.oracle.max.vm.ext.maxri.*; import com.oracle.max.vm.ext.t1x.amd64.*; import com.sun.cri.ci.*; import com.sun.cri.ci.CiCallingConvention.Type; import com.sun.max.annotate.*; import com.sun.max.vm.actor.member.*; import com.sun.max.vm.classfile.*; import com.sun.max.vm.classfile.LocalVariableTable.Entry; import com.sun.max.vm.collect.*; import com.sun.max.vm.compiler.*; import com.sun.max.vm.compiler.target.*; import com.sun.max.vm.compiler.target.Safepoints.Attr; import com.sun.max.vm.runtime.*; import com.sun.max.vm.type.*; /** * A T1X template is a piece of machine code (and its associated metadata) that * is used by the T1X compiler to quickly translate a bytecode instruction * to native code. */ public class T1XTemplate { public static class T1XSafepoint { public static final int NO_BSM_INDEX = Integer.MIN_VALUE; public T1XSafepoint() { } public T1XSafepoint(int safepoint, int bci) { this.safepoint = safepoint; this.bci = bci; } int safepoint; public int pos() { return Safepoints.pos(safepoint); } public int causePos() { return Safepoints.causePos(safepoint); } public int attrs() { return safepoint & ATTRS_MASK; } public boolean isSet(Attr a) { return a.isSet(safepoint); } public boolean isCall() { return isSet(DIRECT_CALL) || isSet(INDIRECT_CALL); } /** * Bytecode index of this safepoint. This is {@code -1} for a template. */ int bci; /** * The direct callee (if any) at this safepoint. */ Object callee; CiBitMap frameRefMap; CiBitMap regRefMap; @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Attr f : Safepoints.ALL_ATTRS) { if (f.isSet(safepoint)) { sb.append(f.name).append(' '); } } sb.append("@ "). append(pos()). append(" [bci: "). append(bci); if (frameRefMap != null) { sb.append(", frameRefMap:").append(frameRefMap); } if (regRefMap != null) { sb.append(", regRefMap:").append(regRefMap); } return sb.append("]").toString(); } } private static final T1XSafepoint[] NO_SAFEPOINTS = {}; /** * The source bytecode of this template. */ public final ClassMethodActor method; /** * The template tag. */ public final T1XTemplateTag tag; /** * This template's machine code. */ public final byte[] code; /** * The safepoints in this template. */ public final T1XSafepoint[] safepoints; /** * An object literal and the patch positions of the instructions that load it. */ public static class ObjectLiteral { public final Object value; /** * The interpretation of the position is platform dependent. * For example, on AMD64, it is the displacement operand of a MOVQ instruction. */ public final int[] patchPosns; public ObjectLiteral(Object value, int[] patchPosns) { super(); this.value = value; this.patchPosns = patchPosns; } } /** * The object literals in this template or {@code null} if there are none. */ public final ObjectLiteral[] objectLiterals; /** * Describes the signature of a {@linkplain T1X_TEMPLATE template} * in terms of register mapping for the parameters and stack usage. */ public final Sig sig; @Override public String toString() { return String.valueOf(tag) + " [" + method + "]"; } static final class SafepointArray { T1XSafepoint[] data = new T1XSafepoint[10]; int size; public void add(T1XSafepoint safepoint) { ensureCapacity(data.length + 1); data[size++] = safepoint; } void ensureCapacity(int minCapacity) { if (minCapacity > data.length) { int newCapacity = (size * 3) / 2 + 1; if (newCapacity < minCapacity) { newCapacity = minCapacity; } data = Arrays.copyOf(data, newCapacity); } } public T1XSafepoint make(int index) { ensureCapacity(index + 1); T1XSafepoint safepoint = data[index]; if (safepoint == null) { safepoint = new T1XSafepoint(); data[index] = safepoint; } return safepoint; } public T1XSafepoint makeNext() { return make(size++); } public void clear() { size = 0; } public T1XSafepoint get(int i) { return data[i]; } } public static class SafepointsBuilder { SafepointArray safepointsArray; Safepoints safepoints; int directCalls; int bcisWithSafepoints; int lastBCI; int lastNonTemplateSafepointBCI; public Object[] directCallees; public byte[] refMaps; public BytecodeSafepointsIterator bytecodeSafepointsIterator; public SafepointsBuilder() { reset(true); } void reset(boolean hard) { directCalls = 0; bcisWithSafepoints = 0; lastBCI = -1; lastNonTemplateSafepointBCI = -1; if (hard) { safepointsArray = new SafepointArray(); } else { safepointsArray.clear(); } } void reserveInBSM(T1XSafepoint safepoint) { if (safepoint.bci >= 0) { if (lastBCI != safepoint.bci) { lastBCI = safepoint.bci; bcisWithSafepoints++; } } } /** * Adds a safepoint for non-template code. Such a safepoint must come * after all template code safepoints. This way we know that the * template slots are dead and so can be ignored in the gc maps. * * @param bci * @param safepoint * @param directCallee */ public void addSafepoint(int bci, int safepoint, ClassMethodActor directCallee) { T1XSafepoint dst = safepointsArray.makeNext(); dst.bci = bci; dst.safepoint = safepoint; dst.callee = directCallee; if (directCallee != null) { directCalls++; } // No GC maps needed: the template slots are dead for the remainder of the template dst.regRefMap = null; dst.frameRefMap = null; reserveInBSM(dst); lastNonTemplateSafepointBCI = bci; } public void add(T1XTemplate template, int pos, int bci) { assert bci == -1 || bci != lastNonTemplateSafepointBCI : "safepoints in template code must always precede non-template safepoints for any specific BCI"; for (T1XSafepoint src : template.safepoints) { T1XSafepoint dst = safepointsArray.makeNext(); dst.bci = bci; dst.safepoint = make(pos + src.pos(), pos + src.causePos(), src.attrs()); if (src.isSet(DIRECT_CALL)) { // The decision as to whether ref-maps are used is made when the template is created dst.frameRefMap = src.frameRefMap; dst.regRefMap = src.regRefMap; dst.callee = src.callee; directCalls++; } else { assert !src.isSet(DIRECT_CALL); dst.callee = null; dst.frameRefMap = src.frameRefMap; dst.regRefMap = src.regRefMap; } reserveInBSM(dst); } } public void pack(int frameRefMapSize, int regRefMapSize, int firstTemplateSlot, Adapter adapter) { final int adapterCount = adapter == null ? 0 : 1; if (adapter != null) { directCalls++; } final int safepointsCount = safepointsArray.size + adapterCount; safepoints = null; directCallees = TargetMethod.NO_DIRECT_CALLEES; refMaps = null; bytecodeSafepointsIterator = null; if (safepointsCount > 0) { assert frameRefMapSize > 0; int refMapSize = frameRefMapSize + regRefMapSize; refMaps = new byte[safepointsCount * refMapSize]; int[] safepoints = new int[safepointsCount]; // An empty method with no method profile only has an adapter and no safepoints int[] bsm = new int[bcisWithSafepoints * 2]; int firstSafepointIndexWithBCI = -1; int bsmIndex = -1; if (directCalls > 0) { directCallees = new Object[directCalls]; } final ByteArrayBitMap bitMap = new ByteArrayBitMap(refMaps, 0, 0); int safepointIndex = 0; int dcIndex = 0; if (adapter != null) { directCallees[dcIndex++] = adapter; int callPos = adapter.callOffsetInPrologue(); int safepointPos = safepointPosForCall(callPos, adapter.callSizeInPrologue()); safepoints[safepointIndex] = Safepoints.make(safepointPos, callPos, DIRECT_CALL); safepointIndex++; } for (int i = 0; i < safepointsArray.size; i++) { T1XSafepoint t1xSafepoint = safepointsArray.get(i); safepoints[safepointIndex] = t1xSafepoint.safepoint; if (t1xSafepoint.bci >= 0) { if (bsmIndex == -1) { bsmIndex = 0; firstSafepointIndexWithBCI = safepointIndex; bsm[bsmIndex] = t1xSafepoint.bci; } else if (bsm[bsmIndex] != t1xSafepoint.bci) { int prev = bsm[bsmIndex]; assert prev < t1xSafepoint.bci; bsmIndex += 2; assert bsmIndex + 1 < bsm.length; bsm[bsmIndex] = t1xSafepoint.bci; } bsm[bsmIndex + 1]++; } if (t1xSafepoint.isSet(DIRECT_CALL)) { directCallees[dcIndex++] = t1xSafepoint.callee; } if (t1xSafepoint.frameRefMap != null) { bitMap.setOffset(safepointIndex * refMapSize); bitMap.setSize(frameRefMapSize); for (int bit = t1xSafepoint.frameRefMap.nextSetBit(0); bit >= 0; bit = t1xSafepoint.frameRefMap.nextSetBit(bit + 1)) { bitMap.set(bit + firstTemplateSlot); } } if (t1xSafepoint.regRefMap != null) { bitMap.setOffset((safepointIndex * refMapSize) + frameRefMapSize); bitMap.setSize(regRefMapSize); for (int bit = t1xSafepoint.regRefMap.nextSetBit(0); bit >= 0; bit = t1xSafepoint.regRefMap.nextSetBit(bit + 1)) { bitMap.set(bit); } } safepointIndex++; } this.safepoints = new Safepoints(safepoints); bytecodeSafepointsIterator = new BytecodeSafepointsIterator(bsm, firstSafepointIndexWithBCI); } else { safepoints = Safepoints.NO_SAFEPOINTS; } } } /** * Describes an argument or return value of a {@linkplain T1X_TEMPLATE template} method. */ public static class Arg { /** * The kind of this arg. */ public final Kind kind; public final String name; /** * The register in which this arg is passed. */ public final CiRegister reg; /** * The operand stack index of the slot(s) holding this arg's value. * This will be -1 if this arg does not get its value from the operand stack. * @see Slot */ public final int slot; public Arg(Kind kind, CiRegister reg, String name, int slot) { this.kind = kind; this.name = name; this.reg = reg; this.slot = slot; } /** * Determines if this arg gets its value from the operand stack. * If this arg represents the return value, then this method * determines if the result is written to the stack. */ public boolean isStack() { return slot >= 0; } /** * Gets the number of stack slots holding this arg's value. * This will be 0 if this arg does not get its value from the stack. */ public int stackSlots() { if (slot < 0) { return 0; } else { return kind.stackSlots; } } @Override public String toString() { return name + ':' + kind + "[reg=" + reg + ", slot=" + slot + "]"; } } /** * Describes the signature of a {@linkplain T1X_TEMPLATE template} * in terms of register mapping for the parameters and stack usage. */ public static class Sig { /** * The parameters of the template method. */ public final Arg[] in; /** * The return value of the template method. */ public final Arg out; /** * The net adjustment in terms of slots to the operand stack based on * the stack-based parameters and stack-based result of the template. */ public final int stackDelta; /** * The number of {@link #in parameters} whose value comes from the operand stack. */ public final int stackArgs; @HOSTED_ONLY public Sig(Arg[] in, Arg out) { this.in = in; this.out = out; int stackDelta = out.stackSlots(); int stackArgs = 0; for (Arg a : in) { if (a.isStack()) { stackDelta -= a.stackSlots(); stackArgs++; } else { assert a.stackSlots() == 0; } } this.stackDelta = stackDelta; this.stackArgs = stackArgs; } } @HOSTED_ONLY private static CiBitMap nullIfEmpty(CiBitMap bm) { return bm.cardinality() == 0 ? null : bm; } @HOSTED_ONLY public T1XTemplate(MaxTargetMethod source, T1XTemplateTag tag, ClassMethodActor method) { this.method = method; this.code = source.code(); this.tag = tag; int nSafepoints = source.safepoints().size(); if (nSafepoints == 0) { safepoints = NO_SAFEPOINTS; } else { safepoints = new T1XSafepoint[nSafepoints]; Safepoints sourceSafepoints = source.safepoints(); int dcIndex = 0; for (int safepointIndex = 0; safepointIndex < sourceSafepoints.size(); safepointIndex++) { int safepoint = sourceSafepoints.safepointAt(safepointIndex); T1XSafepoint t1xSafepoint = new T1XSafepoint(safepoint, -1); t1xSafepoint.callee = null; t1xSafepoint.frameRefMap = nullIfEmpty(source.debugInfo().frameRefMapAt(safepointIndex)); t1xSafepoint.regRefMap = nullIfEmpty(source.debugInfo().regRefMapAt(safepointIndex)); if (sourceSafepoints.isSetAt(DIRECT_CALL, safepointIndex)) { t1xSafepoint.callee = source.directCallees()[dcIndex]; assert t1xSafepoint.callee != null; // The calling convention for stubs is not guaranteed to be compatible with T1X's // calling convention. For example, some compiler stubs pass arguments and receive // return values purely on the stack. assert !(t1xSafepoint.callee instanceof Stub) : source + " cannot call a stub: " + t1xSafepoint.callee; dcIndex++; } safepoints[safepointIndex] = t1xSafepoint; } } sig = initSig(method); if (source.referenceLiterals() != null) { Object[] sourceLiterals = source.referenceLiterals(); List<ObjectLiteral> buf = new ArrayList<ObjectLiteral>(source.referenceLiterals().length); for (int i = 0; i < sourceLiterals.length; i++) { int dispFromCodeStart = dispFromCodeStart(sourceLiterals.length, 0, i, true); int[] patchPosns = findDataPatchPosns(source, dispFromCodeStart); FatalError.check(patchPosns.length > 0, source + ": could not find load of reference literal " + i + " \"" + sourceLiterals[i] + "\""); buf.add(new ObjectLiteral(sourceLiterals[i], patchPosns)); } this.objectLiterals = buf.toArray(new ObjectLiteral[buf.size()]); } else { objectLiterals = null; } } @HOSTED_ONLY static String toHexString(byte[] bytes) { String result = "["; String separator = ""; for (byte b : bytes) { result += separator + String.format("%02X", b); separator = " "; } result += "]"; return result; } /** * Finds the platform dependent positions needed to patch instructions that load * an object from the object literals array immediately preceding the code array in memory. */ @HOSTED_ONLY static int[] findDataPatchPosns(MaxTargetMethod source, int dispFromCodeStart) { if (T1X.isAMD64()) { return AMD64T1XCompilation.findDataPatchPosns(source, dispFromCodeStart); } else { throw T1X.unimplISA(); } } @HOSTED_ONLY public Sig initSig(ClassMethodActor method) { CiRegisterConfig regConfig = vm().registerConfigs.standard; Map<Integer, Integer> slots = extractSlots(method); SignatureDescriptor sig = method.descriptor(); Kind[] kinds = method.getParameterKinds(); Arg[] in = new Arg[kinds.length]; CiCallingConvention cc = regConfig.getCallingConvention(Type.RuntimeCall, WordUtil.ciKinds(kinds, true), target(), false); int localVarIndex = 0; for (int i = 0; i < kinds.length; i++) { Kind kind = kinds[i]; Integer slotObj = slots.get(i); int slot = slotObj == null ? -1 : slotObj; assert cc.locations[i].isRegister() : "templates with non-reg args are not supported: " + method; CiRegister reg = cc.locations[i].asRegister(); in[i] = new Arg(kind, reg, localVarName(method, localVarIndex, kind), slot); localVarIndex += kind.stackSlots; } Kind outKind = sig.resultKind(); int outSlot = outKind == Kind.VOID ? -1 : 0; Slot slot = method.toJava().getAnnotation(Slot.class); if (slot != null) { outSlot = slot.value(); } Arg out = new Arg(outKind, regConfig.getReturnRegister(WordUtil.ciKind(outKind, true)), null, outSlot); Sig s = new Sig(in, out); return s; } @HOSTED_ONLY private static Map<Integer, Integer> extractSlots(ClassMethodActor template) { SignatureDescriptor sig = template.descriptor(); Annotation[][] annotations = template.toJava().getParameterAnnotations(); Map<Integer, Integer> slots = new HashMap<Integer, Integer>(annotations.length); for (int i = 0; i < annotations.length; i++) { Slot s = null; for (Annotation a : annotations[i]) { if (a.annotationType() == Slot.class) { s = (Slot) a; } } if (s != null) { assert !slots.containsValue(s.value()) : "operand stack index of " + sig.parameterDescriptorAt(i).toKind() + " parameter " + i + " of " + template + " conflicts with another parameter"; slots.put(i, s.value()); } } return slots; } @HOSTED_ONLY private static String localVarName(ClassMethodActor template, int localVarIndex, Kind kind) { CodeAttribute codeAttribute = template.codeAttribute(); LocalVariableTable lvt = codeAttribute.localVariableTable(); Entry e = lvt.findLocalVariable(localVarIndex, 0); assert e.descriptor(codeAttribute.cp).toKind() == kind; return e.name(codeAttribute.cp).string; } }