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.ir.operand;
014
015import org.jikesrvm.classloader.RVMField;
016import org.jikesrvm.classloader.MemberReference;
017import org.jikesrvm.classloader.RVMMethod;
018import org.jikesrvm.classloader.MethodReference;
019import org.jikesrvm.classloader.RVMType;
020import org.jikesrvm.compilers.opt.specialization.SpecializedMethod;
021import org.vmmagic.unboxed.Offset;
022
023/**
024 * Refers to a method. Used for method call instructions.
025 * Contains a RVMMethod (which may or may not have been resolved yet.)
026 * <p>
027 * TODO: Create subclasses of MethodOperand for internal &amp; specialized
028 * targets.
029 *
030 * @see Operand
031 * @see RVMMethod
032 */
033public final class MethodOperand extends Operand {
034
035  /* Enumeration of types of invokes */
036  private static final byte STATIC = 0;
037  private static final byte SPECIAL = 1;
038  private static final byte VIRTUAL = 2;
039  private static final byte INTERFACE = 3;
040
041  /**
042   * Member reference for target.<p>
043   *
044   * Usually a MethodReference, but may be a FieldReference for
045   * internal methods that don't have 'real' Java method but come from
046   * OutOfLineMachineCode.
047   */
048  final MemberReference memRef;
049
050  /**
051   * Target RVMMethod of invocation.
052   */
053  RVMMethod target;
054
055  /**
056   * Is target exactly the method being invoked by this call, or is it
057   * a representative for a family of virtual/interface methods?
058   */
059  boolean isPreciseTarget;
060
061  /**
062   * Is this the operand of a call that never returns?
063   */
064  boolean isNonReturningCall;
065
066  /**
067   * Is this the operand of a call that is the off-branch of a guarded inline?
068   */
069  boolean isGuardedInlineOffBranch;
070
071  /**
072   * The type of the invoke (STATIC, SPECIAL, VIRTUAL, INTERFACE)
073   */
074  byte type = -1;
075
076  private boolean designatedOffset = false;
077  public Offset jtocOffset;
078
079  /**
080   * @param ref MemberReference of method to call
081   * @param tar the RVMMethod to call (may be null)
082   * @param t the type of invoke used to call it (STATIC, SPECIAL, VIRTUAL, INTERFACE)
083   */
084  private MethodOperand(MemberReference ref, RVMMethod tar, byte t) {
085    memRef = ref;
086    target = tar;
087    type = t;
088    setPreciseTarget();
089  }
090
091  private void setPreciseTarget() {
092    if (isVirtual()) {
093      isPreciseTarget = target != null && (target.isFinal() || target.getDeclaringClass().isFinal());
094    } else {
095      isPreciseTarget = !isInterface();
096    }
097  }
098
099  /**
100   * Returns a method operand representing a compiled method with designated
101   * JTOC offset. (used by ConvertToLowLevelIR)
102   * @param callee the callee method
103   * @param offset designated jtop offset of compiled method of callee
104   * @return the method operand
105   */
106  public static MethodOperand COMPILED(RVMMethod callee, Offset offset) {
107    byte type = callee.isStatic() ? STATIC : VIRTUAL;
108    MethodOperand op = new MethodOperand(callee.getMemberRef(), callee, type);
109    op.jtocOffset = offset;
110    op.designatedOffset = true;
111    op.isPreciseTarget = true;
112    return op;
113  }
114
115  public boolean hasDesignatedTarget() {
116    return this.designatedOffset;
117  }
118
119  /**
120   * create a method operand for an INVOKE_SPECIAL bytecode
121   *
122   * @param ref MemberReference of method to call
123   * @param target the RVMMethod to call (may be null)
124   * @return the newly created method operand
125   */
126  public static MethodOperand SPECIAL(MethodReference ref, RVMMethod target) {
127    return new MethodOperand(ref, target, SPECIAL);
128  }
129
130  /**
131   * create a method operand for an INVOKE_STATIC bytecode
132   *
133   * @param ref MemberReference of method to call
134   * @param target the RVMMethod to call (may be null)
135   * @return the newly created method operand
136   */
137  public static MethodOperand STATIC(MethodReference ref, RVMMethod target) {
138    return new MethodOperand(ref, target, STATIC);
139  }
140
141  /**
142   * create a method operand for an INVOKE_STATIC bytecode
143   * where the target method is known at compile time.
144   *
145   * @param target the RVMMethod to call
146   * @return the newly created method operand
147   */
148  public static MethodOperand STATIC(RVMMethod target) {
149    MethodOperand ans = new MethodOperand(target.getMemberRef(), target, STATIC);
150    return ans;
151  }
152
153  /**
154   * create a method operand for an INVOKE_STATIC bytecode
155   * where the target method is known at compile time.
156   *
157   * @param target the RVMMethod to call
158   * @return the newly created method operand
159   */
160  public static MethodOperand STATIC(RVMField target) {
161    return new MethodOperand(target.getMemberRef(), null, STATIC);
162  }
163
164  /**
165   * create a method operand for an INVOKE_VIRTUAL bytecode
166   *
167   * @param ref MemberReference of method to call
168   * @param target the RVMMethod to call (may be null)
169   * @return the newly created method operand
170   */
171  public static MethodOperand VIRTUAL(MethodReference ref, RVMMethod target) {
172    return new MethodOperand(ref, target, VIRTUAL);
173  }
174
175  /**
176   * create a method operand for an INVOKE_INTERFACE bytecode
177   *
178   * @param ref MemberReference of method to call
179   * @param target the RVMMethod to call (may be null)
180   * @return the newly created method operand
181   */
182  public static MethodOperand INTERFACE(MethodReference ref, RVMMethod target) {
183    return new MethodOperand(ref, target, INTERFACE);
184  }
185
186  public boolean isStatic() {
187    return type == STATIC;
188  }
189
190  public boolean isVirtual() {
191    return type == VIRTUAL;
192  }
193
194  public boolean isSpecial() {
195    return type == SPECIAL;
196  }
197
198  public boolean isInterface() {
199    return type == INTERFACE;
200  }
201
202  public boolean hasTarget() {
203    return target != null;
204  }
205
206  public boolean hasPreciseTarget() {
207    return target != null && isPreciseTarget;
208  }
209
210  public RVMMethod getTarget() {
211    return target;
212  }
213
214  public MemberReference getMemberRef() {
215    return memRef;
216  }
217
218  /**
219   * Get whether this operand represents a method call that never
220   * returns (such as a call to athrow());
221   *
222   * @return Does this op represent a call that never returns?
223   */
224  public boolean isNonReturningCall() {
225    return isNonReturningCall;
226  }
227
228  /**
229   * Records whether this operand represents a method call that never
230   * returns (such as a call to athrow()).
231   *
232   * @param neverReturns whether this function will return
233   */
234  public void setIsNonReturningCall(boolean neverReturns) {
235    isNonReturningCall = neverReturns;
236  }
237
238  /**
239   * @return whether this operand is the off branch of a guarded inline
240   */
241  public boolean isGuardedInlineOffBranch() {
242    return isGuardedInlineOffBranch;
243  }
244
245  /**
246   * Record that this operand is the off branch of a guarded inline
247   *
248   * @param f if the operand is in the off branch of a guarded inline
249   */
250  public void setIsGuardedInlineOffBranch(boolean f) {
251    isGuardedInlineOffBranch = f;
252  }
253
254  /**
255   * Refines the target information. Used to reduce the set of
256   * targets for an invokevirtual.
257   *
258   * @param target method to use for refining of information
259   */
260  public void refine(RVMMethod target) {
261    this.target = target;
262    setPreciseTarget();
263  }
264
265  /**
266   * Refines the target information. Used to reduce the set of
267   * targets for an invokevirtual.
268   *
269   * @param targetClass class to use for refining of information
270   */
271  public void refine(RVMType targetClass) {
272    this.target = targetClass.findVirtualMethod(memRef.getName(), memRef.getDescriptor());
273    setPreciseTarget();
274  }
275
276  /**
277   * Refine the target information. Used to reduce the set of
278   * targets for an invokevirtual.
279   *
280   * @param target the target method
281   * @param isPreciseTarget whether the target is precise
282   */
283  public void refine(RVMMethod target, boolean isPreciseTarget) {
284    this.target = target;
285    if (isPreciseTarget) {
286      this.isPreciseTarget = isPreciseTarget;
287    } else {
288      setPreciseTarget();
289    }
290  }
291
292  @Override
293  public Operand copy() {
294    MethodOperand mo = new MethodOperand(memRef, target, type);
295    mo.isPreciseTarget = isPreciseTarget;
296    mo.isNonReturningCall = isNonReturningCall;
297    mo.isGuardedInlineOffBranch = isGuardedInlineOffBranch;
298    return mo;
299  }
300
301  @Override
302  public boolean similar(Operand op) {
303    if (op instanceof MethodOperand) {
304      MethodOperand mop = (MethodOperand) op;
305      return memRef == mop.memRef && target == mop.target && isPreciseTarget == mop.isPreciseTarget;
306    } else {
307      return false;
308    }
309  }
310
311  /**
312   * Returns the string representation of this operand.
313   *
314   * @return a string representation of this operand.
315   */
316  @Override
317  public String toString() {
318    String s = "";
319    switch (type) {
320      case STATIC:
321        s += "static";
322        break;
323      case SPECIAL:
324        s += "special";
325        break;
326      case VIRTUAL:
327        s += "virtual";
328        break;
329      case INTERFACE:
330        s += "interface";
331        break;
332    }
333    if (isPreciseTarget && (type != STATIC)) {
334      s += "_exact";
335    }
336    if (hasSpecialVersion()) {
337      return s + "\"" + spMethod + "\"";
338    }
339    if (target != null) {
340      return s + "\"" + target + "\"";
341    } else {
342      return s + "<" + memRef + ">";
343    }
344  }
345
346  /*
347   * SPECIALIZATION SUPPORT
348   */
349  public SpecializedMethod spMethod;
350
351  public boolean hasSpecialVersion() {
352    return spMethod != null;
353  }
354}