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