summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gampe <agampe@google.com>2014-06-16 18:39:09 -0700
committerAndreas Gampe <agampe@google.com>2014-07-01 11:06:24 -0700
commitc200a4abeca91e19969f5b35543f17f812ba32b9 (patch)
tree5912a08310e7ddb4b3907c0dc687006669c0cedd
parent73904fed884bf216b51acdc64402c427cc34725b (diff)
downloadart-c200a4abeca91e19969f5b35543f17f812ba32b9.zip
art-c200a4abeca91e19969f5b35543f17f812ba32b9.tar.gz
art-c200a4abeca91e19969f5b35543f17f812ba32b9.tar.bz2
ART: Rework Generic JNI, add ARM version
Refactors and optimizes Generic JNI. This version uses TwoWordReturn to avoid writing to / loading from the bottom of the alloca. Change-Id: I3287007c976f79c9fd32d3b3a43f2d1371bf4cd3
-rw-r--r--compiler/jni/jni_compiler_test.cc127
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S89
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S47
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S38
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S43
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc582
-rw-r--r--runtime/utils.h13
7 files changed, 560 insertions, 379 deletions
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 8f4eddb..25b489b 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -1284,13 +1284,6 @@ TEST_F(JniCompilerTest, WithoutImplementation) {
EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE);
}
-template <typename U, typename V> V convert(U in) {
- DCHECK_LE(sizeof(U), sizeof(V));
- union { U u; V v; } tmp;
- tmp.u = in;
- return tmp.v;
-}
-
void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv* env, jclass klass, jint i1, jint i2, jint i3,
jint i4, jint i5, jint i6, jint i7, jint i8, jint i9,
jint i10, jfloat f1, jfloat f2, jfloat f3, jfloat f4,
@@ -1307,25 +1300,25 @@ void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv* env, jclass klass, jint i1,
EXPECT_EQ(i9, 9);
EXPECT_EQ(i10, 10);
- jint i11 = convert<jfloat, jint>(f1);
+ jint i11 = bit_cast<jfloat, jint>(f1);
EXPECT_EQ(i11, 11);
- jint i12 = convert<jfloat, jint>(f2);
+ jint i12 = bit_cast<jfloat, jint>(f2);
EXPECT_EQ(i12, 12);
- jint i13 = convert<jfloat, jint>(f3);
+ jint i13 = bit_cast<jfloat, jint>(f3);
EXPECT_EQ(i13, 13);
- jint i14 = convert<jfloat, jint>(f4);
+ jint i14 = bit_cast<jfloat, jint>(f4);
EXPECT_EQ(i14, 14);
- jint i15 = convert<jfloat, jint>(f5);
+ jint i15 = bit_cast<jfloat, jint>(f5);
EXPECT_EQ(i15, 15);
- jint i16 = convert<jfloat, jint>(f6);
+ jint i16 = bit_cast<jfloat, jint>(f6);
EXPECT_EQ(i16, 16);
- jint i17 = convert<jfloat, jint>(f7);
+ jint i17 = bit_cast<jfloat, jint>(f7);
EXPECT_EQ(i17, 17);
- jint i18 = convert<jfloat, jint>(f8);
+ jint i18 = bit_cast<jfloat, jint>(f8);
EXPECT_EQ(i18, 18);
- jint i19 = convert<jfloat, jint>(f9);
+ jint i19 = bit_cast<jfloat, jint>(f9);
EXPECT_EQ(i19, 19);
- jint i20 = convert<jfloat, jint>(f10);
+ jint i20 = bit_cast<jfloat, jint>(f10);
EXPECT_EQ(i20, 20);
}
@@ -1345,16 +1338,16 @@ TEST_F(JniCompilerTest, StackArgsIntsFirst) {
jint i9 = 9;
jint i10 = 10;
- jfloat f1 = convert<jint, jfloat>(11);
- jfloat f2 = convert<jint, jfloat>(12);
- jfloat f3 = convert<jint, jfloat>(13);
- jfloat f4 = convert<jint, jfloat>(14);
- jfloat f5 = convert<jint, jfloat>(15);
- jfloat f6 = convert<jint, jfloat>(16);
- jfloat f7 = convert<jint, jfloat>(17);
- jfloat f8 = convert<jint, jfloat>(18);
- jfloat f9 = convert<jint, jfloat>(19);
- jfloat f10 = convert<jint, jfloat>(20);
+ jfloat f1 = bit_cast<jint, jfloat>(11);
+ jfloat f2 = bit_cast<jint, jfloat>(12);
+ jfloat f3 = bit_cast<jint, jfloat>(13);
+ jfloat f4 = bit_cast<jint, jfloat>(14);
+ jfloat f5 = bit_cast<jint, jfloat>(15);
+ jfloat f6 = bit_cast<jint, jfloat>(16);
+ jfloat f7 = bit_cast<jint, jfloat>(17);
+ jfloat f8 = bit_cast<jint, jfloat>(18);
+ jfloat f9 = bit_cast<jint, jfloat>(19);
+ jfloat f10 = bit_cast<jint, jfloat>(20);
env_->CallStaticVoidMethod(jklass_, jmethod_, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, f1, f2,
f3, f4, f5, f6, f7, f8, f9, f10);
@@ -1376,25 +1369,25 @@ void Java_MyClassNatives_stackArgsFloatsFirst(JNIEnv* env, jclass klass, jfloat
EXPECT_EQ(i9, 9);
EXPECT_EQ(i10, 10);
- jint i11 = convert<jfloat, jint>(f1);
+ jint i11 = bit_cast<jfloat, jint>(f1);
EXPECT_EQ(i11, 11);
- jint i12 = convert<jfloat, jint>(f2);
+ jint i12 = bit_cast<jfloat, jint>(f2);
EXPECT_EQ(i12, 12);
- jint i13 = convert<jfloat, jint>(f3);
+ jint i13 = bit_cast<jfloat, jint>(f3);
EXPECT_EQ(i13, 13);
- jint i14 = convert<jfloat, jint>(f4);
+ jint i14 = bit_cast<jfloat, jint>(f4);
EXPECT_EQ(i14, 14);
- jint i15 = convert<jfloat, jint>(f5);
+ jint i15 = bit_cast<jfloat, jint>(f5);
EXPECT_EQ(i15, 15);
- jint i16 = convert<jfloat, jint>(f6);
+ jint i16 = bit_cast<jfloat, jint>(f6);
EXPECT_EQ(i16, 16);
- jint i17 = convert<jfloat, jint>(f7);
+ jint i17 = bit_cast<jfloat, jint>(f7);
EXPECT_EQ(i17, 17);
- jint i18 = convert<jfloat, jint>(f8);
+ jint i18 = bit_cast<jfloat, jint>(f8);
EXPECT_EQ(i18, 18);
- jint i19 = convert<jfloat, jint>(f9);
+ jint i19 = bit_cast<jfloat, jint>(f9);
EXPECT_EQ(i19, 19);
- jint i20 = convert<jfloat, jint>(f10);
+ jint i20 = bit_cast<jfloat, jint>(f10);
EXPECT_EQ(i20, 20);
}
@@ -1414,16 +1407,16 @@ TEST_F(JniCompilerTest, StackArgsFloatsFirst) {
jint i9 = 9;
jint i10 = 10;
- jfloat f1 = convert<jint, jfloat>(11);
- jfloat f2 = convert<jint, jfloat>(12);
- jfloat f3 = convert<jint, jfloat>(13);
- jfloat f4 = convert<jint, jfloat>(14);
- jfloat f5 = convert<jint, jfloat>(15);
- jfloat f6 = convert<jint, jfloat>(16);
- jfloat f7 = convert<jint, jfloat>(17);
- jfloat f8 = convert<jint, jfloat>(18);
- jfloat f9 = convert<jint, jfloat>(19);
- jfloat f10 = convert<jint, jfloat>(20);
+ jfloat f1 = bit_cast<jint, jfloat>(11);
+ jfloat f2 = bit_cast<jint, jfloat>(12);
+ jfloat f3 = bit_cast<jint, jfloat>(13);
+ jfloat f4 = bit_cast<jint, jfloat>(14);
+ jfloat f5 = bit_cast<jint, jfloat>(15);
+ jfloat f6 = bit_cast<jint, jfloat>(16);
+ jfloat f7 = bit_cast<jint, jfloat>(17);
+ jfloat f8 = bit_cast<jint, jfloat>(18);
+ jfloat f9 = bit_cast<jint, jfloat>(19);
+ jfloat f10 = bit_cast<jint, jfloat>(20);
env_->CallStaticVoidMethod(jklass_, jmethod_, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, i1, i2, i3,
i4, i5, i6, i7, i8, i9, i10);
@@ -1444,25 +1437,25 @@ void Java_MyClassNatives_stackArgsMixed(JNIEnv* env, jclass klass, jint i1, jflo
EXPECT_EQ(i9, 9);
EXPECT_EQ(i10, 10);
- jint i11 = convert<jfloat, jint>(f1);
+ jint i11 = bit_cast<jfloat, jint>(f1);
EXPECT_EQ(i11, 11);
- jint i12 = convert<jfloat, jint>(f2);
+ jint i12 = bit_cast<jfloat, jint>(f2);
EXPECT_EQ(i12, 12);
- jint i13 = convert<jfloat, jint>(f3);
+ jint i13 = bit_cast<jfloat, jint>(f3);
EXPECT_EQ(i13, 13);
- jint i14 = convert<jfloat, jint>(f4);
+ jint i14 = bit_cast<jfloat, jint>(f4);
EXPECT_EQ(i14, 14);
- jint i15 = convert<jfloat, jint>(f5);
+ jint i15 = bit_cast<jfloat, jint>(f5);
EXPECT_EQ(i15, 15);
- jint i16 = convert<jfloat, jint>(f6);
+ jint i16 = bit_cast<jfloat, jint>(f6);
EXPECT_EQ(i16, 16);
- jint i17 = convert<jfloat, jint>(f7);
+ jint i17 = bit_cast<jfloat, jint>(f7);
EXPECT_EQ(i17, 17);
- jint i18 = convert<jfloat, jint>(f8);
+ jint i18 = bit_cast<jfloat, jint>(f8);
EXPECT_EQ(i18, 18);
- jint i19 = convert<jfloat, jint>(f9);
+ jint i19 = bit_cast<jfloat, jint>(f9);
EXPECT_EQ(i19, 19);
- jint i20 = convert<jfloat, jint>(f10);
+ jint i20 = bit_cast<jfloat, jint>(f10);
EXPECT_EQ(i20, 20);
}
@@ -1482,16 +1475,16 @@ TEST_F(JniCompilerTest, StackArgsMixed) {
jint i9 = 9;
jint i10 = 10;
- jfloat f1 = convert<jint, jfloat>(11);
- jfloat f2 = convert<jint, jfloat>(12);
- jfloat f3 = convert<jint, jfloat>(13);
- jfloat f4 = convert<jint, jfloat>(14);
- jfloat f5 = convert<jint, jfloat>(15);
- jfloat f6 = convert<jint, jfloat>(16);
- jfloat f7 = convert<jint, jfloat>(17);
- jfloat f8 = convert<jint, jfloat>(18);
- jfloat f9 = convert<jint, jfloat>(19);
- jfloat f10 = convert<jint, jfloat>(20);
+ jfloat f1 = bit_cast<jint, jfloat>(11);
+ jfloat f2 = bit_cast<jint, jfloat>(12);
+ jfloat f3 = bit_cast<jint, jfloat>(13);
+ jfloat f4 = bit_cast<jint, jfloat>(14);
+ jfloat f5 = bit_cast<jint, jfloat>(15);
+ jfloat f6 = bit_cast<jint, jfloat>(16);
+ jfloat f7 = bit_cast<jint, jfloat>(17);
+ jfloat f8 = bit_cast<jint, jfloat>(18);
+ jfloat f9 = bit_cast<jint, jfloat>(19);
+ jfloat f10 = bit_cast<jint, jfloat>(20);
env_->CallStaticVoidMethod(jklass_, jmethod_, i1, f1, i2, f2, i3, f3, i4, f4, i5, f5, i6, f6, i7,
f7, i8, f8, i9, f9, i10, f10);
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 83a683d..4939610 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -127,7 +127,7 @@
// Ugly compile-time check, but we only have the preprocessor.
#if (FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE != 40 + 8)
-#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(ARM64) size not as expected."
+#error "REFS_AND_ARGS_CALLEE_SAVE_FRAME(ARM) size not as expected."
#endif
.endm
@@ -1007,7 +1007,92 @@ ENTRY art_quick_resolution_trampoline
DELIVER_PENDING_EXCEPTION
END art_quick_resolution_trampoline
-UNIMPLEMENTED art_quick_generic_jni_trampoline
+ /*
+ * Called to do a generic JNI down-call
+ */
+ENTRY art_quick_generic_jni_trampoline
+ SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
+ str r0, [sp, #0] // Store native ArtMethod* to bottom of stack.
+
+ // Save rSELF
+ mov r11, rSELF
+ // Save SP , so we can have static CFI info. r10 is saved in ref_and_args.
+ mov r10, sp
+ .cfi_def_cfa_register r10
+
+ sub sp, sp, #5120
+
+ // prepare for artQuickGenericJniTrampoline call
+ // (Thread*, SP)
+ // r0 r1 <= C calling convention
+ // rSELF r10 <= where they are
+
+ mov r0, rSELF // Thread*
+ mov r1, r10
+ blx artQuickGenericJniTrampoline // (Thread*, sp)
+
+ // The C call will have registered the complete save-frame on success.
+ // The result of the call is:
+ // r0: pointer to native code, 0 on error.
+ // r1: pointer to the bottom of the used area of the alloca, can restore stack till there.
+
+ // Check for error = 0.
+ cbz r0, .Lentry_error
+
+ // Release part of the alloca.
+ mov sp, r1
+
+ // Save the code pointer
+ mov r12, r0
+
+ // Load parameters from frame into registers.
+ pop {r0-r3}
+
+ // Softfloat.
+ // TODO: Change to hardfloat when supported.
+
+ blx r12 // native call.
+
+ // result sign extension is handled in C code
+ // prepare for artQuickGenericJniEndTrampoline call
+ // (Thread*, result, result_f)
+ // r0 r1,r2 r3,stack <= C calling convention
+ // r11 r0,r1 r0,r1 <= where they are
+ sub sp, sp, #12 // Stack alignment.
+
+ push {r1}
+ mov r3, r0
+ mov r2, r1
+ mov r1, r0
+ mov r0, r11
+
+ blx artQuickGenericJniEndTrampoline
+
+ // Tear down the alloca.
+ mov sp, r10
+ .cfi_def_cfa_register sp
+
+ // Restore self pointer.
+ mov r9, r11
+
+ // Pending exceptions possible.
+ ldr r2, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_
+ cbnz r2, .Lexception_in_native
+
+ // Tear down the callee-save frame.
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+
+ bx lr // ret
+
+.Lentry_error:
+ mov sp, r10
+ .cfi_def_cfa_register sp
+ mov r9, r11
+.Lexception_in_native:
+ RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
+ DELIVER_PENDING_EXCEPTION
+
+END art_quick_generic_jni_trampoline
.extern artQuickToInterpreterBridge
ENTRY art_quick_to_interpreter_bridge
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index e088751..7907b6e 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1485,33 +1485,34 @@ ENTRY art_quick_generic_jni_trampoline
mov x1, xFP
bl artQuickGenericJniTrampoline // (Thread*, sp)
- // Get the updated pointer. This is the bottom of the frame _with_ handle scope.
- ldr xFP, [sp]
- add x9, sp, #8
+ // The C call will have registered the complete save-frame on success.
+ // The result of the call is:
+ // x0: pointer to native code, 0 on error.
+ // x1: pointer to the bottom of the used area of the alloca, can restore stack till there.
- cmp x0, #0
- b.mi .Lentry_error // Check for error, negative value.
+ // Check for error = 0.
+ cbz x0, .Lentry_error
- // release part of the alloca.
- add x9, x9, x0
+ // Release part of the alloca.
+ mov sp, x1
- // Get the code pointer
- ldr xIP0, [x9, #0]
+ // Save the code pointer
+ mov xIP0, x0
// Load parameters from frame into registers.
// TODO Check with artQuickGenericJniTrampoline.
// Also, check again APPCS64 - the stack arguments are interleaved.
- ldp x0, x1, [x9, #8]
- ldp x2, x3, [x9, #24]
- ldp x4, x5, [x9, #40]
- ldp x6, x7, [x9, #56]
+ ldp x0, x1, [sp]
+ ldp x2, x3, [sp, #16]
+ ldp x4, x5, [sp, #32]
+ ldp x6, x7, [sp, #48]
- ldp d0, d1, [x9, #72]
- ldp d2, d3, [x9, #88]
- ldp d4, d5, [x9, #104]
- ldp d6, d7, [x9, #120]
+ ldp d0, d1, [sp, #64]
+ ldp d2, d3, [sp, #80]
+ ldp d4, d5, [sp, #96]
+ ldp d6, d7, [sp, #112]
- add sp, x9, #136
+ add sp, sp, #128
blr xIP0 // native call.
@@ -1520,13 +1521,11 @@ ENTRY art_quick_generic_jni_trampoline
// result sign extension is handled in C code
// prepare for artQuickGenericJniEndTrampoline call
- // (Thread*, SP, result, result_f)
- // x0 x1 x2 x3 <= C calling convention
- mov x5, x0 // Save return value
+ // (Thread*, result, result_f)
+ // x0 x1 x2 <= C calling convention
+ mov x1, x0 // Result (from saved)
mov x0, xSELF // Thread register
- mov x1, xFP // Stack pointer
- mov x2, x5 // Result (from saved)
- fmov x3, d0 // d0 will contain floating point result, but needs to go into x3
+ fmov x2, d0 // d0 will contain floating point result, but needs to go into x2
bl artQuickGenericJniEndTrampoline
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 0326f9e..24b9e46 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1127,8 +1127,7 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline
// This also stores the native ArtMethod reference at the bottom of the stack.
movl %esp, %ebp // save SP at callee-save frame
- movl %esp, %edi
- CFI_DEF_CFA_REGISTER(edi)
+ CFI_DEF_CFA_REGISTER(ebp)
subl LITERAL(5120), %esp
// prepare for artQuickGenericJniTrampoline call
// (Thread*, SP)
@@ -1141,46 +1140,39 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
SETUP_GOT_NOSAVE // Clobbers ebx.
call PLT_SYMBOL(artQuickGenericJniTrampoline) // (Thread*, sp)
- // Drop call stack.
- addl LITERAL(16), %esp
- // At the bottom of the alloca we now have the name pointer to the method=bottom of callee-save
- // get the adjusted frame pointer
- popl %ebp
+ // The C call will have registered the complete save-frame on success.
+ // The result of the call is:
+ // eax: pointer to native code, 0 on error.
+ // edx: pointer to the bottom of the used area of the alloca, can restore stack till there.
- // Check for error, negative value.
+ // Check for error = 0.
test %eax, %eax
- js .Lentry_error
+ jz .Lentry_error
- // release part of the alloca, get the code pointer
- addl %eax, %esp
- popl %eax
+ // Release part of the alloca.
+ movl %edx, %esp
// On x86 there are no registers passed, so nothing to pop here.
// Native call.
call *%eax
- // Pop native stack, but keep the space that was reserved cookie.
- movl %ebp, %esp
- subl LITERAL(16), %esp // Alignment.
-
// result sign extension is handled in C code
// prepare for artQuickGenericJniEndTrampoline call
- // (Thread*, SP, result, result_f)
- // (esp) 4(esp) 8(esp) 16(esp) <= C calling convention
- // fs:... ebp eax:edx xmm0 <= where they are
+ // (Thread*, result, result_f)
+ // (esp) 4(esp) 12(esp) <= C calling convention
+ // fs:... eax:edx xmm0 <= where they are
- subl LITERAL(8), %esp // Pass float result.
+ subl LITERAL(20), %esp // Padding & pass float result.
movsd %xmm0, (%esp)
pushl %edx // Pass int result.
pushl %eax
- pushl %ebp // Pass SP (to ArtMethod).
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
call PLT_SYMBOL(artQuickGenericJniEndTrampoline)
// Tear down the alloca.
- movl %edi, %esp
+ movl %ebp, %esp
CFI_DEF_CFA_REGISTER(esp)
// Pending exceptions possible.
@@ -1204,7 +1196,7 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline
punpckldq %xmm1, %xmm0
ret
.Lentry_error:
- movl %edi, %esp
+ movl %ebp, %esp
CFI_DEF_CFA_REGISTER(esp)
.Lexception_in_native:
RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 668fb88..8fa947c 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1167,11 +1167,9 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline
movq %xmm5, 56(%rsp)
movq %xmm6, 64(%rsp)
movq %xmm7, 72(%rsp)
- // Store native ArtMethod* to bottom of stack.
- movq %rdi, 0(%rsp)
- movq %rsp, %rbp // save SP at callee-save frame
- movq %rsp, %rbx
- CFI_DEF_CFA_REGISTER(rbx)
+ movq %rdi, 0(%rsp) // Store native ArtMethod* to bottom of stack.
+ movq %rsp, %rbp // save SP at (old) callee-save frame
+ CFI_DEF_CFA_REGISTER(rbp)
//
// reserve a lot of space
//
@@ -1198,17 +1196,17 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline
movq %rbp, %rsi
call PLT_SYMBOL(artQuickGenericJniTrampoline) // (Thread*, sp)
- // 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
+ // The C call will have registered the complete save-frame on success.
+ // The result of the call is:
+ // %rax: pointer to native code, 0 on error.
+ // %rdx: pointer to the bottom of the used area of the alloca, can restore stack till there.
- // Check for error, negative value.
+ // Check for error = 0.
test %rax, %rax
- js .Lentry_error
+ jz .Lentry_error
- // release part of the alloca, get the code pointer
- addq %rax, %rsp
- popq %rax
+ // Release part of the alloca.
+ movq %rdx, %rsp
// pop from the register-passing alloca region
// what's the right layout?
@@ -1228,21 +1226,22 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline
movq 48(%rsp), %xmm6
movq 56(%rsp), %xmm7
addq LITERAL(64), %rsp // floating-point done
+
// native call
- call *%rax // Stack should be aligned 16B without the return addr?
+ call *%rax
+
// result sign extension is handled in C code
// prepare for artQuickGenericJniEndTrampoline call
- // (Thread*, SP, result, result_f)
- // rdi rsi rdx rcx <= C calling convention
- // gs:... rbp rax xmm0 <= where they are
+ // (Thread*, result, result_f)
+ // rdi rsi rdx <= C calling convention
+ // gs:... rax xmm0 <= where they are
movq %gs:THREAD_SELF_OFFSET, %rdi
- movq %rbp, %rsi
- movq %rax, %rdx
- movq %xmm0, %rcx
+ movq %rax, %rsi
+ movq %xmm0, %rdx
call PLT_SYMBOL(artQuickGenericJniEndTrampoline)
// Tear down the alloca.
- movq %rbx, %rsp
+ movq %rbp, %rsp
CFI_DEF_CFA_REGISTER(rsp)
// Pending exceptions possible.
@@ -1280,7 +1279,7 @@ DEFINE_FUNCTION art_quick_generic_jni_trampoline
movq %rax, %xmm0
ret
.Lentry_error:
- movq %rbx, %rsp
+ movq %rbp, %rsp
CFI_DEF_CFA_REGISTER(rsp)
.Lexception_in_native:
// TODO: the handle scope contains the this pointer which is used by the debugger for exception
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 7a144b6..6fb9624 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -185,8 +185,8 @@ class QuickArgumentVisitor {
case 3: return (5 * GetBytesPerGprSpillLocation(kRuntimeISA));
case 4: return (6 * GetBytesPerGprSpillLocation(kRuntimeISA));
default:
- LOG(FATAL) << "Unexpected GPR index: " << gpr_index;
- return 0;
+ LOG(FATAL) << "Unexpected GPR index: " << gpr_index;
+ return 0;
}
}
#else
@@ -209,16 +209,15 @@ class QuickArgumentVisitor {
return *reinterpret_cast<uintptr_t*>(lr);
}
- QuickArgumentVisitor(StackReference<mirror::ArtMethod>* sp, bool is_static,
- const char* shorty, uint32_t shorty_len)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
- is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len),
- gpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset),
- fpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset),
- stack_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize
- + StackArgumentStartFromShorty(is_static, shorty, shorty_len)),
- gpr_index_(0), fpr_index_(0), stack_index_(0), cur_type_(Primitive::kPrimVoid),
- is_split_long_or_double_(false) { }
+ QuickArgumentVisitor(StackReference<mirror::ArtMethod>* sp, bool is_static, const char* shorty,
+ uint32_t shorty_len) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) :
+ is_static_(is_static), shorty_(shorty), shorty_len_(shorty_len),
+ gpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset),
+ fpr_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset),
+ stack_args_(reinterpret_cast<byte*>(sp) + kQuickCalleeSaveFrame_RefAndArgs_FrameSize
+ + StackArgumentStartFromShorty(is_static, shorty, shorty_len)),
+ gpr_index_(0), fpr_index_(0), stack_index_(0), cur_type_(Primitive::kPrimVoid),
+ is_split_long_or_double_(false) {}
virtual ~QuickArgumentVisitor() {}
@@ -388,9 +387,12 @@ class QuickArgumentVisitor {
}
}
+ protected:
const bool is_static_;
const char* const shorty_;
const uint32_t shorty_len_;
+
+ private:
byte* const gpr_args_; // Address of GPR arguments in callee save frame.
byte* const fpr_args_; // Address of FPR arguments in callee save frame.
byte* const stack_args_; // Address of stack arguments in caller's frame.
@@ -409,7 +411,7 @@ class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor {
BuildQuickShadowFrameVisitor(StackReference<mirror::ArtMethod>* sp, bool is_static,
const char* shorty, uint32_t shorty_len, ShadowFrame* sf,
size_t first_arg_reg) :
- QuickArgumentVisitor(sp, is_static, shorty, shorty_len), sf_(sf), cur_reg_(first_arg_reg) {}
+ QuickArgumentVisitor(sp, is_static, shorty, shorty_len), sf_(sf), cur_reg_(first_arg_reg) {}
void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE;
@@ -420,7 +422,7 @@ class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor {
DISALLOW_COPY_AND_ASSIGN(BuildQuickShadowFrameVisitor);
};
-void BuildQuickShadowFrameVisitor::Visit() {
+void BuildQuickShadowFrameVisitor::Visit() {
Primitive::Type type = GetParamPrimitiveType();
switch (type) {
case Primitive::kPrimLong: // Fall-through.
@@ -465,13 +467,14 @@ extern "C" uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Threa
return 0;
} else {
DCHECK(!method->IsNative()) << PrettyMethod(method);
- const char* old_cause = self->StartAssertNoThreadSuspension("Building interpreter shadow frame");
+ const char* old_cause = self->StartAssertNoThreadSuspension(
+ "Building interpreter shadow frame");
const DexFile::CodeItem* code_item = method->GetCodeItem();
DCHECK(code_item != nullptr) << PrettyMethod(method);
uint16_t num_regs = code_item->registers_size_;
void* memory = alloca(ShadowFrame::ComputeSize(num_regs));
- ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, NULL, // No last shadow coming from quick.
- method, 0, memory));
+ // No last shadow coming from quick.
+ ShadowFrame* shadow_frame(ShadowFrame::Create(num_regs, nullptr, method, 0, memory));
size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_;
uint32_t shorty_len = 0;
const char* shorty = method->GetShorty(&shorty_len);
@@ -512,7 +515,7 @@ class BuildQuickArgumentVisitor FINAL : public QuickArgumentVisitor {
BuildQuickArgumentVisitor(StackReference<mirror::ArtMethod>* sp, bool is_static,
const char* shorty, uint32_t shorty_len,
ScopedObjectAccessUnchecked* soa, std::vector<jvalue>* args) :
- QuickArgumentVisitor(sp, is_static, shorty, shorty_len), soa_(soa), args_(args) {}
+ QuickArgumentVisitor(sp, is_static, shorty, shorty_len), soa_(soa), args_(args) {}
void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE;
@@ -584,7 +587,8 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method,
const char* old_cause =
self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments");
// Register the top of the managed stack, making stack crawlable.
- DCHECK_EQ(sp->AsMirrorPtr(), proxy_method) << PrettyMethod(proxy_method);
+ DCHECK_EQ(sp->AsMirrorPtr(), proxy_method)
+ << PrettyMethod(proxy_method);
self->SetTopOfStack(sp, 0);
DCHECK_EQ(proxy_method->GetFrameSizeInBytes(),
Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes())
@@ -600,7 +604,7 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method,
// Placing arguments into args vector and remove the receiver.
mirror::ArtMethod* non_proxy_method = proxy_method->GetInterfaceMethodIfProxy();
CHECK(!non_proxy_method->IsStatic()) << PrettyMethod(proxy_method) << " "
- << PrettyMethod(non_proxy_method);
+ << PrettyMethod(non_proxy_method);
std::vector<jvalue> args;
uint32_t shorty_len = 0;
const char* shorty = proxy_method->GetShorty(&shorty_len);
@@ -632,7 +636,7 @@ class RememberForGcArgumentVisitor FINAL : public QuickArgumentVisitor {
RememberForGcArgumentVisitor(StackReference<mirror::ArtMethod>* sp, bool is_static,
const char* shorty, uint32_t shorty_len,
ScopedObjectAccessUnchecked* soa) :
- QuickArgumentVisitor(sp, is_static, shorty, shorty_len), soa_(soa) {}
+ QuickArgumentVisitor(sp, is_static, shorty, shorty_len), soa_(soa) {}
void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE;
@@ -641,7 +645,8 @@ class RememberForGcArgumentVisitor FINAL : public QuickArgumentVisitor {
private:
ScopedObjectAccessUnchecked* const soa_;
// References which we must update when exiting in case the GC moved the objects.
- std::vector<std::pair<jobject, StackReference<mirror::Object>*>> references_;
+ std::vector<std::pair<jobject, StackReference<mirror::Object>*> > references_;
+
DISALLOW_COPY_AND_ASSIGN(RememberForGcArgumentVisitor);
};
@@ -663,7 +668,6 @@ void RememberForGcArgumentVisitor::FixupReferences() {
}
}
-
// Lazily resolve a method for quick. Called by stub code.
extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
mirror::Object* receiver,
@@ -740,7 +744,6 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
is_range = false;
}
dex_method_idx = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
-
} else {
invoke_type = kStatic;
dex_file = called->GetDexFile();
@@ -825,8 +828,6 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
return code;
}
-
-
/*
* This class uses a couple of observations to unite the different calling conventions through
* a few constants.
@@ -867,7 +868,7 @@ extern "C" const void* artQuickResolutionTrampoline(mirror::ArtMethod* called,
* entry in the HandleScope (nullptr if necessary).
*
*/
-template <class T> class BuildGenericJniFrameStateMachine {
+template<class T> class BuildNativeCallFrameStateMachine {
public:
#if defined(__arm__)
// TODO: These are all dummy values!
@@ -912,7 +913,7 @@ template <class T> class BuildGenericJniFrameStateMachine {
static constexpr size_t kRegistersNeededForLong = 2;
static constexpr size_t kRegistersNeededForDouble = 2;
- static constexpr bool kMultiRegistersAligned = false; // x86 not using regs, anyways
+ static constexpr bool kMultiRegistersAligned = false; // x86 not using regs, anyways
static constexpr bool kMultiRegistersWidened = false;
static constexpr bool kAlignLongOnStack = false;
static constexpr bool kAlignDoubleOnStack = false;
@@ -932,34 +933,34 @@ template <class T> class BuildGenericJniFrameStateMachine {
#endif
public:
- explicit BuildGenericJniFrameStateMachine(T* delegate) : gpr_index_(kNumNativeGprArgs),
- fpr_index_(kNumNativeFprArgs),
- stack_entries_(0),
- delegate_(delegate) {
+ explicit BuildNativeCallFrameStateMachine(T* delegate)
+ : gpr_index_(kNumNativeGprArgs),
+ fpr_index_(kNumNativeFprArgs),
+ stack_entries_(0),
+ delegate_(delegate) {
// For register alignment, we want to assume that counters (gpr_index_, fpr_index_) are even iff
// the next register is even; counting down is just to make the compiler happy...
CHECK_EQ(kNumNativeGprArgs % 2, 0U);
CHECK_EQ(kNumNativeFprArgs % 2, 0U);
}
- virtual ~BuildGenericJniFrameStateMachine() {}
+ virtual ~BuildNativeCallFrameStateMachine() {}
bool HavePointerGpr() {
return gpr_index_ > 0;
}
- void AdvancePointer(void* val) {
+ void AdvancePointer(const void* val) {
if (HavePointerGpr()) {
gpr_index_--;
PushGpr(reinterpret_cast<uintptr_t>(val));
} else {
- stack_entries_++; // TODO: have a field for pointer length as multiple of 32b
+ stack_entries_++; // TODO: have a field for pointer length as multiple of 32b
PushStack(reinterpret_cast<uintptr_t>(val));
gpr_index_ = 0;
}
}
-
bool HaveHandleScopeGpr() {
return gpr_index_ > 0;
}
@@ -976,7 +977,6 @@ template <class T> class BuildGenericJniFrameStateMachine {
}
}
-
bool HaveIntGpr() {
return gpr_index_ > 0;
}
@@ -992,7 +992,6 @@ template <class T> class BuildGenericJniFrameStateMachine {
}
}
-
bool HaveLongGpr() {
return gpr_index_ >= kRegistersNeededForLong + (LongGprNeedsPadding() ? 1 : 0);
}
@@ -1039,30 +1038,22 @@ template <class T> class BuildGenericJniFrameStateMachine {
}
}
-
bool HaveFloatFpr() {
return fpr_index_ > 0;
}
- template <typename U, typename V> V convert(U in) {
- CHECK_LE(sizeof(U), sizeof(V));
- union { U u; V v; } tmp;
- tmp.u = in;
- return tmp.v;
- }
-
void AdvanceFloat(float val) {
if (kNativeSoftFloatAbi) {
- AdvanceInt(convert<float, uint32_t>(val));
+ AdvanceInt(bit_cast<float, uint32_t>(val));
} else {
if (HaveFloatFpr()) {
fpr_index_--;
if (kRegistersNeededForDouble == 1) {
if (kMultiRegistersWidened) {
- PushFpr8(convert<double, uint64_t>(val));
+ PushFpr8(bit_cast<double, uint64_t>(val));
} else {
// No widening, just use the bits.
- PushFpr8(convert<float, uint64_t>(val));
+ PushFpr8(bit_cast<float, uint64_t>(val));
}
} else {
PushFpr4(val);
@@ -1071,16 +1062,17 @@ template <class T> class BuildGenericJniFrameStateMachine {
stack_entries_++;
if (kRegistersNeededForDouble == 1 && kMultiRegistersWidened) {
// Need to widen before storing: Note the "double" in the template instantiation.
- PushStack(convert<double, uintptr_t>(val));
+ // Note: We need to jump through those hoops to make the compiler happy.
+ DCHECK_EQ(sizeof(uintptr_t), sizeof(uint64_t));
+ PushStack(static_cast<uintptr_t>(bit_cast<double, uint64_t>(val)));
} else {
- PushStack(convert<float, uintptr_t>(val));
+ PushStack(bit_cast<float, uintptr_t>(val));
}
fpr_index_ = 0;
}
}
}
-
bool HaveDoubleFpr() {
return fpr_index_ >= kRegistersNeededForDouble + (DoubleFprNeedsPadding() ? 1 : 0);
}
@@ -1162,101 +1154,66 @@ template <class T> class BuildGenericJniFrameStateMachine {
T* delegate_; // What Push implementation gets called
};
-class ComputeGenericJniFrameSize FINAL {
+// Computes the sizes of register stacks and call stack area. Handling of references can be extended
+// in subclasses.
+//
+// To handle native pointers, use "L" in the shorty for an object reference, which simulates
+// them with handles.
+class ComputeNativeCallFrameSize {
public:
- ComputeGenericJniFrameSize() : num_handle_scope_references_(0), num_stack_entries_(0) {}
+ ComputeNativeCallFrameSize() : num_stack_entries_(0) {}
+
+ virtual ~ComputeNativeCallFrameSize() {}
uint32_t GetStackSize() {
return num_stack_entries_ * sizeof(uintptr_t);
}
- // WARNING: After this, *sp won't be pointing to the method anymore!
- void ComputeLayout(StackReference<mirror::ArtMethod>** m, bool is_static, const char* shorty,
- uint32_t shorty_len, void* sp, HandleScope** table,
- uint32_t* handle_scope_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)->AsMirrorPtr();
-
- uint8_t* sp8 = reinterpret_cast<uint8_t*>(sp);
-
- // First, fix up the layout of the callee-save frame.
- // We have to squeeze in the HandleScope, and relocate the method pointer.
-
- // "Free" the slot for the method.
- sp8 += kPointerSize; // In the callee-save frame we use a full pointer.
-
- // Under the callee saves put handle scope and new method stack reference.
- *handle_scope_entries = num_handle_scope_references_;
-
- size_t handle_scope_size = HandleScope::SizeOf(num_handle_scope_references_);
- size_t scope_and_method = handle_scope_size + sizeof(StackReference<mirror::ArtMethod>);
-
- sp8 -= scope_and_method;
- // Align by kStackAlignment.
- sp8 = reinterpret_cast<uint8_t*>(RoundDown(reinterpret_cast<uintptr_t>(sp8), kStackAlignment));
-
- uint8_t* sp8_table = sp8 + sizeof(StackReference<mirror::ArtMethod>);
- *table = reinterpret_cast<HandleScope*>(sp8_table);
- (*table)->SetNumberOfReferences(num_handle_scope_references_);
-
- // Add a slot for the method pointer, and fill it. Fix the pointer-pointer given to us.
- uint8_t* method_pointer = sp8;
- StackReference<mirror::ArtMethod>* new_method_ref =
- reinterpret_cast<StackReference<mirror::ArtMethod>*>(method_pointer);
- new_method_ref->Assign(method);
- *m = new_method_ref;
-
- // Reference cookie and padding
- sp8 -= 8;
- // Store HandleScope size
- *reinterpret_cast<uint32_t*>(sp8) = static_cast<uint32_t>(handle_scope_size & 0xFFFFFFFF);
-
- // Next comes the native call stack.
+ uint8_t* LayoutCallStack(uint8_t* sp8) {
sp8 -= GetStackSize();
// Align by kStackAlignment.
sp8 = reinterpret_cast<uint8_t*>(RoundDown(reinterpret_cast<uintptr_t>(sp8), kStackAlignment));
- *start_stack = reinterpret_cast<uintptr_t*>(sp8);
+ return sp8;
+ }
- // put fprs and gprs below
+ uint8_t* LayoutCallRegisterStacks(uint8_t* sp8, uintptr_t** start_gpr, uint32_t** start_fpr) {
// Assumption is OK right now, as we have soft-float arm
- size_t fregs = BuildGenericJniFrameStateMachine<ComputeGenericJniFrameSize>::kNumNativeFprArgs;
+ size_t fregs = BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>::kNumNativeFprArgs;
sp8 -= fregs * sizeof(uintptr_t);
*start_fpr = reinterpret_cast<uint32_t*>(sp8);
- size_t iregs = BuildGenericJniFrameStateMachine<ComputeGenericJniFrameSize>::kNumNativeGprArgs;
+ size_t iregs = BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>::kNumNativeGprArgs;
sp8 -= iregs * sizeof(uintptr_t);
*start_gpr = reinterpret_cast<uintptr_t*>(sp8);
+ return sp8;
+ }
- // reserve space for the code pointer
- sp8 -= kPointerSize;
- *code_return = reinterpret_cast<void*>(sp8);
+ uint8_t* LayoutNativeCall(uint8_t* sp8, uintptr_t** start_stack, uintptr_t** start_gpr,
+ uint32_t** start_fpr) {
+ // Native call stack.
+ sp8 = LayoutCallStack(sp8);
+ *start_stack = reinterpret_cast<uintptr_t*>(sp8);
- *overall_size = reinterpret_cast<uint8_t*>(sp) - sp8;
+ // Put fprs and gprs below.
+ sp8 = LayoutCallRegisterStacks(sp8, start_gpr, start_fpr);
- // 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;
+ // Return the new bottom.
+ return sp8;
}
- void ComputeHandleScopeOffset() { } // nothing to do, static right now
+ virtual void WalkHeader(BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>* sm)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {}
- void ComputeAll(bool is_static, const char* shorty, uint32_t shorty_len)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- BuildGenericJniFrameStateMachine<ComputeGenericJniFrameSize> sm(this);
-
- // JNIEnv
- sm.AdvancePointer(nullptr);
+ void Walk(const char* shorty, uint32_t shorty_len) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize> sm(this);
- // Class object or this as first argument
- sm.AdvanceHandleScope(reinterpret_cast<mirror::Object*>(0x12345678));
+ WalkHeader(&sm);
for (uint32_t i = 1; i < shorty_len; ++i) {
Primitive::Type cur_type_ = Primitive::GetType(shorty[i]);
switch (cur_type_) {
case Primitive::kPrimNot:
- sm.AdvanceHandleScope(reinterpret_cast<mirror::Object*>(0x12345678));
+ sm.AdvanceHandleScope(
+ reinterpret_cast<mirror::Object*>(0x12345678));
break;
case Primitive::kPrimBoolean:
@@ -1299,50 +1256,135 @@ class ComputeGenericJniFrameSize FINAL {
// counting is already done in the superclass
}
- uintptr_t PushHandle(mirror::Object* /* ptr */) {
- num_handle_scope_references_++;
+ virtual uintptr_t PushHandle(mirror::Object* /* ptr */) {
return reinterpret_cast<uintptr_t>(nullptr);
}
- private:
- uint32_t num_handle_scope_references_;
+ protected:
uint32_t num_stack_entries_;
};
-// Visits arguments on the stack placing them into a region lower down the stack for the benefit
-// of transitioning into native code.
-class BuildGenericJniFrameVisitor FINAL : public QuickArgumentVisitor {
+class ComputeGenericJniFrameSize FINAL : public ComputeNativeCallFrameSize {
public:
- BuildGenericJniFrameVisitor(StackReference<mirror::ArtMethod>** sp, bool is_static,
- const char* shorty, uint32_t shorty_len, Thread* self) :
- QuickArgumentVisitor(*sp, is_static, shorty, shorty_len), sm_(this) {
- ComputeGenericJniFrameSize fsc;
- fsc.ComputeLayout(sp, is_static, shorty, shorty_len, *sp, &handle_scope_, &handle_scope_expected_refs_,
- &cur_stack_arg_, &cur_gpr_reg_, &cur_fpr_reg_, &code_return_,
- &alloca_used_size_);
- handle_scope_number_of_references_ = 0;
- cur_hs_entry_ = GetFirstHandleScopeEntry();
+ ComputeGenericJniFrameSize() : num_handle_scope_references_(0) {}
+
+ // Lays out the callee-save frame. Assumes that the incorrect frame corresponding to RefsAndArgs
+ // is at *m = sp. Will update to point to the bottom of the save frame.
+ //
+ // Note: assumes ComputeAll() has been run before.
+ void LayoutCalleeSaveFrame(StackReference<mirror::ArtMethod>** m, void* sp, HandleScope** table,
+ uint32_t* handle_scope_entries)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* method = (*m)->AsMirrorPtr();
- // jni environment is always first argument
- sm_.AdvancePointer(self->GetJniEnv());
+ uint8_t* sp8 = reinterpret_cast<uint8_t*>(sp);
- if (is_static) {
- sm_.AdvanceHandleScope((*sp)->AsMirrorPtr()->GetDeclaringClass());
- }
- }
+ // First, fix up the layout of the callee-save frame.
+ // We have to squeeze in the HandleScope, and relocate the method pointer.
- void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE;
+ // "Free" the slot for the method.
+ sp8 += kPointerSize; // In the callee-save frame we use a full pointer.
- void FinalizeHandleScope(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Under the callee saves put handle scope and new method stack reference.
+ *handle_scope_entries = num_handle_scope_references_;
- StackReference<mirror::Object>* GetFirstHandleScopeEntry()
+ size_t handle_scope_size = HandleScope::SizeOf(num_handle_scope_references_);
+ size_t scope_and_method = handle_scope_size + sizeof(StackReference<mirror::ArtMethod>);
+
+ sp8 -= scope_and_method;
+ // Align by kStackAlignment.
+ sp8 = reinterpret_cast<uint8_t*>(RoundDown(
+ reinterpret_cast<uintptr_t>(sp8), kStackAlignment));
+
+ uint8_t* sp8_table = sp8 + sizeof(StackReference<mirror::ArtMethod>);
+ *table = reinterpret_cast<HandleScope*>(sp8_table);
+ (*table)->SetNumberOfReferences(num_handle_scope_references_);
+
+ // Add a slot for the method pointer, and fill it. Fix the pointer-pointer given to us.
+ uint8_t* method_pointer = sp8;
+ StackReference<mirror::ArtMethod>* new_method_ref =
+ reinterpret_cast<StackReference<mirror::ArtMethod>*>(method_pointer);
+ new_method_ref->Assign(method);
+ *m = new_method_ref;
+ }
+
+ // Adds space for the cookie. Note: may leave stack unaligned.
+ void LayoutCookie(uint8_t** sp) {
+ // Reference cookie and padding
+ *sp -= 8;
+ }
+
+ // Re-layout the callee-save frame (insert a handle-scope). Then add space for the cookie.
+ // Returns the new bottom. Note: this may be unaligned.
+ uint8_t* LayoutJNISaveFrame(StackReference<mirror::ArtMethod>** m, void* sp, HandleScope** table,
+ uint32_t* handle_scope_entries)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return handle_scope_->GetHandle(0).GetReference();
+ // First, fix up the layout of the callee-save frame.
+ // We have to squeeze in the HandleScope, and relocate the method pointer.
+ LayoutCalleeSaveFrame(m, sp, table, handle_scope_entries);
+
+ // The bottom of the callee-save frame is now where the method is, *m.
+ uint8_t* sp8 = reinterpret_cast<uint8_t*>(*m);
+
+ // Add space for cookie.
+ LayoutCookie(&sp8);
+
+ return sp8;
}
- jobject GetFirstHandleScopeJObject()
+ // WARNING: After this, *sp won't be pointing to the method anymore!
+ uint8_t* ComputeLayout(StackReference<mirror::ArtMethod>** m, bool is_static, const char* shorty,
+ uint32_t shorty_len, HandleScope** table, uint32_t* handle_scope_entries,
+ uintptr_t** start_stack, uintptr_t** start_gpr, uint32_t** start_fpr)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return handle_scope_->GetHandle(0).ToJObject();
+ Walk(shorty, shorty_len);
+
+ // JNI part.
+ uint8_t* sp8 = LayoutJNISaveFrame(m, reinterpret_cast<void*>(*m), table, handle_scope_entries);
+
+ sp8 = LayoutNativeCall(sp8, start_stack, start_gpr, start_fpr);
+
+ // Return the new bottom.
+ return sp8;
+ }
+
+ uintptr_t PushHandle(mirror::Object* /* ptr */) OVERRIDE;
+
+ // Add JNIEnv* and jobj/jclass before the shorty-derived elements.
+ void WalkHeader(BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>* sm) OVERRIDE
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+ uint32_t num_handle_scope_references_;
+};
+
+uintptr_t ComputeGenericJniFrameSize::PushHandle(mirror::Object* /* ptr */) {
+ num_handle_scope_references_++;
+ return reinterpret_cast<uintptr_t>(nullptr);
+}
+
+void ComputeGenericJniFrameSize::WalkHeader(
+ BuildNativeCallFrameStateMachine<ComputeNativeCallFrameSize>* sm) {
+ // JNIEnv
+ sm->AdvancePointer(nullptr);
+
+ // Class object or this as first argument
+ sm->AdvanceHandleScope(reinterpret_cast<mirror::Object*>(0x12345678));
+}
+
+// Class to push values to three separate regions. Used to fill the native call part. Adheres to
+// the template requirements of BuildGenericJniFrameStateMachine.
+class FillNativeCall {
+ public:
+ FillNativeCall(uintptr_t* gpr_regs, uint32_t* fpr_regs, uintptr_t* stack_args) :
+ cur_gpr_reg_(gpr_regs), cur_fpr_reg_(fpr_regs), cur_stack_arg_(stack_args) {}
+
+ virtual ~FillNativeCall() {}
+
+ void Reset(uintptr_t* gpr_regs, uint32_t* fpr_regs, uintptr_t* stack_args) {
+ cur_gpr_reg_ = gpr_regs;
+ cur_fpr_reg_ = fpr_regs;
+ cur_stack_arg_ = stack_args;
}
void PushGpr(uintptr_t val) {
@@ -1366,46 +1408,110 @@ class BuildGenericJniFrameVisitor FINAL : public QuickArgumentVisitor {
cur_stack_arg_++;
}
- uintptr_t PushHandle(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- uintptr_t tmp;
- if (ref == nullptr) {
- *cur_hs_entry_ = StackReference<mirror::Object>();
- tmp = reinterpret_cast<uintptr_t>(nullptr);
- } else {
- *cur_hs_entry_ = StackReference<mirror::Object>::FromMirrorPtr(ref);
- tmp = reinterpret_cast<uintptr_t>(cur_hs_entry_);
+ virtual uintptr_t PushHandle(mirror::Object* ref) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ LOG(FATAL) << "(Non-JNI) Native call does not use handles.";
+ return 0U;
+ }
+
+ private:
+ uintptr_t* cur_gpr_reg_;
+ uint32_t* cur_fpr_reg_;
+ uintptr_t* cur_stack_arg_;
+};
+
+// Visits arguments on the stack placing them into a region lower down the stack for the benefit
+// of transitioning into native code.
+class BuildGenericJniFrameVisitor FINAL : public QuickArgumentVisitor {
+ public:
+ BuildGenericJniFrameVisitor(StackReference<mirror::ArtMethod>** sp, bool is_static,
+ const char* shorty, uint32_t shorty_len, Thread* self)
+ : QuickArgumentVisitor(*sp, is_static, shorty, shorty_len),
+ jni_call_(nullptr, nullptr, nullptr, nullptr), sm_(&jni_call_) {
+ ComputeGenericJniFrameSize fsc;
+ uintptr_t* start_gpr_reg;
+ uint32_t* start_fpr_reg;
+ uintptr_t* start_stack_arg;
+ uint32_t handle_scope_entries;
+ bottom_of_used_area_ = fsc.ComputeLayout(sp, is_static, shorty, shorty_len, &handle_scope_,
+ &handle_scope_entries, &start_stack_arg,
+ &start_gpr_reg, &start_fpr_reg);
+
+ handle_scope_->SetNumberOfReferences(handle_scope_entries);
+ jni_call_.Reset(start_gpr_reg, start_fpr_reg, start_stack_arg, handle_scope_);
+
+ // jni environment is always first argument
+ sm_.AdvancePointer(self->GetJniEnv());
+
+ if (is_static) {
+ sm_.AdvanceHandleScope((*sp)->AsMirrorPtr()->GetDeclaringClass());
}
- cur_hs_entry_++;
- handle_scope_number_of_references_++;
- return tmp;
}
- // Size of the part of the alloca that we actually need.
- size_t GetAllocaUsedSize() {
- return alloca_used_size_;
+ void Visit() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) OVERRIDE;
+
+ void FinalizeHandleScope(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ StackReference<mirror::Object>* GetFirstHandleScopeEntry()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return handle_scope_->GetHandle(0).GetReference();
+ }
+
+ jobject GetFirstHandleScopeJObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return handle_scope_->GetHandle(0).ToJObject();
}
- void* GetCodeReturn() {
- return code_return_;
+ void* GetBottomOfUsedArea() {
+ return bottom_of_used_area_;
}
private:
- uint32_t handle_scope_number_of_references_;
- StackReference<mirror::Object>* cur_hs_entry_;
+ // A class to fill a JNI call. Adds reference/handle-scope management to FillNativeCall.
+ class FillJniCall FINAL : public FillNativeCall {
+ public:
+ FillJniCall(uintptr_t* gpr_regs, uint32_t* fpr_regs, uintptr_t* stack_args,
+ HandleScope* handle_scope) : FillNativeCall(gpr_regs, fpr_regs, stack_args),
+ handle_scope_(handle_scope), cur_entry_(0) {}
+
+ uintptr_t PushHandle(mirror::Object* ref) OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void Reset(uintptr_t* gpr_regs, uint32_t* fpr_regs, uintptr_t* stack_args, HandleScope* scope) {
+ FillNativeCall::Reset(gpr_regs, fpr_regs, stack_args);
+ handle_scope_ = scope;
+ cur_entry_ = 0U;
+ }
+
+ void ResetRemainingScopeSlots() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ // Initialize padding entries.
+ size_t expected_slots = handle_scope_->NumberOfReferences();
+ while (cur_entry_ < expected_slots) {
+ handle_scope_->GetHandle(cur_entry_++).Assign(nullptr);
+ }
+ DCHECK_NE(cur_entry_, 0U);
+ }
+
+ private:
+ HandleScope* handle_scope_;
+ size_t cur_entry_;
+ };
+
HandleScope* handle_scope_;
- uint32_t handle_scope_expected_refs_;
- uintptr_t* cur_gpr_reg_;
- uint32_t* cur_fpr_reg_;
- uintptr_t* cur_stack_arg_;
- // StackReference<mirror::Object>* top_of_handle_scope_;
- void* code_return_;
- size_t alloca_used_size_;
+ FillJniCall jni_call_;
+ void* bottom_of_used_area_;
- BuildGenericJniFrameStateMachine<BuildGenericJniFrameVisitor> sm_;
+ BuildNativeCallFrameStateMachine<FillJniCall> sm_;
DISALLOW_COPY_AND_ASSIGN(BuildGenericJniFrameVisitor);
};
+uintptr_t BuildGenericJniFrameVisitor::FillJniCall::PushHandle(mirror::Object* ref) {
+ uintptr_t tmp;
+ Handle<mirror::Object> h = handle_scope_->GetHandle(cur_entry_);
+ h.Assign(ref);
+ tmp = reinterpret_cast<uintptr_t>(h.ToJObject());
+ cur_entry_++;
+ return tmp;
+}
+
void BuildGenericJniFrameVisitor::Visit() {
Primitive::Type type = GetParamPrimitiveType();
switch (type) {
@@ -1453,14 +1559,8 @@ void BuildGenericJniFrameVisitor::Visit() {
}
void BuildGenericJniFrameVisitor::FinalizeHandleScope(Thread* self) {
- // Initialize padding entries.
- while (handle_scope_number_of_references_ < handle_scope_expected_refs_) {
- *cur_hs_entry_ = StackReference<mirror::Object>();
- cur_hs_entry_++;
- handle_scope_number_of_references_++;
- }
- handle_scope_->SetNumberOfReferences(handle_scope_expected_refs_);
- DCHECK_NE(handle_scope_expected_refs_, 0U);
+ // Clear out rest of the scope.
+ jni_call_.ResetRemainingScopeSlots();
// Install HandleScope.
self->PushHandleScope(handle_scope_);
}
@@ -1495,19 +1595,20 @@ void artQuickGenericJniEndJNINonRef(Thread* self, uint32_t cookie, jobject lock)
* 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, StackReference<mirror::ArtMethod>* sp)
+extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
mirror::ArtMethod* called = sp->AsMirrorPtr();
DCHECK(called->IsNative()) << PrettyMethod(called, true);
-
- // run the visitor
uint32_t shorty_len = 0;
const char* shorty = called->GetShorty(&shorty_len);
+
+ // Run the visitor.
BuildGenericJniFrameVisitor visitor(&sp, called->IsStatic(), shorty, shorty_len, self);
visitor.VisitArguments();
visitor.FinalizeHandleScope(self);
- // fix up managed-stack things in Thread
+ // Fix up managed-stack things in Thread.
self->SetTopOfStack(sp, 0);
self->VerifyStack();
@@ -1519,7 +1620,7 @@ extern "C" ssize_t artQuickGenericJniTrampoline(Thread* self, StackReference<mir
if (self->IsExceptionPending()) {
self->PopHandleScope();
// A negative value denotes an error.
- return -1;
+ return GetTwoWordFailureValue();
}
} else {
cookie = JniMethodStart(self);
@@ -1550,36 +1651,31 @@ extern "C" ssize_t artQuickGenericJniTrampoline(Thread* self, StackReference<mir
artQuickGenericJniEndJNINonRef(self, cookie, lock);
}
- return -1;
+ return GetTwoWordFailureValue();
}
// Note that the native code pointer will be automatically set by artFindNativeMethod().
}
- // Store the native code pointer in the stack at the right location.
- uintptr_t* code_pointer = reinterpret_cast<uintptr_t*>(visitor.GetCodeReturn());
- *code_pointer = reinterpret_cast<uintptr_t>(nativeCode);
-
- // 5K reserved, window_size + frame pointer used.
- size_t window_size = visitor.GetAllocaUsedSize();
- return (5 * KB) - window_size - kPointerSize;
+ // Return native code addr(lo) and bottom of alloca address(hi).
+ return GetTwoWordSuccessValue(reinterpret_cast<uintptr_t>(visitor.GetBottomOfUsedArea()),
+ reinterpret_cast<uintptr_t>(nativeCode));
}
/*
* Is called after the native JNI code. Responsible for cleanup (handle scope, saved state) and
* unlocking.
*/
-extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self,
- StackReference<mirror::ArtMethod>* sp,
- jvalue result, uint64_t result_f)
+extern "C" uint64_t artQuickGenericJniEndTrampoline(Thread* self, jvalue result, uint64_t result_f)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ StackReference<mirror::ArtMethod>* sp = self->GetManagedStack()->GetTopQuickFrame();
uint32_t* sp32 = reinterpret_cast<uint32_t*>(sp);
mirror::ArtMethod* called = sp->AsMirrorPtr();
uint32_t cookie = *(sp32 - 1);
jobject lock = nullptr;
if (called->IsSynchronized()) {
- HandleScope* table = reinterpret_cast<HandleScope*>(
- reinterpret_cast<uint8_t*>(sp) + sizeof(StackReference<mirror::ArtMethod>));
+ HandleScope* table = reinterpret_cast<HandleScope*>(reinterpret_cast<uint8_t*>(sp)
+ + sizeof(StackReference<mirror::ArtMethod>));
lock = table->GetHandle(0).ToJObject();
}
@@ -1636,8 +1732,7 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_o
FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
uint32_t shorty_len;
- const char* shorty =
- dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len);
+ const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len);
{
// Remember the args in case a GC happens in FindMethodFromCode.
ScopedObjectAccessUnchecked soa(self->GetJniEnv());
@@ -1657,8 +1752,9 @@ static TwoWordReturn artInvokeCommon(uint32_t method_idx, mirror::Object* this_o
const void* code = method->GetEntryPointFromQuickCompiledCode();
// When we return, the caller will branch to this address, so it had better not be 0!
- DCHECK(code != nullptr) << "Code was NULL in method: " << PrettyMethod(method) << " location: "
- << method->GetDexFile()->GetLocation();
+ DCHECK(code != nullptr) << "Code was NULL in method: " << PrettyMethod(method)
+ << " location: "
+ << method->GetDexFile()->GetLocation();
return GetTwoWordSuccessValue(reinterpret_cast<uintptr_t>(code),
reinterpret_cast<uintptr_t>(method));
@@ -1685,47 +1781,50 @@ EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kSuper, false);
EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL(kSuper, true);
#undef EXPLICIT_INVOKE_COMMON_TEMPLATE_DECL
-
// See comments in runtime_support_asm.S
-extern "C" TwoWordReturn artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kInterface, true>(method_idx, this_object, caller_method, self, sp);
+extern "C" TwoWordReturn artInvokeInterfaceTrampolineWithAccessCheck(
+ uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kInterface, true>(method_idx, this_object,
+ caller_method, self, sp);
}
-
-extern "C" TwoWordReturn artInvokeDirectTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method, self, sp);
+extern "C" TwoWordReturn artInvokeDirectTrampolineWithAccessCheck(
+ uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method,
+ self, sp);
}
-extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method, self, sp);
+extern "C" TwoWordReturn artInvokeStaticTrampolineWithAccessCheck(
+ uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method,
+ self, sp);
}
-extern "C" TwoWordReturn artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method, self, sp);
+extern "C" TwoWordReturn artInvokeSuperTrampolineWithAccessCheck(
+ uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method,
+ self, sp);
}
-extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
- mirror::Object* this_object,
- mirror::ArtMethod* caller_method,
- Thread* self,
- StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
- return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method, self, sp);
+extern "C" TwoWordReturn artInvokeVirtualTrampolineWithAccessCheck(
+ uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method, Thread* self,
+ StackReference<mirror::ArtMethod>* sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method,
+ self, sp);
}
// Determine target of interface dispatch. This object is known non-null.
@@ -1769,10 +1868,11 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(mirror::ArtMethod* interfa
dex_method_idx = instr->VRegB_3rc();
}
- const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()
+ ->GetDexFile();
uint32_t shorty_len;
- const char* shorty =
- dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx), &shorty_len);
+ const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx),
+ &shorty_len);
{
// Remember the args in case a GC happens in FindMethodFromCode.
ScopedObjectAccessUnchecked soa(self->GetJniEnv());
@@ -1791,8 +1891,8 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(mirror::ArtMethod* interfa
const void* code = method->GetEntryPointFromQuickCompiledCode();
// When we return, the caller will branch to this address, so it had better not be 0!
- DCHECK(code != nullptr) << "Code was NULL in method: " << PrettyMethod(method) << " location: "
- << method->GetDexFile()->GetLocation();
+ DCHECK(code != nullptr) << "Code was NULL in method: " << PrettyMethod(method)
+ << " location: " << method->GetDexFile()->GetLocation();
return GetTwoWordSuccessValue(reinterpret_cast<uintptr_t>(code),
reinterpret_cast<uintptr_t>(method));
diff --git a/runtime/utils.h b/runtime/utils.h
index 68ea475..eb79968 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -203,6 +203,19 @@ static inline bool NeedsEscaping(uint16_t ch) {
return (ch < ' ' || ch > '~');
}
+// Interpret the bit pattern of input (type U) as type V. Requires the size
+// of V >= size of U (compile-time checked).
+template<typename U, typename V>
+static inline V bit_cast(U in) {
+ COMPILE_ASSERT(sizeof(U) <= sizeof(V), size_of_u_not_le_size_of_v);
+ union {
+ U u;
+ V v;
+ } tmp;
+ tmp.u = in;
+ return tmp.v;
+}
+
std::string PrintableChar(uint16_t ch);
// Returns an ASCII string corresponding to the given UTF-8 string.