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