Source code: Allocator/RuntimeCodeAllocator.java
1 // RuntimeCodeAllocator.java, created Tue Feb 27 2:53:11 2001 by joewhaley
2 // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3 // Licensed under the terms of the GNU LGPL; see COPYING for details.
4 package Allocator;
5
6 import java.util.List;
7
8 import Clazz.jq_BytecodeMap;
9 import Clazz.jq_CompiledCode;
10 import Clazz.jq_Method;
11 import Clazz.jq_TryCatch;
12 import Memory.Address;
13 import Memory.CodeAddress;
14 import Run_Time.ExceptionDeliverer;
15 import Run_Time.SystemInterface;
16 import Util.Assert;
17 import Util.Strings;
18
19 /**
20 * RuntimeCodeAllocator
21 *
22 * @author John Whaley <jwhaley@alum.mit.edu>
23 * @version $Id: RuntimeCodeAllocator.java,v 1.19 2003/05/12 10:04:52 joewhaley Exp $
24 */
25 public class RuntimeCodeAllocator extends CodeAllocator {
26
27 // Memory layout:
28 //
29 // start: | next ptr |---->
30 // | end ptr |
31 // |.....data.....|
32 // current: |.....free.....|
33 // end: | current ptr |
34
35 /** Size of blocks allocated from the OS.
36 */
37 public static final int BLOCK_SIZE = 131072;
38
39 /** Pointers to the start, current, and end of the heap.
40 */
41 private CodeAddress heapStart, heapCurrent, heapEnd;
42
43 /** Pointer to the first block.
44 */
45 private CodeAddress heapFirst;
46
47 /** Max memory free in all allocated blocks.
48 */
49 private int maxFreePrevious;
50
51 volatile boolean isGenerating = false;
52
53 public void init()
54 throws OutOfMemoryError {
55 heapStart = heapFirst = (CodeAddress)SystemInterface.syscalloc(BLOCK_SIZE);
56 if (heapStart.isNull())
57 HeapAllocator.outOfMemory();
58 heapStart.poke(null);
59 heapEnd = (CodeAddress)heapStart.offset(BLOCK_SIZE - CodeAddress.size());
60 heapStart.offset(CodeAddress.size()).poke(heapEnd);
61 heapCurrent = (CodeAddress)heapStart.offset(CodeAddress.size() * 2);
62 heapEnd.poke(heapCurrent);
63 }
64
65 /** Allocate a code buffer of the given estimated size, such that the given
66 * offset will have the given alignment.
67 * It is legal for code to exceed the estimated size, but the cost may be
68 * high (i.e. it may require recopying of the buffer.)
69 *
70 * @param estimatedSize estimated size, in bytes, of desired code buffer
71 * @param offset desired offset to align to
72 * @param alignment desired alignment, or 0 if don't care
73 * @return the new code buffer
74 */
75 public x86CodeBuffer getCodeBuffer(int estimatedSize,
76 int offset,
77 int alignment) {
78 // should not be called recursively.
79 Assert._assert(!isGenerating);
80 if (TRACE) SystemInterface.debugwriteln("Code generation started: "+this);
81 isGenerating = true;
82 // align pointer
83 CodeAddress entrypoint = (CodeAddress)heapCurrent.offset(offset);
84 if (alignment > 0) entrypoint.align(alignment);
85 if (entrypoint.offset(estimatedSize - offset).difference(heapEnd) <= 0) {
86 return new Runtimex86CodeBuffer((CodeAddress)entrypoint.offset(-offset), heapEnd);
87 }
88 if (estimatedSize < maxFreePrevious) {
89 // use a prior block's unused space.
90 if (TRACE) SystemInterface.debugwriteln("Estimated size ("+Strings.hex(estimatedSize)+" fits within a prior block: maxfreeprev="+Strings.hex(maxFreePrevious));
91 // start searching at the first block
92 CodeAddress start_ptr = heapFirst;
93 for (;;) {
94 // start_ptr: points to start of current block
95 // end_ptr: points to end of current block
96 // current_ptr: current pointer for current block
97 Assert._assert(!start_ptr.isNull());
98 CodeAddress end_ptr = (CodeAddress)start_ptr.offset(CodeAddress.size()).peek();
99 CodeAddress current_ptr = (CodeAddress)end_ptr.peek();
100 if (end_ptr.difference(current_ptr) >= estimatedSize) {
101 return new Runtimex86CodeBuffer(current_ptr, end_ptr);
102 }
103 start_ptr = (CodeAddress)start_ptr.peek(); // go to the next block
104 }
105 }
106 // allocate new block.
107 allocateNewBlock(Math.max(estimatedSize, BLOCK_SIZE));
108 return new Runtimex86CodeBuffer(heapCurrent, heapEnd);
109 }
110
111 private void allocateNewBlock(int blockSize)
112 throws OutOfMemoryError {
113 heapStart.offset(CodeAddress.size()).poke(heapCurrent);
114 CodeAddress newBlock = (CodeAddress)SystemInterface.syscalloc(blockSize);
115 if (newBlock.isNull())
116 HeapAllocator.outOfMemory();
117 heapStart.poke(newBlock);
118 heapStart = newBlock;
119 heapStart.poke(null);
120 heapEnd = (CodeAddress)newBlock.offset(blockSize - CodeAddress.size());
121 heapStart.offset(CodeAddress.size()).poke(heapEnd);
122 heapCurrent = (CodeAddress)newBlock.offset(CodeAddress.size() * 2);
123 heapEnd.poke(heapCurrent);
124 }
125
126 public void patchAbsolute(Address addr1, Address addr2) {
127 addr1.poke(addr2);
128 }
129 public void patchRelativeOffset(CodeAddress code, CodeAddress target) {
130 code.poke4(target.difference(code)-4);
131 }
132
133 public class Runtimex86CodeBuffer extends CodeAllocator.x86CodeBuffer {
134
135 private CodeAddress startAddress;
136 private CodeAddress entrypointAddress;
137 private CodeAddress currentAddress;
138 private CodeAddress endAddress;
139
140 Runtimex86CodeBuffer(CodeAddress startAddress, CodeAddress endAddress) {
141 this.startAddress = startAddress;
142 this.endAddress = endAddress;
143 this.currentAddress = (CodeAddress)startAddress.offset(-1);
144 }
145
146 public int getCurrentOffset() { return currentAddress.difference(startAddress) + 1; }
147 public CodeAddress getStartAddress() { return startAddress; }
148 public CodeAddress getCurrentAddress() { return (CodeAddress)currentAddress.offset(1); }
149
150 public CodeAddress getStart() { return startAddress; }
151 public CodeAddress getCurrent() { return (CodeAddress)currentAddress.offset(1); }
152 public CodeAddress getEntry() { return entrypointAddress; }
153 public CodeAddress getEnd() { return endAddress; }
154
155 public void setEntrypoint() { this.entrypointAddress = getCurrent(); }
156
157 public void checkSize(int size) {
158 if (currentAddress.offset(size).difference(endAddress) < 0) return;
159 // overflow!
160 int newEstimatedSize = endAddress.difference(startAddress) << 1;
161 allocateNewBlock(Math.max(BLOCK_SIZE, newEstimatedSize));
162 Assert._assert(currentAddress.difference(startAddress)+size < heapEnd.difference(heapCurrent));
163 SystemInterface.mem_cpy(heapCurrent, startAddress, currentAddress.difference(startAddress));
164 if (!entrypointAddress.isNull())
165 entrypointAddress = (CodeAddress)heapCurrent.offset(entrypointAddress.difference(startAddress));
166 currentAddress = (CodeAddress)heapCurrent.offset(currentAddress.difference(startAddress));
167 startAddress = heapCurrent;
168 endAddress = heapEnd;
169 }
170
171 public void add1(byte i) {
172 checkSize(1);
173 currentAddress = (CodeAddress)currentAddress.offset(1);
174 currentAddress.poke1(i);
175 }
176 public void add2_endian(int i) {
177 checkSize(2);
178 currentAddress.offset(1).poke2((short)i);
179 currentAddress = (CodeAddress)currentAddress.offset(2);
180 }
181 public void add2(int i) {
182 checkSize(2);
183 currentAddress.offset(1).poke2(endian2(i));
184 currentAddress = (CodeAddress)currentAddress.offset(2);
185 }
186 public void add3(int i) {
187 checkSize(3);
188 currentAddress.offset(1).poke1((byte)(i >> 16));
189 currentAddress.offset(2).poke2(endian2(i));
190 currentAddress = (CodeAddress)currentAddress.offset(3);
191 }
192 public void add4_endian(int i) {
193 checkSize(4);
194 currentAddress.offset(1).poke4(i);
195 currentAddress = (CodeAddress)currentAddress.offset(4);
196 }
197
198 public byte get1(int k) {
199 return startAddress.offset(k).peek1();
200 }
201 public int get4_endian(int k) {
202 return startAddress.offset(k).peek4();
203 }
204
205 public void put1(int k, byte instr) {
206 startAddress.offset(k).poke1(instr);
207 }
208 public void put4_endian(int k, int instr) {
209 startAddress.offset(k).poke4(instr);
210 }
211
212 public void skip(int nbytes) {
213 currentAddress = (CodeAddress)currentAddress.offset(nbytes);
214 }
215
216 public jq_CompiledCode allocateCodeBlock(jq_Method m, jq_TryCatch[] ex,
217 jq_BytecodeMap bcm, ExceptionDeliverer exd,
218 int stackframesize,
219 List code_relocs, List data_relocs) {
220 Assert._assert(isGenerating);
221 CodeAddress start = getStart();
222 CodeAddress entrypoint = getEntry();
223 CodeAddress current = getCurrent();
224 CodeAddress end = getEnd();
225 Assert._assert(current.difference(end) <= 0);
226 if (end != heapEnd) {
227 if (TRACE) SystemInterface.debugwriteln("Prior block, recalculating maxfreeprevious (was "+Strings.hex(maxFreePrevious)+")");
228 // prior block
229 end.poke(current);
230 // recalculate max free previous
231 maxFreePrevious = 0;
232 CodeAddress start_ptr = heapFirst;
233 while (!start_ptr.isNull()) {
234 CodeAddress end_ptr = (CodeAddress)start_ptr.offset(CodeAddress.size());
235 CodeAddress current_ptr = (CodeAddress)end_ptr.peek();
236 int temp = end_ptr.difference(current_ptr);
237 maxFreePrevious = Math.max(maxFreePrevious, temp);
238 start_ptr = (CodeAddress)start_ptr.peek();
239 }
240 if (TRACE) SystemInterface.debugwriteln("New maxfreeprevious: "+Strings.hex(maxFreePrevious));
241 } else {
242 // current block
243 heapCurrent = current;
244 heapEnd.poke(heapCurrent);
245 }
246 isGenerating = false;
247 if (TRACE) SystemInterface.debugwriteln("Code generation completed: "+this);
248 jq_CompiledCode cc = new jq_CompiledCode(m, start, current.difference(start), entrypoint, ex, bcm, exd, stackframesize, code_relocs, data_relocs);
249 CodeAllocator.registerCode(cc);
250 return cc;
251 }
252 }
253
254 public static short endian2(int k) {
255 return (short)(((k>>8)&0xFF) | (k<<8));
256 }
257 }