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      /**
110       * Perform the transformation
111       */
112      public void transform() {
113        // store the object's fields in a ArrayList
114        ArrayList<RVMField> fields = getFieldsAsArrayList(klass);
115        // create a scalar for each field. initialize the scalar to
116        // default values before the object's def
117        RegisterOperand[] scalars = new RegisterOperand[fields.size()];
118        RegisterOperand def = reg.defList;
119        Instruction defI = def.instruction;
120        for (int i = 0; i < fields.size(); i++) {
121          RVMField f = fields.get(i);
122          Operand defaultValue = IRTools.getDefaultOperand(f.getType());
123          scalars[i] = IRTools.moveIntoRegister(ir.regpool, defI, defaultValue);
124          scalars[i].setType(f.getType());
125        }
126        transform2(this.reg, defI, scalars, fields, null);
127      }
128    
129      private void transform2(Register reg, Instruction defI, RegisterOperand[] scalars, ArrayList<RVMField> fields, Set<Register> visited) {
130        final boolean DEBUG = false;
131    
132        // now remove the def
133        if (DEBUG) {
134          System.out.println("Removing " + defI);
135        }
136        DefUse.removeInstructionAndUpdateDU(defI);
137        // now handle the uses
138        for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) {
139          scalarReplace(use, scalars, fields, visited);
140        }
141      }
142    
143      /**
144       * Returns a ArrayList<RVMField>, holding the fields of the object
145       * @param klass the type 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       */
173      private void scalarReplace(RegisterOperand use, RegisterOperand[] scalars, ArrayList<RVMField> fields, Set<Register> visited) {
174        Instruction inst = use.instruction;
175        try{
176          switch (inst.getOpcode()) {
177          case PUTFIELD_opcode: {
178            FieldReference fr = PutField.getLocation(inst).getFieldRef();
179            if (VM.VerifyAssertions) VM._assert(fr.isResolved());
180            RVMField f = fr.peekResolvedField();
181            int index = fields.indexOf(f);
182            TypeReference type = scalars[index].getType();
183            Operator moveOp = IRTools.getMoveOp(type);
184            Instruction i = Move.create(moveOp, scalars[index].copyRO(), PutField.getClearValue(inst));
185            inst.insertBefore(i);
186            DefUse.removeInstructionAndUpdateDU(inst);
187            DefUse.updateDUForNewInstruction(i);
188          }
189          break;
190          case GETFIELD_opcode: {
191            FieldReference fr = GetField.getLocation(inst).getFieldRef();
192            if (VM.VerifyAssertions) VM._assert(fr.isResolved());
193            RVMField f = fr.peekResolvedField();
194            int index = fields.indexOf(f);
195            TypeReference type = scalars[index].getType();
196            Operator moveOp = IRTools.getMoveOp(type);
197            Instruction i = Move.create(moveOp, GetField.getClearResult(inst), scalars[index].copyRO());
198            inst.insertBefore(i);
199            DefUse.removeInstructionAndUpdateDU(inst);
200            DefUse.updateDUForNewInstruction(i);
201          }
202          break;
203          case MONITORENTER_opcode:
204            inst.insertBefore(Empty.create(READ_CEILING));
205            DefUse.removeInstructionAndUpdateDU(inst);
206            break;
207          case MONITOREXIT_opcode:
208            inst.insertBefore(Empty.create(WRITE_FLOOR));
209            DefUse.removeInstructionAndUpdateDU(inst);
210            break;
211          case CALL_opcode:
212          case NULL_CHECK_opcode:
213            // (SJF) TODO: Why wasn't this caught by BC2IR for
214            //      java.lang.Double.<init> (Ljava/lang/String;)V ?
215            DefUse.removeInstructionAndUpdateDU(inst);
216            break;
217          case CHECKCAST_opcode:
218          case CHECKCAST_NOTNULL_opcode:
219          case CHECKCAST_UNRESOLVED_opcode: {
220            // We cannot handle removing the checkcast if the result of the
221            // checkcast test is unknown
222            TypeReference lhsType = TypeCheck.getType(inst).getTypeRef();
223            if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.YES) {
224              if (visited == null) {
225                visited = new HashSet<Register>();
226              }
227              Register copy = TypeCheck.getResult(inst).getRegister();
228              if(!visited.contains(copy)) {
229                visited.add(copy);
230                transform2(copy, inst, scalars, fields, visited);
231                // NB will remove inst
232              } else {
233                DefUse.removeInstructionAndUpdateDU(inst);
234              }
235            } else {
236              Instruction i2 = Trap.create(TRAP, null, TrapCodeOperand.CheckCast());
237              DefUse.replaceInstructionAndUpdateDU(inst, i2);
238            }
239          }
240          break;
241          case INSTANCEOF_opcode:
242          case INSTANCEOF_NOTNULL_opcode:
243          case INSTANCEOF_UNRESOLVED_opcode: {
244            // We cannot handle removing the instanceof if the result of the
245            // instanceof test is unknown
246            TypeReference lhsType = InstanceOf.getType(inst).getTypeRef();
247            Instruction i2;
248            if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.YES) {
249              i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(1));
250            } else {
251              i2 = Move.create(INT_MOVE, InstanceOf.getClearResult(inst), IC(0));
252            }
253            DefUse.replaceInstructionAndUpdateDU(inst, i2);
254          }
255          break;
256          case GET_OBJ_TIB_opcode: {
257            Instruction i2 = Move.create(REF_MOVE, GuardedUnary.getClearResult(inst), new TIBConstantOperand(klass));
258            DefUse.replaceInstructionAndUpdateDU(inst, i2);
259          }
260          break;
261          case REF_MOVE_opcode: {
262            if (visited == null) {
263              visited = new HashSet<Register>();
264            }
265            Register copy = Move.getResult(use.instruction).getRegister();
266            if(!visited.contains(copy)) {
267              visited.add(copy);
268              transform2(copy, inst, scalars, fields, visited);
269              // NB will remove inst
270            } else {
271              DefUse.removeInstructionAndUpdateDU(inst);
272            }
273          }
274          break;
275          default:
276            throw new OptimizingCompilerException("ObjectReplacer: unexpected use " + inst);
277          }
278        } catch (Exception e) {
279          OptimizingCompilerException oe = new OptimizingCompilerException("Error handling use ("+ use +") of: "+ inst);
280          oe.initCause(e);
281          throw oe;
282        }
283      }
284    
285      /**
286       * Some cases we don't handle yet. TODO: handle them.
287       */
288      private static boolean containsUnsupportedUse(IR ir, Register reg, RVMClass klass, Set<Register> visited) {
289        for (RegisterOperand use = reg.useList; use != null; use = use.getNext()) {
290          switch (use.instruction.getOpcode()) {
291            case MUST_IMPLEMENT_INTERFACE_opcode:
292            case REF_IFCMP_opcode:
293              return true;
294            case CHECKCAST_opcode:
295            case CHECKCAST_NOTNULL_opcode:
296            case CHECKCAST_UNRESOLVED_opcode: {
297              // We cannot handle removing the checkcast if the result of the
298              // checkcast test is unknown
299              TypeReference lhsType = TypeCheck.getType(use.instruction).getTypeRef();
300              byte ans = ClassLoaderProxy.includesType(lhsType, klass.getTypeRef());
301              if (ans == OptConstants.MAYBE) {
302                return true;
303              } else if (ans == OptConstants.YES) {
304                // handle as a move
305                if (visited == null) {
306                  visited = new HashSet<Register>();
307                }
308                Register copy = TypeCheck.getResult(use.instruction).getRegister();
309                if(!visited.contains(copy)) {
310                  visited.add(copy);
311                  if(containsUnsupportedUse(ir, copy, klass, visited)) {
312                    return true;
313                  }
314                }
315              }
316            }
317            break;
318            case INSTANCEOF_opcode:
319            case INSTANCEOF_NOTNULL_opcode:
320            case INSTANCEOF_UNRESOLVED_opcode: {
321              // We cannot handle removing the instanceof if the result of the
322              // instanceof test is unknown
323              TypeReference lhsType = InstanceOf.getType(use.instruction).getTypeRef();
324              if (ClassLoaderProxy.includesType(lhsType, klass.getTypeRef()) == OptConstants.MAYBE) {
325                return true;
326              }
327            }
328            break;
329            case REF_MOVE_opcode:
330              if (visited == null) {
331                visited = new HashSet<Register>();
332              }
333              Register copy = Move.getResult(use.instruction).getRegister();
334              if(!visited.contains(copy)) {
335                visited.add(copy);
336                if(containsUnsupportedUse(ir, copy, klass, visited)) {
337                  return true;
338                }
339              }
340              break;
341            case BOOLEAN_CMP_INT_opcode:
342            case BOOLEAN_CMP_ADDR_opcode:
343            case LONG_STORE_opcode:
344              throw new OptimizingCompilerException("Unexpected use of reference considered for replacement: " + use.instruction + " in " + ir.method);
345          }
346        }
347        return false;
348      }
349    }