1 /*
   2  * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.lir;
  24 
  25 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST;
  26 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
  27 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK;
  28 
  29 import java.lang.annotation.Annotation;
  30 import java.lang.reflect.Field;
  31 import java.lang.reflect.Modifier;
  32 import java.util.ArrayList;
  33 import java.util.Arrays;
  34 import java.util.EnumSet;
  35 
  36 import org.graalvm.compiler.core.common.FieldIntrospection;
  37 import org.graalvm.compiler.core.common.Fields;
  38 import org.graalvm.compiler.core.common.FieldsScanner;
  39 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
  40 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
  41 import org.graalvm.util.Equivalence;
  42 import org.graalvm.util.EconomicMap;
  43 import org.graalvm.util.MapCursor;
  44 
  45 import jdk.vm.ci.code.RegisterValue;
  46 import jdk.vm.ci.code.StackSlot;
  47 import jdk.vm.ci.meta.Value;
  48 
  49 abstract class LIRIntrospection<T> extends FieldIntrospection<T> {
  50 
  51     private static final Class<Value> VALUE_CLASS = Value.class;
  52     private static final Class<ConstantValue> CONSTANT_VALUE_CLASS = ConstantValue.class;
  53     private static final Class<Variable> VARIABLE_CLASS = Variable.class;
  54     private static final Class<RegisterValue> REGISTER_VALUE_CLASS = RegisterValue.class;
  55     private static final Class<StackSlot> STACK_SLOT_CLASS = StackSlot.class;
  56     private static final Class<Value[]> VALUE_ARRAY_CLASS = Value[].class;
  57 
  58     LIRIntrospection(Class<T> clazz) {
  59         super(clazz);
  60     }
  61 
  62     protected static class Values extends Fields {
  63         private final int directCount;
  64         private final EnumSet<OperandFlag>[] flags;
  65 
  66         public Values(OperandModeAnnotation mode) {
  67             this(mode.directCount, mode.values);
  68         }
  69 
  70         @SuppressWarnings({"unchecked"})
  71         public Values(int directCount, ArrayList<ValueFieldInfo> fields) {
  72             super(fields);
  73             this.directCount = directCount;
  74             flags = (EnumSet<OperandFlag>[]) new EnumSet<?>[fields.size()];
  75             for (int i = 0; i < fields.size(); i++) {
  76                 flags[i] = fields.get(i).flags;
  77             }
  78         }
  79 
  80         public int getDirectCount() {
  81             return directCount;
  82         }
  83 
  84         public EnumSet<OperandFlag> getFlags(int i) {
  85             return flags[i];
  86         }
  87 
  88         protected Value getValue(Object obj, int index) {
  89             return (Value) getObject(obj, index);
  90         }
  91 
  92         protected void setValue(Object obj, int index, Value value) {
  93             putObject(obj, index, value);
  94         }
  95 
  96         protected Value[] getValueArray(Object obj, int index) {
  97             return (Value[]) getObject(obj, index);
  98         }
  99 
 100         protected void setValueArray(Object obj, int index, Value[] valueArray) {
 101             putObject(obj, index, valueArray);
 102         }
 103     }
 104 
 105     /**
 106      * The component values in an {@link LIRInstruction} or {@link CompositeValue}.
 107      */
 108     protected Values values;
 109 
 110     protected static class ValueFieldInfo extends FieldsScanner.FieldInfo {
 111 
 112         final EnumSet<OperandFlag> flags;
 113 
 114         public ValueFieldInfo(long offset, String name, Class<?> type, Class<?> declaringClass, EnumSet<OperandFlag> flags) {
 115             super(offset, name, type, declaringClass);
 116             assert VALUE_ARRAY_CLASS.isAssignableFrom(type) || VALUE_CLASS.isAssignableFrom(type);
 117             this.flags = flags;
 118         }
 119 
 120         /**
 121          * Sorts non-array fields before array fields.
 122          */
 123         @Override
 124         public int compareTo(FieldsScanner.FieldInfo o) {
 125             if (VALUE_ARRAY_CLASS.isAssignableFrom(o.type)) {
 126                 if (!VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
 127                     return -1;
 128                 }
 129             } else {
 130                 if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
 131                     return 1;
 132                 }
 133             }
 134             return super.compareTo(o);
 135         }
 136 
 137         @Override
 138         public String toString() {
 139             return super.toString() + flags;
 140         }
 141     }
 142 
 143     protected static class OperandModeAnnotation {
 144 
 145         /**
 146          * Number of non-array fields in {@link #values}.
 147          */
 148         public int directCount;
 149         public final ArrayList<ValueFieldInfo> values = new ArrayList<>();
 150     }
 151 
 152     protected abstract static class LIRFieldsScanner extends FieldsScanner {
 153 
 154         public final EconomicMap<Class<? extends Annotation>, OperandModeAnnotation> valueAnnotations;
 155         public final ArrayList<FieldsScanner.FieldInfo> states = new ArrayList<>();
 156 
 157         public LIRFieldsScanner(FieldsScanner.CalcOffset calc) {
 158             super(calc);
 159             valueAnnotations = EconomicMap.create(Equivalence.DEFAULT);
 160         }
 161 
 162         protected OperandModeAnnotation getOperandModeAnnotation(Field field) {
 163             OperandModeAnnotation result = null;
 164             MapCursor<Class<? extends Annotation>, OperandModeAnnotation> cursor = valueAnnotations.getEntries();
 165             while (cursor.advance()) {
 166                 Annotation annotation = field.getAnnotation(cursor.getKey());
 167                 if (annotation != null) {
 168                     assert result == null : "Field has two operand mode annotations: " + field;
 169                     result = cursor.getValue();
 170                 }
 171             }
 172             return result;
 173         }
 174 
 175         protected abstract EnumSet<OperandFlag> getFlags(Field field);
 176 
 177         @Override
 178         protected void scanField(Field field, long offset) {
 179             Class<?> type = field.getType();
 180             if (VALUE_CLASS.isAssignableFrom(type) && !CONSTANT_VALUE_CLASS.isAssignableFrom(type)) {
 181                 assert !Modifier.isFinal(field.getModifiers()) : "Value field must not be declared final because it is modified by register allocator: " + field;
 182                 OperandModeAnnotation annotation = getOperandModeAnnotation(field);
 183                 assert annotation != null : "Field must have operand mode annotation: " + field;
 184                 EnumSet<OperandFlag> flags = getFlags(field);
 185                 assert verifyFlags(field, type, flags);
 186                 annotation.values.add(new ValueFieldInfo(offset, field.getName(), type, field.getDeclaringClass(), flags));
 187                 annotation.directCount++;
 188             } else if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
 189                 OperandModeAnnotation annotation = getOperandModeAnnotation(field);
 190                 assert annotation != null : "Field must have operand mode annotation: " + field;
 191                 EnumSet<OperandFlag> flags = getFlags(field);
 192                 assert verifyFlags(field, type.getComponentType(), flags);
 193                 annotation.values.add(new ValueFieldInfo(offset, field.getName(), type, field.getDeclaringClass(), flags));
 194             } else {
 195                 assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field;
 196                 assert field.getAnnotation(LIRInstruction.State.class) == null : "Field must not have state annotation: " + field;
 197                 super.scanField(field, offset);
 198             }
 199         }
 200 
 201         private static boolean verifyFlags(Field field, Class<?> type, EnumSet<OperandFlag> flags) {
 202             if (flags.contains(REG)) {
 203                 assert type.isAssignableFrom(REGISTER_VALUE_CLASS) || type.isAssignableFrom(VARIABLE_CLASS) : "Cannot assign RegisterValue / Variable to field with REG flag:" + field;
 204             }
 205             if (flags.contains(STACK)) {
 206                 assert type.isAssignableFrom(STACK_SLOT_CLASS) : "Cannot assign StackSlot to field with STACK flag:" + field;
 207             }
 208             if (flags.contains(CONST)) {
 209                 assert type.isAssignableFrom(CONSTANT_VALUE_CLASS) : "Cannot assign Constant to field with CONST flag:" + field;
 210             }
 211             return true;
 212         }
 213     }
 214 
 215     protected static void forEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueProcedure proc) {
 216         for (int i = 0; i < values.getCount(); i++) {
 217             assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i));
 218 
 219             if (i < values.getDirectCount()) {
 220                 Value value = values.getValue(inst, i);
 221                 Value newValue;
 222                 if (value instanceof CompositeValue) {
 223                     CompositeValue composite = (CompositeValue) value;
 224                     newValue = composite.forEachComponent(inst, mode, proc);
 225                 } else {
 226                     newValue = proc.doValue(inst, value, mode, values.getFlags(i));
 227                 }
 228                 if (!value.identityEquals(newValue)) {
 229                     values.setValue(inst, i, newValue);
 230                 }
 231             } else {
 232                 Value[] valueArray = values.getValueArray(inst, i);
 233                 for (int j = 0; j < valueArray.length; j++) {
 234                     Value value = valueArray[j];
 235                     Value newValue;
 236                     if (value instanceof CompositeValue) {
 237                         CompositeValue composite = (CompositeValue) value;
 238                         newValue = composite.forEachComponent(inst, mode, proc);
 239                     } else {
 240                         newValue = proc.doValue(inst, value, mode, values.getFlags(i));
 241                     }
 242                     if (!value.identityEquals(newValue)) {
 243                         valueArray[j] = newValue;
 244                     }
 245                 }
 246             }
 247         }
 248     }
 249 
 250     protected static void visitEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueConsumer proc) {
 251         for (int i = 0; i < values.getCount(); i++) {
 252             assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i));
 253 
 254             if (i < values.getDirectCount()) {
 255                 Value value = values.getValue(inst, i);
 256                 if (value instanceof CompositeValue) {
 257                     CompositeValue composite = (CompositeValue) value;
 258                     composite.visitEachComponent(inst, mode, proc);
 259                 } else {
 260                     proc.visitValue(inst, value, mode, values.getFlags(i));
 261                 }
 262             } else {
 263                 Value[] valueArray = values.getValueArray(inst, i);
 264                 for (int j = 0; j < valueArray.length; j++) {
 265                     Value value = valueArray[j];
 266                     if (value instanceof CompositeValue) {
 267                         CompositeValue composite = (CompositeValue) value;
 268                         composite.visitEachComponent(inst, mode, proc);
 269                     } else {
 270                         proc.visitValue(inst, value, mode, values.getFlags(i));
 271                     }
 272                 }
 273             }
 274         }
 275     }
 276 
 277     protected static void appendValues(StringBuilder sb, Object obj, String start, String end, String startMultiple, String endMultiple, String[] prefix, Fields... fieldsList) {
 278         int total = 0;
 279         for (Fields fields : fieldsList) {
 280             total += fields.getCount();
 281         }
 282         if (total == 0) {
 283             return;
 284         }
 285 
 286         sb.append(start);
 287         if (total > 1) {
 288             sb.append(startMultiple);
 289         }
 290         String sep = "";
 291         int i = 0;
 292         for (Fields fields : fieldsList) {
 293             for (int j = 0; j < fields.getCount(); j++) {
 294                 sb.append(sep).append(prefix[i]);
 295                 if (total > 1) {
 296                     sb.append(fields.getName(j)).append(": ");
 297                 }
 298                 sb.append(getFieldString(obj, j, fields));
 299                 sep = ", ";
 300             }
 301             i++;
 302         }
 303         if (total > 1) {
 304             sb.append(endMultiple);
 305         }
 306         sb.append(end);
 307     }
 308 
 309     protected static String getFieldString(Object obj, int index, Fields fields) {
 310         Object value = fields.get(obj, index);
 311         Class<?> type = fields.getType(index);
 312         if (value == null || type.isPrimitive() || !type.isArray()) {
 313             return String.valueOf(value);
 314         }
 315         if (type == int[].class) {
 316             return Arrays.toString((int[]) value);
 317         } else if (type == double[].class) {
 318             return Arrays.toString((double[]) value);
 319         } else if (type == byte[].class) {
 320             byte[] byteValue = (byte[]) value;
 321             if (isPrintableAsciiString(byteValue)) {
 322                 return toString(byteValue);
 323             } else {
 324                 return Arrays.toString(byteValue);
 325             }
 326         } else if (!type.getComponentType().isPrimitive()) {
 327             return Arrays.toString((Object[]) value);
 328         }
 329         assert false : "unhandled field type: " + type;
 330         return "";
 331     }
 332 
 333     /**
 334      * Tests if all values in this string are printable ASCII characters or value \0 (b in
 335      * [0x20,0x7F]) or b == 0.
 336      *
 337      * @param array
 338      * @return true if there are only printable ASCII characters and \0, false otherwise
 339      */
 340     private static boolean isPrintableAsciiString(byte[] array) {
 341         for (byte b : array) {
 342             char c = (char) b;
 343             if (c != 0 && c < 0x20 && c > 0x7F) {
 344                 return false;
 345             }
 346         }
 347         return true;
 348     }
 349 
 350     private static String toString(byte[] bytes) {
 351         StringBuilder sb = new StringBuilder();
 352         sb.append('"');
 353         for (byte b : bytes) {
 354             if (b == 0) {
 355                 sb.append("\\0");
 356             } else if (b == '"') {
 357                 sb.append("\\\"");
 358             } else if (b == '\n') {
 359                 sb.append("\\n");
 360             } else {
 361                 sb.append((char) b);
 362             }
 363         }
 364         sb.append('"');
 365         return sb.toString();
 366     }
 367 }