22054
|
1 /*
|
|
2 * Copyright (c) 2009, 2014, 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.internal.jvmci.code;
|
|
24
|
|
25 import static java.util.Collections.*;
|
|
26 import static jdk.internal.jvmci.meta.MetaUtil.*;
|
|
27
|
|
28 import java.util.*;
|
|
29
|
|
30 import jdk.internal.jvmci.meta.*;
|
|
31 import jdk.internal.jvmci.meta.Assumptions.*;
|
|
32
|
|
33 /**
|
|
34 * Represents the output from compiling a method, including the compiled machine code, associated
|
|
35 * data and references, relocation information, deoptimization information, etc.
|
|
36 */
|
|
37 public class CompilationResult {
|
|
38
|
|
39 /**
|
|
40 * Represents a code position with associated additional information.
|
|
41 */
|
|
42 public abstract static class Site {
|
|
43
|
|
44 /**
|
|
45 * The position (or offset) of this site with respect to the start of the target method.
|
|
46 */
|
|
47 public final int pcOffset;
|
|
48
|
|
49 public Site(int pos) {
|
|
50 this.pcOffset = pos;
|
|
51 }
|
|
52
|
|
53 @Override
|
|
54 public final int hashCode() {
|
|
55 throw new UnsupportedOperationException("hashCode");
|
|
56 }
|
|
57
|
|
58 @Override
|
|
59 public String toString() {
|
|
60 return identityHashCodeString(this);
|
|
61 }
|
|
62
|
|
63 @Override
|
|
64 public abstract boolean equals(Object obj);
|
|
65 }
|
|
66
|
|
67 /**
|
|
68 * Represents an infopoint with associated debug info. Note that safepoints are also infopoints.
|
|
69 */
|
|
70 public static class Infopoint extends Site implements Comparable<Infopoint> {
|
|
71
|
|
72 public final DebugInfo debugInfo;
|
|
73
|
|
74 public final InfopointReason reason;
|
|
75
|
|
76 public Infopoint(int pcOffset, DebugInfo debugInfo, InfopointReason reason) {
|
|
77 super(pcOffset);
|
|
78 this.debugInfo = debugInfo;
|
|
79 this.reason = reason;
|
|
80 }
|
|
81
|
|
82 @Override
|
|
83 public String toString() {
|
|
84 StringBuilder sb = new StringBuilder();
|
|
85 sb.append(pcOffset);
|
|
86 sb.append("[<infopoint>]");
|
|
87 appendDebugInfo(sb, debugInfo);
|
|
88 return sb.toString();
|
|
89 }
|
|
90
|
|
91 @Override
|
|
92 public int compareTo(Infopoint o) {
|
|
93 if (pcOffset < o.pcOffset) {
|
|
94 return -1;
|
|
95 } else if (pcOffset > o.pcOffset) {
|
|
96 return 1;
|
|
97 }
|
|
98 return this.reason.compareTo(o.reason);
|
|
99 }
|
|
100
|
|
101 @Override
|
|
102 public boolean equals(Object obj) {
|
|
103 if (this == obj) {
|
|
104 return true;
|
|
105 }
|
|
106 if (obj != null && obj.getClass() == getClass()) {
|
|
107 Infopoint that = (Infopoint) obj;
|
|
108 if (this.pcOffset == that.pcOffset && Objects.equals(this.debugInfo, that.debugInfo) && Objects.equals(this.reason, that.reason)) {
|
|
109 return true;
|
|
110 }
|
|
111 }
|
|
112 return false;
|
|
113 }
|
|
114 }
|
|
115
|
|
116 /**
|
|
117 * Represents a call in the code.
|
|
118 */
|
|
119 public static final class Call extends Infopoint {
|
|
120
|
|
121 /**
|
|
122 * The target of the call.
|
|
123 */
|
|
124 public final InvokeTarget target;
|
|
125
|
|
126 /**
|
|
127 * The size of the call instruction.
|
|
128 */
|
|
129 public final int size;
|
|
130
|
|
131 /**
|
|
132 * Specifies if this call is direct or indirect. A direct call has an immediate operand
|
|
133 * encoding the absolute or relative (to the call itself) address of the target. An indirect
|
|
134 * call has a register or memory operand specifying the target address of the call.
|
|
135 */
|
|
136 public final boolean direct;
|
|
137
|
|
138 public Call(InvokeTarget target, int pcOffset, int size, boolean direct, DebugInfo debugInfo) {
|
|
139 super(pcOffset, debugInfo, InfopointReason.CALL);
|
|
140 this.size = size;
|
|
141 this.target = target;
|
|
142 this.direct = direct;
|
|
143 }
|
|
144
|
|
145 @Override
|
|
146 public boolean equals(Object obj) {
|
|
147 if (this == obj) {
|
|
148 return true;
|
|
149 }
|
|
150 if (obj instanceof Call && super.equals(obj)) {
|
|
151 Call that = (Call) obj;
|
|
152 if (this.size == that.size && this.direct == that.direct && Objects.equals(this.target, that.target)) {
|
|
153 return true;
|
|
154 }
|
|
155 }
|
|
156 return false;
|
|
157 }
|
|
158
|
|
159 @Override
|
|
160 public String toString() {
|
|
161 StringBuilder sb = new StringBuilder();
|
|
162 sb.append(pcOffset);
|
|
163 sb.append('[');
|
|
164 sb.append(target);
|
|
165 sb.append(']');
|
|
166
|
|
167 if (debugInfo != null) {
|
|
168 appendDebugInfo(sb, debugInfo);
|
|
169 }
|
|
170
|
|
171 return sb.toString();
|
|
172 }
|
|
173 }
|
|
174
|
|
175 /**
|
|
176 * Represents some external data that is referenced by the code.
|
|
177 */
|
|
178 public abstract static class Reference {
|
|
179
|
|
180 @Override
|
|
181 public abstract int hashCode();
|
|
182
|
|
183 @Override
|
|
184 public abstract boolean equals(Object obj);
|
|
185 }
|
|
186
|
|
187 public static final class ConstantReference extends Reference {
|
|
188
|
|
189 private final VMConstant constant;
|
|
190
|
|
191 public ConstantReference(VMConstant constant) {
|
|
192 this.constant = constant;
|
|
193 }
|
|
194
|
|
195 public VMConstant getConstant() {
|
|
196 return constant;
|
|
197 }
|
|
198
|
|
199 @Override
|
|
200 public String toString() {
|
|
201 return constant.toString();
|
|
202 }
|
|
203
|
|
204 @Override
|
|
205 public int hashCode() {
|
|
206 return constant.hashCode();
|
|
207 }
|
|
208
|
|
209 @Override
|
|
210 public boolean equals(Object obj) {
|
|
211 if (this == obj) {
|
|
212 return true;
|
|
213 }
|
|
214 if (obj instanceof ConstantReference) {
|
|
215 ConstantReference that = (ConstantReference) obj;
|
|
216 return Objects.equals(this.constant, that.constant);
|
|
217 }
|
|
218 return false;
|
|
219 }
|
|
220 }
|
|
221
|
|
222 public static final class DataSectionReference extends Reference {
|
|
223
|
|
224 private boolean initialized;
|
|
225 private int offset;
|
|
226
|
|
227 public DataSectionReference() {
|
|
228 // will be set after the data section layout is fixed
|
|
229 offset = 0xDEADDEAD;
|
|
230 }
|
|
231
|
|
232 public int getOffset() {
|
|
233 assert initialized;
|
|
234
|
|
235 return offset;
|
|
236 }
|
|
237
|
|
238 public void setOffset(int offset) {
|
|
239 assert !initialized;
|
|
240 initialized = true;
|
|
241
|
|
242 this.offset = offset;
|
|
243 }
|
|
244
|
|
245 @Override
|
|
246 public int hashCode() {
|
|
247 return offset;
|
|
248 }
|
|
249
|
|
250 @Override
|
|
251 public boolean equals(Object obj) {
|
|
252 if (this == obj) {
|
|
253 return true;
|
|
254 }
|
|
255 if (obj instanceof DataSectionReference) {
|
|
256 DataSectionReference that = (DataSectionReference) obj;
|
|
257 return this.offset == that.offset;
|
|
258 }
|
|
259 return false;
|
|
260 }
|
|
261 }
|
|
262
|
|
263 /**
|
|
264 * Represents a code site that references some data. The associated data can be either a
|
|
265 * {@link DataSectionReference reference} to the data section, or it may be an inlined
|
|
266 * {@link JavaConstant} that needs to be patched.
|
|
267 */
|
|
268 public static final class DataPatch extends Site {
|
|
269
|
|
270 public Reference reference;
|
|
271
|
|
272 public DataPatch(int pcOffset, Reference reference) {
|
|
273 super(pcOffset);
|
|
274 this.reference = reference;
|
|
275 }
|
|
276
|
|
277 @Override
|
|
278 public String toString() {
|
|
279 return String.format("%d[<data patch referring to %s>]", pcOffset, reference.toString());
|
|
280 }
|
|
281
|
|
282 @Override
|
|
283 public boolean equals(Object obj) {
|
|
284 if (this == obj) {
|
|
285 return true;
|
|
286 }
|
|
287 if (obj instanceof DataPatch) {
|
|
288 DataPatch that = (DataPatch) obj;
|
|
289 if (this.pcOffset == that.pcOffset && Objects.equals(this.reference, that.reference)) {
|
|
290 return true;
|
|
291 }
|
|
292 }
|
|
293 return false;
|
|
294 }
|
|
295 }
|
|
296
|
|
297 /**
|
|
298 * Provides extra information about instructions or data at specific positions in
|
|
299 * {@link CompilationResult#getTargetCode()}. This is optional information that can be used to
|
|
300 * enhance a disassembly of the code.
|
|
301 */
|
|
302 public abstract static class CodeAnnotation {
|
|
303
|
|
304 public final int position;
|
|
305
|
|
306 public CodeAnnotation(int position) {
|
|
307 this.position = position;
|
|
308 }
|
|
309
|
|
310 @Override
|
|
311 public final int hashCode() {
|
|
312 throw new UnsupportedOperationException("hashCode");
|
|
313 }
|
|
314
|
|
315 @Override
|
|
316 public String toString() {
|
|
317 return identityHashCodeString(this);
|
|
318 }
|
|
319
|
|
320 @Override
|
|
321 public abstract boolean equals(Object obj);
|
|
322 }
|
|
323
|
|
324 /**
|
|
325 * A string comment about one or more instructions at a specific position in the code.
|
|
326 */
|
|
327 public static final class CodeComment extends CodeAnnotation {
|
|
328
|
|
329 public final String value;
|
|
330
|
|
331 public CodeComment(int position, String comment) {
|
|
332 super(position);
|
|
333 this.value = comment;
|
|
334 }
|
|
335
|
|
336 @Override
|
|
337 public boolean equals(Object obj) {
|
|
338 if (this == obj) {
|
|
339 return true;
|
|
340 }
|
|
341 if (obj instanceof CodeComment) {
|
|
342 CodeComment that = (CodeComment) obj;
|
|
343 if (this.position == that.position && this.value.equals(that.value)) {
|
|
344 return true;
|
|
345 }
|
|
346 }
|
|
347 return false;
|
|
348 }
|
|
349
|
|
350 @Override
|
|
351 public String toString() {
|
|
352 return getClass().getSimpleName() + "@" + position + ": " + value;
|
|
353 }
|
|
354 }
|
|
355
|
|
356 /**
|
|
357 * Describes a table of signed offsets embedded in the code. The offsets are relative to the
|
|
358 * starting address of the table. This type of table maybe generated when translating a
|
|
359 * multi-way branch based on a key value from a dense value set (e.g. the {@code tableswitch}
|
|
360 * JVM instruction).
|
|
361 *
|
|
362 * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high}
|
|
363 * inclusive.
|
|
364 */
|
|
365 public static final class JumpTable extends CodeAnnotation {
|
|
366
|
|
367 /**
|
|
368 * The low value in the key range (inclusive).
|
|
369 */
|
|
370 public final int low;
|
|
371
|
|
372 /**
|
|
373 * The high value in the key range (inclusive).
|
|
374 */
|
|
375 public final int high;
|
|
376
|
|
377 /**
|
|
378 * The size (in bytes) of each table entry.
|
|
379 */
|
|
380 public final int entrySize;
|
|
381
|
|
382 public JumpTable(int position, int low, int high, int entrySize) {
|
|
383 super(position);
|
|
384 this.low = low;
|
|
385 this.high = high;
|
|
386 this.entrySize = entrySize;
|
|
387 }
|
|
388
|
|
389 @Override
|
|
390 public boolean equals(Object obj) {
|
|
391 if (this == obj) {
|
|
392 return true;
|
|
393 }
|
|
394 if (obj instanceof JumpTable) {
|
|
395 JumpTable that = (JumpTable) obj;
|
|
396 if (this.position == that.position && this.entrySize == that.entrySize && this.low == that.low && this.high == that.high) {
|
|
397 return true;
|
|
398 }
|
|
399 }
|
|
400 return false;
|
|
401 }
|
|
402
|
|
403 @Override
|
|
404 public String toString() {
|
|
405 return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]";
|
|
406 }
|
|
407 }
|
|
408
|
|
409 /**
|
|
410 * Represents exception handler information for a specific code position. It includes the catch
|
|
411 * code position as well as the caught exception type.
|
|
412 */
|
|
413 public static final class ExceptionHandler extends Site {
|
|
414
|
|
415 public final int handlerPos;
|
|
416
|
|
417 ExceptionHandler(int pcOffset, int handlerPos) {
|
|
418 super(pcOffset);
|
|
419 this.handlerPos = handlerPos;
|
|
420 }
|
|
421
|
|
422 @Override
|
|
423 public String toString() {
|
|
424 return String.format("%d[<exception edge to %d>]", pcOffset, handlerPos);
|
|
425 }
|
|
426
|
|
427 @Override
|
|
428 public boolean equals(Object obj) {
|
|
429 if (this == obj) {
|
|
430 return true;
|
|
431 }
|
|
432 if (obj instanceof ExceptionHandler) {
|
|
433 ExceptionHandler that = (ExceptionHandler) obj;
|
|
434 if (this.pcOffset == that.pcOffset && this.handlerPos == that.handlerPos) {
|
|
435 return true;
|
|
436 }
|
|
437 }
|
|
438 return false;
|
|
439 }
|
|
440 }
|
|
441
|
|
442 /**
|
|
443 * Represents a mark in the machine code that can be used by the runtime for its own purposes. A
|
|
444 * mark can reference other marks.
|
|
445 */
|
|
446 public static final class Mark extends Site {
|
|
447
|
|
448 public final Object id;
|
|
449
|
|
450 public Mark(int pcOffset, Object id) {
|
|
451 super(pcOffset);
|
|
452 this.id = id;
|
|
453 }
|
|
454
|
|
455 @Override
|
|
456 public String toString() {
|
|
457 if (id == null) {
|
|
458 return String.format("%d[<mar>]", pcOffset);
|
|
459 } else if (id instanceof Integer) {
|
|
460 return String.format("%d[<mark with id %s>]", pcOffset, Integer.toHexString((Integer) id));
|
|
461 } else {
|
|
462 return String.format("%d[<mark with id %s>]", pcOffset, id.toString());
|
|
463 }
|
|
464 }
|
|
465
|
|
466 @Override
|
|
467 public boolean equals(Object obj) {
|
|
468 if (this == obj) {
|
|
469 return true;
|
|
470 }
|
|
471 if (obj instanceof Mark) {
|
|
472 Mark that = (Mark) obj;
|
|
473 if (this.pcOffset == that.pcOffset && Objects.equals(this.id, that.id)) {
|
|
474 return true;
|
|
475 }
|
|
476 }
|
|
477 return false;
|
|
478 }
|
|
479 }
|
|
480
|
|
481 private int id = -1;
|
|
482 private int entryBCI = -1;
|
|
483
|
|
484 private final DataSection dataSection = new DataSection();
|
|
485
|
|
486 private final List<Infopoint> infopoints = new ArrayList<>();
|
|
487 private final List<DataPatch> dataPatches = new ArrayList<>();
|
|
488 private final List<ExceptionHandler> exceptionHandlers = new ArrayList<>();
|
|
489 private final List<Mark> marks = new ArrayList<>();
|
|
490
|
|
491 private int totalFrameSize = -1;
|
|
492 private int customStackAreaOffset = -1;
|
|
493
|
|
494 private final String name;
|
|
495
|
|
496 /**
|
|
497 * The buffer containing the emitted machine code.
|
|
498 */
|
|
499 private byte[] targetCode;
|
|
500
|
|
501 /**
|
|
502 * The leading number of bytes in {@link #targetCode} containing the emitted machine code.
|
|
503 */
|
|
504 private int targetCodeSize;
|
|
505
|
|
506 private ArrayList<CodeAnnotation> annotations;
|
|
507
|
|
508 private Assumption[] assumptions;
|
|
509
|
|
510 /**
|
|
511 * The list of the methods whose bytecodes were used as input to the compilation. If
|
|
512 * {@code null}, then the compilation did not record method dependencies. Otherwise, the first
|
|
513 * element of this array is the root method of the compilation.
|
|
514 */
|
|
515 private ResolvedJavaMethod[] methods;
|
|
516
|
|
517 private int bytecodeSize;
|
|
518
|
|
519 private boolean hasUnsafeAccess;
|
|
520
|
|
521 public CompilationResult() {
|
|
522 this(null);
|
|
523 }
|
|
524
|
|
525 public CompilationResult(String name) {
|
|
526 this.name = name;
|
|
527 }
|
|
528
|
|
529 @Override
|
|
530 public int hashCode() {
|
|
531 // CompilationResult instances should not be used as hash map keys
|
|
532 throw new UnsupportedOperationException("hashCode");
|
|
533 }
|
|
534
|
|
535 @Override
|
|
536 public String toString() {
|
|
537 if (methods != null) {
|
|
538 return getClass().getName() + "[" + methods[0].format("%H.%n(%p)%r") + "]";
|
|
539 }
|
|
540 return identityHashCodeString(this);
|
|
541 }
|
|
542
|
|
543 @Override
|
|
544 public boolean equals(Object obj) {
|
|
545 if (this == obj) {
|
|
546 return true;
|
|
547 }
|
|
548 if (obj != null && obj.getClass() == getClass()) {
|
|
549 CompilationResult that = (CompilationResult) obj;
|
|
550 // @formatter:off
|
|
551 if (this.entryBCI == that.entryBCI &&
|
|
552 this.id == that.id &&
|
|
553 this.customStackAreaOffset == that.customStackAreaOffset &&
|
|
554 this.totalFrameSize == that.totalFrameSize &&
|
|
555 this.targetCodeSize == that.targetCodeSize &&
|
|
556 Objects.equals(this.name, that.name) &&
|
|
557 Objects.equals(this.annotations, that.annotations) &&
|
|
558 Objects.equals(this.dataSection, that.dataSection) &&
|
|
559 Objects.equals(this.exceptionHandlers, that.exceptionHandlers) &&
|
|
560 Objects.equals(this.dataPatches, that.dataPatches) &&
|
|
561 Objects.equals(this.infopoints, that.infopoints) &&
|
|
562 Objects.equals(this.marks, that.marks) &&
|
|
563 Arrays.equals(this.assumptions, that.assumptions) &&
|
|
564 Arrays.equals(targetCode, that.targetCode)) {
|
|
565 return true;
|
|
566 }
|
|
567 // @formatter:on
|
|
568 }
|
|
569 return false;
|
|
570 }
|
|
571
|
|
572 /**
|
|
573 * @return the compile id
|
|
574 */
|
|
575 public int getId() {
|
|
576 return id;
|
|
577 }
|
|
578
|
|
579 /**
|
|
580 * @param id the compile id to set
|
|
581 */
|
|
582 public void setId(int id) {
|
|
583 this.id = id;
|
|
584 }
|
|
585
|
|
586 /**
|
|
587 * @return the entryBCI
|
|
588 */
|
|
589 public int getEntryBCI() {
|
|
590 return entryBCI;
|
|
591 }
|
|
592
|
|
593 /**
|
|
594 * @param entryBCI the entryBCI to set
|
|
595 */
|
|
596 public void setEntryBCI(int entryBCI) {
|
|
597 this.entryBCI = entryBCI;
|
|
598 }
|
|
599
|
|
600 /**
|
|
601 * Sets the assumptions made during compilation.
|
|
602 */
|
|
603 public void setAssumptions(Assumption[] assumptions) {
|
|
604 this.assumptions = assumptions;
|
|
605 }
|
|
606
|
|
607 /**
|
|
608 * Gets the assumptions made during compilation.
|
|
609 */
|
|
610 public Assumption[] getAssumptions() {
|
|
611 return assumptions;
|
|
612 }
|
|
613
|
|
614 /**
|
|
615 * Sets the methods whose bytecodes were used as input to the compilation.
|
|
616 *
|
|
617 * @param rootMethod the root method of the compilation
|
|
618 * @param inlinedMethods the methods inlined during compilation
|
|
619 */
|
|
620 public void setMethods(ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods) {
|
|
621 assert rootMethod != null;
|
|
622 assert inlinedMethods != null;
|
|
623 if (inlinedMethods.contains(rootMethod)) {
|
|
624 methods = inlinedMethods.toArray(new ResolvedJavaMethod[inlinedMethods.size()]);
|
|
625 for (int i = 0; i < methods.length; i++) {
|
|
626 if (methods[i].equals(rootMethod)) {
|
|
627 if (i != 0) {
|
|
628 ResolvedJavaMethod tmp = methods[0];
|
|
629 methods[0] = methods[i];
|
|
630 methods[i] = tmp;
|
|
631 }
|
|
632 break;
|
|
633 }
|
|
634 }
|
|
635 } else {
|
|
636 methods = new ResolvedJavaMethod[1 + inlinedMethods.size()];
|
|
637 methods[0] = rootMethod;
|
|
638 int i = 1;
|
|
639 for (ResolvedJavaMethod m : inlinedMethods) {
|
|
640 methods[i++] = m;
|
|
641 }
|
|
642 }
|
|
643 }
|
|
644
|
|
645 /**
|
|
646 * Gets the methods whose bytecodes were used as input to the compilation.
|
|
647 *
|
|
648 * @return {@code null} if the compilation did not record method dependencies otherwise the
|
|
649 * methods whose bytecodes were used as input to the compilation with the first element
|
|
650 * being the root method of the compilation
|
|
651 */
|
|
652 public ResolvedJavaMethod[] getMethods() {
|
|
653 return methods;
|
|
654 }
|
|
655
|
|
656 public void setBytecodeSize(int bytecodeSize) {
|
|
657 this.bytecodeSize = bytecodeSize;
|
|
658 }
|
|
659
|
|
660 public int getBytecodeSize() {
|
|
661 return bytecodeSize;
|
|
662 }
|
|
663
|
|
664 public DataSection getDataSection() {
|
|
665 return dataSection;
|
|
666 }
|
|
667
|
|
668 /**
|
|
669 * The total frame size of the method in bytes. This includes the return address pushed onto the
|
|
670 * stack, if any.
|
|
671 *
|
|
672 * @return the frame size
|
|
673 */
|
|
674 public int getTotalFrameSize() {
|
|
675 assert totalFrameSize != -1 : "frame size not yet initialized!";
|
|
676 return totalFrameSize;
|
|
677 }
|
|
678
|
|
679 /**
|
|
680 * Sets the total frame size in bytes. This includes the return address pushed onto the stack,
|
|
681 * if any.
|
|
682 *
|
|
683 * @param size the size of the frame in bytes
|
|
684 */
|
|
685 public void setTotalFrameSize(int size) {
|
|
686 totalFrameSize = size;
|
|
687 }
|
|
688
|
|
689 /**
|
|
690 * Sets the machine that has been generated by the compiler.
|
|
691 *
|
|
692 * @param code the machine code generated
|
|
693 * @param size the size of the machine code
|
|
694 */
|
|
695 public void setTargetCode(byte[] code, int size) {
|
|
696 targetCode = code;
|
|
697 targetCodeSize = size;
|
|
698 }
|
|
699
|
|
700 /**
|
|
701 * Records a data patch in the code section. The data patch can refer to something in the
|
|
702 * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined
|
|
703 * constant}.
|
|
704 *
|
|
705 * @param codePos The position in the code that needs to be patched.
|
|
706 * @param ref The reference that should be inserted in the code.
|
|
707 */
|
|
708 public void recordDataPatch(int codePos, Reference ref) {
|
|
709 assert codePos >= 0 && ref != null;
|
|
710 dataPatches.add(new DataPatch(codePos, ref));
|
|
711 }
|
|
712
|
|
713 /**
|
|
714 * Records a call in the code array.
|
|
715 *
|
|
716 * @param codePos the position of the call in the code array
|
|
717 * @param size the size of the call instruction
|
|
718 * @param target the being called
|
|
719 * @param debugInfo the debug info for the call
|
|
720 * @param direct specifies if this is a {@linkplain Call#direct direct} call
|
|
721 */
|
|
722 public void recordCall(int codePos, int size, InvokeTarget target, DebugInfo debugInfo, boolean direct) {
|
|
723 final Call call = new Call(target, codePos, size, direct, debugInfo);
|
|
724 addInfopoint(call);
|
|
725 }
|
|
726
|
|
727 /**
|
|
728 * Records an exception handler for this method.
|
|
729 *
|
|
730 * @param codePos the position in the code that is covered by the handler
|
|
731 * @param handlerPos the position of the handler
|
|
732 */
|
|
733 public void recordExceptionHandler(int codePos, int handlerPos) {
|
|
734 assert validateExceptionHandlerAdd(codePos, handlerPos) : String.format("Duplicate exception handler for pc 0x%x handlerPos 0x%x", codePos, handlerPos);
|
|
735 exceptionHandlers.add(new ExceptionHandler(codePos, handlerPos));
|
|
736 }
|
|
737
|
|
738 /**
|
|
739 * Validate if the exception handler for codePos already exists and handlerPos is different.
|
|
740 *
|
|
741 * @param codePos
|
|
742 * @param handlerPos
|
|
743 * @return true if the validation is successful
|
|
744 */
|
|
745 private boolean validateExceptionHandlerAdd(int codePos, int handlerPos) {
|
|
746 ExceptionHandler exHandler = getExceptionHandlerForCodePos(codePos);
|
|
747 return exHandler == null || exHandler.handlerPos == handlerPos;
|
|
748 }
|
|
749
|
|
750 /**
|
|
751 * Returns the first ExceptionHandler which matches codePos.
|
|
752 *
|
|
753 * @param codePos position to search for
|
|
754 * @return first matching ExceptionHandler
|
|
755 */
|
|
756 private ExceptionHandler getExceptionHandlerForCodePos(int codePos) {
|
|
757 for (ExceptionHandler h : exceptionHandlers) {
|
|
758 if (h.pcOffset == codePos) {
|
|
759 return h;
|
|
760 }
|
|
761 }
|
|
762 return null;
|
|
763 }
|
|
764
|
|
765 /**
|
|
766 * Records an infopoint in the code array.
|
|
767 *
|
|
768 * @param codePos the position of the infopoint in the code array
|
|
769 * @param debugInfo the debug info for the infopoint
|
|
770 */
|
|
771 public void recordInfopoint(int codePos, DebugInfo debugInfo, InfopointReason reason) {
|
|
772 addInfopoint(new Infopoint(codePos, debugInfo, reason));
|
|
773 }
|
|
774
|
|
775 /**
|
|
776 * Records a custom infopoint in the code section.
|
|
777 *
|
|
778 * Compiler implementations can use this method to record non-standard infopoints, which are not
|
|
779 * handled by the dedicated methods like {@link #recordCall}.
|
|
780 *
|
|
781 * @param infopoint the infopoint to record, usually a derived class from {@link Infopoint}
|
|
782 */
|
|
783 public void addInfopoint(Infopoint infopoint) {
|
|
784 // The infopoints list must always be sorted
|
|
785 if (!infopoints.isEmpty()) {
|
|
786 Infopoint previousInfopoint = infopoints.get(infopoints.size() - 1);
|
|
787 if (previousInfopoint.pcOffset > infopoint.pcOffset) {
|
|
788 // This re-sorting should be very rare
|
|
789 Collections.sort(infopoints);
|
|
790 previousInfopoint = infopoints.get(infopoints.size() - 1);
|
|
791 }
|
|
792 if (previousInfopoint.pcOffset == infopoint.pcOffset) {
|
|
793 if (infopoint.reason.canBeOmitted()) {
|
|
794 return;
|
|
795 }
|
|
796 if (previousInfopoint.reason.canBeOmitted()) {
|
|
797 Infopoint removed = infopoints.remove(infopoints.size() - 1);
|
|
798 assert removed == previousInfopoint;
|
|
799 } else {
|
|
800 throw new RuntimeException("Infopoints that can not be omited should have distinct PCs");
|
|
801 }
|
|
802 }
|
|
803 }
|
|
804 infopoints.add(infopoint);
|
|
805 }
|
|
806
|
|
807 /**
|
|
808 * Records an instruction mark within this method.
|
|
809 *
|
|
810 * @param codePos the position in the code that is covered by the handler
|
|
811 * @param markId the identifier for this mark
|
|
812 */
|
|
813 public Mark recordMark(int codePos, Object markId) {
|
|
814 Mark mark = new Mark(codePos, markId);
|
|
815 marks.add(mark);
|
|
816 return mark;
|
|
817 }
|
|
818
|
|
819 /**
|
|
820 * Offset in bytes for the custom stack area (relative to sp).
|
|
821 *
|
|
822 * @return the offset in bytes
|
|
823 */
|
|
824 public int getCustomStackAreaOffset() {
|
|
825 return customStackAreaOffset;
|
|
826 }
|
|
827
|
|
828 /**
|
|
829 * @see #getCustomStackAreaOffset()
|
|
830 * @param offset
|
|
831 */
|
|
832 public void setCustomStackAreaOffset(int offset) {
|
|
833 customStackAreaOffset = offset;
|
|
834 }
|
|
835
|
|
836 /**
|
|
837 * @return the machine code generated for this method
|
|
838 */
|
|
839 public byte[] getTargetCode() {
|
|
840 return targetCode;
|
|
841 }
|
|
842
|
|
843 /**
|
|
844 * @return the size of the machine code generated for this method
|
|
845 */
|
|
846 public int getTargetCodeSize() {
|
|
847 return targetCodeSize;
|
|
848 }
|
|
849
|
|
850 /**
|
|
851 * @return the code annotations or {@code null} if there are none
|
|
852 */
|
|
853 public List<CodeAnnotation> getAnnotations() {
|
|
854 if (annotations == null) {
|
|
855 return Collections.emptyList();
|
|
856 }
|
|
857 return annotations;
|
|
858 }
|
|
859
|
|
860 public void addAnnotation(CodeAnnotation annotation) {
|
|
861 assert annotation != null;
|
|
862 if (annotations == null) {
|
|
863 annotations = new ArrayList<>();
|
|
864 }
|
|
865 annotations.add(annotation);
|
|
866 }
|
|
867
|
|
868 private static void appendDebugInfo(StringBuilder sb, DebugInfo info) {
|
|
869 if (info != null) {
|
|
870 ReferenceMap refMap = info.getReferenceMap();
|
|
871 if (refMap != null) {
|
|
872 sb.append(refMap.toString());
|
|
873 sb.append(']');
|
|
874 }
|
|
875 RegisterSaveLayout calleeSaveInfo = info.getCalleeSaveInfo();
|
|
876 if (calleeSaveInfo != null) {
|
|
877 sb.append(" callee-save-info[");
|
|
878 String sep = "";
|
|
879 for (Map.Entry<Register, Integer> e : calleeSaveInfo.registersToSlots(true).entrySet()) {
|
|
880 sb.append(sep).append(e.getKey()).append("->").append(e.getValue());
|
|
881 sep = ", ";
|
|
882 }
|
|
883 sb.append(']');
|
|
884 }
|
|
885 BytecodePosition codePos = info.getBytecodePosition();
|
|
886 if (codePos != null) {
|
|
887 MetaUtil.appendLocation(sb.append(" "), codePos.getMethod(), codePos.getBCI());
|
|
888 if (info.hasFrame()) {
|
|
889 sb.append(" #locals=").append(info.frame().numLocals).append(" #expr=").append(info.frame().numStack);
|
|
890 if (info.frame().numLocks > 0) {
|
|
891 sb.append(" #locks=").append(info.frame().numLocks);
|
|
892 }
|
|
893 }
|
|
894 }
|
|
895 }
|
|
896 }
|
|
897
|
|
898 /**
|
|
899 * @return the list of infopoints, sorted by {@link Site#pcOffset}
|
|
900 */
|
|
901 public List<Infopoint> getInfopoints() {
|
|
902 if (infopoints.isEmpty()) {
|
|
903 return emptyList();
|
|
904 }
|
|
905 return unmodifiableList(infopoints);
|
|
906 }
|
|
907
|
|
908 /**
|
|
909 * @return the list of data references
|
|
910 */
|
|
911 public List<DataPatch> getDataPatches() {
|
|
912 if (dataPatches.isEmpty()) {
|
|
913 return emptyList();
|
|
914 }
|
|
915 return unmodifiableList(dataPatches);
|
|
916 }
|
|
917
|
|
918 /**
|
|
919 * @return the list of exception handlers
|
|
920 */
|
|
921 public List<ExceptionHandler> getExceptionHandlers() {
|
|
922 if (exceptionHandlers.isEmpty()) {
|
|
923 return emptyList();
|
|
924 }
|
|
925 return unmodifiableList(exceptionHandlers);
|
|
926 }
|
|
927
|
|
928 /**
|
|
929 * @return the list of marks
|
|
930 */
|
|
931 public List<Mark> getMarks() {
|
|
932 if (marks.isEmpty()) {
|
|
933 return emptyList();
|
|
934 }
|
|
935 return unmodifiableList(marks);
|
|
936 }
|
|
937
|
|
938 public String getName() {
|
|
939 return name;
|
|
940 }
|
|
941
|
|
942 public void setHasUnsafeAccess(boolean hasUnsafeAccess) {
|
|
943 this.hasUnsafeAccess = hasUnsafeAccess;
|
|
944 }
|
|
945
|
|
946 public boolean hasUnsafeAccess() {
|
|
947 return hasUnsafeAccess;
|
|
948 }
|
|
949
|
|
950 public void reset() {
|
|
951 hasUnsafeAccess = false;
|
|
952 infopoints.clear();
|
|
953 dataPatches.clear();
|
|
954 exceptionHandlers.clear();
|
|
955 marks.clear();
|
|
956 if (annotations != null) {
|
|
957 annotations.clear();
|
|
958 }
|
|
959 }
|
|
960 }
|