1 /*
   2  * Copyright (c) 2012, 2013, 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 java.lang.reflect.Field;
  26 import java.util.Arrays;
  27 import java.util.EnumSet;
  28 
  29 import org.graalvm.compiler.core.common.Fields;
  30 import org.graalvm.compiler.core.common.FieldsScanner;
  31 import org.graalvm.compiler.debug.GraalError;
  32 import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
  33 import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
  34 
  35 import jdk.vm.ci.code.BytecodeFrame;
  36 import jdk.vm.ci.meta.Value;
  37 
  38 public class LIRInstructionClass<T> extends LIRIntrospection<T> {
  39 
  40     public static <T extends LIRInstruction> LIRInstructionClass<T> create(Class<T> c) {
  41         return new LIRInstructionClass<>(c);
  42     }
  43 
  44     private static final Class<LIRInstruction> INSTRUCTION_CLASS = LIRInstruction.class;
  45     private static final Class<LIRFrameState> STATE_CLASS = LIRFrameState.class;
  46 
  47     private final Values uses;
  48     private final Values alives;
  49     private final Values temps;
  50     private final Values defs;
  51     private final Fields states;
  52 
  53     private String opcodeConstant;
  54     private int opcodeIndex;
  55 
  56     private LIRInstructionClass(Class<T> clazz) {
  57         this(clazz, new FieldsScanner.DefaultCalcOffset());
  58     }
  59 
  60     public LIRInstructionClass(Class<T> clazz, FieldsScanner.CalcOffset calcOffset) {
  61         super(clazz);
  62         assert INSTRUCTION_CLASS.isAssignableFrom(clazz);
  63 
  64         LIRInstructionFieldsScanner ifs = new LIRInstructionFieldsScanner(calcOffset);
  65         ifs.scan(clazz);
  66 
  67         uses = new Values(ifs.valueAnnotations.get(LIRInstruction.Use.class));
  68         alives = new Values(ifs.valueAnnotations.get(LIRInstruction.Alive.class));
  69         temps = new Values(ifs.valueAnnotations.get(LIRInstruction.Temp.class));
  70         defs = new Values(ifs.valueAnnotations.get(LIRInstruction.Def.class));
  71 
  72         states = new Fields(ifs.states);
  73         data = new Fields(ifs.data);
  74 
  75         opcodeConstant = ifs.opcodeConstant;
  76         if (ifs.opcodeField == null) {
  77             opcodeIndex = -1;
  78         } else {
  79             opcodeIndex = ifs.data.indexOf(ifs.opcodeField);
  80         }
  81     }
  82 
  83     @SuppressWarnings("unchecked")
  84     public static <T> LIRInstructionClass<T> get(Class<T> clazz) {
  85         try {
  86             Field field = clazz.getDeclaredField("TYPE");
  87             field.setAccessible(true);
  88             return (LIRInstructionClass<T>) field.get(null);
  89         } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
  90             throw new RuntimeException(e);
  91         }
  92     }
  93 
  94     private static class LIRInstructionFieldsScanner extends LIRFieldsScanner {
  95 
  96         private String opcodeConstant;
  97 
  98         /**
  99          * Field (if any) annotated by {@link Opcode}.
 100          */
 101         private FieldsScanner.FieldInfo opcodeField;
 102 
 103         LIRInstructionFieldsScanner(FieldsScanner.CalcOffset calc) {
 104             super(calc);
 105 
 106             valueAnnotations.put(LIRInstruction.Use.class, new OperandModeAnnotation());
 107             valueAnnotations.put(LIRInstruction.Alive.class, new OperandModeAnnotation());
 108             valueAnnotations.put(LIRInstruction.Temp.class, new OperandModeAnnotation());
 109             valueAnnotations.put(LIRInstruction.Def.class, new OperandModeAnnotation());
 110         }
 111 
 112         @Override
 113         protected EnumSet<OperandFlag> getFlags(Field field) {
 114             EnumSet<OperandFlag> result = EnumSet.noneOf(OperandFlag.class);
 115             // Unfortunately, annotations cannot have class hierarchies or implement interfaces, so
 116             // we have to duplicate the code for every operand mode.
 117             // Unfortunately, annotations cannot have an EnumSet property, so we have to convert
 118             // from arrays to EnumSet manually.
 119             if (field.isAnnotationPresent(LIRInstruction.Use.class)) {
 120                 result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Use.class).value()));
 121             } else if (field.isAnnotationPresent(LIRInstruction.Alive.class)) {
 122                 result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Alive.class).value()));
 123             } else if (field.isAnnotationPresent(LIRInstruction.Temp.class)) {
 124                 result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Temp.class).value()));
 125             } else if (field.isAnnotationPresent(LIRInstruction.Def.class)) {
 126                 result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Def.class).value()));
 127             } else {
 128                 GraalError.shouldNotReachHere();
 129             }
 130             return result;
 131         }
 132 
 133         public void scan(Class<?> clazz) {
 134             if (clazz.getAnnotation(Opcode.class) != null) {
 135                 opcodeConstant = clazz.getAnnotation(Opcode.class).value();
 136             }
 137             opcodeField = null;
 138 
 139             super.scan(clazz, LIRInstruction.class, false);
 140 
 141             if (opcodeConstant == null && opcodeField == null) {
 142                 opcodeConstant = clazz.getSimpleName();
 143                 if (opcodeConstant.endsWith("Op")) {
 144                     opcodeConstant = opcodeConstant.substring(0, opcodeConstant.length() - 2);
 145                 }
 146             }
 147         }
 148 
 149         @Override
 150         protected void scanField(Field field, long offset) {
 151             Class<?> type = field.getType();
 152             if (STATE_CLASS.isAssignableFrom(type)) {
 153                 assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field;
 154                 assert field.getAnnotation(LIRInstruction.State.class) != null : "Field must have state annotation: " + field;
 155                 states.add(new FieldsScanner.FieldInfo(offset, field.getName(), type, field.getDeclaringClass()));
 156             } else {
 157                 super.scanField(field, offset);
 158             }
 159 
 160             if (field.getAnnotation(Opcode.class) != null) {
 161                 assert opcodeConstant == null && opcodeField == null : "Can have only one Opcode definition: " + type;
 162                 assert data.get(data.size() - 1).offset == offset;
 163                 opcodeField = data.get(data.size() - 1);
 164             }
 165         }
 166     }
 167 
 168     @Override
 169     public Fields[] getAllFields() {
 170         assert values == null;
 171         return new Fields[]{data, uses, alives, temps, defs, states};
 172     }
 173 
 174     @Override
 175     public String toString() {
 176         StringBuilder str = new StringBuilder();
 177         str.append(getClass().getSimpleName()).append(" ").append(getClazz().getSimpleName()).append(" use[");
 178         uses.appendFields(str);
 179         str.append("] alive[");
 180         alives.appendFields(str);
 181         str.append("] temp[");
 182         temps.appendFields(str);
 183         str.append("] def[");
 184         defs.appendFields(str);
 185         str.append("] state[");
 186         states.appendFields(str);
 187         str.append("] data[");
 188         data.appendFields(str);
 189         str.append("]");
 190         return str.toString();
 191     }
 192 
 193     Values getValues(OperandMode mode) {
 194         switch (mode) {
 195             case USE:
 196                 return uses;
 197             case ALIVE:
 198                 return alives;
 199             case TEMP:
 200                 return temps;
 201             case DEF:
 202                 return defs;
 203             default:
 204                 throw GraalError.shouldNotReachHere("unknown OperandMode: " + mode);
 205         }
 206     }
 207 
 208     final String getOpcode(LIRInstruction obj) {
 209         if (opcodeConstant != null) {
 210             return opcodeConstant;
 211         }
 212         assert opcodeIndex != -1;
 213         return String.valueOf(data.getObject(obj, opcodeIndex));
 214     }
 215 
 216     final boolean hasOperands() {
 217         return uses.getCount() > 0 || alives.getCount() > 0 || temps.getCount() > 0 || defs.getCount() > 0;
 218     }
 219 
 220     final boolean hasState(LIRInstruction obj) {
 221         for (int i = 0; i < states.getCount(); i++) {
 222             if (states.getObject(obj, i) != null) {
 223                 return true;
 224             }
 225         }
 226         return false;
 227     }
 228 
 229     final void forEachUse(LIRInstruction obj, InstructionValueProcedure proc) {
 230         forEach(obj, uses, OperandMode.USE, proc);
 231     }
 232 
 233     final void forEachAlive(LIRInstruction obj, InstructionValueProcedure proc) {
 234         forEach(obj, alives, OperandMode.ALIVE, proc);
 235     }
 236 
 237     final void forEachTemp(LIRInstruction obj, InstructionValueProcedure proc) {
 238         forEach(obj, temps, OperandMode.TEMP, proc);
 239     }
 240 
 241     final void forEachDef(LIRInstruction obj, InstructionValueProcedure proc) {
 242         forEach(obj, defs, OperandMode.DEF, proc);
 243     }
 244 
 245     final void visitEachUse(LIRInstruction obj, InstructionValueConsumer proc) {
 246         visitEach(obj, uses, OperandMode.USE, proc);
 247     }
 248 
 249     final void visitEachAlive(LIRInstruction obj, InstructionValueConsumer proc) {
 250         visitEach(obj, alives, OperandMode.ALIVE, proc);
 251     }
 252 
 253     final void visitEachTemp(LIRInstruction obj, InstructionValueConsumer proc) {
 254         visitEach(obj, temps, OperandMode.TEMP, proc);
 255     }
 256 
 257     final void visitEachDef(LIRInstruction obj, InstructionValueConsumer proc) {
 258         visitEach(obj, defs, OperandMode.DEF, proc);
 259     }
 260 
 261     final void forEachState(LIRInstruction obj, InstructionValueProcedure proc) {
 262         for (int i = 0; i < states.getCount(); i++) {
 263             LIRFrameState state = (LIRFrameState) states.getObject(obj, i);
 264             if (state != null) {
 265                 state.forEachState(obj, proc);
 266             }
 267         }
 268     }
 269 
 270     final void visitEachState(LIRInstruction obj, InstructionValueConsumer proc) {
 271         for (int i = 0; i < states.getCount(); i++) {
 272             LIRFrameState state = (LIRFrameState) states.getObject(obj, i);
 273             if (state != null) {
 274                 state.visitEachState(obj, proc);
 275             }
 276         }
 277     }
 278 
 279     final void forEachState(LIRInstruction obj, InstructionStateProcedure proc) {
 280         for (int i = 0; i < states.getCount(); i++) {
 281             LIRFrameState state = (LIRFrameState) states.getObject(obj, i);
 282             if (state != null) {
 283                 proc.doState(obj, state);
 284             }
 285         }
 286     }
 287 
 288     final Value forEachRegisterHint(LIRInstruction obj, OperandMode mode, InstructionValueProcedure proc) {
 289         Values hints;
 290         if (mode == OperandMode.USE) {
 291             hints = defs;
 292         } else if (mode == OperandMode.DEF) {
 293             hints = uses;
 294         } else {
 295             return null;
 296         }
 297 
 298         for (int i = 0; i < hints.getCount(); i++) {
 299             if (i < hints.getDirectCount()) {
 300                 Value hintValue = hints.getValue(obj, i);
 301                 Value result = proc.doValue(obj, hintValue, null, null);
 302                 if (result != null) {
 303                     return result;
 304                 }
 305             } else {
 306                 Value[] hintValues = hints.getValueArray(obj, i);
 307                 for (int j = 0; j < hintValues.length; j++) {
 308                     Value hintValue = hintValues[j];
 309                     Value result = proc.doValue(obj, hintValue, null, null);
 310                     if (result != null) {
 311                         return result;
 312                     }
 313                 }
 314             }
 315         }
 316         return null;
 317     }
 318 
 319     String toString(LIRInstruction obj) {
 320         StringBuilder result = new StringBuilder();
 321 
 322         appendValues(result, obj, "", " = ", "(", ")", new String[]{""}, defs);
 323         result.append(String.valueOf(getOpcode(obj)).toUpperCase());
 324         appendValues(result, obj, " ", "", "(", ")", new String[]{"", "~"}, uses, alives);
 325         appendValues(result, obj, " ", "", "{", "}", new String[]{""}, temps);
 326 
 327         for (int i = 0; i < data.getCount(); i++) {
 328             if (i == opcodeIndex) {
 329                 continue;
 330             }
 331             result.append(" ").append(data.getName(i)).append(": ").append(getFieldString(obj, i, data));
 332         }
 333 
 334         for (int i = 0; i < states.getCount(); i++) {
 335             LIRFrameState state = (LIRFrameState) states.getObject(obj, i);
 336             if (state != null) {
 337                 result.append(" ").append(states.getName(i)).append(" [bci:");
 338                 String sep = "";
 339                 for (BytecodeFrame cur = state.topFrame; cur != null; cur = cur.caller()) {
 340                     result.append(sep).append(cur.getBCI());
 341                     sep = ", ";
 342                 }
 343                 result.append("]");
 344             }
 345         }
 346 
 347         return result.toString();
 348     }
 349 }