blob: 5dda584d31c3382c43da6437bf3110987584ecda [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed 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.
*/
#include "stack.h"
#include "compiler.h"
#include "object.h"
#include "object_utils.h"
#include "thread_list.h"
namespace art {
bool Frame::HasMethod() const {
return GetMethod() != NULL && (!GetMethod()->IsCalleeSaveMethod());
}
void Frame::Next() {
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
#else
size_t frame_size = GetMethod()->GetFrameSizeInBytes();
DCHECK_NE(frame_size, 0u);
DCHECK_LT(frame_size, 1024u);
byte* next_sp = reinterpret_cast<byte*>(sp_) + frame_size;
sp_ = reinterpret_cast<Method**>(next_sp);
if (*sp_ != NULL) {
DCHECK((*sp_)->GetClass() == Method::GetMethodClass() ||
(*sp_)->GetClass() == Method::GetConstructorClass());
}
#endif
}
uintptr_t Frame::GetReturnPC() const {
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
return 0;
#else
byte* pc_addr = reinterpret_cast<byte*>(sp_) + GetMethod()->GetReturnPcOffsetInBytes();
return *reinterpret_cast<uintptr_t*>(pc_addr);
#endif
}
void Frame::SetReturnPC(uintptr_t pc) {
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
#else
byte* pc_addr = reinterpret_cast<byte*>(sp_) + GetMethod()->GetReturnPcOffsetInBytes();
*reinterpret_cast<uintptr_t*>(pc_addr) = pc;
#endif
}
/*
* Return sp-relative offset for a Dalvik virtual register, compiler
* spill or Method* in bytes using Method*.
* Note that (reg >= 0) refers to a Dalvik register, (reg == -2)
* denotes Method* and (reg <= -3) denotes a compiler temp.
*
* +------------------------+
* | IN[ins-1] | {Note: resides in caller's frame}
* | . |
* | IN[0] |
* | caller's Method* |
* +========================+ {Note: start of callee's frame}
* | core callee-save spill | {variable sized}
* +------------------------+
* | fp callee-save spill |
* +------------------------+
* | filler word | {For compatibility, if V[locals-1] used as wide
* +------------------------+
* | V[locals-1] |
* | V[locals-2] |
* | . |
* | . | ... (reg == 2)
* | V[1] | ... (reg == 1)
* | V[0] | ... (reg == 0) <---- "locals_start"
* +------------------------+
* | Compiler temps | ... (reg == -2)
* | | ... (reg == -3)
* | | ... (reg == -4)
* +------------------------+
* | stack alignment padding| {0 to (kStackAlignWords-1) of padding}
* +------------------------+
* | OUT[outs-1] |
* | OUT[outs-2] |
* | . |
* | OUT[0] |
* | curMethod* | ... (reg == -1) <<== sp, 16-byte aligned
* +========================+
*/
int Frame::GetVRegOffset(const DexFile::CodeItem* code_item,
uint32_t core_spills, uint32_t fp_spills,
size_t frame_size, int reg)
{
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
return 0;
#else
DCHECK_EQ( frame_size & (kStackAlignment - 1), 0U);
int num_spills = __builtin_popcount(core_spills) + __builtin_popcount(fp_spills) + 1 /* filler */;
int num_ins = code_item->ins_size_;
int num_regs = code_item->registers_size_ - num_ins;
int locals_start = frame_size - ((num_spills + num_regs) * sizeof(uint32_t));
if (reg == -2) {
return 0; // Method*
} else if (reg <= -3) {
return locals_start - ((reg + 1) * sizeof(uint32_t)); // Compiler temp
} else if (reg < num_regs) {
return locals_start + (reg * sizeof(uint32_t)); // Dalvik local reg
} else {
return frame_size + ((reg - num_regs) * sizeof(uint32_t)) + sizeof(uint32_t); // Dalvik in
}
#endif
}
uint32_t Frame::GetVReg(const DexFile::CodeItem* code_item, uint32_t core_spills,
uint32_t fp_spills, size_t frame_size, int vreg) const {
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
return 0;
#else
int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
byte* vreg_addr = reinterpret_cast<byte*>(sp_) + offset;
return *reinterpret_cast<uint32_t*>(vreg_addr);
#endif
}
uint32_t Frame::GetVReg(Method* m, int vreg) const {
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
return 0;
#else
DCHECK(m == GetMethod());
const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
DCHECK(code_item != NULL); // can't be NULL or how would we compile its instructions?
uint32_t core_spills = m->GetCoreSpillMask();
uint32_t fp_spills = m->GetFpSpillMask();
size_t frame_size = m->GetFrameSizeInBytes();
return GetVReg(code_item, core_spills, fp_spills, frame_size, vreg);
#endif
}
void Frame::SetVReg(Method* m, int vreg, uint32_t new_value) {
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
#else
DCHECK(m == GetMethod());
const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
DCHECK(code_item != NULL); // can't be NULL or how would we compile its instructions?
uint32_t core_spills = m->GetCoreSpillMask();
uint32_t fp_spills = m->GetFpSpillMask();
size_t frame_size = m->GetFrameSizeInBytes();
int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
byte* vreg_addr = reinterpret_cast<byte*>(sp_) + offset;
*reinterpret_cast<uint32_t*>(vreg_addr) = new_value;
#endif
}
uintptr_t Frame::LoadCalleeSave(int num) const {
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
return 0;
#else
// Callee saves are held at the top of the frame
Method* method = GetMethod();
DCHECK(method != NULL);
size_t frame_size = method->GetFrameSizeInBytes();
byte* save_addr = reinterpret_cast<byte*>(sp_) + frame_size - ((num + 1) * kPointerSize);
#if defined(__i386__)
save_addr -= kPointerSize; // account for return address
#endif
return *reinterpret_cast<uintptr_t*>(save_addr);
#endif
}
Method* Frame::NextMethod() const {
#if defined(ART_USE_LLVM_COMPILER)
LOG(FATAL) << "LLVM compiler don't support this function";
return NULL;
#else
byte* next_sp = reinterpret_cast<byte*>(sp_) + GetMethod()->GetFrameSizeInBytes();
return *reinterpret_cast<Method**>(next_sp);
#endif
}
class StackGetter {
public:
StackGetter(JNIEnv* env, Thread* thread) : env_(env), thread_(thread), trace_(NULL) {
}
static void Callback(void* arg) {
reinterpret_cast<StackGetter*>(arg)->Callback();
}
jobject GetTrace() {
return trace_;
}
private:
void Callback() {
trace_ = thread_->CreateInternalStackTrace(env_);
}
JNIEnv* env_;
Thread* thread_;
jobject trace_;
};
jobject GetThreadStack(JNIEnv* env, Thread* thread) {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
StackGetter stack_getter(env, thread);
thread_list->RunWhileSuspended(thread, StackGetter::Callback, &stack_getter);
return stack_getter.GetTrace();
}
} // namespace art