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