# HG changeset patch # User Christian Wimmer # Date 1342644591 25200 # Node ID 911315a3e642a5ba31df288c29619c7724869d70 # Parent 7b7881766ed1fefeb52a19b164d4bd925405fdb7 Factor out common infrastructure from NodeClass and LIRInstructionClass diff -r 7b7881766ed1 -r 911315a3e642 graal/com.oracle.graal.graph/src/com/oracle/graal/graph/FieldIntrospection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/FieldIntrospection.java Wed Jul 18 13:49:51 2012 -0700 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2012, 2012, 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.graal.graph; +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.*; + +import sun.misc.*; + +public abstract class FieldIntrospection { + + /** + * Interface used by {@link #rescanAllFieldOffsets(CalcOffset)} to determine the offset (in bytes) of a field. + */ + public interface CalcOffset { + long getOffset(Field field); + } + + public static class DefaultCalcOffset implements CalcOffset { + @Override + public long getOffset(Field field) { + return unsafe.objectFieldOffset(field); + } + } + + protected static final Unsafe unsafe = getUnsafe(); + + private static Unsafe getUnsafe() { + try { + // this will only fail if graal is not part of the boot class path + return Unsafe.getUnsafe(); + } catch (SecurityException e) { + // nothing to do + } + try { + Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafeInstance.setAccessible(true); + return (Unsafe) theUnsafeInstance.get(Unsafe.class); + } catch (Exception e) { + // currently we rely on being able to use Unsafe... + throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); + } + } + + protected static final ConcurrentHashMap, FieldIntrospection> allClasses = new ConcurrentHashMap<>(); + + protected final Class clazz; + protected long[] dataOffsets; + protected Map fieldNames; + protected Map> fieldTypes; + + public FieldIntrospection(Class clazz) { + this.clazz = clazz; + } + + public static void rescanAllFieldOffsets(CalcOffset calc) { + for (FieldIntrospection nodeClass : allClasses.values()) { + nodeClass.rescanFieldOffsets(calc); + } + } + + protected abstract void rescanFieldOffsets(CalcOffset calc); + + public abstract static class BaseFieldScanner { + private final CalcOffset calc; + + /** The offsets of fields that are not specially handled by subclasses. */ + public final ArrayList dataOffsets = new ArrayList<>(); + + public final Map fieldNames = new HashMap<>(); + public final Map> fieldTypes = new HashMap<>(); + + protected BaseFieldScanner(CalcOffset calc) { + this.calc = calc; + } + + protected void scan(Class clazz) { + Class currentClazz = clazz; + do { + for (Field field : currentClazz.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + continue; + } + Class type = field.getType(); + long offset = calc.getOffset(field); + + // scanField() may overwrite the name with a customized name. + fieldNames.put(offset, field.getName()); + fieldTypes.put(offset, type); + + scanField(field, type, offset); + } + currentClazz = currentClazz.getSuperclass(); + } while (currentClazz.getSuperclass() != Object.class); + } + + protected abstract void scanField(Field field, Class type, long offset); + } + + protected static void copyInto(long[] dest, long[] src) { + assert dest.length == src.length; + for (int i = 0; i < dest.length; i++) { + dest[i] = src[i]; + } + } + + protected static void copyInto(T[] dest, T[] src) { + assert dest.length == src.length; + for (int i = 0; i < dest.length; i++) { + dest[i] = src[i]; + } + } + + protected static void copyInto(T[] dest, List src) { + assert dest.length == src.size(); + for (int i = 0; i < dest.length; i++) { + dest[i] = src.get(i); + } + } + + protected static T[] arrayUsingSortedOffsets(Map map, long[] sortedOffsets, T[] result) { + for (int i = 0; i < sortedOffsets.length; i++) { + result[i] = map.get(sortedOffsets[i]); + } + return result; + } + + protected static long[] sortedLongCopy(ArrayList list1) { + Collections.sort(list1); + long[] result = new long[list1.size()]; + for (int i = 0; i < list1.size(); i++) { + result[i] = list1.get(i); + } + return result; + } + + protected static long[] sortedLongCopy(ArrayList list1, ArrayList list2) { + Collections.sort(list1); + Collections.sort(list2); + long[] result = new long[list1.size() + list2.size()]; + for (int i = 0; i < list1.size(); i++) { + result[i] = list1.get(i); + } + for (int i = 0; i < list2.size(); i++) { + result[list1.size() + i] = list2.get(i); + } + return result; + } +} diff -r 7b7881766ed1 -r 911315a3e642 graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java --- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java Wed Jul 18 10:50:57 2012 -0700 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java Wed Jul 18 13:49:51 2012 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -21,66 +21,46 @@ * questions. */ package com.oracle.graal.graph; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; +import java.lang.reflect.*; import java.util.*; -import java.util.Map.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Map.Entry; import com.oracle.graal.graph.Graph.DuplicationReplacement; -import com.oracle.graal.graph.Node.*; +import com.oracle.graal.graph.Node.Verbosity; + +public class NodeClass extends FieldIntrospection { + + public static final NodeClass get(Class c) { + NodeClass clazz = (NodeClass) allClasses.get(c); + if (clazz != null) { + return clazz; + } -import sun.misc.Unsafe; + // We can have a race of multiple threads creating the LIRInstructionClass at the same time. + // However, only one will be put into the map, and this is the one returned by all threads. + clazz = new NodeClass(c); + NodeClass oldClazz = (NodeClass) allClasses.putIfAbsent(c, clazz); + if (oldClazz != null) { + return oldClazz; + } else { + return clazz; + } + } -public class NodeClass { public static final int NOT_ITERABLE = -1; - /** - * Interface used by {@link NodeClass#rescanAllFieldOffsets(CalcOffset)} to determine the offset (in bytes) of a field. - */ - public interface CalcOffset { - long getOffset(Field field); - } - - private static final Class< ? > NODE_CLASS = Node.class; - private static final Class< ? > INPUT_LIST_CLASS = NodeInputList.class; - private static final Class< ? > SUCCESSOR_LIST_CLASS = NodeSuccessorList.class; - - private static final Unsafe unsafe = getUnsafe(); + private static final Class NODE_CLASS = Node.class; + private static final Class INPUT_LIST_CLASS = NodeInputList.class; + private static final Class SUCCESSOR_LIST_CLASS = NodeSuccessorList.class; - private static Unsafe getUnsafe() { - try { - // this will only fail if graal is not part of the boot class path - return Unsafe.getUnsafe(); - } catch (SecurityException e) { - // nothing to do - } - try { - Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeInstance.setAccessible(true); - return (Unsafe) theUnsafeInstance.get(Unsafe.class); - } catch (Exception e) { - // currently we rely on being able to use Unsafe... - throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); - } - } - - private static final Map, NodeClass> nodeClasses = new ConcurrentHashMap<>(); private static int nextIterableId = 0; - private final Class< ? > clazz; private final int directInputCount; private final long[] inputOffsets; - private final Class[] inputTypes; - private final String[] inputNames; private final int directSuccessorCount; private final long[] successorOffsets; - private final Class[] successorTypes; - private final String[] successorNames; - private final long[] dataOffsets; private final Class[] dataTypes; - private final String[] dataNames; private final boolean canGVN; private final int startGVNNumber; private final String shortName; @@ -88,16 +68,10 @@ private final int iterableId; private final boolean hasOutgoingEdges; - static class DefaultCalcOffset implements CalcOffset { - @Override - public long getOffset(Field field) { - return unsafe.objectFieldOffset(field); - } - } - public NodeClass(Class< ? > clazz) { + public NodeClass(Class clazz) { + super(clazz); assert NODE_CLASS.isAssignableFrom(clazz); - this.clazz = clazz; FieldScanner scanner = new FieldScanner(new DefaultCalcOffset()); scanner.scan(clazz); @@ -106,16 +80,15 @@ inputOffsets = sortedLongCopy(scanner.inputOffsets, scanner.inputListOffsets); directSuccessorCount = scanner.successorOffsets.size(); successorOffsets = sortedLongCopy(scanner.successorOffsets, scanner.successorListOffsets); - dataOffsets = new long[scanner.dataOffsets.size()]; - for (int i = 0; i < scanner.dataOffsets.size(); ++i) { - dataOffsets[i] = scanner.dataOffsets.get(i); + + dataOffsets = sortedLongCopy(scanner.dataOffsets); + dataTypes = new Class[dataOffsets.length]; + for (int i = 0; i < dataOffsets.length; i++) { + dataTypes[i] = scanner.fieldTypes.get(dataOffsets[i]); } - dataTypes = scanner.dataTypes.toArray(new Class[0]); - dataNames = scanner.dataNames.toArray(new String[0]); - inputTypes = arrayUsingSortedOffsets(scanner.inputTypesMap, inputOffsets, new Class[inputOffsets.length]); - inputNames = arrayUsingSortedOffsets(scanner.inputNamesMap, inputOffsets, new String[inputOffsets.length]); - successorTypes = arrayUsingSortedOffsets(scanner.successorTypesMap, successorOffsets, new Class[successorOffsets.length]); - successorNames = arrayUsingSortedOffsets(scanner.successorNamesMap, successorOffsets, new String[successorOffsets.length]); + + fieldNames = scanner.fieldNames; + fieldTypes = scanner.fieldTypes; canGVN = Node.ValueNumberable.class.isAssignableFrom(clazz); startGVNNumber = clazz.hashCode(); @@ -150,52 +123,26 @@ this.hasOutgoingEdges = this.inputOffsets.length > 0 || this.successorOffsets.length > 0; } - public static void rescanAllFieldOffsets(CalcOffset calc) { - for (NodeClass nodeClass : nodeClasses.values()) { - nodeClass.rescanFieldOffsets(calc); - } - } - - private void rescanFieldOffsets(CalcOffset calc) { + @Override + protected void rescanFieldOffsets(CalcOffset calc) { FieldScanner scanner = new FieldScanner(calc); scanner.scan(clazz); assert directInputCount == scanner.inputOffsets.size(); copyInto(inputOffsets, sortedLongCopy(scanner.inputOffsets, scanner.inputListOffsets)); assert directSuccessorCount == scanner.successorOffsets.size(); copyInto(successorOffsets, sortedLongCopy(scanner.successorOffsets, scanner.successorListOffsets)); - assert dataOffsets.length == scanner.dataOffsets.size(); - for (int i = 0; i < scanner.dataOffsets.size(); ++i) { - dataOffsets[i] = scanner.dataOffsets.get(i); + copyInto(dataOffsets, sortedLongCopy(scanner.dataOffsets)); + + for (int i = 0; i < dataOffsets.length; i++) { + dataTypes[i] = scanner.fieldTypes.get(dataOffsets[i]); } - copyInto(dataTypes, scanner.dataTypes); - copyInto(dataNames, scanner.dataNames); - copyInto(inputTypes, arrayUsingSortedOffsets(scanner.inputTypesMap, this.inputOffsets, new Class[this.inputOffsets.length])); - copyInto(inputNames, arrayUsingSortedOffsets(scanner.inputNamesMap, this.inputOffsets, new String[this.inputOffsets.length])); - copyInto(successorTypes, arrayUsingSortedOffsets(scanner.successorTypesMap, this.successorOffsets, new Class[this.successorOffsets.length])); - copyInto(successorNames, arrayUsingSortedOffsets(scanner.successorNamesMap, this.successorOffsets, new String[this.successorNames.length])); + fieldNames.clear(); + fieldNames.putAll(scanner.fieldNames); + fieldTypes.clear(); + fieldTypes.putAll(scanner.fieldTypes); } - private static void copyInto(long[] dest, long[] src) { - assert dest.length == src.length; - for (int i = 0; i < dest.length; i++) { - dest[i] = src[i]; - } - } - - private static void copyInto(T[] dest, T[] src) { - assert dest.length == src.length; - for (int i = 0; i < dest.length; i++) { - dest[i] = src[i]; - } - } - - private static void copyInto(T[] dest, List src) { - assert dest.length == src.size(); - for (int i = 0; i < dest.length; i++) { - dest[i] = src.get(i); - } - } public boolean hasOutgoingEdges() { return hasOutgoingEdges; @@ -213,107 +160,49 @@ return canGVN; } - private static synchronized NodeClass getSynchronized(Class< ? > c) { - NodeClass clazz = nodeClasses.get(c); - if (clazz == null) { - clazz = new NodeClass(c); - nodeClasses.put(c, clazz); - } - return clazz; - } - - public static final NodeClass get(Class< ? > c) { - NodeClass clazz = nodeClasses.get(c); - return clazz == null ? getSynchronized(c) : clazz; - } - public static int cacheSize() { return nextIterableId; } - private static class FieldScanner { + protected static class FieldScanner extends BaseFieldScanner { public final ArrayList inputOffsets = new ArrayList<>(); public final ArrayList inputListOffsets = new ArrayList<>(); - public final Map> inputTypesMap = new HashMap<>(); - public final Map inputNamesMap = new HashMap<>(); public final ArrayList successorOffsets = new ArrayList<>(); public final ArrayList successorListOffsets = new ArrayList<>(); - public final Map> successorTypesMap = new HashMap<>(); - public final Map successorNamesMap = new HashMap<>(); - public final ArrayList dataOffsets = new ArrayList<>(); - public final ArrayList> dataTypes = new ArrayList<>(); - public final ArrayList dataNames = new ArrayList<>(); - public final CalcOffset calc; - public FieldScanner(CalcOffset calc) { - this.calc = calc; + protected FieldScanner(CalcOffset calc) { + super(calc); } - public void scan(Class< ? > clazz) { - Class< ? > currentClazz = clazz; - do { - for (Field field : currentClazz.getDeclaredFields()) { - if (!Modifier.isStatic(field.getModifiers())) { - Class< ? > type = field.getType(); - long offset = calc.getOffset(field); - String name = field.getName(); - if (field.isAnnotationPresent(Node.Input.class)) { - assert !field.isAnnotationPresent(Node.Successor.class) : "field cannot be both input and successor"; - if (INPUT_LIST_CLASS.isAssignableFrom(type)) { - inputListOffsets.add(offset); - } else { - assert NODE_CLASS.isAssignableFrom(type) : "invalid input type: " + type; - inputOffsets.add(offset); - inputTypesMap.put(offset, type); - } - if (field.getAnnotation(Node.Input.class).notDataflow()) { - inputNamesMap.put(offset, name + "#NDF"); - } else { - inputNamesMap.put(offset, name); - } - } else if (field.isAnnotationPresent(Node.Successor.class)) { - if (SUCCESSOR_LIST_CLASS.isAssignableFrom(type)) { - successorListOffsets.add(offset); - } else { - assert NODE_CLASS.isAssignableFrom(type) : "invalid successor type: " + type; - successorOffsets.add(offset); - successorTypesMap.put(offset, type); - } - successorNamesMap.put(offset, name); - } else { - assert !NODE_CLASS.isAssignableFrom(type) || name.equals("Null") : "suspicious node field: " + field; - assert !INPUT_LIST_CLASS.isAssignableFrom(type) : "suspicious node input list field: " + field; - assert !SUCCESSOR_LIST_CLASS.isAssignableFrom(type) : "suspicious node successor list field: " + field; - dataOffsets.add(offset); - dataTypes.add(type); - dataNames.add(name); - } - } + @Override + protected void scanField(Field field, Class type, long offset) { + if (field.isAnnotationPresent(Node.Input.class)) { + assert !field.isAnnotationPresent(Node.Successor.class) : "field cannot be both input and successor"; + if (INPUT_LIST_CLASS.isAssignableFrom(type)) { + inputListOffsets.add(offset); + } else { + assert NODE_CLASS.isAssignableFrom(type) : "invalid input type: " + type; + inputOffsets.add(offset); + } + if (field.getAnnotation(Node.Input.class).notDataflow()) { + fieldNames.put(offset, field.getName() + "#NDF"); } - currentClazz = currentClazz.getSuperclass(); - } while (currentClazz != Node.class); + } else if (field.isAnnotationPresent(Node.Successor.class)) { + if (SUCCESSOR_LIST_CLASS.isAssignableFrom(type)) { + successorListOffsets.add(offset); + } else { + assert NODE_CLASS.isAssignableFrom(type) : "invalid successor type: " + type; + successorOffsets.add(offset); + } + } else { + assert !NODE_CLASS.isAssignableFrom(type) || field.getName().equals("Null") : "suspicious node field: " + field; + assert !INPUT_LIST_CLASS.isAssignableFrom(type) : "suspicious node input list field: " + field; + assert !SUCCESSOR_LIST_CLASS.isAssignableFrom(type) : "suspicious node successor list field: " + field; + dataOffsets.add(offset); + } } } - private static T[] arrayUsingSortedOffsets(Map map, long[] sortedOffsets, T[] result) { - for (int i = 0; i < sortedOffsets.length; i++) { - result[i] = map.get(sortedOffsets[i]); - } - return result; - } - - private static long[] sortedLongCopy(ArrayList list1, ArrayList list2) { - Collections.sort(list1); - Collections.sort(list2); - long[] result = new long[list1.size() + list2.size()]; - for (int i = 0; i < list1.size(); i++) { - result[i] = list1.get(i); - } - for (int i = 0; i < list2.size(); i++) { - result[list1.size() + i] = list2.get(i); - } - return result; - } @Override public String toString() { @@ -561,7 +450,7 @@ */ public void getDebugProperties(Node node, Map properties) { for (int i = 0; i < dataOffsets.length; ++i) { - Class type = dataTypes[i]; + Class type = fieldTypes.get(dataOffsets[i]); Object value = null; if (type.isPrimitive()) { if (type == Integer.TYPE) { @@ -580,7 +469,7 @@ } else { value = unsafe.getObject(node, dataOffsets[i]); } - properties.put(dataNames[i], value); + properties.put(fieldNames.get(dataOffsets[i]), value); } } @@ -654,14 +543,14 @@ } public String getName(Position pos) { - return pos.input ? inputNames[pos.index] : successorNames[pos.index]; + return fieldNames.get(pos.input ? inputOffsets[pos.index] : successorOffsets[pos.index]); } private void set(Node node, Position pos, Node x) { long offset = pos.input ? inputOffsets[pos.index] : successorOffsets[pos.index]; if (pos.subIndex == NOT_ITERABLE) { Node old = getNode(node, offset); - assert x == null || (pos.input ? inputTypes : successorTypes)[pos.index].isAssignableFrom(x.getClass()) : this + ".set(node, pos, " + x + ") while type is " + (pos.input ? inputTypes : successorTypes)[pos.index]; + assert x == null || fieldTypes.get((pos.input ? inputOffsets : successorOffsets)[pos.index]).isAssignableFrom(x.getClass()) : this + ".set(node, pos, " + x + ")"; putNode(node, offset, x); if (pos.input) { node.updateUsages(old, x); @@ -717,7 +606,7 @@ while (index < directInputCount) { Node input = getNode(node, inputOffsets[index]); if (input == old) { - assert other == null || inputTypes[index].isAssignableFrom(other.getClass()); // : "Can not assign " + other.getClass() + " to " + inputTypes[index] + " in " + node; + assert other == null || fieldTypes.get(inputOffsets[index]).isAssignableFrom(other.getClass()); // : "Can not assign " + other.getClass() + " to " + inputTypes[index] + " in " + node; putNode(node, inputOffsets[index], other); return true; } @@ -739,7 +628,7 @@ while (index < directSuccessorCount) { Node successor = getNode(node, successorOffsets[index]); if (successor == old) { - assert other == null || successorTypes[index].isAssignableFrom(other.getClass()); // : successorTypes[index] + " is not compatible with " + other.getClass(); + assert other == null || fieldTypes.get(successorOffsets[index]).isAssignableFrom(other.getClass()); // : successorTypes[index] + " is not compatible with " + other.getClass(); putNode(node, successorOffsets[index], other); return true; } @@ -970,7 +859,7 @@ Node replacement = replacements.replacement(input); if (replacement != input) { replacementsMap.put(input, replacement); - assert replacement == null || node.getNodeClass().inputTypes[pos.index] == null || node.getNodeClass().inputTypes[pos.index].isAssignableFrom(replacement.getClass()); + assert isAssignable(node.getNodeClass().fieldTypes.get(node.getNodeClass().inputOffsets[pos.index]), replacement); target = replacement; } else if (input.graph() == graph) { // patch to the outer world target = input; @@ -998,7 +887,7 @@ Node replacement = replacements.replacement(succ); if (replacement != succ) { replacementsMap.put(succ, replacement); - assert replacement == null || node.getNodeClass().successorTypes[pos.index] == null || node.getNodeClass().successorTypes[pos.index].isAssignableFrom(replacement.getClass()); + assert isAssignable(node.getNodeClass().fieldTypes.get(node.getNodeClass().successorOffsets[pos.index]), replacement); target = replacement; } } @@ -1008,4 +897,8 @@ } return newNodes; } + + private static boolean isAssignable(Class fieldType, Node replacement) { + return replacement == null || !NODE_CLASS.isAssignableFrom(fieldType) || fieldType.isAssignableFrom(replacement.getClass()); + } } diff -r 7b7881766ed1 -r 911315a3e642 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRInstructionClass.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRInstructionClass.java Wed Jul 18 10:50:57 2012 -0700 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRInstructionClass.java Wed Jul 18 13:49:51 2012 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2012, 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 @@ -27,9 +27,6 @@ import java.lang.reflect.*; import java.util.*; import java.util.Map.Entry; -import java.util.concurrent.*; - -import sun.misc.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; @@ -39,51 +36,31 @@ import com.oracle.graal.lir.LIRInstruction.StateProcedure; import com.oracle.graal.lir.LIRInstruction.ValueProcedure; -public class LIRInstructionClass { - // TODO(cwimmer) factor out the common methods of this class and NodeClass into a base class. +public class LIRInstructionClass extends FieldIntrospection { - /** - * Interface used by {@link LIRInstructionClass#rescanAllFieldOffsets(CalcOffset)} to determine the offset (in bytes) of a field. - */ - public interface CalcOffset { - long getOffset(Field field); - } - - private static final Unsafe unsafe = getUnsafe(); + public static final LIRInstructionClass get(Class c) { + LIRInstructionClass clazz = (LIRInstructionClass) allClasses.get(c); + if (clazz != null) { + return clazz; + } - private static Unsafe getUnsafe() { - try { - // this will only fail if graal is not part of the boot class path - return Unsafe.getUnsafe(); - } catch (SecurityException e) { - // nothing to do - } - try { - Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafeInstance.setAccessible(true); - return (Unsafe) theUnsafeInstance.get(Unsafe.class); - } catch (Exception e) { - // currently we rely on being able to use Unsafe... - throw new RuntimeException("exception while trying to get Unsafe.theUnsafe via reflection:", e); + // We can have a race of multiple threads creating the LIRInstructionClass at the same time. + // However, only one will be put into the map, and this is the one returned by all threads. + clazz = new LIRInstructionClass(c); + LIRInstructionClass oldClazz = (LIRInstructionClass) allClasses.putIfAbsent(c, clazz); + if (oldClazz != null) { + return oldClazz; + } else { + return clazz; } } - static class DefaultCalcOffset implements CalcOffset { - @Override - public long getOffset(Field field) { - return unsafe.objectFieldOffset(field); - } - } private static final Class INSTRUCTION_CLASS = LIRInstruction.class; private static final Class VALUE_CLASS = Value.class; private static final Class VALUE_ARRAY_CLASS = Value[].class; private static final Class STATE_CLASS = LIRFrameState.class; - - private static final ConcurrentHashMap, LIRInstructionClass> classes = new ConcurrentHashMap<>(); - - private final Class< ? > clazz; private final int directUseCount; private final long[] useOffsets; private final EnumSet[] useFlags; @@ -98,18 +75,14 @@ private final EnumSet[] defFlags; private final long[] stateOffsets; - private final long[] dataOffsets; - - private final Map fieldNames; - private final Map> fieldTypes; private String opcodeConstant; private long opcodeOffset; @SuppressWarnings("unchecked") public LIRInstructionClass(Class clazz) { + super(clazz); assert INSTRUCTION_CLASS.isAssignableFrom(clazz); - this.clazz = clazz; FieldScanner scanner = new FieldScanner(new DefaultCalcOffset()); scanner.scan(clazz); @@ -144,13 +117,8 @@ opcodeOffset = scanner.opcodeOffset; } - public static void rescanAllFieldOffsets(CalcOffset calc) { - for (LIRInstructionClass nodeClass : classes.values()) { - nodeClass.rescanFieldOffsets(calc); - } - } - - private void rescanFieldOffsets(CalcOffset calc) { + @Override + protected void rescanFieldOffsets(CalcOffset calc) { FieldScanner scanner = new FieldScanner(calc); scanner.scan(clazz); @@ -175,30 +143,6 @@ opcodeOffset = scanner.opcodeOffset; } - private static void copyInto(long[] dest, long[] src) { - assert dest.length == src.length; - for (int i = 0; i < dest.length; i++) { - dest[i] = src[i]; - } - } - - public static final LIRInstructionClass get(Class c) { - LIRInstructionClass clazz = classes.get(c); - if (clazz != null) { - return clazz; - } - - // We can have a race of multiple threads creating the LIRInstructionClass at the same time. - // However, only one will be put into the map, and this is the one returned by all threads. - clazz = new LIRInstructionClass(c); - LIRInstructionClass oldClazz = classes.putIfAbsent(c, clazz); - if (oldClazz != null) { - return oldClazz; - } else { - return clazz; - } - } - private static class OperandModeAnnotation { public final ArrayList scalarOffsets = new ArrayList<>(); @@ -206,21 +150,15 @@ public final Map> flags = new HashMap<>(); } - private static class FieldScanner { - public final CalcOffset calc; - + protected static class FieldScanner extends BaseFieldScanner { public final Map, OperandModeAnnotation> valueAnnotations; public final ArrayList stateOffsets = new ArrayList<>(); - public final ArrayList dataOffsets = new ArrayList<>(); - - public final Map fieldNames = new HashMap<>(); - public final Map> fieldTypes = new HashMap<>(); private String opcodeConstant; private long opcodeOffset; public FieldScanner(CalcOffset calc) { - this.calc = calc; + super(calc); valueAnnotations = new HashMap<>(); valueAnnotations.put(LIRInstruction.Use.class, new OperandModeAnnotation()); //LIRInstruction.Use.class)); @@ -259,52 +197,14 @@ return result; } - public void scan(Class clazz) { + @Override + protected void scan(Class clazz) { if (clazz.getAnnotation(LIRInstruction.Opcode.class) != null) { opcodeConstant = clazz.getAnnotation(LIRInstruction.Opcode.class).value(); } opcodeOffset = -1; - Class currentClazz = clazz; - do { - for (Field field : currentClazz.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) { - continue; - } - - Class< ? > type = field.getType(); - long offset = calc.getOffset(field); - - if (VALUE_CLASS.isAssignableFrom(type)) { - assert Modifier.isProtected(field.getModifiers()) && !Modifier.isFinal(field.getModifiers()) : "Value field must not be declared final or [package] private because it is modified by register allocator: " + field; - OperandModeAnnotation annotation = getOperandModeAnnotation(field); - assert annotation != null : "Field must have operand mode annotation: " + field; - annotation.scalarOffsets.add(offset); - annotation.flags.put(offset, getFlags(field)); - } else if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) { - OperandModeAnnotation annotation = getOperandModeAnnotation(field); - assert annotation != null : "Field must have operand mode annotation: " + field; - annotation.arrayOffsets.add(offset); - annotation.flags.put(offset, getFlags(field)); - } else if (STATE_CLASS.isAssignableFrom(type)) { - assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field; - assert field.getAnnotation(LIRInstruction.State.class) != null : "Field must have state annotation: " + field; - stateOffsets.add(offset); - } else { - assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field; - assert field.getAnnotation(LIRInstruction.State.class) == null : "Field must not have state annotation: " + field; - dataOffsets.add(offset); - } - fieldNames.put(offset, field.getName()); - fieldTypes.put(offset, type); - - if (field.getAnnotation(LIRInstruction.Opcode.class) != null) { - assert opcodeConstant == null && opcodeOffset == -1 : "Can have only one Opcode definition: " + clazz; - opcodeOffset = offset; - } - } - currentClazz = currentClazz.getSuperclass(); - } while (currentClazz != LIRInstruction.class); + super.scan(clazz); if (opcodeConstant == null && opcodeOffset == -1) { opcodeConstant = clazz.getSimpleName(); @@ -313,38 +213,37 @@ } } } - } - private static T[] arrayUsingSortedOffsets(Map map, long[] sortedOffsets, T[] result) { - for (int i = 0; i < sortedOffsets.length; i++) { - result[i] = map.get(sortedOffsets[i]); - } - return result; - } + @Override + protected void scanField(Field field, Class type, long offset) { + if (VALUE_CLASS.isAssignableFrom(type)) { + assert Modifier.isProtected(field.getModifiers()) && !Modifier.isFinal(field.getModifiers()) : "Value field must not be declared final or [package] private because it is modified by register allocator: " + field; + OperandModeAnnotation annotation = getOperandModeAnnotation(field); + assert annotation != null : "Field must have operand mode annotation: " + field; + annotation.scalarOffsets.add(offset); + annotation.flags.put(offset, getFlags(field)); + } else if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) { + OperandModeAnnotation annotation = getOperandModeAnnotation(field); + assert annotation != null : "Field must have operand mode annotation: " + field; + annotation.arrayOffsets.add(offset); + annotation.flags.put(offset, getFlags(field)); + } else if (STATE_CLASS.isAssignableFrom(type)) { + assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field; + assert field.getAnnotation(LIRInstruction.State.class) != null : "Field must have state annotation: " + field; + stateOffsets.add(offset); + } else { + assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field; + assert field.getAnnotation(LIRInstruction.State.class) == null : "Field must not have state annotation: " + field; + dataOffsets.add(offset); + } - private static long[] sortedLongCopy(ArrayList list1) { - Collections.sort(list1); - long[] result = new long[list1.size()]; - for (int i = 0; i < list1.size(); i++) { - result[i] = list1.get(i); + if (field.getAnnotation(LIRInstruction.Opcode.class) != null) { + assert opcodeConstant == null && opcodeOffset == -1 : "Can have only one Opcode definition: " + field.getType(); + opcodeOffset = offset; + } } - return result; } - private static long[] sortedLongCopy(ArrayList list1, ArrayList list2) { - Collections.sort(list1); - Collections.sort(list2); - long[] result = new long[list1.size() + list2.size()]; - for (int i = 0; i < list1.size(); i++) { - result[i] = list1.get(i); - } - for (int i = 0; i < list2.size(); i++) { - result[list1.size() + i] = list2.get(i); - } - return result; - } - - @Override public String toString() { StringBuilder str = new StringBuilder();