001/*
002 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.lir;
024
025import java.lang.reflect.*;
026import java.util.*;
027
028import jdk.internal.jvmci.code.*;
029import jdk.internal.jvmci.common.*;
030import jdk.internal.jvmci.meta.*;
031
032import com.oracle.graal.compiler.common.*;
033import com.oracle.graal.lir.LIRInstruction.OperandFlag;
034import com.oracle.graal.lir.LIRInstruction.OperandMode;
035
036public class LIRInstructionClass<T> extends LIRIntrospection<T> {
037
038    public static final <T extends LIRInstruction> LIRInstructionClass<T> create(Class<T> c) {
039        return new LIRInstructionClass<>(c);
040    }
041
042    private static final Class<LIRInstruction> INSTRUCTION_CLASS = LIRInstruction.class;
043    private static final Class<LIRFrameState> STATE_CLASS = LIRFrameState.class;
044
045    private final Values uses;
046    private final Values alives;
047    private final Values temps;
048    private final Values defs;
049    private final Fields states;
050
051    private String opcodeConstant;
052    private int opcodeIndex;
053
054    private LIRInstructionClass(Class<T> clazz) {
055        this(clazz, new FieldsScanner.DefaultCalcOffset());
056    }
057
058    public LIRInstructionClass(Class<T> clazz, FieldsScanner.CalcOffset calcOffset) {
059        super(clazz);
060        assert INSTRUCTION_CLASS.isAssignableFrom(clazz);
061
062        LIRInstructionFieldsScanner ifs = new LIRInstructionFieldsScanner(calcOffset);
063        ifs.scan(clazz);
064
065        uses = new Values(ifs.valueAnnotations.get(LIRInstruction.Use.class));
066        alives = new Values(ifs.valueAnnotations.get(LIRInstruction.Alive.class));
067        temps = new Values(ifs.valueAnnotations.get(LIRInstruction.Temp.class));
068        defs = new Values(ifs.valueAnnotations.get(LIRInstruction.Def.class));
069
070        states = new Fields(ifs.states);
071        data = new Fields(ifs.data);
072
073        opcodeConstant = ifs.opcodeConstant;
074        if (ifs.opcodeField == null) {
075            opcodeIndex = -1;
076        } else {
077            opcodeIndex = ifs.data.indexOf(ifs.opcodeField);
078        }
079    }
080
081    @SuppressWarnings("unchecked")
082    public static <T> LIRInstructionClass<T> get(Class<T> clazz) {
083        try {
084            Field field = clazz.getDeclaredField("TYPE");
085            field.setAccessible(true);
086            return (LIRInstructionClass<T>) field.get(null);
087        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
088            throw new RuntimeException(e);
089        }
090    }
091
092    private static class LIRInstructionFieldsScanner extends LIRFieldsScanner {
093
094        private String opcodeConstant;
095
096        /**
097         * Field (if any) annotated by {@link Opcode}.
098         */
099        private FieldsScanner.FieldInfo opcodeField;
100
101        public LIRInstructionFieldsScanner(FieldsScanner.CalcOffset calc) {
102            super(calc);
103
104            valueAnnotations.put(LIRInstruction.Use.class, new OperandModeAnnotation());
105            valueAnnotations.put(LIRInstruction.Alive.class, new OperandModeAnnotation());
106            valueAnnotations.put(LIRInstruction.Temp.class, new OperandModeAnnotation());
107            valueAnnotations.put(LIRInstruction.Def.class, new OperandModeAnnotation());
108        }
109
110        @Override
111        protected EnumSet<OperandFlag> getFlags(Field field) {
112            EnumSet<OperandFlag> result = EnumSet.noneOf(OperandFlag.class);
113            // Unfortunately, annotations cannot have class hierarchies or implement interfaces, so
114            // we have to duplicate the code for every operand mode.
115            // Unfortunately, annotations cannot have an EnumSet property, so we have to convert
116            // from arrays to EnumSet manually.
117            if (field.isAnnotationPresent(LIRInstruction.Use.class)) {
118                result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Use.class).value()));
119            } else if (field.isAnnotationPresent(LIRInstruction.Alive.class)) {
120                result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Alive.class).value()));
121            } else if (field.isAnnotationPresent(LIRInstruction.Temp.class)) {
122                result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Temp.class).value()));
123            } else if (field.isAnnotationPresent(LIRInstruction.Def.class)) {
124                result.addAll(Arrays.asList(field.getAnnotation(LIRInstruction.Def.class).value()));
125            } else {
126                JVMCIError.shouldNotReachHere();
127            }
128            return result;
129        }
130
131        public void scan(Class<?> clazz) {
132            if (clazz.getAnnotation(Opcode.class) != null) {
133                opcodeConstant = clazz.getAnnotation(Opcode.class).value();
134            }
135            opcodeField = null;
136
137            super.scan(clazz, LIRInstruction.class, false);
138
139            if (opcodeConstant == null && opcodeField == null) {
140                opcodeConstant = clazz.getSimpleName();
141                if (opcodeConstant.endsWith("Op")) {
142                    opcodeConstant = opcodeConstant.substring(0, opcodeConstant.length() - 2);
143                }
144            }
145        }
146
147        @Override
148        protected void scanField(Field field, long offset) {
149            Class<?> type = field.getType();
150            if (STATE_CLASS.isAssignableFrom(type)) {
151                assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field;
152                assert field.getAnnotation(LIRInstruction.State.class) != null : "Field must have state annotation: " + field;
153                states.add(new FieldsScanner.FieldInfo(offset, field.getName(), type, field.getDeclaringClass()));
154            } else {
155                super.scanField(field, offset);
156            }
157
158            if (field.getAnnotation(Opcode.class) != null) {
159                assert opcodeConstant == null && opcodeField == null : "Can have only one Opcode definition: " + type;
160                assert data.get(data.size() - 1).offset == offset;
161                opcodeField = data.get(data.size() - 1);
162            }
163        }
164    }
165
166    @Override
167    public Fields[] getAllFields() {
168        assert values == null;
169        return new Fields[]{data, uses, alives, temps, defs, states};
170    }
171
172    @Override
173    public String toString() {
174        StringBuilder str = new StringBuilder();
175        str.append(getClass().getSimpleName()).append(" ").append(getClazz().getSimpleName()).append(" use[");
176        uses.appendFields(str);
177        str.append("] alive[");
178        alives.appendFields(str);
179        str.append("] temp[");
180        temps.appendFields(str);
181        str.append("] def[");
182        defs.appendFields(str);
183        str.append("] state[");
184        states.appendFields(str);
185        str.append("] data[");
186        data.appendFields(str);
187        str.append("]");
188        return str.toString();
189    }
190
191    Values getValues(OperandMode mode) {
192        switch (mode) {
193            case USE:
194                return uses;
195            case ALIVE:
196                return alives;
197            case TEMP:
198                return temps;
199            case DEF:
200                return defs;
201            default:
202                throw JVMCIError.shouldNotReachHere("unknown OperandMode: " + mode);
203        }
204    }
205
206    final String getOpcode(LIRInstruction obj) {
207        if (opcodeConstant != null) {
208            return opcodeConstant;
209        }
210        assert opcodeIndex != -1;
211        return String.valueOf(data.getObject(obj, opcodeIndex));
212    }
213
214    final boolean hasOperands() {
215        return uses.getCount() > 0 || alives.getCount() > 0 || temps.getCount() > 0 || defs.getCount() > 0;
216    }
217
218    final boolean hasState(LIRInstruction obj) {
219        for (int i = 0; i < states.getCount(); i++) {
220            if (states.getObject(obj, i) != null) {
221                return true;
222            }
223        }
224        return false;
225    }
226
227    final void forEachUse(LIRInstruction obj, InstructionValueProcedure proc) {
228        forEach(obj, uses, OperandMode.USE, proc);
229    }
230
231    final void forEachAlive(LIRInstruction obj, InstructionValueProcedure proc) {
232        forEach(obj, alives, OperandMode.ALIVE, proc);
233    }
234
235    final void forEachTemp(LIRInstruction obj, InstructionValueProcedure proc) {
236        forEach(obj, temps, OperandMode.TEMP, proc);
237    }
238
239    final void forEachDef(LIRInstruction obj, InstructionValueProcedure proc) {
240        forEach(obj, defs, OperandMode.DEF, proc);
241    }
242
243    final void forEachUse(LIRInstruction obj, InstructionValueConsumer proc) {
244        forEach(obj, uses, OperandMode.USE, proc);
245    }
246
247    final void forEachAlive(LIRInstruction obj, InstructionValueConsumer proc) {
248        forEach(obj, alives, OperandMode.ALIVE, proc);
249    }
250
251    final void forEachTemp(LIRInstruction obj, InstructionValueConsumer proc) {
252        forEach(obj, temps, OperandMode.TEMP, proc);
253    }
254
255    final void forEachDef(LIRInstruction obj, InstructionValueConsumer proc) {
256        forEach(obj, defs, OperandMode.DEF, proc);
257    }
258
259    final void forEachState(LIRInstruction obj, InstructionValueProcedure proc) {
260        for (int i = 0; i < states.getCount(); i++) {
261            LIRFrameState state = (LIRFrameState) states.getObject(obj, i);
262            if (state != null) {
263                state.forEachState(obj, proc);
264            }
265        }
266    }
267
268    final void forEachState(LIRInstruction obj, InstructionValueConsumer proc) {
269        for (int i = 0; i < states.getCount(); i++) {
270            LIRFrameState state = (LIRFrameState) states.getObject(obj, i);
271            if (state != null) {
272                state.forEachState(obj, proc);
273            }
274        }
275    }
276
277    final void forEachState(LIRInstruction obj, InstructionStateProcedure proc) {
278        for (int i = 0; i < states.getCount(); i++) {
279            LIRFrameState state = (LIRFrameState) states.getObject(obj, i);
280            if (state != null) {
281                proc.doState(obj, state);
282            }
283        }
284    }
285
286    final Value forEachRegisterHint(LIRInstruction obj, OperandMode mode, InstructionValueProcedure proc) {
287        Values hints;
288        if (mode == OperandMode.USE) {
289            hints = defs;
290        } else if (mode == OperandMode.DEF) {
291            hints = uses;
292        } else {
293            return null;
294        }
295
296        for (int i = 0; i < hints.getCount(); i++) {
297            if (i < hints.getDirectCount()) {
298                Value hintValue = hints.getValue(obj, i);
299                Value result = proc.doValue(obj, hintValue, null, null);
300                if (result != null) {
301                    return result;
302                }
303            } else {
304                Value[] hintValues = hints.getValueArray(obj, i);
305                for (int j = 0; j < hintValues.length; j++) {
306                    Value hintValue = hintValues[j];
307                    Value result = proc.doValue(obj, hintValue, null, null);
308                    if (result != null) {
309                        return result;
310                    }
311                }
312            }
313        }
314        return null;
315    }
316
317    String toString(LIRInstruction obj) {
318        StringBuilder result = new StringBuilder();
319
320        appendValues(result, obj, "", " = ", "(", ")", new String[]{""}, defs);
321        result.append(String.valueOf(getOpcode(obj)).toUpperCase());
322        appendValues(result, obj, " ", "", "(", ")", new String[]{"", "~"}, uses, alives);
323        appendValues(result, obj, " ", "", "{", "}", new String[]{""}, temps);
324
325        for (int i = 0; i < data.getCount(); i++) {
326            if (i == opcodeIndex) {
327                continue;
328            }
329            result.append(" ").append(data.getName(i)).append(": ").append(getFieldString(obj, i, data));
330        }
331
332        for (int i = 0; i < states.getCount(); i++) {
333            LIRFrameState state = (LIRFrameState) states.getObject(obj, i);
334            if (state != null) {
335                result.append(" ").append(states.getName(i)).append(" [bci:");
336                String sep = "";
337                for (BytecodeFrame cur = state.topFrame; cur != null; cur = cur.caller()) {
338                    result.append(sep).append(cur.getBCI());
339                    sep = ", ";
340                }
341                result.append("]");
342            }
343        }
344
345        return result.toString();
346    }
347}