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.escape;
014
015import static org.jikesrvm.compilers.opt.driver.OptConstants.MAYBE;
016import static org.jikesrvm.compilers.opt.driver.OptConstants.YES;
017import static org.jikesrvm.compilers.opt.ir.IRTools.IC;
018import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_ADDR_opcode;
019import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_INT_opcode;
020import static org.jikesrvm.compilers.opt.ir.Operators.CALL_opcode;
021import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_NOTNULL_opcode;
022import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_UNRESOLVED_opcode;
023import static org.jikesrvm.compilers.opt.ir.Operators.CHECKCAST_opcode;
024import static org.jikesrvm.compilers.opt.ir.Operators.GETFIELD_opcode;
025import static org.jikesrvm.compilers.opt.ir.Operators.GET_OBJ_TIB_opcode;
026import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_NOTNULL_opcode;
027import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_UNRESOLVED_opcode;
028import static org.jikesrvm.compilers.opt.ir.Operators.INSTANCEOF_opcode;
029import static org.jikesrvm.compilers.opt.ir.Operators.INT_MOVE;
030import static org.jikesrvm.compilers.opt.ir.Operators.LONG_STORE_opcode;
031import static org.jikesrvm.compilers.opt.ir.Operators.MONITORENTER_opcode;
032import static org.jikesrvm.compilers.opt.ir.Operators.MONITOREXIT_opcode;
033import static org.jikesrvm.compilers.opt.ir.Operators.MUST_IMPLEMENT_INTERFACE_opcode;
034import static org.jikesrvm.compilers.opt.ir.Operators.NULL_CHECK_opcode;
035import static org.jikesrvm.compilers.opt.ir.Operators.PUTFIELD_opcode;
036import static org.jikesrvm.compilers.opt.ir.Operators.READ_CEILING;
037import static org.jikesrvm.compilers.opt.ir.Operators.REF_IFCMP_opcode;
038import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE;
039import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE_opcode;
040import static org.jikesrvm.compilers.opt.ir.Operators.TRAP;
041import static org.jikesrvm.compilers.opt.ir.Operators.WRITE_FLOOR;
042
043import java.util.ArrayList;
044import java.util.HashSet;
045import java.util.Set;
046
047import org.jikesrvm.VM;
048import org.jikesrvm.classloader.FieldReference;
049import org.jikesrvm.classloader.RVMClass;
050import org.jikesrvm.classloader.RVMField;
051import org.jikesrvm.classloader.TypeReference;
052import org.jikesrvm.compilers.opt.ClassLoaderProxy;
053import org.jikesrvm.compilers.opt.DefUse;
054import org.jikesrvm.compilers.opt.OptimizingCompilerException;
055import org.jikesrvm.compilers.opt.ir.Empty;
056import org.jikesrvm.compilers.opt.ir.GetField;
057import org.jikesrvm.compilers.opt.ir.GuardedUnary;
058import org.jikesrvm.compilers.opt.ir.IR;
059import org.jikesrvm.compilers.opt.ir.IRTools;
060import org.jikesrvm.compilers.opt.ir.InstanceOf;
061import org.jikesrvm.compilers.opt.ir.Instruction;
062import org.jikesrvm.compilers.opt.ir.Move;
063import org.jikesrvm.compilers.opt.ir.New;
064import org.jikesrvm.compilers.opt.ir.Operator;
065import org.jikesrvm.compilers.opt.ir.PutField;
066import org.jikesrvm.compilers.opt.ir.Register;
067import org.jikesrvm.compilers.opt.ir.Trap;
068import org.jikesrvm.compilers.opt.ir.TypeCheck;
069import org.jikesrvm.compilers.opt.ir.operand.Operand;
070import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
071import org.jikesrvm.compilers.opt.ir.operand.TIBConstantOperand;
072import org.jikesrvm.compilers.opt.ir.operand.TrapCodeOperand;
073
074/**
075 * Class that performs scalar replacement of aggregates for non-array
076 * objects
077 */
078final class ObjectReplacer implements AggregateReplacer {
079  /**
080   * type of the object
081   */
082  private final RVMClass klass;
083  /**
084   * the IR
085   */
086  private final IR ir;
087  /**
088   * the register holding the object reference
089   */
090  private final Register reg;
091
092  /**
093   * Return an object representing this transformation for a given
094   * allocation site
095   *
096   * @param inst the allocation site
097   * @param ir the governing IR
098   * @return the object, or null if illegal
099   */
100  public static ObjectReplacer getReplacer(Instruction inst, IR ir) {
101    Register r = New.getResult(inst).getRegister();
102    RVMClass klass = New.getType(inst).getVMType().asClass();
103    // TODO :handle these cases
104    if (klass.hasFinalizer() || containsUnsupportedUse(ir, r, klass, null)) {
105      return null;
106    }
107    return new ObjectReplacer(r, klass, ir);
108  }
109
110  @Override
111  public void transform() {
112    // store the object's fields in a ArrayList
113    ArrayList<RVMField> fields = getFieldsAsArrayList(klass);
114    // create a scalar for each field. initialize the scalar to
115    // default values before the object's def
116    RegisterOperand[] scalars = new RegisterOperand[fields.size()];
117    RegisterOperand def = reg.defList;
118    Instruction defI = def.instruction;
119    for (int i = 0; i < fields.size(); i++) {
120      RVMField f = fields.get(i);
121      Operand defaultValue = IRTools.getDefaultOperand(f.getType());
122      scalars[i] = IRTools.moveIntoRegister(ir.regpool, defI, defaultValue);
123      scalars[i].setType(f.getType());
124    }
125    transform2(this.reg, defI, scalars, fields, null);
126  }
127
128  private void transform2(Register reg, Instruction defI, RegisterOperand[] scalars, ArrayList<RVMField> fields, Set<Register> visited) {
129    final boolean DEBUG = false;
130
131    // now remove the def
132    if (DEBUG) {
133      System.out.println("Removing " + defI);
134    }
135    DefUse.removeInstructionAndUpdateDU(defI);
136    // now handle the uses
137    for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) {
138      scalarReplace(use, scalars, fields, visited);
139    }
140  }
141
142  /**
143   * Returns the instance fields of the object.
144   * @param klass the type of the object
145   * @return a list holding the instance fields of the object
146   */
147  private static ArrayList<RVMField> getFieldsAsArrayList(RVMClass klass) {
148    ArrayList<RVMField> v = new ArrayList<RVMField>();
149    for (RVMField field : klass.getInstanceFields()) {
150      v.add(field);
151    }
152    return v;
153  }
154
155  /**
156   * @param r the register holding the object reference
157   * @param _klass the type of the object to replace
158   * @param i the IR
159   */
160  private ObjectReplacer(Register r, RVMClass _klass, IR i) {
161    reg = r;
162    klass = _klass;
163    ir = i;
164  }
165
166  /**
167   * Replace a given use of a object with its scalar equivalent
168   *
169   * @param use the use to replace
170   * @param scalars an array of scalar register operands to replace
171   *                  the object's fields with
172   * @param fields the object's fields
173   * @param visited the registers that were already seen
174   */
175  private void scalarReplace(RegisterOperand use, RegisterOperand[] scalars, ArrayList<RVMField> fields, Set<Register> visited) {
176    Instruction inst = use.instruction;
177    try {
178      switch (inst.getOpcode()) {
179      case PUTFIELD_opcode: {
180        FieldReference fr = PutField.getLocation(inst).getFieldRef();
181        if (VM.VerifyAssertions) VM._assert(fr.isResolved());
182        RVMField f = fr.peekResolvedField();
183        int index = fields.indexOf(f);
184        TypeReference type = scalars[index].getType();
185        Operator moveOp = IRTools.getMoveOp(type);
186        Instruction i = Move.create(moveOp, scalars[index].copyRO(), PutField.getClearValue(inst));
187        inst.insertBefore(i);
188        DefUse.removeInstructionAndUpdateDU(inst);
189        DefUse.updateDUForNewInstruction(i);
190      }
191      break;
192      case GETFIELD_opcode: {
193        FieldReference fr = GetField.getLocation(inst).getFieldRef();
194        if (VM.VerifyAssertions) VM._assert(fr.isResolved());
195        RVMField f = fr.peekResolvedField();
196        int index = fields.indexOf(f);
197        TypeReference type = scalars[index].getType();
198        Operator moveOp = IRTools.getMoveOp(type);
199        Instruction i = Move.create(moveOp, GetField.getClearResult(inst), scalars[index].copyRO());
200        inst.insertBefore(i);
201        DefUse.removeInstructionAndUpdateDU(inst);
202        DefUse.updateDUForNewInstruction(i);
203      }
204      break;
205      case MONITORENTER_opcode:
206        inst.insertBefore(Empty.create(READ_CEILING));
207        DefUse.removeInstructionAndUpdateDU(inst);
208        break;
209      case MONITOREXIT_opcode:
210        inst.insertBefore(Empty.create(WRITE_FLOOR));
211        DefUse.removeInstructionAndUpdateDU(inst);
212        break;
213      case CALL_opcode:
214      case NULL_CHECK_opcode:
215        // (SJF) TODO: Why wasn't this caught by BC2IR for
216        //      java.lang.Double.<init> (Ljava/lang/String;)V ?
217        DefUse.removeInstructionAndUpdateDU(inst);
218        break;
219      case CHECKCAST_opcode:
220      case CHECKCAST_NOTNULL_opcode:
221      case CHECKCAST_UNRESOLVED_opcode: {
222        // We cannot handle removing the checkcast if the result of the
223        // checkcast test is unknown
224        TypeReference lhsType = TypeCheck.getType(inst).getTypeRef();
225        if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == YES) {
226          if (visited == null) {
227            visited = new HashSet<Register>();
228          }
229          Register copy = TypeCheck.getResult(inst).getRegister();
230          if (!visited.contains(copy)) {
231            visited.add(copy);
232            transform2(copy, inst, scalars, fields, visited);
233            // NB will remove inst
234          } else {
235            DefUse.removeInstructionAndUpdateDU(inst);
236          }
237        } else {
238          Instruction i2 = Trap.create(TRAP, null, TrapCodeOperand.CheckCast());
239          DefUse.replaceInstructionAndUpdateDU(inst, i2);
240        }
241      }
242      break;
243      case INSTANCEOF_opcode:
244      case INSTANCEOF_NOTNULL_opcode:
245      case INSTANCEOF_UNRESOLVED_opcode: {
246        // We cannot handle removing the instanceof if the result of the
247        // instanceof test is unknown
248        TypeReference lhsType = InstanceOf.getType(inst).getTypeRef();
249        Instruction i2;
250        if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == YES) {
251          i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(1));
252        } else {
253          i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(0));
254        }
255        DefUse.replaceInstructionAndUpdateDU(inst, i2);
256      }
257      break;
258      case GET_OBJ_TIB_opcode: {
259        Instruction i2 = Move.create(REF_MOVE, GuardedUnary.getClearResult(inst), new TIBConstantOperand(klass));
260        DefUse.replaceInstructionAndUpdateDU(inst, i2);
261      }
262      break;
263      case REF_MOVE_opcode: {
264        if (visited == null) {
265          visited = new HashSet<Register>();
266        }
267        Register copy = Move.getResult(use.instruction).getRegister();
268        if (!visited.contains(copy)) {
269          visited.add(copy);
270          transform2(copy, inst, scalars, fields, visited);
271          // NB will remove inst
272        } else {
273          DefUse.removeInstructionAndUpdateDU(inst);
274        }
275      }
276      break;
277      default:
278        throw new OptimizingCompilerException("ObjectReplacer: unexpected use " + inst);
279      }
280    } catch (Exception e) {
281      OptimizingCompilerException oe = new OptimizingCompilerException("Error handling use (" + use + ") of: " + inst);
282      oe.initCause(e);
283      throw oe;
284    }
285  }
286
287  /**
288   * Some cases we don't handle yet. TODO: handle them.
289   *
290   * @param ir the IR to check
291   * @param reg the register whose uses are being checked
292   * @param klass the class of the newly created object
293   * @param visited registers that were already seen
294   *
295   * @return {@code true} if the IR contains a case that we don't handle yet
296   */
297  private static boolean containsUnsupportedUse(IR ir, Register reg, RVMClass klass, Set<Register> visited) {
298    for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) {
299      switch (use.instruction.getOpcode()) {
300        case MUST_IMPLEMENT_INTERFACE_opcode:
301        case REF_IFCMP_opcode:
302          return true;
303        case CHECKCAST_opcode:
304        case CHECKCAST_NOTNULL_opcode:
305        case CHECKCAST_UNRESOLVED_opcode: {
306          // We cannot handle removing the checkcast if the result of the
307          // checkcast test is unknown
308          TypeReference lhsType = TypeCheck.getType(use.instruction).getTypeRef();
309          byte ans = ClassLoaderProxy.includesType(lhsType, klass.getTypeRef());
310          if (ans == MAYBE) {
311            return true;
312          } else if (ans == YES) {
313            // handle as a move
314            if (visited == null) {
315              visited = new HashSet<Register>();
316            }
317            Register copy = TypeCheck.getResult(use.instruction).getRegister();
318            if (!visited.contains(copy)) {
319              visited.add(copy);
320              if (containsUnsupportedUse(ir, copy, klass, visited)) {
321                return true;
322              }
323            }
324          }
325        }
326        break;
327        case INSTANCEOF_opcode:
328        case INSTANCEOF_NOTNULL_opcode:
329        case INSTANCEOF_UNRESOLVED_opcode: {
330          // We cannot handle removing the instanceof if the result of the
331          // instanceof test is unknown
332          TypeReference lhsType = InstanceOf.getType(use.instruction).getTypeRef();
333          if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == MAYBE) {
334            return true;
335          }
336        }
337        break;
338        case REF_MOVE_opcode:
339          if (visited == null) {
340            visited = new HashSet<Register>();
341          }
342          Register copy = Move.getResult(use.instruction).getRegister();
343          if (!visited.contains(copy)) {
344            visited.add(copy);
345            if (containsUnsupportedUse(ir, copy, klass, visited)) {
346              return true;
347            }
348          }
349          break;
350        case BOOLEAN_CMP_INT_opcode:
351        case BOOLEAN_CMP_ADDR_opcode:
352        case LONG_STORE_opcode:
353          throw new OptimizingCompilerException("Unexpected use of reference considered for replacement: " + use.instruction + " in " + ir.method);
354      }
355    }
356    return false;
357  }
358}