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