001/*
002 * Copyright (c) 2012, 2014, 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 static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
026
027import java.lang.annotation.*;
028import java.lang.reflect.*;
029import java.util.*;
030import java.util.Map.Entry;
031
032import jdk.internal.jvmci.code.*;
033import jdk.internal.jvmci.meta.*;
034
035import com.oracle.graal.compiler.common.*;
036import com.oracle.graal.lir.LIRInstruction.OperandFlag;
037import com.oracle.graal.lir.LIRInstruction.OperandMode;
038
039abstract class LIRIntrospection<T> extends FieldIntrospection<T> {
040
041    private static final Class<Value> VALUE_CLASS = Value.class;
042    private static final Class<JavaConstant> CONSTANT_CLASS = JavaConstant.class;
043    private static final Class<Variable> VARIABLE_CLASS = Variable.class;
044    private static final Class<RegisterValue> REGISTER_VALUE_CLASS = RegisterValue.class;
045    private static final Class<StackSlot> STACK_SLOT_CLASS = StackSlot.class;
046    private static final Class<Value[]> VALUE_ARRAY_CLASS = Value[].class;
047
048    public LIRIntrospection(Class<T> clazz) {
049        super(clazz);
050    }
051
052    protected static class Values extends Fields {
053        private final int directCount;
054        private final EnumSet<OperandFlag>[] flags;
055
056        public Values(OperandModeAnnotation mode) {
057            this(mode.directCount, mode.values);
058        }
059
060        @SuppressWarnings("unchecked")
061        public Values(int directCount, ArrayList<ValueFieldInfo> fields) {
062            super(fields);
063            this.directCount = directCount;
064            flags = new EnumSet[fields.size()];
065            for (int i = 0; i < fields.size(); i++) {
066                flags[i] = fields.get(i).flags;
067            }
068        }
069
070        public int getDirectCount() {
071            return directCount;
072        }
073
074        public EnumSet<OperandFlag> getFlags(int i) {
075            return flags[i];
076        }
077
078        protected Value getValue(Object obj, int index) {
079            return (Value) getObject(obj, index);
080        }
081
082        protected void setValue(Object obj, int index, Value value) {
083            putObject(obj, index, value);
084        }
085
086        protected Value[] getValueArray(Object obj, int index) {
087            return (Value[]) getObject(obj, index);
088        }
089
090        protected void setValueArray(Object obj, int index, Value[] valueArray) {
091            putObject(obj, index, valueArray);
092        }
093    }
094
095    /**
096     * The component values in an {@link LIRInstruction} or {@link CompositeValue}.
097     */
098    protected Values values;
099
100    protected static class ValueFieldInfo extends FieldsScanner.FieldInfo {
101
102        final EnumSet<OperandFlag> flags;
103
104        public ValueFieldInfo(long offset, String name, Class<?> type, Class<?> declaringClass, EnumSet<OperandFlag> flags) {
105            super(offset, name, type, declaringClass);
106            assert VALUE_ARRAY_CLASS.isAssignableFrom(type) || VALUE_CLASS.isAssignableFrom(type);
107            this.flags = flags;
108        }
109
110        /**
111         * Sorts non-array fields before array fields.
112         */
113        @Override
114        public int compareTo(FieldsScanner.FieldInfo o) {
115            if (VALUE_ARRAY_CLASS.isAssignableFrom(o.type)) {
116                if (!VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
117                    return -1;
118                }
119            } else {
120                if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
121                    return 1;
122                }
123            }
124            return super.compareTo(o);
125        }
126
127        @Override
128        public String toString() {
129            return super.toString() + flags;
130        }
131    }
132
133    protected static class OperandModeAnnotation {
134
135        /**
136         * Number of non-array fields in {@link #values}.
137         */
138        public int directCount;
139        public final ArrayList<ValueFieldInfo> values = new ArrayList<>();
140    }
141
142    protected abstract static class LIRFieldsScanner extends FieldsScanner {
143
144        public final Map<Class<? extends Annotation>, OperandModeAnnotation> valueAnnotations;
145        public final ArrayList<FieldsScanner.FieldInfo> states = new ArrayList<>();
146
147        public LIRFieldsScanner(FieldsScanner.CalcOffset calc) {
148            super(calc);
149            valueAnnotations = new HashMap<>();
150        }
151
152        protected OperandModeAnnotation getOperandModeAnnotation(Field field) {
153            OperandModeAnnotation result = null;
154            for (Entry<Class<? extends Annotation>, OperandModeAnnotation> entry : valueAnnotations.entrySet()) {
155                Annotation annotation = field.getAnnotation(entry.getKey());
156                if (annotation != null) {
157                    assert result == null : "Field has two operand mode annotations: " + field;
158                    result = entry.getValue();
159                }
160            }
161            return result;
162        }
163
164        protected abstract EnumSet<OperandFlag> getFlags(Field field);
165
166        @Override
167        protected void scanField(Field field, long offset) {
168            Class<?> type = field.getType();
169            if (VALUE_CLASS.isAssignableFrom(type) && !CONSTANT_CLASS.isAssignableFrom(type)) {
170                assert !Modifier.isFinal(field.getModifiers()) : "Value field must not be declared final because it is modified by register allocator: " + field;
171                OperandModeAnnotation annotation = getOperandModeAnnotation(field);
172                assert annotation != null : "Field must have operand mode annotation: " + field;
173                EnumSet<OperandFlag> flags = getFlags(field);
174                assert verifyFlags(field, type, flags);
175                annotation.values.add(new ValueFieldInfo(offset, field.getName(), type, field.getDeclaringClass(), flags));
176                annotation.directCount++;
177            } else if (VALUE_ARRAY_CLASS.isAssignableFrom(type)) {
178                OperandModeAnnotation annotation = getOperandModeAnnotation(field);
179                assert annotation != null : "Field must have operand mode annotation: " + field;
180                EnumSet<OperandFlag> flags = getFlags(field);
181                assert verifyFlags(field, type.getComponentType(), flags);
182                annotation.values.add(new ValueFieldInfo(offset, field.getName(), type, field.getDeclaringClass(), flags));
183            } else {
184                assert getOperandModeAnnotation(field) == null : "Field must not have operand mode annotation: " + field;
185                assert field.getAnnotation(LIRInstruction.State.class) == null : "Field must not have state annotation: " + field;
186                super.scanField(field, offset);
187            }
188        }
189
190        private static boolean verifyFlags(Field field, Class<?> type, EnumSet<OperandFlag> flags) {
191            if (flags.contains(REG)) {
192                assert type.isAssignableFrom(REGISTER_VALUE_CLASS) || type.isAssignableFrom(VARIABLE_CLASS) : "Cannot assign RegisterValue / Variable to field with REG flag:" + field;
193            }
194            if (flags.contains(STACK)) {
195                assert type.isAssignableFrom(STACK_SLOT_CLASS) : "Cannot assign StackSlot to field with STACK flag:" + field;
196            }
197            if (flags.contains(CONST)) {
198                assert type.isAssignableFrom(CONSTANT_CLASS) : "Cannot assign Constant to field with CONST flag:" + field;
199            }
200            return true;
201        }
202    }
203
204    protected static void forEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueProcedure proc) {
205        for (int i = 0; i < values.getCount(); i++) {
206            assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i));
207
208            if (i < values.getDirectCount()) {
209                Value value = values.getValue(inst, i);
210                Value newValue;
211                if (value instanceof CompositeValue) {
212                    CompositeValue composite = (CompositeValue) value;
213                    newValue = composite.forEachComponent(inst, mode, proc);
214                } else {
215                    newValue = proc.doValue(inst, value, mode, values.getFlags(i));
216                }
217                if (!value.identityEquals(newValue)) {
218                    values.setValue(inst, i, newValue);
219                }
220            } else {
221                Value[] valueArray = values.getValueArray(inst, i);
222                for (int j = 0; j < valueArray.length; j++) {
223                    Value value = valueArray[j];
224                    Value newValue;
225                    if (value instanceof CompositeValue) {
226                        CompositeValue composite = (CompositeValue) value;
227                        newValue = composite.forEachComponent(inst, mode, proc);
228                    } else {
229                        newValue = proc.doValue(inst, value, mode, values.getFlags(i));
230                    }
231                    if (!value.identityEquals(newValue)) {
232                        valueArray[j] = newValue;
233                    }
234                }
235            }
236        }
237    }
238
239    protected static void forEach(LIRInstruction inst, Values values, OperandMode mode, InstructionValueConsumer proc) {
240        for (int i = 0; i < values.getCount(); i++) {
241            assert LIRInstruction.ALLOWED_FLAGS.get(mode).containsAll(values.getFlags(i));
242
243            if (i < values.getDirectCount()) {
244                Value value = values.getValue(inst, i);
245                if (value instanceof CompositeValue) {
246                    CompositeValue composite = (CompositeValue) value;
247                    composite.forEachComponent(inst, mode, proc);
248                } else {
249                    proc.visitValue(inst, value, mode, values.getFlags(i));
250                }
251            } else {
252                Value[] valueArray = values.getValueArray(inst, i);
253                for (int j = 0; j < valueArray.length; j++) {
254                    Value value = valueArray[j];
255                    if (value instanceof CompositeValue) {
256                        CompositeValue composite = (CompositeValue) value;
257                        composite.forEachComponent(inst, mode, proc);
258                    } else {
259                        proc.visitValue(inst, value, mode, values.getFlags(i));
260                    }
261                }
262            }
263        }
264    }
265
266    protected static void appendValues(StringBuilder sb, Object obj, String start, String end, String startMultiple, String endMultiple, String[] prefix, Fields... fieldsList) {
267        int total = 0;
268        for (Fields fields : fieldsList) {
269            total += fields.getCount();
270        }
271        if (total == 0) {
272            return;
273        }
274
275        sb.append(start);
276        if (total > 1) {
277            sb.append(startMultiple);
278        }
279        String sep = "";
280        int i = 0;
281        for (Fields fields : fieldsList) {
282            for (int j = 0; j < fields.getCount(); j++) {
283                sb.append(sep).append(prefix[i]);
284                if (total > 1) {
285                    sb.append(fields.getName(j)).append(": ");
286                }
287                sb.append(getFieldString(obj, j, fields));
288                sep = ", ";
289            }
290            i++;
291        }
292        if (total > 1) {
293            sb.append(endMultiple);
294        }
295        sb.append(end);
296    }
297
298    protected static String getFieldString(Object obj, int index, Fields fields) {
299        Object value = fields.get(obj, index);
300        Class<?> type = fields.getType(index);
301        if (value == null || type.isPrimitive() || !type.isArray()) {
302            return String.valueOf(value);
303        }
304        if (type == int[].class) {
305            return Arrays.toString((int[]) value);
306        } else if (type == double[].class) {
307            return Arrays.toString((double[]) value);
308        } else if (type == byte[].class) {
309            byte[] byteValue = (byte[]) value;
310            if (isPrintableAsciiString(byteValue)) {
311                return toString(byteValue);
312            } else {
313                return Arrays.toString(byteValue);
314            }
315        } else if (!type.getComponentType().isPrimitive()) {
316            return Arrays.toString((Object[]) value);
317        }
318        assert false : "unhandled field type: " + type;
319        return "";
320    }
321
322    /**
323     * Tests if all values in this string are printable ASCII characters or value \0 (b in
324     * [0x20,0x7F]) or b == 0.
325     *
326     * @param array
327     * @return true if there are only printable ASCII characters and \0, false otherwise
328     */
329    private static boolean isPrintableAsciiString(byte[] array) {
330        for (byte b : array) {
331            char c = (char) b;
332            if (c != 0 && c < 0x20 && c > 0x7F) {
333                return false;
334            }
335        }
336        return true;
337    }
338
339    private static String toString(byte[] bytes) {
340        StringBuilder sb = new StringBuilder();
341        sb.append('"');
342        for (byte b : bytes) {
343            if (b == 0) {
344                sb.append("\\0");
345            } else if (b == '"') {
346                sb.append("\\\"");
347            } else if (b == '\n') {
348                sb.append("\\n");
349            } else {
350                sb.append((char) b);
351            }
352        }
353        sb.append('"');
354        return sb.toString();
355    }
356}