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