--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java 2016-12-09 00:54:00.638273032 -0800 @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.lir; + +import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST; +import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; +import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.graalvm.compiler.core.common.FieldIntrospection; +import org.graalvm.compiler.core.common.Fields; +import org.graalvm.compiler.core.common.FieldsScanner; +import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; +import org.graalvm.compiler.lir.LIRInstruction.OperandMode; + +import jdk.vm.ci.code.RegisterValue; +import jdk.vm.ci.code.StackSlot; +import jdk.vm.ci.meta.Value; + +abstract class LIRIntrospection extends FieldIntrospection { + + private static final Class VALUE_CLASS = Value.class; + private static final Class CONSTANT_VALUE_CLASS = ConstantValue.class; + private static final Class VARIABLE_CLASS = Variable.class; + private static final Class REGISTER_VALUE_CLASS = RegisterValue.class; + private static final Class STACK_SLOT_CLASS = StackSlot.class; + private static final Class VALUE_ARRAY_CLASS = Value[].class; + + LIRIntrospection(Class clazz) { + super(clazz); + } + + protected static class Values extends Fields { + private final int directCount; + private final EnumSet[] flags; + + public Values(OperandModeAnnotation mode) { + this(mode.directCount, mode.values); + } + + @SuppressWarnings({"unchecked"}) + public Values(int directCount, ArrayList fields) { + super(fields); + this.directCount = directCount; + flags = (EnumSet[]) new EnumSet[fields.size()]; + for (int i = 0; i < fields.size(); i++) { + flags[i] = fields.get(i).flags; + } + } + + public int getDirectCount() { + return directCount; + } + + public EnumSet getFlags(int i) { + return flags[i]; + } + + protected Value getValue(Object obj, int index) { + return (Value) getObject(obj, index); + } + + protected void setValue(Object obj, int index, Value value) { + putObject(obj, index, value); + } + + protected Value[] getValueArray(Object obj, int index) { + return (Value[]) getObject(obj, index); + } + + protected void setValueArray(Object obj, int index, Value[] valueArray) { + putObject(obj, index, valueArray); + } + } + + /** + * The component values in an {@link LIRInstruction} or {@link CompositeValue}. + */ + protected Values values; + + protected static class ValueFieldInfo extends FieldsScanner.FieldInfo { + + final EnumSet flags; + + public ValueFieldInfo(long offset, String name, Class type, Class declaringClass, EnumSet flags) { + super(offset, name, type, declaringClass); + assert VALUE_ARRAY_CLASS.isAssignableFrom(type) || VALUE_CLASS.isAssignableFrom(type); + this.flags = flags; + } + + /** + * Sorts non-array fields before array fields. + */ + @Override + public int compareTo(FieldsScanner.FieldInfo o) { + if (VALUE_ARRAY_CLASS.isAssignableFrom(o.type)) { + if (!VALUE_ARRAY_CLASS.isAssignableFrom(type)) { + return -1; + } + } else { + if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) { + return 1; + } + } + return super.compareTo(o); + } + + @Override + public String toString() { + return super.toString() + flags; + } + } + + protected static class OperandModeAnnotation { + + /** + * Number of non-array fields in {@link #values}. + */ + public int directCount; + public final ArrayList values = new ArrayList<>(); + } + + protected abstract static class LIRFieldsScanner extends FieldsScanner { + + public final Map, OperandModeAnnotation> valueAnnotations; + public final ArrayList states = new ArrayList<>(); + + public LIRFieldsScanner(FieldsScanner.CalcOffset calc) { + super(calc); + valueAnnotations = new HashMap<>(); + } + + protected OperandModeAnnotation getOperandModeAnnotation(Field field) { + OperandModeAnnotation result = null; + for (Entry, OperandModeAnnotation> entry : valueAnnotations.entrySet()) { + Annotation annotation = field.getAnnotation(entry.getKey()); + if (annotation != null) { + assert result == null : "Field has two operand mode annotations: " + field; + result = entry.getValue(); + } + } + return result; + } + + protected abstract EnumSet getFlags(Field field); + + @Override + protected void scanField(Field field, long offset) { + Class type = field.getType(); + if (VALUE_CLASS.isAssignableFrom(type) && !CONSTANT_VALUE_CLASS.isAssignableFrom(type)) { + assert !Modifier.isFinal(field.getModifiers()) : "Value field must not be declared final because it is modified by register allocator: " + field; + OperandModeAnnotation annotation = getOperandModeAnnotation(field); + assert annotation != null : "Field must have operand mode annotation: " + field; + EnumSet flags = getFlags(field); + assert verifyFlags(field, type, flags); + annotation.values.add(new ValueFieldInfo(offset, field.getName(), type, field.getDeclaringClass(), flags)); + annotation.directCount++; + } else if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) { + OperandModeAnnotation annotation = getOperandModeAnnotation(field); + assert annotation != null : "Field must have operand mode annotation: " + field; + EnumSet flags = getFlags(field); + assert verifyFlags(field, type.getComponentType(), flags); + annotation.values.add(new ValueFieldInfo(offset, field.getName(), type, field.getDeclaringClass(), flags)); + } 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; + super.scanField(field, offset); + } + } + + private static boolean verifyFlags(Field field, Class type, EnumSet flags) { + if (flags.contains(REG)) { + assert type.isAssignableFrom(REGISTER_VALUE_CLASS) || type.isAssignableFrom(VARIABLE_CLASS) : "Cannot assign RegisterValue / Variable to field with REG flag:" + field; + } + if (flags.contains(STACK)) { + assert type.isAssignableFrom(STACK_SLOT_CLASS) : "Cannot assign StackSlot to field with STACK flag:" + field; + } + if (flags.contains(CONST)) { + assert type.isAssignableFrom(CONSTANT_VALUE_CLASS) : "Cannot assign Constant to field with CONST flag:" + field; + } + return true; + } + } + + protected static void forEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueProcedure proc) { + for (int i = 0; i < values.getCount(); i++) { + assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i)); + + if (i < values.getDirectCount()) { + Value value = values.getValue(inst, i); + Value newValue; + if (value instanceof CompositeValue) { + CompositeValue composite = (CompositeValue) value; + newValue = composite.forEachComponent(inst, mode, proc); + } else { + newValue = proc.doValue(inst, value, mode, values.getFlags(i)); + } + if (!value.identityEquals(newValue)) { + values.setValue(inst, i, newValue); + } + } else { + Value[] valueArray = values.getValueArray(inst, i); + for (int j = 0; j < valueArray.length; j++) { + Value value = valueArray[j]; + Value newValue; + if (value instanceof CompositeValue) { + CompositeValue composite = (CompositeValue) value; + newValue = composite.forEachComponent(inst, mode, proc); + } else { + newValue = proc.doValue(inst, value, mode, values.getFlags(i)); + } + if (!value.identityEquals(newValue)) { + valueArray[j] = newValue; + } + } + } + } + } + + protected static void visitEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueConsumer proc) { + for (int i = 0; i < values.getCount(); i++) { + assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i)); + + if (i < values.getDirectCount()) { + Value value = values.getValue(inst, i); + if (value instanceof CompositeValue) { + CompositeValue composite = (CompositeValue) value; + composite.visitEachComponent(inst, mode, proc); + } else { + proc.visitValue(inst, value, mode, values.getFlags(i)); + } + } else { + Value[] valueArray = values.getValueArray(inst, i); + for (int j = 0; j < valueArray.length; j++) { + Value value = valueArray[j]; + if (value instanceof CompositeValue) { + CompositeValue composite = (CompositeValue) value; + composite.visitEachComponent(inst, mode, proc); + } else { + proc.visitValue(inst, value, mode, values.getFlags(i)); + } + } + } + } + } + + protected static void appendValues(StringBuilder sb, Object obj, String start, String end, String startMultiple, String endMultiple, String[] prefix, Fields... fieldsList) { + int total = 0; + for (Fields fields : fieldsList) { + total += fields.getCount(); + } + if (total == 0) { + return; + } + + sb.append(start); + if (total > 1) { + sb.append(startMultiple); + } + String sep = ""; + int i = 0; + for (Fields fields : fieldsList) { + for (int j = 0; j < fields.getCount(); j++) { + sb.append(sep).append(prefix[i]); + if (total > 1) { + sb.append(fields.getName(j)).append(": "); + } + sb.append(getFieldString(obj, j, fields)); + sep = ", "; + } + i++; + } + if (total > 1) { + sb.append(endMultiple); + } + sb.append(end); + } + + protected static String getFieldString(Object obj, int index, Fields fields) { + Object value = fields.get(obj, index); + Class type = fields.getType(index); + if (value == null || type.isPrimitive() || !type.isArray()) { + return String.valueOf(value); + } + if (type == int[].class) { + return Arrays.toString((int[]) value); + } else if (type == double[].class) { + return Arrays.toString((double[]) value); + } else if (type == byte[].class) { + byte[] byteValue = (byte[]) value; + if (isPrintableAsciiString(byteValue)) { + return toString(byteValue); + } else { + return Arrays.toString(byteValue); + } + } else if (!type.getComponentType().isPrimitive()) { + return Arrays.toString((Object[]) value); + } + assert false : "unhandled field type: " + type; + return ""; + } + + /** + * Tests if all values in this string are printable ASCII characters or value \0 (b in + * [0x20,0x7F]) or b == 0. + * + * @param array + * @return true if there are only printable ASCII characters and \0, false otherwise + */ + private static boolean isPrintableAsciiString(byte[] array) { + for (byte b : array) { + char c = (char) b; + if (c != 0 && c < 0x20 && c > 0x7F) { + return false; + } + } + return true; + } + + private static String toString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + sb.append('"'); + for (byte b : bytes) { + if (b == 0) { + sb.append("\\0"); + } else if (b == '"') { + sb.append("\\\""); + } else if (b == '\n') { + sb.append("\\n"); + } else { + sb.append((char) b); + } + } + sb.append('"'); + return sb.toString(); + } +}