methodinliner.java

来自「纯java操作系统jnode,安装简单和操作简单的个人使用的Java操作系统」· Java 代码 · 共 511 行

JAVA
511
字号
/*
 * $Id: MethodInliner.java,v 1.2 2004/01/02 08:42:00 epr Exp $
 */
package org.jnode.vm.bytecode;

import org.jnode.vm.classmgr.VmClassLoader;
import org.jnode.vm.classmgr.VmByteCode;
import org.jnode.vm.classmgr.VmConstMethodRef;
import org.jnode.vm.classmgr.VmMethod;
import org.jnode.vm.classmgr.VmType;

/**
 * @author Ewout Prangsma (epr@users.sourceforge.net)
 */
public class MethodInliner {

	private final VmByteCode dst;
	int noLocals;
	int maxStack;
	private final BytecodeModifier modifier;

	/**
	 * Create a new instance
	 * @param dst
	 */
	public MethodInliner(VmByteCode dst) {
		this.dst = dst;
		this.noLocals = dst.getNoLocals();
		this.maxStack = dst.getMaxStack();
		this.modifier = new BytecodeModifier(dst);
	}

	/**
	 * Go through the bytecode and inline all method calls for which 
	 * the oracle has decided that the method call should be inlined.
	 * @param oracle
	 * @param classLoader
	 * @return The number of inlined method calls
	 */
	public int inline(InlineOracle oracle, VmClassLoader classLoader) {
		final InlineMethodVisitor visitor = new InlineMethodVisitor(oracle, classLoader, dst.getMethod());
		BytecodeParser.parse(toByteCode(), visitor);
		return visitor.getInlineCount();
	}

	/**
	 * Create the modified bytecode structure
	 * @return The bytecode
	 */
	public VmByteCode toByteCode() {
		return modifier.toByteCode(noLocals, maxStack);
	}

	/**
	 * Inline bytecode src into bytecode dst at offset dstOfs.
	 * @param dstOfs
	 * @param src
	 * @param skipLen
	 */
	void inline(int dstOfs, VmByteCode src, int skipLen) {
		final int localDelta = dst.getNoLocals();
		noLocals = Math.max(noLocals, dst.getNoExceptionHandlers() + src.getNoLocals());
		maxStack = Math.max(maxStack, dst.getMaxStack() + src.getMaxStack());
		final VmMethod srcMethod = src.getMethod();
		final BytecodeWriter writer = new BytecodeWriter();

		// Create the inline header
		createHeader(writer, srcMethod, localDelta);

		// Insert the header code
		final byte[] header = writer.toByteArray();
		final int headerLen;
		if (header != null) {
			headerLen = header.length;
			modifier.insert(dstOfs, skipLen, header, 0, headerLen, noLocals, maxStack);
		} else {
			headerLen = 0;
		}
		writer.clear();

		// Insert the new method
		final int insOfs = dstOfs + headerLen;
		final byte[] srcBytes = src.getBytecode();
		modifier.insert(insOfs, 0, srcBytes, 0, srcBytes.length, noLocals, maxStack);

		// Create the footer code
		createFooter(writer, srcMethod, localDelta);

		// Insert the footer code
		final byte[] footer = writer.toByteArray();
		final int footerOfs = insOfs + srcBytes.length;
		if (footer != null) {
			modifier.insert(footerOfs, 0, footer, 0, footer.length, noLocals, maxStack);
		}

		// Modify the insert method code (between insOfs and footerOfs)
		final InlinedMethodModifier imModifier = new InlinedMethodModifier(modifier, localDelta);
		final VmByteCode newBc = modifier.toByteCode(noLocals, maxStack);
		BytecodeParser.parse(newBc, imModifier, insOfs, footerOfs, false);
	}

	/**
	 * Create an inline header for a given method to inline
	 * @param writer
	 * @param srcMethod
	 * @param localDelta
	 */
	private void createHeader(BytecodeWriter writer, VmMethod srcMethod, int localDelta) {
		// Pop all arguments into locals
		popArgumentsToLocals(writer, localDelta, srcMethod);

		// Insert a nullpointer check for non-static src methods
		// For synchronized methods, the nullcheck is done automatically
		// in the following monitorenter
		if (!srcMethod.isStatic() && !srcMethod.isSynchronized()) {
			insertNullCheck(writer, localDelta);
		}

		// Insert a monitor if the src method is synchronized
		if (srcMethod.isSynchronized()) {
			insertMonitor(writer, localDelta, true, srcMethod);
		}
	}

	/**
	 * Create an inline footer for a given method to inline
	 * @param writer
	 * @param srcMethod
	 * @param localDelta
	 */
	private void createFooter(BytecodeWriter writer, VmMethod srcMethod, int localDelta) {
		// Insert a monitor if the src method is synchronized
		if (srcMethod.isSynchronized()) {
			insertMonitor(writer, localDelta, false, srcMethod);
		}
	}

