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 #include "jit/arm64/vixl/Constants-vixl.h"
31 #include "jit/arm64/vixl/Instructions-vixl.h"
32 
33 #include "jit/shared/Assembler-shared.h"
34 #include "jit/shared/IonAssemblerBufferWithConstantPools.h"
35 
36 namespace vixl {
37 
38 
39 using js::jit::BufferOffset;
40 
41 
42 class MozBaseAssembler;
43 typedef js::jit::AssemblerBufferWithConstantPools<1024, 4, Instruction, MozBaseAssembler,
44                                                   NumShortBranchRangeTypes> ARMBuffer;
45 
46 // Base class for vixl::Assembler, for isolating Moz-specific changes to VIXL.
47 class MozBaseAssembler : public js::jit::AssemblerShared {
48   // Buffer initialization constants.
49   static const unsigned BufferGuardSize = 1;
50   static const unsigned BufferHeaderSize = 1;
51   static const size_t   BufferCodeAlignment = 8;
52   static const size_t   BufferMaxPoolOffset = 1024;
53   static const unsigned BufferPCBias = 0;
54   static const uint32_t BufferAlignmentFillInstruction = BRK | (0xdead << ImmException_offset);
55   static const uint32_t BufferNopFillInstruction = HINT | (31 << Rt_offset);
56   static const unsigned BufferNumDebugNopsToInsert = 0;
57 
58  public:
MozBaseAssembler()59   MozBaseAssembler()
60     : armbuffer_(BufferGuardSize,
61                  BufferHeaderSize,
62                  BufferCodeAlignment,
63                  BufferMaxPoolOffset,
64                  BufferPCBias,
65                  BufferAlignmentFillInstruction,
66                  BufferNopFillInstruction,
67                  BufferNumDebugNopsToInsert)
68   { }
69 
70  public:
71   // Helper function for use with the ARMBuffer.
72   // The MacroAssembler must create an AutoJitContextAlloc before initializing the buffer.
initWithAllocator()73   void initWithAllocator() {
74     armbuffer_.initWithAllocator();
75   }
76 
77   // Return the Instruction at a given byte offset.
getInstructionAt(BufferOffset offset)78   Instruction* getInstructionAt(BufferOffset offset) {
79     return armbuffer_.getInst(offset);
80   }
81 
82   // Return the byte offset of a bound label.
83   template <typename T>
GetLabelByteOffset(const js::jit::Label * label)84   inline T GetLabelByteOffset(const js::jit::Label* label) {
85     VIXL_ASSERT(label->bound());
86     JS_STATIC_ASSERT(sizeof(T) >= sizeof(uint32_t));
87     return reinterpret_cast<T>(label->offset());
88   }
89 
90  protected:
91   // Get the buffer offset of the next inserted instruction. This may flush
92   // constant pools.
nextInstrOffset()93   BufferOffset nextInstrOffset() {
94     return armbuffer_.nextInstrOffset();
95   }
96 
97   // Get the next usable buffer offset. Note that a constant pool may be placed
98   // here before the next instruction is emitted.
nextOffset()99   BufferOffset nextOffset() const {
100     return armbuffer_.nextOffset();
101   }
102 
103   // Allocate memory in the buffer by forwarding to armbuffer_.
104   // Propagate OOM errors.
105   BufferOffset allocEntry(size_t numInst, unsigned numPoolEntries,
106                           uint8_t* inst, uint8_t* data,
107                           ARMBuffer::PoolEntry* pe = nullptr,
108                           bool markAsBranch = false)
109   {
110     BufferOffset offset = armbuffer_.allocEntry(numInst, numPoolEntries, inst,
111                                                 data, pe, markAsBranch);
112     propagateOOM(offset.assigned());
113     return offset;
114   }
115 
116   // Emit the instruction, returning its offset.
117   BufferOffset Emit(Instr instruction, bool isBranch = false) {
118     JS_STATIC_ASSERT(sizeof(instruction) == kInstructionSize);
119     return armbuffer_.putInt(*(uint32_t*)(&instruction), isBranch);
120   }
121 
EmitBranch(Instr instruction)122   BufferOffset EmitBranch(Instr instruction) {
123     return Emit(instruction, true);
124   }
125 
126  public:
127   // Emit the instruction at |at|.
Emit(Instruction * at,Instr instruction)128   static void Emit(Instruction* at, Instr instruction) {
129     JS_STATIC_ASSERT(sizeof(instruction) == kInstructionSize);
130     memcpy(at, &instruction, sizeof(instruction));
131   }
132 
EmitBranch(Instruction * at,Instr instruction)133   static void EmitBranch(Instruction* at, Instr instruction) {
134     // TODO: Assert that the buffer already has the instruction marked as a branch.
135     Emit(at, instruction);
136   }
137 
138   // Emit data inline in the instruction stream.
EmitData(void const * data,unsigned size)139   BufferOffset EmitData(void const * data, unsigned size) {
140     VIXL_ASSERT(size % 4 == 0);
141     return armbuffer_.allocEntry(size / sizeof(uint32_t), 0, (uint8_t*)(data), nullptr);
142   }
143 
144  public:
145   // Size of the code generated in bytes, including pools.
SizeOfCodeGenerated()146   size_t SizeOfCodeGenerated() const {
147     return armbuffer_.size();
148   }
149 
150   // Move the pool into the instruction stream.
flushBuffer()151   void flushBuffer() {
152     armbuffer_.flushPool();
153   }
154 
155   // Inhibit pool flushing for the given number of instructions.
156   // Generating more than |maxInst| instructions in a no-pool region
157   // triggers an assertion within the ARMBuffer.
158   // Does not nest.
enterNoPool(size_t maxInst)159   void enterNoPool(size_t maxInst) {
160     armbuffer_.enterNoPool(maxInst);
161   }
162 
163   // Marks the end of a no-pool region.
leaveNoPool()164   void leaveNoPool() {
165     armbuffer_.leaveNoPool();
166   }
167 
168  public:
169   // Static interface used by IonAssemblerBufferWithConstantPools.
170   static void InsertIndexIntoTag(uint8_t* load, uint32_t index);
171   static bool PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
172   static void PatchShortRangeBranchToVeneer(ARMBuffer*, unsigned rangeIdx, BufferOffset deadline,
173                                             BufferOffset veneer);
174   static uint32_t PlaceConstantPoolBarrier(int offset);
175 
176   static void WritePoolHeader(uint8_t* start, js::jit::Pool* p, bool isNatural);
177   static void WritePoolFooter(uint8_t* start, js::jit::Pool* p, bool isNatural);
178   static void WritePoolGuard(BufferOffset branch, Instruction* inst, BufferOffset dest);
179 
180   static ptrdiff_t GetBranchOffset(const Instruction* i);
181   static void RetargetNearBranch(Instruction* i, int offset, Condition cond, bool final = true);
182   static void RetargetNearBranch(Instruction* i, int offset, bool final = true);
183   static void RetargetFarBranch(Instruction* i, uint8_t** slot, uint8_t* dest, Condition cond);
184 
185  protected:
186   // Functions for managing Labels and linked lists of Label uses.
187 
188   // Get the next Label user in the linked list of Label uses.
189   // Return an unassigned BufferOffset when the end of the list is reached.
190   BufferOffset NextLink(BufferOffset cur);
191 
192   // Patch the instruction at cur to link to the instruction at next.
193   void SetNextLink(BufferOffset cur, BufferOffset next);
194 
195   // Link the current (not-yet-emitted) instruction to the specified label,
196   // then return a raw offset to be encoded in the instruction.
197   ptrdiff_t LinkAndGetByteOffsetTo(BufferOffset branch, js::jit::Label* label);
198   ptrdiff_t LinkAndGetInstructionOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange,
199                                           js::jit::Label* label);
200   ptrdiff_t LinkAndGetPageOffsetTo(BufferOffset branch, js::jit::Label* label);
201 
202   // A common implementation for the LinkAndGet<Type>OffsetTo helpers.
203   ptrdiff_t LinkAndGetOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange,
204                                unsigned elementSizeBits, js::jit::Label* label);
205 
206  protected:
207   // The buffer into which code and relocation info are generated.
208   ARMBuffer armbuffer_;
209 };
210 
211 
212 }  // namespace vixl
213 
214 
215 #endif  // jit_arm64_vixl_MozBaseAssembler_vixl_h
216 
217