001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.compilers.baseline.ia32;
014    
015    import org.jikesrvm.ArchitectureSpecific;
016    import org.jikesrvm.VM;
017    import org.jikesrvm.classloader.NormalMethod;
018    import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
019    import org.jikesrvm.compilers.common.CompiledMethod;
020    import org.jikesrvm.ia32.BaselineConstants;
021    import org.jikesrvm.objectmodel.ObjectModel;
022    import org.jikesrvm.runtime.ExceptionDeliverer;
023    import org.jikesrvm.runtime.Magic;
024    import org.jikesrvm.scheduler.RVMThread;
025    import org.vmmagic.pragma.Unpreemptible;
026    import org.vmmagic.unboxed.Address;
027    import org.vmmagic.unboxed.Offset;
028    
029    /**
030     * Handle exception delivery and stack unwinding for methods compiled by
031     * baseline compiler.
032     */
033    public abstract class BaselineExceptionDeliverer extends ExceptionDeliverer implements BaselineConstants {
034    
035      /**
036       * Pass control to a catch block.
037       */
038      @Override
039      @Unpreemptible("Deliver exception possibly from unpreemptible code")
040      public void deliverException(CompiledMethod compiledMethod, Address catchBlockInstructionAddress,
041                                   Throwable exceptionObject, ArchitectureSpecific.Registers registers) {
042        Address fp = registers.getInnermostFramePointer();
043        NormalMethod method = (NormalMethod) compiledMethod.getMethod();
044        RVMThread myThread = RVMThread.getCurrentThread();
045    
046        // reset sp to "empty expression stack" state
047        //
048        Address sp = fp.plus(BaselineCompilerImpl.getEmptyStackOffset(method));
049    
050        // push exception object as argument to catch block
051        //
052        sp = sp.minus(BYTES_IN_ADDRESS);
053        sp.store(Magic.objectAsAddress(exceptionObject));
054        registers.gprs.set(SP.value(), sp.toWord());
055    
056        // set address at which to resume executing frame
057        registers.ip = catchBlockInstructionAddress;
058    
059        // branch to catch block
060        //
061        VM.enableGC(); // disabled right before RuntimeEntrypoints.deliverException was called
062        if (VM.VerifyAssertions) VM._assert(registers.inuse);
063    
064        registers.inuse = false;
065    
066        // 'give back' the portion of the stack we borrowed to run
067        // exception delivery code when invoked for a hardware trap.
068        // If this was a straight software trap (athrow) then setting
069        // the stacklimit should be harmless, since the stacklimit should already have exactly
070        // the value we are setting it too.
071        myThread.stackLimit = Magic.objectAsAddress(myThread.getStack()).plus(STACK_SIZE_GUARD);
072        Magic.restoreHardwareExceptionState(registers);
073        if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
074      }
075    
076      /**
077       * Unwind a stackframe.
078       */
079      @Override
080      @Unpreemptible("Unwind stack possibly from unpreemptible code")
081      public void unwindStackFrame(CompiledMethod compiledMethod, ArchitectureSpecific.Registers registers) {
082        NormalMethod method = (NormalMethod) compiledMethod.getMethod();
083        Address fp = registers.getInnermostFramePointer();
084        if (method.isSynchronized()) { // release the lock, if it is being held
085          Address ip = registers.getInnermostInstructionAddress();
086          Offset instr = compiledMethod.getInstructionOffset(ip);
087          Offset lockOffset = ((BaselineCompiledMethod) compiledMethod).getLockAcquisitionOffset();
088          if (instr.sGT(lockOffset)) { // we actually have the lock, so must unlock it.
089            Object lock;
090            if (method.isStatic()) {
091              lock = method.getDeclaringClass().getResolvedClassForType();
092            } else {
093              lock =
094                  Magic.addressAsObject(fp.plus(BaselineCompilerImpl.locationToOffset(((BaselineCompiledMethod) compiledMethod).getGeneralLocalLocation(
095                      0)) - BYTES_IN_ADDRESS).loadAddress());
096            }
097            if (ObjectModel.holdsLock(lock, RVMThread.getCurrentThread())) {
098              ObjectModel.genericUnlock(lock);
099            }
100          }
101        }
102        // Restore nonvolatile registers used by the baseline compiler.
103        if (VM.VerifyAssertions) VM._assert(SAVED_GPRS == 2);
104        registers.gprs.set(EDI.value(), fp.plus(EDI_SAVE_OFFSET).loadWord());
105        registers.gprs.set(EBX.value(), fp.plus(EBX_SAVE_OFFSET).loadWord());
106        if (method.hasBaselineSaveLSRegistersAnnotation()) {
107          registers.gprs.set(EBP.value(), fp.plus(EBP_SAVE_OFFSET).toWord());
108        }
109    
110        registers.unwindStackFrame();
111      }
112    }
113    
114