1 /*
2  * Copyright (c) 2010, 2016, 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 package jdk.vm.ci.code;
24 
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Map;
29 
30 import jdk.vm.ci.meta.JavaType;
31 import jdk.vm.ci.meta.MetaUtil;
32 import jdk.vm.ci.meta.ResolvedJavaMethod;
33 import jdk.vm.ci.meta.Signature;
34 
35 /**
36  * Miscellaneous collection of utility methods used by {@code jdk.vm.ci.code} and its clients.
37  */
38 public class CodeUtil {
39 
40     public static final String NEW_LINE = String.format("%n");
41 
42     public static final int K = 1024;
43     public static final int M = 1024 * 1024;
44 
isOdd(int n)45     public static boolean isOdd(int n) {
46         return (n & 1) == 1;
47     }
48 
isEven(int n)49     public static boolean isEven(int n) {
50         return (n & 1) == 0;
51     }
52 
53     /**
54      * Checks whether the specified integer is a power of two.
55      *
56      * @param val the value to check
57      * @return {@code true} if the value is a power of two; {@code false} otherwise
58      */
isPowerOf2(int val)59     public static boolean isPowerOf2(int val) {
60         return val > 0 && (val & val - 1) == 0;
61     }
62 
63     /**
64      * Checks whether the specified long is a power of two.
65      *
66      * @param val the value to check
67      * @return {@code true} if the value is a power of two; {@code false} otherwise
68      */
isPowerOf2(long val)69     public static boolean isPowerOf2(long val) {
70         return val > 0 && (val & val - 1) == 0;
71     }
72 
73     /**
74      * Computes the log (base 2) of the specified integer, rounding down. (E.g {@code log2(8) = 3},
75      * {@code log2(21) = 4} )
76      *
77      * @param val the value
78      * @return the log base 2 of the value
79      */
log2(int val)80     public static int log2(int val) {
81         assert val > 0;
82         return (Integer.SIZE - 1) - Integer.numberOfLeadingZeros(val);
83     }
84 
85     /**
86      * Computes the log (base 2) of the specified long, rounding down. (E.g {@code log2(8) = 3},
87      * {@code log2(21) = 4})
88      *
89      * @param val the value
90      * @return the log base 2 of the value
91      */
log2(long val)92     public static int log2(long val) {
93         assert val > 0;
94         return (Long.SIZE - 1) - Long.numberOfLeadingZeros(val);
95     }
96 
97     /**
98      * Narrow an integer value to a given bit width, and return the result as a signed long.
99      *
100      * @param value the value
101      * @param resultBits the result bit width
102      * @return {@code value} interpreted as {@code resultBits} bit number, encoded as signed long
103      */
narrow(long value, int resultBits)104     public static long narrow(long value, int resultBits) {
105         long ret = value & mask(resultBits);
106         return signExtend(ret, resultBits);
107     }
108 
109     /**
110      * Sign extend an integer.
111      *
112      * @param value the input value
113      * @param inputBits the bit width of the input value
114      * @return a signed long with the same value as the signed {@code inputBits}-bit number
115      *         {@code value}
116      */
signExtend(long value, int inputBits)117     public static long signExtend(long value, int inputBits) {
118         if (inputBits < 64) {
119             if ((value >>> (inputBits - 1) & 1) == 1) {
120                 return value | (-1L << inputBits);
121             } else {
122                 return value & ~(-1L << inputBits);
123             }
124         } else {
125             return value;
126         }
127     }
128 
129     /**
130      * Zero extend an integer.
131      *
132      * @param value the input value
133      * @param inputBits the bit width of the input value
134      * @return an unsigned long with the same value as the unsigned {@code inputBits}-bit number
135      *         {@code value}
136      */
zeroExtend(long value, int inputBits)137     public static long zeroExtend(long value, int inputBits) {
138         if (inputBits < 64) {
139             return value & ~(-1L << inputBits);
140         } else {
141             return value;
142         }
143     }
144 
145     /**
146      * Convert an integer to long.
147      *
148      * @param value the input value
149      * @param inputBits the bit width of the input value
150      * @param unsigned whether the values should be interpreted as signed or unsigned
151      * @return a long with the same value as the {@code inputBits}-bit number {@code value}
152      */
convert(long value, int inputBits, boolean unsigned)153     public static long convert(long value, int inputBits, boolean unsigned) {
154         if (unsigned) {
155             return zeroExtend(value, inputBits);
156         } else {
157             return signExtend(value, inputBits);
158         }
159     }
160 
161     /**
162      * Get a bitmask with the low {@code bits} bit set and the high {@code 64 - bits} bit clear.
163      */
mask(int bits)164     public static long mask(int bits) {
165         assert 0 <= bits && bits <= 64;
166         if (bits == 64) {
167             return 0xffffffffffffffffL;
168         } else {
169             return (1L << bits) - 1;
170         }
171     }
172 
173     /**
174      * Get the minimum value representable in a {@code bits} bit signed integer.
175      */
minValue(int bits)176     public static long minValue(int bits) {
177         assert 0 < bits && bits <= 64;
178         return -1L << (bits - 1);
179     }
180 
181     /**
182      * Get the maximum value representable in a {@code bits} bit signed integer.
183      */
184     public static long maxValue(int bits) {
185         assert 0 < bits && bits <= 64;
186         return mask(bits - 1);
187     }
188 
189     /**
190      * Formats the values in a frame as a tabulated string.
191      *
192      * @param frame
193      * @return the values in {@code frame} as a tabulated string
194      */
195     public static String tabulateValues(BytecodeFrame frame) {
196         int cols = Math.max(frame.numLocals, Math.max(frame.numStack, frame.numLocks));
197         assert cols > 0;
198         ArrayList<Object> cells = new ArrayList<>();
199         cells.add("");
200         for (int i = 0; i < cols; i++) {
201             cells.add(i);
202         }
203         cols++;
204         if (frame.numLocals != 0) {
205             cells.add("locals:");
206             cells.addAll(Arrays.asList(frame.values).subList(0, frame.numLocals));
207             cells.addAll(Collections.nCopies(cols - frame.numLocals - 1, ""));
208         }
209         if (frame.numStack != 0) {
210             cells.add("stack:");
211             cells.addAll(Arrays.asList(frame.values).subList(frame.numLocals, frame.numLocals + frame.numStack));
212             cells.addAll(Collections.nCopies(cols - frame.numStack - 1, ""));
213         }
214         if (frame.numLocks != 0) {
215             cells.add("locks:");
216             cells.addAll(Arrays.asList(frame.values).subList(frame.numLocals + frame.numStack, frame.values.length));
217             cells.addAll(Collections.nCopies(cols - frame.numLocks - 1, ""));
218         }
219         Object[] cellArray = cells.toArray();
220         for (int i = 0; i < cellArray.length; i++) {
221             if ((i % cols) != 0) {
222                 cellArray[i] = "|" + cellArray[i];
223             }
224         }
225         return CodeUtil.tabulate(cellArray, cols, 1, 1);
226     }
227 
228     /**
229      * Formats a given table as a string. The value of each cell is produced by
230      * {@link String#valueOf(Object)}.
231      *
232      * @param cells the cells of the table in row-major order
233      * @param cols the number of columns per row
234      * @param lpad the number of space padding inserted before each formatted cell value
235      * @param rpad the number of space padding inserted after each formatted cell value
236      * @return a string with one line per row and each column left-aligned
237      */
238     public static String tabulate(Object[] cells, int cols, int lpad, int rpad) {
239         int rows = (cells.length + (cols - 1)) / cols;
240         int[] colWidths = new int[cols];
241         for (int col = 0; col < cols; col++) {
242             for (int row = 0; row < rows; row++) {
243                 int index = col + (row * cols);
244                 if (index < cells.length) {
245                     Object cell = cells[index];
246                     colWidths[col] = Math.max(colWidths[col], String.valueOf(cell).length());
247                 }
248             }
249         }
250         StringBuilder sb = new StringBuilder();
251         String nl = NEW_LINE;
252         for (int row = 0; row < rows; row++) {
253             for (int col = 0; col < cols; col++) {
254                 int index = col + (row * cols);
255                 if (index < cells.length) {
256                     for (int i = 0; i < lpad; i++) {
257                         sb.append(' ');
258                     }
259                     Object cell = cells[index];
260                     String s = String.valueOf(cell);
261                     int w = s.length();
262                     sb.append(s);
263                     while (w < colWidths[col]) {
264                         sb.append(' ');
265                         w++;
266                     }
267                     for (int i = 0; i < rpad; i++) {
268                         sb.append(' ');
269                     }
270                 }
271             }
272             sb.append(nl);
273         }
274         return sb.toString();
275     }
276 
277     /**
278      * Appends a formatted code position to a {@link StringBuilder}.
279      *
280      * @param sb the {@link StringBuilder} to append to
281      * @param pos the code position to format and append to {@code sb}
282      * @return the value of {@code sb}
283      */
284     public static StringBuilder append(StringBuilder sb, BytecodePosition pos) {
285         MetaUtil.appendLocation(sb.append("at "), pos.getMethod(), pos.getBCI());
286         if (pos.getCaller() != null) {
287             sb.append(NEW_LINE);
288             append(sb, pos.getCaller());
289         }
290         return sb;
291     }
292 
293     /**
294      * Appends a formatted frame to a {@link StringBuilder}.
295      *
296      * @param sb the {@link StringBuilder} to append to
297      * @param frame the frame to format and append to {@code sb}
298      * @return the value of {@code sb}
299      */
300     public static StringBuilder append(StringBuilder sb, BytecodeFrame frame) {
301         MetaUtil.appendLocation(sb.append("at "), frame.getMethod(), frame.getBCI());
302         assert sb.charAt(sb.length() - 1) == ']';
303         sb.deleteCharAt(sb.length() - 1);
304         sb.append(", duringCall: ").append(frame.duringCall).append(", rethrow: ").append(frame.rethrowException).append(']');
305         if (frame.values != null && frame.values.length > 0) {
306             sb.append(NEW_LINE);
307             String table = tabulateValues(frame);
308             String[] rows = table.split(NEW_LINE);
309             for (int i = 0; i < rows.length; i++) {
310                 String row = rows[i];
311                 if (!row.trim().isEmpty()) {
312                     sb.append("  ").append(row);
313                     if (i != rows.length - 1) {
314                         sb.append(NEW_LINE);
315                     }
316                 }
317             }
318         }
319         if (frame.caller() != null) {
320             sb.append(NEW_LINE);
321             append(sb, frame.caller());
322         } else if (frame.getCaller() != null) {
323             sb.append(NEW_LINE);
324             append(sb, frame.getCaller());
325         }
326         return sb;
327     }
328 
329     public interface RefMapFormatter {
330 
331         String formatStackSlot(int frameRefMapIndex);
332     }
333 
334     /**
335      * Formats a location present in a reference map.
336      */
337     public static class DefaultRefMapFormatter implements RefMapFormatter {
338 
339         /**
340          * The size of a stack slot.
341          */
342         public final int slotSize;
343 
344         /**
345          * The register used as the frame pointer.
346          */
347         public final Register fp;
348 
349         /**
350          * The offset (in bytes) from the slot pointed to by {@link #fp} to the slot corresponding
351          * to bit 0 in the frame reference map.
352          */
353         public final int refMapToFPOffset;
354 
355         public DefaultRefMapFormatter(int slotSize, Register fp, int refMapToFPOffset) {
356             this.slotSize = slotSize;
357             this.fp = fp;
358             this.refMapToFPOffset = refMapToFPOffset;
359         }
360 
361         @Override
362         public String formatStackSlot(int frameRefMapIndex) {
363             int refMapOffset = frameRefMapIndex * slotSize;
364             int fpOffset = refMapOffset + refMapToFPOffset;
365             if (fpOffset >= 0) {
366                 return fp + "+" + fpOffset;
367             }
368             return fp.name + fpOffset;
369         }
370     }
371 
372     public static class NumberedRefMapFormatter implements RefMapFormatter {
373 
374         @Override
375         public String formatStackSlot(int frameRefMapIndex) {
376             return "s" + frameRefMapIndex;
377         }
378 
379         public String formatRegister(int regRefMapIndex) {
380             return "r" + regRefMapIndex;
381         }
382     }
383 
384     /**
385      * Appends a formatted debug info to a {@link StringBuilder}.
386      *
387      * @param sb the {@link StringBuilder} to append to
388      * @param info the debug info to format and append to {@code sb}
389      * @return the value of {@code sb}
390      */
391     public static StringBuilder append(StringBuilder sb, DebugInfo info, RefMapFormatter formatterArg) {
392         RefMapFormatter formatter = formatterArg;
393         if (formatter == null) {
394             formatter = new NumberedRefMapFormatter();
395         }
396         String nl = NEW_LINE;
397         ReferenceMap refMap = info.getReferenceMap();
398         if (refMap != null) {
399             sb.append(refMap.toString());
400         }
401         RegisterSaveLayout calleeSaveInfo = info.getCalleeSaveInfo();
402         if (calleeSaveInfo != null) {
403             sb.append("callee-save-info:").append(nl);
404             Map<Integer, Register> map = calleeSaveInfo.slotsToRegisters(true);
405             for (Map.Entry<Integer, Register> e : map.entrySet()) {
406                 sb.append("    ").append(e.getValue()).append(" -> ").append(formatter.formatStackSlot(e.getKey())).append(nl);
407             }
408         }
409         BytecodeFrame frame = info.frame();
410         if (frame != null) {
411             append(sb, frame);
412         } else if (info.getBytecodePosition() != null) {
413             append(sb, info.getBytecodePosition());
414         }
415         return sb;
416     }
417 
418     /**
419      * Create a calling convention from a {@link ResolvedJavaMethod}.
420      */
421     public static CallingConvention getCallingConvention(CodeCacheProvider codeCache, CallingConvention.Type type, ResolvedJavaMethod method, ValueKindFactory<?> valueKindFactory) {
422         Signature sig = method.getSignature();
423         JavaType retType = sig.getReturnType(null);
424         int sigCount = sig.getParameterCount(false);
425         JavaType[] argTypes;
426         int argIndex = 0;
427         if (!method.isStatic()) {
428             argTypes = new JavaType[sigCount + 1];
429             argTypes[argIndex++] = method.getDeclaringClass();
430         } else {
431             argTypes = new JavaType[sigCount];
432         }
433         for (int i = 0; i < sigCount; i++) {
434             argTypes[argIndex++] = sig.getParameterType(i, null);
435         }
436 
437         RegisterConfig registerConfig = codeCache.getRegisterConfig();
438         return registerConfig.getCallingConvention(type, retType, argTypes, valueKindFactory);
439     }
440 }
441