1 // [AsmJit]
2 // Complete JIT Assembler for C++ Language.
3 //
4 // [License]
5 // Zlib - See COPYING file in this package.
6 
7 // [Guard]
8 #ifndef _ASMJIT_CORE_ASSEMBLER_H
9 #define _ASMJIT_CORE_ASSEMBLER_H
10 
11 // [Dependencies - AsmJit]
12 #include "../core/buffer.h"
13 #include "../core/context.h"
14 #include "../core/defs.h"
15 #include "../core/logger.h"
16 #include "../core/podvector.h"
17 #include "../core/zonememory.h"
18 
19 // [Api-Begin]
20 #include "../core/apibegin.h"
21 
22 namespace AsmJit {
23 
24 //! @addtogroup AsmJit_Core
25 //! @{
26 
27 // ============================================================================
28 // [AsmJit::Assembler]
29 // ============================================================================
30 
31 //! @brief Base class for @ref Assembler.
32 //!
33 //! This class implements core setialization API only. The platform specific
34 //! methods and intrinsics is implemented by derived classes.
35 //!
36 //! @sa @c Assembler.
37 struct Assembler
38 {
39   // --------------------------------------------------------------------------
40   // [Construction / Destruction]
41   // --------------------------------------------------------------------------
42 
43   //! @brief Creates Assembler instance.
44   ASMJIT_API Assembler(Context* context);
45   //! @brief Destroys Assembler instance
46   ASMJIT_API virtual ~Assembler();
47 
48   // --------------------------------------------------------------------------
49   // [LabelLink]
50   // --------------------------------------------------------------------------
51 
52   //! @brief Data structure used to link linked-labels.
53   struct LabelLink
54   {
55     //! @brief Previous link.
56     LabelLink* prev;
57     //! @brief Offset.
58     sysint_t offset;
59     //! @brief Inlined displacement.
60     sysint_t displacement;
61     //! @brief RelocId if link must be absolute when relocated.
62     sysint_t relocId;
63   };
64 
65   // --------------------------------------------------------------------------
66   // [LabelData]
67   // --------------------------------------------------------------------------
68 
69   //! @brief Label data.
70   struct LabelData
71   {
72     //! @brief Label offset.
73     sysint_t offset;
74     //! @brief Label links chain.
75     LabelLink* links;
76   };
77 
78   // --------------------------------------------------------------------------
79   // [RelocData]
80   // --------------------------------------------------------------------------
81 
82   // X86 architecture uses 32-bit absolute addressing model by memory operands,
83   // but 64-bit mode uses relative addressing model (RIP + displacement). In
84   // code we are always using relative addressing model for referencing labels
85   // and embedded data. In 32-bit mode we must patch all references to absolute
86   // address before we can call generated function. We are patching only memory
87   // operands.
88 
89   //! @brief Code relocation data (relative vs absolute addresses).
90   struct RelocData
91   {
92     //! @brief Type of relocation.
93     uint32_t type;
94     //! @brief Size of relocation (4 or 8 bytes).
95     uint32_t size;
96     //! @brief Offset from code begin address.
97     sysint_t offset;
98 
99     //! @brief Relative displacement or absolute address.
100     union
101     {
102       //! @brief Relative displacement from code begin address (not to @c offset).
103       sysint_t destination;
104       //! @brief Absolute address where to jump;
105       void* address;
106     };
107   };
108 
109   // --------------------------------------------------------------------------
110   // [Context]
111   // --------------------------------------------------------------------------
112 
113   //! @brief Get code generator.
getContextAssembler114   inline Context* getContext() const
115   { return _context; }
116 
117   // --------------------------------------------------------------------------
118   // [Memory Management]
119   // --------------------------------------------------------------------------
120 
121   //! @brief Get zone memory manager.
getZoneMemoryAssembler122   inline ZoneMemory* getZoneMemory() const
123   { return const_cast<ZoneMemory*>(&_zoneMemory); }
124 
125   // --------------------------------------------------------------------------
126   // [Logging]
127   // --------------------------------------------------------------------------
128 
129   //! @brief Get logger.
getLoggerAssembler130   inline Logger* getLogger() const
131   { return _logger; }
132 
133   //! @brief Set logger to @a logger.
134   ASMJIT_API virtual void setLogger(Logger* logger);
135 
136   // --------------------------------------------------------------------------
137   // [Error Handling]
138   // --------------------------------------------------------------------------
139 
140   //! @brief Get error code.
getErrorAssembler141   inline uint32_t getError() const
142   { return _error; }
143 
144   //! @brief Set error code.
145   //!
146   //! This method is virtual, because higher classes can use it to catch all
147   //! errors.
148   ASMJIT_API virtual void setError(uint32_t error);
149 
150   // --------------------------------------------------------------------------
151   // [Properties]
152   // --------------------------------------------------------------------------
153 
154   //! @brief Get assembler property.
155   ASMJIT_API virtual uint32_t getProperty(uint32_t propertyId) const;
156 
157   //! @brief Set assembler property.
158   ASMJIT_API virtual void setProperty(uint32_t propertyId, uint32_t value);
159 
160   // --------------------------------------------------------------------------
161   // [Capacity]
162   // --------------------------------------------------------------------------
163 
164   //! @brief Get capacity of internal code buffer.
getCapacityAssembler165   inline size_t getCapacity() const
166   { return _buffer.getCapacity(); }
167 
168   // --------------------------------------------------------------------------
169   // [Offset]
170   // --------------------------------------------------------------------------
171 
172   //! @brief Return current offset in buffer.
getOffsetAssembler173   inline size_t getOffset() const
174   { return _buffer.getOffset(); }
175 
176   //! @brief Set offset to @a o and returns previous offset.
177   //!
178   //! This method can be used to truncate code (previous offset is not
179   //! recorded) or to overwrite instruction stream at position @a o.
180   //!
181   //! @return Previous offset value that can be uset to set offset back later.
toOffsetAssembler182   inline size_t toOffset(size_t o)
183   { return _buffer.toOffset(o); }
184 
185   // --------------------------------------------------------------------------
186   // [GetCode / GetCodeSize]
187   // --------------------------------------------------------------------------
188 
189   //! @brief Return start of assembler code buffer.
190   //!
191   //! Note that buffer address can change if you emit instruction or something
192   //! else. Use this pointer only when you finished or make sure you do not
193   //! use returned pointer after emitting.
getCodeAssembler194   inline uint8_t* getCode() const
195   { return _buffer.getData(); }
196 
197   //! @brief Return current offset in buffer (same as <code>getOffset() + getTramplineSize()</code>).
getCodeSizeAssembler198   inline size_t getCodeSize() const
199   { return _buffer.getOffset() + getTrampolineSize(); }
200 
201   // --------------------------------------------------------------------------
202   // [TakeCode]
203   // --------------------------------------------------------------------------
204 
205   //! @brief Take internal code buffer and NULL all pointers (you take the ownership).
206   ASMJIT_API uint8_t* takeCode();
207 
208   // --------------------------------------------------------------------------
209   // [Clear / Reset]
210   // --------------------------------------------------------------------------
211 
212   //! @brief Clear everything, but not deallocate buffers.
213   ASMJIT_API void clear();
214 
215   //! @brief Reset everything (means also to free all buffers).
216   ASMJIT_API void reset();
217 
218   //! @brief Called by clear() and reset() to clear all data related to derived
219   //! class implementation.
220   ASMJIT_API virtual void _purge();
221 
222   // --------------------------------------------------------------------------
223   // [EnsureSpace]
224   // --------------------------------------------------------------------------
225 
226   //! @brief Ensure space for next instruction.
227   //!
228   //! Note that this method can return false. It's rare and probably you never
229   //! get this, but in some situations it's still possible.
ensureSpaceAssembler230   inline bool ensureSpace()
231   { return _buffer.ensureSpace(); }
232 
233   // --------------------------------------------------------------------------
234   // [GetTrampolineSize]
235   // --------------------------------------------------------------------------
236 
237   //! @brief Get size of all possible trampolines needed to successfuly generate
238   //! relative jumps to absolute addresses. This value is only non-zero if jmp
239   //! of call instructions were used with immediate operand (this means jump or
240   //! call absolute address directly).
241   //!
242   //! Currently only _emitJmpOrCallReloc() method can increase trampoline size
243   //! value.
getTrampolineSizeAssembler244   inline size_t getTrampolineSize() const
245   { return _trampolineSize; }
246 
247   // --------------------------------------------------------------------------
248   // [Buffer - Getters]
249   // --------------------------------------------------------------------------
250 
251   //! @brief Get byte at position @a pos.
getByteAtAssembler252   inline uint8_t getByteAt(size_t pos) const
253   { return _buffer.getByteAt(pos); }
254 
255   //! @brief Get word at position @a pos.
getWordAtAssembler256   inline uint16_t getWordAt(size_t pos) const
257   { return _buffer.getWordAt(pos); }
258 
259   //! @brief Get dword at position @a pos.
getDWordAtAssembler260   inline uint32_t getDWordAt(size_t pos) const
261   { return _buffer.getDWordAt(pos); }
262 
263   //! @brief Get qword at position @a pos.
getQWordAtAssembler264   inline uint64_t getQWordAt(size_t pos) const
265   { return _buffer.getQWordAt(pos); }
266 
267   //! @brief Get int32_t at position @a pos.
getInt32AtAssembler268   inline int32_t getInt32At(size_t pos) const
269   { return (int32_t)_buffer.getDWordAt(pos); }
270 
271   //! @brief Get int64_t at position @a pos.
getInt64AtAssembler272   inline int64_t getInt64At(size_t pos) const
273   { return (int64_t)_buffer.getQWordAt(pos); }
274 
275   //! @brief Get intptr_t at position @a pos.
getIntPtrTAtAssembler276   inline intptr_t getIntPtrTAt(size_t pos) const
277   { return _buffer.getIntPtrTAt(pos); }
278 
279   //! @brief Get uintptr_t at position @a pos.
getUIntPtrTAtAssembler280   inline uintptr_t getUIntPtrTAt(size_t pos) const
281   { return _buffer.getUIntPtrTAt(pos); }
282 
283   //! @brief Get uintptr_t at position @a pos.
getSizeTAtAssembler284   inline size_t getSizeTAt(size_t pos) const
285   { return _buffer.getSizeTAt(pos); }
286 
287   // --------------------------------------------------------------------------
288   // [Buffer - Setters]
289   // --------------------------------------------------------------------------
290 
291   //! @brief Set byte at position @a pos.
setByteAtAssembler292   inline void setByteAt(size_t pos, uint8_t x)
293   { _buffer.setByteAt(pos, x); }
294 
295   //! @brief Set word at position @a pos.
setWordAtAssembler296   inline void setWordAt(size_t pos, uint16_t x)
297   { _buffer.setWordAt(pos, x); }
298 
299   //! @brief Set dword at position @a pos.
setDWordAtAssembler300   inline void setDWordAt(size_t pos, uint32_t x)
301   { _buffer.setDWordAt(pos, x); }
302 
303   //! @brief Set qword at position @a pos.
setQWordAtAssembler304   inline void setQWordAt(size_t pos, uint64_t x)
305   { _buffer.setQWordAt(pos, x); }
306 
307   //! @brief Set int32_t at position @a pos.
setInt32AtAssembler308   inline void setInt32At(size_t pos, int32_t x)
309   { _buffer.setDWordAt(pos, (uint32_t)x); }
310 
311   //! @brief Set int64_t at position @a pos.
setInt64AtAssembler312   inline void setInt64At(size_t pos, int64_t x)
313   { _buffer.setQWordAt(pos, (uint64_t)x); }
314 
315   //! @brief Set intptr_t at position @a pos.
setIntPtrTAtAssembler316   inline void setIntPtrTAt(size_t pos, intptr_t x)
317   { _buffer.setIntPtrTAt(pos, x); }
318 
319   //! @brief Set uintptr_t at position @a pos.
setUInt64AtAssembler320   inline void setUInt64At(size_t pos, uintptr_t x)
321   { _buffer.setUIntPtrTAt(pos, x); }
322 
323   //! @brief Set size_t at position @a pos.
setSizeTAtAssembler324   inline void setSizeTAt(size_t pos, size_t x)
325   { _buffer.setSizeTAt(pos, x); }
326 
327   // --------------------------------------------------------------------------
328   // [CanEmit]
329   // --------------------------------------------------------------------------
330 
331   //! @brief Get whether the instruction can be emitted.
332   //!
333   //! This function behaves like @c ensureSpace(), but it also checks if
334   //! assembler is in error state and in that case it returns @c false.
335   //! Assembler internally always uses this function before new instruction is
336   //! emitted.
337   //!
338   //! It's implemented like:
339   //!   <code>return ensureSpace() && !getError();</code>
canEmitAssembler340   inline bool canEmit()
341   {
342     // If there is an error, we can't emit another instruction until last error
343     // is cleared by calling @c setError(kErrorOk). If something caused the
344     // error while generating code it's probably fatal in all cases. You can't
345     // use generated code anymore, because you are not sure about the status.
346     if (_error)
347       return false;
348 
349     // The ensureSpace() method returns true on success and false on failure. We
350     // are catching return value and setting error code here.
351     if (ensureSpace())
352       return true;
353 
354     // If we are here, there is memory allocation error. Note that this is HEAP
355     // allocation error, virtual allocation error can be caused only by
356     // AsmJit::VirtualMemory class!
357     setError(kErrorNoHeapMemory);
358     return false;
359   }
360 
361   // --------------------------------------------------------------------------
362   // [Emit]
363   //
364   // These functions are not protected against buffer overrun. Each place of
365   // code which calls these functions ensures that there is some space using
366   // canEmit() method. Emitters are internally protected in AsmJit::Buffer,
367   // but only in debug builds.
368   // --------------------------------------------------------------------------
369 
370   //! @brief Emit Byte to internal buffer.
_emitByteAssembler371   inline void _emitByte(uint8_t x)
372   { _buffer.emitByte(x); }
373 
374   //! @brief Emit word (2 bytes) to internal buffer.
_emitWordAssembler375   inline void _emitWord(uint16_t x)
376   { _buffer.emitWord(x); }
377 
378   //! @brief Emit dword (4 bytes) to internal buffer.
_emitDWordAssembler379   inline void _emitDWord(uint32_t x)
380   { _buffer.emitDWord(x); }
381 
382   //! @brief Emit qword (8 bytes) to internal buffer.
_emitQWordAssembler383   inline void _emitQWord(uint64_t x)
384   { _buffer.emitQWord(x); }
385 
386   //! @brief Emit Int32 (4 bytes) to internal buffer.
_emitInt32Assembler387   inline void _emitInt32(int32_t x)
388   { _buffer.emitDWord((uint32_t)x); }
389 
390   //! @brief Emit Int64 (8 bytes) to internal buffer.
_emitInt64Assembler391   inline void _emitInt64(int64_t x)
392   { _buffer.emitQWord((uint64_t)x); }
393 
394   //! @brief Emit intptr_t (4 or 8 bytes) to internal buffer.
_emitIntPtrTAssembler395   inline void _emitIntPtrT(intptr_t x)
396   { _buffer.emitIntPtrT(x); }
397 
398   //! @brief Emit uintptr_t (4 or 8 bytes) to internal buffer.
_emitUIntPtrTAssembler399   inline void _emitUIntPtrT(uintptr_t x)
400   { _buffer.emitUIntPtrT(x); }
401 
402   //! @brief Emit size_t (4 or 8 bytes) to internal buffer.
_emitSizeTAssembler403   inline void _emitSizeT(size_t x)
404   { _buffer.emitSizeT(x); }
405 
406   //! @brief Embed data into instruction stream.
407   ASMJIT_API void embed(const void* data, size_t len);
408 
409   // --------------------------------------------------------------------------
410   // [Reloc]
411   // --------------------------------------------------------------------------
412 
413   //! @brief Relocate code to a given address @a dst.
414   //!
415   //! @param dst Where the relocated code should me stored. The pointer can be
416   //! address returned by virtual memory allocator or your own address if you
417   //! want only to store the code for later reuse (or load, etc...).
418   //! @param addressBase Base address used for relocation. When using JIT code
419   //! generation, this will be the same as @a dst, only casted to system
420   //! integer type. But when generating code for remote process then the value
421   //! can be different.
422   //!
423   //! @retval The bytes used. Code-generator can create trampolines which are
424   //! used when calling other functions inside the JIT code. However, these
425   //! trampolines can be unused so the relocCode() returns the exact size needed
426   //! for the function.
427   //!
428   //! A given buffer will be overwritten, to get number of bytes required use
429   //! @c getCodeSize().
430   virtual size_t relocCode(void* dst, sysuint_t addressBase) const = 0;
431 
432   //! @brief Simplifed version of @c relocCode() method designed for JIT.
433   //!
434   //! @overload
relocCodeAssembler435   inline size_t relocCode(void* dst) const
436   { return relocCode(dst, (uintptr_t)dst); }
437 
438   // --------------------------------------------------------------------------
439   // [Make]
440   // --------------------------------------------------------------------------
441 
442   //! @brief Make is convenience method to make currently serialized code and
443   //! return pointer to generated function.
444   //!
445   //! What you need is only to cast this pointer to your function type and call
446   //! it. Note that if there was an error and calling @c getError() method not
447   //! returns @c kErrorOk (zero) then this function always return @c NULL and
448   //! error value remains the same.
449   virtual void* make() = 0;
450 
451   // --------------------------------------------------------------------------
452   // [Helpers]
453   // --------------------------------------------------------------------------
454 
455   ASMJIT_API LabelLink* _newLabelLink();
456 
457   // --------------------------------------------------------------------------
458   // [Members]
459   // --------------------------------------------------------------------------
460 
461   //! @brief ZoneMemory management.
462   ZoneMemory _zoneMemory;
463   //! @brief Binary code buffer.
464   Buffer _buffer;
465 
466   //! @brief Context (for example @ref JitContext).
467   Context* _context;
468   //! @brief Logger.
469   Logger* _logger;
470 
471   //! @brief Error code.
472   uint32_t _error;
473   //! @brief Properties.
474   uint32_t _properties;
475   //! @brief Emit flags for next instruction (cleared after emit).
476   uint32_t _emitOptions;
477   //! @brief Size of possible trampolines.
478   uint32_t _trampolineSize;
479 
480   //! @brief Inline comment that will be logged by the next instruction and
481   //! set to NULL.
482   const char* _inlineComment;
483   //! @brief Linked list of unused links (@c LabelLink* structures)
484   LabelLink* _unusedLinks;
485 
486   //! @brief Labels data.
487   PodVector<LabelData> _labels;
488   //! @brief Relocations data.
489   PodVector<RelocData> _relocData;
490 };
491 
492 //! @}
493 
494 } // AsmJit namespace
495 
496 // [Api-End]
497 #include "../core/apiend.h"
498 
499 // [Guard]
500 #endif // _ASMJIT_CORE_ASSEMBLER_H
501