Fix FOUR_ARG_DOWNCALL assembly stubs on arm and x86.
They were creating a stack that the runtime did not understand.
bug:28348339
Change-Id: Ic03663552209beda8ff1e79db58bedc8f34d9a0e
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 1bba4f9..5209bb6 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -88,6 +88,36 @@
#endif
.endm
+ /*
+ * Macro that sets up the callee save frame to conform with
+ * Runtime::CreateCalleeSaveMethod(kRefsOnly)
+ * and preserves the value of rTemp2 at entry.
+ */
+.macro SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_RTEMP2 rTemp1, rTemp2
+ push {r5-r8, r10-r11, lr} @ 7 words of callee saves
+ .cfi_adjust_cfa_offset 28
+ .cfi_rel_offset r5, 0
+ .cfi_rel_offset r6, 4
+ .cfi_rel_offset r7, 8
+ .cfi_rel_offset r8, 12
+ .cfi_rel_offset r10, 16
+ .cfi_rel_offset r11, 20
+ .cfi_rel_offset lr, 24
+ sub sp, #4 @ bottom word will hold Method*
+ .cfi_adjust_cfa_offset 4
+ str \rTemp2, [sp, #0] @ save rTemp2
+ RUNTIME_CURRENT2 \rTemp1, \rTemp2 @ Load Runtime::Current into rTemp1.
+ ldr \rTemp1, [\rTemp1, #RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET] @ rTemp1 is kRefsOnly Method*.
+ ldr \rTemp2, [sp, #0] @ restore rTemp2
+ str \rTemp1, [sp, #0] @ Place Method* at bottom of stack.
+ str sp, [r9, #THREAD_TOP_QUICK_FRAME_OFFSET] @ Place sp in Thread::Current()->top_quick_frame.
+
+ // Ugly compile-time check, but we only have the preprocessor.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 28 + 4)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(ARM) size not as expected."
+#endif
+.endm
+
.macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
add sp, #4 @ bottom word holds Method*
.cfi_adjust_cfa_offset -4
@@ -831,23 +861,13 @@
.macro FOUR_ARG_DOWNCALL name, entrypoint, return
.extern \entrypoint
ENTRY \name
- sub sp, #12 @ alignment padding
- .cfi_adjust_cfa_offset 12
- push {r3} @ Save r3 as is it used as a temp register in the
- .cfi_adjust_cfa_offset 4 @ expansion of the SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
- .cfi_rel_offset r3, 0 @ macro below, which clobbers its arguments.
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME r3, r12 @ save callee saves in case of GC
- ldr r3, [sp, 32] @ restore r3
- .cfi_restore r3
-
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_RTEMP2 r12, r3 @ save callee saves in case of GC
str r9, [sp, #-16]! @ expand the frame and pass Thread::Current
.cfi_adjust_cfa_offset 16
bl \entrypoint
add sp, #16 @ strip the extra frame
.cfi_adjust_cfa_offset -16
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
- add sp, #16 @ pop r3 + padding
- .cfi_adjust_cfa_offset -16
\return
END \name
.endm
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 485da9f..2d7f664 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -73,6 +73,38 @@
#endif
END_MACRO
+ /*
+ * Macro that sets up the callee save frame to conform with
+ * Runtime::CreateCalleeSaveMethod(kRefsOnly)
+ * and preserves the value of got_reg at entry.
+ */
+MACRO2(SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_GOT_REG, got_reg, temp_reg)
+ PUSH edi // Save callee saves (ebx is saved/restored by the upcall)
+ PUSH esi
+ PUSH ebp
+ pushl REG_VAR(got_reg) // Save got_reg
+ subl MACRO_LITERAL(8), %esp // Grow stack by 2 words.
+ CFI_ADJUST_CFA_OFFSET(8)
+
+ SETUP_GOT_NOSAVE RAW_VAR(got_reg)
+ // Load Runtime::instance_ from GOT.
+ movl SYMBOL(_ZN3art7Runtime9instance_E)@GOT(REG_VAR(got_reg)), REG_VAR(temp_reg)
+ movl (REG_VAR(temp_reg)), REG_VAR(temp_reg)
+ // Push save all callee-save method.
+ pushl RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET(REG_VAR(temp_reg))
+ CFI_ADJUST_CFA_OFFSET(4)
+ // Store esp as the top quick frame.
+ movl %esp, %fs:THREAD_TOP_QUICK_FRAME_OFFSET
+ // Restore got_reg.
+ movl 12(%esp), REG_VAR(got_reg)
+
+ // Ugly compile-time check, but we only have the preprocessor.
+ // Last +4: implicit return address pushed on stack when caller made call.
+#if (FRAME_SIZE_REFS_ONLY_CALLEE_SAVE != 3*4 + 16 + 4)
+#error "REFS_ONLY_CALLEE_SAVE_FRAME(X86) size not as expected."
+#endif
+END_MACRO
+
MACRO0(RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME)
addl MACRO_LITERAL(16), %esp // Unwind stack up to saved values
CFI_ADJUST_CFA_OFFSET(-16)
@@ -686,14 +718,7 @@
MACRO3(FOUR_ARG_DOWNCALL, c_name, cxx_name, return_macro)
DEFINE_FUNCTION VAR(c_name)
- subl MACRO_LITERAL(12), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(12)
- PUSH ebx // Save ebx as the expansion of the
- // SETUP_REFS_ONLY_CALLEE_SAVE_FRAME
- // macro below clobbers it.
- SETUP_REFS_ONLY_CALLEE_SAVE_FRAME ebx, ebx // save ref containing registers for GC
- movl 28(%esp), %ebx // restore ebx
- CFI_RESTORE_REG ebx
+ SETUP_REFS_ONLY_CALLEE_SAVE_FRAME_PRESERVE_GOT_REG ebx, ebx // save ref containing registers for GC
// Outgoing argument set up
subl MACRO_LITERAL(12), %esp // alignment padding
@@ -708,8 +733,6 @@
addl MACRO_LITERAL(32), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-32)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME // restore frame up to return address
- addl MACRO_LITERAL(16), %esp // pop ebx + padding
- CFI_ADJUST_CFA_OFFSET(-16)
CALL_MACRO(return_macro) // return or deliver exception
END_FUNCTION VAR(c_name)
END_MACRO
diff --git a/test/605-new-string-from-bytes/expected.txt b/test/605-new-string-from-bytes/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/605-new-string-from-bytes/expected.txt
diff --git a/test/605-new-string-from-bytes/info.txt b/test/605-new-string-from-bytes/info.txt
new file mode 100644
index 0000000..be02c43
--- /dev/null
+++ b/test/605-new-string-from-bytes/info.txt
@@ -0,0 +1,2 @@
+Regression test for the newStringFromBytes entrypoint,
+which used to wrongly setup the stack.
diff --git a/test/605-new-string-from-bytes/src/Main.java b/test/605-new-string-from-bytes/src/Main.java
new file mode 100644
index 0000000..bd24b50
--- /dev/null
+++ b/test/605-new-string-from-bytes/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ Class c = Class.forName("java.lang.StringFactory");
+ Method m = c.getDeclaredMethod("newStringFromBytes", byte[].class, int.class);
+
+ // Loop over allocations to get more chances of doing GC while in the
+ // newStringFromBytes intrinsic.
+ for (int i = 0; i < 10; i++) {
+ try {
+ byte[] f = new byte[100000000];
+ f[0] = (byte)i;
+ f[1] = (byte)i;
+ m.invoke(null, f, 0);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof OutOfMemoryError) {
+ // Ignore, this is a stress test.
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+}