	private void popArgumentsToLocals(BytecodeWriter writer, int localDelta, VmMethod method) {
		final int cnt = method.getNoArguments();
		final int[] locals = new int[cnt];
		// Calculate the load indexes
		int ofs = localDelta;
		for (int i = 0; i < cnt; i++) {
			locals[i] = ofs;
			final VmType argType = method.getArgumentType(i);
			final int size;
			if (argType.isPrimitive()) {
				if (argType.equals(VmType.getPrimitiveClass('J'))) {
					size = 2;
				} else if (argType.equals(VmType.getPrimitiveClass('D'))) {
					size = 2;
				} else {
					size = 1;
				}
			} else {
				size = 1;
			}
			ofs += size;
		}
		// Now pop and store in locals, (note in reverse argument order)
		for (int i = cnt - 1; i >= 0; i--) {
			final VmType argType = method.getArgumentType(i);
			final int index = locals[i];
			if (argType.isPrimitive()) {
				final String typeName = argType.getName();
				if (typeName.equals("boolean")) {
					writer.istore(index);
				} else if (typeName.equals("byte")) {
					writer.istore(index);
				} else if (typeName.equals("char")) {
					writer.istore(index);
				} else if (typeName.equals("short")) {
					writer.istore(index);
				} else if (typeName.equals("int")) {
					writer.istore(index);
				} else if (typeName.equals("float")) {
					writer.fstore(index);
				} else if (typeName.equals("long")) {
					writer.lstore(index);
				} else if (typeName.equals("double")) {
					writer.dstore(index);
				}
			} else {
				writer.astore(index);
			}
		}
		// If non-static, pop the object ref
		if (!method.isStatic()) {
			writer.astore(ofs);
		}
	}

	private void insertNullCheck(BytecodeWriter writer, int localDelta) {
		//writer.aload(localDelta); // object-ref of srcMethod
		// TODO Implement me
	}

	private void insertMonitor(BytecodeWriter writer, int localDelta, boolean entry, VmMethod method) {
		if (method.isStatic()) {
			// TODO Implement me
			throw new RuntimeException("Cannot inline static synchronized methods yet");
		} else {
			writer.aload(localDelta); // object-ref of srcMethod
		}
		if (entry) {
			writer.monitorenter();
		} else {
			writer.monitorexit();
		}
	}
	
	final BytecodeModifier getModifier() {
		return modifier;
	}

	/**
	 * This class calls the inline decider on all method calls
	 * and inlines the called methods if the decider has decided
	 * so.
	 * 
	 * For now, interface method invocations are never inlined.
	 * 
	 * @author Ewout Prangsma (epr@users.sourceforge.net)
	 */
	class InlineMethodVisitor extends BytecodeVisitorSupport {

		private final InlineOracle oracle;
		private final VmClassLoader classLoader;
		private int count;
		private final VmMethod dstMethod;

		public InlineMethodVisitor(InlineOracle oracle, VmClassLoader classLoader, VmMethod dstMethod) {
			this.oracle = oracle;
			this.classLoader = classLoader;
			this.dstMethod = dstMethod;
		}

		private void adjustInvoke(VmConstMethodRef methodRef, int invokeType, int invokeLength) {
			methodRef.resolve(classLoader);
			final VmMethod callee = methodRef.getResolvedVmMethod();
			if ((callee.getBytecode() != null) && (callee.getDeclaringClass() == dstMethod.getDeclaringClass())) {
				if (oracle.shouldInline(toByteCode(), callee, invokeType)) {
					final BytecodeParser parser = getParser();
					final int start = parser.getAddress();
					inline(start, callee.getBytecode(), invokeLength);
					final byte[] newCode = getModifier().getNewCode();
					parser.setCode(newCode);
					parser.setContinueAt(start);
					parser.setEndPC(newCode.length);
					count++;
				}
			}
		}

		public int getInlineCount() {
			return count;
		}

		/**
		 * @param methodRef
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokespecial(org.jnode.vm.classmgr.VmConstMethodRef)
		 */
		public void visit_invokespecial(VmConstMethodRef methodRef) {
			adjustInvoke(methodRef, InlineOracle.INVOKE_SPECIAL, 3);
		}

		/**
		 * @param methodRef
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokestatic(org.jnode.vm.classmgr.VmConstMethodRef)
		 */
		public void visit_invokestatic(VmConstMethodRef methodRef) {
			adjustInvoke(methodRef, InlineOracle.INVOKE_STATIC, 3);
		}

		/**
		 * @param methodRef
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokevirtual(org.jnode.vm.classmgr.VmConstMethodRef)
		 */
		public void visit_invokevirtual(VmConstMethodRef methodRef) {
			adjustInvoke(methodRef, InlineOracle.INVOKE_VIRTUAL, 3);
		}

	}

	/**
	 * This class modifies all indexes to local variables and replaced
	 * all returns with goto's.
	 * 
	 * @author Ewout Prangsma (epr@users.sourceforge.net)
	 */
	class InlinedMethodModifier extends ModifiableBytecodeVisitor {

		private final int localDelta;

		public InlinedMethodModifier(BytecodeModifier modifier, int localDelta) {
			super(modifier, noLocals, maxStack);
			this.localDelta = localDelta;
		}

