comparison graal/com.oracle.max.asmdis/src/com/sun/max/asm/Assembler.java @ 3733:e233f5660da4

Added Java files from Maxine project.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Sat, 17 Dec 2011 19:59:18 +0100
parents
children bc8527f3071c
comparison
equal deleted inserted replaced
3732:3e2e8b8abdaf 3733:e233f5660da4
1 /*
2 * Copyright (c) 2007, 2011, 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 com.sun.max.asm;
24
25 import java.io.*;
26 import java.util.*;
27
28 import com.sun.max.lang.*;
29 import com.sun.max.program.*;
30
31 /**
32 * Assembler base class.
33 */
34 public abstract class Assembler {
35
36 private final Directives directives;
37
38 protected Assembler(byte byteData, boolean isValidCode) {
39 this.directives = new Directives(byteData, isValidCode);
40 }
41
42 public abstract ISA isa();
43
44 public final Directives directives() {
45 return directives;
46 }
47
48 /**
49 * Gets the width of a word on machine for which the code is being assembled.
50 */
51 public abstract WordWidth wordWidth();
52
53 /**
54 * Resets any internal state in this assembler to the equivalent state it had when first constructed.
55 * <p>
56 * This method is overridden as needed to ensure the state in subclasses is reset as well. Any
57 * overriding implementation must call this method in its superclass.
58 */
59 public Assembler reset() {
60 boundLabels.clear();
61 assembledObjects.clear();
62 mutableAssembledObjects.clear();
63 padOutput = false;
64 potentialExpansionSize = 0;
65 selectingLabelInstructions = true;
66 stream.reset();
67 if (this instanceof Assembler32) {
68 final Assembler32 assembler32 = (Assembler32) this;
69 assembler32.setStartAddress(0);
70 } else if (this instanceof Assembler64) {
71 final Assembler64 assembler64 = (Assembler64) this;
72 assembler64.setStartAddress(0);
73 }
74 return this;
75 }
76
77 /**
78 * A facility for including output during assembly that may not necessarily be decoded interpreted as code.
79 *
80 */
81 public final class Directives {
82
83 private final byte padByte;
84 private final boolean isValidCode;
85
86 public Directives(byte padByte, boolean isValidCode) {
87 this.padByte = padByte;
88 this.isValidCode = isValidCode;
89 }
90 /**
91 * Inserts as many {@linkplain #padByte pad bytes} as necessary to ensure that the next assembled object starts
92 * at an address aligned by a given number.
93 *
94 * @param alignment
95 * the next assembled object is guaranteed to start at the next highest address starting at the
96 * current address that is divisible by this value. Note that this computed address will be the
97 * current address if the current address is already aligned by {@code alignment}
98 */
99 private void alignIfSpaceLeftSmallerThan(int alignment, int requiredSpace, int alignmentStart) {
100 final int startPosition = currentPosition();
101
102 // We avoid sign problems with '%' below by masking off the sign bit:
103 final long unsignedAddend = (baseAddress() + startPosition + alignmentStart) & Long.MAX_VALUE;
104
105 final int misalignmentSize = (int) (unsignedAddend % alignment);
106 final int padSize = misalignmentSize > 0 ? (alignment - misalignmentSize) : 0;
107 for (int i = 0; i < padSize; i++) {
108 emitByte(padByte);
109 }
110 new AlignmentPadding(Assembler.this, startPosition, currentPosition(), alignment, alignmentStart, requiredSpace, padByte) {
111 public boolean isCode() {
112 return isValidCode;
113 }
114 };
115 }
116
117 public void align(int alignment) {
118 alignIfSpaceLeftSmallerThan(alignment, alignment, 0);
119 }
120
121 /**
122 * Enforce that the next assembled object fits within an aligned chunk.
123 * The specified space required by the next assembled object must be smaller or equals to the specified alignment. If the object cannot fit in the current chunk,
124 * Inserts as many {@linkplain #padByte pad bytes} as necessary to ensure that the next assembled object starts
125 * at the next alignment.
126 *
127 * @param alignment
128 * the next assembled object is guaranteed to fit in within a block of memory whose starting address is the next
129 * address starting at the
130 * current address that is divisible by this value.
131 * @param requiredSpace size of the next assembled object; it must be smaller or equals to alignment
132 */
133 public boolean align(int alignment, int requiredSpace) {
134 if (alignment < requiredSpace) {
135 return false;
136 }
137 alignIfSpaceLeftSmallerThan(alignment, requiredSpace, 0);
138 return true;
139 }
140
141 /**
142 * Inserts as many {@linkplain #padByte pad bytes} as necessary to ensure that the nth byte within the next assembled object starts
143 * at an address aligned by a given number.
144 * @param alignment
145 * @param alignmentStart where the alignment should start within the next assembled object.
146 */
147 public void alignAfter(int alignment, int alignmentStart) {
148 alignIfSpaceLeftSmallerThan(alignment, alignment, alignmentStart);
149 }
150
151 public void inlineByte(byte byteValue) {
152 addInlineData(currentPosition(), Bytes.SIZE);
153 emitByte(byteValue);
154 }
155
156 public void inlineByteArray(byte[] byteArrayValue) {
157 addInlineData(currentPosition(), byteArrayValue.length);
158 emitByteArray(byteArrayValue, 0, byteArrayValue.length);
159 }
160
161 public void inlineByteArray(byte[] byteArrayValue, int offset, int length) {
162 addInlineData(currentPosition(), length);
163 emitByteArray(byteArrayValue, offset, length);
164 }
165
166 public void inlineShort(short shortValue) {
167 addInlineData(currentPosition(), Shorts.SIZE);
168 emitShort(shortValue);
169 }
170
171 public void inlineInt(int intValue) {
172 addInlineData(currentPosition(), Ints.SIZE);
173 emitInt(intValue);
174 }
175
176 public void inlineLong(long longValue) {
177 addInlineData(currentPosition(), Longs.SIZE);
178 emitLong(longValue);
179 }
180
181 /**
182 * Inlines the absolute address of a position (represented by a given label) in the assembled code.
183 * The absolute address is calculated as {@code baseAddress() + label.position()}. The size
184 * of the inlined address is determined by {@link Assembler#wordWidth()}.
185 *
186 * @param label the label whose absolute address is to be inlined
187 */
188 public AddressLiteral inlineAddress(Label label) {
189 final int startPosition = currentPosition();
190 // Emit placeholder bytes
191 final WordWidth width = wordWidth();
192 for (int i = 0; i < width.numberOfBytes; i++) {
193 emitByte((byte) 0);
194 }
195 final AddressLiteral addressLiteral = new AddressLiteral(Assembler.this, startPosition, currentPosition(), label);
196 assert addressLiteral.size() == width.numberOfBytes;
197 return addressLiteral;
198 }
199
200 /**
201 * Inlines the offset between two positions (represented by given labels) in the assembled code.
202 *
203 * @param base the label whose position marks the base of the offset
204 * @param target the label whose position marks the target of the offset
205 * @param width the fixed size to be used for the offset
206 */
207 public OffsetLiteral inlineOffset(Label target, Label base, WordWidth width) {
208 final int startPosition = currentPosition();
209 for (int i = 0; i < width.numberOfBytes; i++) {
210 emitByte((byte) 0);
211 }
212 final OffsetLiteral offsetLiteral = new OffsetLiteral(Assembler.this, startPosition, currentPosition(), target, base);
213 assert offsetLiteral.size() == width.numberOfBytes;
214 return offsetLiteral;
215 }
216 }
217
218 /**
219 * Gets the number of bytes that have been written to the underlying output stream.
220 */
221 public int currentPosition() {
222 return stream.size();
223 }
224
225 /**
226 * Gets the start address of the code assembled by this assembler.
227 */
228 public abstract long baseAddress();
229
230 private ByteArrayOutputStream stream = new ByteArrayOutputStream();
231
232 protected void emitByte(int byteValue) {
233 stream.write(byteValue);
234 }
235
236 protected void emitZeroes(int count) {
237 for (int i = 0; i < count; ++i) {
238 stream.write(0);
239 }
240 }
241
242 protected abstract void emitShort(short shortValue);
243
244 protected abstract void emitInt(int intValue);
245
246 protected abstract void emitLong(long longValue);
247
248 public void emitByteArray(byte[] byteArrayValue, int off, int len) {
249 stream.write(byteArrayValue, off, len);
250 }
251
252 private boolean selectingLabelInstructions = true;
253
254 boolean selectingLabelInstructions() {
255 return selectingLabelInstructions;
256 }
257
258 private final Set<Label> boundLabels = Collections.newSetFromMap(new IdentityHashMap<Label, Boolean>());
259
260 public Set<Label> boundLabels() {
261 return boundLabels;
262 }
263
264 /**
265 * Binds a given label to the current position in the assembler's instruction stream. The assembler may update the
266 * label's position if any emitted instructions change lengths, so that this label keeps addressing the same logical
267 * position.
268 *
269 * @param label
270 * the label that is to be bound to the current position
271 *
272 * @see Label#fix32
273 */
274 public final void bindLabel(Label label) {
275 label.bind(currentPosition());
276 boundLabels.add(label);
277 }
278
279 private final List<AssembledObject> assembledObjects = new LinkedList<AssembledObject>();
280 private final List<MutableAssembledObject> mutableAssembledObjects = new LinkedList<MutableAssembledObject>();
281
282 private int potentialExpansionSize;
283
284 /**
285 * Adds the description of an instruction that is fixed in size.
286 *
287 * @param fixedSizeAssembledObject
288 */
289 void addFixedSizeAssembledObject(AssembledObject fixedSizeAssembledObject) {
290 assembledObjects.add(fixedSizeAssembledObject);
291 if (fixedSizeAssembledObject instanceof MutableAssembledObject) {
292 mutableAssembledObjects.add((MutableAssembledObject) fixedSizeAssembledObject);
293 }
294 }
295
296 /**
297 * Adds the description of an instruction that can change in size, depending on where it is located in the
298 * instruction and/or where another object it addresses is located in the instruction stream.
299 *
300 * @param spanDependentInstruction
301 */
302 void addSpanDependentInstruction(InstructionWithOffset spanDependentInstruction) {
303 assembledObjects.add(spanDependentInstruction);
304 mutableAssembledObjects.add(spanDependentInstruction);
305 // A span-dependent instruction's offset operand can potentially grow from 8 bits to 32 bits.
306 // Also, some instructions need an extra byte for encoding when not using an 8-bit operand.
307 // Together, this might enlarge every span-dependent label instruction by maximally 4 bytes.
308 potentialExpansionSize += 4;
309 }
310
311 void addAlignmentPadding(AlignmentPadding alignmentPadding) {
312 assembledObjects.add(alignmentPadding);
313 mutableAssembledObjects.add(alignmentPadding);
314 potentialExpansionSize += alignmentPadding.alignment() - alignmentPadding.size();
315 }
316
317 void addInlineData(int startPosition, int size) {
318 assembledObjects.add(new AssembledObject(startPosition, startPosition + size) {
319 public boolean isCode() {
320 return false;
321 }
322 });
323 }
324
325 private void gatherLabels() throws AssemblyException {
326 for (AssembledObject assembledObject : assembledObjects) {
327 if (assembledObject instanceof InstructionWithLabel) {
328 final InstructionWithLabel labelInstruction = (InstructionWithLabel) assembledObject;
329 switch (labelInstruction.label().state()) {
330 case UNASSIGNED:
331 throw new AssemblyException("unassigned label");
332 case BOUND:
333 boundLabels.add(labelInstruction.label());
334 break;
335 default:
336 break;
337 }
338 }
339 }
340 }
341
342 private boolean updateSpanDependentInstruction(InstructionWithOffset instruction) throws AssemblyException {
343 if (!instruction.updateLabelSize()) {
344 return false;
345 }
346 final int oldSize = instruction.size();
347 final int oldEndPosition = instruction.endPosition();
348 stream.reset();
349 instruction.assemble();
350 final int newSize = stream.toByteArray().length;
351 instruction.setSize(newSize);
352 final int delta = newSize - oldSize;
353 adjustMutableAssembledObjects(delta, oldEndPosition, null);
354 return true;
355 }
356
357 private boolean updateAlignmentPadding(AlignmentPadding alignmentPadding) throws AssemblyException {
358 final int oldSize = alignmentPadding.size();
359 final int oldEndPosition = alignmentPadding.endPosition();
360 alignmentPadding.updatePadding();
361 final int newSize = alignmentPadding.size();
362 if (oldSize != newSize) {
363 // Only if the padding expanded will subsequent objects in the stream need to be adjusted
364 final int delta = newSize - oldSize;
365 adjustMutableAssembledObjects(delta, oldEndPosition, alignmentPadding);
366 return true;
367 }
368 return false;
369 }
370
371 /**
372 * Adjusts the position of all the mutable objects that are currently at or after a given position.
373 *
374 * @param delta the amount by which the position of each mutable object is to be adjusted
375 * @param startPosition only mutable objects whose current {@linkplain AssembledObject#startPosition() start
376 * position} is equal to or greater than this value are adjusted
377 * @param adjustedPadding the padding object whose adjustment caused the need for this call. If this call was made
378 * for some other reason than having adjusted a padding object's size, then this value will be null. In this
379 * value is not null, then its position will not be adjusted by this call.
380 */
381 private void adjustMutableAssembledObjects(int delta, int startPosition, AlignmentPadding adjustedPadding) throws AssemblyException {
382 for (Label label : boundLabels) {
383 if (label.position() >= startPosition) {
384 label.adjust(delta);
385 }
386 }
387
388 for (MutableAssembledObject mutableAssembledObject : mutableAssembledObjects) {
389 if (mutableAssembledObject != adjustedPadding && mutableAssembledObject.startPosition() >= startPosition) {
390 mutableAssembledObject.adjust(delta);
391 }
392 }
393 }
394
395 private void updateSpanDependentVariableInstructions() throws AssemblyException {
396 boolean changed;
397 do {
398 changed = false;
399 for (MutableAssembledObject mutableAssembledObject : mutableAssembledObjects) {
400 if (mutableAssembledObject instanceof InstructionWithOffset) {
401 changed |= updateSpanDependentInstruction((InstructionWithOffset) mutableAssembledObject);
402 } else if (mutableAssembledObject instanceof AlignmentPadding) {
403 changed |= updateAlignmentPadding((AlignmentPadding) mutableAssembledObject);
404 }
405 }
406 } while (changed);
407 }
408
409 private int writeOutput(OutputStream outputStream, byte[] initialBytes, InlineDataRecorder inlineDataRecorder) throws IOException, AssemblyException {
410 selectingLabelInstructions = false;
411 int bytesWritten = 0;
412 try {
413 int initialOffset = 0;
414 for (AssembledObject assembledObject : assembledObjects) {
415 if (inlineDataRecorder != null && !assembledObject.isCode()) {
416 inlineDataRecorder.add(new InlineDataDescriptor.ByteData(assembledObject.startPosition(), assembledObject.size()));
417 }
418
419 if (assembledObject instanceof MutableAssembledObject) {
420 final MutableAssembledObject mutableAssembledObject = (MutableAssembledObject) assembledObject;
421
422 // Copy the original assembler output between the end of the last mutable assembled object and the start of the current one
423 final int length = mutableAssembledObject.initialStartPosition() - initialOffset;
424 outputStream.write(initialBytes, initialOffset, length);
425 bytesWritten += length;
426
427 // Now (re)assemble the mutable assembled object
428 stream.reset();
429 mutableAssembledObject.assemble();
430 stream.writeTo(outputStream);
431 bytesWritten += stream.size();
432 initialOffset = mutableAssembledObject.initialEndPosition();
433 } else {
434 // Copy the original assembler output between the end of the last assembled object and the end of current one
435 final int length = assembledObject.endPosition() - initialOffset;
436 outputStream.write(initialBytes, initialOffset, length);
437 bytesWritten += length;
438 initialOffset = assembledObject.endPosition();
439 }
440 }
441
442 // Copy the original assembler output (if any) after the last mutable assembled object
443 outputStream.write(initialBytes, initialOffset, initialBytes.length - initialOffset);
444 bytesWritten += initialBytes.length - initialOffset;
445
446 if (padOutput) {
447 final int padding = (initialBytes.length + potentialExpansionSize) - bytesWritten;
448 assert padding >= 0;
449 if (padding > 0) {
450 stream.reset();
451 emitPadding(padding);
452 stream.writeTo(outputStream);
453 bytesWritten += padding;
454 }
455 }
456 return bytesWritten;
457 } finally {
458 selectingLabelInstructions = true;
459 }
460 }
461
462 /**
463 * Emits padding to the instruction stream in the form of NOP instructions.
464 *
465 * @param numberOfBytes
466 * @throws AssemblyException if exactly {@code numberOfBytes} cannot be emitted as a sequence of one or more valid NOP instructions
467 */
468 protected abstract void emitPadding(int numberOfBytes) throws AssemblyException;
469
470 /**
471 * Writes the object code assembled so far to a given output stream.
472 *
473 * @return the number of bytes written {@code outputStream}
474 * @throws AssemblyException
475 * if there any problem with binding labels to addresses
476 */
477 public int output(OutputStream outputStream, InlineDataRecorder inlineDataRecorder) throws IOException, AssemblyException {
478 final int upperLimitForCurrentOutputSize = upperLimitForCurrentOutputSize();
479 final byte[] initialBytes = stream.toByteArray();
480 gatherLabels();
481 updateSpanDependentVariableInstructions();
482 final int bytesWritten = writeOutput(outputStream, initialBytes, inlineDataRecorder);
483 assert !padOutput || upperLimitForCurrentOutputSize == bytesWritten;
484 return bytesWritten;
485 }
486
487 /**
488 * Gets the maximum size of the code array that would be assembled by a call to {@link #output(OutputStream)} or
489 * {@link #toByteArray()}. For a variable sized instruction set (e.g. x86), the exact size may be known until the
490 * code is assembled as the size of certain instructions depends on their position in the instruction and/or the
491 * {@linkplain #baseAddress() base address} at which the code is being assembled.
492 * <p>
493 * <b>Note that any subsequent call that adds a new instruction to the instruction stream invalidates the value
494 * returned by this method.</b>
495 */
496 public int upperLimitForCurrentOutputSize() {
497 return currentPosition() + potentialExpansionSize;
498 }
499
500 private boolean padOutput;
501
502 /**
503 * Sets or unsets the flag determining if the code assembled by a call to {@link #output(OutputStream)} or
504 * {@link #toByteArray()} should be padded with NOPs at the end to ensure that the code size equals the value
505 * returned by {@link #upperLimitForCurrentOutputSize()}. This default value of the flag is {@code false}.
506 */
507 public void setPadOutput(boolean flag) {
508 padOutput = flag;
509 }
510
511 /**
512 * Returns the object code assembled so far in a byte array.
513 *
514 * @throws AssemblyException
515 * if there any problem with binding labels to addresses
516 */
517 public byte[] toByteArray(InlineDataRecorder inlineDataRecorder) throws AssemblyException {
518 final ByteArrayOutputStream baos = new ByteArrayOutputStream(upperLimitForCurrentOutputSize());
519 try {
520 output(baos, inlineDataRecorder);
521 baos.close();
522 final byte[] result = baos.toByteArray();
523 return result;
524 } catch (IOException ioException) {
525 throw ProgramError.unexpected("IOException during output to byte array", ioException);
526 }
527 }
528
529 public byte[] toByteArray() throws AssemblyException {
530 return toByteArray(null);
531 }
532
533 /**
534 * @see Label#fix32(int)
535 */
536 protected void fixLabel32(Label label, int address32) {
537 label.fix32(address32);
538 }
539
540 /**
541 * @see Label#fix64(long)
542 */
543 protected void fixLabel64(Label label, long address64) {
544 label.fix64(address64);
545 }
546
547 protected int address32(Label label) throws AssemblyException {
548 return label.address32();
549 }
550
551 protected long address64(Label label) throws AssemblyException {
552 return label.address64();
553 }
554
555 protected void checkConstraint(boolean passed, String expression) {
556 if (!passed) {
557 throw new IllegalArgumentException(expression);
558 }
559 }
560
561 /**
562 * Calculate the difference between two Labels. This works whether the labels
563 * are fixed or bound.
564 * @throws AssemblyException
565 */
566 public int labelOffsetRelative(Label label, Label relativeTo) throws AssemblyException {
567 return labelOffsetRelative(label, 0) - labelOffsetRelative(relativeTo, 0);
568 }
569
570 /**
571 * Calculate the difference between a Label and a position within the assembled code.
572 * @throws AssemblyException
573 */
574 public int labelOffsetRelative(Label label, int position) throws AssemblyException {
575 switch (label.state()) {
576 case BOUND: {
577 return label.position() - position;
578 }
579 case FIXED_32: {
580 final Assembler32 assembler32 = (Assembler32) this;
581 return assembler32.address(label) - (assembler32.startAddress() + position);
582 }
583 case FIXED_64: {
584 final Assembler64 assembler64 = (Assembler64) this;
585 final long offset64 = assembler64.address(label) - (assembler64.startAddress() + position);
586 if (Longs.numberOfEffectiveSignedBits(offset64) > 32) {
587 throw new AssemblyException("fixed 64-bit label out of 32-bit range");
588 }
589 return (int) offset64;
590 }
591 default: {
592 throw new AssemblyException("unassigned label");
593 }
594 }
595 }
596
597 /**
598 * Calculate the difference between a Label and an assembled object.
599 * Different CPUs have different conventions for which end of an
600 * instruction to measure from.
601 * @throws AssemblyException
602 */
603 public final int offsetInstructionRelative(Label label, AssemblyObject assembledObject) throws AssemblyException {
604 final int position = (isa().relativeBranchFromStart) ? assembledObject.startPosition() : assembledObject.endPosition();
605 return labelOffsetRelative(label, position);
606 }
607 }