1 // Copyright 2013, 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 #ifndef jit_arm64_vixl_MozBaseAssembler_vixl_h
28 #define jit_arm64_vixl_MozBaseAssembler_vixl_h
29 
30 
31 #include "mozilla/Assertions.h"  // MOZ_ASSERT
32 #include "mozilla/Sprintf.h"     // SprintfLiteral
33 
34 #include <stddef.h>  // size_t
35 #include <stdint.h>  // uint8_t, uint32_t
36 #include <string.h>  // strstr
37 
38 #include "jit/arm64/vixl/Constants-vixl.h"     // vixl::{HINT, NOP, ImmHint_offset}
39 #include "jit/arm64/vixl/Globals-vixl.h"       // VIXL_ASSERT
40 #include "jit/arm64/vixl/Instructions-vixl.h"  // vixl::{Instruction, NumShortBranchRangeTypes, Instr, ImmBranchRangeType}
41 
42 #include "jit/Label.h"                       // jit::Label
43 #include "jit/shared/Assembler-shared.h"     // jit::AssemblerShared
44 #include "jit/shared/Disassembler-shared.h"  // jit::DisassemblerSpew
45 #include "jit/shared/IonAssemblerBuffer.h"   // jit::BufferOffset
46 #include "jit/shared/IonAssemblerBufferWithConstantPools.h"  // jit::AssemblerBufferWithConstantPools
47 
48 namespace vixl {
49 
50 
51 using js::jit::BufferOffset;
52 using js::jit::DisassemblerSpew;
53 using js::jit::Label;
54 
55 using LabelDoc = DisassemblerSpew::LabelDoc;
56 using LiteralDoc = DisassemblerSpew::LiteralDoc;
57 
58 #ifdef JS_DISASM_ARM64
59 void DisassembleInstruction(char* buffer, size_t bufsize, const Instruction* instr);
60 #endif
61 
62 class MozBaseAssembler;
63 typedef js::jit::AssemblerBufferWithConstantPools<1024, 4, Instruction, MozBaseAssembler,
64                                                   NumShortBranchRangeTypes> ARMBuffer;
65 
66 // Base class for vixl::Assembler, for isolating Moz-specific changes to VIXL.
67 class MozBaseAssembler : public js::jit::AssemblerShared {
68   // Buffer initialization constants.
69   static const unsigned BufferGuardSize = 1;
70   static const unsigned BufferHeaderSize = 1;
71   static const size_t   BufferCodeAlignment = 8;
72   static const size_t   BufferMaxPoolOffset = 1024;
73   static const unsigned BufferPCBias = 0;
74   static const uint32_t BufferAlignmentFillInstruction = HINT | (NOP << ImmHint_offset);
75   static const uint32_t BufferNopFillInstruction = HINT | (NOP << ImmHint_offset);
76   static const unsigned BufferNumDebugNopsToInsert = 0;
77 
78 #ifdef JS_DISASM_ARM64
79   static constexpr const char* const InstrIndent = "        ";
80   static constexpr const char* const LabelIndent = "                 ";
81   static constexpr const char* const TargetIndent = "                    ";
82 #endif
83 
84  public:
MozBaseAssembler()85   MozBaseAssembler()
86     : armbuffer_(BufferGuardSize,
87                  BufferHeaderSize,
88                  BufferCodeAlignment,
89                  BufferMaxPoolOffset,
90                  BufferPCBias,
91                  BufferAlignmentFillInstruction,
92                  BufferNopFillInstruction,
93                  BufferNumDebugNopsToInsert)
94   {
95 #ifdef JS_DISASM_ARM64
96       spew_.setLabelIndent(LabelIndent);
97       spew_.setTargetIndent(TargetIndent);
98 #endif
99 }
~MozBaseAssembler()100   ~MozBaseAssembler()
101   {
102 #ifdef JS_DISASM_ARM64
103       spew_.spewOrphans();
104 #endif
105   }
106 
107  public:
108   // Helper function for use with the ARMBuffer.
109   // The MacroAssembler must create an AutoJitContextAlloc before initializing the buffer.
initWithAllocator()110   void initWithAllocator() {
111     armbuffer_.initWithAllocator();
112   }
113 
114   // Return the Instruction at a given byte offset.
getInstructionAt(BufferOffset offset)115   Instruction* getInstructionAt(BufferOffset offset) {
116     return armbuffer_.getInst(offset);
117   }
118 
119   // Return the byte offset of a bound label.
120   template <typename T>
GetLabelByteOffset(const js::jit::Label * label)121   inline T GetLabelByteOffset(const js::jit::Label* label) {
122     VIXL_ASSERT(label->bound());
123     static_assert(sizeof(T) >= sizeof(uint32_t));
124     return reinterpret_cast<T>(label->offset());
125   }
126 
127  protected:
128   // Get the buffer offset of the next inserted instruction. This may flush
129   // constant pools.
nextInstrOffset()130   BufferOffset nextInstrOffset() {
131     return armbuffer_.nextInstrOffset();
132   }
133 
134   // Get the next usable buffer offset. Note that a constant pool may be placed
135   // here before the next instruction is emitted.
nextOffset()136   BufferOffset nextOffset() const {
137     return armbuffer_.nextOffset();
138   }
139 
140   // Allocate memory in the buffer by forwarding to armbuffer_.
141   // Propagate OOM errors.
142   BufferOffset allocLiteralLoadEntry(size_t numInst, unsigned numPoolEntries,
143 				     uint8_t* inst, uint8_t* data,
144 				     const LiteralDoc& doc = LiteralDoc(),
145 				     ARMBuffer::PoolEntry* pe = nullptr)
146   {
147     MOZ_ASSERT(inst);
148     MOZ_ASSERT(numInst == 1);	/* If not, then fix disassembly */
149     BufferOffset offset = armbuffer_.allocEntry(numInst, numPoolEntries, inst,
150                                                 data, pe);
151     propagateOOM(offset.assigned());
152 #ifdef JS_DISASM_ARM64
153     Instruction* instruction = armbuffer_.getInstOrNull(offset);
154     if (instruction)
155         spewLiteralLoad(offset,
156                         reinterpret_cast<vixl::Instruction*>(instruction), doc);
157 #endif
158     return offset;
159   }
160 
161 #ifdef JS_DISASM_ARM64
162   DisassemblerSpew spew_;
163 
spew(BufferOffset offs,const vixl::Instruction * instr)164   void spew(BufferOffset offs, const vixl::Instruction* instr) {
165     if (spew_.isDisabled() || !instr)
166       return;
167 
168     char buffer[2048];
169     DisassembleInstruction(buffer, sizeof(buffer), instr);
170     spew_.spew("%06" PRIx32 " %08" PRIx32 "%s%s",
171                (uint32_t)offs.getOffset(),
172                instr->InstructionBits(), InstrIndent, buffer);
173   }
174 
spewBranch(BufferOffset offs,const vixl::Instruction * instr,const LabelDoc & target)175   void spewBranch(BufferOffset offs,
176                   const vixl::Instruction* instr, const LabelDoc& target) {
177     if (spew_.isDisabled() || !instr)
178       return;
179 
180     char buffer[2048];
181     DisassembleInstruction(buffer, sizeof(buffer), instr);
182 
183     char labelBuf[128];
184     labelBuf[0] = 0;
185 
186     bool hasTarget = target.valid;
187     if (!hasTarget)
188       SprintfLiteral(labelBuf, "-> (link-time target)");
189 
190     if (instr->IsImmBranch() && hasTarget) {
191       // The target information in the instruction is likely garbage, so remove it.
192       // The target label will in any case be printed if we have it.
193       //
194       // The format of the instruction disassembly is /.*#.*/.  Strip the # and later.
195       size_t i;
196       const size_t BUFLEN = sizeof(buffer)-1;
197       for ( i=0 ; i < BUFLEN && buffer[i] && buffer[i] != '#' ; i++ )
198 	;
199       buffer[i] = 0;
200 
201       SprintfLiteral(labelBuf, "-> %d%s", target.doc, !target.bound ? "f" : "");
202       hasTarget = false;
203     }
204 
205     spew_.spew("%06" PRIx32 " %08" PRIx32 "%s%s%s",
206                (uint32_t)offs.getOffset(),
207                instr->InstructionBits(), InstrIndent, buffer, labelBuf);
208 
209     if (hasTarget)
210       spew_.spewRef(target);
211   }
212 
spewLiteralLoad(BufferOffset offs,const vixl::Instruction * instr,const LiteralDoc & doc)213   void spewLiteralLoad(BufferOffset offs,
214                        const vixl::Instruction* instr, const LiteralDoc& doc) {
215     if (spew_.isDisabled() || !instr)
216       return;
217 
218     char buffer[2048];
219     DisassembleInstruction(buffer, sizeof(buffer), instr);
220 
221     char litbuf[2048];
222     spew_.formatLiteral(doc, litbuf, sizeof(litbuf));
223 
224     // The instruction will have the form /^.*pc\+0/ followed by junk that we
225     // don't need; try to strip it.
226 
227     char *probe = strstr(buffer, "pc+0");
228     if (probe)
229       *(probe + 4) = 0;
230     spew_.spew("%06" PRIx32 " %08" PRIx32 "%s%s    ; .const %s",
231                (uint32_t)offs.getOffset(),
232                instr->InstructionBits(), InstrIndent, buffer, litbuf);
233   }
234 
refLabel(Label * label)235   LabelDoc refLabel(Label* label) {
236     if (spew_.isDisabled())
237       return LabelDoc();
238 
239     return spew_.refLabel(label);
240   }
241 #else
refLabel(js::jit::Label *)242   LabelDoc refLabel(js::jit::Label*) {
243       return LabelDoc();
244   }
245 #endif
246 
247   // Emit the instruction, returning its offset.
248   BufferOffset Emit(Instr instruction, bool isBranch = false) {
249     static_assert(sizeof(instruction) == kInstructionSize);
250     // TODO: isBranch is obsolete and should be removed.
251     (void)isBranch;
252     MOZ_ASSERT(hasCreator());
253     BufferOffset offs = armbuffer_.putInt(*(uint32_t*)(&instruction));
254 #ifdef JS_DISASM_ARM64
255     if (!isBranch)
256         spew(offs, armbuffer_.getInstOrNull(offs));
257 #endif
258     return offs;
259   }
260 
EmitBranch(Instr instruction,const LabelDoc & doc)261   BufferOffset EmitBranch(Instr instruction, const LabelDoc& doc) {
262     BufferOffset offs = Emit(instruction, true);
263 #ifdef JS_DISASM_ARM64
264     spewBranch(offs, armbuffer_.getInstOrNull(offs), doc);
265 #endif
266     return offs;
267   }
268 
269  public:
270   // Emit the instruction at |at|.
Emit(Instruction * at,Instr instruction)271   static void Emit(Instruction* at, Instr instruction) {
272     static_assert(sizeof(instruction) == kInstructionSize);
273     memcpy(at, &instruction, sizeof(instruction));
274   }
275 
EmitBranch(Instruction * at,Instr instruction)276   static void EmitBranch(Instruction* at, Instr instruction) {
277     // TODO: Assert that the buffer already has the instruction marked as a branch.
278     Emit(at, instruction);
279   }
280 
281   // Emit data inline in the instruction stream.
EmitData(void const * data,unsigned size)282   BufferOffset EmitData(void const * data, unsigned size) {
283     VIXL_ASSERT(size % 4 == 0);
284     MOZ_ASSERT(hasCreator());
285     return armbuffer_.allocEntry(size / sizeof(uint32_t), 0, (uint8_t*)(data), nullptr);
286   }
287 
288  public:
289   // Size of the code generated in bytes, including pools.
SizeOfCodeGenerated()290   size_t SizeOfCodeGenerated() const {
291     return armbuffer_.size();
292   }
293 
294   // Move the pool into the instruction stream.
flushBuffer()295   void flushBuffer() {
296     armbuffer_.flushPool();
297   }
298 
299   // Inhibit pool flushing for the given number of instructions.
300   // Generating more than |maxInst| instructions in a no-pool region
301   // triggers an assertion within the ARMBuffer.
302   // Does not nest.
enterNoPool(size_t maxInst)303   void enterNoPool(size_t maxInst) {
304     armbuffer_.enterNoPool(maxInst);
305   }
306 
307   // Marks the end of a no-pool region.
leaveNoPool()308   void leaveNoPool() {
309     armbuffer_.leaveNoPool();
310   }
311 
enterNoNops()312   void enterNoNops() {
313     armbuffer_.enterNoNops();
314   }
leaveNoNops()315   void leaveNoNops() {
316     armbuffer_.leaveNoNops();
317   }
318 
319  public:
320   // Static interface used by IonAssemblerBufferWithConstantPools.
321   static void InsertIndexIntoTag(uint8_t* load, uint32_t index);
322   static bool PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
323   static void PatchShortRangeBranchToVeneer(ARMBuffer*, unsigned rangeIdx, BufferOffset deadline,
324                                             BufferOffset veneer);
325   static uint32_t PlaceConstantPoolBarrier(int offset);
326 
327   static void WritePoolHeader(uint8_t* start, js::jit::Pool* p, bool isNatural);
328   static void WritePoolFooter(uint8_t* start, js::jit::Pool* p, bool isNatural);
329   static void WritePoolGuard(BufferOffset branch, Instruction* inst, BufferOffset dest);
330 
331  protected:
332   // Functions for managing Labels and linked lists of Label uses.
333 
334   // Get the next Label user in the linked list of Label uses.
335   // Return an unassigned BufferOffset when the end of the list is reached.
336   BufferOffset NextLink(BufferOffset cur);
337 
338   // Patch the instruction at cur to link to the instruction at next.
339   void SetNextLink(BufferOffset cur, BufferOffset next);
340 
341   // Link the current (not-yet-emitted) instruction to the specified label,
342   // then return a raw offset to be encoded in the instruction.
343   ptrdiff_t LinkAndGetByteOffsetTo(BufferOffset branch, js::jit::Label* label);
344   ptrdiff_t LinkAndGetInstructionOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange,
345                                           js::jit::Label* label);
346   ptrdiff_t LinkAndGetPageOffsetTo(BufferOffset branch, js::jit::Label* label);
347 
348   // A common implementation for the LinkAndGet<Type>OffsetTo helpers.
349   ptrdiff_t LinkAndGetOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange,
350                                unsigned elementSizeBits, js::jit::Label* label);
351 
352  protected:
353   // The buffer into which code and relocation info are generated.
354   ARMBuffer armbuffer_;
355 };
356 
357 
358 }  // namespace vixl
359 
360 
361 #endif  // jit_arm64_vixl_MozBaseAssembler_vixl_h
362 
363