Change flush order in JIT code cache

Flush the data cache before flushing the instruction when dual mapped.

Bug: 62356545
Test: art/test.py -j4 --jit --64 -r --verbose -t test-art-target-run-test-debug-prebuild-jit-no-relocate-ntrace-gcstress-checkjni-picimage-npictest-ndebuggable-no-jvmti-916-obsolete-jit64
Test: Using an Android N image and testing on device from master-art
Change-Id: Ia73da7449cc3a40be0128825233086924eca90a5
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 8295f46..eb9199a 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -90,7 +90,6 @@
   return new_map;
 }
 
-
 JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
                                    size_t max_capacity,
                                    bool generate_debug_info,
@@ -802,12 +801,27 @@
       //
       // For reference, this behavior is caused by this commit:
       // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c
-      FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
-                            reinterpret_cast<char*>(code_ptr + code_size));
       if (writable_ptr != code_ptr) {
+        // When there are two mappings of the JIT code cache, RX and
+        // RW, flush the RW version first as we've just dirtied the
+        // cache lines with new code. Flushing the RX version first
+        // can cause a permission fault as the those addresses are not
+        // writable, but can appear dirty in the cache. There is a lot
+        // of potential subtlety here depending on how the cache is
+        // indexed and tagged.
+        //
+        // Flushing the RX version after the RW version is just
+        // invalidating cachelines in the instruction cache. This is
+        // necessary as the instruction cache will often have a
+        // different set of cache lines present and because the JIT
+        // code cache can start a new function at any boundary within
+        // a cache-line.
         FlushDataCache(reinterpret_cast<char*>(writable_ptr),
                        reinterpret_cast<char*>(writable_ptr + code_size));
       }
+      FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
+                            reinterpret_cast<char*>(code_ptr + code_size));
+
       DCHECK(!Runtime::Current()->IsAotCompiler());
       if (has_should_deoptimize_flag) {
         writable_method_header->SetHasShouldDeoptimizeFlag();