/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* 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/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.baseline.ia32;
import static org.jikesrvm.VM.NOT_REACHED;
import static org.jikesrvm.ia32.BaselineConstants.EBP_SAVE_OFFSET;
import static org.jikesrvm.ia32.BaselineConstants.EBX_SAVE_OFFSET;
import static org.jikesrvm.ia32.BaselineConstants.EDI_SAVE_OFFSET;
import static org.jikesrvm.ia32.BaselineConstants.SAVED_GPRS;
import static org.jikesrvm.ia32.BaselineConstants.SP;
import static org.jikesrvm.ia32.RegisterConstants.EBP;
import static org.jikesrvm.ia32.RegisterConstants.EBX;
import static org.jikesrvm.ia32.RegisterConstants.EDI;
import static org.jikesrvm.ia32.StackframeLayoutConstants.STACK_SIZE_GUARD;
import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS;
import org.jikesrvm.VM;
import org.jikesrvm.architecture.AbstractRegisters;
import org.jikesrvm.classloader.NormalMethod;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.objectmodel.ObjectModel;
import org.jikesrvm.runtime.ExceptionDeliverer;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.scheduler.RVMThread;
import org.vmmagic.pragma.Unpreemptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
/**
* Handle exception delivery and stack unwinding for methods compiled by
* baseline compiler.
*/
public final class BaselineExceptionDeliverer extends ExceptionDeliverer {
/**
* Pass control to a catch block.
*/
@Override
@Unpreemptible("Deliver exception possibly from unpreemptible code")
public void deliverException(CompiledMethod compiledMethod, Address catchBlockInstructionAddress,
Throwable exceptionObject, AbstractRegisters registers) {
Address fp = registers.getInnermostFramePointer();
RVMThread myThread = RVMThread.getCurrentThread();
// reset sp to "empty expression stack" state
//
Address sp = fp.plus(((ArchBaselineCompiledMethod) compiledMethod).getEmptyStackOffset());
// push exception object as argument to catch block
//
sp = sp.minus(BYTES_IN_ADDRESS);
sp.store(Magic.objectAsAddress(exceptionObject));
registers.getGPRs().set(SP.value(), sp.toWord());
// set address at which to resume executing frame
registers.setIP(catchBlockInstructionAddress);
// branch to catch block
//
VM.enableGC(); // disabled right before RuntimeEntrypoints.deliverException was called
if (VM.VerifyAssertions) VM._assert(registers.getInUse());
registers.setInUse(false);
// 'give back' the portion of the stack we borrowed to run
// exception delivery code when invoked for a hardware trap.
// If this was a straight software trap (athrow) then setting
// the stacklimit should be harmless, since the stacklimit should already have exactly
// the value we are setting it too.
myThread.stackLimit = Magic.objectAsAddress(myThread.getStack()).plus(STACK_SIZE_GUARD);
Magic.restoreHardwareExceptionState(registers);
if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
}
/**
* Unwind a stackframe.
*/
@Override
@Unpreemptible("Unwind stack possibly from unpreemptible code")
public void unwindStackFrame(CompiledMethod compiledMethod, AbstractRegisters registers) {
NormalMethod method = (NormalMethod) compiledMethod.getMethod();
Address fp = registers.getInnermostFramePointer();
if (method.isSynchronized()) { // release the lock, if it is being held
Address ip = registers.getInnermostInstructionAddress();
Offset instr = compiledMethod.getInstructionOffset(ip);
Offset lockOffset = ((ArchBaselineCompiledMethod) compiledMethod).getLockAcquisitionOffset();
if (instr.sGT(lockOffset)) { // we actually have the lock, so must unlock it.
Object lock;
if (method.isStatic()) {
lock = method.getDeclaringClass().getResolvedClassForType();
} else {
lock =
Magic.addressAsObject(fp.plus(BaselineCompilerImpl.locationToOffset(((ArchBaselineCompiledMethod) compiledMethod).getGeneralLocalLocation(
0)) - BYTES_IN_ADDRESS).loadAddress());
}
if (ObjectModel.holdsLock(lock, RVMThread.getCurrentThread())) {
ObjectModel.genericUnlock(lock);
}
}
}
// Restore nonvolatile registers used by the baseline compiler.
if (VM.VerifyAssertions) VM._assert(SAVED_GPRS == 2);
registers.getGPRs().set(EDI.value(), fp.plus(EDI_SAVE_OFFSET).loadWord());
registers.getGPRs().set(EBX.value(), fp.plus(EBX_SAVE_OFFSET).loadWord());
if (method.hasBaselineSaveLSRegistersAnnotation()) {
registers.getGPRs().set(EBP.value(), fp.plus(EBP_SAVE_OFFSET).toWord());
}
registers.unwindStackFrame();
}
}