/* * Copyright (c) 2012, 2013, 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 java.lang.reflect.Field; import java.util.Arrays; import java.util.EnumSet; import org.graalvm.compiler.core.common.Fields; import org.graalvm.compiler.core.common.FieldsScanner; import org.graalvm.compiler.debug.GraalError; import org.graalvm.compiler.lir.LIRInstruction.OperandFlag; import org.graalvm.compiler.lir.LIRInstruction.OperandMode; import org.graalvm.compiler.lir.StandardOp.LoadConstantOp; import org.graalvm.compiler.lir.StandardOp.MoveOp; import org.graalvm.compiler.lir.StandardOp.ValueMoveOp; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.meta.Value; public class LIRInstructionClass<T> extends LIRIntrospection<T> { public static <T extends LIRInstruction> LIRInstructionClass<T> create(Class<T> c) { return new LIRInstructionClass<>(c); } private static final Class<LIRInstruction> INSTRUCTION_CLASS = LIRInstruction.class; private static final Class<LIRFrameState> STATE_CLASS = LIRFrameState.class; private final Values uses; private final Values alives; private final Values temps; private final Values defs; private final Fields states; private final boolean isMoveOp; private final boolean isValueMoveOp; private final boolean isLoadConstantOp; private String opcodeConstant; private int opcodeIndex; private LIRInstructionClass(Class<T> clazz) { this(clazz, new FieldsScanner.DefaultCalcOffset()); } public LIRInstructionClass(Class<T> clazz, FieldsScanner.CalcOffset calcOffset) { super(clazz); assert INSTRUCTION_CLASS.isAssignableFrom(clazz); LIRInstructionFieldsScanner ifs = new LIRInstructionFieldsScanner(calcOffset); ifs.scan(clazz); uses = new Values(ifs.valueAnnotations.get(LIRInstruction.Use.class)); alives = new Values(ifs.valueAnnotations.get(LIRInstruction.Alive.class)); temps = new Values(ifs.valueAnnotations.get(LIRInstruction.Temp.class)); defs = new Values(ifs.valueAnnotations.get(LIRInstruction.Def.class)); states = new Fields(ifs.states); data = new Fields(ifs.data); opcodeConstant = ifs.opcodeConstant; if (ifs.opcodeField == null) { opcodeIndex = -1; } else { opcodeIndex = ifs.data.indexOf(ifs.opcodeField); } isMoveOp = MoveOp.class.isAssignableFrom(clazz); isValueMoveOp = ValueMoveOp.class.isAssignableFrom(clazz); isLoadConstantOp = LoadConstantOp.class.isAssignableFrom(clazz); } @SuppressWarnings("unchecked") public static <T> LIRInstructionClass<T> get(Class<T> clazz) { try { Field field = clazz.getDeclaredField("TYPE"); field.setAccessible(true); return (LIRInstructionClass<T>) field.get(null); } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { throw new RuntimeException(e); } } private static class LIRInstructionFieldsScanner extends LIRFieldsScanner { private String opcodeConstant; /** * Field (if any) annotated by {@link Opcode}. */ private FieldsScanner.FieldInfo opcodeField; LIRInstructionFieldsScanner(FieldsScanner.CalcOffset calc) { super(calc); valueAnnotations.put(LIRInstruction.Use.class, new OperandModeAnnotation()); valueAnnotations.put(LIRInstruction.Alive.class, new OperandModeAnnotation()); valueAnnotations.put(LIRInstruction.Temp.class, new OperandModeAnnotation()); valueAnnotations.put(LIRInstruction.Def.class, new OperandModeAnnotation()); } @Override protected EnumSet<OperandFlag> getFlags(Field field) { EnumSet<OperandFlag> result = EnumSet.noneOf(OperandFlag.class); // Unfortunately, annotations cannot have class hierarchies or implement interfaces, so // we have to duplicate the code for every operand mode. // Unfortunately, annotations cannot have an EnumSet property, so we have to convert // from arrays to EnumSet manually. if (field.isAnnotationPresent(LIRInstruction.Use.class)) { result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Use.class).value())); } else if (field.isAnnotationPresent(LIRInstruction.Alive.class)) { result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Alive.class).value())); } else if (field.isAnnotationPresent(LIRInstruction.Temp.class)) { result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Temp.class).value())); } else if (field.isAnnotationPresent(LIRInstruction.Def.class)) { result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Def.class).value())); } else { GraalError.shouldNotReachHere(); } return result; } public void scan(Class<?> clazz) { if (clazz.getAnnotation(Opcode.class) != null) { opcodeConstant = clazz.getAnnotation(Opcode.class).value(); } opcodeField = null; super.scan(clazz, LIRInstruction.class, false); if (opcodeConstant == null && opcodeField == null) { opcodeConstant = clazz.getSimpleName(); if (opcodeConstant.endsWith("Op")) { opcodeConstant = opcodeConstant.substring(0, opcodeConstant.length() - 2); } } } @Override protected void scanField(Field field, long offset) { Class<?> type = field.getType(); 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; states.add(new FieldsScanner.FieldInfo(offset, field.getName(), type, field.getDeclaringClass())); } else { super.scanField(field, offset); } if (field.getAnnotation(Opcode.class) != null) { assert opcodeConstant == null && opcodeField == null : "Can have only one Opcode definition: " + type; assert data.get(data.size() - 1).offset == offset; opcodeField = data.get(data.size() - 1); } } } @Override public Fields[] getAllFields() { assert values == null; return new Fields[]{data, uses, alives, temps, defs, states}; } @Override public String toString() { StringBuilder str = new StringBuilder(); str.append(getClass().getSimpleName()).append(" ").append(getClazz().getSimpleName()).append(" use["); uses.appendFields(str); str.append("] alive["); alives.appendFields(str); str.append("] temp["); temps.appendFields(str); str.append("] def["); defs.appendFields(str); str.append("] state["); states.appendFields(str); str.append("] data["); data.appendFields(str); str.append("]"); return str.toString(); } Values getValues(OperandMode mode) { switch (mode) { case USE: return uses; case ALIVE: return alives; case TEMP: return temps; case DEF: return defs; default: throw GraalError.shouldNotReachHere("unknown OperandMode: " + mode); } } final String getOpcode(LIRInstruction obj) { if (opcodeConstant != null) { return opcodeConstant; } assert opcodeIndex != -1; return String.valueOf(data.getObject(obj, opcodeIndex)); } final boolean hasOperands() { return uses.getCount() > 0 || alives.getCount() > 0 || temps.getCount() > 0 || defs.getCount() > 0; } final boolean hasState(LIRInstruction obj) { for (int i = 0; i < states.getCount(); i++) { if (states.getObject(obj, i) != null) { return true; } } return false; } final void forEachUse(LIRInstruction obj, InstructionValueProcedure proc) { forEach(obj, uses, OperandMode.USE, proc); } final void forEachAlive(LIRInstruction obj, InstructionValueProcedure proc) { forEach(obj, alives, OperandMode.ALIVE, proc); } final void forEachTemp(LIRInstruction obj, InstructionValueProcedure proc) { forEach(obj, temps, OperandMode.TEMP, proc); } final void forEachDef(LIRInstruction obj, InstructionValueProcedure proc) { forEach(obj, defs, OperandMode.DEF, proc); } final void visitEachUse(LIRInstruction obj, InstructionValueConsumer proc) { visitEach(obj, uses, OperandMode.USE, proc); } final void visitEachAlive(LIRInstruction obj, InstructionValueConsumer proc) { visitEach(obj, alives, OperandMode.ALIVE, proc); } final void visitEachTemp(LIRInstruction obj, InstructionValueConsumer proc) { visitEach(obj, temps, OperandMode.TEMP, proc); } final void visitEachDef(LIRInstruction obj, InstructionValueConsumer proc) { visitEach(obj, defs, OperandMode.DEF, proc); } final void forEachState(LIRInstruction obj, InstructionValueProcedure proc) { for (int i = 0; i < states.getCount(); i++) { LIRFrameState state = (LIRFrameState) states.getObject(obj, i); if (state != null) { state.forEachState(obj, proc); } } } final void visitEachState(LIRInstruction obj, InstructionValueConsumer proc) { for (int i = 0; i < states.getCount(); i++) { LIRFrameState state = (LIRFrameState) states.getObject(obj, i); if (state != null) { state.visitEachState(obj, proc); } } } final void forEachState(LIRInstruction obj, InstructionStateProcedure proc) { for (int i = 0; i < states.getCount(); i++) { LIRFrameState state = (LIRFrameState) states.getObject(obj, i); if (state != null) { proc.doState(obj, state); } } } final Value forEachRegisterHint(LIRInstruction obj, OperandMode mode, InstructionValueProcedure proc) { Values hints; if (mode == OperandMode.USE) { hints = defs; } else if (mode == OperandMode.DEF) { hints = uses; } else { return null; } for (int i = 0; i < hints.getCount(); i++) { if (i < hints.getDirectCount()) { Value hintValue = hints.getValue(obj, i); Value result = proc.doValue(obj, hintValue, null, null); if (result != null) { return result; } } else { Value[] hintValues = hints.getValueArray(obj, i); for (int j = 0; j < hintValues.length; j++) { Value hintValue = hintValues[j]; Value result = proc.doValue(obj, hintValue, null, null); if (result != null) { return result; } } } } return null; } String toString(LIRInstruction obj) { StringBuilder result = new StringBuilder(); appendValues(result, obj, "", " = ", "(", ")", new String[]{""}, defs); result.append(String.valueOf(getOpcode(obj)).toUpperCase()); appendValues(result, obj, " ", "", "(", ")", new String[]{"", "~"}, uses, alives); appendValues(result, obj, " ", "", "{", "}", new String[]{""}, temps); for (int i = 0; i < data.getCount(); i++) { if (i == opcodeIndex) { continue; } result.append(" ").append(data.getName(i)).append(": ").append(getFieldString(obj, i, data)); } for (int i = 0; i < states.getCount(); i++) { LIRFrameState state = (LIRFrameState) states.getObject(obj, i); if (state != null) { result.append(" ").append(states.getName(i)).append(" [bci:"); String sep = ""; for (BytecodeFrame cur = state.topFrame; cur != null; cur = cur.caller()) { result.append(sep).append(cur.getBCI()); sep = ", "; } result.append("]"); } } return result.toString(); } final boolean isMoveOp() { return isMoveOp; } final boolean isValueMoveOp() { return isValueMoveOp; } final boolean isLoadConstantOp() { return isLoadConstantOp; } }