MozBaseAssembler-vixl.h (12683B)
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: 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 } 100 ~MozBaseAssembler() 101 { 102 #ifdef JS_DISASM_ARM64 103 spew_.spewOrphans(); 104 #endif 105 } 106 107 public: 108 // Return the Instruction at a given byte offset. 109 Instruction* getInstructionAt(BufferOffset offset) { 110 return armbuffer_.getInst(offset); 111 } 112 113 // Return the byte offset of a bound label. 114 template <typename T> 115 inline T GetLabelByteOffset(const js::jit::Label* label) { 116 VIXL_ASSERT(label->bound()); 117 static_assert(sizeof(T) >= sizeof(uint32_t)); 118 return reinterpret_cast<T>(label->offset()); 119 } 120 121 protected: 122 // Get the buffer offset of the next inserted instruction. This may flush 123 // constant pools. 124 BufferOffset nextInstrOffset() { 125 return armbuffer_.nextInstrOffset(); 126 } 127 128 // Get the next usable buffer offset. Note that a constant pool may be placed 129 // here before the next instruction is emitted. 130 BufferOffset nextOffset() const { 131 return armbuffer_.nextOffset(); 132 } 133 134 // Allocate memory in the buffer by forwarding to armbuffer_. 135 // Propagate OOM errors. 136 BufferOffset allocLiteralLoadEntry(size_t numInst, unsigned numPoolEntries, 137 uint8_t* inst, uint8_t* data, 138 const LiteralDoc& doc = LiteralDoc(), 139 ARMBuffer::PoolEntry* pe = nullptr) 140 { 141 MOZ_ASSERT(inst); 142 MOZ_ASSERT(numInst == 1); /* If not, then fix disassembly */ 143 BufferOffset offset = armbuffer_.allocEntry(numInst, numPoolEntries, inst, 144 data, pe); 145 propagateOOM(offset.assigned()); 146 #ifdef JS_DISASM_ARM64 147 Instruction* instruction = armbuffer_.getInstOrNull(offset); 148 if (instruction) 149 spewLiteralLoad(offset, 150 reinterpret_cast<vixl::Instruction*>(instruction), doc); 151 #endif 152 return offset; 153 } 154 155 #ifdef JS_DISASM_ARM64 156 DisassemblerSpew spew_; 157 158 void spew(BufferOffset offs, const vixl::Instruction* instr) { 159 if (spew_.isDisabled() || !instr) 160 return; 161 162 char buffer[2048]; 163 DisassembleInstruction(buffer, sizeof(buffer), instr); 164 spew_.spew("%06" PRIx32 " %08" PRIx32 "%s%s", 165 (uint32_t)offs.getOffset(), 166 instr->InstructionBits(), InstrIndent, buffer); 167 } 168 169 void spewBranch(BufferOffset offs, 170 const vixl::Instruction* instr, const LabelDoc& target) { 171 if (spew_.isDisabled() || !instr) 172 return; 173 174 char buffer[2048]; 175 DisassembleInstruction(buffer, sizeof(buffer), instr); 176 177 char labelBuf[128]; 178 labelBuf[0] = 0; 179 180 bool hasTarget = target.valid; 181 if (!hasTarget) 182 SprintfLiteral(labelBuf, "-> (link-time target)"); 183 184 if (instr->IsImmBranch() && hasTarget) { 185 // The target information in the instruction is likely garbage, so remove it. 186 // The target label will in any case be printed if we have it. 187 // 188 // The format of the instruction disassembly is /.*#.*/. Strip the # and later. 189 size_t i; 190 const size_t BUFLEN = sizeof(buffer)-1; 191 for ( i=0 ; i < BUFLEN && buffer[i] && buffer[i] != '#' ; i++ ) 192 ; 193 buffer[i] = 0; 194 195 SprintfLiteral(labelBuf, "-> %d%s", target.doc, !target.bound ? "f" : ""); 196 hasTarget = false; 197 } 198 199 spew_.spew("%06" PRIx32 " %08" PRIx32 "%s%s%s", 200 (uint32_t)offs.getOffset(), 201 instr->InstructionBits(), InstrIndent, buffer, labelBuf); 202 203 if (hasTarget) 204 spew_.spewRef(target); 205 } 206 207 void spewLiteralLoad(BufferOffset offs, 208 const vixl::Instruction* instr, const LiteralDoc& doc) { 209 if (spew_.isDisabled() || !instr) 210 return; 211 212 char buffer[2048]; 213 DisassembleInstruction(buffer, sizeof(buffer), instr); 214 215 char litbuf[2048]; 216 spew_.formatLiteral(doc, litbuf, sizeof(litbuf)); 217 218 // The instruction will have the form /^.*pc\+0/ followed by junk that we 219 // don't need; try to strip it. 220 221 char *probe = strstr(buffer, "pc+0"); 222 if (probe) 223 *(probe + 4) = 0; 224 spew_.spew("%06" PRIx32 " %08" PRIx32 "%s%s ; .const %s", 225 (uint32_t)offs.getOffset(), 226 instr->InstructionBits(), InstrIndent, buffer, litbuf); 227 } 228 229 LabelDoc refLabel(Label* label) { 230 if (spew_.isDisabled()) 231 return LabelDoc(); 232 233 return spew_.refLabel(label); 234 } 235 #else 236 LabelDoc refLabel(js::jit::Label*) { 237 return LabelDoc(); 238 } 239 #endif 240 241 // Emit the instruction, returning its offset. 242 BufferOffset Emit(Instr instruction, bool isBranch = false) { 243 static_assert(sizeof(instruction) == kInstructionSize); 244 // TODO: isBranch is obsolete and should be removed. 245 (void)isBranch; 246 MOZ_ASSERT(hasCreator()); 247 BufferOffset offs = armbuffer_.putInt(*(uint32_t*)(&instruction)); 248 #ifdef JS_DISASM_ARM64 249 if (!isBranch) 250 spew(offs, armbuffer_.getInstOrNull(offs)); 251 #endif 252 return offs; 253 } 254 255 BufferOffset EmitBranch(Instr instruction, const LabelDoc& doc) { 256 BufferOffset offs = Emit(instruction, true); 257 #ifdef JS_DISASM_ARM64 258 spewBranch(offs, armbuffer_.getInstOrNull(offs), doc); 259 #endif 260 return offs; 261 } 262 263 public: 264 // Emit the instruction at |at|. 265 static void Emit(Instruction* at, Instr instruction) { 266 static_assert(sizeof(instruction) == kInstructionSize); 267 memcpy(at, &instruction, sizeof(instruction)); 268 } 269 270 static void EmitBranch(Instruction* at, Instr instruction) { 271 // TODO: Assert that the buffer already has the instruction marked as a branch. 272 Emit(at, instruction); 273 } 274 275 // Emit data inline in the instruction stream. 276 BufferOffset EmitData(void const * data, unsigned size) { 277 VIXL_ASSERT(size % 4 == 0); 278 MOZ_ASSERT(hasCreator()); 279 return armbuffer_.allocEntry(size / sizeof(uint32_t), 0, (uint8_t*)(data), nullptr); 280 } 281 282 public: 283 // Size of the code generated in bytes, including pools. 284 size_t SizeOfCodeGenerated() const { 285 return armbuffer_.size(); 286 } 287 288 // Move the pool into the instruction stream. 289 void flushBuffer() { 290 armbuffer_.flushPool(); 291 } 292 293 // Inhibit pool flushing for the given number of instructions. 294 // Generating more than |maxInst| instructions in a no-pool region 295 // triggers an assertion within the ARMBuffer. 296 // Does not nest. 297 void enterNoPool(size_t maxInst) { 298 armbuffer_.enterNoPool(maxInst); 299 } 300 301 // Marks the end of a no-pool region. 302 void leaveNoPool() { 303 armbuffer_.leaveNoPool(); 304 } 305 306 void enterNoNops() { 307 armbuffer_.enterNoNops(); 308 } 309 void leaveNoNops() { 310 armbuffer_.leaveNoNops(); 311 } 312 313 public: 314 // Static interface used by IonAssemblerBufferWithConstantPools. 315 static void InsertIndexIntoTag(uint8_t* load, uint32_t index); 316 static bool PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr); 317 static void PatchShortRangeBranchToVeneer(ARMBuffer*, unsigned rangeIdx, BufferOffset deadline, 318 BufferOffset veneer); 319 static uint32_t PlaceConstantPoolBarrier(int offset); 320 321 static void WritePoolHeader(uint8_t* start, js::jit::Pool* p, bool isNatural); 322 static void WritePoolFooter(uint8_t* start, js::jit::Pool* p, bool isNatural); 323 static void WritePoolGuard(BufferOffset branch, Instruction* inst, BufferOffset dest); 324 325 protected: 326 // Functions for managing Labels and linked lists of Label uses. 327 328 // Get the next Label user in the linked list of Label uses. 329 // Return an unassigned BufferOffset when the end of the list is reached. 330 BufferOffset NextLink(BufferOffset cur); 331 332 // Patch the instruction at cur to link to the instruction at next. 333 void SetNextLink(BufferOffset cur, BufferOffset next); 334 335 // Link the current (not-yet-emitted) instruction to the specified label, 336 // then return a raw offset to be encoded in the instruction. 337 ptrdiff_t LinkAndGetByteOffsetTo(BufferOffset branch, js::jit::Label* label); 338 ptrdiff_t LinkAndGetInstructionOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange, 339 js::jit::Label* label); 340 ptrdiff_t LinkAndGetPageOffsetTo(BufferOffset branch, js::jit::Label* label); 341 342 // A common implementation for the LinkAndGet<Type>OffsetTo helpers. 343 ptrdiff_t LinkAndGetOffsetTo(BufferOffset branch, ImmBranchRangeType branchRange, 344 unsigned elementSizeBits, js::jit::Label* label); 345 346 protected: 347 // The buffer into which code and relocation info are generated. 348 ARMBuffer armbuffer_; 349 }; 350 351 352 } // namespace vixl 353 354 355 #endif // jit_arm64_vixl_MozBaseAssembler_vixl_h