1/*
2 * Copyright (c) 2009, 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 */
23package org.graalvm.compiler.code;
24
25import static java.util.Collections.emptyList;
26import static java.util.Collections.unmodifiableList;
27import static jdk.vm.ci.meta.MetaUtil.identityHashCodeString;
28
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Collection;
32import java.util.Collections;
33import java.util.List;
34import java.util.Objects;
35
36import org.graalvm.compiler.graph.NodeSourcePosition;
37
38import jdk.vm.ci.code.DebugInfo;
39import jdk.vm.ci.code.StackSlot;
40import jdk.vm.ci.code.site.Call;
41import jdk.vm.ci.code.site.ConstantReference;
42import jdk.vm.ci.code.site.DataPatch;
43import jdk.vm.ci.code.site.DataSectionReference;
44import jdk.vm.ci.code.site.ExceptionHandler;
45import jdk.vm.ci.code.site.Infopoint;
46import jdk.vm.ci.code.site.InfopointReason;
47import jdk.vm.ci.code.site.Mark;
48import jdk.vm.ci.code.site.Reference;
49import jdk.vm.ci.code.site.Site;
50import jdk.vm.ci.meta.Assumptions.Assumption;
51import jdk.vm.ci.meta.InvokeTarget;
52import jdk.vm.ci.meta.ResolvedJavaField;
53import jdk.vm.ci.meta.ResolvedJavaMethod;
54
55/**
56 * Represents the output from compiling a method, including the compiled machine code, associated
57 * data and references, relocation information, deoptimization information, etc.
58 */
59public class CompilationResult {
60
61    /**
62     * Provides extra information about instructions or data at specific positions in
63     * {@link CompilationResult#getTargetCode()}. This is optional information that can be used to
64     * enhance a disassembly of the code.
65     */
66    public abstract static class CodeAnnotation {
67
68        public final int position;
69
70        public CodeAnnotation(int position) {
71            this.position = position;
72        }
73
74        @Override
75        public final int hashCode() {
76            throw new UnsupportedOperationException("hashCode");
77        }
78
79        @Override
80        public String toString() {
81            return identityHashCodeString(this);
82        }
83
84        @Override
85        public abstract boolean equals(Object obj);
86    }
87
88    /**
89     * A string comment about one or more instructions at a specific position in the code.
90     */
91    public static final class CodeComment extends CodeAnnotation {
92
93        public final String value;
94
95        public CodeComment(int position, String comment) {
96            super(position);
97            this.value = comment;
98        }
99
100        @Override
101        public boolean equals(Object obj) {
102            if (this == obj) {
103                return true;
104            }
105            if (obj instanceof CodeComment) {
106                CodeComment that = (CodeComment) obj;
107                if (this.position == that.position && this.value.equals(that.value)) {
108                    return true;
109                }
110            }
111            return false;
112        }
113
114        @Override
115        public String toString() {
116            return getClass().getSimpleName() + "@" + position + ": " + value;
117        }
118    }
119
120    /**
121     * Describes a table of signed offsets embedded in the code. The offsets are relative to the
122     * starting address of the table. This type of table maybe generated when translating a
123     * multi-way branch based on a key value from a dense value set (e.g. the {@code tableswitch}
124     * JVM instruction).
125     *
126     * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high}
127     * inclusive.
128     */
129    public static final class JumpTable extends CodeAnnotation {
130
131        /**
132         * The low value in the key range (inclusive).
133         */
134        public final int low;
135
136        /**
137         * The high value in the key range (inclusive).
138         */
139        public final int high;
140
141        /**
142         * The size (in bytes) of each table entry.
143         */
144        public final int entrySize;
145
146        public JumpTable(int position, int low, int high, int entrySize) {
147            super(position);
148            this.low = low;
149            this.high = high;
150            this.entrySize = entrySize;
151        }
152
153        @Override
154        public boolean equals(Object obj) {
155            if (this == obj) {
156                return true;
157            }
158            if (obj instanceof JumpTable) {
159                JumpTable that = (JumpTable) obj;
160                if (this.position == that.position && this.entrySize == that.entrySize && this.low == that.low && this.high == that.high) {
161                    return true;
162                }
163            }
164            return false;
165        }
166
167        @Override
168        public String toString() {
169            return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]";
170        }
171    }
172
173    private boolean closed;
174
175    private int entryBCI = -1;
176
177    private final DataSection dataSection = new DataSection();
178
179    private final List<Infopoint> infopoints = new ArrayList<>();
180    private final List<SourceMapping> sourceMapping = new ArrayList<>();
181    private final List<DataPatch> dataPatches = new ArrayList<>();
182    private final List<ExceptionHandler> exceptionHandlers = new ArrayList<>();
183    private final List<Mark> marks = new ArrayList<>();
184
185    private int totalFrameSize = -1;
186    private int maxInterpreterFrameSize = -1;
187
188    private StackSlot customStackArea = null;
189
190    private final String name;
191
192    /**
193     * The buffer containing the emitted machine code.
194     */
195    private byte[] targetCode;
196
197    /**
198     * The leading number of bytes in {@link #targetCode} containing the emitted machine code.
199     */
200    private int targetCodeSize;
201
202    private ArrayList<CodeAnnotation> annotations;
203
204    private Assumption[] assumptions;
205
206    /**
207     * The list of the methods whose bytecodes were used as input to the compilation. If
208     * {@code null}, then the compilation did not record method dependencies. Otherwise, the first
209     * element of this array is the root method of the compilation.
210     */
211    private ResolvedJavaMethod[] methods;
212
213    /**
214     * The list of fields that were accessed from the bytecodes.
215     */
216    private ResolvedJavaField[] fields;
217
218    private int bytecodeSize;
219
220    private boolean hasUnsafeAccess;
221
222    private boolean isImmutablePIC;
223
224    public CompilationResult() {
225        this(null, false);
226    }
227
228    public CompilationResult(String name) {
229        this(name, false);
230    }
231
232    public CompilationResult(boolean isImmutablePIC) {
233        this(null, isImmutablePIC);
234    }
235
236    public CompilationResult(String name, boolean isImmutablePIC) {
237        this.name = name;
238        this.isImmutablePIC = isImmutablePIC;
239    }
240
241    @Override
242    public int hashCode() {
243        // CompilationResult instances should not be used as hash map keys
244        throw new UnsupportedOperationException("hashCode");
245    }
246
247    @Override
248    public String toString() {
249        if (methods != null) {
250            return getClass().getName() + "[" + methods[0].format("%H.%n(%p)%r") + "]";
251        }
252        return identityHashCodeString(this);
253    }
254
255    @Override
256    public boolean equals(Object obj) {
257        if (this == obj) {
258            return true;
259        }
260        if (obj != null && obj.getClass() == getClass()) {
261            CompilationResult that = (CompilationResult) obj;
262            // @formatter:off
263            if (this.entryBCI == that.entryBCI &&
264                Objects.equals(this.customStackArea, that.customStackArea) &&
265                this.totalFrameSize == that.totalFrameSize &&
266                this.targetCodeSize == that.targetCodeSize &&
267                Objects.equals(this.name, that.name) &&
268                Objects.equals(this.annotations, that.annotations) &&
269                Objects.equals(this.dataSection, that.dataSection) &&
270                Objects.equals(this.exceptionHandlers, that.exceptionHandlers) &&
271                Objects.equals(this.dataPatches, that.dataPatches) &&
272                Objects.equals(this.infopoints, that.infopoints) &&
273                Objects.equals(this.marks,  that.marks) &&
274                Arrays.equals(this.assumptions, that.assumptions) &&
275                Arrays.equals(targetCode, that.targetCode)) {
276                return true;
277            }
278            // @formatter:on
279        }
280        return false;
281    }
282
283    /**
284     * @return the entryBCI
285     */
286    public int getEntryBCI() {
287        return entryBCI;
288    }
289
290    /**
291     * @param entryBCI the entryBCI to set
292     */
293    public void setEntryBCI(int entryBCI) {
294        checkOpen();
295        this.entryBCI = entryBCI;
296    }
297
298    /**
299     * Sets the assumptions made during compilation.
300     */
301    public void setAssumptions(Assumption[] assumptions) {
302        checkOpen();
303        this.assumptions = assumptions;
304    }
305
306    /**
307     * Gets the assumptions made during compilation.
308     *
309     * The caller must not modify the contents of the returned array.
310     */
311    public Assumption[] getAssumptions() {
312        return assumptions;
313    }
314
315    /**
316     * Sets the methods whose bytecodes were used as input to the compilation.
317     *
318     * @param rootMethod the root method of the compilation
319     * @param inlinedMethods the methods inlined during compilation
320     */
321    public void setMethods(ResolvedJavaMethod rootMethod, Collection<ResolvedJavaMethod> inlinedMethods) {
322        checkOpen();
323        assert rootMethod != null;
324        assert inlinedMethods != null;
325        if (inlinedMethods.contains(rootMethod)) {
326            methods = inlinedMethods.toArray(new ResolvedJavaMethod[inlinedMethods.size()]);
327            for (int i = 0; i < methods.length; i++) {
328                if (methods[i].equals(rootMethod)) {
329                    if (i != 0) {
330                        ResolvedJavaMethod tmp = methods[0];
331                        methods[0] = methods[i];
332                        methods[i] = tmp;
333                    }
334                    break;
335                }
336            }
337        } else {
338            methods = new ResolvedJavaMethod[1 + inlinedMethods.size()];
339            methods[0] = rootMethod;
340            int i = 1;
341            for (ResolvedJavaMethod m : inlinedMethods) {
342                methods[i++] = m;
343            }
344        }
345    }
346
347    /**
348     * Gets the methods whose bytecodes were used as input to the compilation.
349     *
350     * The caller must not modify the contents of the returned array.
351     *
352     * @return {@code null} if the compilation did not record method dependencies otherwise the
353     *         methods whose bytecodes were used as input to the compilation with the first element
354     *         being the root method of the compilation
355     */
356    public ResolvedJavaMethod[] getMethods() {
357        return methods;
358    }
359
360    /**
361     * Sets the fields that were referenced from the bytecodes that were used as input to the
362     * compilation.
363     *
364     * @param accessedFields the collected set of fields accessed during compilation
365     */
366    public void setFields(Collection<ResolvedJavaField> accessedFields) {
367        assert accessedFields != null;
368        fields = accessedFields.toArray(new ResolvedJavaField[accessedFields.size()]);
369    }
370
371    /**
372     * Gets the fields that were referenced from bytecodes that were used as input to the
373     * compilation.
374     *
375     * The caller must not modify the contents of the returned array.
376     *
377     * @return {@code null} if the compilation did not record fields dependencies otherwise the
378     *         fields that were accessed from bytecodes were used as input to the compilation.
379     */
380    public ResolvedJavaField[] getFields() {
381        return fields;
382    }
383
384    public void setBytecodeSize(int bytecodeSize) {
385        checkOpen();
386        this.bytecodeSize = bytecodeSize;
387    }
388
389    public int getBytecodeSize() {
390        return bytecodeSize;
391    }
392
393    public DataSection getDataSection() {
394        return dataSection;
395    }
396
397    /**
398     * The total frame size of the method in bytes. This includes the return address pushed onto the
399     * stack, if any.
400     *
401     * @return the frame size
402     */
403    public int getTotalFrameSize() {
404        assert totalFrameSize != -1 : "frame size not yet initialized!";
405        return totalFrameSize;
406    }
407
408    /**
409     * Sets the total frame size in bytes. This includes the return address pushed onto the stack,
410     * if any.
411     *
412     * @param size the size of the frame in bytes
413     */
414    public void setTotalFrameSize(int size) {
415        checkOpen();
416        totalFrameSize = size;
417    }
418
419    public int getMaxInterpreterFrameSize() {
420        return maxInterpreterFrameSize;
421    }
422
423    public void setMaxInterpreterFrameSize(int maxInterpreterFrameSize) {
424        checkOpen();
425        this.maxInterpreterFrameSize = maxInterpreterFrameSize;
426    }
427
428    public boolean isImmutablePIC() {
429        return this.isImmutablePIC;
430    }
431
432    /**
433     * Sets the machine that has been generated by the compiler.
434     *
435     * @param code the machine code generated
436     * @param size the size of the machine code
437     */
438    public void setTargetCode(byte[] code, int size) {
439        checkOpen();
440        targetCode = code;
441        targetCodeSize = size;
442    }
443
444    /**
445     * Records a data patch in the code section. The data patch can refer to something in the
446     * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined
447     * constant}.
448     *
449     * @param codePos the position in the code that needs to be patched
450     * @param ref the reference that should be inserted in the code
451     */
452    public void recordDataPatch(int codePos, Reference ref) {
453        checkOpen();
454        assert codePos >= 0 && ref != null;
455        dataPatches.add(new DataPatch(codePos, ref));
456    }
457
458    /**
459     * Records a data patch in the code section. The data patch can refer to something in the
460     * {@link DataSectionReference data section} or directly to an {@link ConstantReference inlined
461     * constant}.
462     *
463     * @param codePos the position in the code that needs to be patched
464     * @param ref the reference that should be inserted in the code
465     * @param note a note attached to data patch for use by post-processing tools
466     */
467    public void recordDataPatchWithNote(int codePos, Reference ref, Object note) {
468        assert codePos >= 0 && ref != null;
469        dataPatches.add(new DataPatch(codePos, ref, note));
470    }
471
472    /**
473     * Records a call in the code array.
474     *
475     * @param codePos the position of the call in the code array
476     * @param size the size of the call instruction
477     * @param target the being called
478     * @param debugInfo the debug info for the call
479     * @param direct specifies if this is a {@linkplain Call#direct direct} call
480     */
481    public void recordCall(int codePos, int size, InvokeTarget target, DebugInfo debugInfo, boolean direct) {
482        checkOpen();
483        final Call call = new Call(target, codePos, size, direct, debugInfo);
484        addInfopoint(call);
485    }
486
487    /**
488     * Records an exception handler for this method.
489     *
490     * @param codePos the position in the code that is covered by the handler
491     * @param handlerPos the position of the handler
492     */
493    public void recordExceptionHandler(int codePos, int handlerPos) {
494        checkOpen();
495        assert validateExceptionHandlerAdd(codePos, handlerPos) : String.format("Duplicate exception handler for pc 0x%x handlerPos 0x%x", codePos, handlerPos);
496        exceptionHandlers.add(new ExceptionHandler(codePos, handlerPos));
497    }
498
499    /**
500     * Validate if the exception handler for codePos already exists and handlerPos is different.
501     *
502     * @param codePos
503     * @param handlerPos
504     * @return true if the validation is successful
505     */
506    private boolean validateExceptionHandlerAdd(int codePos, int handlerPos) {
507        ExceptionHandler exHandler = getExceptionHandlerForCodePos(codePos);
508        return exHandler == null || exHandler.handlerPos == handlerPos;
509    }
510
511    /**
512     * Returns the first ExceptionHandler which matches codePos.
513     *
514     * @param codePos position to search for
515     * @return first matching ExceptionHandler
516     */
517    private ExceptionHandler getExceptionHandlerForCodePos(int codePos) {
518        for (ExceptionHandler h : exceptionHandlers) {
519            if (h.pcOffset == codePos) {
520                return h;
521            }
522        }
523        return null;
524    }
525
526    /**
527     * Records an infopoint in the code array.
528     *
529     * @param codePos the position of the infopoint in the code array
530     * @param debugInfo the debug info for the infopoint
531     */
532    public void recordInfopoint(int codePos, DebugInfo debugInfo, InfopointReason reason) {
533        addInfopoint(new Infopoint(codePos, debugInfo, reason));
534    }
535
536    /**
537     * Records a custom infopoint in the code section.
538     *
539     * Compiler implementations can use this method to record non-standard infopoints, which are not
540     * handled by dedicated methods like {@link #recordCall}.
541     *
542     * @param infopoint the infopoint to record, usually a derived class from {@link Infopoint}
543     */
544    public void addInfopoint(Infopoint infopoint) {
545        checkOpen();
546        infopoints.add(infopoint);
547    }
548
549    public void recordSourceMapping(int startOffset, int endOffset, NodeSourcePosition sourcePosition) {
550        checkOpen();
551        sourceMapping.add(new SourceMapping(startOffset, endOffset, sourcePosition));
552    }
553
554    /**
555     * Records an instruction mark within this method.
556     *
557     * @param codePos the position in the code that is covered by the handler
558     * @param markId the identifier for this mark
559     */
560    public Mark recordMark(int codePos, Object markId) {
561        checkOpen();
562        Mark mark = new Mark(codePos, markId);
563        marks.add(mark);
564        return mark;
565    }
566
567    /**
568     * Start of the custom stack area.
569     *
570     * @return the first stack slot of the custom stack area
571     */
572    public StackSlot getCustomStackArea() {
573        return customStackArea;
574    }
575
576    /**
577     * @see #getCustomStackArea()
578     * @param slot
579     */
580    public void setCustomStackAreaOffset(StackSlot slot) {
581        checkOpen();
582        customStackArea = slot;
583    }
584
585    /**
586     * @return the machine code generated for this method
587     */
588    public byte[] getTargetCode() {
589        return targetCode;
590    }
591
592    /**
593     * @return the size of the machine code generated for this method
594     */
595    public int getTargetCodeSize() {
596        return targetCodeSize;
597    }
598
599    /**
600     * @return the code annotations or {@code null} if there are none
601     */
602    public List<CodeAnnotation> getAnnotations() {
603        if (annotations == null) {
604            return Collections.emptyList();
605        }
606        return annotations;
607    }
608
609    public void addAnnotation(CodeAnnotation annotation) {
610        checkOpen();
611        assert annotation != null;
612        if (annotations == null) {
613            annotations = new ArrayList<>();
614        }
615        annotations.add(annotation);
616    }
617
618    /**
619     * @return the list of infopoints, sorted by {@link Site#pcOffset}
620     */
621    public List<Infopoint> getInfopoints() {
622        if (infopoints.isEmpty()) {
623            return emptyList();
624        }
625        return unmodifiableList(infopoints);
626    }
627
628    /**
629     * @return the list of data references
630     */
631    public List<DataPatch> getDataPatches() {
632        if (dataPatches.isEmpty()) {
633            return emptyList();
634        }
635        return unmodifiableList(dataPatches);
636    }
637
638    /**
639     * @return the list of exception handlers
640     */
641    public List<ExceptionHandler> getExceptionHandlers() {
642        if (exceptionHandlers.isEmpty()) {
643            return emptyList();
644        }
645        return unmodifiableList(exceptionHandlers);
646    }
647
648    /**
649     * @return the list of marks
650     */
651    public List<Mark> getMarks() {
652        if (marks.isEmpty()) {
653            return emptyList();
654        }
655        return unmodifiableList(marks);
656    }
657
658    /**
659     * @return the list of {@link SourceMapping}s
660     */
661    public List<SourceMapping> getSourceMappings() {
662        if (sourceMapping.isEmpty()) {
663            return emptyList();
664        }
665        return unmodifiableList(sourceMapping);
666    }
667
668    public String getName() {
669        return name;
670    }
671
672    public void setHasUnsafeAccess(boolean hasUnsafeAccess) {
673        checkOpen();
674        this.hasUnsafeAccess = hasUnsafeAccess;
675    }
676
677    public boolean hasUnsafeAccess() {
678        return hasUnsafeAccess;
679    }
680
681    /**
682     * Clears the information in this object pertaining to generating code. That is, the
683     * {@linkplain #getMarks() marks}, {@linkplain #getInfopoints() infopoints},
684     * {@linkplain #getExceptionHandlers() exception handlers}, {@linkplain #getDataPatches() data
685     * patches} and {@linkplain #getAnnotations() annotations} recorded in this object are cleared.
686     */
687    public void resetForEmittingCode() {
688        checkOpen();
689        infopoints.clear();
690        sourceMapping.clear();
691        dataPatches.clear();
692        exceptionHandlers.clear();
693        marks.clear();
694        dataSection.clear();
695        if (annotations != null) {
696            annotations.clear();
697        }
698    }
699
700    private void checkOpen() {
701        if (closed) {
702            throw new IllegalStateException();
703        }
704    }
705
706    /**
707     * Closes this compilation result to future updates.
708     */
709    public void close() {
710        if (closed) {
711            throw new IllegalStateException("Cannot re-close compilation result " + this);
712        }
713        dataSection.close();
714        closed = true;
715    }
716}
717