tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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