/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.cellspu; import org.jikesrvm.SubordinateArchitecture; import org.jikesrvm.VM; import org.jikesrvm.classloader.VM_Atom; import org.jikesrvm.compilers.common.assembler.VM_ForwardReference; import org.jikesrvm.compilers.common.assembler.cellspu.VM_Assembler; import org.jikesrvm.compilers.common.assembler.cellspu.VM_AssemblerConstants; import org.jikesrvm.objectmodel.VM_JavaHeader; import org.jikesrvm.objectmodel.VM_JavaHeaderConstants; import org.jikesrvm.objectmodel.VM_ObjectModel; import org.jikesrvm.objectmodel.VM_TIBLayoutConstants; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.runtime.VM_Memory; import org.jikesrvm.runtime.VM_Statics; import org.jikesrvm.runtime.VM_SubArchStatics; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Offset; /** * A place to put hand written machine code typically invoked by VM_Magic * methods. * * Hand coding of small inline instruction sequences is typically handled by * each compiler's implementation of VM_Magic methods. A few VM_Magic methods * are so complex that their implementations require many instructions. * But our compilers do not inline arbitrary amounts of machine code. * We therefore write such code blocks here, out of line. * * These code blocks can be shared by all compilers. They can be branched to * via a jtoc offset (obtained from VM_SubArchEntrypoints.XXXInstructionsMethod). * * 17 Mar 1999 Derek Lieber * * 15 Jun 2001 Dave Grove and Bowen Alpern (Derek believed that compilers * could inline these methods if they wanted. We do not believe this would * be very easy since they return thru the LR.) */ public abstract class VM_OutOfLineMachineCode implements VM_BaselineConstants, VM_AssemblerConstants { private static Offset codeCacheNextOff = null; private static Offset codeCacheEndOff = null; private static Offset objectCacheNextOff = null; private static Offset objectCacheEndOff = null; private static Offset staticCacheNextOff = null; private static Offset staticCacheEndOff = null; private static Offset classTibsCacheNextOff= null; private static Offset classTibsCacheEndOff = null; public static SubordinateArchitecture.VM_CodeArray runtimeInstructionsMainMem; // use int's so this doesn't mess up in the bootwriter (should be LocalAddresse) public static int initRuntimeInstructions = Integer.MAX_VALUE; public static int flushCacheInstructions = Integer.MAX_VALUE; public static int blockUntilTagCompletesInstructions = Integer.MAX_VALUE; public static int cacheMethodInstructions = Integer.MAX_VALUE; public static int allocObjectCacheInstructions = Integer.MAX_VALUE; public static int cacheObjectInstructions = Integer.MAX_VALUE; public static int cacheArrayInstructions = Integer.MAX_VALUE; public static int cacheArrayBlockInstructions = Integer.MAX_VALUE; public static int cacheStaticInstructions = Integer.MAX_VALUE; public static int cacheClassTibInstructions = Integer.MAX_VALUE; public static int reflectiveMethodInvokerInstructions = Integer.MAX_VALUE; public static int trapHandlerInstructions = Integer.MAX_VALUE; public static void init() { if (VM.writingBootImage) { // allocate space in subarch JTOC for memory allocation pointers codeCacheNextOff = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT); codeCacheEndOff = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT); objectCacheNextOff = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT); objectCacheEndOff = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT); staticCacheNextOff = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT); staticCacheEndOff = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT); classTibsCacheNextOff= VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT); classTibsCacheEndOff = VM_SubArchStatics.allocateNumericSlot(BYTES_IN_INT); VM_SubArchStatics.setSlotContents(codeCacheNextOff, CODE_CACHE_START); VM_SubArchStatics.setSlotContents(codeCacheEndOff, CODE_CACHE_END); VM_SubArchStatics.setSlotContents(objectCacheNextOff, OBJECT_CACHE_START); VM_SubArchStatics.setSlotContents(objectCacheEndOff, OBJECT_CACHE_END); VM_SubArchStatics.setSlotContents(staticCacheNextOff, STATICS_START); VM_SubArchStatics.setSlotContents(staticCacheEndOff, STATICS_END); VM_SubArchStatics.setSlotContents(classTibsCacheNextOff, CLASS_TIBS_START); VM_SubArchStatics.setSlotContents(classTibsCacheEndOff, CLASS_TIBS_END); // generate out of line instructions VM_Assembler asm = new SubordinateArchitecture.VM_Assembler(0); int codeStartOffset = RUNTIME_CODE_START; initRuntimeInstructions = codeStartOffset; codeStartOffset = genInstructions("generateInitRuntimeInstructions", asm, codeStartOffset); flushCacheInstructions = codeStartOffset; codeStartOffset = genInstructions("generateFlushCacheInstructions", asm, codeStartOffset); blockUntilTagCompletesInstructions = codeStartOffset; codeStartOffset = genInstructions("generateBlockUntilTagCompletes", asm, codeStartOffset); cacheMethodInstructions = codeStartOffset; codeStartOffset = genInstructions("generateCacheMethodInstructions", asm, codeStartOffset); allocObjectCacheInstructions = codeStartOffset; codeStartOffset = genInstructions("genAllocFromObjectCacheInstructions", asm, codeStartOffset); cacheObjectInstructions = codeStartOffset; codeStartOffset = genInstructions("generateCacheObjectInstructions", asm, codeStartOffset); cacheArrayInstructions = codeStartOffset; codeStartOffset = genInstructions("generateCacheArrayInstructions", asm, codeStartOffset); cacheArrayBlockInstructions = codeStartOffset; codeStartOffset = genInstructions("generateCacheArrayBlockInstructions", asm, codeStartOffset); cacheStaticInstructions = codeStartOffset; codeStartOffset = genInstructions("generateCacheStaticInstructions", asm, codeStartOffset); cacheClassTibInstructions = codeStartOffset; codeStartOffset = genInstructions("generateCacheClassTibInstructions", asm, codeStartOffset); reflectiveMethodInvokerInstructions = codeStartOffset; codeStartOffset = genInstructions("generateReflectiveMethodInvokerInstructions", asm, codeStartOffset); if (VM.VerifyAssertions) VM._assert(codeStartOffset <= TRAP_ENTRYPOINT); codeStartOffset = TRAP_ENTRYPOINT; // TODO - sort out trap bit! trapHandlerInstructions = codeStartOffset; codeStartOffset = genInstructions("generateTrapHandlerInstructions", asm, codeStartOffset); runtimeInstructionsMainMem = asm.makeMachineCode().getInstructions(); if (VM.VerifyAssertions) VM._assert(codeStartOffset <= CODE_ENTRYPOINT); } } private static int genInstructions(String genMethod, VM_Assembler asm, int codeStartOffset) { try { int diff, prev; prev = (asm.getMachineCodeIndex() << LG_INSTRUCTION_WIDTH); VM_OutOfLineMachineCode.class.getDeclaredMethod(genMethod, new Class[] {VM_Assembler.class}).invoke(VM_OutOfLineMachineCode.class, new Object[] {asm}); diff = (asm.getMachineCodeIndex() << LG_INSTRUCTION_WIDTH) - prev; codeStartOffset = codeStartOffset + diff; return codeStartOffset; } catch (Exception e) { e.printStackTrace(); VM._assert(NOT_REACHED); return 0; } } private static void generateInitRuntimeInstructions(VM_Assembler asm) { // copy phys_id to T0 to be used in asm.emitORI(T0, 0, 0); asm.emitILW(FP, STACK_BEGIN); // start stack from end of memory (leave buffer for stack objects) asm.emitILA(JTOC, JTOC_TABLE_START + (JTOC_TABLE_LENGTH / 2)); asm.emitILA(TRAP_ENTRY_REG, TRAP_ENTRYPOINT); asm.emitIL(PROCESSOR_REGISTER, -1); // This will be set later in VM_RuntimeMethods asm.emitILA(LINK_REG, CODE_ENTRYPOINT); // runon to flush cache, jump to code entrypoint through link register } private static void generateFlushCacheInstructions(VM_Assembler asm) { // reload numeric and ref static tables to flush statics asm.emitLoad(S2, JTOC, VM_SubArchStatics.mainStaticTocAddrOff); asm.emitILA(S3, STATICS_TOC); asm.emitILA(S4, STATICS_TOC_LENGTH); asm.emitGET(S2, S4, S3, STATIC_CACHE_READ_TAG_GROUP); // reset statics cache as empty asm.emitILA(T6, STATICS_START); asm.emitStore(T6, JTOC, staticCacheNextOff); // clear out object cache table // TODO - Add hint for branch asm.emitIL(S4, 0); asm.emitILA(T1, OBJECT_CACHE_TABLE); asm.emitILW(S5, OBJECT_CACHE_TABLE_LENGTH); int zeroLoopIndex = asm.getMachineCodeIndex(); asm.emitAI(S5, S5, -BYTES_IN_QUAD); asm.emitSTQX(S4, T1, S5); asm.emitBRNZ(S5, zeroLoopIndex); // reset cache as empty asm.emitILA(T6, OBJECT_CACHE_START); asm.emitStore(T6, JTOC, objectCacheNextOff); // wait for writes to complete (otherwise backing may be overwritten since cache now empty) asm.emitBlockUntilComplete(OBJECT_CACHE_WRITE_TAG_GROUP); // wait for static TOC to be reloaded asm.emitBlockUntilComplete(STATIC_CACHE_READ_TAG_GROUP); // branch back to method asm.emitBI(LINK_REG); } /** Expects tag bitmask in S0 */ private static void generateBlockUntilTagCompletes(VM_Assembler asm) { // block until method is read asm.emitBlockUntilComplete(); // branch back to method asm.emitBI(LINK_REG); } /** * trap Params - methodRefReg(mainAddr): S5, staticsAddrReg: T7, methodOffsetReg: T5 (if methodOffset is in JTOC, size offset is int T6) * trap Returns - methodRefReg(localAddr): S5 */ private static void generateCacheMethodInstructions(VM_Assembler asm) { // TODO - Branch hint // load size into T2 asm.emitAI(S1, T5, BYTES_IN_ADDRESS); asm.emitCEQ(S0, JTOC, T7); asm.emitSELB(T6, S1, T6, S0); asm.emitLoad(T2, T7, T6); genAllocFromCodeCache(asm); // pull code into local memory asm.emitGET(S5, T2, T3, METHOD_CACHE_READ_TAG_GROUP); // update method pointer in class static to point to cached entry asm.emitStore(T3, T7, T5); // update S5 which is used to jump to the newly cached method asm.emitORI(S5, T3, 0); asm.emitAI(LINK_REG, LINK_REG, BYTES_IN_ADDRESS); // correct link reg asm.emitILW(S0, (0x1 << METHOD_CACHE_READ_TAG_GROUP)); if (VM.VerifyAssertions) VM._assert(blockUntilTagCompletesInstructions != Integer.MAX_VALUE); int currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBR((blockUntilTagCompletesInstructions - currAddr) >> LOG_BYTES_IN_INT); } private static void generateCacheObjectInstructions(VM_Assembler asm) { // Object reference in T4 and expected size in T2, S6 holds cache entry index // output resulting reference in T1 // correct pointer for header asm.emitAI(S5, T4, -(3 * BYTES_IN_INT)); // align transfer to 16 bytes asm.emitANDI(S7, S5, 0xf); asm.emitANDC(S5, S5, S7); // update size with alignment asm.emitA(T2, T2, S7); // align to a 16 byte transfer asm.emitANDI(S4, T2, 0xf); asm.emitANDC(T2, T2, S4); // TODO get rid of this branch if possible VM_ForwardReference fr1 = asm.emitForwardBRZ(S4); asm.emitAI(T2, T2, BYTES_IN_QUAD); fr1.resolve(asm); if (VM.VerifyAssertions) VM._assert(allocObjectCacheInstructions != Integer.MAX_VALUE); int currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBRSL(S4, (allocObjectCacheInstructions - currAddr) >> LOG_BYTES_IN_INT); // pull object into local memory asm.emitGET(S5, T2, T3, OBJECT_CACHE_READ_TAG_GROUP); // generate actual local memory reference to object after alignment asm.emitAI(S2, T3, (3 * BYTES_IN_INT)); asm.emitA(S3, S2, S7); // embed size into pointer asm.emitSF(T2, S7, T2); // subtract size added before object for alignment asm.emitSHLI(T2, T2, 18); asm.emitOR(T1, S3, T2); // update object cache with this entry asm.emitCWD(S4, JTOC, BYTES_IN_INT); asm.emitSHUFB(S5, T1, T4, S4); asm.emitILW(S4, OBJECT_CACHE_TABLE); asm.emitStoreDouble(S5, S4, S6); asm.emitILW(S0, (0x1 << OBJECT_CACHE_READ_TAG_GROUP)); asm.emitAI(LINK_REG, LINK_REG, BYTES_IN_INT); // correct link register if (VM.VerifyAssertions) VM._assert(blockUntilTagCompletesInstructions != Integer.MAX_VALUE); currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBR((blockUntilTagCompletesInstructions - currAddr) >> LOG_BYTES_IN_INT); } private static void generateCacheArrayInstructions(VM_Assembler asm) { // Pull header into local memory (Arrays are aligned to quadword boundaries asm.emitAI(S5, T0, -BYTES_IN_QUAD); // returns local reference in T3 asm.emitIL(T2, BYTES_IN_QUAD); if (VM.VerifyAssertions) VM._assert(allocObjectCacheInstructions != Integer.MAX_VALUE); int currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBRSL(S4, (allocObjectCacheInstructions - currAddr) >> LOG_BYTES_IN_INT); // pull object into local memory asm.emitGET(S5, T2, T3, OBJECT_CACHE_READ_TAG_GROUP); // block until it has been array header is cached asm.emitBlockUntilComplete(OBJECT_CACHE_READ_TAG_GROUP); // check how many block pointers we will require for this array asm.emitLoad(S0, T3, VM_ObjectModel.getArrayLengthOffset().plus(BYTES_IN_QUAD)); asm.emitIL(S7, 0); asm.emitROTMI(S3, S0, LOG_ARRAY_BLOCK_ENTRIES); // number of blocks // add extra block if needed asm.emitANDI(S8, S0, ARRAY_BLOCK_MASK); asm.emitCEQ(S5, S8, S7); asm.emitAI(S8, S3, 1); asm.emitSELB(T2, S8, S3, S5); asm.emitSHLI(S0, T2, LOG_BYTES_IN_INT); // shift into bytes // align to quadword asm.emitANDI(S3, S0, (BYTES_IN_QUAD - 1)); asm.emitCEQ(S5, S3, S7); asm.emitSFI(S8, S3, BYTES_IN_QUAD); asm.emitA(S3, S0, S8); asm.emitSELB(T2, S3, S0, S5); if (VM.VerifyAssertions) VM._assert(allocObjectCacheInstructions != Integer.MAX_VALUE); currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBRSL(S4, (allocObjectCacheInstructions - currAddr) >> LOG_BYTES_IN_INT); // copy actual local memory reference to object asm.emitORI(T5, T3, 0); // update object cache with this entry asm.emitCWD(S8, JTOC, BYTES_IN_INT); asm.emitSHUFB(S5, T5, T0, S8); asm.emitILW(S8, OBJECT_CACHE_TABLE); asm.emitStoreDouble(S5, S8, S6); // zero out array block indexes int zeroLoopIndex = asm.getMachineCodeIndex(); asm.emitAI(T2, T2, -BYTES_IN_QUAD); asm.emitSTQX(S7, T5, T2); asm.emitBRNZ(T2, zeroLoopIndex); // branch back to method asm.emitAI(LINK_REG, LINK_REG, BYTES_IN_INT); // correct link register asm.emitBI(LINK_REG); } private static void generateCacheArrayBlockInstructions(VM_Assembler asm) { asm.emitLoad(S0, T5, VM_ObjectModel.getArrayLengthOffset()); asm.emitORI(S9, T5, 0); // TODO - try and remove this!! // check if we are loading the last block (if so it will be smaller than a full block) asm.emitROTMI(T2, S0, (-LOG_ARRAY_BLOCK_ENTRIES)); asm.emitCEQ(S2, T2, S7); asm.emitILW(S5, ARRAY_BLOCK_ENTRIES); asm.emitANDI(S6, S0, ARRAY_BLOCK_MASK); asm.emitSELB(T2, S5, S6, S2); // select full block or end part block asm.emitSHL(S0, T2, S8); // shift into bytes // align to quadword boundary asm.emitANDI(S3, S0, (BYTES_IN_QUAD - 1)); asm.emitCEQI(S5, S3, 0); asm.emitSFI(S6, S3, BYTES_IN_QUAD); asm.emitA(S3, S0, S6); asm.emitSELB(T2, S3, S0, S5); // allocate space for block if (VM.VerifyAssertions) VM._assert(allocObjectCacheInstructions != Integer.MAX_VALUE); int currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBRSL(S4, (allocObjectCacheInstructions - currAddr) >> LOG_BYTES_IN_INT); // get block from main memory asm.emitSHLI(S5, S7, LOG_ARRAY_BLOCK_ENTRIES); asm.emitA(T0, T0, S5); asm.emitGET(T0, T2, T3, OBJECT_CACHE_READ_TAG_GROUP); // result is T3 - update array table asm.emitStore(T3, S9, S7); asm.emitILW(S0, (0x1 << OBJECT_CACHE_READ_TAG_GROUP)); asm.emitAI(LINK_REG, LINK_REG, BYTES_IN_INT); // correct link register if (VM.VerifyAssertions) VM._assert(blockUntilTagCompletesInstructions != Integer.MAX_VALUE); currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBR((blockUntilTagCompletesInstructions - currAddr) >> LOG_BYTES_IN_INT); } /** * Cache a static block (numeric or reference) in local memory * * Trap Params: staticsAddrReg(mainMem Addr): T4, tocOffsetReg == T5 * Trap Returns: staticsAddrReg(local Addr): T4 */ private static void generateCacheStaticInstructions(VM_Assembler asm) { asm.emitILA(S0, STATIC_TOC_JTOC_OFF); asm.emitSF(S2, S0, T5); asm.emitROTMI(S4, S2, -1); // get index to size table asm.emitILA(S0, SIZE_STATICS_TABLE); asm.emitLoad(S3, S0, S4); // load size // shift size if we are looking for a ref block asm.emitANDI(S0, T5, BYTES_IN_INT); asm.emitCEQI(S0, S0, 0); asm.emitROTMI(T3, S3, -10); asm.emitSELB(T3, T3, S3, S0); asm.emitILA(S3, (1<<10) - 1); asm.emitAND(S2, T3, S3); // and mask asm.emitSHLI(T3, S2, LOG_BYTES_IN_QUAD); // allocate space in the statics // TODO - implement a better allocation technique asm.emitLoad(S4, JTOC, staticCacheNextOff, T6); asm.emitLoad(S5, JTOC, staticCacheEndOff); // check if this will overflow the statics cache asm.emitA(S6, S4, T3); asm.emitCGT(S0, S6, S5); asm.emitTRAP(S0, true, VM_TrapConstants.TRAP_STATIC_CACHE_FULL); // TODO - Chuck things out if the cache is full asm.emitGET(T4, T3, S4, STATIC_CACHE_READ_TAG_GROUP); // save the new staticsCacheNext asm.emitStore(S6, JTOC, staticCacheNextOff, T6); // update pointer to local memory address asm.emitORI(T4, S4, 0x0); // save the local memory value in the TOC asm.emitStore(T4, JTOC, T5); asm.emitAI(LINK_REG, LINK_REG, BYTES_IN_ADDRESS); // correct link reg asm.emitILW(S0, (0x1 << STATIC_CACHE_READ_TAG_GROUP)); if (VM.VerifyAssertions) VM._assert(blockUntilTagCompletesInstructions != Integer.MAX_VALUE); int currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBR((blockUntilTagCompletesInstructions - currAddr) >> LOG_BYTES_IN_INT); } /** * Cache a class TIB in local memory * * Trap Params: tibAddrReg(mainMem Addr): T6, tocOffsetReg == T5 (if index loaded from object, T1(localMem), T4(mainMem) holds that object * Trap Returns: tibAddrReg(local Addr): T6 */ private static void generateCacheClassTibInstructions(VM_Assembler asm) { // if tibAddrReg is invalid, load the correct value asm.emitXORI(S0, T6, 0xffffffff); VM_ForwardReference fr1 = asm.emitForwardBRZ(S0); int backFromFindTib = asm.getMachineCodeIndex(); asm.emitILA(S0, TIB_TABLE_JTOC_OFF); asm.emitSF(S2, S0, T5); // get index to table asm.emitILA(S0, SIZE_STATICS_TABLE); asm.emitLoad(S3, S0, S2); // load size asm.emitROTMI(S2, S3, -20); // and shift to method tib size asm.emitSHLI(T3, S2, LOG_BYTES_IN_QUAD); // allocate space in the statics // TODO - implement a better allocation technique asm.emitLoad(S4, JTOC, classTibsCacheNextOff, T6); asm.emitLoad(S5, JTOC, classTibsCacheEndOff); // check if this will overflow the statics cache asm.emitA(S6, S4, T3); asm.emitCGT(S0, S6, S5); asm.emitTRAP(S0, true, VM_TrapConstants.TRAP_CLASS_TIBS_CACHE_FULL); // TODO - Chuck things out if the cache is full asm.emitGET(T6, T3, S4, STATIC_CACHE_READ_TAG_GROUP); // save the new classTibCacheNext asm.emitStore(S6, JTOC, classTibsCacheNextOff, T6); // update pointer to local memory address asm.emitORI(T6, S4, 0x0); // save the local memory value in the TOC asm.emitStore(T6, JTOC, T5); asm.emitAI(LINK_REG, LINK_REG, BYTES_IN_ADDRESS); // correct link reg asm.emitILW(S0, (0x1 << STATIC_CACHE_READ_TAG_GROUP)); if (VM.VerifyAssertions) VM._assert(blockUntilTagCompletesInstructions != Integer.MAX_VALUE); int currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBR((blockUntilTagCompletesInstructions - currAddr) >> LOG_BYTES_IN_INT); fr1.resolve(asm); // if we are here then the object had an invalid toc index - correct it asm.emitLoadUnaligned(S4, T1, VM_JavaHeader.TIB_OFFSET); asm.emitAI(S4, S4, TIB_SUBARCH_CLASS_IDX << LOG_BYTES_IN_ADDRESS); asm.emitANDI(S3, S4, BYTES_IN_QUAD); asm.emitILA(S2, ATOMIC_CACHE_LINE); asm.emitA(S3, S2, S3); asm.emitIL(S2, BYTES_IN_INT); asm.emitGET(S4, S2, S3, STATIC_CACHE_READ_TAG_GROUP); // save the link register asm.emitORI(S5, LINK_REG, 0x0); asm.emitILA(S0, (0x1 << STATIC_CACHE_READ_TAG_GROUP)); if (VM.VerifyAssertions) VM._assert(blockUntilTagCompletesInstructions != Integer.MAX_VALUE); currAddr = asm.getMachineCodeIndex() << LOG_BYTES_IN_INT; asm._emitBRSL(LINK_REG, (blockUntilTagCompletesInstructions - currAddr) >> LOG_BYTES_IN_INT); // restore the link register asm.emitORI(LINK_REG, S5, 0x0); asm.emitLoadUnaligned(T5, S3); // check if the object is actually resolved for the subarch asm.emitILA(S0, VM_TIBLayoutConstants.NOT_RESOLVED_FOR_SUBARCH); asm.emitCEQ(S1, T5, S0); asm.emitTRAP(S1, true, VM_TrapConstants.TRAP_CLASS_NOT_RESOLVED_FOR_SUBARCH); // save the class toc index in the object asm.emitLoadUnaligned(S6, T1, VM_JavaHeader.STATUS_OFFSET); asm.emitANDI(S6, S6, ~0x1ff); asm.emitROTMI(S5, T5, -LOG_BYTES_IN_ADDRESS); asm.emitOR(S6, S6, S5); asm.emitStore(S6, T1, VM_JavaHeader.STATUS_OFFSET); // save in main memory - use prepare and attempt for atomic save asm.emitAI(S6, T4, VM_JavaHeader.STATUS_OFFSET.toInt()); asm.emitANDI(S4, S6, ATOMIC_CACHE_LINE_LENTH - 1); asm.emitSF(T1, S4, S6); // align to cache line asm.emitILA(T6, ATOMIC_CACHE_LINE); int atomicLoop = asm.getMachineCodeIndex(); asm.emitGETLLAR(T1, T6); // *(object+offset), setting processor's reservation address asm.emitATOMIC_WAIT(S0); asm.emitLoad(S0, T6, S4, S6); asm.emitANDI(S0, S0, ~0x1ff); asm.emitOR(S0, S0, S5); asm.emitStore(S0, T6, S4, S6); asm.emitPUTLLC(T1, T6); asm.emitATOMIC_WAIT(S0); asm.emitBRNZ(S0, atomicLoop); asm.emitIOHL(T5, TIB_TABLE_JTOC_OFF); asm.emitLoad(T4, JTOC, T5); asm.emitBR(backFromFindTib); } private static void generateReflectiveMethodInvokerInstructions(VM_Assembler asm) { // save... asm.emitStore(LINK_REG, FP, Offset.fromIntSignExtend(STACKFRAME_NEXT_INSTRUCTION_OFFSET)); // ...return address // TODO - spill area // save old frame pointer asm.emitStore(FP, FP, Offset.fromIntSignExtend(-VM_Memory.alignUp(STACKFRAME_HEADER_SIZE, BYTES_IN_QUAD))); // allocate frame header and save old fp asm.emitAI(FP, FP, -VM_Memory.alignUp(STACKFRAME_HEADER_SIZE, BYTES_IN_QUAD)); asm.emitILW(S4, INVISIBLE_METHOD_ID); asm.emitStore(S4, FP, Offset.fromIntSignExtend(STACKFRAME_METHOD_ID_OFFSET)); // set method id // save the method entry address asm.emitORI(S0, T0, 0x0); // TODO - proper parameter loads asm.emitORI(T0, T1, 0x0); asm.emitORI(T1, T2, 0x0); asm.emitORI(T2, T3, 0x0); asm.emitORI(T3, T4, 0x0); // branch and link to method asm.emitBISL(LINK_REG, S0); // method epilogue code asm.emitLoad(FP, FP); // restore caller's frame asm.emitLoad(S0, FP, Offset.fromIntSignExtend(STACKFRAME_NEXT_INSTRUCTION_OFFSET)); // pick up return address asm.emitBI(S0); } /** * Generates the code necessary to handle traps */ private static void generateTrapHandlerInstructions(VM_Assembler asm) { asm.emitIL(S0, TRAP_MESSAGE); asm.emitWRCH(SPU_WR_OUT_INTR_MBOX, S0); asm.emitWRCH(SPU_WR_OUT_MBOX, S2); // write out the trap code asm.emitRDCH(SPU_RD_IN_MBOX, S0); // wait for instructions from ppu } /** * Generates code necessary to allocate a block of the length in T2 from the code cache * Code returns start of allocated block in T3. * * @param asm VM_Assember */ private static void genAllocFromObjectCacheInstructions(VM_Assembler asm) { // TODO - implement a better allocation technique asm.emitLoad(T3, JTOC, objectCacheNextOff, T7); asm.emitLoad(T5, JTOC, objectCacheEndOff); // check if this will overflow the code cache asm.emitA(T6, T3, T2); asm.emitCGT(T5, T6, T5); asm.emitTRAP(T5, true, VM_TrapConstants.TRAP_OBJECT_CACHE_FULL); // TODO - Chuck things out if the cache is full // otherwise save the new objectCacheNext asm.emitStore(T6, JTOC, objectCacheNextOff, T7); asm.emitBI(S4); } /** * Generates code necessary to allocate a block of the length in T2 from the code cache * Code returns start of allocated block in T3. * * @param asm VM_Assember */ private static void genAllocFromCodeCache(VM_Assembler asm) { // TODO - implement a better allocation technique asm.emitLoad(T3, JTOC, codeCacheNextOff, S6); asm.emitLoad(S4, JTOC, codeCacheEndOff); // check if this will overflow the code cache asm.emitA(S0, T3, T2); asm.emitCGT(S4, S0, S4); asm.emitTRAP(S4, true, VM_TrapConstants.TRAP_CODE_CACHE_FULL); // TODO - Chuck things out if the cache is full // otherwise save the new codeCacheNext asm.emitStore(S0, JTOC, codeCacheNextOff, S6); } }