VM_Constants.STACKFRAME_SENTINAL_FP,
VM_Constants.STACKFRAME_SENTINEL_FP,
  public static void main(String[] args) {    VM_Address externalJNIEnv = VM_Address.zero();    int pthread_id = 0;    if (trace) {      System.out.println("VM_JNIStartUp: starting RVM ... ");    }    // Mark the flag indicating RVM is running as a subsystem under C    // so the VM won't exit the process when it terminates    VM.runningAsSubsystem = true;    // initialize the JNI environment    VM_JNIEnvironment.boot();    for (int i=0; i<args.length; i++) {      if (args[i].startsWith("-jni ")){ 	//-#if RVM_FOR_64_ADDR	externalJNIEnv = VM_Address.fromLong(Long.valueOf(args[i].substring(5)).longValue());	//-#else	externalJNIEnv = VM_Address.fromIntZeroExtend(Integer.valueOf(args[i].substring(5)).intValue());	//-#endif      }      if (args[i].startsWith("-pid ")) 	pthread_id = Integer.valueOf(args[i].substring(5)).intValue();    }        if (externalJNIEnv.isZero()) {      System.out.println("VM_JNIStartUp:  ERROR, external JNIEnv required for JNI_CreateJavaVM");      System.exit(-1);    } else if (pthread_id==0) {      System.out.println("VM_JNIStartUp:  ERROR, external phtread ID required for JNI_CreateJavaVM");      System.exit(-1);    }          if (trace) {      System.out.println("Main VM_Thread is " + 			 VM.addressAsHexString(VM_Magic.objectAsAddress(VM_Thread.getCurrentThread())));      System.out.println("env for external pthread is " + VM.addressAsHexString(externalJNIEnv));      System.out.println("AttachRequest at " + VM.addressAsHexString(VM_Magic.getTocPointer().add(			 VM_BootRecord.the_boot_record.attachThreadRequestedOffset)));    }    // before executing the following code, bind ourselves to the current processor    VM_Processor currentVP = VM_Processor.getCurrentProcessor();    VM_Thread.getCurrentThread().processorAffinity = currentVP;    // set up the JNI service thread in VM_Scheduler    // we need to do this in the main Java thread because new Java threads created    // by JNIServiceThread for AttachCurrentThread needs to be from the "main" thread    // group     VM_Thread j = new JNIServiceThread();    j.start();    // wait for the service thread to start up and go into its attachThreadQueue    while (VM_Scheduler.attachThreadQueue.isEmpty()) {      VM_Thread.getCurrentThread().yield();    }    if (trace)      System.out.println("VM_JNIStartUp: JNIServiceThread started");    // Set up the JNICleanUpThread that will signal the external pthread    // it will also be bound to the same processor    VM_JNICreateVMFinishThread cleanupThread =       new VM_JNICreateVMFinishThread(VM_Thread.getCurrentThread(),				     externalJNIEnv,				     currentVP);    // get it going    cleanupThread.start(currentVP.readyQueue);    // wait for it to come up    if (trace)      System.out.println("VM_JNIStartUp: waiting for clean up thread");     while (!cleanupThread.ready) {      VM_Thread.getCurrentThread().yield();    }    /***************************************************************************     * Now we prepare the data structure to create the appearance that the Java main     * thread has made a native method call and is executing in the native pthread     */    // Set up the native VM_Processor to associate with the external pthread    VM_Processor nativeVP = VM_Thread.getCurrentThread().nativeAffinity;    if ( nativeVP == null) {      nativeVP =  VM_Processor.createNativeProcessorForExistingOSThread(VM_Thread.getCurrentThread());       VM_Thread.getCurrentThread().nativeAffinity = nativeVP;      // register the vpStatusAddress for this thread in the VM_JNIEnvironment.JNIFunctionPointers      // this is normally done in the prolog of the native method on the Java to C transition      VM_JNIEnvironment.JNIFunctionPointers[(VM_Thread.getCurrentThread().getIndex() * 2) + 1] = 	nativeVP.vpStatusAddress.toInt();    }    // normally done by StartupThread    nativeVP.pthread_id = pthread_id;    ++nativeVP.threadSwitchingEnabledCount;    // Set up the JNI environment for this thread for the external pthread    // to make JNI calls    VM_JNIEnvironment myEnv = VM_Thread.getCurrentThread().getJNIEnv();    myEnv.setFromNative(      VM_Constants.STACKFRAME_SENTINAL_FP,      nativeVP,      VM_Magic.getThreadId());        // set affinity so JNI calls will get scheduled on this VM_Processor    VM_Thread.getCurrentThread().returnAffinity = VM_Processor.getCurrentProcessor();    VM_BootRecord.the_boot_record.bootCompleted = 1;    if (trace)       System.out.println("VM_JNIStartUp: RVM is ready, main thread moving to native VM_Processor ... ");    // take this main thread off the current VM_Processor    // from this point, it exists on the native VM_Processor and will    // only resume execution when the associated pthread make a JNI call    VM_Thread.getCurrentThread().beingDispatched = true;    cleanupThread.proceedToFinish = true;    VM_Thread.morph();    // The VM_Processor should eventually execute the cleanupThread to     // finish setting up this Java main thread and signal the     // external pthread to continue     // Resume execution here when the main pthread calls DestroyJavaVm    // The thread state is restored here by JNIServiceThread    // We are now running on the original Java stack instead of the pthread stack    if (trace) {      System.out.println("VM_JNIStartUp: main Java thread terminated, " +			 Thread.currentThread().getName() + ", " + 			 VM.addressAsHexString(VM_Magic.objectAsAddress(VM_Thread.getCurrentThread())));    }    //  System.out.println("VM_JNIStartUp: " + VM_Scheduler.numActiveThreads + " active threads, " +    //	      VM_Scheduler.numDaemons + " daemons.");    // wait until the main Java thread is the last one    while ((VM_Scheduler.numActiveThreads - VM_Scheduler.numDaemons) > 1) {      VM_Thread.yield();    }        // remove the VP data structure from the list maintained for GC    if (VM_Processor.unregisterAttachedProcessor(nativeVP)==0)      VM.sysWrite("ERROR:  VM_Processor for JNI_CreateJavaVM was not registered\n");    VM_Thread.terminate();  }