		private final void adjustLoadStore(int index, int singleByteOpcode, int twoByteOpcode) {
			final int start = getParser().getAddress();
			final int newIndex = index + localDelta;
			final byte[] code = getNewCode();
			final int curOpcode = BytecodeWriter.get8(code, start);
			if (curOpcode == singleByteOpcode + index) {
				// It is a single byte opcode
				if (newIndex > 255) {
					// Upgrade to wide opcode
					final byte[] opcode = new byte[4];
					opcode[0] = (byte) 0xc4; // wide
					opcode[1] = (byte) twoByteOpcode;
					BytecodeWriter.set16(opcode, 2, newIndex);
					insert(start, 1, opcode);
					getParser().setContinueAt(start + 4);
				} else if (newIndex > 3) {
					// Upgrade to 2-byte opcode
					final byte[] opcode = new byte[2];
					opcode[0] = (byte) twoByteOpcode;
					opcode[1] = (byte) newIndex;
					insert(start, 1, opcode);
					getParser().setContinueAt(start + 2);
				} else {
					// Keep 1-byte opcode
					BytecodeWriter.set8(code, start, singleByteOpcode + newIndex);
				}
			} else if (curOpcode == 0xC4) {
				// It is a wide opcode
				BytecodeWriter.set16(code, start + 2, newIndex);
			} else {
				// It is a 2-byte opcode
				if (newIndex > 255) {
					// Upgrade to wide opcode
					final byte[] opcode = new byte[4];
					opcode[0] = (byte) 0xc4; // wide
					opcode[1] = (byte) twoByteOpcode;
					BytecodeWriter.set16(opcode, 2, newIndex);
					insert(start, 2, opcode);
					getParser().setContinueAt(start + 4);
				} else {
					BytecodeWriter.set8(code, start + 1, newIndex);
				}
			}
		}

		private final void adjustReturn() {
			final BytecodeParser parser = getParser();
			final int start = parser.getAddress();
			if (start + 1 == parser.getEndPC()) {
				// We're at the end, just remove the return
				remove(start, 1);
			} else {
				// We're somewhere in the middle, replace the return with goto
				final byte[] opcode = new byte[3];
				opcode[0] = (byte) 0xa7; // goto
				BytecodeWriter.set16(opcode, 1, parser.getEndPC() - start + 2);
				insert(start, 1, opcode);
			}
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_aload(int)
		 */
		public void visit_aload(int index) {
			adjustLoadStore(index, 0x2a, 0x19);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_astore(int)
		 */
		public void visit_astore(int index) {
			adjustLoadStore(index, 0x4b, 0x3a);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dload(int)
		 */
		public void visit_dload(int index) {
			adjustLoadStore(index, 0x26, 0x18);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dstore(int)
		 */
		public void visit_dstore(int index) {
			adjustLoadStore(index, 0x47, 0x39);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fload(int)
		 */
		public void visit_fload(int index) {
			adjustLoadStore(index, 0x22, 0x17);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fstore(int)
		 */
		public void visit_fstore(int index) {
			adjustLoadStore(index, 0x43, 0x38);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iload(int)
		 */
		public void visit_iload(int index) {
			adjustLoadStore(index, 0x1a, 0x15);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_istore(int)
		 */
		public void visit_istore(int index) {
			adjustLoadStore(index, 0x3b, 0x36);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lload(int)
		 */
		public void visit_lload(int index) {
			adjustLoadStore(index, 0x1e, 0x16);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lstore(int)
		 */
		public void visit_lstore(int index) {
			adjustLoadStore(index, 0x3f, 0x37);
		}

		/**
		 * @param index
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ret(int)
		 */
		public void visit_ret(int index) {
			final int start = getParser().getAddress();
			final int newIndex = index + localDelta;
			final byte[] code = getNewCode();
			final int curOpcode = BytecodeWriter.get8(code, start);
			if (curOpcode == 0xC4) {
				// It is a wide opcode
				BytecodeWriter.set16(code, start + 2, newIndex);
			} else {
				// It is a ret opcode
				if (newIndex > 255) {
					// Upgrade to wide opcode
					final byte[] opcode = new byte[4];
					opcode[0] = (byte) 0xc4; // wide
					opcode[1] = (byte) 0xA9; // ret
					BytecodeWriter.set16(opcode, 2, newIndex);
					insert(start, 2, opcode);
					getParser().setContinueAt(start + 4);
				} else {
					BytecodeWriter.set8(code, start + 1, newIndex);
				}
			}
		}
		/**
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_areturn()
		 */
		public void visit_areturn() {
			adjustReturn();
		}

		/**
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dreturn()
		 */
		public void visit_dreturn() {
			adjustReturn();
		}

		/**
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_freturn()
		 */
		public void visit_freturn() {
			adjustReturn();
		}

		/**
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ireturn()
		 */
		public void visit_ireturn() {
			adjustReturn();
		}

		/**
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lreturn()
		 */
		public void visit_lreturn() {
			adjustReturn();
		}

		/**
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_return()
		 */
		public void visit_return() {
			adjustReturn();
		}

	}
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?