001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.compilers.opt.mir2mc.ia32;
014
015import static org.jikesrvm.compilers.opt.ir.Operators.NULL_CHECK_opcode;
016import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_BACKEDGE_opcode;
017import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_EPILOGUE_opcode;
018import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_OSR_opcode;
019import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_PROLOGUE_opcode;
020import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.ADVISE_ESP_opcode;
021import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.CALL_SAVE_VOLATILE;
022import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.CALL_SAVE_VOLATILE_opcode;
023import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.DUMMY_DEF_opcode;
024import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.DUMMY_USE_opcode;
025import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_ADD;
026import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_CALL;
027import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_CMP;
028import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_CMPXCHG;
029import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_CMPXCHG8B;
030import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_FCLEAR_opcode;
031import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_FFREE;
032import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_FLD;
033import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_FMOV_ENDING_LIVE_RANGE_opcode;
034import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_FMOV_opcode;
035import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_FST;
036import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_FSTP;
037import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_FXCH;
038import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_INT;
039import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_JCC;
040import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_JCC2_opcode;
041import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_JMP;
042import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_LEA_opcode;
043import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_LOCK;
044import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_LOCK_CMPXCHG8B_opcode;
045import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_LOCK_CMPXCHG_opcode;
046import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_MOV;
047import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_MOVAPD_opcode;
048import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_MOVAPS_opcode;
049import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_MOVSD;
050import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_MOVSS;
051import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_MOVZX__B;
052import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_MOV_opcode;
053import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_SET__B_opcode;
054import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_SHL;
055import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_TEST_opcode;
056import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_TRAPIF;
057import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_TRAPIF_opcode;
058import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.IA32_XOR;
059import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.REQUIRE_ESP_opcode;
060
061import java.util.Enumeration;
062
063import org.jikesrvm.VM;
064import org.jikesrvm.classloader.RVMMethod;
065import org.jikesrvm.compilers.opt.ir.BBend;
066import org.jikesrvm.compilers.opt.ir.BasicBlock;
067import org.jikesrvm.compilers.opt.ir.IR;
068import org.jikesrvm.compilers.opt.ir.IRTools;
069import org.jikesrvm.compilers.opt.ir.Instruction;
070import org.jikesrvm.compilers.opt.ir.Label;
071import org.jikesrvm.compilers.opt.ir.NullCheck;
072import org.jikesrvm.compilers.opt.ir.Register;
073import org.jikesrvm.compilers.opt.ir.ia32.MIR_BinaryAcc;
074import org.jikesrvm.compilers.opt.ir.ia32.MIR_Branch;
075import org.jikesrvm.compilers.opt.ir.ia32.MIR_Call;
076import org.jikesrvm.compilers.opt.ir.ia32.MIR_Compare;
077import org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch;
078import org.jikesrvm.compilers.opt.ir.ia32.MIR_CondBranch2;
079import org.jikesrvm.compilers.opt.ir.ia32.MIR_Empty;
080import org.jikesrvm.compilers.opt.ir.ia32.MIR_Lea;
081import org.jikesrvm.compilers.opt.ir.ia32.MIR_Move;
082import org.jikesrvm.compilers.opt.ir.ia32.MIR_Nullary;
083import org.jikesrvm.compilers.opt.ir.ia32.MIR_Set;
084import org.jikesrvm.compilers.opt.ir.ia32.MIR_Test;
085import org.jikesrvm.compilers.opt.ir.ia32.MIR_Trap;
086import org.jikesrvm.compilers.opt.ir.ia32.MIR_TrapIf;
087import org.jikesrvm.compilers.opt.ir.ia32.MIR_Unary;
088import org.jikesrvm.compilers.opt.ir.ia32.MIR_UnaryNoRes;
089import org.jikesrvm.compilers.opt.ir.ia32.MIR_XChng;
090import org.jikesrvm.compilers.opt.ir.ia32.PhysicalDefUse;
091import org.jikesrvm.compilers.opt.ir.ia32.PhysicalRegisterSet;
092import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand;
093import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
094import org.jikesrvm.compilers.opt.ir.operand.LocationOperand;
095import org.jikesrvm.compilers.opt.ir.operand.MemoryOperand;
096import org.jikesrvm.compilers.opt.ir.operand.MethodOperand;
097import org.jikesrvm.compilers.opt.ir.operand.Operand;
098import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
099import org.jikesrvm.compilers.opt.ir.operand.TrapCodeOperand;
100import org.jikesrvm.compilers.opt.ir.operand.ia32.IA32ConditionOperand;
101import org.jikesrvm.compilers.opt.mir2mc.MachineCodeOffsets;
102import org.jikesrvm.runtime.ArchEntrypoints;
103import org.jikesrvm.runtime.Entrypoints;
104import org.jikesrvm.runtime.Magic;
105import org.vmmagic.unboxed.Offset;
106
107/**
108 * Final acts of MIR expansion for the IA32 architecture.
109 * Things that are expanded here (immediately before final assembly)
110 * should only be those sequences that cannot be expanded earlier
111 * due to difficulty in keeping optimizations from interfering with them.
112 * <p>
113 * One job of this phase is to handle the expansion of the remains of
114 * table switch.  The code looks like a mess (which it is), but there
115 * is little choice for relocatable IA32 code that does this.  And the
116 * details of this code are shared with the baseline compiler and
117 * dependent in detail on the Assembler (see {@link
118 * org.jikesrvm.compilers.common.assembler.ia32.Assembler#emitOFFSET_Imm_ImmOrLabel}).  If you want to mess with
119 * it, you will probably need to mess with them as well.
120 */
121public class FinalMIRExpansion extends IRTools {
122
123  /**
124   * @param ir the IR to expand
125   * @return return value is garbage for IA32
126   */
127  public static int expand(IR ir) {
128    PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet().asIA32();
129    MachineCodeOffsets mcOffsets = ir.MIRInfo.mcOffsets;
130
131    for (Instruction next, p = ir.firstInstructionInCodeOrder(); p != null; p = next) {
132      next = p.nextInstructionInCodeOrder();
133      mcOffsets.setMachineCodeOffset(p, -1);
134
135      switch (p.getOpcode()) {
136        case IA32_MOVAPS_opcode:
137          // a reg-reg move turned into a memory move where we can't guarantee alignment
138          if (MIR_Move.getResult(p).isMemory() || MIR_Move.getValue(p).isMemory()) {
139             MIR_Move.mutate(p, IA32_MOVSS, MIR_Move.getClearResult(p), MIR_Move.getClearValue(p));
140          }
141          break;
142
143        case IA32_MOVAPD_opcode:
144          // a reg-reg move turned into a memory move where we can't guarantee alignment
145          if (MIR_Move.getResult(p).isMemory() || MIR_Move.getValue(p).isMemory()) {
146             MIR_Move.mutate(p, IA32_MOVSD, MIR_Move.getClearResult(p), MIR_Move.getClearValue(p));
147          }
148          break;
149
150        case IA32_TEST_opcode:
151          // don't bother telling rest of compiler that memory operand
152          // must be first; we can just commute it here.
153          if (MIR_Test.getVal2(p).isMemory()) {
154            Operand tmp = MIR_Test.getClearVal1(p);
155            MIR_Test.setVal1(p, MIR_Test.getClearVal2(p));
156            MIR_Test.setVal2(p, tmp);
157          }
158          break;
159
160        case NULL_CHECK_opcode: {
161          // mutate this into a TRAPIF, and then fall through to the the
162          // TRAP_IF case.
163          Operand ref = NullCheck.getRef(p);
164          MIR_TrapIf.mutate(p,
165                            IA32_TRAPIF,
166                            null,
167                            ref.copy(),
168                            IC(0),
169                            IA32ConditionOperand.EQ(),
170                            TrapCodeOperand.NullPtr());
171        }
172        // There is no break statement here on purpose!
173        case IA32_TRAPIF_opcode: {
174          // split the basic block right before the IA32_TRAPIF
175          BasicBlock thisBlock = p.getBasicBlock();
176          BasicBlock trap = thisBlock.createSubBlock(p.bcIndex, ir, 0f);
177          thisBlock.insertOut(trap);
178          BasicBlock nextBlock = thisBlock.splitNodeWithLinksAt(p, ir);
179          thisBlock.insertOut(trap);
180          TrapCodeOperand tc = MIR_TrapIf.getClearTrapCode(p);
181          p.remove();
182          mcOffsets.setMachineCodeOffset(nextBlock.firstInstruction(), -1);
183          // add code to thisBlock to conditionally jump to trap
184          Instruction cmp = MIR_Compare.create(IA32_CMP, MIR_TrapIf.getVal1(p), MIR_TrapIf.getVal2(p));
185          if (p.isMarkedAsPEI()) {
186            // The trap if was explictly marked, which means that it has
187            // a memory operand into which we've folded a null check.
188            // Actually need a GC map for both the compare and the INT.
189            cmp.markAsPEI();
190            cmp.copyPosition(p);
191            ir.MIRInfo.gcIRMap.insertTwin(p, cmp);
192          }
193          thisBlock.appendInstruction(cmp);
194          thisBlock.appendInstruction(MIR_CondBranch.create(IA32_JCC,
195                                                            MIR_TrapIf.getCond(p),
196                                                            trap.makeJumpTarget(),
197                                                            null));
198
199          // add block at end to hold trap instruction, and
200          // insert trap sequence
201          ir.cfg.addLastInCodeOrder(trap);
202          if (tc.isArrayBounds()) {
203            // attempt to store index expression in processor object for
204            // C trap handler
205            Operand index = MIR_TrapIf.getVal2(p);
206            if (!(index instanceof RegisterOperand || index instanceof IntConstantOperand)) {
207              index = IC(0xdeadbeef); // index was spilled, and
208              // we can't get it back here.
209            }
210            MemoryOperand mo =
211                MemoryOperand.BD(ir.regpool.makeTROp(),
212                                     ArchEntrypoints.arrayIndexTrapParamField.getOffset(),
213                                     (byte) 4,
214                                     null,
215                                     null);
216            trap.appendInstruction(MIR_Move.create(IA32_MOV, mo, index.copy()));
217          }
218          // NOTE: must make p the trap instruction: it is the GC point!
219          // IMPORTANT: must also inform the GCMap that the instruction has
220          // been moved!!!
221          trap.appendInstruction(MIR_Trap.mutate(p, IA32_INT, null, tc));
222          ir.MIRInfo.gcIRMap.moveToEnd(p);
223
224          if (tc.isStackOverflow()) {
225            // only stackoverflow traps resume at next instruction.
226            trap.appendInstruction(MIR_Branch.create(IA32_JMP, nextBlock.makeJumpTarget()));
227          }
228        }
229        break;
230
231        case IA32_FMOV_ENDING_LIVE_RANGE_opcode: {
232          Operand result = MIR_Move.getResult(p);
233          Operand value = MIR_Move.getValue(p);
234          if (result.isRegister() && value.isRegister()) {
235            if (result.similar(value)) {
236              // eliminate useless move
237              p.remove();
238            } else {
239              int i = PhysicalRegisterSet.getFPRIndex(result.asRegister().getRegister());
240              int j = PhysicalRegisterSet.getFPRIndex(value.asRegister().getRegister());
241              if (i == 0) {
242                MIR_XChng.mutate(p, IA32_FXCH, result, value);
243              } else if (j == 0) {
244                MIR_XChng.mutate(p, IA32_FXCH, value, result);
245              } else {
246                expandFmov(p, phys);
247              }
248            }
249          } else {
250            expandFmov(p, phys);
251          }
252          break;
253        }
254
255        case DUMMY_DEF_opcode:
256        case DUMMY_USE_opcode:
257        case REQUIRE_ESP_opcode:
258        case ADVISE_ESP_opcode:
259          p.remove();
260          break;
261
262        case IA32_FMOV_opcode:
263          expandFmov(p, phys);
264          break;
265
266        case IA32_MOV_opcode:
267          // Replace result = IA32_MOV 0 with result = IA32_XOR result, result
268          if (MIR_Move.getResult(p).isRegister() &&
269              MIR_Move.getValue(p).isIntConstant() &&
270              MIR_Move.getValue(p).asIntConstant().value == 0) {
271            // Calculate what flags are defined in coming instructions before a use of a flag or BBend
272            Instruction x = next;
273            int futureDefs = 0;
274            while (!BBend.conforms(x) && !PhysicalDefUse.usesEFLAGS(x.operator())) {
275              futureDefs |= x.operator().implicitDefs;
276              x = x.nextInstructionInCodeOrder();
277            }
278            // If the flags will be destroyed prior to use or we reached the end of the basic block
279            if (BBend.conforms(x) ||
280                (futureDefs & PhysicalDefUse.maskAF_CF_OF_PF_SF_ZF) == PhysicalDefUse.maskAF_CF_OF_PF_SF_ZF) {
281              Operand result = MIR_Move.getClearResult(p);
282              MIR_BinaryAcc.mutate(p, IA32_XOR, result, result.copy());
283            }
284          }
285          break;
286
287        case IA32_SET__B_opcode:
288          // Replace <cmp>, set__b, movzx__b with xor, <cmp>, set__b
289          if (MIR_Set.getResult(p).isRegister() &&
290              MIR_Unary.conforms(next) &&
291              (next.operator() == IA32_MOVZX__B) &&
292              MIR_Unary.getResult(next).isRegister() &&
293              MIR_Unary.getVal(next).similar(MIR_Unary.getResult(next)) &&
294              MIR_Unary.getVal(next).similar(MIR_Set.getResult(p))) {
295            // Find instruction in this basic block that defines flags
296            Instruction x = p.prevInstructionInCodeOrder();
297            Operand result = MIR_Unary.getResult(next);
298            boolean foundCmp = false;
299            outer:
300            while (!Label.conforms(x)) {
301              Enumeration<Operand> e = x.getUses();
302              while (e.hasMoreElements()) {
303                // We can't use an xor to clear the register if that register is
304                // used by the <cmp> or intervening instruction
305                if (e.nextElement().similar(result)) {
306                  break outer;
307                }
308              }
309              if (PhysicalDefUse.definesEFLAGS(x.operator()) &&
310                  !PhysicalDefUse.usesEFLAGS(x.operator())) {
311                // we found a <cmp> that doesn't use the result or the flags
312                // that would be clobbered by the xor
313                foundCmp = true;
314                break outer;
315              }
316              x = x.prevInstructionInCodeOrder();
317            }
318            if (foundCmp) {
319              // We found the <cmp>, mutate the movzx__b into an xor and insert it before the <cmp>
320              next.remove();
321              MIR_BinaryAcc.mutate(next, IA32_XOR, result, MIR_Unary.getVal(next));
322              x.insertBefore(next);
323              // get ready for the next instruction
324              next = p.nextInstructionInCodeOrder();
325            }
326          }
327          break;
328
329        case IA32_LEA_opcode: {
330          // Sometimes we're over eager in BURS in using LEAs and after register
331          // allocation we can simplify to the accumulate form
332          // replace reg1 = LEA [reg1 + reg2] with reg1 = reg1 + reg2
333          // replace reg1 = LEA [reg1 + c1] with reg1 = reg1 + c1
334          // replace reg1 = LEA [reg1 << c1] with reg1 = reg1 << c1
335          MemoryOperand value = MIR_Lea.getValue(p);
336          RegisterOperand result = MIR_Lea.getResult(p);
337          if ((value.base != null && value.base.getRegister() == result.getRegister()) ||
338              (value.index != null && value.index.getRegister() == result.getRegister())) {
339            // Calculate what flags are defined in coming instructions before a use of a flag or BBend
340            Instruction x = next;
341            int futureDefs = 0;
342            while (!BBend.conforms(x) && !PhysicalDefUse.usesEFLAGS(x.operator())) {
343              futureDefs |= x.operator().implicitDefs;
344              x = x.nextInstructionInCodeOrder();
345            }
346            // If the flags will be destroyed prior to use or we reached the end of the basic block
347            if (BBend.conforms(x) ||
348                (futureDefs & PhysicalDefUse.maskAF_CF_OF_PF_SF_ZF) == PhysicalDefUse.maskAF_CF_OF_PF_SF_ZF) {
349              if (value.base != null &&
350                  value.index != null && value.index.getRegister() == result.getRegister() &&
351                  value.disp.isZero() &&
352                  value.scale == 0) {
353                // reg1 = lea [base + reg1] -> add reg1, base
354                MIR_BinaryAcc.mutate(p, IA32_ADD, result, value.base);
355              } else if (value.base != null && value.base.getRegister() == result.getRegister() &&
356                         value.index != null &&
357                         value.disp.isZero() &&
358                         value.scale == 0) {
359                // reg1 = lea [reg1 + index] -> add reg1, index
360                MIR_BinaryAcc.mutate(p, IA32_ADD, result, value.index);
361              } else if (value.base != null && value.base.getRegister() == result.getRegister() &&
362                         value.index == null) {
363                // reg1 = lea [reg1 + disp] -> add reg1, disp
364                MIR_BinaryAcc.mutate(p, IA32_ADD, result, IC(value.disp.toInt()));
365              } else if (value.base == null &&
366                         value.index != null && value.index.getRegister() == result.getRegister() &&
367                         value.scale == 0) {
368                // reg1 = lea [reg1 + disp] -> add reg1, disp
369                MIR_BinaryAcc.mutate(p, IA32_ADD, result, IC(value.disp.toInt()));
370              } else if (value.base == null &&
371                         value.index != null && value.index.getRegister() == result.getRegister() &&
372                         value.disp.isZero()) {
373                // reg1 = lea [reg1 << scale] -> shl reg1, scale
374                if (value.scale == 0) {
375                  p.remove();
376                } else if (value.scale == 1) {
377                  MIR_BinaryAcc.mutate(p, IA32_ADD, result, value.index);
378                } else {
379                  MIR_BinaryAcc.mutate(p, IA32_SHL, result, IC(value.scale));
380                }
381              }
382            }
383          }
384        }
385        break;
386
387        case IA32_FCLEAR_opcode:
388          expandFClear(p, ir);
389          break;
390
391        case IA32_JCC2_opcode:
392          p.insertBefore(MIR_CondBranch.create(IA32_JCC,
393                                               MIR_CondBranch2.getCond1(p),
394                                               MIR_CondBranch2.getTarget1(p),
395                                               MIR_CondBranch2.getBranchProfile1(p)));
396          MIR_CondBranch.mutate(p,
397                                IA32_JCC,
398                                MIR_CondBranch2.getCond2(p),
399                                MIR_CondBranch2.getTarget2(p),
400                                MIR_CondBranch2.getBranchProfile2(p));
401          break;
402
403        case CALL_SAVE_VOLATILE_opcode:
404          p.changeOperatorTo(IA32_CALL);
405          break;
406
407        case IA32_LOCK_CMPXCHG_opcode:
408          p.insertBefore(MIR_Empty.create(IA32_LOCK));
409          p.changeOperatorTo(IA32_CMPXCHG);
410          break;
411
412        case IA32_LOCK_CMPXCHG8B_opcode:
413          p.insertBefore(MIR_Empty.create(IA32_LOCK));
414          p.changeOperatorTo(IA32_CMPXCHG8B);
415          break;
416
417        case YIELDPOINT_PROLOGUE_opcode:
418          expandYieldpoint(p, ir, Entrypoints.optThreadSwitchFromPrologueMethod, IA32ConditionOperand.NE());
419          break;
420
421        case YIELDPOINT_EPILOGUE_opcode:
422          expandYieldpoint(p, ir, Entrypoints.optThreadSwitchFromEpilogueMethod, IA32ConditionOperand.NE());
423          break;
424
425        case YIELDPOINT_BACKEDGE_opcode:
426          expandYieldpoint(p, ir, Entrypoints.optThreadSwitchFromBackedgeMethod, IA32ConditionOperand.GT());
427          break;
428
429        case YIELDPOINT_OSR_opcode:
430          // must yield, does not check threadSwitch request
431          expandUnconditionalYieldpoint(p, ir, Entrypoints.optThreadSwitchFromOsrOptMethod);
432          break;
433
434      }
435    }
436    return 0;
437  }
438
439  /**
440   * expand an FCLEAR pseudo-insruction using FFREEs.
441   *
442   * @param s the instruction to expand
443   * @param ir the containing IR
444   */
445  private static void expandFClear(Instruction s, IR ir) {
446    int nSave = MIR_UnaryNoRes.getVal(s).asIntConstant().value;
447    int fpStackHeight = ir.MIRInfo.fpStackHeight;
448    PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet().asIA32();
449
450    for (int i = nSave; i < fpStackHeight; i++) {
451      Register f = phys.getFPR(i);
452      s.insertBefore(MIR_Nullary.create(IA32_FFREE, D(f)));
453    }
454
455    // Remove the FCLEAR.
456    s.remove();
457  }
458
459  /**
460   * expand an FMOV pseudo-insruction.
461   *
462   * @param s the instruction to expand
463   * @param phys controlling physical register set
464   */
465  private static void expandFmov(Instruction s, PhysicalRegisterSet phys) {
466    Operand result = MIR_Move.getResult(s);
467    Operand value = MIR_Move.getValue(s);
468
469    if (result.isRegister() && value.isRegister()) {
470      if (result.similar(value)) {
471        // eliminate useless move
472        s.remove();
473      } else {
474        int i = PhysicalRegisterSet.getFPRIndex(result.asRegister().getRegister());
475        int j = PhysicalRegisterSet.getFPRIndex(value.asRegister().getRegister());
476        if (j == 0) {
477          // We have FMOV Fi, F0
478          // Expand as:
479          //        FST F(i)  (copy F0 to F(i))
480          MIR_Move.mutate(s, IA32_FST, D(phys.getFPR(i)), D(phys.getFPR(0)));
481        } else {
482          // We have FMOV Fi, Fj
483          // Expand as:
484          //        FLD Fj  (push Fj on FP stack).
485          //        FSTP F(i+1)  (copy F0 to F(i+1) and then pop register stack)
486          s.insertBefore(MIR_Move.create(IA32_FLD, D(phys.getFPR(0)), value));
487
488          MIR_Move.mutate(s, IA32_FSTP, D(phys.getFPR(i + 1)), D(phys.getFPR(0)));
489        }
490
491      }
492    } else if (value instanceof MemoryOperand) {
493      if (result instanceof MemoryOperand) {
494        // We have FMOV M1, M2
495        // Expand as:
496        //        FLD M1   (push M1 on FP stack).
497        //        FSTP M2  (copy F0 to M2 and pop register stack)
498        s.insertBefore(MIR_Move.create(IA32_FLD, D(phys.getFPR(0)), value));
499        MIR_Move.mutate(s, IA32_FSTP, result, D(phys.getFPR(0)));
500      } else {
501        // We have FMOV Fi, M
502        // Expand as:
503        //        FLD M    (push M on FP stack).
504        //        FSTP F(i+1)  (copy F0 to F(i+1) and pop register stack)
505        if (VM.VerifyAssertions) VM._assert(result.isRegister());
506        int i = PhysicalRegisterSet.getFPRIndex(result.asRegister().getRegister());
507        s.insertBefore(MIR_Move.create(IA32_FLD, D(phys.getFPR(0)), value));
508        MIR_Move.mutate(s, IA32_FSTP, D(phys.getFPR(i + 1)), D(phys.getFPR(0)));
509      }
510    } else {
511      // We have FMOV M, Fi
512      if (VM.VerifyAssertions) VM._assert(value.isRegister());
513      if (VM.VerifyAssertions) {
514        VM._assert(result instanceof MemoryOperand);
515      }
516      int i = PhysicalRegisterSet.getFPRIndex(value.asRegister().getRegister());
517      if (i != 0) {
518        // Expand as:
519        //        FLD Fi    (push Fi on FP stack).
520        //        FSTP M    (store F0 in M and pop register stack);
521        s.insertBefore(MIR_Move.create(IA32_FLD, D(phys.getFPR(0)), value));
522        MIR_Move.mutate(s, IA32_FSTP, result, D(phys.getFPR(0)));
523      } else {
524        // Expand as:
525        //        FST M    (store F0 in M);
526        MIR_Move.mutate(s, IA32_FST, result, value);
527      }
528    }
529  }
530
531  private static void expandYieldpoint(Instruction s, IR ir, RVMMethod meth, IA32ConditionOperand ypCond) {
532    // split the basic block after the yieldpoint, create a new
533    // block at the end of the IR to hold the yieldpoint,
534    // remove the yieldpoint (to prepare to out it in the new block at the end)
535    BasicBlock thisBlock = s.getBasicBlock();
536    BasicBlock nextBlock = thisBlock.splitNodeWithLinksAt(s, ir);
537    BasicBlock yieldpoint = thisBlock.createSubBlock(s.bcIndex, ir, 0);
538    thisBlock.insertOut(yieldpoint);
539    yieldpoint.insertOut(nextBlock);
540    ir.cfg.addLastInCodeOrder(yieldpoint);
541    s.remove();
542
543    // change thread switch instruction into call to thread switch routine
544    // NOTE: must make s the call instruction: it is the GC point!
545    //       must also inform the GCMap that s has been moved!!!
546    Offset offset = meth.getOffset();
547    LocationOperand loc = new LocationOperand(offset);
548    Operand guard = TG();
549    Operand target = MemoryOperand.D(Magic.getTocPointer().plus(offset), (byte) 4, loc, guard);
550    MIR_Call.mutate0(s, CALL_SAVE_VOLATILE, null, null, target, MethodOperand.STATIC(meth));
551    yieldpoint.appendInstruction(s);
552    ir.MIRInfo.gcIRMap.moveToEnd(s);
553
554    yieldpoint.appendInstruction(MIR_Branch.create(IA32_JMP, nextBlock.makeJumpTarget()));
555
556    // Check to see if threadSwitch requested
557    Offset tsr = Entrypoints.takeYieldpointField.getOffset();
558    MemoryOperand M =
559        MemoryOperand.BD(ir.regpool.makeTROp(), tsr, (byte) 4, null, null);
560    thisBlock.appendInstruction(MIR_Compare.create(IA32_CMP, M, IC(0)));
561    thisBlock.appendInstruction(MIR_CondBranch.create(IA32_JCC,
562                                                      ypCond,
563                                                      yieldpoint.makeJumpTarget(),
564                                                      BranchProfileOperand.never()));
565  }
566
567  /* generate yieldpoint without checking threadSwith request
568   */
569  private static void expandUnconditionalYieldpoint(Instruction s, IR ir, RVMMethod meth) {
570    // split the basic block after the yieldpoint, create a new
571    // block at the end of the IR to hold the yieldpoint,
572    // remove the yieldpoint (to prepare to out it in the new block at the end)
573    BasicBlock thisBlock = s.getBasicBlock();
574    BasicBlock nextBlock = thisBlock.splitNodeWithLinksAt(s, ir);
575    BasicBlock yieldpoint = thisBlock.createSubBlock(s.bcIndex, ir);
576    thisBlock.insertOut(yieldpoint);
577    yieldpoint.insertOut(nextBlock);
578    ir.cfg.addLastInCodeOrder(yieldpoint);
579    s.remove();
580
581    // change thread switch instruction into call to thread switch routine
582    // NOTE: must make s the call instruction: it is the GC point!
583    //       must also inform the GCMap that s has been moved!!!
584    Offset offset = meth.getOffset();
585    LocationOperand loc = new LocationOperand(offset);
586    Operand guard = TG();
587    Operand target = MemoryOperand.D(Magic.getTocPointer().plus(offset), (byte) 4, loc, guard);
588    MIR_Call.mutate0(s, CALL_SAVE_VOLATILE, null, null, target, MethodOperand.STATIC(meth));
589    yieldpoint.appendInstruction(s);
590    ir.MIRInfo.gcIRMap.moveToEnd(s);
591
592    yieldpoint.appendInstruction(MIR_Branch.create(IA32_JMP, nextBlock.makeJumpTarget()));
593
594    // make a jump to yield block
595    thisBlock.appendInstruction(MIR_Branch.create(IA32_JMP, yieldpoint.makeJumpTarget()));
596  }
597}