1/*
2 * Copyright (c) 2012, 2015, 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 static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.CONST;
26import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
27import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.STACK;
28
29import java.lang.annotation.Annotation;
30import java.lang.reflect.Field;
31import java.lang.reflect.Modifier;
32import java.util.ArrayList;
33import java.util.Arrays;
34import java.util.EnumSet;
35import java.util.HashMap;
36import java.util.Map;
37import java.util.Map.Entry;
38
39import org.graalvm.compiler.core.common.FieldIntrospection;
40import org.graalvm.compiler.core.common.Fields;
41import org.graalvm.compiler.core.common.FieldsScanner;
42import org.graalvm.compiler.lir.LIRInstruction.OperandFlag;
43import org.graalvm.compiler.lir.LIRInstruction.OperandMode;
44
45import jdk.vm.ci.code.RegisterValue;
46import jdk.vm.ci.code.StackSlot;
47import jdk.vm.ci.meta.Value;
48
49abstract class LIRIntrospection<T> extends FieldIntrospection<T> {
50
51    private static final Class<Value> VALUE_CLASS = Value.class;
52    private static final Class<ConstantValue> CONSTANT_VALUE_CLASS = ConstantValue.class;
53    private static final Class<Variable> VARIABLE_CLASS = Variable.class;
54    private static final Class<RegisterValue> REGISTER_VALUE_CLASS = RegisterValue.class;
55    private static final Class<StackSlot> STACK_SLOT_CLASS = StackSlot.class;
56    private static final Class<Value[]> VALUE_ARRAY_CLASS = Value[].class;
57
58    LIRIntrospection(Class<T> clazz) {
59        super(clazz);
60    }
61
62    protected static class Values extends Fields {
63        private final int directCount;
64        private final EnumSet<OperandFlag>[] flags;
65
66        public Values(OperandModeAnnotation mode) {
67            this(mode.directCount, mode.values);
68        }
69
70        @SuppressWarnings({"unchecked"})
71        public Values(int directCount, ArrayList<ValueFieldInfo> fields) {
72            super(fields);
73            this.directCount = directCount;
74            flags = (EnumSet<OperandFlag>[]) new EnumSet<?>[fields.size()];
75            for (int i = 0; i < fields.size(); i++) {
76                flags[i] = fields.get(i).flags;
77            }
78        }
79
80        public int getDirectCount() {
81            return directCount;
82        }
83
84        public EnumSet<OperandFlag> getFlags(int i) {
85            return flags[i];
86        }
87
88        protected Value getValue(Object obj, int index) {
89            return (Value) getObject(obj, index);
90        }
91
92        protected void setValue(Object obj, int index, Value value) {
93            putObject(obj, index, value);
94        }
95
96        protected Value[] getValueArray(Object obj, int index) {
97            return (Value[]) getObject(obj, index);
98        }
99
100        protected void setValueArray(Object obj, int index, Value[] valueArray) {
101            putObject(obj, index, valueArray);
102        }
103    }
104
105    /**
106     * The component values in an {@link LIRInstruction} or {@link CompositeValue}.
107     */
108    protected Values values;
109
110    protected static class ValueFieldInfo extends FieldsScanner.FieldInfo {
111
112        final EnumSet<OperandFlag> flags;
113
114        public ValueFieldInfo(long offset, String name, Class<?> type, Class<?> declaringClass, EnumSet<OperandFlag> flags) {
115            super(offset, name, type, declaringClass);
116            assert VALUE_ARRAY_CLASS.isAssignableFrom(type) || VALUE_CLASS.isAssignableFrom(type);
117            this.flags = flags;
118        }
119
120        /**
121         * Sorts non-array fields before array fields.
122         */
123        @Override
124        public int compareTo(FieldsScanner.FieldInfo o) {
125            if (VALUE_ARRAY_CLASS.isAssignableFrom(o.type)) {
126                if (!VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
127                    return -1;
128                }
129            } else {
130                if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
131                    return 1;
132                }
133            }
134            return super.compareTo(o);
135        }
136
137        @Override
138        public String toString() {
139            return super.toString() + flags;
140        }
141    }
142
143    protected static class OperandModeAnnotation {
144
145        /**
146         * Number of non-array fields in {@link #values}.
147         */
148        public int directCount;
149        public final ArrayList<ValueFieldInfo> values = new ArrayList<>();
150    }
151
152    protected abstract static class LIRFieldsScanner extends FieldsScanner {
153
154        public final Map<Class<? extends Annotation>, OperandModeAnnotation> valueAnnotations;
155        public final ArrayList<FieldsScanner.FieldInfo> states = new ArrayList<>();
156
157        public LIRFieldsScanner(FieldsScanner.CalcOffset calc) {
158            super(calc);
159            valueAnnotations = new HashMap<>();
160        }
161
162        protected OperandModeAnnotation getOperandModeAnnotation(Field field) {
163            OperandModeAnnotation result = null;
164            for (Entry<Class<? extends Annotation>, OperandModeAnnotation> entry : valueAnnotations.entrySet()) {
165                Annotation annotation = field.getAnnotation(entry.getKey());
166                if (annotation != null) {
167                    assert result == null : "Field has two operand mode annotations: " + field;
168                    result = entry.getValue();
169                }
170            }
171            return result;
172        }
173
174        protected abstract EnumSet<OperandFlag> getFlags(Field field);
175
176        @Override
177        protected void scanField(Field field, long offset) {
178            Class<?> type = field.getType();
179            if (VALUE_CLASS.isAssignableFrom(type) && !CONSTANT_VALUE_CLASS.isAssignableFrom(type)) {
180                assert !Modifier.isFinal(field.getModifiers()) : "Value field must not be declared final because it is modified by register allocator: " + field;
181                OperandModeAnnotation annotation = getOperandModeAnnotation(field);
182                assert annotation != null : "Field must have operand mode annotation: " + field;
183                EnumSet<OperandFlag> flags = getFlags(field);
184                assert verifyFlags(field, type, flags);
185                annotation.values.add(new ValueFieldInfo(offset, field.getName(), type, field.getDeclaringClass(), flags));
186                annotation.directCount++;
187            } else if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
188                OperandModeAnnotation annotation = getOperandModeAnnotation(field);
189                assert annotation != null : "Field must have operand mode annotation: " + field;
190                EnumSet<OperandFlag> flags = getFlags(field);
191                assert verifyFlags(field, type.getComponentType(), flags);
192                annotation.values.add(new ValueFieldInfo(offset, field.getName(), type, field.getDeclaringClass(), flags));
193            } else {
194                assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field;
195                assert field.getAnnotation(LIRInstruction.State.class) == null : "Field must not have state annotation: " + field;
196                super.scanField(field, offset);
197            }
198        }
199
200        private static boolean verifyFlags(Field field, Class<?> type, EnumSet<OperandFlag> flags) {
201            if (flags.contains(REG)) {
202                assert type.isAssignableFrom(REGISTER_VALUE_CLASS) || type.isAssignableFrom(VARIABLE_CLASS) : "Cannot assign RegisterValue / Variable to field with REG flag:" + field;
203            }
204            if (flags.contains(STACK)) {
205                assert type.isAssignableFrom(STACK_SLOT_CLASS) : "Cannot assign StackSlot to field with STACK flag:" + field;
206            }
207            if (flags.contains(CONST)) {
208                assert type.isAssignableFrom(CONSTANT_VALUE_CLASS) : "Cannot assign Constant to field with CONST flag:" + field;
209            }
210            return true;
211        }
212    }
213
214    protected static void forEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueProcedure proc) {
215        for (int i = 0; i < values.getCount(); i++) {
216            assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i));
217
218            if (i < values.getDirectCount()) {
219                Value value = values.getValue(inst, i);
220                Value newValue;
221                if (value instanceof CompositeValue) {
222                    CompositeValue composite = (CompositeValue) value;
223                    newValue = composite.forEachComponent(inst, mode, proc);
224                } else {
225                    newValue = proc.doValue(inst, value, mode, values.getFlags(i));
226                }
227                if (!value.identityEquals(newValue)) {
228                    values.setValue(inst, i, newValue);
229                }
230            } else {
231                Value[] valueArray = values.getValueArray(inst, i);
232                for (int j = 0; j < valueArray.length; j++) {
233                    Value value = valueArray[j];
234                    Value newValue;
235                    if (value instanceof CompositeValue) {
236                        CompositeValue composite = (CompositeValue) value;
237                        newValue = composite.forEachComponent(inst, mode, proc);
238                    } else {
239                        newValue = proc.doValue(inst, value, mode, values.getFlags(i));
240                    }
241                    if (!value.identityEquals(newValue)) {
242                        valueArray[j] = newValue;
243                    }
244                }
245            }
246        }
247    }
248
249    protected static void visitEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueConsumer proc) {
250        for (int i = 0; i < values.getCount(); i++) {
251            assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i));
252
253            if (i < values.getDirectCount()) {
254                Value value = values.getValue(inst, i);
255                if (value instanceof CompositeValue) {
256                    CompositeValue composite = (CompositeValue) value;
257                    composite.visitEachComponent(inst, mode, proc);
258                } else {
259                    proc.visitValue(inst, value, mode, values.getFlags(i));
260                }
261            } else {
262                Value[] valueArray = values.getValueArray(inst, i);
263                for (int j = 0; j < valueArray.length; j++) {
264                    Value value = valueArray[j];
265                    if (value instanceof CompositeValue) {
266                        CompositeValue composite = (CompositeValue) value;
267                        composite.visitEachComponent(inst, mode, proc);
268                    } else {
269                        proc.visitValue(inst, value, mode, values.getFlags(i));
270                    }
271                }
272            }
273        }
274    }
275
276    protected static void appendValues(StringBuilder sb, Object obj, String start, String end, String startMultiple, String endMultiple, String[] prefix, Fields... fieldsList) {
277        int total = 0;
278        for (Fields fields : fieldsList) {
279            total += fields.getCount();
280        }
281        if (total == 0) {
282            return;
283        }
284
285        sb.append(start);
286        if (total > 1) {
287            sb.append(startMultiple);
288        }
289        String sep = "";
290        int i = 0;
291        for (Fields fields : fieldsList) {
292            for (int j = 0; j < fields.getCount(); j++) {
293                sb.append(sep).append(prefix[i]);
294                if (total > 1) {
295                    sb.append(fields.getName(j)).append(": ");
296                }
297                sb.append(getFieldString(obj, j, fields));
298                sep = ", ";
299            }
300            i++;
301        }
302        if (total > 1) {
303            sb.append(endMultiple);
304        }
305        sb.append(end);
306    }
307
308    protected static String getFieldString(Object obj, int index, Fields fields) {
309        Object value = fields.get(obj, index);
310        Class<?> type = fields.getType(index);
311        if (value == null || type.isPrimitive() || !type.isArray()) {
312            return String.valueOf(value);
313        }
314        if (type == int[].class) {
315            return Arrays.toString((int[]) value);
316        } else if (type == double[].class) {
317            return Arrays.toString((double[]) value);
318        } else if (type == byte[].class) {
319            byte[] byteValue = (byte[]) value;
320            if (isPrintableAsciiString(byteValue)) {
321                return toString(byteValue);
322            } else {
323                return Arrays.toString(byteValue);
324            }
325        } else if (!type.getComponentType().isPrimitive()) {
326            return Arrays.toString((Object[]) value);
327        }
328        assert false : "unhandled field type: " + type;
329        return "";
330    }
331
332    /**
333     * Tests if all values in this string are printable ASCII characters or value \0 (b in
334     * [0x20,0x7F]) or b == 0.
335     *
336     * @param array
337     * @return true if there are only printable ASCII characters and \0, false otherwise
338     */
339    private static boolean isPrintableAsciiString(byte[] array) {
340        for (byte b : array) {
341            char c = (char) b;
342            if (c != 0 && c < 0x20 && c > 0x7F) {
343                return false;
344            }
345        }
346        return true;
347    }
348
349    private static String toString(byte[] bytes) {
350        StringBuilder sb = new StringBuilder();
351        sb.append('"');
352        for (byte b : bytes) {
353            if (b == 0) {
354                sb.append("\\0");
355            } else if (b == '"') {
356                sb.append("\\\"");
357            } else if (b == '\n') {
358                sb.append("\\n");
359            } else {
360                sb.append((char) b);
361            }
362        }
363        sb.append('"');
364        return sb.toString();
365    }
366}
367