001: /*
002: * @(#)CodeHacker.java 1.41 06/10/22
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package vm;
029:
030: import components.*;
031: import java.io.PrintStream;
032: import consts.Const;
033: import consts.CVMConst;
034: import util.DataFormatException;
035: import util.Localizer;
036: import util.SignatureIterator;
037:
038: /*
039: * This class "quickens" Java bytecodes.
040: * That is, it looks at the instructions having symbolic references,
041: * such as get/putfield and methodcalls, attempts to resolve the references
042: * and re-writes the instructions in their JVM-specific, quick form.
043: *
044: * Resolution failures are not (any longer) considered fatal errors, they
045: * just result in code that cannot be considered as read-only, as more
046: * resolution will be required at runtime, as we refuse to re-write any
047: * instructions having unresolved references.
048: *
049: * The following is an exception to the above rule:
050: * References to array classes (having names starting in "[") are
051: * quickened anyway, as we have confidence that these classes, though not
052: * instantiated when this code is run, will be instantiated in time.
053: * (Perhaps this should be under control of a flag?)
054: *
055: */
056: public class CodeHacker {
057:
058: boolean verbose;
059: PrintStream log;
060: boolean useLosslessOpcodes = false;
061: boolean jitOn = false;
062: ClassInfo java_lang_Object;
063:
064: private boolean success;
065: private byte newopcode;
066:
067: public static int quickenings = 0;
068: public static int checkinitQuickenings = 0;
069:
070: public CodeHacker(boolean lossless, boolean jitOn, boolean v) {
071: verbose = v;
072: log = System.err;
073: java_lang_Object = ClassTable.lookupClass("java/lang/Object");
074: if (java_lang_Object == null) {
075: log.println(Localizer.getString("codehacker.lookup_failed",
076: "", "java.lang.Object"));
077: }
078: useLosslessOpcodes = lossless;
079: this .jitOn = jitOn;
080: }
081:
082: private void lookupFailed(ClassConstant me, FMIrefConstant target) {
083: log.println(Localizer.getString("codehacker.lookup_failed",
084: me.name, target));
085: success = false;
086: }
087:
088: private static int getInt(byte code[], int w) {
089: return ((int) code[w] << 24)
090: | (((int) code[w + 1] & 0xff) << 16)
091: | (((int) code[w + 2] & 0xff) << 8)
092: | ((int) code[w + 3] & 0xff);
093: }
094:
095: private static int getUnsignedShort(byte code[], int w) {
096: return (((int) code[w] & 0xff) << 8)
097: | ((int) code[w + 1] & 0xff);
098: }
099:
100: private static void putShort(byte code[], int w, short v) {
101: code[w] = (byte) (v >>> 8);
102: code[w + 1] = (byte) v;
103: }
104:
105: /*
106: * Returns true if the reference was fully quickened.
107: * Returns false if the reference could not be deleted, either because
108: * the lookup failed, or because we had to use the wide variant.
109: * Sets this.success to false if the lookup failed or there was no
110: * usable variant.
111: */
112: private boolean quickenFieldAccess(MethodInfo m, ClassConstant me,
113: byte code[], int loc, boolean isStatic, ConstantObject c[],
114: int oneWord, int twoWords, int refReference,
115: int wideReference, int oneWord_checkinit,
116: int twoWords_checkinit, int refReference_checkinit) {
117: FieldConstant fc = (FieldConstant) c[getUnsignedShort(code,
118: loc + 1)];
119: FieldInfo fi = fc.find();
120: if (fi == null) {
121: //
122: // never even try to quicken anything we cannot resolve.
123: //
124: lookupFailed(me, fc);
125: return false;
126: }
127: byte newCode;
128: boolean useCheckinit = (isStatic && fi.parent.vmClass.hasStaticInitializer);
129:
130: switch (fc.sig.type.string.charAt(0)) {
131: case '[':
132: case 'L':
133: newCode = (byte) ((useCheckinit) ? (refReference_checkinit)
134: : (refReference));
135: break;
136: case 'D':
137: case 'J':
138: newCode = (byte) ((useCheckinit) ? (twoWords_checkinit)
139: : (twoWords));
140: break;
141: default:
142: newCode = (byte) ((useCheckinit) ? (oneWord_checkinit)
143: : (oneWord));
144: break;
145: }
146: if (isStatic) {
147: if (useCheckinit) {
148: CodeHacker.checkinitQuickenings++;
149: m.hasCheckinits = true;
150: }
151: CodeHacker.quickenings++;
152: code[loc] = newCode;
153: return false; // still references constant pool!
154: } else {
155: int fieldOffset = fi.instanceOffset;
156: // CVM wants field offset as actual WORD offset
157: // from the start of the object. This requires
158: // knowing the size of the header.
159: // JVM just wanted a 0-based word index.
160: //
161: fieldOffset += CVMConst.ObjHeaderWords;
162: if (fieldOffset < 0) {
163: lookupFailed(me, fc);
164: log.println(Localizer
165: .getString("codehacker.negative_field_offset"));
166: return false;
167: }
168: //
169: // Disallow lossy field access quickening if
170: // 1 Non-lossy opcodes are requested
171: // 2 The offset won't fit in a byte
172: // 3 The JIT is on, and the field is volatile
173: //
174: if (fieldOffset > 0xFF || useLosslessOpcodes
175: || (jitOn && fi.isVolatile())) {
176: if (wideReference == 0) {
177: lookupFailed(me, fc);
178: log.println(Localizer
179: .getString("codehacker.need_wide_op"));
180: }
181: code[loc] = (byte) wideReference;
182: return false; // still references constant pool!
183: }
184: code[loc + 1] = (byte) fieldOffset;
185: code[loc + 2] = 0;
186: code[loc] = newCode;
187: return true;
188: }
189: }
190:
191: /*
192: * Should a call to the specified methodblock be turned into
193: * an invokesuper_quick instead of a invokenonvirtual_quick?
194: *
195: * The four conditions that have to be satisfied:
196: * The method isn't private
197: * The method isn't an <init> method
198: * The ACC_SUPER flag is set in the current class
199: * The method's class is a superclass (and not equal) to
200: * the current class.
201: */
202: private boolean isSpecialSuperCall(MethodInfo me, MethodInfo callee) {
203: String name = callee.name.string;
204: if ((callee.access & Const.ACC_PRIVATE) != 0)
205: return false;
206: if (name.equals("<init>"))
207: return false;
208: ClassInfo myclass = me.parent;
209: ClassInfo hisclass = callee.parent;
210: if (myclass == hisclass)
211: return false;
212: // walk up my chain, looking for other's class
213: while (myclass != null) {
214: myclass = myclass.super ClassInfo;
215: if (myclass == hisclass)
216: return true;
217: }
218: return false;
219: }
220:
221: private boolean quickenCode(MethodInfo m, ConstantObject c[])
222: throws DataFormatException {
223: byte code[] = m.code;
224: int list[] = m.getLdcInstructions();
225: ConstantObject co;
226: FieldConstant fc;
227: MethodConstant mc;
228: NameAndTypeConstant nt;
229: ClassConstant cc;
230: String t;
231: ClassConstant me = m.parent.this Class;
232: MethodInfo mi;
233: ClassInfo ci;
234:
235: success = true;
236: if (list != null) {
237: for (int i = 0; i < list.length; i++) {
238: int loc = list[i];
239: if (loc < 0)
240: continue;
241: switch ((int) code[loc] & 0xff) {
242: case Const.opc_ldc:
243: //
244: // no danger of lookup failure here,
245: // so don't even examine the referenced object.
246: //
247: co = c[(int) code[loc + 1] & 0xff];
248: if (co instanceof StringConstant) {
249: code[loc] = (byte) Const.opc_aldc_quick;
250: } else if (!(co instanceof ClassConstant)) {
251: code[loc] = (byte) Const.opc_ldc_quick;
252: }
253: co.incldcReference();
254: break;
255: default:
256: throw new DataFormatException(
257: "unexpected opcode in ldc="
258: + ((int) code[loc] & 0xff)
259: + " at loc=" + loc + " in "
260: + m.qualifiedName());
261: }
262: }
263: }
264: list = m.getWideConstantRefInstructions();
265: if (list != null) {
266: MethodInfo[] tList = null;
267: int tli = 0; // index into tList
268: if (VMMethodInfo.SAVE_TARGET_METHODS) {
269: tList = new MethodInfo[list.length];
270: }
271: for (int i = 0; i < list.length; i++) {
272: int loc = list[i];
273: if (loc < 0)
274: continue;
275: co = c[getUnsignedShort(code, loc + 1)];
276: if (!co.isResolved()) {
277: //
278: // don't try to quicken unresolved references.
279: // this is not fatal!
280: //
281: // Do quicken if its a reference to an array!!
282: if ((co instanceof ClassConstant)
283: && ((ClassConstant) co).name.string
284: .charAt(0) == Const.SIGC_ARRAY) {
285: ((ClassConstant) co).forget();
286: } else {
287: if (verbose) {
288: log.println(Localizer.getString(
289: "codehacker.could_not_quicken", m
290: .qualifiedName(), co));
291: }
292: continue;
293: }
294: }
295: switch ((int) code[loc] & 0xff) {
296: case Const.opc_ldc_w:
297: if (co instanceof StringConstant) {
298: code[loc] = (byte) Const.opc_aldc_w_quick;
299: } else if (!(co instanceof ClassConstant)) {
300: code[loc] = (byte) Const.opc_ldc_w_quick;
301: }
302: co.incldcReference();
303: break;
304: case Const.opc_ldc2_w:
305: code[loc] = (byte) Const.opc_ldc2_w_quick;
306: break;
307: case Const.opc_getstatic:
308: // All the accesses to static field are done using
309: // checkinit version of opcodes.
310: quickenFieldAccess(m, me, code, loc, true, c,
311: Const.opc_getstatic_quick,
312: Const.opc_getstatic2_quick,
313: Const.opc_agetstatic_quick, 0,
314: Const.opc_getstatic_checkinit_quick,
315: Const.opc_getstatic2_checkinit_quick,
316: Const.opc_agetstatic_checkinit_quick);
317: break;
318: case Const.opc_putstatic:
319: // All the accesses to static field are done using
320: // checkinit version of opcodes.
321: quickenFieldAccess(m, me, code, loc, true, c,
322: Const.opc_putstatic_quick,
323: Const.opc_putstatic2_quick,
324: Const.opc_aputstatic_quick, 0,
325: Const.opc_putstatic_checkinit_quick,
326: Const.opc_putstatic2_checkinit_quick,
327: Const.opc_aputstatic_checkinit_quick);
328: break;
329: case Const.opc_getfield:
330: if (quickenFieldAccess(m, me, code, loc, false, c,
331: Const.opc_getfield_quick,
332: Const.opc_getfield2_quick,
333: Const.opc_agetfield_quick,
334: Const.opc_getfield_quick_w, 0, 0, 0)) {
335: // doesn't reference constant pool any more
336: list[i] = -1;
337: }
338: break;
339: case Const.opc_putfield:
340: if (quickenFieldAccess(m, me, code, loc, false, c,
341: Const.opc_putfield_quick,
342: Const.opc_putfield2_quick,
343: Const.opc_aputfield_quick,
344: Const.opc_putfield_quick_w, 0, 0, 0)) {
345: // doesn't reference constant pool any more
346: list[i] = -1;
347: }
348: break;
349: case Const.opc_invokevirtual:
350: mc = (MethodConstant) co;
351: mi = mc.find(); // must succeed, if isResolved succeeded!
352: int x = -1;
353: if (mi.parent.isFinal() || mi.isFinalMember()) {
354: code[loc] = (byte) Const.opc_invokenonvirtual_quick;
355: } else if (!jitOn
356: && (x = mi.methodTableIndex) <= 255
357: && !useLosslessOpcodes) {
358: // Do really quick quickening, but only if the JIT
359: // is not on
360: if (mi.parent == java_lang_Object) {
361: newopcode = (byte) Const.opc_invokevirtualobject_quick;
362: } else {
363: String sig = mc.sig.type.string;
364: new SignatureIterator(sig) {
365: public void do_array(int d, int st,
366: int end) {
367: newopcode = (byte) Const.opc_ainvokevirtual_quick;
368: }
369:
370: public void do_object(int st, int end) {
371: newopcode = (byte) Const.opc_ainvokevirtual_quick;
372: }
373:
374: public void do_scalar(char c) {
375: switch (c) {
376: case Const.SIGC_LONG:
377: case Const.SIGC_DOUBLE:
378: newopcode = (byte) Const.opc_dinvokevirtual_quick;
379: break;
380: case Const.SIGC_VOID:
381: newopcode = (byte) Const.opc_vinvokevirtual_quick;
382: break;
383: default:
384: newopcode = (byte) Const.opc_invokevirtual_quick;
385: break;
386: }
387: }
388: }.iterate_returntype();
389: }
390: code[loc] = newopcode;
391: code[loc + 1] = (byte) x;
392: code[loc + 2] = (byte) mi.argsSize;
393: list[i] = -1; // doesn't reference constant pool any more
394: if (VMMethodInfo.SAVE_TARGET_METHODS) {
395: // Save the target method info for inlining
396: tList[tli++] = mi;
397: }
398: } else {
399: //
400: // big index OR useLosslessOpcodes OR jit
401: //
402: code[loc] = (byte) Const.opc_invokevirtual_quick_w;
403: }
404: break;
405: case Const.opc_invokeinterface:
406: code[loc] = (byte) Const.opc_invokeinterface_quick;
407: break;
408: case Const.opc_invokestatic:
409: mc = (MethodConstant) co;
410: mi = mc.find(); // must succeed, if isResolved succeeded!
411: if (mi.parent.vmClass.hasStaticInitializer) {
412: CodeHacker.checkinitQuickenings++;
413: m.hasCheckinits = true;
414: }
415: CodeHacker.quickenings++;
416: code[loc] = (byte) ((mi.parent.vmClass.hasStaticInitializer) ? Const.opc_invokestatic_checkinit_quick
417: : Const.opc_invokestatic_quick);
418: break;
419: case Const.opc_new:
420: /*
421: * If the class to be instantiated has a static
422: * initializer, we must quicken opc_new into
423: * opc_new_checkinit_quick rather than
424: * opc_new_quick.
425: */
426: cc = (ClassConstant) co;
427: ci = cc.find(); // must succeed, if isResolved succeeded!
428: if (ci.vmClass.hasStaticInitializer) {
429: CodeHacker.checkinitQuickenings++;
430: m.hasCheckinits = true;
431: }
432: CodeHacker.quickenings++;
433: code[loc] = (byte) ((ci.vmClass.hasStaticInitializer) ? Const.opc_new_checkinit_quick
434: : Const.opc_new_quick);
435: break;
436: case Const.opc_anewarray:
437: code[loc] = (byte) Const.opc_anewarray_quick;
438: break;
439: case Const.opc_checkcast:
440: code[loc] = (byte) Const.opc_checkcast_quick;
441: break;
442: case Const.opc_instanceof :
443: code[loc] = (byte) Const.opc_instanceof _quick;
444: break;
445: case Const.opc_multianewarray:
446: code[loc] = (byte) Const.opc_multianewarray_quick;
447: break;
448: case Const.opc_invokespecial:
449: mc = (MethodConstant) co;
450: mi = mc.find(); // must succeed.
451: byte newop;
452: if (false) {
453: newop = (byte) Const.opc_invokesuper _quick;
454: } else {
455: newop = (byte) Const.opc_invokenonvirtual_quick;
456: }
457: code[loc] = newop;
458: break;
459: default:
460: throw new DataFormatException(
461: "unexpected opcode in wideConstantRef="
462: + ((int) code[loc] & 0xff)
463: + " at loc=" + loc + " in "
464: + m.qualifiedName());
465: }
466: }
467: // Alloc and copy to new targetMethods array
468: if (VMMethodInfo.SAVE_TARGET_METHODS) {
469: m.targetMethods = new MethodInfo[tli];
470: System.arraycopy(tList, 0, m.targetMethods, 0, tli);
471: }
472: }
473: return success;
474: }
475:
476: public boolean quickenCode(ClassInfo c) {
477: ConstantObject constants[] = c.getConstantPool().getConstants();
478: MethodInfo method[] = c.methods;
479: int n = method.length;
480: int i = 0;
481: boolean result = true;
482: try {
483: for (i = 0; i < n; i++) {
484: if (!quickenCode(method[i], constants))
485: result = false;
486: }
487: } catch (DataFormatException e) {
488: System.err.println("Quickening "
489: + method[i].qualifiedName() + " got exception:");
490: e.printStackTrace();
491: return false;
492: }
493: return result;
494: }
495:
496: }
|