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