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