diff options
| author | 2019-07-23 16:41:54 +0100 | |
|---|---|---|
| committer | 2019-07-31 08:16:27 +0000 | |
| commit | 9df37b9f0fc2046ceabeea0d0638ac286bfc0f37 (patch) | |
| tree | 7f1bf36d2373c6666c24f8a35509f929c7e16944 | |
| parent | 92fc2c0241590e475a2a37c9864633b88f97b280 (diff) | |
ART: ARM64: Fix saved fpu stack offsets for SIMD.
Fix the bug when a wrong stack offset was recorded for a FP
saved in a SlowPathCode: this happened when graph had SIMD
loops and some regular FP registers live across a slow path.
Test: test-art-target, test-art-host.
Change-Id: I08b32c9877fcd468dafa6027c156e544d730f1f7
| -rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 21 | ||||
| -rw-r--r-- | test/597-deopt-busy-loop/expected.txt | 3 | ||||
| -rw-r--r-- | test/597-deopt-busy-loop/src/FloatLoop.java | 97 | ||||
| -rw-r--r-- | test/597-deopt-busy-loop/src/Main.java | 54 | ||||
| -rw-r--r-- | test/597-deopt-busy-loop/src/SimpleLoop.java | 62 |
5 files changed, 183 insertions, 54 deletions
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index aeef90ac96..492fe4da8e 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -224,12 +224,13 @@ void SlowPathCodeARM64::SaveLiveRegisters(CodeGenerator* codegen, LocationSummar stack_offset += kXRegSizeInBytes; } + const size_t fp_reg_size = codegen->GetGraph()->HasSIMD() ? kQRegSizeInBytes : kDRegSizeInBytes; const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false); for (uint32_t i : LowToHighBits(fp_spills)) { DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize()); DCHECK_LT(i, kMaximumNumberOfExpectedRegisters); saved_fpu_stack_offsets_[i] = stack_offset; - stack_offset += kDRegSizeInBytes; + stack_offset += fp_reg_size; } SaveRestoreLiveRegistersHelper(codegen, @@ -1279,16 +1280,18 @@ size_t CodeGeneratorARM64::RestoreCoreRegister(size_t stack_index, uint32_t reg_ return kArm64WordSize; } -size_t CodeGeneratorARM64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { - FPRegister reg = FPRegister(reg_id, kDRegSize); - __ Str(reg, MemOperand(sp, stack_index)); - return kArm64WordSize; +size_t CodeGeneratorARM64::SaveFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED, + uint32_t reg_id ATTRIBUTE_UNUSED) { + LOG(FATAL) << "FP registers shouldn't be saved/restored individually, " + << "use SaveRestoreLiveRegistersHelper"; + UNREACHABLE(); } -size_t CodeGeneratorARM64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { - FPRegister reg = FPRegister(reg_id, kDRegSize); - __ Ldr(reg, MemOperand(sp, stack_index)); - return kArm64WordSize; +size_t CodeGeneratorARM64::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED, + uint32_t reg_id ATTRIBUTE_UNUSED) { + LOG(FATAL) << "FP registers shouldn't be saved/restored individually, " + << "use SaveRestoreLiveRegistersHelper"; + UNREACHABLE(); } void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const { diff --git a/test/597-deopt-busy-loop/expected.txt b/test/597-deopt-busy-loop/expected.txt index f993efcdad..6e2fc8e25e 100644 --- a/test/597-deopt-busy-loop/expected.txt +++ b/test/597-deopt-busy-loop/expected.txt @@ -1,2 +1,3 @@ JNI_OnLoad called -Finishing +Simple loop finishing +Float loop finishing diff --git a/test/597-deopt-busy-loop/src/FloatLoop.java b/test/597-deopt-busy-loop/src/FloatLoop.java new file mode 100644 index 0000000000..57667a68a4 --- /dev/null +++ b/test/597-deopt-busy-loop/src/FloatLoop.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2019 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. + */ + +// This test checks that FP registers spill offset is correctly recorded in the SlowPath; by causing +// asynchronous deoptimization in debuggable mode we observe the FP values in the interpreter. +public class FloatLoop implements Runnable { + static final int numberOfThreads = 2; + volatile static boolean sExitFlag = false; + volatile static boolean sEntered = false; + int threadIndex; + + FloatLoop(int index) { + threadIndex = index; + } + + public static void main() throws Exception { + final Thread[] threads = new Thread[numberOfThreads]; + for (int t = 0; t < threads.length; t++) { + threads[t] = new Thread(new FloatLoop(t)); + threads[t].start(); + } + for (Thread t : threads) { + t.join(); + } + + System.out.println("Float loop finishing"); + } + + static final float kFloatConst0 = 256.0f; + static final float kFloatConst1 = 128.0f; + static final int kArraySize = 128; + volatile static float floatField; + + public void expectEqualToEither(float value, float expected0, float expected1) { + if (value != expected0 && value != expected1) { + throw new Error("Expected: " + expected0 + " or "+ expected1 + + ", found: " + value); + } + } + + public void $noinline$busyLoop() { + Main.assertIsManaged(); + + // On Arm64: + // This loop is likely to be vectorized which causes the full 16-byte Q-register to be saved + // across slow paths. + int[] array = new int[kArraySize]; + for (int i = 0; i < kArraySize; i++) { + array[i]++; + } + + sEntered = true; + float s0 = kFloatConst0; + float s1 = kFloatConst1; + for (int i = 0; !sExitFlag; i++) { + if (i % 2 == 0) { + s0 += 2.0; + s1 += 2.0; + } else { + s0 -= 2.0; + s1 -= 2.0; + } + // SuspendCheckSlowPath must record correct stack offset for spilled FP registers. + } + Main.assertIsInterpreted(); + + expectEqualToEither(s0, kFloatConst0, kFloatConst0 + 2.0f); + expectEqualToEither(s1, kFloatConst1, kFloatConst1 + 2.0f); + + floatField = s0 + s1; + } + + public void run() { + if (threadIndex == 0) { + while (!sEntered) { + Thread.yield(); + } + Main.deoptimizeAll(); + sExitFlag = true; + } else { + $noinline$busyLoop(); + } + } +} diff --git a/test/597-deopt-busy-loop/src/Main.java b/test/597-deopt-busy-loop/src/Main.java index 46b6bbf4f3..d15c6c7835 100644 --- a/test/597-deopt-busy-loop/src/Main.java +++ b/test/597-deopt-busy-loop/src/Main.java @@ -14,56 +14,22 @@ * limitations under the License. */ -public class Main implements Runnable { - static final int numberOfThreads = 2; - volatile static boolean sExitFlag = false; - volatile static boolean sEntered = false; - int threadIndex; +public class Main { - private static native void deoptimizeAll(); - private static native void assertIsInterpreted(); - private static native void assertIsManaged(); + public static native void deoptimizeAll(); + public static native void undeoptimizeAll(); + public static native void assertIsInterpreted(); + public static native void assertIsManaged(); private static native void ensureJitCompiled(Class<?> cls, String methodName); - Main(int index) { - threadIndex = index; - } - public static void main(String[] args) throws Exception { System.loadLibrary(args[0]); - final Thread[] threads = new Thread[numberOfThreads]; - for (int t = 0; t < threads.length; t++) { - threads[t] = new Thread(new Main(t)); - threads[t].start(); - } - for (Thread t : threads) { - t.join(); - } - System.out.println("Finishing"); - } - - public void $noinline$busyLoop() { - assertIsManaged(); - sEntered = true; - for (;;) { - if (sExitFlag) { - break; - } - } - assertIsInterpreted(); - } + ensureJitCompiled(SimpleLoop.class, "$noinline$busyLoop"); + SimpleLoop.main(); - public void run() { - if (threadIndex == 0) { - while (!sEntered) { - Thread.yield(); - } - deoptimizeAll(); - sExitFlag = true; - } else { - ensureJitCompiled(Main.class, "$noinline$busyLoop"); - $noinline$busyLoop(); - } + undeoptimizeAll(); + ensureJitCompiled(FloatLoop.class, "$noinline$busyLoop"); + FloatLoop.main(); } } diff --git a/test/597-deopt-busy-loop/src/SimpleLoop.java b/test/597-deopt-busy-loop/src/SimpleLoop.java new file mode 100644 index 0000000000..12298a90d9 --- /dev/null +++ b/test/597-deopt-busy-loop/src/SimpleLoop.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 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. + */ + +public class SimpleLoop implements Runnable { + static final int numberOfThreads = 2; + volatile static boolean sExitFlag = false; + volatile static boolean sEntered = false; + int threadIndex; + + SimpleLoop(int index) { + threadIndex = index; + } + + public static void main() throws Exception { + final Thread[] threads = new Thread[numberOfThreads]; + for (int t = 0; t < threads.length; t++) { + threads[t] = new Thread(new SimpleLoop(t)); + threads[t].start(); + } + for (Thread t : threads) { + t.join(); + } + + System.out.println("Simple loop finishing"); + } + + public void $noinline$busyLoop() { + Main.assertIsManaged(); + sEntered = true; + for (;;) { + if (sExitFlag) { + break; + } + } + Main.assertIsInterpreted(); + } + + public void run() { + if (threadIndex == 0) { + while (!sEntered) { + Thread.yield(); + } + Main.deoptimizeAll(); + sExitFlag = true; + } else { + $noinline$busyLoop(); + } + } +} |