blob: 8869ce44a541bc70f1ace1e486ca58aec8e5ff43 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Intel, Nikolay A. Sidelnikov
*/
#include "Ia32IRManager.h"
#include "Stl.h"
#include "Ia32StackInfo.h"
#include "Ia32RuntimeInterface.h"
#include "Ia32InternalTrace.h"
#include <math.h>
namespace Jitrino
{
namespace Ia32 {
//========================================================================================
// class StackInfoInstRegistrar
//========================================================================================
/**
* class StackInfoInstRegistrar performs calling of StackInfo method which
* performes saving information about call instructions for stack unwinding
*
* This transformer ensures that it calls the proper method
*
* This transformer must inserted after Code Emitter, because it needs
* EIP value for instructions.
*/
class StackInfoInstRegistrar : public SessionAction {
void runImpl()
{
StackInfo * stackInfo = (StackInfo*)irManager->getInfo(STACK_INFO_KEY);
assert(stackInfo!=NULL);
stackInfo->registerInsts(*irManager);
stackInfo->setMethodExitString(*irManager);
}
U_32 getNeedInfo()const{ return 0; }
U_32 getSideEffects()const{ return 0; }
bool isIRDumpEnabled(){ return false; }
};
static ActionFactory<StackInfoInstRegistrar> _si_insts("si_insts");
#define CALL_MIN_SIZE 2
#define CALL_MAX_SIZE 6
POINTER_SIZE_INT hashFunc(POINTER_SIZE_INT key, U_32 hashTableSize) {
return (key % hashTableSize);
}
StackDepthInfo hashGet(DepthEntry** entries, POINTER_SIZE_INT key, U_32 hashTableSize) {
DepthEntry * e = entries[hashFunc(key,hashTableSize)];
assert(e);
for(;e !=NULL;) {
if(e->eip == key)
return e->info;
else
e = e->next;
assert(e);
}
return e->info;
}
void hashSet(DepthEntry** entries, POINTER_SIZE_INT eip, U_32 hashTableSize,StackDepthInfo info, MemoryManager& mm) {
POINTER_SIZE_INT key = hashFunc(eip,hashTableSize);
DepthEntry * e = entries[key];
if(!e) {
entries[key] = new(mm) DepthEntry(eip, info, NULL);
} else {
for(;e->next!=NULL;e = e->next) ;
e->next = new(mm) DepthEntry(eip, info, NULL);
}
}
POINTER_SIZE_INT StackInfo::getByteSize() const {
return byteSize ?
byteSize :
(sizeof(StackInfo)+(stackDepthInfo?stackDepthInfo->size()*sizeof(DepthEntry):0))+hashTableSize*sizeof(POINTER_SIZE_INT);
}
POINTER_SIZE_INT StackInfo::getByteSize(MethodDesc* md) {
U_8* data = md->getInfoBlock();
return *(POINTER_SIZE_INT*)data;
}
POINTER_SIZE_INT StackInfo::readByteSize(const U_8* bytes) const{
POINTER_SIZE_INT* data = (POINTER_SIZE_INT *) bytes;
POINTER_SIZE_INT sizeInBytes = *data;
return sizeInBytes;
}
typedef DepthEntry * EntryPtr;
void StackInfo::write(U_8* bytes) {
U_8* data = bytes;
StackInfo* serializedInfo = (StackInfo*)data;
*serializedInfo = *this;
serializedInfo->byteSize = getByteSize();
data+=sizeof(StackInfo);
MemoryManager mm("DepthInfo");
EntryPtr * entries = new(mm) EntryPtr[hashTableSize];
for(U_32 i = 0; i< hashTableSize; i++)
entries[i] = NULL;
for(DepthMap::iterator dmit = stackDepthInfo->begin(); dmit != stackDepthInfo->end(); dmit++) {
hashSet(entries, dmit->first, hashTableSize, dmit->second, mm);
}
U_8* next = data + hashTableSize * sizeof(POINTER_SIZE_INT);
for(U_32 i = 0; i< hashTableSize; i++) {
DepthEntry * e = entries[i];
POINTER_SIZE_INT serializedEntryAddr = 0;
if(entries[i]) {
serializedEntryAddr = (POINTER_SIZE_INT)next;
for(; e != NULL; e = e->next) {
DepthEntry* serialized = (DepthEntry*)next;
*serialized = *e;
next+=sizeof(DepthEntry);
serialized->next = e->next ? (DepthEntry*)next : NULL;
}
}
*((POINTER_SIZE_INT*)data)= serializedEntryAddr;
data+=sizeof(POINTER_SIZE_INT);
}
assert(getByteSize() == (POINTER_SIZE_INT) (((U_8*)next) - bytes));
}
DepthEntry * getHashEntry(U_8* data, POINTER_SIZE_INT eip, U_32 size)
{
if(!size)
return NULL;
POINTER_SIZE_INT key = hashFunc(eip,size);
data += sizeof(StackInfo) + key * sizeof(POINTER_SIZE_INT);
DepthEntry * entry = (DepthEntry *)*(POINTER_SIZE_INT*)data;
for(;entry && entry->eip != eip; entry = entry->next) {
assert(entry!=entry->next);
}
return entry;
}
void StackInfo::read(MethodDesc* pMethodDesc, POINTER_SIZE_INT eip, bool isFirst) {
U_8* data = pMethodDesc->getInfoBlock();
byteSize = ((StackInfo *)data)->byteSize;
hashTableSize = ((StackInfo *)data)->hashTableSize;
frameSize = ((StackInfo *)data)->frameSize;
icalleeOffset = ((StackInfo *)data)->icalleeOffset;
icalleeMask = ((StackInfo *)data)->icalleeMask;
localOffset = ((StackInfo *)data)->localOffset;
offsetOfThis = ((StackInfo *)data)->offsetOfThis;
itraceMethodExitString = ((StackInfo *)data)->itraceMethodExitString;
soeCheckAreaOffset = ((StackInfo *)data)->soeCheckAreaOffset;
if (!isFirst){
DepthEntry * entry = getHashEntry(data, eip, hashTableSize);
assert(entry);
if (!entry){
if (Log::cat_rt()->isEnabled())
Log::cat_rt()->out()<<"eip=0x"<<(void*)eip<<" not found during stack unwinding!"<<::std::endl;
::std::cerr<<"eip=0x"<<(void*)eip<<" not found during stack unwinding!"<<::std::endl;
}
calleeSaveRegsMask = entry->info.calleeSaveRegs;
stackDepth = entry->info.stackDepth;
}else{
assert(eip >= (POINTER_SIZE_INT)pMethodDesc->getCodeBlockAddress(0));
POINTER_SIZE_INT eipOffset = eip - (POINTER_SIZE_INT)pMethodDesc->getCodeBlockAddress(0);
assert(fit32(eipOffset));
if (eipOffset <= soeCheckAreaOffset) {
stackDepth = 0; //0 depth -> stack overflow error
} else {
DepthEntry * entry = NULL;
U_32 i = CALL_MIN_SIZE;
for (; i<= CALL_MAX_SIZE; i++) {
entry = getHashEntry(data, eip + i, hashTableSize);
if(entry)
break;
}
if (entry && (entry->info.callSize == i)) {
stackDepth = entry->info.stackDepth; //call site's depth
} else {
stackDepth = frameSize; //hardware NPE's depth
}
}
if (Log::cat_rt()->isEnabled())
Log::cat_rt()->out()<<"Hardware exception from eip=0x"<<(void*)eip<<::std::endl;
}
}
void StackInfo::unwind(MethodDesc* pMethodDesc, JitFrameContext* context, bool isFirst) const {
POINTER_SIZE_INT offset_step = sizeof(POINTER_SIZE_INT);
#ifdef _EM64T_
if (itraceMethodExitString!=NULL){
Log::cat_rt()->out()<<"__UNWIND__:"
<<(itraceMethodExitString!=(const char*)1?itraceMethodExitString:"")
<<"; unwound from EIP="<<(void*)*context->p_rip
<<::std::endl;
}
context->rsp += stackDepth;
assert((context->rsp & 0xf) == 0x8);
POINTER_SIZE_INT offset = context->rsp;
context->p_rip = (POINTER_SIZE_INT *) (offset);
offset += icalleeOffset;
if(stackDepth != 0) {
if(getRegMask(RegName_R15) & icalleeMask) {
context->p_r15 = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_R14) & icalleeMask) {
context->p_r14 = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_R13) & icalleeMask) {
context->p_r13 = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_R12) & icalleeMask) {
context->p_r12 = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_RDI) & icalleeMask) {
context->p_rdi = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_RSI) & icalleeMask) {
context->p_rsi = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_RBP) & icalleeMask) {
context->p_rbp = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_RBX) & icalleeMask) {
context->p_rbx = (POINTER_SIZE_INT *) offset;
}
}
context->rsp += offset_step; //IP register size
#else
if (itraceMethodExitString!=NULL){
Log::cat_rt()->out()<<"__UNWIND__:"
<<(itraceMethodExitString!=(const char*)1?itraceMethodExitString:"")
<<"; unwound from EIP="<<(void*)*context->p_eip
<<::std::endl;
}
context->esp += stackDepth;
U_32 offset = context->esp;
context->p_eip = (POINTER_SIZE_INT *) offset;
offset += icalleeOffset;
if(stackDepth != 0) {
if(getRegMask(RegName_EDI) & icalleeMask) {
context->p_edi = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_ESI) & icalleeMask) {
context->p_esi = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_EBP) & icalleeMask) {
context->p_ebp = (POINTER_SIZE_INT *) offset;
offset += offset_step;
}
if(getRegMask(RegName_EBX) & icalleeMask) {
context->p_ebx = (POINTER_SIZE_INT *) offset;
}
}
context->esp += offset_step; //IP register size
#endif
}
POINTER_SIZE_INT* StackInfo::getRegOffset(const JitFrameContext* context, RegName reg) const
{
assert(stackDepth >=0);
if(calleeSaveRegsMask & getRegMask(reg)) { //callee save regs
//regnames are hardcoded -> unwind() has hardcoded regs too.
//return register offset for previous stack frame.
//MUST be called before unwind()
switch(reg) {
#ifdef _EM64T_
case RegName_R15:
return context->p_r15;
case RegName_R14:
return context->p_r14;
case RegName_R13:
return context->p_r13;
case RegName_R12:
return context->p_r12;
case RegName_RBP:
return context->p_rbp;
case RegName_RBX:
return context->p_rbx;
#ifdef _WIN64
case RegName_RSI:
return context->p_rsi;
case RegName_RDI:
return context->p_rdi;
#endif
#else
case RegName_ESI:
return context->p_esi;
case RegName_EDI:
return context->p_edi;
case RegName_EBP:
return context->p_ebp;
case RegName_EBX:
return context->p_ebx;
#endif
default:
assert(0);
return NULL;
}
} else { //caller save regs
assert(0);
VERIFY_OUT("Caller save register requested!");
exit(1);
}
}
void StackInfo::fixHandlerContext(JitFrameContext* context)
{
#ifdef _EM64T_
if (itraceMethodExitString!=NULL){
Log::cat_rt()->out()<<"__CATCH_HANDLER__:"
<<(itraceMethodExitString!=(const char*)1?itraceMethodExitString:"")
<<"; unwound from EIP="<<(void*)*context->p_rip
<<::std::endl;
}
context->rsp -= frameSize - stackDepth;
#else
if (itraceMethodExitString!=NULL){
Log::cat_rt()->out()<<"__CATCH_HANDLER__:"
<<(itraceMethodExitString!=(const char*)1?itraceMethodExitString:"")
<<"; unwound from EIP="<<(void*)*context->p_eip
<<::std::endl;
}
context->esp -= frameSize - stackDepth;
#endif
}
void StackInfo::registerInsts(IRManager& irm)
{
MethodDesc& md = irm.getMethodDesc();
if (!md.isStatic()) {
#ifdef _EM64T_
if ((md.isSynchronized() || md.isParentClassIsLikelyExceptionType())) {
EntryPointPseudoInst * entryPointInst = irm.getEntryPointInst();
offsetOfThis = (U_32)entryPointInst->thisOpnd->getMemOpndSubOpnd(MemOpndSubOpndKind_Displacement)->getImmValue();
} else {
offsetOfThis = 0;
}
#else
EntryPointPseudoInst * entryPointInst = irm.getEntryPointInst();
offsetOfThis = (U_32)entryPointInst->getOpnd(0)->getMemOpndSubOpnd(MemOpndSubOpndKind_Displacement)->getImmValue();
#endif
}
const Nodes& nodes = irm.getFlowGraph()->getNodes();
for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) {
Node* node = *it;
if (node->isBlockNode()){
for (Inst * inst=(Inst*)node->getFirstInst(); inst!=NULL; inst=inst->getNextInst()){
if(inst->getMnemonic() == Mnemonic_CALL) {
(*stackDepthInfo)[(POINTER_SIZE_INT)inst->getCodeStartAddr()+inst->getCodeSize()]=
StackDepthInfo(((CallInst *)inst)->getCallingConventionClient().getCallingConvention()->getCalleeSavedRegs(OpndKind_GPReg).getMask(),
inst->getStackDepth(),
inst->getCodeSize());
}
}
}
}
hashTableSize = (U_32)stackDepthInfo->size();
}
void StackInfo::setMethodExitString(IRManager& irm)
{
ConstantAreaItem * cai=(ConstantAreaItem *)irm.getInfo("itraceMethodExitString");
if (cai!=NULL){
itraceMethodExitString=(const char*)cai->getAddress();
if (itraceMethodExitString==NULL)
itraceMethodExitString=(const char*)1;
}
}
}} //namespace