1 /*
2  * Copyright (c) 2009, 2019, 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 
24 
25 package org.graalvm.compiler.asm;
26 
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.function.Consumer;
32 
33 import jdk.vm.ci.code.Register;
34 import jdk.vm.ci.code.StackSlot;
35 import jdk.vm.ci.code.TargetDescription;
36 
37 /**
38  * The platform-independent base class for the assembler.
39  */
40 public abstract class Assembler {
41 
42     public abstract static class CodeAnnotation {
43         /**
44          * The position (bytes from the beginning of the method) of the annotated instruction.
45          */
46         public final int instructionPosition;
47 
CodeAnnotation(int instructionStartPosition)48         protected CodeAnnotation(int instructionStartPosition) {
49             this.instructionPosition = instructionStartPosition;
50         }
51     }
52 
53     public final TargetDescription target;
54     private List<LabelHint> jumpDisplacementHints;
55 
56     /**
57      * Labels with instructions to be patched when it is {@linkplain Label#bind bound}.
58      */
59     Label labelsWithPatches;
60 
61     /**
62      * Backing code buffer.
63      */
64     private final Buffer codeBuffer;
65 
66     protected Consumer<CodeAnnotation> codePatchingAnnotationConsumer;
67 
Assembler(TargetDescription target)68     public Assembler(TargetDescription target) {
69         this.target = target;
70         this.codeBuffer = new Buffer(target.arch.getByteOrder());
71     }
72 
setCodePatchingAnnotationConsumer(Consumer<CodeAnnotation> codeAnnotationConsumer)73     public void setCodePatchingAnnotationConsumer(Consumer<CodeAnnotation> codeAnnotationConsumer) {
74         assert this.codePatchingAnnotationConsumer == null : "overwriting existing value";
75         this.codePatchingAnnotationConsumer = codeAnnotationConsumer;
76     }
77 
78     /**
79      * Returns the current position of the underlying code buffer.
80      *
81      * @return current position in code buffer
82      */
position()83     public int position() {
84         return codeBuffer.position();
85     }
86 
emitByte(int x)87     public final void emitByte(int x) {
88         codeBuffer.emitByte(x);
89     }
90 
emitShort(int x)91     public final void emitShort(int x) {
92         codeBuffer.emitShort(x);
93     }
94 
emitInt(int x)95     public final void emitInt(int x) {
96         codeBuffer.emitInt(x);
97     }
98 
emitLong(long x)99     public final void emitLong(long x) {
100         codeBuffer.emitLong(x);
101     }
102 
emitByte(int b, int pos)103     public final void emitByte(int b, int pos) {
104         codeBuffer.emitByte(b, pos);
105     }
106 
emitShort(int b, int pos)107     public final void emitShort(int b, int pos) {
108         codeBuffer.emitShort(b, pos);
109     }
110 
emitInt(int b, int pos)111     public final void emitInt(int b, int pos) {
112         codeBuffer.emitInt(b, pos);
113     }
114 
emitLong(long b, int pos)115     public final void emitLong(long b, int pos) {
116         codeBuffer.emitLong(b, pos);
117     }
118 
getByte(int pos)119     public final int getByte(int pos) {
120         return codeBuffer.getByte(pos);
121     }
122 
getShort(int pos)123     public final int getShort(int pos) {
124         return codeBuffer.getShort(pos);
125     }
126 
getInt(int pos)127     public final int getInt(int pos) {
128         return codeBuffer.getInt(pos);
129     }
130 
131     private static final String NEWLINE = System.lineSeparator();
132 
133     /**
134      * Some GPU architectures have a text based encoding.
135      */
emitString(String x)136     public final void emitString(String x) {
137         emitString0("\t");  // XXX REMOVE ME pretty-printing
138         emitString0(x);
139         emitString0(NEWLINE);
140     }
141 
142     // XXX for pretty-printing
emitString0(String x)143     public final void emitString0(String x) {
144         codeBuffer.emitBytes(x.getBytes(), 0, x.length());
145     }
146 
emitString(String s, int pos)147     public void emitString(String s, int pos) {
148         codeBuffer.emitBytes(s.getBytes(), pos);
149     }
150 
151     /**
152      * Closes this assembler. No extra data can be written to this assembler after this call.
153      *
154      * @param trimmedCopy if {@code true}, then a copy of the underlying byte array up to (but not
155      *            including) {@code position()} is returned
156      * @return the data in this buffer or a trimmed copy if {@code trimmedCopy} is {@code true}
157      */
close(boolean trimmedCopy)158     public byte[] close(boolean trimmedCopy) {
159         checkAndClearLabelsWithPatches();
160         return codeBuffer.close(trimmedCopy);
161     }
162 
checkAndClearLabelsWithPatches()163     private void checkAndClearLabelsWithPatches() throws InternalError {
164         Label label = labelsWithPatches;
165         while (label != null) {
166             if (label.patchPositions != null) {
167                 throw new InternalError("Label used by instructions at following offsets has not been bound: " + label.patchPositions);
168             }
169             Label next = label.nextWithPatches;
170             label.nextWithPatches = null;
171             label = next;
172         }
173         labelsWithPatches = null;
174     }
175 
bind(Label l)176     public void bind(Label l) {
177         assert !l.isBound() : "can bind label only once";
178         l.bind(position(), this);
179     }
180 
align(int modulus)181     public abstract void align(int modulus);
182 
jmp(Label l)183     public abstract void jmp(Label l);
184 
patchJumpTarget(int branch, int jumpTarget)185     protected abstract void patchJumpTarget(int branch, int jumpTarget);
186 
187     private Map<Label, String> nameMap;
188 
189     /**
190      * Creates a name for a label.
191      *
192      * @param l the label for which a name is being created
193      * @param id a label identifier that is unique with the scope of this assembler
194      * @return a label name in the form of "L123"
195      */
createLabelName(Label l, int id)196     protected String createLabelName(Label l, int id) {
197         return "L" + id;
198     }
199 
200     /**
201      * Gets a name for a label, creating it if it does not yet exist. By default, the returned name
202      * is only unique with the scope of this assembler.
203      */
nameOf(Label l)204     public String nameOf(Label l) {
205         if (nameMap == null) {
206             nameMap = new HashMap<>();
207         }
208         String name = nameMap.get(l);
209         if (name == null) {
210             name = createLabelName(l, nameMap.size());
211             nameMap.put(l, name);
212         }
213         return name;
214     }
215 
216     /**
217      * This is used by the CompilationResultBuilder to convert a {@link StackSlot} to an
218      * {@link AbstractAddress}.
219      */
makeAddress(Register base, int displacement)220     public abstract AbstractAddress makeAddress(Register base, int displacement);
221 
222     /**
223      * Returns a target specific placeholder address that can be used for code patching.
224      *
225      * @param instructionStartPosition The start of the instruction, i.e., the value that is used as
226      *            the key for looking up placeholder patching information.
227      */
getPlaceholder(int instructionStartPosition)228     public abstract AbstractAddress getPlaceholder(int instructionStartPosition);
229 
230     /**
231      * Emits a NOP instruction to advance the current PC.
232      */
ensureUniquePC()233     public abstract void ensureUniquePC();
234 
reset()235     public void reset() {
236         codeBuffer.reset();
237         captureLabelPositions();
238     }
239 
captureLabelPositions()240     private void captureLabelPositions() {
241         if (jumpDisplacementHints == null) {
242             return;
243         }
244         for (LabelHint request : this.jumpDisplacementHints) {
245             request.capture();
246         }
247     }
248 
requestLabelHint(Label label)249     public LabelHint requestLabelHint(Label label) {
250         if (jumpDisplacementHints == null) {
251             jumpDisplacementHints = new ArrayList<>();
252         }
253         LabelHint hint = new LabelHint(label, position());
254         this.jumpDisplacementHints.add(hint);
255         return hint;
256     }
257 
getInstructionCounter()258     public InstructionCounter getInstructionCounter() {
259         throw new UnsupportedOperationException("Instruction counter is not implemented for " + this);
260     }
261 
262     public static class LabelHint {
263         private Label label;
264         private int forPosition;
265         private int capturedTarget = -1;
266 
LabelHint(Label label, int lastPosition)267         protected LabelHint(Label label, int lastPosition) {
268             super();
269             this.label = label;
270             this.forPosition = lastPosition;
271         }
272 
capture()273         protected void capture() {
274             this.capturedTarget = label.position();
275         }
276 
getTarget()277         public int getTarget() {
278             assert isValid();
279             return capturedTarget;
280         }
281 
getPosition()282         public int getPosition() {
283             assert isValid();
284             return forPosition;
285         }
286 
isValid()287         public boolean isValid() {
288             return capturedTarget >= 0;
289         }
290     }
291 
292     /**
293      * Instruction counter class which gives the user of the assembler to count different kinds of
294      * instructions in the generated assembler code.
295      */
296     public interface InstructionCounter {
getSupportedInstructionTypes()297         String[] getSupportedInstructionTypes();
298 
countInstructions(String[] instructionTypes, int beginPc, int endPc)299         int[] countInstructions(String[] instructionTypes, int beginPc, int endPc);
300     }
301 }
302