AssemblerBuffer-x86-shared.h (8060B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * 4 * ***** BEGIN LICENSE BLOCK ***** 5 * Copyright (C) 2008 Apple Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * ***** END LICENSE BLOCK ***** */ 29 30 #ifndef jit_x86_shared_AssemblerBuffer_x86_shared_h 31 #define jit_x86_shared_AssemblerBuffer_x86_shared_h 32 33 #include "mozilla/Assertions.h" 34 #include "mozilla/Attributes.h" 35 #include "mozilla/Likely.h" 36 #include "mozilla/Vector.h" 37 38 #include <stdarg.h> 39 #include <stddef.h> 40 #include <stdint.h> 41 42 #include "jit/JitContext.h" 43 #include "jit/JitSpewer.h" 44 #include "jit/ProcessExecutableMemory.h" 45 #include "js/AllocPolicy.h" 46 #include "js/Vector.h" 47 48 // Spew formatting helpers. 49 #define PRETTYHEX(x) \ 50 (((x) < 0) ? "-" : ""), \ 51 ((unsigned)((x) ^ ((x) >> 31)) + ((unsigned)(x) >> 31)) 52 53 #define MEM_o "%s0x%x" 54 #define MEM_os MEM_o "(,%s,%d)" 55 #define MEM_ob MEM_o "(%s)" 56 #define MEM_obs MEM_o "(%s,%s,%d)" 57 58 #define MEM_o32 "%s0x%04x" 59 #define MEM_o32s MEM_o32 "(,%s,%d)" 60 #define MEM_o32b MEM_o32 "(%s)" 61 #define MEM_o32bs MEM_o32 "(%s,%s,%d)" 62 #define MEM_o32r ".Lfrom%d(%%rip)" 63 64 #define ADDR_o(offset) PRETTYHEX(offset) 65 #define ADDR_os(offset, index, scale) \ 66 ADDR_o(offset), GPRegName((index)), (1 << (scale)) 67 #define ADDR_ob(offset, base) ADDR_o(offset), GPRegName((base)) 68 #define ADDR_obs(offset, base, index, scale) \ 69 ADDR_ob(offset, base), GPRegName((index)), (1 << (scale)) 70 71 #define ADDR_o32(offset) ADDR_o(offset) 72 #define ADDR_o32s(offset, index, scale) ADDR_os(offset, index, scale) 73 #define ADDR_o32b(offset, base) ADDR_ob(offset, base) 74 #define ADDR_o32bs(offset, base, index, scale) \ 75 ADDR_obs(offset, base, index, scale) 76 #define ADDR_o32r(offset) (offset) 77 78 namespace js { 79 80 class JS_PUBLIC_API Sprinter; 81 82 namespace jit { 83 84 namespace x86_shared { 85 86 // AllocPolicy for AssemblerBuffer. OOMs when trying to allocate more than 87 // MaxCodeBytesPerProcess bytes. Use private inheritance to make sure we 88 // explicitly have to expose SystemAllocPolicy methods. 89 class AssemblerBufferAllocPolicy : private SystemAllocPolicy { 90 public: 91 using SystemAllocPolicy::checkSimulatedOOM; 92 using SystemAllocPolicy::free_; 93 using SystemAllocPolicy::reportAllocOverflow; 94 95 template <typename T> 96 T* pod_realloc(T* p, size_t oldSize, size_t newSize) { 97 static_assert( 98 sizeof(T) == 1, 99 "AssemblerBufferAllocPolicy should only be used with byte vectors"); 100 MOZ_ASSERT(oldSize <= MaxCodeBytesPerProcess); 101 if (MOZ_UNLIKELY(newSize > MaxCodeBytesPerProcess)) { 102 return nullptr; 103 } 104 return SystemAllocPolicy::pod_realloc<T>(p, oldSize, newSize); 105 } 106 template <typename T> 107 T* pod_malloc(size_t numElems) { 108 static_assert( 109 sizeof(T) == 1, 110 "AssemblerBufferAllocPolicy should only be used with byte vectors"); 111 if (MOZ_UNLIKELY(numElems > MaxCodeBytesPerProcess)) { 112 return nullptr; 113 } 114 return SystemAllocPolicy::pod_malloc<T>(numElems); 115 } 116 }; 117 118 class AssemblerBuffer { 119 template <size_t size, typename T> 120 MOZ_ALWAYS_INLINE void sizedAppendUnchecked(T value) { 121 m_buffer.infallibleAppend(reinterpret_cast<unsigned char*>(&value), size); 122 } 123 124 template <size_t size, typename T> 125 MOZ_ALWAYS_INLINE void sizedAppend(T value) { 126 if (MOZ_UNLIKELY( 127 !m_buffer.append(reinterpret_cast<unsigned char*>(&value), size))) { 128 oomDetected(); 129 } 130 } 131 132 public: 133 AssemblerBuffer() : m_oom(false) {} 134 135 void ensureSpace(size_t space) { 136 // This should only be called with small |space| values to ensure 137 // we don't overflow below. 138 MOZ_ASSERT(space <= 16); 139 if (MOZ_UNLIKELY(!m_buffer.reserve(m_buffer.length() + space))) { 140 oomDetected(); 141 } 142 } 143 144 bool isAligned(size_t alignment) const { 145 return !(m_buffer.length() & (alignment - 1)); 146 } 147 148 MOZ_ALWAYS_INLINE void putByteUnchecked(int value) { 149 sizedAppendUnchecked<1>(value); 150 } 151 MOZ_ALWAYS_INLINE void putShortUnchecked(int value) { 152 sizedAppendUnchecked<2>(value); 153 } 154 MOZ_ALWAYS_INLINE void putIntUnchecked(int value) { 155 sizedAppendUnchecked<4>(value); 156 } 157 MOZ_ALWAYS_INLINE void putInt64Unchecked(int64_t value) { 158 sizedAppendUnchecked<8>(value); 159 } 160 161 MOZ_ALWAYS_INLINE void putByte(int value) { sizedAppend<1>(value); } 162 MOZ_ALWAYS_INLINE void putShort(int value) { sizedAppend<2>(value); } 163 MOZ_ALWAYS_INLINE void putInt(int value) { sizedAppend<4>(value); } 164 MOZ_ALWAYS_INLINE void putInt64(int64_t value) { sizedAppend<8>(value); } 165 166 [[nodiscard]] bool append(const unsigned char* values, size_t size) { 167 if (MOZ_UNLIKELY(!m_buffer.append(values, size))) { 168 oomDetected(); 169 return false; 170 } 171 return true; 172 } 173 174 size_t size() const { return m_buffer.length(); } 175 176 bool oom() const { return m_oom; } 177 178 bool reserve(size_t size) { return !m_oom && m_buffer.reserve(size); } 179 180 bool swap(Vector<uint8_t, 0, SystemAllocPolicy>& bytes); 181 182 const unsigned char* buffer() const { 183 MOZ_RELEASE_ASSERT(!m_oom); 184 return m_buffer.begin(); 185 } 186 187 unsigned char* data() { return m_buffer.begin(); } 188 189 protected: 190 /* 191 * OOM handling: This class can OOM in the ensureSpace() method trying 192 * to allocate a new buffer. In response to an OOM, we need to avoid 193 * crashing and report the error. We also want to make it so that 194 * users of this class need to check for OOM only at certain points 195 * and not after every operation. 196 * 197 * Our strategy for handling an OOM is to set m_oom, and then clear (but 198 * not free) m_buffer, preserving the current buffer. This way, the user 199 * can continue assembling into the buffer, deferring OOM checking 200 * until the user wants to read code out of the buffer. 201 * 202 * See also the |buffer| method. 203 */ 204 void oomDetected() { 205 m_oom = true; 206 m_buffer.clear(); 207 #ifdef DEBUG 208 JitContext* context = MaybeGetJitContext(); 209 if (context) { 210 context->setOOM(); 211 } 212 #endif 213 } 214 215 mozilla::Vector<unsigned char, 256, AssemblerBufferAllocPolicy> m_buffer; 216 bool m_oom; 217 }; 218 219 } // namespace x86_shared 220 221 class GenericAssembler { 222 #ifdef JS_JITSPEW 223 Sprinter* printer; 224 #endif 225 public: 226 GenericAssembler() 227 #ifdef JS_JITSPEW 228 : printer(nullptr) 229 #endif 230 { 231 } 232 233 void setPrinter(Sprinter* sp) { 234 #ifdef JS_JITSPEW 235 printer = sp; 236 #endif 237 } 238 239 #ifdef JS_JITSPEW 240 inline void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3) { 241 if (MOZ_UNLIKELY(printer || JitSpewEnabled(JitSpew_Codegen))) { 242 va_list va; 243 va_start(va, fmt); 244 spew(fmt, va); 245 va_end(va); 246 } 247 } 248 #else 249 MOZ_ALWAYS_INLINE void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3) {} 250 #endif 251 252 #ifdef JS_JITSPEW 253 MOZ_COLD void spew(const char* fmt, va_list va) MOZ_FORMAT_PRINTF(2, 0); 254 #endif 255 }; 256 257 } // namespace jit 258 } // namespace js 259 260 #endif /* jit_x86_shared_AssemblerBuffer_x86_shared_h */