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 */
013package org.jikesrvm.compilers.baseline.ia32;
014
015import static org.jikesrvm.VM.NOT_REACHED;
016import static org.jikesrvm.ia32.BaselineConstants.EBP_SAVE_OFFSET;
017import static org.jikesrvm.ia32.BaselineConstants.EBX_SAVE_OFFSET;
018import static org.jikesrvm.ia32.BaselineConstants.EDI_SAVE_OFFSET;
019import static org.jikesrvm.ia32.BaselineConstants.SAVED_GPRS;
020import static org.jikesrvm.ia32.BaselineConstants.SP;
021import static org.jikesrvm.ia32.RegisterConstants.EBP;
022import static org.jikesrvm.ia32.RegisterConstants.EBX;
023import static org.jikesrvm.ia32.RegisterConstants.EDI;
024import static org.jikesrvm.ia32.StackframeLayoutConstants.STACK_SIZE_GUARD;
025import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
026
027import org.jikesrvm.VM;
028import org.jikesrvm.architecture.AbstractRegisters;
029import org.jikesrvm.classloader.NormalMethod;
030import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
031import org.jikesrvm.compilers.common.CompiledMethod;
032import org.jikesrvm.objectmodel.ObjectModel;
033import org.jikesrvm.runtime.ExceptionDeliverer;
034import org.jikesrvm.runtime.Magic;
035import org.jikesrvm.scheduler.RVMThread;
036import org.vmmagic.pragma.Unpreemptible;
037import org.vmmagic.unboxed.Address;
038import org.vmmagic.unboxed.Offset;
039
040/**
041 * Handle exception delivery and stack unwinding for methods compiled by
042 * baseline compiler.
043 */
044public final class BaselineExceptionDeliverer extends ExceptionDeliverer {
045
046  /**
047   * Pass control to a catch block.
048   */
049  @Override
050  @Unpreemptible("Deliver exception possibly from unpreemptible code")
051  public void deliverException(CompiledMethod compiledMethod, Address catchBlockInstructionAddress,
052                               Throwable exceptionObject, AbstractRegisters registers) {
053    Address fp = registers.getInnermostFramePointer();
054    NormalMethod method = (NormalMethod) compiledMethod.getMethod();
055    RVMThread myThread = RVMThread.getCurrentThread();
056
057    // reset sp to "empty expression stack" state
058    //
059    Address sp = fp.plus(BaselineCompilerImpl.getEmptyStackOffset(method));
060
061    // push exception object as argument to catch block
062    //
063    sp = sp.minus(BYTES_IN_ADDRESS);
064    sp.store(Magic.objectAsAddress(exceptionObject));
065    registers.getGPRs().set(SP.value(), sp.toWord());
066
067    // set address at which to resume executing frame
068    registers.setIP(catchBlockInstructionAddress);
069
070    // branch to catch block
071    //
072    VM.enableGC(); // disabled right before RuntimeEntrypoints.deliverException was called
073    if (VM.VerifyAssertions) VM._assert(registers.getInUse());
074
075    registers.setInUse(false);
076
077    // 'give back' the portion of the stack we borrowed to run
078    // exception delivery code when invoked for a hardware trap.
079    // If this was a straight software trap (athrow) then setting
080    // the stacklimit should be harmless, since the stacklimit should already have exactly
081    // the value we are setting it too.
082    myThread.stackLimit = Magic.objectAsAddress(myThread.getStack()).plus(STACK_SIZE_GUARD);
083    Magic.restoreHardwareExceptionState(registers);
084    if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
085  }
086
087  /**
088   * Unwind a stackframe.
089   */
090  @Override
091  @Unpreemptible("Unwind stack possibly from unpreemptible code")
092  public void unwindStackFrame(CompiledMethod compiledMethod, AbstractRegisters registers) {
093    NormalMethod method = (NormalMethod) compiledMethod.getMethod();
094    Address fp = registers.getInnermostFramePointer();
095    if (method.isSynchronized()) { // release the lock, if it is being held
096      Address ip = registers.getInnermostInstructionAddress();
097      Offset instr = compiledMethod.getInstructionOffset(ip);
098      Offset lockOffset = ((BaselineCompiledMethod) compiledMethod).getLockAcquisitionOffset();
099      if (instr.sGT(lockOffset)) { // we actually have the lock, so must unlock it.
100        Object lock;
101        if (method.isStatic()) {
102          lock = method.getDeclaringClass().getResolvedClassForType();
103        } else {
104          lock =
105              Magic.addressAsObject(fp.plus(BaselineCompilerImpl.locationToOffset(((BaselineCompiledMethod) compiledMethod).getGeneralLocalLocation(
106                  0)) - BYTES_IN_ADDRESS).loadAddress());
107        }
108        if (ObjectModel.holdsLock(lock, RVMThread.getCurrentThread())) {
109          ObjectModel.genericUnlock(lock);
110        }
111      }
112    }
113    // Restore nonvolatile registers used by the baseline compiler.
114    if (VM.VerifyAssertions) VM._assert(SAVED_GPRS == 2);
115    registers.getGPRs().set(EDI.value(), fp.plus(EDI_SAVE_OFFSET).loadWord());
116    registers.getGPRs().set(EBX.value(), fp.plus(EBX_SAVE_OFFSET).loadWord());
117    if (method.hasBaselineSaveLSRegistersAnnotation()) {
118      registers.getGPRs().set(EBP.value(), fp.plus(EBP_SAVE_OFFSET).toWord());
119    }
120
121    registers.unwindStackFrame();
122  }
123}