/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.vm.compiler; import java.io.Writer; import org.jnode.assembler.Label; import org.jnode.assembler.NativeStream; import org.jnode.assembler.ObjectResolver; import org.jnode.assembler.UnresolvedObjectRefException; import org.jnode.vm.VmAddress; import org.jnode.vm.VmMagic; import org.jnode.annotation.MagicPermission; import org.jnode.vm.bytecode.BasicBlock; import org.jnode.vm.bytecode.ControlFlowGraph; import org.jnode.vm.classmgr.VmAddressMap; import org.jnode.vm.classmgr.VmByteCode; import org.jnode.vm.classmgr.VmClassLoader; import org.jnode.vm.classmgr.VmCompiledExceptionHandler; import org.jnode.vm.classmgr.VmConstClass; import org.jnode.vm.classmgr.VmMethod; import org.jnode.vm.facade.VmUtils; import org.jnode.vm.objects.VmSystemObject; import org.vmmagic.unboxed.Address; /** * Abstract native code compiler. * * @author Ewout Prangsma (epr@users.sourceforge.net) */ @MagicPermission public abstract class NativeCodeCompiler extends VmSystemObject { /** * Compile the given method during bootstrapping * * @param method * @param os * @param level Optimization level */ public final void compileBootstrap(VmMethod method, NativeStream os, int level) { int start = os.getLength(); final CompiledMethod cm; final boolean abstractM = method.isAbstract(); if (abstractM) { if (method.isStatic()) { throw new Error("Abstract & static"); } if (method.isNative()) { throw new Error("Abstract & native"); } cm = doCompileAbstract(method, os, level, true); if (cm == null) { return; } } else { cm = doCompile(method, os, level, true); } int end = os.getLength(); final VmAddress nativeCode = (VmAddress) cm.getCodeStart().getObject(); final VmCompiledExceptionHandler[] eTable; final VmAddress defExHandler; final VmByteCode bc; final VmAddressMap aTable = cm.getAddressTable(); if (!(method.isNative() || abstractM)) { final NativeStream.ObjectRef defExHRef = cm .getDefExceptionHandler(); if (defExHRef != null) { defExHandler = (VmAddress) defExHRef.getObject(); } else { defExHandler = null; } bc = method.getBytecode(); final CompiledExceptionHandler[] ceh = cm.getExceptionHandlers(); if (ceh != null) { eTable = new VmCompiledExceptionHandler[ceh.length]; for (int i = 0; i < ceh.length; i++) { final VmConstClass catchType = bc.getExceptionHandler(i) .getCatchType(); final VmAddress startPtr = (VmAddress) ceh[i].getStartPc() .getObject(); final VmAddress endPtr = (VmAddress) ceh[i].getEndPc() .getObject(); final VmAddress handler = (VmAddress) ceh[i].getHandler() .getObject(); eTable[i] = new VmCompiledExceptionHandler(catchType, startPtr, endPtr, handler); } } else { eTable = null; } } else { eTable = null; defExHandler = null; bc = null; } method.addCompiledCode(VmUtils.getVm().getCompiledMethods().createCompiledCode(cm, method, this, bc, nativeCode, null, end - start, eTable, defExHandler, aTable), level); } /** * Compile the given method during runtime. * * @param method * @param resolver * @param level Optimization level * @param os The native stream, can be null */ public void compileRuntime(VmMethod method, ObjectResolver resolver, int level, NativeStream os) { if (method.isNative()) { throw new IllegalArgumentException("Cannot compile native methods"); } // long start = System.currentTimeMillis(); // System.out.println("Compiling " + method); if (os == null) { os = createNativeStream(resolver); } final CompiledMethod cm; if (method.isAbstract()) { cm = doCompileAbstract(method, os, level, false); if (cm == null) { return; } } else { cm = doCompile(method, os, level, false); } try { final int startOffset = cm.getCodeStart().getOffset(); final int size = cm.getCodeEnd().getOffset() - startOffset; final byte[] code = new byte[size]; System.arraycopy(os.getBytes(), startOffset, code, 0, size); final Address codePtr = VmMagic.getArrayData(code); final NativeStream.ObjectRef defExHRef = cm .getDefExceptionHandler(); final Address defExHandler; if (defExHRef != null) { defExHandler = codePtr.add(cm.getDefExceptionHandler() .getOffset() - startOffset); } else { defExHandler = Address.zero(); } final VmCompiledExceptionHandler[] eTable; final VmAddressMap aTable = cm.getAddressTable(); final VmByteCode bc = method.getBytecode(); final CompiledExceptionHandler[] ceh = cm.getExceptionHandlers(); if (ceh != null) { eTable = new VmCompiledExceptionHandler[ceh.length]; for (int i = 0; i < ceh.length; i++) { final VmConstClass catchType = bc.getExceptionHandler(i) .getCatchType(); final Address startPtr = codePtr.add(ceh[i].getStartPc() .getOffset() - startOffset); final Address endPtr = codePtr.add(ceh[i].getEndPc() .getOffset() - startOffset); final Address handler = codePtr.add(ceh[i].getHandler() .getOffset() - startOffset); eTable[i] = new VmCompiledExceptionHandler(catchType, startPtr.toAddress(), endPtr.toAddress(), handler .toAddress()); } } else { eTable = null; } method.addCompiledCode(VmUtils.getVm().getCompiledMethods().createCompiledCode( cm, method, this, bc, codePtr.toAddress(), code, size, eTable, defExHandler.toAddress(), aTable), level); // For debugging only // System.out.println("Code: " + NumberUtils.hex(code)); // System.out.println(); // End of debugging only } catch (UnresolvedObjectRefException ex) { throw new CompileError(ex); } if (os.hasUnresolvedObjectRefs()) { throw new CompileError("Unresolved labels after compile!"); } } public abstract void disassemble(VmMethod method, ObjectResolver resolver, int level, Writer writer); /** * Create a native stream for the current architecture. * * @param resolver * @return NativeStream */ public abstract NativeStream createNativeStream(ObjectResolver resolver); /** * Compile the given method into the given stream. * * @param method * @param os * @param level Optimization level * @param isBootstrap * @return The compiled method */ protected CompiledMethod doCompile(VmMethod method, NativeStream os, int level, boolean isBootstrap) { final CompiledMethod cm = new CompiledMethod(level); try { if (method.isNative()) { Object label = new Label(method.getMangledName()); cm.setCodeStart(os.getObjectRef(label)); } else { // Create the visitor CompilerBytecodeVisitor bcv = getBytecodeVisitor(method, cm, os, level, isBootstrap); try { // Wrap in verifier if needed if (!(bcv instanceof VerifyingCompilerBytecodeVisitor)) { bcv = new VerifyingCompilerBytecodeVisitor<CompilerBytecodeVisitor>(bcv); } // Get the bytecode final VmByteCode bc = method.getBytecode(); // Create the control flow graph ControlFlowGraph cfg = (ControlFlowGraph) bc.getCompilerData(); if (cfg == null) { cfg = new ControlFlowGraph(bc); bc.setCompilerData(cfg); } // Compile the code 1 basic block at a time final CompilerBytecodeParser parser = new CompilerBytecodeParser(bc, cfg, bcv); bcv.startMethod(method); for (BasicBlock bb : cfg) { bcv.startBasicBlock(bb); parser.parse(bb.getStartPC(), bb.getEndPC(), false); bcv.endBasicBlock(); } bcv.endMethod(); //remove the compiler data to save memory, will be regenerated if needed bc.setCompilerData(null); } finally { releaseBytecodeVisitor(bcv); } } } catch (RuntimeException x) { System.err.println("ERROR in compilation of " + method.getFullName()); throw x; } catch (Error x) { System.err.println("ERROR in compilation of " + method.getFullName()); throw x; } return cm; } /** * Compile the given abstract method into the given stream. * * @param method * @param os * @param level Optimization level * @param isBootstrap * @return The compiled method */ protected abstract CompiledMethod doCompileAbstract(VmMethod method, NativeStream os, int level, boolean isBootstrap); /** * Create the visitor that converts bytecodes into native code. * * @param method * @param cm * @param os * @param level Optimization level * @param isBootstrap * @return The new bytecode visitor. */ protected abstract CompilerBytecodeVisitor createBytecodeVisitor(VmMethod method, CompiledMethod cm, NativeStream os, int level, boolean isBootstrap); /** * Returns an unused or newly created byte code visitor. * @see #createBytecodeVisitor(org.jnode.vm.classmgr.VmMethod, CompiledMethod, org.jnode.assembler.NativeStream, * int, boolean) */ protected CompilerBytecodeVisitor getBytecodeVisitor(VmMethod method, CompiledMethod cm, NativeStream os, int level, boolean isBootstrap) { return createBytecodeVisitor(method, cm, os, level, isBootstrap); } /** * Call this method when the specified bytecode visitor finished working. * * @param visitor a bytecode visitor */ protected void releaseBytecodeVisitor(CompilerBytecodeVisitor visitor) { } /** * Initialize this compiler * * @param loader */ public abstract void initialize(VmClassLoader loader); /** * Dump compiler statistics to System.out */ public abstract void dumpStatistics(); /** * Gets the magic value of this compiler. * * @return the magic value * @see org.jnode.vm.VmStackFrame#MAGIC_COMPILED * @see org.jnode.vm.VmStackFrame#MAGIC_MASK */ public abstract int getMagic(); /** * Gets the name of this compiler. * * @return the compiler name */ public abstract String getName(); /** * Create an iterator that can iterator of GCMaps generated by this compiler. * * @return the iterator */ public abstract GCMapIterator createGCMapIterator(); /** * Gets the names of the packages that are required by this compiler. * * @return the names */ public abstract String[] getCompilerPackages(); }