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.classloader;
014    
015    import org.jikesrvm.VM;
016    import org.jikesrvm.compilers.common.BootImageCompiler;
017    import org.jikesrvm.compilers.common.CompiledMethod;
018    import org.jikesrvm.compilers.common.RuntimeCompiler;
019    import org.jikesrvm.runtime.DynamicLink;
020    import org.jikesrvm.util.HashMapRVM;
021    import org.vmmagic.pragma.Uninterruptible;
022    
023    /**
024     * A method of a java class that has bytecodes.
025     */
026    public final class NormalMethod extends RVMMethod implements BytecodeConstants {
027    
028      /* As we read the bytecodes for the method, we compute
029       * a simple summary of some interesting properties of the method.
030       * Because we do this for every method, we require the summarization to
031       * be fast and the computed summary to be very space efficient.
032       *
033       * The following constants encode the estimated relative cost in
034       * machine instructions when a particular class of bytecode is compiled
035       * by the optimizing compiler. The estimates approximate the typical
036       * optimization the compiler is able to perform.
037       * This information is used to estimate how big a method will be when
038       * it is inlined.
039       */
040      public static final int SIMPLE_OPERATION_COST = 1;
041      public static final int LONG_OPERATION_COST = 2;
042      public static final int ARRAY_LOAD_COST = 2;
043      public static final int ARRAY_STORE_COST = 2;
044      public static final int JSR_COST = 5;
045      public static final int CALL_COST = 6;
046      // Bias to inlining methods with magic
047      // most magics are quite cheap (0-1 instructions)
048      public static final int MAGIC_COST = 0;
049      // News are actually more expensive than calls
050      // but bias to inline methods that allocate
051      // objects becuase we expect better downstream optimization of
052      // the caller due to class analysis
053      // and propagation of nonNullness
054      public static final int ALLOCATION_COST = 4;
055      // Approximations, assuming some CSE/PRE of object model computations
056      public static final int CLASS_CHECK_COST = 2 * SIMPLE_OPERATION_COST;
057      public static final int STORE_CHECK_COST = 4 * SIMPLE_OPERATION_COST;
058      // Just a call.
059      public static final int THROW_COST = CALL_COST;
060      // Really a bunch of operations plus a call, but undercharge because
061      // we don't have worry about this causing an exponential growth of call chain
062      // and we probably want to inline synchronization
063      // (to get a chance to optimize it).
064      public static final int SYNCH_COST = 4 * SIMPLE_OPERATION_COST;
065      // The additional cost of a switch isn't that large, since if the
066      // switch has more than a few cases the method will be too big to inline
067      // anyways.
068      public static final int SWITCH_COST = CALL_COST;
069    
070      // Definition of flag bits
071      private static final char HAS_MAGIC = 0x8000;
072      private static final char HAS_SYNCH = 0x4000;
073      private static final char HAS_ALLOCATION = 0x2000;
074      private static final char HAS_THROW = 0x1000;
075      private static final char HAS_INVOKE = 0x0800;
076      private static final char HAS_FIELD_READ = 0x0400;
077      private static final char HAS_FIELD_WRITE = 0x0200;
078      private static final char HAS_ARRAY_READ = 0x0100;
079      private static final char HAS_ARRAY_WRITE = 0x0080;
080      private static final char HAS_JSR = 0x0040;
081      private static final char HAS_COND_BRANCH = 0x0020;
082      private static final char HAS_SWITCH = 0x0010;
083      private static final char HAS_BACK_BRANCH = 0x0008;
084      private static final char IS_RS_METHOD = 0x0004;
085    
086      /**
087       * storage for bytecode summary flags
088       */
089      private char summaryFlags;
090      /**
091       * storage for bytecode summary size
092       */
093      private char summarySize;
094    
095      /**
096       * words needed for local variables (including parameters)
097       */
098      private final short localWords;
099    
100      /**
101       * words needed for operand stack (high water mark)
102       * TODO: OSR redesign;  add subclass of NormalMethod for OSR method
103       *       and then make this field final in NormalMethod.
104       */
105      private short operandWords;
106    
107      /**
108       * bytecodes for this method (null --> none)
109       */
110      private final byte[] bytecodes;
111    
112      /**
113       * try/catch/finally blocks for this method (null --> none)
114       */
115      private final ExceptionHandlerMap exceptionHandlerMap;
116    
117      /**
118       * pc to source-line info (null --> none)
119       * Each entry contains both the line number (upper 16 bits)
120       * and corresponding start PC (lower 16 bits).
121       */
122      private final int[] lineNumberMap;
123    
124      /**
125       * the local variable table
126       */
127      private static final HashMapRVM<NormalMethod, LocalVariableTable> localVariableTables = new HashMapRVM<NormalMethod, LocalVariableTable>();
128    
129      // Extra fields for on-stack replacement
130      /** Possible OSR bytecode array consisting of prologue and original bytecodes */
131      private static final HashMapRVM<NormalMethod, byte[]> synthesizedBytecodes =
132        new HashMapRVM<NormalMethod, byte[]>();
133      /** Possible OSR record of osr prologue */
134      private static final HashMapRVM<NormalMethod, byte[]> osrPrologues =
135        new HashMapRVM<NormalMethod, byte[]>();
136      /**
137       * Possibly OSR prologue may change the maximum stack height, remember the
138       * original stack height
139       */
140      private static final HashMapRVM<NormalMethod, Integer> savedOperandWords =
141        new HashMapRVM<NormalMethod, Integer>();
142    
143      /**
144       * Construct a normal Java bytecode method's information
145       *
146       * @param dc the TypeReference object of the class that declared this field
147       * @param mr the canonical memberReference for this member.
148       * @param mo modifiers associated with this member.
149       * @param et exceptions thrown by this method.
150       * @param lw the number of local words used by the bytecode of this method
151       * @param ow the number of operand words used by the bytecode of this method
152       * @param bc the bytecodes of this method
153       * @param eMap the exception handler map for this method
154       * @param lm the line number map for this method
155       * @param lvt the local variable table for this method
156       * @param constantPool the constantPool for this method
157       * @param sig generic type of this method.
158       * @param annotations array of runtime visible annotations
159       * @param parameterAnnotations array of runtime visible paramter annotations
160       * @param ad annotation default value for that appears in annotation classes
161       */
162      NormalMethod(TypeReference dc, MemberReference mr, short mo, TypeReference[] et, short lw, short ow,
163                      byte[] bc, ExceptionHandlerMap eMap, int[] lm, LocalVariableTable lvt, int[] constantPool, Atom sig,
164                      RVMAnnotation[] annotations, RVMAnnotation[][] parameterAnnotations, Object ad) {
165        super(dc, mr, mo, et, sig, annotations, parameterAnnotations, ad);
166        localWords = lw;
167        operandWords = ow;
168        bytecodes = bc;
169        exceptionHandlerMap = eMap;
170        lineNumberMap = lm;
171        localVariableTables.put(this, lvt);
172        computeSummary(constantPool);
173      }
174    
175      /**
176       * Generate the code for this method
177       */
178      protected CompiledMethod genCode() throws VerifyError {
179        if (VM.writingBootImage) {
180          return BootImageCompiler.compile(this);
181        } else {
182          return RuntimeCompiler.compile(this);
183        }
184      }
185    
186      /**
187       * Space required by this method for its local variables, in words.
188       * Note: local variables include parameters
189       */
190      @Uninterruptible
191      public int getLocalWords() {
192        return localWords;
193      }
194    
195      /**
196       * Space required by this method for its operand stack, in words.
197       */
198      @Uninterruptible
199      public int getOperandWords() {
200        return operandWords;
201      }
202    
203      /**
204       * Get a representation of the bytecodes in the code attribute of this method.
205       * @return object representing the bytecodes
206       */
207      public BytecodeStream getBytecodes() {
208        return new BytecodeStream(this, bytecodes);
209      }
210    
211      /**
212       * Fill in DynamicLink object for the invoke at the given bytecode index
213       * @param dynamicLink the dynamicLink object to initialize
214       * @param bcIndex the bcIndex of the invoke instruction
215       */
216      @Uninterruptible
217      public void getDynamicLink(DynamicLink dynamicLink, int bcIndex) {
218        if (VM.VerifyAssertions) VM._assert(bytecodes != null);
219        if (VM.VerifyAssertions) VM._assert(bcIndex + 2 < bytecodes.length);
220        int bytecode = bytecodes[bcIndex] & 0xFF;
221        if (VM.VerifyAssertions) {
222          VM._assert((BytecodeConstants.JBC_invokevirtual <= bytecode) &&
223                     (bytecode <= BytecodeConstants.JBC_invokeinterface));
224        }
225        int constantPoolIndex = ((bytecodes[bcIndex + 1] & 0xFF) << BITS_IN_BYTE) | (bytecodes[bcIndex + 2] & 0xFF);
226        dynamicLink.set(getDeclaringClass().getMethodRef(constantPoolIndex), bytecode);
227      }
228    
229      /**
230       * Size of bytecodes for this method
231       */
232      public int getBytecodeLength() {
233        return bytecodes.length;
234      }
235    
236      /**
237       * Exceptions caught by this method.
238       * @return info (null --> method doesn't catch any exceptions)
239       */
240      @Uninterruptible
241      public ExceptionHandlerMap getExceptionHandlerMap() {
242        return exceptionHandlerMap;
243      }
244    
245      /**
246       * Return the line number information for the argument bytecode index.
247       * @return The line number, a positive integer.  Zero means unable to find.
248       */
249      @Uninterruptible
250      public int getLineNumberForBCIndex(int bci) {
251        if (lineNumberMap == null) return 0;
252        int idx;
253        for (idx = 0; idx < lineNumberMap.length; idx++) {
254          int pc = lineNumberMap[idx] & 0xffff; // lower 16 bits are bcIndex
255          if (bci < pc) {
256            if (idx == 0) idx++; // add 1, so we can subtract 1 below.
257            break;
258          }
259        }
260        return lineNumberMap[--idx] >>> 16; // upper 16 bits are line number
261      }
262    
263      // Extra methods for on-stack replacement
264      // BaselineCompiler and BC2IR should check if a method is
265      // for specialization by calling isForOsrSpecialization, the compiler
266      // uses synthesized bytecodes (prologue + original bytecodes) for
267      // OSRing method. Other interfaces of method are not changed, therefore,
268      // dynamic linking and gc referring to bytecodes are safe.
269    
270      /**
271       * Checks if the method is in state for OSR specialization now
272       * @return true, if it is (with prologue)
273       */
274      public boolean isForOsrSpecialization() {
275        synchronized(synthesizedBytecodes) {
276          return synthesizedBytecodes.get(this) != null;
277        }
278      }
279    
280      /**
281       * Sets method in state for OSR specialization, i.e, the subsequent calls
282       * of {@link #getBytecodes} return the stream of specialized bytecodes.
283       *
284       * NB: between flag and action, it should not allow GC or threadSwitch happen.
285       * @param prologue   The bytecode of prologue
286       * @param newStackHeight  The prologue may change the default height of
287       *                        stack
288       */
289      public void setForOsrSpecialization(byte[] prologue, short newStackHeight) {
290        if (VM.VerifyAssertions) {
291          synchronized (synthesizedBytecodes) {
292            VM._assert(synthesizedBytecodes.get(this) == null);
293          }
294        }
295    
296        byte[] newBytecodes = new byte[prologue.length + bytecodes.length];
297        System.arraycopy(prologue, 0, newBytecodes, 0, prologue.length);
298        System.arraycopy(bytecodes, 0, newBytecodes, prologue.length, bytecodes.length);
299    
300        synchronized(osrPrologues) {
301          osrPrologues.put(this, prologue);
302        }
303        synchronized(synthesizedBytecodes) {
304          synthesizedBytecodes.put(this, newBytecodes);
305        }
306        synchronized(savedOperandWords) {
307          savedOperandWords.put(this, Integer.valueOf(operandWords));
308        }
309        if (newStackHeight > operandWords) {
310          this.operandWords = newStackHeight;
311        }
312      }
313    
314      /**
315       * Restores the original state of the method.
316       */
317      public void finalizeOsrSpecialization() {
318        if (VM.VerifyAssertions) {
319          synchronized (synthesizedBytecodes) {
320            VM._assert(synthesizedBytecodes.get(this) != null);
321          }
322        }
323        synchronized(osrPrologues) {
324          osrPrologues.remove(this);
325        }
326        synchronized(synthesizedBytecodes) {
327          synthesizedBytecodes.remove(this);
328        }
329        synchronized(savedOperandWords) {
330          this.operandWords = (short)(savedOperandWords.get(this).intValue());
331          savedOperandWords.remove(this);
332        }
333      }
334    
335      /**
336       * Returns the OSR prologue length for adjusting various tables and maps.
337       * @return the length of prologue if the method is in state for OSR,
338       *         0 otherwise.
339       */
340      public int getOsrPrologueLength() {
341        if(isForOsrSpecialization()) {
342          synchronized(osrPrologues) {
343            return osrPrologues.get(this).length;
344          }
345        } else {
346          return 0;
347        }
348      }
349    
350      /**
351       * Returns a bytecode stream of osr prologue
352       * @return osr prologue bytecode stream
353       */
354      public BytecodeStream getOsrPrologue() {
355        if (VM.VerifyAssertions) {
356          synchronized (synthesizedBytecodes) {
357            VM._assert(synthesizedBytecodes.get(this) != null);
358          }
359        }
360        byte[] osrPrologue;
361        synchronized(osrPrologues) {
362          osrPrologue = osrPrologues.get(this);
363        }
364        return new BytecodeStream(this, osrPrologue);
365      }
366    
367      /**
368       * Returns the synthesized bytecode stream with osr prologue
369       * @return bytecode stream
370       */
371      public BytecodeStream getOsrSynthesizedBytecodes() {
372        byte[] bytecodes;
373        synchronized(synthesizedBytecodes) {
374          bytecodes = synthesizedBytecodes.get(this);
375          if (VM.VerifyAssertions) VM._assert(bytecodes != null);
376        }
377        return new BytecodeStream(this, bytecodes);
378      }
379    
380      /*
381      * Methods to access and compute method summary information
382      */
383    
384      /**
385       * @return An estimate of the expected size of the machine code instructions
386       * that will be generated by the opt compiler if the method is inlined.
387       */
388      public int inlinedSizeEstimate() {
389        return summarySize & 0xFFFF;
390      }
391    
392      /**
393       * @return true if the method contains a Magic.xxx or Address.yyy
394       */
395      public boolean hasMagic() {
396        return (summaryFlags & HAS_MAGIC) != 0;
397      }
398    
399      /**
400       * @return true if the method contains a monitorenter/exit or is synchronized
401       */
402      public boolean hasSynch() {
403        return (summaryFlags & HAS_SYNCH) != 0;
404      }
405    
406      /**
407       * @return true if the method contains an allocation
408       */
409      public boolean hasAllocation() {
410        return (summaryFlags & HAS_ALLOCATION) != 0;
411      }
412    
413      /**
414       * @return true if the method contains an athrow
415       */
416      public boolean hasThrow() {
417        return (summaryFlags & HAS_THROW) != 0;
418      }
419    
420      /**
421       * @return true if the method contains an invoke
422       */
423      public boolean hasInvoke() {
424        return (summaryFlags & HAS_INVOKE) != 0;
425      }
426    
427      /**
428       * @return true if the method contains a getfield or getstatic
429       */
430      public boolean hasFieldRead() {
431        return (summaryFlags & HAS_FIELD_READ) != 0;
432      }
433    
434      /**
435       * @return true if the method contains a putfield or putstatic
436       */
437      public boolean hasFieldWrite() {
438        return (summaryFlags & HAS_FIELD_WRITE) != 0;
439      }
440    
441      /**
442       * @return true if the method contains an array load
443       */
444      public boolean hasArrayRead() {
445        return (summaryFlags & HAS_ARRAY_READ) != 0;
446      }
447    
448      /**
449       * @return true if the method contains an array store
450       */
451      public boolean hasArrayWrite() {
452        return (summaryFlags & HAS_ARRAY_WRITE) != 0;
453      }
454    
455      /**
456       * @return true if the method contains a jsr
457       */
458      public boolean hasJSR() {
459        return (summaryFlags & HAS_JSR) != 0;
460      }
461    
462      /**
463       * @return true if the method contains a conditional branch
464       */
465      public boolean hasCondBranch() {
466        return (summaryFlags & HAS_COND_BRANCH) != 0;
467      }
468    
469      /**
470       * @return true if the method contains a switch
471       */
472      public boolean hasSwitch() {
473        return (summaryFlags & HAS_SWITCH) != 0;
474      }
475    
476      /**
477       * @return true if the method contains a backwards branch
478       */
479      public boolean hasBackwardsBranch() {
480        return (summaryFlags & HAS_BACK_BRANCH) != 0;
481      }
482    
483      /**
484       * @return true if the method is the implementation of a runtime service
485       * that is called "under the covers" from the generated code and thus is not subject to
486       * inlining via the normal mechanisms.
487       */
488      public boolean isRuntimeServiceMethod() {
489        return (summaryFlags & IS_RS_METHOD) != 0;
490      }
491    
492      /**
493       * Set the value of the 'runtime service method' flag to the argument
494       * value.  A method is considered to be a runtime service method if it
495       * is only/primarily invoked "under the covers" from the generated code
496       * and thus is not subject to inlining via the normal mechanisms.
497       * For example, the implementations of bytecodes such as new or checkcast
498       * or the implementation of yieldpoints.
499       * @param value true if this is a runtime service method, false it is not.
500       */
501      public void setRuntimeServiceMethod(boolean value) {
502        if (value) {
503          summaryFlags |= IS_RS_METHOD;
504        } else {
505          summaryFlags &= ~IS_RS_METHOD;
506        }
507      }
508    
509      /**
510       * @return true if the method may write to a given field
511       */
512      public boolean mayWrite(RVMField field) {
513        if (!hasFieldWrite()) return false;
514        FieldReference it = field.getMemberRef().asFieldReference();
515        BytecodeStream bcodes = getBytecodes();
516        while (bcodes.hasMoreBytecodes()) {
517          int opcode = bcodes.nextInstruction();
518          if (opcode == JBC_putstatic || opcode == JBC_putfield) {
519            FieldReference fr = bcodes.getFieldReference();
520            if (!fr.definitelyDifferent(it)) return true;
521          } else {
522            bcodes.skipInstruction();
523          }
524        }
525        return false;
526      }
527    
528      /**
529       * For use by {@link RVMClass#allBootImageTypesResolved()} only.
530       */
531      void recomputeSummary(int[] constantPool) {
532        if (hasFieldRead()) {
533          // Now that all bootimage classes are resolved, we may be able to lower the
534          // estimated machine code size of some getstatics, so recompute summary.
535          computeSummary(constantPool);
536        }
537    
538      }
539    
540      /**
541       * This method computes a summary of interesting method characteristics
542       * and stores an encoding of the summary as an int.
543       */
544      private void computeSummary(int[] constantPool) {
545        int calleeSize = 0;
546        if (isSynchronized()) {
547          summaryFlags |= HAS_SYNCH;
548          calleeSize += 2 * SYNCH_COST; // NOTE: ignoring catch/unlock/rethrow block.  Probably the right thing to do.
549        }
550    
551        BytecodeStream bcodes = getBytecodes();
552        while (bcodes.hasMoreBytecodes()) {
553          switch (bcodes.nextInstruction()) {
554            // Array loads: null check, bounds check, index computation, load
555            case JBC_iaload:
556            case JBC_laload:
557            case JBC_faload:
558            case JBC_daload:
559            case JBC_aaload:
560            case JBC_baload:
561            case JBC_caload:
562            case JBC_saload:
563              summaryFlags |= HAS_ARRAY_READ;
564              calleeSize += ARRAY_LOAD_COST;
565              break;
566    
567              // Array stores: null check, bounds check, index computation, load
568            case JBC_iastore:
569            case JBC_lastore:
570            case JBC_fastore:
571            case JBC_dastore:
572            case JBC_bastore:
573            case JBC_castore:
574            case JBC_sastore:
575              summaryFlags |= HAS_ARRAY_WRITE;
576              calleeSize += ARRAY_STORE_COST;
577              break;
578            case JBC_aastore:
579              summaryFlags |= HAS_ARRAY_WRITE;
580              calleeSize += ARRAY_STORE_COST + STORE_CHECK_COST;
581              break;
582    
583              // primitive computations (likely to be very cheap)
584            case JBC_iadd:
585            case JBC_fadd:
586            case JBC_dadd:
587            case JBC_isub:
588            case JBC_fsub:
589            case JBC_dsub:
590            case JBC_imul:
591            case JBC_fmul:
592            case JBC_dmul:
593            case JBC_idiv:
594            case JBC_fdiv:
595            case JBC_ddiv:
596            case JBC_irem:
597            case JBC_frem:
598            case JBC_drem:
599            case JBC_ineg:
600            case JBC_fneg:
601            case JBC_dneg:
602            case JBC_ishl:
603            case JBC_ishr:
604            case JBC_lshr:
605            case JBC_iushr:
606            case JBC_iand:
607            case JBC_ior:
608            case JBC_ixor:
609            case JBC_iinc:
610              calleeSize += SIMPLE_OPERATION_COST;
611              break;
612    
613              // long computations may be different cost than primitive computations
614            case JBC_ladd:
615            case JBC_lsub:
616            case JBC_lmul:
617            case JBC_ldiv:
618            case JBC_lrem:
619            case JBC_lneg:
620            case JBC_lshl:
621            case JBC_lushr:
622            case JBC_land:
623            case JBC_lor:
624            case JBC_lxor:
625              calleeSize += LONG_OPERATION_COST;
626              break;
627    
628              // Some conversion operations are very cheap
629            case JBC_int2byte:
630            case JBC_int2char:
631            case JBC_int2short:
632              calleeSize += SIMPLE_OPERATION_COST;
633              break;
634              // Others are a little more costly
635            case JBC_i2l:
636            case JBC_l2i:
637              calleeSize += LONG_OPERATION_COST;
638              break;
639              // Most are roughly as expensive as a call
640            case JBC_i2f:
641            case JBC_i2d:
642            case JBC_l2f:
643            case JBC_l2d:
644            case JBC_f2i:
645            case JBC_f2l:
646            case JBC_f2d:
647            case JBC_d2i:
648            case JBC_d2l:
649            case JBC_d2f:
650              calleeSize += CALL_COST;
651              break;
652    
653              // approximate compares as 1 simple operation
654            case JBC_lcmp:
655            case JBC_fcmpl:
656            case JBC_fcmpg:
657            case JBC_dcmpl:
658            case JBC_dcmpg:
659              calleeSize += SIMPLE_OPERATION_COST;
660              break;
661    
662              // most control flow is cheap; jsr is more expensive
663            case JBC_ifeq:
664            case JBC_ifne:
665            case JBC_iflt:
666            case JBC_ifge:
667            case JBC_ifgt:
668            case JBC_ifle:
669            case JBC_if_icmpeq:
670            case JBC_if_icmpne:
671            case JBC_if_icmplt:
672            case JBC_if_icmpge:
673            case JBC_if_icmpgt:
674            case JBC_if_icmple:
675            case JBC_if_acmpeq:
676            case JBC_if_acmpne:
677            case JBC_ifnull:
678            case JBC_ifnonnull:
679              summaryFlags |= HAS_COND_BRANCH;
680              if (bcodes.getBranchOffset() < 0) summaryFlags |= HAS_BACK_BRANCH;
681              calleeSize += SIMPLE_OPERATION_COST;
682              continue; // we've processed all of the bytes, so avoid the call to skipInstruction()
683            case JBC_goto:
684              if (bcodes.getBranchOffset() < 0) summaryFlags |= HAS_BACK_BRANCH;
685              calleeSize += SIMPLE_OPERATION_COST;
686              continue; // we've processed all of the bytes, so avoid the call to skipInstruction()
687            case JBC_goto_w:
688              if (bcodes.getWideBranchOffset() < 0) summaryFlags |= HAS_BACK_BRANCH;
689              calleeSize += SIMPLE_OPERATION_COST;
690              continue; // we've processed all of the bytes, so avoid the call to skipInstruction()
691            case JBC_jsr:
692            case JBC_jsr_w:
693              summaryFlags |= HAS_JSR;
694              calleeSize += JSR_COST;
695              break;
696    
697            case JBC_tableswitch:
698            case JBC_lookupswitch:
699              summaryFlags |= HAS_SWITCH;
700              calleeSize += SWITCH_COST;
701              break;
702    
703            case JBC_putstatic:
704            case JBC_putfield:
705              summaryFlags |= HAS_FIELD_WRITE;
706              calleeSize += SIMPLE_OPERATION_COST;
707              break;
708    
709            case JBC_getstatic:
710              summaryFlags |= HAS_FIELD_READ;
711    
712              // Treat getstatic of primitive values from final static fields
713              // as "free" since we expect it be a compile time constant by the
714              // time the opt compiler compiles the method.
715              FieldReference fldRef = bcodes.getFieldReference(constantPool);
716              if (fldRef.getFieldContentsType().isPrimitiveType()) {
717                RVMField fld = fldRef.peekResolvedField();
718                if (fld == null || !fld.isFinal()){
719                  calleeSize += SIMPLE_OPERATION_COST;
720                }
721              } else {
722                calleeSize += SIMPLE_OPERATION_COST;
723              }
724              continue; // we've processed all of the bytes, so avoid the call to skipInstruction()
725    
726            case JBC_getfield:
727              summaryFlags |= HAS_FIELD_READ;
728              calleeSize += SIMPLE_OPERATION_COST;
729              break;
730    
731              // Various flavors of calls. Assign them call cost (differentiate?)
732            case JBC_invokevirtual:
733            case JBC_invokespecial:
734            case JBC_invokestatic:
735              // Special case Magic's as being cheaper.
736              MethodReference meth = bcodes.getMethodReference(constantPool);
737              if (meth.getType().isMagicType()) {
738                summaryFlags |= HAS_MAGIC;
739                calleeSize += MAGIC_COST;
740              } else {
741                summaryFlags |= HAS_INVOKE;
742                calleeSize += CALL_COST;
743              }
744              continue; // we've processed all of the bytes, so avoid the call to skipInstruction()
745    
746            case JBC_invokeinterface:
747              summaryFlags |= HAS_INVOKE;
748              calleeSize += CALL_COST;
749              break;
750    
751            case JBC_xxxunusedxxx:
752              if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
753              break;
754    
755            case JBC_new:
756            case JBC_newarray:
757            case JBC_anewarray:
758              summaryFlags |= HAS_ALLOCATION;
759              calleeSize += ALLOCATION_COST;
760              break;
761    
762            case JBC_arraylength:
763              calleeSize += SIMPLE_OPERATION_COST;
764              break;
765    
766            case JBC_athrow:
767              summaryFlags |= HAS_THROW;
768              calleeSize += THROW_COST;
769              break;
770    
771            case JBC_checkcast:
772            case JBC_instanceof:
773              calleeSize += CLASS_CHECK_COST;
774              break;
775    
776            case JBC_monitorenter:
777            case JBC_monitorexit:
778              summaryFlags |= HAS_SYNCH;
779              calleeSize += SYNCH_COST;
780              break;
781    
782            case JBC_multianewarray:
783              summaryFlags |= HAS_ALLOCATION;
784              calleeSize += CALL_COST;
785              break;
786          }
787          bcodes.skipInstruction();
788        }
789        if (calleeSize > Character.MAX_VALUE) {
790          summarySize = Character.MAX_VALUE;
791        } else {
792          summarySize = (char) calleeSize;
793        }
794      }
795    
796      /**
797       * @return LocalVariableTable associated with this method
798       */
799      public LocalVariableTable getLocalVariableTable() {
800        return localVariableTables.get(this);
801      }
802    }