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 Sprinter; 81 82 namespace jit { 83 84 // AllocPolicy for AssemblerBuffer. OOMs when trying to allocate more than 85 // MaxCodeBytesPerProcess bytes. Use private inheritance to make sure we 86 // explicitly have to expose SystemAllocPolicy methods. 87 class AssemblerBufferAllocPolicy : private SystemAllocPolicy { 88 public: 89 using SystemAllocPolicy::checkSimulatedOOM; 90 using SystemAllocPolicy::free_; 91 using SystemAllocPolicy::reportAllocOverflow; 92 93 template <typename T> pod_realloc(T * p,size_t oldSize,size_t newSize)94 T* pod_realloc(T* p, size_t oldSize, size_t newSize) { 95 static_assert( 96 sizeof(T) == 1, 97 "AssemblerBufferAllocPolicy should only be used with byte vectors"); 98 MOZ_ASSERT(oldSize <= MaxCodeBytesPerProcess); 99 if (MOZ_UNLIKELY(newSize > MaxCodeBytesPerProcess)) { 100 return nullptr; 101 } 102 return SystemAllocPolicy::pod_realloc<T>(p, oldSize, newSize); 103 } 104 template <typename T> pod_malloc(size_t numElems)105 T* pod_malloc(size_t numElems) { 106 static_assert( 107 sizeof(T) == 1, 108 "AssemblerBufferAllocPolicy should only be used with byte vectors"); 109 if (MOZ_UNLIKELY(numElems > MaxCodeBytesPerProcess)) { 110 return nullptr; 111 } 112 return SystemAllocPolicy::pod_malloc<T>(numElems); 113 } 114 }; 115 116 class AssemblerBuffer { 117 template <size_t size, typename T> sizedAppendUnchecked(T value)118 MOZ_ALWAYS_INLINE void sizedAppendUnchecked(T value) { 119 m_buffer.infallibleAppend(reinterpret_cast<unsigned char*>(&value), size); 120 } 121 122 template <size_t size, typename T> sizedAppend(T value)123 MOZ_ALWAYS_INLINE void sizedAppend(T value) { 124 if (MOZ_UNLIKELY( 125 !m_buffer.append(reinterpret_cast<unsigned char*>(&value), size))) { 126 oomDetected(); 127 } 128 } 129 130 public: AssemblerBuffer()131 AssemblerBuffer() : m_oom(false) {} 132 ensureSpace(size_t space)133 void ensureSpace(size_t space) { 134 // This should only be called with small |space| values to ensure 135 // we don't overflow below. 136 MOZ_ASSERT(space <= 16); 137 if (MOZ_UNLIKELY(!m_buffer.reserve(m_buffer.length() + space))) { 138 oomDetected(); 139 } 140 } 141 isAligned(size_t alignment)142 bool isAligned(size_t alignment) const { 143 return !(m_buffer.length() & (alignment - 1)); 144 } 145 putByteUnchecked(int value)146 MOZ_ALWAYS_INLINE void putByteUnchecked(int value) { 147 sizedAppendUnchecked<1>(value); 148 } putShortUnchecked(int value)149 MOZ_ALWAYS_INLINE void putShortUnchecked(int value) { 150 sizedAppendUnchecked<2>(value); 151 } putIntUnchecked(int value)152 MOZ_ALWAYS_INLINE void putIntUnchecked(int value) { 153 sizedAppendUnchecked<4>(value); 154 } putInt64Unchecked(int64_t value)155 MOZ_ALWAYS_INLINE void putInt64Unchecked(int64_t value) { 156 sizedAppendUnchecked<8>(value); 157 } 158 putByte(int value)159 MOZ_ALWAYS_INLINE void putByte(int value) { sizedAppend<1>(value); } putShort(int value)160 MOZ_ALWAYS_INLINE void putShort(int value) { sizedAppend<2>(value); } putInt(int value)161 MOZ_ALWAYS_INLINE void putInt(int value) { sizedAppend<4>(value); } putInt64(int64_t value)162 MOZ_ALWAYS_INLINE void putInt64(int64_t value) { sizedAppend<8>(value); } 163 append(const unsigned char * values,size_t size)164 [[nodiscard]] bool append(const unsigned char* values, size_t size) { 165 if (MOZ_UNLIKELY(!m_buffer.append(values, size))) { 166 oomDetected(); 167 return false; 168 } 169 return true; 170 } 171 size()172 size_t size() const { return m_buffer.length(); } 173 oom()174 bool oom() const { return m_oom; } 175 reserve(size_t size)176 bool reserve(size_t size) { return !m_oom && m_buffer.reserve(size); } 177 178 bool swap(Vector<uint8_t, 0, SystemAllocPolicy>& bytes); 179 buffer()180 const unsigned char* buffer() const { 181 MOZ_RELEASE_ASSERT(!m_oom); 182 return m_buffer.begin(); 183 } 184 data()185 unsigned char* data() { return m_buffer.begin(); } 186 187 protected: 188 /* 189 * OOM handling: This class can OOM in the ensureSpace() method trying 190 * to allocate a new buffer. In response to an OOM, we need to avoid 191 * crashing and report the error. We also want to make it so that 192 * users of this class need to check for OOM only at certain points 193 * and not after every operation. 194 * 195 * Our strategy for handling an OOM is to set m_oom, and then clear (but 196 * not free) m_buffer, preserving the current buffer. This way, the user 197 * can continue assembling into the buffer, deferring OOM checking 198 * until the user wants to read code out of the buffer. 199 * 200 * See also the |buffer| method. 201 */ oomDetected()202 void oomDetected() { 203 m_oom = true; 204 m_buffer.clear(); 205 #ifdef DEBUG 206 JitContext* context = MaybeGetJitContext(); 207 if (context) { 208 context->setOOM(); 209 } 210 #endif 211 } 212 213 mozilla::Vector<unsigned char, 256, AssemblerBufferAllocPolicy> m_buffer; 214 bool m_oom; 215 }; 216 217 class GenericAssembler { 218 #ifdef JS_JITSPEW 219 Sprinter* printer; 220 #endif 221 public: GenericAssembler()222 GenericAssembler() 223 #ifdef JS_JITSPEW 224 : printer(nullptr) 225 #endif 226 { 227 } 228 setPrinter(Sprinter * sp)229 void setPrinter(Sprinter* sp) { 230 #ifdef JS_JITSPEW 231 printer = sp; 232 #endif 233 } 234 235 #ifdef JS_JITSPEW spew(const char * fmt,...)236 inline void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3) { 237 if (MOZ_UNLIKELY(printer || JitSpewEnabled(JitSpew_Codegen))) { 238 va_list va; 239 va_start(va, fmt); 240 spew(fmt, va); 241 va_end(va); 242 } 243 } 244 #else spew(const char * fmt,...)245 MOZ_ALWAYS_INLINE void spew(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3) {} 246 #endif 247 248 #ifdef JS_JITSPEW 249 MOZ_COLD void spew(const char* fmt, va_list va) MOZ_FORMAT_PRINTF(2, 0); 250 #endif 251 }; 252 253 } // namespace jit 254 } // namespace js 255 256 #endif /* jit_x86_shared_AssemblerBuffer_x86_shared_h */ 257