MozCpu-vixl.cpp (9412B)
1 // Copyright 2015, ARM Limited 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are met: 6 // 7 // * Redistributions of source code must retain the above copyright notice, 8 // this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above copyright notice, 10 // this list of conditions and the following disclaimer in the documentation 11 // and/or other materials provided with the distribution. 12 // * Neither the name of ARM Limited nor the names of its contributors may be 13 // used to endorse or promote products derived from this software without 14 // specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 17 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27 #include "jit/arm64/vixl/Cpu-vixl.h" 28 #include "jit/arm64/vixl/Simulator-vixl.h" 29 #include "jit/arm64/vixl/Utils-vixl.h" 30 #include "util/WindowsWrapper.h" 31 32 #if defined(XP_DARWIN) 33 # include <libkern/OSCacheControl.h> 34 #endif 35 36 namespace vixl { 37 38 // Currently computes I and D cache line size. 39 void CPU::SetUp() { 40 uint32_t cache_type_register = GetCacheType(); 41 42 // The cache type register holds information about the caches, including I 43 // D caches line size. 44 static const int kDCacheLineSizeShift = 16; 45 static const int kICacheLineSizeShift = 0; 46 static const uint32_t kDCacheLineSizeMask = 0xf << kDCacheLineSizeShift; 47 static const uint32_t kICacheLineSizeMask = 0xf << kICacheLineSizeShift; 48 49 // The cache type register holds the size of the I and D caches in words as 50 // a power of two. 51 uint32_t dcache_line_size_power_of_two = 52 (cache_type_register & kDCacheLineSizeMask) >> kDCacheLineSizeShift; 53 uint32_t icache_line_size_power_of_two = 54 (cache_type_register & kICacheLineSizeMask) >> kICacheLineSizeShift; 55 56 dcache_line_size_ = 4 << dcache_line_size_power_of_two; 57 icache_line_size_ = 4 << icache_line_size_power_of_two; 58 59 // Bug 1521158 suggests that having CPU with different cache line sizes could 60 // cause issues as we would only invalidate half of the cache line of we 61 // invalidate every 128 bytes, but other little cores have a different stride 62 // such as 64 bytes. To be conservative, we will try reducing the stride to 32 63 // bytes, which should be smaller than any known cache line. 64 const uint32_t conservative_line_size = 32; 65 dcache_line_size_ = std::min(dcache_line_size_, conservative_line_size); 66 icache_line_size_ = std::min(icache_line_size_, conservative_line_size); 67 } 68 69 70 uint32_t CPU::GetCacheType() { 71 #if defined(__aarch64__) && !defined(_MSC_VER) && !defined(XP_DARWIN) && !defined(__OpenBSD__) 72 uint64_t cache_type_register; 73 // Copy the content of the cache type register to a core register. 74 __asm__ __volatile__ ("mrs %[ctr], ctr_el0" // NOLINT 75 : [ctr] "=r" (cache_type_register)); 76 // The top bits are reserved, or report information about MTE which currently 77 // we discard. This will likely need to be changed to just return 78 // cache_type_register when we update VIXL next. 79 uint64_t mask = 0xffffffffull; 80 return static_cast<uint32_t>(cache_type_register & mask); 81 #else 82 // This will lead to a cache with 1 byte long lines, which is fine since 83 // neither EnsureIAndDCacheCoherency nor the simulator will need this 84 // information. 85 return 0; 86 #endif 87 } 88 89 void CPU::EnsureIAndDCacheCoherency(void* address, size_t length) { 90 #if defined(JS_SIMULATOR_ARM64) && defined(JS_CACHE_SIMULATOR_ARM64) 91 // This code attempts to emulate what the following assembly sequence is 92 // doing, which is sending the information to all cores that some cache line 93 // have to be invalidated and invalidating them only on the current core. 94 // 95 // This is done by recording the current range to be flushed to all 96 // simulators, then if there is a simulator associated with the current 97 // thread, applying all flushed ranges as the "isb" instruction would do. 98 // 99 // As we have no control over the CPU cores used by the code generator and the 100 // execution threads, this code assumes that each thread runs on its own core. 101 // 102 // See Bug 1529933 for more detailed explanation of this issue. 103 using js::jit::SimulatorProcess; 104 js::jit::AutoLockSimulatorCache alsc; 105 if (length > 0) { 106 SimulatorProcess::recordICacheFlush(address, length); 107 } 108 Simulator* sim = vixl::Simulator::Current(); 109 if (sim) { 110 sim->FlushICache(); 111 } 112 #elif defined(_MSC_VER) && defined(_M_ARM64) 113 FlushInstructionCache(GetCurrentProcess(), address, length); 114 #elif defined(XP_DARWIN) 115 sys_icache_invalidate(address, length); 116 #elif defined(__aarch64__) && (defined(__linux__) || defined(__android__) || defined(__FreeBSD__)) 117 // Implement the cache synchronisation for all targets where AArch64 is the 118 // host, even if we're building the simulator for an AAarch64 host. This 119 // allows for cases where the user wants to simulate code as well as run it 120 // natively. 121 122 if (length == 0) { 123 return; 124 } 125 126 // The code below assumes user space cache operations are allowed. 127 128 // Work out the line sizes for each cache, and use them to determine the 129 // start addresses. 130 uintptr_t start = reinterpret_cast<uintptr_t>(address); 131 uintptr_t dsize = static_cast<uintptr_t>(dcache_line_size_); 132 uintptr_t isize = static_cast<uintptr_t>(icache_line_size_); 133 uintptr_t dline = start & ~(dsize - 1); 134 uintptr_t iline = start & ~(isize - 1); 135 136 // Cache line sizes are always a power of 2. 137 VIXL_ASSERT(IsPowerOf2(dsize)); 138 VIXL_ASSERT(IsPowerOf2(isize)); 139 uintptr_t end = start + length; 140 141 do { 142 __asm__ __volatile__ ( 143 // Clean each line of the D cache containing the target data. 144 // 145 // dc : Data Cache maintenance 146 // c : Clean 147 // i : Invalidate 148 // va : by (Virtual) Address 149 // c : to the point of Coherency 150 // Original implementation used cvau, but changed to civac due to 151 // errata on Cortex-A53 819472, 826319, 827319 and 824069. 152 // See ARM DDI 0406B page B2-12 for more information. 153 // 154 " dc civac, %[dline]\n" 155 : 156 : [dline] "r" (dline) 157 // This code does not write to memory, but the "memory" dependency 158 // prevents GCC from reordering the code. 159 : "memory"); 160 dline += dsize; 161 } while (dline < end); 162 163 __asm__ __volatile__ ( 164 // Make sure that the data cache operations (above) complete before the 165 // instruction cache operations (below). 166 // 167 // dsb : Data Synchronisation Barrier 168 // ish : Inner SHareable domain 169 // 170 // The point of unification for an Inner Shareable shareability domain is 171 // the point by which the instruction and data caches of all the processors 172 // in that Inner Shareable shareability domain are guaranteed to see the 173 // same copy of a memory location. See ARM DDI 0406B page B2-12 for more 174 // information. 175 " dsb ish\n" 176 : : : "memory"); 177 178 do { 179 __asm__ __volatile__ ( 180 // Invalidate each line of the I cache containing the target data. 181 // 182 // ic : Instruction Cache maintenance 183 // i : Invalidate 184 // va : by Address 185 // u : to the point of Unification 186 " ic ivau, %[iline]\n" 187 : 188 : [iline] "r" (iline) 189 : "memory"); 190 iline += isize; 191 } while (iline < end); 192 193 __asm__ __volatile__( 194 // Make sure that the instruction cache operations (above) take effect 195 // before the isb (below). 196 " dsb ish\n" 197 198 // Ensure that any instructions already in the pipeline are discarded and 199 // reloaded from the new data. 200 // isb : Instruction Synchronisation Barrier 201 " isb\n" 202 : 203 : 204 : "memory"); 205 #elif defined(__aarch64__) && !defined(__OpenBSD__) 206 # error Please check/implement the cache invalidation code for your OS 207 208 #else 209 // If the host isn't AArch64, we must be using the simulator, so this function 210 // doesn't have to do anything. 211 USE(address, length); 212 #endif 213 } 214 215 void CPU::FlushExecutionContext() { 216 #if defined(JS_SIMULATOR_ARM64) && defined(JS_CACHE_SIMULATOR_ARM64) 217 // Performing an 'isb' will ensure the current core instruction pipeline is 218 // synchronized with an icache flush executed by another core. 219 using js::jit::SimulatorProcess; 220 js::jit::AutoLockSimulatorCache alsc; 221 Simulator* sim = vixl::Simulator::Current(); 222 if (sim) { 223 sim->FlushICache(); 224 } 225 #elif defined(__aarch64__) 226 // Ensure that any instructions already in the pipeline are discarded and 227 // reloaded from the icache. 228 __asm__ __volatile__("isb\n" : : : "memory"); 229 #endif 230 } 231 232 } // namespace vixl