* | |
* | caller method... |
* #-------------------# <--- SP on entry
+ *
+ * |
+ * V
+ *
+ * #-------------------#
+ * | caller method... |
+ * #-------------------#
* | Return |
* | R15 | callee save
* | R14 | callee save
* | Padding |
* | RDI/Method* | <- sp
* #-------------------#
- * | local ref cookie | // 4B
- * | padding | // 4B
- * #----------#--------#
- * | | | |
- * | Temp/ | SIRT | | Scratch frame is 4k
- * | Scratch | v |
- * | Frame #--------|
- * | |
- * | #--------|
- * | | ^ |
- * | | JNI | |
- * | | Stack| |
- * #----------#--------# <--- SP on native call (needs alignment?)
- * | |
- * | Stack for Regs | The trampoline assembly will pop these values
- * | | into registers for native call
+ * | Scratch Alloca | 5K scratch space
* #---------#---------#
* | | sp* |
* | Tramp. #---------#
* | Tramp. #---------#
* | | method |
* #-------------------# <--- SP on artQuickGenericJniTrampoline
+ *
+ * |
+ * v artQuickGenericJniTrampoline
+ *
+ * #-------------------#
+ * | caller method... |
+ * #-------------------#
+ * | Return |
+ * | Callee-Save Data |
+ * #-------------------#
+ * | SIRT |
+ * #-------------------#
+ * | Method* | <--- (1)
+ * #-------------------#
+ * | local ref cookie | // 4B
+ * | SIRT size | // 4B TODO: roll into call stack alignment?
+ * #-------------------#
+ * | JNI Call Stack |
+ * #-------------------# <--- SP on native call
+ * | |
+ * | Stack for Regs | The trampoline assembly will pop these values
+ * | | into registers for native call
+ * #-------------------#
+ * | Native code ptr |
+ * #-------------------#
+ * | Free scratch |
+ * #-------------------#
+ * | Ptr to (1) | <--- RSP
+ * #-------------------#
*/
/*
* Called to do a generic JNI down-call
// Store native ArtMethod* to bottom of stack.
movq %rdi, 0(%rsp)
movq %rsp, %rbp // save SP at callee-save frame
- CFI_DEF_CFA_REGISTER(rbp)
+ movq %rsp, %rbx
+ CFI_DEF_CFA_REGISTER(rbx)
//
// reserve a lot of space
//
movq %gs:THREAD_SELF_OFFSET, %rdi
movq %rbp, %rsi
call PLT_SYMBOL(artQuickGenericJniTrampoline) // (Thread*, sp)
- test %rax, %rax // Check for error, negative value.
+
+ // At the bottom of the alloca we now have the name pointer to the method=bottom of callee-save
+ // get the adjusted frame pointer
+ popq %rbp
+
+ // Check for error, negative value.
+ test %rax, %rax
js .Lentry_error
- // release part of the alloca
+
+ // release part of the alloca, get the code pointer
addq %rax, %rsp
- // get the code pointer
popq %rax
+
// pop from the register-passing alloca region
// what's the right layout?
popq %rdi
call PLT_SYMBOL(artQuickGenericJniEndTrampoline)
// Tear down the alloca.
- movq %rbp, %rsp
+ movq %rbx, %rsp
CFI_DEF_CFA_REGISTER(rsp)
// Pending exceptions possible.
movq %rax, %xmm0
ret
.Lentry_error:
- movq %rbp, %rsp
+ movq %rbx, %rsp
+ CFI_DEF_CFA_REGISTER(rsp)
.Lexception_in_native:
- CFI_REL_OFFSET(rsp,176)
// TODO: the SIRT contains the this pointer which is used by the debugger for exception
// delivery.
- RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+ movq %xmm0, 16(%rsp) // doesn't make sense!!!
+ movq 24(%rsp), %xmm1 // neither does this!!!
+ movq 32(%rsp), %xmm2
+ movq 40(%rsp), %xmm3
+ movq 48(%rsp), %xmm4
+ movq 56(%rsp), %xmm5
+ movq 64(%rsp), %xmm6
+ movq 72(%rsp), %xmm7
+ // was 80 bytes
+ addq LITERAL(80), %rsp
+ CFI_ADJUST_CFA_OFFSET(-80)
+ // Save callee and GPR args, mixed together to agree with core spills bitmap.
+ POP rcx // Arg.
+ POP rdx // Arg.
+ POP rbx // Callee save.
+ POP rbp // Callee save.
+ POP rsi // Arg.
+ POP r8 // Arg.
+ POP r9 // Arg.
+ POP r12 // Callee save.
+ POP r13 // Callee save.
+ POP r14 // Callee save.
+ POP r15 // Callee save.
+
DELIVER_PENDING_EXCEPTION
END_FUNCTION art_quick_generic_jni_trampoline
*
* void PushStack(uintptr_t): Push a value to the stack.
*
- * uintptr_t PushSirt(mirror::Object* ref): Add a reference to the Sirt. Is guaranteed != nullptr.
+ * uintptr_t PushSirt(mirror::Object* ref): Add a reference to the Sirt. This _will_ have nullptr,
+ * as this might be important for null initialization.
* Must return the jobject, that is, the reference to the
- * entry in the Sirt.
+ * entry in the Sirt (nullptr if necessary).
*
*/
template <class T> class BuildGenericJniFrameStateMachine {
}
void AdvanceSirt(mirror::Object* ptr) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- uintptr_t sirtRef;
- if (ptr != nullptr) {
- sirtRef = PushSirt(ptr);
- } else {
- sirtRef = reinterpret_cast<uintptr_t>(nullptr);
- }
+ uintptr_t sirtRef = PushSirt(ptr);
if (HaveSirtGpr()) {
gpr_index_--;
PushGpr(sirtRef);
public:
ComputeGenericJniFrameSize() : num_sirt_references_(0), num_stack_entries_(0) {}
- // (negative) offset from SP to top of Sirt.
- uint32_t GetSirtOffset() {
- return 8;
- }
-
- uint32_t GetFirstSirtEntryOffset() {
- return GetSirtOffset() + sizeof(StackReference<mirror::Object>);
- }
-
- uint32_t GetNumSirtReferences() {
- return num_sirt_references_;
- }
-
uint32_t GetStackSize() {
return num_stack_entries_ * sizeof(uintptr_t);
}
- void ComputeLayout(bool is_static, const char* shorty, uint32_t shorty_len, void* sp,
- StackReference<mirror::Object>** start_sirt, StackIndirectReferenceTable** table,
- uint32_t* sirt_entries, uintptr_t** start_stack, uintptr_t** start_gpr,
- uint32_t** start_fpr, void** code_return, size_t* overall_size)
+ // WARNING: After this, *sp won't be pointing to the method anymore!
+ void ComputeLayout(mirror::ArtMethod*** m, bool is_static, const char* shorty, uint32_t shorty_len,
+ void* sp, StackIndirectReferenceTable** table, uint32_t* sirt_entries,
+ uintptr_t** start_stack, uintptr_t** start_gpr, uint32_t** start_fpr,
+ void** code_return, size_t* overall_size)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
ComputeAll(is_static, shorty, shorty_len);
+ mirror::ArtMethod* method = **m;
+
uint8_t* sp8 = reinterpret_cast<uint8_t*>(sp);
- *start_sirt = reinterpret_cast<StackReference<mirror::Object>*>(sp8-GetFirstSirtEntryOffset());
-
- // Add padding entries if necessary for alignment.
- if (sizeof(uintptr_t) < sizeof(uint64_t)) {
- uint32_t size = sizeof(uintptr_t) * num_sirt_references_;
- uint32_t rem = size % 8;
- if (rem != 0) {
- DCHECK_EQ(rem, 4U);
- num_sirt_references_++;
- }
- }
+
+ // First, fix up the layout of the callee-save frame.
+ // We have to squeeze in the Sirt, and relocate the method pointer.
+
+ // "Free" the slot for the method.
+ sp8 += kPointerSize;
+
+ // Add the Sirt.
*sirt_entries = num_sirt_references_;
- size_t sirt_size = StackIndirectReferenceTable::SizeOf(num_sirt_references_);
- sp8 -= GetSirtOffset() + sirt_size;
+ size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSize(num_sirt_references_);
+ sp8 -= sirt_size;
*table = reinterpret_cast<StackIndirectReferenceTable*>(sp8);
+ (*table)->SetNumberOfReferences(num_sirt_references_);
+
+ // Add a slot for the method pointer, and fill it. Fix the pointer-pointer given to us.
+ sp8 -= kPointerSize;
+ uint8_t* method_pointer = sp8;
+ *(reinterpret_cast<mirror::ArtMethod**>(method_pointer)) = method;
+ *m = reinterpret_cast<mirror::ArtMethod**>(method_pointer);
+ // Reference cookie and padding
+ sp8 -= 8;
+ // Store Sirt size
+ *reinterpret_cast<uint32_t*>(sp8) = static_cast<uint32_t>(sirt_size & 0xFFFFFFFF);
+
+ // Next comes the native call stack.
sp8 -= GetStackSize();
- // Now align the call stack under the Sirt. This aligns by 16.
+ // Now align the call stack below. This aligns by 16, as AArch64 seems to require.
uintptr_t mask = ~0x0F;
sp8 = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(sp8) & mask);
*start_stack = reinterpret_cast<uintptr_t*>(sp8);
*start_gpr = reinterpret_cast<uintptr_t*>(sp8);
// reserve space for the code pointer
- sp8 -= sizeof(void*);
+ sp8 -= kPointerSize;
*code_return = reinterpret_cast<void*>(sp8);
*overall_size = reinterpret_cast<uint8_t*>(sp) - sp8;
+
+ // The new SP is stored at the end of the alloca, so it can be immediately popped
+ sp8 = reinterpret_cast<uint8_t*>(sp) - 5 * KB;
+ *(reinterpret_cast<uint8_t**>(sp8)) = method_pointer;
}
void ComputeSirtOffset() { } // nothing to do, static right now
// of transitioning into native code.
class BuildGenericJniFrameVisitor FINAL : public QuickArgumentVisitor {
public:
- BuildGenericJniFrameVisitor(mirror::ArtMethod** sp, bool is_static, const char* shorty,
+ BuildGenericJniFrameVisitor(mirror::ArtMethod*** sp, bool is_static, const char* shorty,
uint32_t shorty_len, Thread* self) :
- QuickArgumentVisitor(sp, is_static, shorty, shorty_len), sm_(this) {
+ QuickArgumentVisitor(*sp, is_static, shorty, shorty_len), sm_(this) {
ComputeGenericJniFrameSize fsc;
- fsc.ComputeLayout(is_static, shorty, shorty_len, sp, &cur_sirt_entry_, &sirt_,
- &sirt_expected_refs_, &cur_stack_arg_, &cur_gpr_reg_, &cur_fpr_reg_,
- &code_return_, &alloca_used_size_);
+ fsc.ComputeLayout(sp, is_static, shorty, shorty_len, *sp, &sirt_, &sirt_expected_refs_,
+ &cur_stack_arg_, &cur_gpr_reg_, &cur_fpr_reg_, &code_return_,
+ &alloca_used_size_);
sirt_number_of_references_ = 0;
- top_of_sirt_ = cur_sirt_entry_;
+ cur_sirt_entry_ = reinterpret_cast<StackReference<mirror::Object>*>(GetFirstSirtEntry());
// jni environment is always first argument
sm_.AdvancePointer(self->GetJniEnv());
if (is_static) {
- sm_.AdvanceSirt((*sp)->GetDeclaringClass());
+ sm_.AdvanceSirt((**sp)->GetDeclaringClass());
}
}
// Initialize padding entries.
while (sirt_number_of_references_ < sirt_expected_refs_) {
*cur_sirt_entry_ = StackReference<mirror::Object>();
- cur_sirt_entry_--;
+ cur_sirt_entry_++;
sirt_number_of_references_++;
}
sirt_->SetNumberOfReferences(sirt_expected_refs_);
self->PushSirt(sirt_);
}
- jobject GetFirstSirtEntry() {
- return reinterpret_cast<jobject>(top_of_sirt_);
+ jobject GetFirstSirtEntry() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return reinterpret_cast<jobject>(sirt_->GetStackReference(0));
}
void PushGpr(uintptr_t val) {
}
uintptr_t PushSirt(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- *cur_sirt_entry_ = StackReference<mirror::Object>::FromMirrorPtr(ref);
- uintptr_t tmp = reinterpret_cast<uintptr_t>(cur_sirt_entry_);
- cur_sirt_entry_--;
+ uintptr_t tmp;
+ if (ref == nullptr) {
+ *cur_sirt_entry_ = StackReference<mirror::Object>();
+ tmp = reinterpret_cast<uintptr_t>(nullptr);
+ } else {
+ *cur_sirt_entry_ = StackReference<mirror::Object>::FromMirrorPtr(ref);
+ tmp = reinterpret_cast<uintptr_t>(cur_sirt_entry_);
+ }
+ cur_sirt_entry_++;
sirt_number_of_references_++;
return tmp;
}
uintptr_t* cur_gpr_reg_;
uint32_t* cur_fpr_reg_;
uintptr_t* cur_stack_arg_;
- StackReference<mirror::Object>* top_of_sirt_;
+ // StackReference<mirror::Object>* top_of_sirt_;
void* code_return_;
size_t alloca_used_size_;
* Create a Sirt and call stack and fill a mini stack with values to be pushed to registers.
* The final element on the stack is a pointer to the native code.
*
+ * On entry, the stack has a standard callee-save frame above sp, and an alloca below it.
+ * We need to fix this, as the Sirt needs to go into the callee-save frame.
+ *
* The return of this function denotes:
* 1) How many bytes of the alloca can be released, if the value is non-negative.
* 2) An error, if the value is negative.
*/
extern "C" ssize_t artQuickGenericJniTrampoline(Thread* self, mirror::ArtMethod** sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- uint32_t* sp32 = reinterpret_cast<uint32_t*>(sp);
mirror::ArtMethod* called = *sp;
- DCHECK(called->IsNative());
+ DCHECK(called->IsNative()) << PrettyMethod(called, true);
// run the visitor
MethodHelper mh(called);
- BuildGenericJniFrameVisitor visitor(sp, called->IsStatic(), mh.GetShorty(), mh.GetShortyLength(),
+ BuildGenericJniFrameVisitor visitor(&sp, called->IsStatic(), mh.GetShorty(), mh.GetShortyLength(),
self);
visitor.VisitArguments();
visitor.FinalizeSirt(self);
if (self->IsExceptionPending()) {
self->PopSirt();
// A negative value denotes an error.
+ // TODO: Do we still need to fix the stack pointer? I think so. Then it's necessary to push
+ // that value!
return -1;
}
} else {
cookie = JniMethodStart(self);
}
+ uint32_t* sp32 = reinterpret_cast<uint32_t*>(sp);
*(sp32 - 1) = cookie;
// retrieve native code
size_t window_size = visitor.GetAllocaUsedSize();
*code_pointer = reinterpret_cast<uintptr_t>(nativeCode);
- // 5K reserved, window_size used.
- return (5 * KB) - window_size;
+ // 5K reserved, window_size + frame pointer used.
+ return (5 * KB) - window_size - kPointerSize;
}
/*
if (return_shorty_char == 'L') {
// the only special ending call
if (called->IsSynchronized()) {
- ComputeGenericJniFrameSize fsc;
- fsc.ComputeSirtOffset();
- uint32_t offset = fsc.GetFirstSirtEntryOffset();
- jobject tmp = reinterpret_cast<jobject>(reinterpret_cast<uint8_t*>(sp) - offset);
+ StackIndirectReferenceTable* table =
+ reinterpret_cast<StackIndirectReferenceTable*>(
+ reinterpret_cast<uint8_t*>(sp) + kPointerSize);
+ jobject tmp = reinterpret_cast<jobject>(table->GetStackReference(0));
return reinterpret_cast<uint64_t>(JniMethodEndWithReferenceSynchronized(result.l, cookie, tmp,
self));
}
} else {
if (called->IsSynchronized()) {
- ComputeGenericJniFrameSize fsc;
- fsc.ComputeSirtOffset();
- uint32_t offset = fsc.GetFirstSirtEntryOffset();
- jobject tmp = reinterpret_cast<jobject>(reinterpret_cast<uint8_t*>(sp) - offset);
+ StackIndirectReferenceTable* table =
+ reinterpret_cast<StackIndirectReferenceTable*>(
+ reinterpret_cast<uint8_t*>(sp) + kPointerSize);
+ jobject tmp = reinterpret_cast<jobject>(table->GetStackReference(0));
JniMethodEndSynchronized(cookie, tmp, self);
} else {