Mercurial > hg > graal-compiler
comparison graal/GraalCompiler/src/com/sun/c1x/gen/LIRGenerator.java @ 2509:16b9a8b5ad39
Renamings Runtime=>GraalRuntime and Compiler=>GraalCompiler
author | Thomas Wuerthinger <thomas@wuerthinger.net> |
---|---|
date | Wed, 27 Apr 2011 11:50:44 +0200 |
parents | graal/Compiler/src/com/sun/c1x/gen/LIRGenerator.java@9ec15d6914ca |
children | 4fdef1464592 |
comparison
equal
deleted
inserted
replaced
2508:fea94949e0a2 | 2509:16b9a8b5ad39 |
---|---|
1 /* | |
2 * Copyright (c) 2009, 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.c1x.gen; | |
24 | |
25 import static com.sun.cri.bytecode.Bytecodes.*; | |
26 import static com.sun.cri.bytecode.Bytecodes.MemoryBarriers.*; | |
27 import static com.sun.cri.ci.CiCallingConvention.Type.*; | |
28 import static com.sun.cri.ci.CiValue.*; | |
29 | |
30 import java.lang.reflect.*; | |
31 import java.util.*; | |
32 | |
33 import com.sun.c1x.*; | |
34 import com.sun.c1x.alloc.*; | |
35 import com.sun.c1x.alloc.OperandPool.VariableFlag; | |
36 import com.sun.c1x.asm.*; | |
37 import com.sun.c1x.debug.*; | |
38 import com.sun.c1x.globalstub.*; | |
39 import com.sun.c1x.graph.*; | |
40 import com.sun.c1x.ir.*; | |
41 import com.sun.c1x.ir.Value.Flag; | |
42 import com.sun.c1x.lir.FrameMap.StackBlock; | |
43 import com.sun.c1x.lir.*; | |
44 import com.sun.c1x.opt.*; | |
45 import com.sun.c1x.util.*; | |
46 import com.sun.c1x.value.*; | |
47 import com.sun.c1x.value.FrameState.PhiProcedure; | |
48 import com.sun.cri.bytecode.*; | |
49 import com.sun.cri.bytecode.Bytecodes.MemoryBarriers; | |
50 import com.sun.cri.ci.*; | |
51 import com.sun.cri.ci.CiAddress.Scale; | |
52 import com.sun.cri.ri.*; | |
53 import com.sun.cri.xir.CiXirAssembler.XirConstant; | |
54 import com.sun.cri.xir.CiXirAssembler.XirInstruction; | |
55 import com.sun.cri.xir.CiXirAssembler.XirOperand; | |
56 import com.sun.cri.xir.CiXirAssembler.XirParameter; | |
57 import com.sun.cri.xir.CiXirAssembler.XirRegister; | |
58 import com.sun.cri.xir.CiXirAssembler.XirTemp; | |
59 import com.sun.cri.xir.*; | |
60 | |
61 /** | |
62 * This class traverses the HIR instructions and generates LIR instructions from them. | |
63 * | |
64 * @author Thomas Wuerthinger | |
65 * @author Ben L. Titzer | |
66 * @author Marcelo Cintra | |
67 * @author Doug Simon | |
68 */ | |
69 public abstract class LIRGenerator extends ValueVisitor { | |
70 | |
71 /** | |
72 * Helper class for inserting memory barriers as necessary to implement the Java Memory Model | |
73 * with respect to volatile field accesses. | |
74 * | |
75 * @see MemoryBarriers | |
76 */ | |
77 class VolatileMemoryAccess { | |
78 /** | |
79 * Inserts any necessary memory barriers before a volatile write as required by the JMM. | |
80 */ | |
81 void preVolatileWrite() { | |
82 int barriers = compilation.target.arch.requiredBarriers(JMM_PRE_VOLATILE_WRITE); | |
83 if (compilation.target.isMP && barriers != 0) { | |
84 lir.membar(barriers); | |
85 } | |
86 } | |
87 | |
88 /** | |
89 * Inserts any necessary memory barriers after a volatile write as required by the JMM. | |
90 */ | |
91 void postVolatileWrite() { | |
92 int barriers = compilation.target.arch.requiredBarriers(JMM_POST_VOLATILE_WRITE); | |
93 if (compilation.target.isMP && barriers != 0) { | |
94 lir.membar(barriers); | |
95 } | |
96 } | |
97 | |
98 /** | |
99 * Inserts any necessary memory barriers before a volatile read as required by the JMM. | |
100 */ | |
101 void preVolatileRead() { | |
102 int barriers = compilation.target.arch.requiredBarriers(JMM_PRE_VOLATILE_READ); | |
103 if (compilation.target.isMP && barriers != 0) { | |
104 lir.membar(barriers); | |
105 } | |
106 } | |
107 | |
108 /** | |
109 * Inserts any necessary memory barriers after a volatile read as required by the JMM. | |
110 */ | |
111 void postVolatileRead() { | |
112 // Ensure field's data is loaded before any subsequent loads or stores. | |
113 int barriers = compilation.target.arch.requiredBarriers(LOAD_LOAD | LOAD_STORE); | |
114 if (compilation.target.isMP && barriers != 0) { | |
115 lir.membar(barriers); | |
116 } | |
117 } | |
118 } | |
119 | |
120 /** | |
121 * Forces the result of a given instruction to be available in a given register, | |
122 * inserting move instructions if necessary. | |
123 * | |
124 * @param instruction an instruction that produces a {@linkplain Value#operand() result} | |
125 * @param register the {@linkplain CiRegister} in which the result of {@code instruction} must be available | |
126 * @return {@code register} as an operand | |
127 */ | |
128 protected CiValue force(Value instruction, CiRegister register) { | |
129 return force(instruction, register.asValue(instruction.kind)); | |
130 } | |
131 | |
132 /** | |
133 * Forces the result of a given instruction to be available in a given operand, | |
134 * inserting move instructions if necessary. | |
135 * | |
136 * @param instruction an instruction that produces a {@linkplain Value#operand() result} | |
137 * @param operand the operand in which the result of {@code instruction} must be available | |
138 * @return {@code operand} | |
139 */ | |
140 protected CiValue force(Value instruction, CiValue operand) { | |
141 CiValue result = makeOperand(instruction); | |
142 if (result != operand) { | |
143 assert result.kind != CiKind.Illegal; | |
144 if (!compilation.archKindsEqual(result.kind, operand.kind)) { | |
145 // moves between different types need an intervening spill slot | |
146 CiValue tmp = forceToSpill(result, operand.kind, false); | |
147 lir.move(tmp, operand); | |
148 } else { | |
149 lir.move(result, operand); | |
150 } | |
151 } | |
152 return operand; | |
153 } | |
154 | |
155 protected CiValue load(Value val) { | |
156 CiValue result = makeOperand(val); | |
157 if (!result.isVariableOrRegister()) { | |
158 CiVariable operand = newVariable(val.kind); | |
159 lir.move(result, operand); | |
160 return operand; | |
161 } | |
162 return result; | |
163 } | |
164 | |
165 // the range of values in a lookupswitch or tableswitch statement | |
166 private static final class SwitchRange { | |
167 final int lowKey; | |
168 int highKey; | |
169 final BlockBegin sux; | |
170 | |
171 SwitchRange(int lowKey, BlockBegin sux) { | |
172 this.lowKey = lowKey; | |
173 this.highKey = lowKey; | |
174 this.sux = sux; | |
175 } | |
176 } | |
177 | |
178 protected final C1XCompilation compilation; | |
179 protected final IR ir; | |
180 protected final XirSupport xirSupport; | |
181 protected final RiXirGenerator xir; | |
182 protected final boolean isTwoOperand; | |
183 | |
184 private BlockBegin currentBlock; | |
185 | |
186 public final OperandPool operands; | |
187 | |
188 private Value currentInstruction; | |
189 private Value lastInstructionPrinted; // Debugging only | |
190 | |
191 private List<CiConstant> constants; | |
192 private List<CiVariable> variablesForConstants; | |
193 protected LIRList lir; | |
194 final VolatileMemoryAccess vma; | |
195 private ArrayList<DeoptimizationStub> deoptimizationStubs; | |
196 | |
197 public LIRGenerator(C1XCompilation compilation) { | |
198 this.compilation = compilation; | |
199 this.ir = compilation.hir(); | |
200 this.xir = compilation.compiler.xir; | |
201 this.xirSupport = new XirSupport(); | |
202 this.isTwoOperand = compilation.target.arch.twoOperandMode(); | |
203 this.vma = new VolatileMemoryAccess(); | |
204 | |
205 constants = new ArrayList<CiConstant>(); | |
206 variablesForConstants = new ArrayList<CiVariable>(); | |
207 | |
208 this.operands = new OperandPool(compilation.target); | |
209 | |
210 // mark the liveness of all instructions if it hasn't already been done by the optimizer | |
211 LivenessMarker livenessMarker = new LivenessMarker(ir); | |
212 C1XMetrics.LiveHIRInstructions += livenessMarker.liveCount(); | |
213 } | |
214 | |
215 public ArrayList<DeoptimizationStub> deoptimizationStubs() { | |
216 return deoptimizationStubs; | |
217 } | |
218 | |
219 public static class DeoptimizationStub { | |
220 public final Label label = new Label(); | |
221 public final LIRDebugInfo info; | |
222 | |
223 public DeoptimizationStub(FrameState state) { | |
224 info = new LIRDebugInfo(state, null); | |
225 } | |
226 } | |
227 | |
228 public final void emitGuard(Guard x) { | |
229 FrameState state = x.stateBefore(); | |
230 assert state != null : "deoptimize instruction always needs a state"; | |
231 | |
232 if (deoptimizationStubs == null) { | |
233 deoptimizationStubs = new ArrayList<DeoptimizationStub>(); | |
234 } | |
235 | |
236 // (tw) TODO: Try to reuse an existing stub if possible. | |
237 // It is only allowed if there are no LIR instructions in between that can modify registers. | |
238 | |
239 DeoptimizationStub stub = new DeoptimizationStub(state); | |
240 deoptimizationStubs.add(stub); | |
241 lir.branch(x.condition.negate(), stub.label, stub.info); | |
242 } | |
243 | |
244 public void doBlock(BlockBegin block) { | |
245 blockDoProlog(block); | |
246 this.currentBlock = block; | |
247 | |
248 for (Instruction instr = block; instr != null; instr = instr.next()) { | |
249 if (instr.isLive()) { | |
250 walkState(instr, instr.stateBefore()); | |
251 doRoot(instr); | |
252 } | |
253 } | |
254 | |
255 this.currentBlock = null; | |
256 blockDoEpilog(block); | |
257 } | |
258 | |
259 @Override | |
260 public void visitArrayLength(ArrayLength x) { | |
261 emitArrayLength(x); | |
262 } | |
263 | |
264 public CiValue emitArrayLength(ArrayLength x) { | |
265 XirArgument array = toXirArgument(x.array()); | |
266 XirSnippet snippet = xir.genArrayLength(site(x), array); | |
267 emitXir(snippet, x, x.needsNullCheck() ? stateFor(x) : null, null, true); | |
268 return x.operand(); | |
269 } | |
270 | |
271 @Override | |
272 public void visitBase(Base x) { | |
273 // emit phi-instruction move after safepoint since this simplifies | |
274 // describing the state at the safepoint. | |
275 moveToPhi(x.stateAfter()); | |
276 | |
277 // all blocks with a successor must end with an unconditional jump | |
278 // to the successor even if they are consecutive | |
279 lir.jump(x.defaultSuccessor()); | |
280 } | |
281 | |
282 private void setOperandsForLocals(FrameState state) { | |
283 CiCallingConvention args = compilation.frameMap().incomingArguments(); | |
284 int javaIndex = 0; | |
285 for (int i = 0; i < args.locations.length; i++) { | |
286 CiValue src = args.locations[i]; | |
287 assert src.isLegal() : "check"; | |
288 | |
289 CiVariable dest = newVariable(src.kind.stackKind()); | |
290 lir.move(src, dest, src.kind); | |
291 | |
292 // Assign new location to Local instruction for this local | |
293 Value instr = state.localAt(javaIndex); | |
294 Local local = ((Local) instr); | |
295 CiKind kind = src.kind.stackKind(); | |
296 assert kind == local.kind.stackKind() : "local type check failed"; | |
297 if (local.isLive()) { | |
298 setResult(local, dest); | |
299 } | |
300 javaIndex += kind.jvmSlots; | |
301 } | |
302 } | |
303 | |
304 @Override | |
305 public void visitResolveClass(ResolveClass i) { | |
306 LIRDebugInfo info = stateFor(i); | |
307 XirSnippet snippet = xir.genResolveClass(site(i), i.type, i.portion); | |
308 emitXir(snippet, i, info, null, true); | |
309 } | |
310 | |
311 @Override | |
312 public void visitCheckCast(CheckCast x) { | |
313 XirArgument obj = toXirArgument(x.object()); | |
314 XirSnippet snippet = xir.genCheckCast(site(x), obj, toXirArgument(x.targetClassInstruction), x.targetClass()); | |
315 emitXir(snippet, x, stateFor(x), null, true); | |
316 } | |
317 | |
318 @Override | |
319 public void visitInstanceOf(InstanceOf x) { | |
320 XirArgument obj = toXirArgument(x.object()); | |
321 XirSnippet snippet = xir.genInstanceOf(site(x), obj, toXirArgument(x.targetClassInstruction), x.targetClass()); | |
322 emitXir(snippet, x, maybeStateFor(x), null, true); | |
323 } | |
324 | |
325 @Override | |
326 public void visitMonitorEnter(MonitorEnter x) { | |
327 XirArgument obj = toXirArgument(x.object()); | |
328 XirArgument lockAddress = toXirArgument(x.lockAddress()); | |
329 XirSnippet snippet = xir.genMonitorEnter(site(x), obj, lockAddress); | |
330 emitXir(snippet, x, maybeStateFor(x), stateFor(x, x.stateAfter()), null, true, null); | |
331 } | |
332 | |
333 @Override | |
334 public void visitMonitorExit(MonitorExit x) { | |
335 XirArgument obj = toXirArgument(x.object()); | |
336 XirArgument lockAddress = toXirArgument(x.lockAddress()); | |
337 XirSnippet snippet = xir.genMonitorExit(site(x), obj, lockAddress); | |
338 emitXir(snippet, x, maybeStateFor(x), null, true); | |
339 } | |
340 | |
341 @Override | |
342 public void visitStoreIndexed(StoreIndexed x) { | |
343 XirArgument array = toXirArgument(x.array()); | |
344 XirArgument length = x.length() == null ? null : toXirArgument(x.length()); | |
345 XirArgument index = toXirArgument(x.index()); | |
346 XirArgument value = toXirArgument(x.value()); | |
347 XirSnippet snippet = xir.genArrayStore(site(x), array, index, length, value, x.elementKind(), null); | |
348 emitXir(snippet, x, maybeStateFor(x), null, true); | |
349 } | |
350 | |
351 @Override | |
352 public void visitNewInstance(NewInstance x) { | |
353 XirSnippet snippet = xir.genNewInstance(site(x), x.instanceClass()); | |
354 emitXir(snippet, x, stateFor(x), null, true); | |
355 } | |
356 | |
357 @Override | |
358 public void visitNewTypeArray(NewTypeArray x) { | |
359 XirArgument length = toXirArgument(x.length()); | |
360 XirSnippet snippet = xir.genNewArray(site(x), length, x.elementKind(), null, null); | |
361 emitXir(snippet, x, stateFor(x), null, true); | |
362 } | |
363 | |
364 @Override | |
365 public void visitNewObjectArray(NewObjectArray x) { | |
366 XirArgument length = toXirArgument(x.length()); | |
367 XirSnippet snippet = xir.genNewArray(site(x), length, CiKind.Object, x.elementClass(), x.exactType()); | |
368 emitXir(snippet, x, stateFor(x), null, true); | |
369 } | |
370 | |
371 @Override | |
372 public void visitNewObjectArrayClone(NewObjectArrayClone x) { | |
373 XirArgument length = toXirArgument(x.length()); | |
374 XirArgument referenceArray = toXirArgument(x.referenceArray()); | |
375 XirSnippet snippet = xir.genNewObjectArrayClone(site(x), length, referenceArray); | |
376 emitXir(snippet, x, stateFor(x), null, true); | |
377 } | |
378 | |
379 @Override | |
380 public void visitNewMultiArray(NewMultiArray x) { | |
381 XirArgument[] dims = new XirArgument[x.dimensions().length]; | |
382 | |
383 for (int i = 0; i < dims.length; i++) { | |
384 dims[i] = toXirArgument(x.dimensions()[i]); | |
385 } | |
386 | |
387 XirSnippet snippet = xir.genNewMultiArray(site(x), dims, x.elementKind); | |
388 emitXir(snippet, x, stateFor(x), null, true); | |
389 } | |
390 | |
391 @Override | |
392 public void visitConstant(Constant x) { | |
393 if (canInlineAsConstant(x)) { | |
394 //setResult(x, loadConstant(x)); | |
395 } else { | |
396 CiValue res = x.operand(); | |
397 if (!(res.isLegal())) { | |
398 res = x.asConstant(); | |
399 } | |
400 if (res.isConstant()) { | |
401 if (isUsedForValue(x)) { | |
402 CiVariable reg = createResultVariable(x); | |
403 lir.move(res, reg); | |
404 } else { | |
405 assert x.checkFlag(Value.Flag.LiveDeopt); | |
406 x.setOperand(res); | |
407 } | |
408 } else { | |
409 setResult(x, (CiVariable) res); | |
410 } | |
411 } | |
412 } | |
413 | |
414 @Override | |
415 public void visitExceptionObject(ExceptionObject x) { | |
416 assert currentBlock.isExceptionEntry() : "ExceptionObject only allowed in exception handler block"; | |
417 assert currentBlock.next() == x : "ExceptionObject must be first instruction of block"; | |
418 | |
419 // no moves are created for phi functions at the begin of exception | |
420 // handlers, so assign operands manually here | |
421 currentBlock.stateBefore().forEachLivePhi(currentBlock, new PhiProcedure() { | |
422 public boolean doPhi(Phi phi) { | |
423 operandForPhi(phi); | |
424 return true; | |
425 } | |
426 }); | |
427 | |
428 XirSnippet snippet = xir.genExceptionObject(site(x)); | |
429 emitXir(snippet, x, maybeStateFor(x), null, true); | |
430 } | |
431 | |
432 @Override | |
433 public void visitGoto(Goto x) { | |
434 setNoResult(x); | |
435 | |
436 if (currentBlock.next() instanceof OsrEntry) { | |
437 // need to free up storage used for OSR entry point | |
438 CiValue osrBuffer = currentBlock.next().operand(); | |
439 callRuntime(CiRuntimeCall.OSRMigrationEnd, null, osrBuffer); | |
440 emitXir(xir.genSafepoint(site(x)), x, stateFor(x, x.stateAfter()), null, false); | |
441 } | |
442 | |
443 // emit phi-instruction moves after safepoint since this simplifies | |
444 // describing the state at the safepoint. | |
445 moveToPhi(x.stateAfter()); | |
446 | |
447 lir.jump(x.defaultSuccessor()); | |
448 } | |
449 | |
450 @Override | |
451 public void visitIfOp(IfOp i) { | |
452 Value x = i.x(); | |
453 Value y = i.y(); | |
454 CiKind xtype = x.kind; | |
455 CiKind ttype = i.trueValue().kind; | |
456 assert xtype.isInt() || xtype.isObject() : "cannot handle others"; | |
457 assert ttype.isInt() || ttype.isObject() || ttype.isLong() || ttype.isWord() : "cannot handle others"; | |
458 assert ttype.equals(i.falseValue().kind) : "cannot handle others"; | |
459 | |
460 CiValue left = load(x); | |
461 CiValue right = null; | |
462 if (!canInlineAsConstant(y)) { | |
463 right = load(y); | |
464 } else { | |
465 right = makeOperand(y); | |
466 } | |
467 | |
468 CiValue tVal = makeOperand(i.trueValue()); | |
469 CiValue fVal = makeOperand(i.falseValue()); | |
470 CiValue reg = createResultVariable(i); | |
471 | |
472 lir.cmp(i.condition(), left, right); | |
473 lir.cmove(i.condition(), tVal, fVal, reg); | |
474 } | |
475 | |
476 @Override | |
477 public void visitIntrinsic(Intrinsic x) { | |
478 Value[] vals = x.arguments(); | |
479 XirSnippet snippet; | |
480 | |
481 switch (x.intrinsic()) { | |
482 case java_lang_Float$intBitsToFloat: | |
483 case java_lang_Double$doubleToRawLongBits: | |
484 case java_lang_Double$longBitsToDouble: | |
485 case java_lang_Float$floatToRawIntBits: { | |
486 visitFPIntrinsics(x); | |
487 return; | |
488 } | |
489 | |
490 case java_lang_System$currentTimeMillis: { | |
491 assert x.numberOfArguments() == 0 : "wrong type"; | |
492 CiValue reg = callRuntimeWithResult(CiRuntimeCall.JavaTimeMillis, null, (CiValue[]) null); | |
493 CiValue result = createResultVariable(x); | |
494 lir.move(reg, result); | |
495 return; | |
496 } | |
497 | |
498 case java_lang_System$nanoTime: { | |
499 assert x.numberOfArguments() == 0 : "wrong type"; | |
500 CiValue reg = callRuntimeWithResult(CiRuntimeCall.JavaTimeNanos, null, (CiValue[]) null); | |
501 CiValue result = createResultVariable(x); | |
502 lir.move(reg, result); | |
503 return; | |
504 } | |
505 | |
506 case java_lang_Object$init: | |
507 visitRegisterFinalizer(x); | |
508 return; | |
509 | |
510 case java_lang_Math$log: // fall through | |
511 case java_lang_Math$log10: // fall through | |
512 case java_lang_Math$abs: // fall through | |
513 case java_lang_Math$sqrt: // fall through | |
514 case java_lang_Math$tan: // fall through | |
515 case java_lang_Math$sin: // fall through | |
516 case java_lang_Math$cos: | |
517 genMathIntrinsic(x); | |
518 return; | |
519 | |
520 case sun_misc_Unsafe$compareAndSwapObject: | |
521 genCompareAndSwap(x, CiKind.Object); | |
522 return; | |
523 case sun_misc_Unsafe$compareAndSwapInt: | |
524 genCompareAndSwap(x, CiKind.Int); | |
525 return; | |
526 case sun_misc_Unsafe$compareAndSwapLong: | |
527 genCompareAndSwap(x, CiKind.Long); | |
528 return; | |
529 | |
530 case java_lang_Thread$currentThread: | |
531 snippet = xir.genCurrentThread(site(x)); | |
532 if (snippet != null) { | |
533 emitXir(snippet, x, null, null, true); | |
534 return; | |
535 } | |
536 break; | |
537 | |
538 case java_lang_Object$getClass: | |
539 snippet = xir.genGetClass(site(x), toXirArgument(vals[0])); | |
540 if (snippet != null) { | |
541 emitXir(snippet, x, stateFor(x), null, true); | |
542 return; | |
543 } | |
544 break; | |
545 } | |
546 | |
547 | |
548 XirArgument[] args = new XirArgument[vals.length]; | |
549 for (int i = 0; i < vals.length; i++) { | |
550 args[i] = toXirArgument(vals[i]); | |
551 } | |
552 snippet = xir.genIntrinsic(site(x), args, x.target()); | |
553 if (snippet != null) { | |
554 emitXir(snippet, x, x.stateBefore() == null ? null : stateFor(x), null, true); | |
555 return; | |
556 } | |
557 x.setOperand(emitInvokeKnown(x.target(), x.stateBefore(), vals)); | |
558 } | |
559 | |
560 @Override | |
561 public void visitInvoke(Invoke x) { | |
562 RiMethod target = x.target(); | |
563 LIRDebugInfo info = stateFor(x, x.stateBefore()); | |
564 | |
565 XirSnippet snippet = null; | |
566 | |
567 int opcode = x.opcode(); | |
568 XirArgument receiver; | |
569 switch (opcode) { | |
570 case INVOKESTATIC: | |
571 snippet = xir.genInvokeStatic(site(x), target); | |
572 break; | |
573 case INVOKESPECIAL: | |
574 receiver = toXirArgument(x.receiver()); | |
575 snippet = xir.genInvokeSpecial(site(x), receiver, target); | |
576 break; | |
577 case INVOKEVIRTUAL: | |
578 receiver = toXirArgument(x.receiver()); | |
579 snippet = xir.genInvokeVirtual(site(x), receiver, target); | |
580 break; | |
581 case INVOKEINTERFACE: | |
582 receiver = toXirArgument(x.receiver()); | |
583 snippet = xir.genInvokeInterface(site(x), receiver, target); | |
584 break; | |
585 } | |
586 | |
587 CiValue destinationAddress = null; | |
588 // emitting the template earlier can ease pressure on register allocation, but the argument loading can destroy an | |
589 // implicit calling convention between the XirSnippet and the call. | |
590 if (!C1XOptions.InvokeSnippetAfterArguments) { | |
591 destinationAddress = emitXir(snippet, x, info.copy(), x.target(), false); | |
592 } | |
593 | |
594 CiValue resultOperand = resultOperandFor(x.kind); | |
595 CiCallingConvention cc = compilation.frameMap().getCallingConvention(x.signature(), JavaCall); | |
596 List<CiValue> pointerSlots = new ArrayList<CiValue>(2); | |
597 List<CiValue> argList = visitInvokeArguments(cc, x.arguments(), pointerSlots); | |
598 | |
599 if (C1XOptions.InvokeSnippetAfterArguments) { | |
600 destinationAddress = emitXir(snippet, x, info.copy(), null, x.target(), false, pointerSlots); | |
601 } | |
602 | |
603 // emit direct or indirect call to the destination address | |
604 if (destinationAddress instanceof CiConstant) { | |
605 // Direct call | |
606 assert ((CiConstant) destinationAddress).isDefaultValue() : "destination address should be zero"; | |
607 lir.callDirect(target, resultOperand, argList, info, snippet.marks, pointerSlots); | |
608 } else { | |
609 // Indirect call | |
610 argList.add(destinationAddress); | |
611 lir.callIndirect(target, resultOperand, argList, info, snippet.marks, pointerSlots); | |
612 } | |
613 | |
614 if (resultOperand.isLegal()) { | |
615 CiValue result = createResultVariable(x); | |
616 lir.move(resultOperand, result); | |
617 } | |
618 } | |
619 | |
620 @Override | |
621 public void visitNativeCall(NativeCall x) { | |
622 LIRDebugInfo info = stateFor(x, x.stateBefore()); | |
623 CiValue resultOperand = resultOperandFor(x.kind); | |
624 CiValue callAddress = load(x.address()); | |
625 CiKind[] signature = Util.signatureToKinds(x.signature, null); | |
626 CiCallingConvention cc = compilation.frameMap().getCallingConvention(signature, NativeCall); | |
627 List<CiValue> argList = visitInvokeArguments(cc, x.arguments, null); | |
628 argList.add(callAddress); | |
629 lir.callNative(x.nativeMethod.jniSymbol(), resultOperand, argList, info, null); | |
630 if (resultOperand.isLegal()) { | |
631 CiValue result = createResultVariable(x); | |
632 lir.move(resultOperand, result); | |
633 } | |
634 } | |
635 | |
636 @Override | |
637 public void visitTemplateCall(TemplateCall x) { | |
638 CiValue resultOperand = resultOperandFor(x.kind); | |
639 List<CiValue> argList; | |
640 if (x.receiver() != null) { | |
641 CiCallingConvention cc = compilation.frameMap().getCallingConvention(new CiKind[] {CiKind.Object}, JavaCall); | |
642 argList = visitInvokeArguments(cc, new Value[] {x.receiver()}, null); | |
643 } else { | |
644 argList = new ArrayList<CiValue>(); | |
645 } | |
646 | |
647 if (x.address() != null) { | |
648 CiValue callAddress = load(x.address()); | |
649 argList.add(callAddress); | |
650 } | |
651 lir.templateCall(resultOperand, argList); | |
652 if (resultOperand.isLegal()) { | |
653 CiValue result = createResultVariable(x); | |
654 lir.move(resultOperand, result); | |
655 } | |
656 } | |
657 | |
658 @Override | |
659 public void visitLoadRegister(LoadRegister x) { | |
660 x.setOperand(x.register.asValue(x.kind)); | |
661 } | |
662 | |
663 @Override | |
664 public void visitPause(Pause i) { | |
665 lir.pause(); | |
666 } | |
667 | |
668 @Override | |
669 public void visitBreakpointTrap(BreakpointTrap i) { | |
670 lir.breakpoint(); | |
671 } | |
672 | |
673 protected CiAddress getAddressForPointerOp(PointerOp x, CiKind kind, CiValue pointer) { | |
674 CiAddress addr; | |
675 Value offset = x.offset(); | |
676 Value index = x.index(); | |
677 if (x.displacement() == null) { | |
678 // address is [pointer + offset] | |
679 if (offset.isConstant() && offset.kind.isInt()) { | |
680 int displacement = x.offset().asConstant().asInt(); | |
681 addr = new CiAddress(kind, pointer, displacement); | |
682 } else { | |
683 addr = new CiAddress(kind, pointer, load(offset)); | |
684 } | |
685 } else { | |
686 // address is [pointer + disp + (index * scale)] | |
687 assert (x.opcode & 0xff) == PGET || (x.opcode & 0xff) == PSET; | |
688 if (!x.displacement().isConstant()) { | |
689 CiVariable tmp = newVariable(CiKind.Word); | |
690 arithmeticOpLong(Bytecodes.LADD, tmp, pointer, load(x.displacement()), null); | |
691 int kindSize = compilation.target.sizeInBytes(kind); | |
692 Scale scale = Scale.fromInt(kindSize); | |
693 if (index.isConstant()) { | |
694 addr = new CiAddress(kind, tmp, index.asConstant().asInt() * kindSize); | |
695 } else { | |
696 addr = new CiAddress(kind, tmp, load(index), scale, 0); | |
697 } | |
698 } else { | |
699 int displacement = x.displacement().asConstant().asInt(); | |
700 int kindSize = compilation.target.sizeInBytes(kind); | |
701 Scale scale = Scale.fromInt(kindSize); | |
702 if (index.isConstant()) { | |
703 displacement += index.asConstant().asInt() * kindSize; | |
704 addr = new CiAddress(kind, pointer, displacement); | |
705 } else { | |
706 addr = new CiAddress(kind, pointer, load(index), scale, displacement); | |
707 } | |
708 } | |
709 } | |
710 return addr; | |
711 } | |
712 | |
713 @Override | |
714 public void visitAllocateStackHandle(StackHandle x) { | |
715 CiValue value = load(x.value()); | |
716 CiValue src = forceToSpill(value, x.value().kind, true); | |
717 CiValue dst = createResultVariable(x); | |
718 | |
719 CiConstant constant = x.value().isConstant() ? x.value().asConstant() : null; | |
720 if (constant == null) { | |
721 CiConstant zero = CiConstant.defaultValue(x.value().kind); | |
722 lir.cmp(Condition.EQ, src, zero); | |
723 } | |
724 lir.lea(src, dst); | |
725 if (constant != null) { | |
726 if (constant.isDefaultValue()) { | |
727 lir.move(value, dst); | |
728 } | |
729 } else { | |
730 lir.cmove(Condition.EQ, CiConstant.ZERO, dst, dst); | |
731 } | |
732 } | |
733 | |
734 @Override | |
735 public void visitLoadPointer(LoadPointer x) { | |
736 LIRDebugInfo info = maybeStateFor(x); | |
737 CiValue pointer = load(x.pointer()); | |
738 CiValue dst = createResultVariable(x); | |
739 CiAddress src = getAddressForPointerOp(x, x.dataKind, pointer); | |
740 lir.load(src, dst, info); | |
741 } | |
742 | |
743 @Override | |
744 public void visitStorePointer(StorePointer x) { | |
745 LIRDebugInfo info = maybeStateFor(x); | |
746 LIRItem value = new LIRItem(x.value(), this); | |
747 CiValue pointer = load(x.pointer()); | |
748 value.loadItem(x.dataKind); | |
749 CiAddress dst = getAddressForPointerOp(x, x.dataKind, pointer); | |
750 lir.store(value.result(), dst, info); | |
751 } | |
752 | |
753 @Override | |
754 public void visitInfopoint(Infopoint x) { | |
755 LIRDebugInfo info = stateFor(x); | |
756 if (x.opcode == SAFEPOINT) { | |
757 emitXir(xir.genSafepoint(site(x)), x, info, null, false); | |
758 return; | |
759 } | |
760 assert x.opcode == HERE || x.opcode == INFO; | |
761 CiValue result = x.kind.isVoid() ? CiValue.IllegalValue : createResultVariable(x); | |
762 LIROpcode opcode = x.opcode == HERE ? LIROpcode.Here : LIROpcode.Info; | |
763 lir.infopoint(opcode, result, info); | |
764 } | |
765 | |
766 @Override | |
767 public void visitStackAllocate(StackAllocate x) { | |
768 CiValue result = createResultVariable(x); | |
769 assert x.size().isConstant() : "ALLOCA bytecode 'size' operand is not a constant: " + x.size(); | |
770 StackBlock stackBlock = compilation.frameMap().reserveStackBlock(x.size().asConstant().asInt()); | |
771 lir.alloca(stackBlock, result); | |
772 } | |
773 | |
774 @Override | |
775 public void visitMonitorAddress(MonitorAddress x) { | |
776 CiValue result = createResultVariable(x); | |
777 lir.monitorAddress(x.monitor(), result); | |
778 } | |
779 | |
780 @Override | |
781 public void visitMemoryBarrier(MemoryBarrier x) { | |
782 if (x.barriers != 0) { | |
783 lir.membar(x.barriers); | |
784 } | |
785 } | |
786 | |
787 @Override | |
788 public void visitUnsafeCast(UnsafeCast i) { | |
789 assert !i.redundant : "redundant UnsafeCasts must be eliminated by the front end"; | |
790 CiValue src = load(i.value()); | |
791 CiValue dst = createResultVariable(i); | |
792 lir.move(src, dst); | |
793 } | |
794 | |
795 /** | |
796 * For note on volatile fields, see {@link #visitStoreField(StoreField)}. | |
797 */ | |
798 @Override | |
799 public void visitLoadField(LoadField x) { | |
800 RiField field = x.field(); | |
801 boolean needsPatching = x.needsPatching(); | |
802 LIRDebugInfo info = null; | |
803 if (needsPatching || x.needsNullCheck()) { | |
804 info = stateFor(x, x.stateBefore()); | |
805 assert info != null; | |
806 } | |
807 | |
808 XirArgument receiver = toXirArgument(x.object()); | |
809 XirSnippet snippet = x.isStatic() ? xir.genGetStatic(site(x), receiver, field) : xir.genGetField(site(x), receiver, field); | |
810 emitXir(snippet, x, info, null, true); | |
811 | |
812 if (x.isVolatile()) { | |
813 vma.postVolatileRead(); | |
814 } | |
815 } | |
816 | |
817 @Override | |
818 public void visitLoadIndexed(LoadIndexed x) { | |
819 XirArgument array = toXirArgument(x.array()); | |
820 XirArgument index = toXirArgument(x.index()); | |
821 XirArgument length = toXirArgument(x.length()); | |
822 XirSnippet snippet = xir.genArrayLoad(site(x), array, index, length, x.elementKind(), null); | |
823 emitXir(snippet, x, maybeStateFor(x), null, true); | |
824 } | |
825 | |
826 protected GlobalStub stubFor(CiRuntimeCall runtimeCall) { | |
827 GlobalStub stub = compilation.compiler.lookupGlobalStub(runtimeCall); | |
828 compilation.frameMap().usesGlobalStub(stub); | |
829 return stub; | |
830 } | |
831 | |
832 protected GlobalStub stubFor(GlobalStub.Id globalStub) { | |
833 GlobalStub stub = compilation.compiler.lookupGlobalStub(globalStub); | |
834 compilation.frameMap().usesGlobalStub(stub); | |
835 return stub; | |
836 } | |
837 | |
838 protected GlobalStub stubFor(XirTemplate template) { | |
839 GlobalStub stub = compilation.compiler.lookupGlobalStub(template); | |
840 compilation.frameMap().usesGlobalStub(stub); | |
841 return stub; | |
842 } | |
843 | |
844 @Override | |
845 public void visitLocal(Local x) { | |
846 if (x.operand().isIllegal()) { | |
847 createResultVariable(x); | |
848 } | |
849 } | |
850 | |
851 @Override | |
852 public void visitLookupSwitch(LookupSwitch x) { | |
853 CiValue tag = load(x.value()); | |
854 setNoResult(x); | |
855 | |
856 if (x.isSafepoint()) { | |
857 emitXir(xir.genSafepoint(site(x)), x, stateFor(x, x.stateAfter()), null, false); | |
858 } | |
859 | |
860 // move values into phi locations | |
861 moveToPhi(x.stateAfter()); | |
862 | |
863 if (x.numberOfCases() == 0 || x.numberOfCases() < C1XOptions.SequentialSwitchLimit) { | |
864 int len = x.numberOfCases(); | |
865 for (int i = 0; i < len; i++) { | |
866 lir.cmp(Condition.EQ, tag, x.keyAt(i)); | |
867 lir.branch(Condition.EQ, CiKind.Int, x.suxAt(i)); | |
868 } | |
869 lir.jump(x.defaultSuccessor()); | |
870 } else { | |
871 visitSwitchRanges(createLookupRanges(x), tag, x.defaultSuccessor()); | |
872 } | |
873 } | |
874 | |
875 @Override | |
876 public void visitNullCheck(NullCheck x) { | |
877 // TODO: this is suboptimal because it may result in an unnecessary move | |
878 CiValue value = load(x.object()); | |
879 if (x.canTrap()) { | |
880 LIRDebugInfo info = stateFor(x); | |
881 lir.nullCheck(value, info); | |
882 } | |
883 x.setOperand(value); | |
884 } | |
885 | |
886 @Override | |
887 public void visitOsrEntry(OsrEntry x) { | |
888 // construct our frame and model the production of incoming pointer | |
889 // to the OSR buffer. | |
890 lir.osrEntry(osrBufferPointer()); | |
891 CiValue result = createResultVariable(x); | |
892 lir.move(osrBufferPointer(), result); | |
893 } | |
894 | |
895 @Override | |
896 public void visitPhi(Phi i) { | |
897 Util.shouldNotReachHere(); | |
898 } | |
899 | |
900 @Override | |
901 public void visitReturn(Return x) { | |
902 if (x.kind.isVoid()) { | |
903 XirSnippet epilogue = xir.genEpilogue(site(x), compilation.method); | |
904 if (epilogue != null) { | |
905 emitXir(epilogue, x, stateFor(x, x.stateAfter()), compilation.method, false); | |
906 lir.returnOp(IllegalValue); | |
907 } | |
908 } else { | |
909 CiValue operand = resultOperandFor(x.kind); | |
910 CiValue result = force(x.result(), operand); | |
911 XirSnippet epilogue = xir.genEpilogue(site(x), compilation.method); | |
912 if (epilogue != null) { | |
913 emitXir(epilogue, x, stateFor(x, x.stateAfter()), compilation.method, false); | |
914 lir.returnOp(result); | |
915 } | |
916 } | |
917 setNoResult(x); | |
918 } | |
919 | |
920 protected XirArgument toXirArgument(CiValue v) { | |
921 if (v == null) { | |
922 return null; | |
923 } | |
924 | |
925 return XirArgument.forInternalObject(v); | |
926 } | |
927 | |
928 protected XirArgument toXirArgument(Value i) { | |
929 if (i == null) { | |
930 return null; | |
931 } | |
932 | |
933 return XirArgument.forInternalObject(new LIRItem(i, this)); | |
934 } | |
935 | |
936 private CiValue allocateOperand(XirSnippet snippet, XirOperand op) { | |
937 if (op instanceof XirParameter) { | |
938 XirParameter param = (XirParameter) op; | |
939 return allocateOperand(snippet.arguments[param.parameterIndex], op, param.canBeConstant); | |
940 } else if (op instanceof XirRegister) { | |
941 XirRegister reg = (XirRegister) op; | |
942 return reg.register; | |
943 } else if (op instanceof XirTemp) { | |
944 return newVariable(op.kind); | |
945 } else { | |
946 Util.shouldNotReachHere(); | |
947 return null; | |
948 } | |
949 } | |
950 | |
951 private CiValue allocateOperand(XirArgument arg, XirOperand var, boolean canBeConstant) { | |
952 if (arg.constant != null) { | |
953 return arg.constant; | |
954 } else { | |
955 assert arg.object != null; | |
956 if (arg.object instanceof CiValue) { | |
957 return (CiValue) arg.object; | |
958 } | |
959 assert arg.object instanceof LIRItem; | |
960 LIRItem item = (LIRItem) arg.object; | |
961 if (canBeConstant) { | |
962 return item.instruction.operand(); | |
963 } else { | |
964 item.loadItem(var.kind); | |
965 return item.result(); | |
966 } | |
967 } | |
968 } | |
969 | |
970 protected CiValue emitXir(XirSnippet snippet, Instruction x, LIRDebugInfo info, RiMethod method, boolean setInstructionResult) { | |
971 return emitXir(snippet, x, info, null, method, setInstructionResult, null); | |
972 } | |
973 | |
974 protected CiValue emitXir(XirSnippet snippet, Instruction instruction, LIRDebugInfo info, LIRDebugInfo infoAfter, RiMethod method, boolean setInstructionResult, List<CiValue> pointerSlots) { | |
975 if (C1XOptions.PrintXirTemplates) { | |
976 TTY.println("Emit XIR template " + snippet.template.name); | |
977 } | |
978 | |
979 final CiValue[] operands = new CiValue[snippet.template.variableCount]; | |
980 | |
981 compilation.frameMap().reserveOutgoing(snippet.template.outgoingStackSize); | |
982 | |
983 XirOperand resultOperand = snippet.template.resultOperand; | |
984 | |
985 if (snippet.template.allocateResultOperand) { | |
986 CiValue outputOperand = IllegalValue; | |
987 // This snippet has a result that must be separately allocated | |
988 // Otherwise it is assumed that the result is part of the inputs | |
989 if (resultOperand.kind != CiKind.Void && resultOperand.kind != CiKind.Illegal) { | |
990 if (setInstructionResult) { | |
991 outputOperand = newVariable(instruction.kind); | |
992 } else { | |
993 outputOperand = newVariable(resultOperand.kind); | |
994 } | |
995 assert operands[resultOperand.index] == null; | |
996 } | |
997 operands[resultOperand.index] = outputOperand; | |
998 if (C1XOptions.PrintXirTemplates) { | |
999 TTY.println("Output operand: " + outputOperand); | |
1000 } | |
1001 } | |
1002 | |
1003 for (XirTemp t : snippet.template.temps) { | |
1004 if (t instanceof XirRegister) { | |
1005 XirRegister reg = (XirRegister) t; | |
1006 if (!t.reserve) { | |
1007 operands[t.index] = reg.register; | |
1008 } | |
1009 } | |
1010 } | |
1011 | |
1012 for (XirTemplate calleeTemplate : snippet.template.calleeTemplates) { | |
1013 // TODO Save these for use in X86LIRAssembler | |
1014 stubFor(calleeTemplate); | |
1015 } | |
1016 | |
1017 for (XirConstant c : snippet.template.constants) { | |
1018 assert operands[c.index] == null; | |
1019 operands[c.index] = c.value; | |
1020 } | |
1021 | |
1022 XirOperand[] inputOperands = snippet.template.inputOperands; | |
1023 XirOperand[] inputTempOperands = snippet.template.inputTempOperands; | |
1024 XirOperand[] tempOperands = snippet.template.tempOperands; | |
1025 | |
1026 CiValue[] operandArray = new CiValue[inputOperands.length + inputTempOperands.length + tempOperands.length]; | |
1027 int[] operandIndicesArray = new int[inputOperands.length + inputTempOperands.length + tempOperands.length]; | |
1028 for (int i = 0; i < inputOperands.length; i++) { | |
1029 XirOperand x = inputOperands[i]; | |
1030 CiValue op = allocateOperand(snippet, x); | |
1031 operands[x.index] = op; | |
1032 operandArray[i] = op; | |
1033 operandIndicesArray[i] = x.index; | |
1034 if (C1XOptions.PrintXirTemplates) { | |
1035 TTY.println("Input operand: " + x); | |
1036 } | |
1037 } | |
1038 | |
1039 for (int i = 0; i < inputTempOperands.length; i++) { | |
1040 XirOperand x = inputTempOperands[i]; | |
1041 CiValue op = allocateOperand(snippet, x); | |
1042 CiValue newOp = newVariable(op.kind); | |
1043 lir.move(op, newOp); | |
1044 operands[x.index] = newOp; | |
1045 operandArray[i + inputOperands.length] = newOp; | |
1046 operandIndicesArray[i + inputOperands.length] = x.index; | |
1047 if (C1XOptions.PrintXirTemplates) { | |
1048 TTY.println("InputTemp operand: " + x); | |
1049 } | |
1050 } | |
1051 | |
1052 for (int i = 0; i < tempOperands.length; i++) { | |
1053 XirOperand x = tempOperands[i]; | |
1054 CiValue op = allocateOperand(snippet, x); | |
1055 operands[x.index] = op; | |
1056 operandArray[i + inputOperands.length + inputTempOperands.length] = op; | |
1057 operandIndicesArray[i + inputOperands.length + inputTempOperands.length] = x.index; | |
1058 if (C1XOptions.PrintXirTemplates) { | |
1059 TTY.println("Temp operand: " + x); | |
1060 } | |
1061 } | |
1062 | |
1063 for (CiValue operand : operands) { | |
1064 assert operand != null; | |
1065 } | |
1066 | |
1067 CiValue allocatedResultOperand = operands[resultOperand.index]; | |
1068 if (!allocatedResultOperand.isVariableOrRegister()) { | |
1069 allocatedResultOperand = IllegalValue; | |
1070 } | |
1071 | |
1072 if (setInstructionResult && allocatedResultOperand.isLegal()) { | |
1073 if (instruction.operand().isIllegal()) { | |
1074 setResult(instruction, (CiVariable) allocatedResultOperand); | |
1075 } else { | |
1076 assert instruction.operand() == allocatedResultOperand; | |
1077 } | |
1078 } | |
1079 | |
1080 | |
1081 XirInstruction[] slowPath = snippet.template.slowPath; | |
1082 if (!operands[resultOperand.index].isConstant() || snippet.template.fastPath.length != 0 || (slowPath != null && slowPath.length > 0)) { | |
1083 // XIR instruction is only needed when the operand is not a constant! | |
1084 lir.xir(snippet, operands, allocatedResultOperand, inputTempOperands.length, tempOperands.length, | |
1085 operandArray, operandIndicesArray, | |
1086 (operands[resultOperand.index] == IllegalValue) ? -1 : resultOperand.index, | |
1087 info, infoAfter, method, pointerSlots); | |
1088 } | |
1089 | |
1090 return operands[resultOperand.index]; | |
1091 } | |
1092 | |
1093 @Override | |
1094 public void visitIncrementRegister(IncrementRegister x) { | |
1095 CiValue reg = x.register.asValue(CiKind.Word); | |
1096 if (x.delta().isConstant()) { | |
1097 int delta = x.delta().asConstant().asInt(); | |
1098 if (delta < 0) { | |
1099 lir.sub(reg, CiConstant.forInt(-delta), reg); | |
1100 } else { | |
1101 lir.add(reg, CiConstant.forInt(delta), reg); | |
1102 } | |
1103 } else { | |
1104 lir.add(reg, makeOperand(x.delta()), reg); | |
1105 } | |
1106 } | |
1107 | |
1108 @Override | |
1109 public void visitStoreRegister(StoreRegister x) { | |
1110 CiValue reg = x.register.asValue(x.kind); | |
1111 lir.move(makeOperand(x.value()), reg); | |
1112 } | |
1113 | |
1114 @Override | |
1115 public void visitStoreField(StoreField x) { | |
1116 RiField field = x.field(); | |
1117 boolean needsPatching = x.needsPatching(); | |
1118 | |
1119 LIRDebugInfo info = null; | |
1120 if (needsPatching || x.needsNullCheck()) { | |
1121 info = stateFor(x, x.stateBefore()); | |
1122 } | |
1123 | |
1124 if (x.isVolatile()) { | |
1125 vma.preVolatileWrite(); | |
1126 } | |
1127 | |
1128 XirArgument receiver = toXirArgument(x.object()); | |
1129 XirArgument value = toXirArgument(x.value()); | |
1130 XirSnippet snippet = x.isStatic() ? xir.genPutStatic(site(x), receiver, field, value) : xir.genPutField(site(x), receiver, field, value); | |
1131 emitXir(snippet, x, info, null, true); | |
1132 | |
1133 if (x.isVolatile()) { | |
1134 vma.postVolatileWrite(); | |
1135 } | |
1136 } | |
1137 | |
1138 @Override | |
1139 public void visitTableSwitch(TableSwitch x) { | |
1140 | |
1141 LIRItem value = new LIRItem(x.value(), this); | |
1142 // Making a copy of the switch value is necessary when generating a jump table | |
1143 value.setDestroysRegister(); | |
1144 value.loadItem(); | |
1145 | |
1146 CiValue tag = value.result(); | |
1147 setNoResult(x); | |
1148 | |
1149 if (x.isSafepoint()) { | |
1150 emitXir(xir.genSafepoint(site(x)), x, stateFor(x, x.stateAfter()), null, false); | |
1151 } | |
1152 | |
1153 // move values into phi locations | |
1154 moveToPhi(x.stateAfter()); | |
1155 | |
1156 // TODO: tune the defaults for the controls used to determine what kind of translation to use | |
1157 if (x.numberOfCases() == 0 || x.numberOfCases() <= C1XOptions.SequentialSwitchLimit) { | |
1158 int loKey = x.lowKey(); | |
1159 int len = x.numberOfCases(); | |
1160 for (int i = 0; i < len; i++) { | |
1161 lir.cmp(Condition.EQ, tag, i + loKey); | |
1162 lir.branch(Condition.EQ, CiKind.Int, x.suxAt(i)); | |
1163 } | |
1164 lir.jump(x.defaultSuccessor()); | |
1165 } else { | |
1166 SwitchRange[] switchRanges = createLookupRanges(x); | |
1167 int rangeDensity = x.numberOfCases() / switchRanges.length; | |
1168 if (rangeDensity >= C1XOptions.RangeTestsSwitchDensity) { | |
1169 visitSwitchRanges(switchRanges, tag, x.defaultSuccessor()); | |
1170 } else { | |
1171 List<BlockBegin> nonDefaultSuccessors = x.successors().subList(0, x.numberOfCases()); | |
1172 BlockBegin[] targets = nonDefaultSuccessors.toArray(new BlockBegin[nonDefaultSuccessors.size()]); | |
1173 lir.tableswitch(tag, x.lowKey(), x.defaultSuccessor(), targets); | |
1174 } | |
1175 } | |
1176 } | |
1177 | |
1178 @Override | |
1179 public void visitThrow(Throw x) { | |
1180 setNoResult(x); | |
1181 CiValue exceptionOpr = load(x.exception()); | |
1182 LIRDebugInfo info = stateFor(x, x.stateAfter()); | |
1183 | |
1184 // check if the instruction has an xhandler in any of the nested scopes | |
1185 boolean unwind = false; | |
1186 if (x.exceptionHandlers().size() == 0) { | |
1187 // this throw is not inside an xhandler | |
1188 unwind = true; | |
1189 } else { | |
1190 // get some idea of the throw type | |
1191 boolean typeIsExact = true; | |
1192 RiType throwType = x.exception().exactType(); | |
1193 if (throwType == null) { | |
1194 typeIsExact = false; | |
1195 throwType = x.exception().declaredType(); | |
1196 } | |
1197 if (throwType != null && throwType.isResolved() && throwType.isInstanceClass()) { | |
1198 unwind = !ExceptionHandler.couldCatch(x.exceptionHandlers(), throwType, typeIsExact); | |
1199 } | |
1200 } | |
1201 | |
1202 assert !currentBlock.checkBlockFlag(BlockBegin.BlockFlag.DefaultExceptionHandler) || unwind : "should be no more handlers to dispatch to"; | |
1203 | |
1204 // move exception oop into fixed register | |
1205 CiCallingConvention callingConvention = compilation.frameMap().getCallingConvention(new CiKind[]{CiKind.Object}, RuntimeCall); | |
1206 CiValue argumentOperand = callingConvention.locations[0]; | |
1207 lir.move(exceptionOpr, argumentOperand); | |
1208 | |
1209 if (unwind) { | |
1210 lir.unwindException(exceptionPcOpr(), exceptionOpr, info); | |
1211 } else { | |
1212 lir.throwException(exceptionPcOpr(), argumentOperand, info); | |
1213 } | |
1214 } | |
1215 | |
1216 @Override | |
1217 public void visitUnsafeGetObject(UnsafeGetObject x) { | |
1218 CiKind kind = x.unsafeOpKind; | |
1219 | |
1220 CiValue off = load(x.offset()); | |
1221 CiValue src = load(x.object()); | |
1222 | |
1223 CiValue reg = createResultVariable(x); | |
1224 | |
1225 if (x.isVolatile()) { | |
1226 vma.preVolatileRead(); | |
1227 } | |
1228 genGetObjectUnsafe(reg, src, off, kind, x.isVolatile()); | |
1229 if (x.isVolatile()) { | |
1230 vma.postVolatileRead(); | |
1231 } | |
1232 } | |
1233 | |
1234 @Override | |
1235 public void visitUnsafeGetRaw(UnsafeGetRaw x) { | |
1236 LIRItem idx = new LIRItem(this); | |
1237 CiValue base = load(x.base()); | |
1238 if (x.hasIndex()) { | |
1239 idx.setInstruction(x.index()); | |
1240 idx.loadNonconstant(); | |
1241 } | |
1242 | |
1243 CiValue reg = createResultVariable(x); | |
1244 | |
1245 int log2scale = 0; | |
1246 if (x.hasIndex()) { | |
1247 assert x.index().kind.isInt() : "should not find non-int index"; | |
1248 log2scale = x.log2Scale(); | |
1249 } | |
1250 | |
1251 assert !x.hasIndex() || idx.instruction == x.index() : "should match"; | |
1252 | |
1253 CiKind dstKind = x.unsafeOpKind; | |
1254 CiValue indexOp = idx.result(); | |
1255 | |
1256 CiAddress addr = null; | |
1257 if (indexOp.isConstant()) { | |
1258 assert log2scale == 0 : "must not have a scale"; | |
1259 CiConstant constantIndexOp = (CiConstant) indexOp; | |
1260 addr = new CiAddress(dstKind, base, constantIndexOp.asInt()); | |
1261 } else { | |
1262 | |
1263 if (compilation.target.arch.isX86()) { | |
1264 addr = new CiAddress(dstKind, base, indexOp, CiAddress.Scale.fromInt(2 ^ log2scale), 0); | |
1265 | |
1266 } else if (compilation.target.arch.isSPARC()) { | |
1267 if (indexOp.isIllegal() || log2scale == 0) { | |
1268 addr = new CiAddress(dstKind, base, indexOp); | |
1269 } else { | |
1270 CiValue tmp = newVariable(CiKind.Int); | |
1271 lir.shiftLeft(indexOp, log2scale, tmp); | |
1272 addr = new CiAddress(dstKind, base, tmp); | |
1273 } | |
1274 | |
1275 } else { | |
1276 Util.shouldNotReachHere(); | |
1277 } | |
1278 } | |
1279 | |
1280 if (x.mayBeUnaligned() && (dstKind == CiKind.Long || dstKind == CiKind.Double)) { | |
1281 lir.unalignedMove(addr, reg); | |
1282 } else { | |
1283 lir.move(addr, reg); | |
1284 } | |
1285 } | |
1286 | |
1287 @Override | |
1288 public void visitUnsafePrefetchRead(UnsafePrefetchRead x) { | |
1289 visitUnsafePrefetch(x, false); | |
1290 } | |
1291 | |
1292 @Override | |
1293 public void visitUnsafePrefetchWrite(UnsafePrefetchWrite x) { | |
1294 visitUnsafePrefetch(x, true); | |
1295 } | |
1296 | |
1297 @Override | |
1298 public void visitUnsafePutObject(UnsafePutObject x) { | |
1299 CiKind kind = x.unsafeOpKind; | |
1300 LIRItem data = new LIRItem(x.value(), this); | |
1301 | |
1302 CiValue src = load(x.object()); | |
1303 data.loadItem(kind); | |
1304 CiValue off = load(x.offset()); | |
1305 | |
1306 setNoResult(x); | |
1307 | |
1308 if (x.isVolatile()) { | |
1309 vma.preVolatileWrite(); | |
1310 } | |
1311 genPutObjectUnsafe(src, off, data.result(), kind, x.isVolatile()); | |
1312 if (x.isVolatile()) { | |
1313 vma.postVolatileWrite(); | |
1314 } | |
1315 } | |
1316 | |
1317 @Override | |
1318 public void visitUnsafePutRaw(UnsafePutRaw x) { | |
1319 int log2scale = 0; | |
1320 CiKind kind = x.unsafeOpKind; | |
1321 | |
1322 if (x.hasIndex()) { | |
1323 assert x.index().kind.isInt() : "should not find non-int index"; | |
1324 log2scale = x.log2scale(); | |
1325 } | |
1326 | |
1327 LIRItem value = new LIRItem(x.value(), this); | |
1328 LIRItem idx = new LIRItem(this); | |
1329 | |
1330 CiValue base = load(x.base()); | |
1331 if (x.hasIndex()) { | |
1332 idx.setInstruction(x.index()); | |
1333 idx.loadItem(); | |
1334 } | |
1335 | |
1336 value.loadItem(kind); | |
1337 | |
1338 setNoResult(x); | |
1339 | |
1340 CiValue indexOp = idx.result(); | |
1341 if (log2scale != 0) { | |
1342 // temporary fix (platform dependent code without shift on Intel would be better) | |
1343 indexOp = newVariable(CiKind.Int); | |
1344 lir.move(idx.result(), indexOp); | |
1345 lir.shiftLeft(indexOp, log2scale, indexOp); | |
1346 } | |
1347 | |
1348 CiValue addr = new CiAddress(x.unsafeOpKind, base, indexOp); | |
1349 lir.move(value.result(), addr); | |
1350 } | |
1351 | |
1352 private void blockDoEpilog(BlockBegin block) { | |
1353 if (C1XOptions.PrintIRWithLIR) { | |
1354 TTY.println(); | |
1355 } | |
1356 | |
1357 // clear out variables for local constants | |
1358 constants.clear(); | |
1359 variablesForConstants.clear(); | |
1360 } | |
1361 | |
1362 private void blockDoProlog(BlockBegin block) { | |
1363 if (C1XOptions.PrintIRWithLIR) { | |
1364 TTY.print(block.toString()); | |
1365 } | |
1366 // set up the list of LIR instructions | |
1367 assert block.lir() == null : "LIR list already computed for this block"; | |
1368 lir = new LIRList(this); | |
1369 block.setLir(lir); | |
1370 | |
1371 lir.branchDestination(block.label()); | |
1372 if (block == ir.startBlock) { | |
1373 XirSnippet prologue = xir.genPrologue(null, compilation.method); | |
1374 if (prologue != null) { | |
1375 emitXir(prologue, null, null, null, false); | |
1376 } | |
1377 setOperandsForLocals(block.end().stateAfter()); | |
1378 } | |
1379 } | |
1380 | |
1381 /** | |
1382 * Copies a given value into an operand that is forced to be a stack location. | |
1383 * | |
1384 * @param value a value to be forced onto the stack | |
1385 * @param kind the kind of new operand | |
1386 * @param mustStayOnStack specifies if the new operand must never be allocated to a register | |
1387 * @return the operand that is guaranteed to be a stack location when it is | |
1388 * initially defined a by move from {@code value} | |
1389 */ | |
1390 CiValue forceToSpill(CiValue value, CiKind kind, boolean mustStayOnStack) { | |
1391 assert value.isLegal() : "value should not be illegal"; | |
1392 assert kind.jvmSlots == value.kind.jvmSlots : "size mismatch"; | |
1393 if (!value.isVariableOrRegister()) { | |
1394 // force into a variable that must start in memory | |
1395 CiValue operand = operands.newVariable(value.kind, mustStayOnStack ? VariableFlag.MustStayInMemory : VariableFlag.MustStartInMemory); | |
1396 lir.move(value, operand); | |
1397 return operand; | |
1398 } | |
1399 | |
1400 // create a spill location | |
1401 CiValue operand = operands.newVariable(kind, mustStayOnStack ? VariableFlag.MustStayInMemory : VariableFlag.MustStartInMemory); | |
1402 // move from register to spill | |
1403 lir.move(value, operand); | |
1404 return operand; | |
1405 } | |
1406 | |
1407 private CiVariable loadConstant(Constant x) { | |
1408 return loadConstant(x.asConstant(), x.kind); | |
1409 } | |
1410 | |
1411 protected CiVariable loadConstant(CiConstant c, CiKind kind) { | |
1412 // XXX: linear search might be kind of slow for big basic blocks | |
1413 int index = constants.indexOf(c); | |
1414 if (index != -1) { | |
1415 C1XMetrics.LoadConstantIterations += index; | |
1416 return variablesForConstants.get(index); | |
1417 } | |
1418 C1XMetrics.LoadConstantIterations += constants.size(); | |
1419 | |
1420 CiVariable result = newVariable(kind); | |
1421 lir.move(c, result); | |
1422 constants.add(c); | |
1423 variablesForConstants.add(result); | |
1424 return result; | |
1425 } | |
1426 | |
1427 /** | |
1428 * Allocates a variable operand to hold the result of a given instruction. | |
1429 * This can only be performed once for any given instruction. | |
1430 * | |
1431 * @param x an instruction that produces a result | |
1432 * @return the variable assigned to hold the result produced by {@code x} | |
1433 */ | |
1434 protected CiVariable createResultVariable(Value x) { | |
1435 CiVariable operand = newVariable(x.kind); | |
1436 setResult(x, operand); | |
1437 return operand; | |
1438 } | |
1439 | |
1440 private void visitFPIntrinsics(Intrinsic x) { | |
1441 assert x.numberOfArguments() == 1 : "wrong type"; | |
1442 CiValue reg = createResultVariable(x); | |
1443 CiValue value = load(x.argumentAt(0)); | |
1444 CiValue tmp = forceToSpill(value, x.kind, false); | |
1445 lir.move(tmp, reg); | |
1446 } | |
1447 | |
1448 private void visitRegisterFinalizer(Intrinsic x) { | |
1449 assert x.numberOfArguments() == 1 : "wrong type"; | |
1450 CiValue receiver = load(x.argumentAt(0)); | |
1451 LIRDebugInfo info = stateFor(x, x.stateBefore()); | |
1452 callRuntime(CiRuntimeCall.RegisterFinalizer, info, receiver); | |
1453 setNoResult(x); | |
1454 } | |
1455 | |
1456 private void visitSwitchRanges(SwitchRange[] x, CiValue value, BlockBegin defaultSux) { | |
1457 for (int i = 0; i < x.length; i++) { | |
1458 SwitchRange oneRange = x[i]; | |
1459 int lowKey = oneRange.lowKey; | |
1460 int highKey = oneRange.highKey; | |
1461 BlockBegin dest = oneRange.sux; | |
1462 if (lowKey == highKey) { | |
1463 lir.cmp(Condition.EQ, value, lowKey); | |
1464 lir.branch(Condition.EQ, CiKind.Int, dest); | |
1465 } else if (highKey - lowKey == 1) { | |
1466 lir.cmp(Condition.EQ, value, lowKey); | |
1467 lir.branch(Condition.EQ, CiKind.Int, dest); | |
1468 lir.cmp(Condition.EQ, value, highKey); | |
1469 lir.branch(Condition.EQ, CiKind.Int, dest); | |
1470 } else { | |
1471 Label l = new Label(); | |
1472 lir.cmp(Condition.LT, value, lowKey); | |
1473 lir.branch(Condition.LT, l); | |
1474 lir.cmp(Condition.LE, value, highKey); | |
1475 lir.branch(Condition.LE, CiKind.Int, dest); | |
1476 lir.branchDestination(l); | |
1477 } | |
1478 } | |
1479 lir.jump(defaultSux); | |
1480 } | |
1481 | |
1482 private void visitUnsafePrefetch(UnsafePrefetch x, boolean isStore) { | |
1483 LIRItem src = new LIRItem(x.object(), this); | |
1484 LIRItem off = new LIRItem(x.offset(), this); | |
1485 | |
1486 src.loadItem(); | |
1487 if (!(off.result().isConstant() && canInlineAsConstant(x.offset()))) { | |
1488 off.loadItem(); | |
1489 } | |
1490 | |
1491 setNoResult(x); | |
1492 | |
1493 CiAddress addr = genAddress(src.result(), off.result(), 0, 0, CiKind.Byte); | |
1494 lir.prefetch(addr, isStore); | |
1495 } | |
1496 | |
1497 protected void arithmeticOpFpu(int code, CiValue result, CiValue left, CiValue right, CiValue tmp) { | |
1498 CiValue leftOp = left; | |
1499 | |
1500 if (isTwoOperand && leftOp != result) { | |
1501 assert right != result : "malformed"; | |
1502 lir.move(leftOp, result); | |
1503 leftOp = result; | |
1504 } | |
1505 | |
1506 switch (code) { | |
1507 case DADD: | |
1508 case FADD: | |
1509 lir.add(leftOp, right, result); | |
1510 break; | |
1511 case FMUL: | |
1512 case DMUL: | |
1513 lir.mul(leftOp, right, result); | |
1514 break; | |
1515 case DSUB: | |
1516 case FSUB: | |
1517 lir.sub(leftOp, right, result); | |
1518 break; | |
1519 case FDIV: | |
1520 case DDIV: | |
1521 lir.div(leftOp, right, result, null); | |
1522 break; | |
1523 default: | |
1524 Util.shouldNotReachHere(); | |
1525 } | |
1526 } | |
1527 | |
1528 protected void arithmeticOpInt(int code, CiValue result, CiValue left, CiValue right, CiValue tmp) { | |
1529 CiValue leftOp = left; | |
1530 | |
1531 if (isTwoOperand && leftOp != result) { | |
1532 assert right != result : "malformed"; | |
1533 lir.move(leftOp, result); | |
1534 leftOp = result; | |
1535 } | |
1536 | |
1537 switch (code) { | |
1538 case IADD: | |
1539 lir.add(leftOp, right, result); | |
1540 break; | |
1541 case IMUL: | |
1542 boolean didStrengthReduce = false; | |
1543 if (right.isConstant()) { | |
1544 CiConstant rightConstant = (CiConstant) right; | |
1545 int c = rightConstant.asInt(); | |
1546 if (CiUtil.isPowerOf2(c)) { | |
1547 // do not need tmp here | |
1548 lir.shiftLeft(leftOp, CiUtil.log2(c), result); | |
1549 didStrengthReduce = true; | |
1550 } else { | |
1551 didStrengthReduce = strengthReduceMultiply(leftOp, c, result, tmp); | |
1552 } | |
1553 } | |
1554 // we couldn't strength reduce so just emit the multiply | |
1555 if (!didStrengthReduce) { | |
1556 lir.mul(leftOp, right, result); | |
1557 } | |
1558 break; | |
1559 case ISUB: | |
1560 lir.sub(leftOp, right, result); | |
1561 break; | |
1562 default: | |
1563 // idiv and irem are handled elsewhere | |
1564 Util.shouldNotReachHere(); | |
1565 } | |
1566 } | |
1567 | |
1568 protected void arithmeticOpLong(int code, CiValue result, CiValue left, CiValue right, LIRDebugInfo info) { | |
1569 CiValue leftOp = left; | |
1570 | |
1571 if (isTwoOperand && leftOp != result) { | |
1572 assert right != result : "malformed"; | |
1573 lir.move(leftOp, result); | |
1574 leftOp = result; | |
1575 } | |
1576 | |
1577 switch (code) { | |
1578 case LADD: | |
1579 lir.add(leftOp, right, result); | |
1580 break; | |
1581 case LMUL: | |
1582 lir.mul(leftOp, right, result); | |
1583 break; | |
1584 case LSUB: | |
1585 lir.sub(leftOp, right, result); | |
1586 break; | |
1587 default: | |
1588 // ldiv and lrem are handled elsewhere | |
1589 Util.shouldNotReachHere(); | |
1590 } | |
1591 } | |
1592 | |
1593 protected final CiValue callRuntime(CiRuntimeCall runtimeCall, LIRDebugInfo info, CiValue... args) { | |
1594 // get a result register | |
1595 CiKind result = runtimeCall.resultKind; | |
1596 CiKind[] arguments = runtimeCall.arguments; | |
1597 | |
1598 CiValue physReg = result.isVoid() ? IllegalValue : resultOperandFor(result); | |
1599 | |
1600 List<CiValue> argumentList; | |
1601 if (arguments.length > 0) { | |
1602 // move the arguments into the correct location | |
1603 CiCallingConvention cc = compilation.frameMap().getCallingConvention(arguments, RuntimeCall); | |
1604 assert cc.locations.length == args.length : "argument count mismatch"; | |
1605 for (int i = 0; i < args.length; i++) { | |
1606 CiValue arg = args[i]; | |
1607 CiValue loc = cc.locations[i]; | |
1608 if (loc.isRegister()) { | |
1609 lir.move(arg, loc); | |
1610 } else { | |
1611 assert loc.isStackSlot(); | |
1612 CiStackSlot slot = (CiStackSlot) loc; | |
1613 if (slot.kind == CiKind.Long || slot.kind == CiKind.Double) { | |
1614 lir.unalignedMove(arg, slot); | |
1615 } else { | |
1616 lir.move(arg, slot); | |
1617 } | |
1618 } | |
1619 } | |
1620 argumentList = Arrays.asList(cc.locations); | |
1621 } else { | |
1622 // no arguments | |
1623 assert args == null || args.length == 0; | |
1624 argumentList = Util.uncheckedCast(Collections.emptyList()); | |
1625 } | |
1626 | |
1627 lir.callRuntime(runtimeCall, physReg, argumentList, info); | |
1628 | |
1629 return physReg; | |
1630 } | |
1631 | |
1632 protected final CiVariable callRuntimeWithResult(CiRuntimeCall runtimeCall, LIRDebugInfo info, CiValue... args) { | |
1633 CiVariable result = newVariable(runtimeCall.resultKind); | |
1634 CiValue location = callRuntime(runtimeCall, info, args); | |
1635 lir.move(location, result); | |
1636 return result; | |
1637 } | |
1638 | |
1639 SwitchRange[] createLookupRanges(LookupSwitch x) { | |
1640 // we expect the keys to be sorted by increasing value | |
1641 List<SwitchRange> res = new ArrayList<SwitchRange>(x.numberOfCases()); | |
1642 int len = x.numberOfCases(); | |
1643 if (len > 0) { | |
1644 BlockBegin defaultSux = x.defaultSuccessor(); | |
1645 int key = x.keyAt(0); | |
1646 BlockBegin sux = x.suxAt(0); | |
1647 SwitchRange range = new SwitchRange(key, sux); | |
1648 for (int i = 1; i < len; i++) { | |
1649 int newKey = x.keyAt(i); | |
1650 BlockBegin newSux = x.suxAt(i); | |
1651 if (key + 1 == newKey && sux == newSux) { | |
1652 // still in same range | |
1653 range.highKey = newKey; | |
1654 } else { | |
1655 // skip tests which explicitly dispatch to the default | |
1656 if (range.sux != defaultSux) { | |
1657 res.add(range); | |
1658 } | |
1659 range = new SwitchRange(newKey, newSux); | |
1660 } | |
1661 key = newKey; | |
1662 sux = newSux; | |
1663 } | |
1664 if (res.size() == 0 || res.get(res.size() - 1) != range) { | |
1665 res.add(range); | |
1666 } | |
1667 } | |
1668 return res.toArray(new SwitchRange[res.size()]); | |
1669 } | |
1670 | |
1671 SwitchRange[] createLookupRanges(TableSwitch x) { | |
1672 // XXX: try to merge this with the code for LookupSwitch | |
1673 List<SwitchRange> res = new ArrayList<SwitchRange>(x.numberOfCases()); | |
1674 int len = x.numberOfCases(); | |
1675 if (len > 0) { | |
1676 BlockBegin sux = x.suxAt(0); | |
1677 int key = x.lowKey(); | |
1678 BlockBegin defaultSux = x.defaultSuccessor(); | |
1679 SwitchRange range = new SwitchRange(key, sux); | |
1680 for (int i = 0; i < len; i++, key++) { | |
1681 BlockBegin newSux = x.suxAt(i); | |
1682 if (sux == newSux) { | |
1683 // still in same range | |
1684 range.highKey = key; | |
1685 } else { | |
1686 // skip tests which explicitly dispatch to the default | |
1687 if (sux != defaultSux) { | |
1688 res.add(range); | |
1689 } | |
1690 range = new SwitchRange(key, newSux); | |
1691 } | |
1692 sux = newSux; | |
1693 } | |
1694 if (res.size() == 0 || res.get(res.size() - 1) != range) { | |
1695 res.add(range); | |
1696 } | |
1697 } | |
1698 return res.toArray(new SwitchRange[res.size()]); | |
1699 } | |
1700 | |
1701 void doRoot(Instruction instr) { | |
1702 currentInstruction = instr; | |
1703 assert instr.isLive() : "use only with roots"; | |
1704 assert !instr.hasSubst() : "shouldn't have missed substitution"; | |
1705 | |
1706 if (C1XOptions.TraceLIRVisit) { | |
1707 TTY.println("Visiting " + instr); | |
1708 } | |
1709 instr.accept(this); | |
1710 if (C1XOptions.TraceLIRVisit) { | |
1711 TTY.println("Operand for " + instr + " = " + instr.operand()); | |
1712 } | |
1713 | |
1714 assert (instr.operand().isLegal()) || !isUsedForValue(instr) || instr.isConstant() || instr instanceof UnsafeCast : "operand was not set for live instruction"; | |
1715 } | |
1716 | |
1717 private boolean isUsedForValue(Instruction instr) { | |
1718 return instr.checkFlag(Value.Flag.LiveValue); | |
1719 } | |
1720 | |
1721 protected void logicOp(int code, CiValue resultOp, CiValue leftOp, CiValue rightOp) { | |
1722 if (isTwoOperand && leftOp != resultOp) { | |
1723 assert rightOp != resultOp : "malformed"; | |
1724 lir.move(leftOp, resultOp); | |
1725 leftOp = resultOp; | |
1726 } | |
1727 | |
1728 switch (code) { | |
1729 case IAND: | |
1730 case LAND: | |
1731 lir.logicalAnd(leftOp, rightOp, resultOp); | |
1732 break; | |
1733 | |
1734 case IOR: | |
1735 case LOR: | |
1736 lir.logicalOr(leftOp, rightOp, resultOp); | |
1737 break; | |
1738 | |
1739 case IXOR: | |
1740 case LXOR: | |
1741 lir.logicalXor(leftOp, rightOp, resultOp); | |
1742 break; | |
1743 | |
1744 default: | |
1745 Util.shouldNotReachHere(); | |
1746 } | |
1747 } | |
1748 | |
1749 void moveToPhi(PhiResolver resolver, Value curVal, Value suxVal) { | |
1750 // move current value to referenced phi function | |
1751 if (suxVal instanceof Phi) { | |
1752 Phi phi = (Phi) suxVal; | |
1753 // curVal can be null without phi being null in conjunction with inlining | |
1754 if (phi.isLive() && curVal != null && curVal != phi) { | |
1755 assert curVal.isLive() : "value not live: " + curVal + ", suxVal=" + suxVal; | |
1756 assert !phi.isIllegal() : "illegal phi cannot be marked as live"; | |
1757 if (curVal instanceof Phi) { | |
1758 operandForPhi((Phi) curVal); | |
1759 } | |
1760 CiValue operand = curVal.operand(); | |
1761 if (operand.isIllegal()) { | |
1762 assert curVal instanceof Constant || curVal instanceof Local : "these can be produced lazily"; | |
1763 operand = operandForInstruction(curVal); | |
1764 } | |
1765 resolver.move(operand, operandForPhi(phi)); | |
1766 } | |
1767 } | |
1768 } | |
1769 | |
1770 protected void moveToPhi(FrameState curState) { | |
1771 // Moves all stack values into their phi position | |
1772 BlockBegin bb = currentBlock; | |
1773 if (bb.numberOfSux() == 1) { | |
1774 BlockBegin sux = bb.suxAt(0); | |
1775 assert sux.numberOfPreds() > 0 : "invalid CFG"; | |
1776 | |
1777 // a block with only one predecessor never has phi functions | |
1778 if (sux.numberOfPreds() > 1) { | |
1779 PhiResolver resolver = new PhiResolver(this); | |
1780 | |
1781 FrameState suxState = sux.stateBefore(); | |
1782 | |
1783 for (int index = 0; index < suxState.stackSize(); index++) { | |
1784 moveToPhi(resolver, curState.stackAt(index), suxState.stackAt(index)); | |
1785 } | |
1786 | |
1787 // walk up the inlined scopes until locals match | |
1788 while (curState.scope() != suxState.scope()) { | |
1789 curState = curState.callerState(); | |
1790 assert curState != null : "scopes don't match up"; | |
1791 } | |
1792 | |
1793 for (int index = 0; index < suxState.localsSize(); index++) { | |
1794 moveToPhi(resolver, curState.localAt(index), suxState.localAt(index)); | |
1795 } | |
1796 | |
1797 assert curState.scope().callerState == suxState.scope().callerState : "caller states must be equal"; | |
1798 resolver.dispose(); | |
1799 } | |
1800 } | |
1801 } | |
1802 | |
1803 /** | |
1804 * Creates a new {@linkplain CiVariable variable}. | |
1805 * | |
1806 * @param kind the kind of the variable | |
1807 * @return a new variable | |
1808 */ | |
1809 public CiVariable newVariable(CiKind kind) { | |
1810 return operands.newVariable(kind); | |
1811 } | |
1812 | |
1813 CiValue operandForInstruction(Value x) { | |
1814 CiValue operand = x.operand(); | |
1815 if (operand.isIllegal()) { | |
1816 if (x instanceof Constant) { | |
1817 x.setOperand(x.asConstant()); | |
1818 } else { | |
1819 assert x instanceof Phi || x instanceof Local : "only for Phi and Local"; | |
1820 // allocate a variable for this local or phi | |
1821 createResultVariable(x); | |
1822 } | |
1823 } | |
1824 return x.operand(); | |
1825 } | |
1826 | |
1827 private CiValue operandForPhi(Phi phi) { | |
1828 if (phi.operand().isIllegal()) { | |
1829 // allocate a variable for this phi | |
1830 CiVariable operand = newVariable(phi.kind); | |
1831 setResult(phi, operand); | |
1832 } | |
1833 return phi.operand(); | |
1834 } | |
1835 | |
1836 protected void postGCWriteBarrier(CiValue addr, CiValue newVal) { | |
1837 XirSnippet writeBarrier = xir.genWriteBarrier(toXirArgument(addr)); | |
1838 if (writeBarrier != null) { | |
1839 emitXir(writeBarrier, null, null, null, false); | |
1840 } | |
1841 } | |
1842 | |
1843 protected void preGCWriteBarrier(CiValue addrOpr, boolean patch, LIRDebugInfo info) { | |
1844 } | |
1845 | |
1846 protected void setNoResult(Instruction x) { | |
1847 assert !isUsedForValue(x) : "can't have use"; | |
1848 x.clearOperand(); | |
1849 } | |
1850 | |
1851 protected CiValue setResult(Value x, CiVariable operand) { | |
1852 x.setOperand(operand); | |
1853 if (C1XOptions.DetailedAsserts) { | |
1854 operands.recordResult(operand, x); | |
1855 } | |
1856 return operand; | |
1857 } | |
1858 | |
1859 protected void shiftOp(int code, CiValue resultOp, CiValue value, CiValue count, CiValue tmp) { | |
1860 if (isTwoOperand && value != resultOp) { | |
1861 assert count != resultOp : "malformed"; | |
1862 lir.move(value, resultOp); | |
1863 value = resultOp; | |
1864 } | |
1865 | |
1866 assert count.isConstant() || count.isVariableOrRegister(); | |
1867 switch (code) { | |
1868 case ISHL: | |
1869 case LSHL: | |
1870 lir.shiftLeft(value, count, resultOp, tmp); | |
1871 break; | |
1872 case ISHR: | |
1873 case LSHR: | |
1874 lir.shiftRight(value, count, resultOp, tmp); | |
1875 break; | |
1876 case IUSHR: | |
1877 case LUSHR: | |
1878 lir.unsignedShiftRight(value, count, resultOp, tmp); | |
1879 break; | |
1880 default: | |
1881 Util.shouldNotReachHere(); | |
1882 } | |
1883 } | |
1884 | |
1885 protected void walkState(Instruction x, FrameState state) { | |
1886 if (state == null) { | |
1887 return; | |
1888 } | |
1889 for (int index = 0; index < state.stackSize(); index++) { | |
1890 walkStateValue(state.stackAt(index)); | |
1891 } | |
1892 FrameState s = state; | |
1893 int bci = x.bci(); | |
1894 | |
1895 while (s != null) { | |
1896 IRScope scope = s.scope(); | |
1897 if (bci == Instruction.SYNCHRONIZATION_ENTRY_BCI) { | |
1898 assert x instanceof ExceptionObject || | |
1899 x instanceof Throw || | |
1900 x instanceof MonitorEnter || | |
1901 x instanceof MonitorExit; | |
1902 } | |
1903 | |
1904 for (int index = 0; index < s.localsSize(); index++) { | |
1905 final Value value = s.localAt(index); | |
1906 if (value != null) { | |
1907 if (!value.isIllegal()) { | |
1908 walkStateValue(value); | |
1909 } | |
1910 } | |
1911 } | |
1912 bci = scope.callerBCI(); | |
1913 s = s.callerState(); | |
1914 } | |
1915 } | |
1916 | |
1917 private void walkStateValue(Value value) { | |
1918 if (value != null) { | |
1919 assert !value.hasSubst() : "missed substitution"; | |
1920 assert value.isLive() : "value must be marked live in frame state"; | |
1921 if (value instanceof Phi && !value.isIllegal()) { | |
1922 // phi's are special | |
1923 operandForPhi((Phi) value); | |
1924 } else if (value.operand().isIllegal() && !(value instanceof UnsafeCast)) { | |
1925 // instruction doesn't have an operand yet | |
1926 CiValue operand = makeOperand(value); | |
1927 assert operand.isLegal() : "must be evaluated now"; | |
1928 } | |
1929 } | |
1930 } | |
1931 | |
1932 protected LIRDebugInfo maybeStateFor(Instruction x) { | |
1933 FrameState stateBefore = x.stateBefore(); | |
1934 if (stateBefore == null) { | |
1935 return null; | |
1936 } | |
1937 return stateFor(x, stateBefore); | |
1938 } | |
1939 | |
1940 protected LIRDebugInfo stateFor(Instruction x) { | |
1941 assert x.stateBefore() != null : "must have state before instruction for " + x; | |
1942 return stateFor(x, x.stateBefore()); | |
1943 } | |
1944 | |
1945 protected LIRDebugInfo stateFor(Instruction x, FrameState state) { | |
1946 if (compilation.placeholderState != null) { | |
1947 state = compilation.placeholderState; | |
1948 } | |
1949 | |
1950 return new LIRDebugInfo(state, x.exceptionHandlers()); | |
1951 } | |
1952 | |
1953 List<CiValue> visitInvokeArguments(CiCallingConvention cc, Value[] args, List<CiValue> pointerSlots) { | |
1954 // for each argument, load it into the correct location | |
1955 List<CiValue> argList = new ArrayList<CiValue>(args.length); | |
1956 int j = 0; | |
1957 for (Value arg : args) { | |
1958 if (arg != null) { | |
1959 CiValue operand = cc.locations[j++]; | |
1960 if (operand.isRegister()) { | |
1961 force(arg, operand); | |
1962 } else { | |
1963 LIRItem param = new LIRItem(arg, this); | |
1964 assert operand.isStackSlot(); | |
1965 CiStackSlot slot = (CiStackSlot) operand; | |
1966 assert !slot.inCallerFrame(); | |
1967 param.loadForStore(slot.kind); | |
1968 if (slot.kind == CiKind.Long || slot.kind == CiKind.Double) { | |
1969 lir.unalignedMove(param.result(), slot); | |
1970 } else { | |
1971 lir.move(param.result(), slot); | |
1972 } | |
1973 | |
1974 if (arg.kind == CiKind.Object && pointerSlots != null) { | |
1975 // This slot must be marked explicitedly in the pointer map. | |
1976 pointerSlots.add(slot); | |
1977 } | |
1978 } | |
1979 argList.add(operand); | |
1980 } | |
1981 } | |
1982 return argList; | |
1983 } | |
1984 | |
1985 /** | |
1986 * Ensures that an operand has been {@linkplain Value#setOperand(CiValue) initialized} | |
1987 * for storing the result of an instruction. | |
1988 * | |
1989 * @param instruction an instruction that produces a result value | |
1990 */ | |
1991 protected CiValue makeOperand(Value instruction) { | |
1992 assert instruction.isLive(); | |
1993 CiValue operand = instruction.operand(); | |
1994 if (operand.isIllegal()) { | |
1995 if (instruction instanceof Phi) { | |
1996 // a phi may not have an operand yet if it is for an exception block | |
1997 operand = operandForPhi((Phi) instruction); | |
1998 } else if (instruction instanceof Constant) { | |
1999 operand = operandForInstruction(instruction); | |
2000 } | |
2001 } | |
2002 // the value must be a constant or have a valid operand | |
2003 assert operand.isLegal() : "this root has not been visited yet"; | |
2004 return operand; | |
2005 } | |
2006 | |
2007 /** | |
2008 * Gets the ABI specific operand used to return a value of a given kind from a method. | |
2009 * | |
2010 * @param kind the kind of value being returned | |
2011 * @return the operand representing the ABI defined location used return a value of kind {@code kind} | |
2012 */ | |
2013 protected CiValue resultOperandFor(CiKind kind) { | |
2014 if (kind == CiKind.Void) { | |
2015 return IllegalValue; | |
2016 } | |
2017 CiRegister returnRegister = compilation.registerConfig.getReturnRegister(kind); | |
2018 return returnRegister.asValue(kind); | |
2019 } | |
2020 | |
2021 protected XirSupport site(Value x) { | |
2022 return xirSupport.site(x); | |
2023 } | |
2024 | |
2025 public void maybePrintCurrentInstruction() { | |
2026 if (currentInstruction != null && lastInstructionPrinted != currentInstruction) { | |
2027 lastInstructionPrinted = currentInstruction; | |
2028 InstructionPrinter ip = new InstructionPrinter(TTY.out()); | |
2029 ip.printInstructionListing(currentInstruction); | |
2030 } | |
2031 } | |
2032 | |
2033 protected abstract boolean canInlineAsConstant(Value i); | |
2034 | |
2035 protected abstract boolean canStoreAsConstant(Value i, CiKind kind); | |
2036 | |
2037 protected abstract CiValue exceptionPcOpr(); | |
2038 | |
2039 protected abstract CiValue osrBufferPointer(); | |
2040 | |
2041 protected abstract boolean strengthReduceMultiply(CiValue left, int constant, CiValue result, CiValue tmp); | |
2042 | |
2043 protected abstract CiAddress genAddress(CiValue base, CiValue index, int shift, int disp, CiKind kind); | |
2044 | |
2045 protected abstract void genCmpMemInt(Condition condition, CiValue base, int disp, int c, LIRDebugInfo info); | |
2046 | |
2047 protected abstract void genCmpRegMem(Condition condition, CiValue reg, CiValue base, int disp, CiKind kind, LIRDebugInfo info); | |
2048 | |
2049 protected abstract void genGetObjectUnsafe(CiValue dest, CiValue src, CiValue offset, CiKind kind, boolean isVolatile); | |
2050 | |
2051 protected abstract void genPutObjectUnsafe(CiValue src, CiValue offset, CiValue data, CiKind kind, boolean isVolatile); | |
2052 | |
2053 protected abstract void genCompareAndSwap(Intrinsic x, CiKind kind); | |
2054 | |
2055 protected abstract void genMathIntrinsic(Intrinsic x); | |
2056 | |
2057 /** | |
2058 * Implements site-specific information for the XIR interface. | |
2059 */ | |
2060 static class XirSupport implements XirSite { | |
2061 Value current; | |
2062 | |
2063 XirSupport() { | |
2064 } | |
2065 | |
2066 public CiCodePos getCodePos() { | |
2067 // TODO: get the code position of the current instruction if possible | |
2068 return null; | |
2069 } | |
2070 | |
2071 public boolean isNonNull(XirArgument argument) { | |
2072 if (argument.constant == null && argument.object instanceof LIRItem) { | |
2073 // check the flag on the original value | |
2074 return ((LIRItem) argument.object).instruction.isNonNull(); | |
2075 } | |
2076 return false; | |
2077 } | |
2078 | |
2079 public boolean requiresNullCheck() { | |
2080 return current == null || current.needsNullCheck(); | |
2081 } | |
2082 | |
2083 public boolean requiresBoundsCheck() { | |
2084 return current == null || !current.checkFlag(Value.Flag.NoBoundsCheck); | |
2085 } | |
2086 | |
2087 public boolean requiresReadBarrier() { | |
2088 return current == null || !current.checkFlag(Value.Flag.NoReadBarrier); | |
2089 } | |
2090 | |
2091 public boolean requiresWriteBarrier() { | |
2092 return current == null || !current.checkFlag(Value.Flag.NoWriteBarrier); | |
2093 } | |
2094 | |
2095 public boolean requiresArrayStoreCheck() { | |
2096 return current == null || !current.checkFlag(Value.Flag.NoStoreCheck); | |
2097 } | |
2098 | |
2099 public RiType getApproximateType(XirArgument argument) { | |
2100 return current == null ? null : current.declaredType(); | |
2101 } | |
2102 | |
2103 public RiType getExactType(XirArgument argument) { | |
2104 return current == null ? null : current.exactType(); | |
2105 } | |
2106 | |
2107 XirSupport site(Value v) { | |
2108 current = v; | |
2109 return this; | |
2110 } | |
2111 | |
2112 @Override | |
2113 public String toString() { | |
2114 return "XirSupport<" + current + ">"; | |
2115 } | |
2116 | |
2117 } | |
2118 | |
2119 public void arrayCopy(RiType type, ArrayCopy arrayCopy, XirSnippet snippet) { | |
2120 emitXir(snippet, arrayCopy, stateFor(arrayCopy), null, false); | |
2121 } | |
2122 | |
2123 @Override | |
2124 public void visitArrayCopy(ArrayCopy arrayCopy) { | |
2125 Value src = arrayCopy.src(); | |
2126 Value dest = arrayCopy.dest(); | |
2127 Value srcPos = arrayCopy.srcPos(); | |
2128 Value destPos = arrayCopy.destPos(); | |
2129 Value length = arrayCopy.length(); | |
2130 RiType srcType = src.declaredType(); | |
2131 RiType destType = dest.declaredType(); | |
2132 if ((srcType != null && srcType.isArrayClass()) || (destType != null && destType.isArrayClass())) { | |
2133 RiType type = (srcType == null) ? destType : srcType; | |
2134 if ((srcType == null || destType == null || srcType.kind() != destType.kind()) && type.kind() != CiKind.Object) { | |
2135 TypeEqualityCheck typeCheck = new TypeEqualityCheck(src, dest, arrayCopy.stateBefore(), Condition.EQ); | |
2136 visitTypeEqualityCheck(typeCheck); | |
2137 } | |
2138 boolean inputsSame = (src == dest); | |
2139 boolean inputsDifferent = !inputsSame && (src.checkFlag(Flag.ResultIsUnique) || dest.checkFlag(Flag.ResultIsUnique)); | |
2140 boolean needsStoreCheck = type.componentType().kind() == CiKind.Object && destType != srcType; | |
2141 if (!needsStoreCheck) { | |
2142 arrayCopy.setFlag(Flag.NoStoreCheck); | |
2143 } | |
2144 XirSnippet snippet = xir.genArrayCopy(site(arrayCopy), toXirArgument(src), toXirArgument(srcPos), toXirArgument(dest), toXirArgument(destPos), toXirArgument(length), type.componentType(), inputsSame, inputsDifferent); | |
2145 arrayCopy(type, arrayCopy, snippet); | |
2146 return; | |
2147 } | |
2148 arrayCopySlow(arrayCopy); | |
2149 } | |
2150 | |
2151 private void arrayCopySlow(ArrayCopy arrayCopy) { | |
2152 emitInvokeKnown(arrayCopy.arrayCopyMethod, arrayCopy.stateBefore(), arrayCopy.src(), arrayCopy.srcPos(), arrayCopy.dest(), arrayCopy.destPos(), arrayCopy.length()); | |
2153 } | |
2154 | |
2155 private CiValue emitInvokeKnown(RiMethod method, FrameState stateBefore, Value... args) { | |
2156 boolean isStatic = Modifier.isStatic(method.accessFlags()); | |
2157 Invoke invoke = new Invoke(isStatic ? Bytecodes.INVOKESTATIC : Bytecodes.INVOKESPECIAL, method.signature().returnKind(), args, isStatic, method, null, stateBefore); | |
2158 visitInvoke(invoke); | |
2159 return invoke.operand(); | |
2160 } | |
2161 | |
2162 @Override | |
2163 public void visitTypeEqualityCheck(TypeEqualityCheck typeEqualityCheck) { | |
2164 Value x = typeEqualityCheck.left(); | |
2165 Value y = typeEqualityCheck.right(); | |
2166 | |
2167 CiValue leftValue = emitXir(xir.genGetClass(site(typeEqualityCheck), toXirArgument(x)), typeEqualityCheck, stateFor(typeEqualityCheck), null, false); | |
2168 CiValue rightValue = emitXir(xir.genGetClass(site(typeEqualityCheck), toXirArgument(y)), typeEqualityCheck, stateFor(typeEqualityCheck), null, false); | |
2169 lir.cmp(typeEqualityCheck.condition.negate(), leftValue, rightValue); | |
2170 emitGuard(typeEqualityCheck); | |
2171 } | |
2172 } |