Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/js/src/jit/riscv64/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 55 kB image not shown  

Quelle  Assembler-riscv64.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


// Copyright (c) 1994-2006 Sun Microsystems Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistribution in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// - Neither the name of Sun Microsystems or the names of contributors may
// be used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// The original source code covered by the above license above has been
// modified significantly by Google Inc.
// Copyright 2021 the V8 project authors. All rights reserved.
#include "jit/riscv64/Assembler-riscv64.h"

#include "mozilla/DebugOnly.h"
#include "mozilla/Maybe.h"

#include "gc/Marking.h"
#include "jit/AutoWritableJitCode.h"
#include "jit/ExecutableAllocator.h"
#include "jit/riscv64/disasm/Disasm-riscv64.h"
#include "vm/Realm.h"

using mozilla::DebugOnly;
namespace js {
namespace jit {

#define UNIMPLEMENTED_RISCV() MOZ_CRASH("RISC_V not implemented");

bool Assembler::FLAG_riscv_debug = false;

void Assembler::nop() { addi(ToRegister(0), ToRegister(0), 0); }

// Size of the instruction stream, in bytes.
size_t Assembler::size() const { return m_buffer.size(); }

bool Assembler::swapBuffer(wasm::Bytes& bytes) {
  // For now, specialize to the one use case. As long as wasm::Bytes is a
  // Vector, not a linked-list of chunks, there's not much we can do other
  // than copy.
  MOZ_ASSERT(bytes.empty());
  if (!bytes.resize(bytesNeeded())) {
    return false;
  }
  m_buffer.executableCopy(bytes.begin());
  return true;
}

// Size of the relocation table, in bytes.
size_t Assembler::jumpRelocationTableBytes() const {
  return jumpRelocations_.length();
}

size_t Assembler::dataRelocationTableBytes() const {
  return dataRelocations_.length();
}
// Size of the data table, in bytes.
size_t Assembler::bytesNeeded() const {
  return size() + jumpRelocationTableBytes() + dataRelocationTableBytes();
}

void Assembler::executableCopy(uint8_t* buffer) {
  MOZ_ASSERT(isFinished);
  m_buffer.executableCopy(buffer);
}

uint32_t Assembler::AsmPoolMaxOffset = 1024;

uint32_t Assembler::GetPoolMaxOffset() {
  static bool isSet = false;
  if (!isSet) {
    char* poolMaxOffsetStr = getenv("ASM_POOL_MAX_OFFSET");
    uint32_t poolMaxOffset;
    if (poolMaxOffsetStr &&
        sscanf(poolMaxOffsetStr, "%u", &poolMaxOffset) == 1) {
      AsmPoolMaxOffset = poolMaxOffset;
    }
    isSet = true;
  }
  return AsmPoolMaxOffset;
}

// Pool callbacks stuff:
void Assembler::InsertIndexIntoTag(uint8_t* load_, uint32_t index) {
  MOZ_CRASH("Unimplement");
}

void Assembler::PatchConstantPoolLoad(void* loadAddr, void* constPoolAddr) {
  MOZ_CRASH("Unimplement");
}

void Assembler::processCodeLabels(uint8_t* rawCode) {
  for (const CodeLabel& label : codeLabels_) {
    Bind(rawCode, label);
  }
}

void Assembler::WritePoolGuard(BufferOffset branch, Instruction* dest,
                               BufferOffset afterPool) {
  DEBUG_PRINTF("\tWritePoolGuard\n");
  int32_t off = afterPool.getOffset() - branch.getOffset();
  if (!is_int21(off) || !((off & 0x1) == 0)) {
    printf("%d\n", off);
    MOZ_CRASH("imm invalid");
  }
  // JAL encode is
  //   31    | 30    21  |  20     | 19     12  | 11 7 |  6   0 |
  // imm[20] | imm[10:1] | imm[11] | imm[19:12] |  rd  |  opcode|
  //   1           10         1           8         5       7
  //                   offset[20:1]               dest      JAL
  int32_t imm20 = (off & 0xff000) |          // bits 19-12
                  ((off & 0x800) << 9) |     // bit  11
                  ((off & 0x7fe) << 20) |    // bits 10-1
                  ((off & 0x100000) << 11);  // bit  20
  Instr instr = JAL | (imm20 & kImm20Mask);
  dest->SetInstructionBits(instr);
  DEBUG_PRINTF("%p(%x): ", dest, branch.getOffset());
  disassembleInstr(dest->InstructionBits(), JitSpew_Codegen);
}

void Assembler::WritePoolHeader(uint8_t* start, Pool* p, bool isNatural) {
  static_assert(sizeof(PoolHeader) == 4);

  // Get the total size of the pool.
  const uintptr_t totalPoolSize = sizeof(PoolHeader) + p->getPoolSize();
  const uintptr_t totalPoolInstructions = totalPoolSize / kInstrSize;

  MOZ_ASSERT((totalPoolSize & 0x3) == 0);
  MOZ_ASSERT(totalPoolInstructions < (1 << 15));

  PoolHeader header(totalPoolInstructions, isNatural);
  *(PoolHeader*)start = header;
}

void Assembler::copyJumpRelocationTable(uint8_t* dest) {
  if (jumpRelocations_.length()) {
    memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length());
  }
}

void Assembler::copyDataRelocationTable(uint8_t* dest) {
  if (dataRelocations_.length()) {
    memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length());
  }
}

void Assembler::RV_li(Register rd, int64_t imm) {
  UseScratchRegisterScope temps(this);
  if (RecursiveLiCount(imm) > GeneralLiCount(imm, temps.hasAvailable())) {
    GeneralLi(rd, imm);
  } else {
    RecursiveLi(rd, imm);
  }
}

int Assembler::RV_li_count(int64_t imm, bool is_get_temp_reg) {
  if (RecursiveLiCount(imm) > GeneralLiCount(imm, is_get_temp_reg)) {
    return GeneralLiCount(imm, is_get_temp_reg);
  } else {
    return RecursiveLiCount(imm);
  }
}

void Assembler::GeneralLi(Register rd, int64_t imm) {
  // 64-bit imm is put in the register rd.
  // In most cases the imm is 32 bit and 2 instructions are generated. If a
  // temporary register is available, in the worst case, 6 instructions are
  // generated for a full 64-bit immediate. If temporay register is not
  // available the maximum will be 8 instructions. If imm is more than 32 bits
  // and a temp register is available, imm is divided into two 32-bit parts,
  // low_32 and up_32. Each part is built in a separate register. low_32 is
  // built before up_32. If low_32 is negative (upper 32 bits are 1), 0xffffffff
  // is subtracted from up_32 before up_32 is built. This compensates for 32
  // bits of 1's in the lower when the two registers are added. If no temp is
  // available, the upper 32 bit is built in rd, and the lower 32 bits are
  // devided to 3 parts (11, 11, and 10 bits). The parts are shifted and added
  // to the upper part built in rd.
  if (is_int32(imm + 0x800)) {
    // 32-bit case. Maximum of 2 instructions generated
    int64_t high_20 = ((imm + 0x800) >> 12);
    int64_t low_12 = imm << 52 >> 52;
    if (high_20) {
      lui(rd, (int32_t)high_20);
      if (low_12) {
        addi(rd, rd, low_12);
      }
    } else {
      addi(rd, zero_reg, low_12);
    }
    return;
  } else {
    UseScratchRegisterScope temps(this);
    BlockTrampolinePoolScope block_trampoline_pool(this, 8);
    // 64-bit case: divide imm into two 32-bit parts, upper and lower
    int64_t up_32 = imm >> 32;
    int64_t low_32 = imm & 0xffffffffull;
    Register temp_reg = rd;
    // Check if a temporary register is available
    if (up_32 == 0 || low_32 == 0) {
      // No temp register is needed
    } else {
      temp_reg = temps.hasAvailable() ? temps.Acquire() : InvalidReg;
    }
    if (temp_reg != InvalidReg) {
      // keep track of hardware behavior for lower part in sim_low
      int64_t sim_low = 0;
      // Build lower part
      if (low_32 != 0) {
        int64_t high_20 = ((low_32 + 0x800) >> 12);
        int64_t low_12 = low_32 & 0xfff;
        if (high_20) {
          // Adjust to 20 bits for the case of overflow
          high_20 &= 0xfffff;
          sim_low = ((high_20 << 12) << 32) >> 32;
          lui(rd, (int32_t)high_20);
          if (low_12) {
            sim_low += (low_12 << 52 >> 52) | low_12;
            addi(rd, rd, low_12);
          }
        } else {
          sim_low = low_12;
          ori(rd, zero_reg, low_12);
        }
      }
      if (sim_low & 0x100000000) {
        // Bit 31 is 1. Either an overflow or a negative 64 bit
        if (up_32 == 0) {
          // Positive number, but overflow because of the add 0x800
          slli(rd, rd, 32);
          srli(rd, rd, 32);
          return;
        }
        // low_32 is a negative 64 bit after the build
        up_32 = (up_32 - 0xffffffff) & 0xffffffff;
      }
      if (up_32 == 0) {
        return;
      }
      // Build upper part in a temporary register
      if (low_32 == 0) {
        // Build upper part in rd
        temp_reg = rd;
      }
      int64_t high_20 = (up_32 + 0x800) >> 12;
      int64_t low_12 = up_32 & 0xfff;
      if (high_20) {
        // Adjust to 20 bits for the case of overflow
        high_20 &= 0xfffff;
        lui(temp_reg, (int32_t)high_20);
        if (low_12) {
          addi(temp_reg, temp_reg, low_12);
        }
      } else {
        ori(temp_reg, zero_reg, low_12);
      }
      // Put it at the bgining of register
      slli(temp_reg, temp_reg, 32);
      if (low_32 != 0) {
        add(rd, rd, temp_reg);
      }
      return;
    }
    // No temp register. Build imm in rd.
    // Build upper 32 bits first in rd. Divide lower 32 bits parts and add
    // parts to the upper part by doing shift and add.
    // First build upper part in rd.
    int64_t high_20 = (up_32 + 0x800) >> 12;
    int64_t low_12 = up_32 & 0xfff;
    if (high_20) {
      // Adjust to 20 bits for the case of overflow
      high_20 &= 0xfffff;
      lui(rd, (int32_t)high_20);
      if (low_12) {
        addi(rd, rd, low_12);
      }
    } else {
      ori(rd, zero_reg, low_12);
    }
    // upper part already in rd. Each part to be added to rd, has maximum of 11
    // bits, and always starts with a 1. rd is shifted by the size of the part
    // plus the number of zeros between the parts. Each part is added after the
    // left shift.
    uint32_t mask = 0x80000000;
    int32_t shift_val = 0;
    int32_t i;
    for (i = 0; i < 32; i++) {
      if ((low_32 & mask) == 0) {
        mask >>= 1;
        shift_val++;
        if (i == 31) {
          // rest is zero
          slli(rd, rd, shift_val);
        }
        continue;
      }
      // The first 1 seen
      int32_t part;
      if ((i + 11) < 32) {
        // Pick 11 bits
        part = ((uint32_t)(low_32 << i) >> i) >> (32 - (i + 11));
        slli(rd, rd, shift_val + 11);
        ori(rd, rd, part);
        i += 10;
        mask >>= 11;
      } else {
        part = (uint32_t)(low_32 << i) >> i;
        slli(rd, rd, shift_val + (32 - i));
        ori(rd, rd, part);
        break;
      }
      shift_val = 0;
    }
  }
}

int Assembler::GeneralLiCount(int64_t imm, bool is_get_temp_reg) {
  int count = 0;
  // imitate Assembler::RV_li
  if (is_int32(imm + 0x800)) {
    // 32-bit case. Maximum of 2 instructions generated
    int64_t high_20 = ((imm + 0x800) >> 12);
    int64_t low_12 = imm << 52 >> 52;
    if (high_20) {
      count++;
      if (low_12) {
        count++;
      }
    } else {
      count++;
    }
    return count;
  } else {
    // 64-bit case: divide imm into two 32-bit parts, upper and lower
    int64_t up_32 = imm >> 32;
    int64_t low_32 = imm & 0xffffffffull;
    // Check if a temporary register is available
    if (is_get_temp_reg) {
      // keep track of hardware behavior for lower part in sim_low
      int64_t sim_low = 0;
      // Build lower part
      if (low_32 != 0) {
        int64_t high_20 = ((low_32 + 0x800) >> 12);
        int64_t low_12 = low_32 & 0xfff;
        if (high_20) {
          // Adjust to 20 bits for the case of overflow
          high_20 &= 0xfffff;
          sim_low = ((high_20 << 12) << 32) >> 32;
          count++;
          if (low_12) {
            sim_low += (low_12 << 52 >> 52) | low_12;
            count++;
          }
        } else {
          sim_low = low_12;
          count++;
        }
      }
      if (sim_low & 0x100000000) {
        // Bit 31 is 1. Either an overflow or a negative 64 bit
        if (up_32 == 0) {
          // Positive number, but overflow because of the add 0x800
          count++;
          count++;
          return count;
        }
        // low_32 is a negative 64 bit after the build
        up_32 = (up_32 - 0xffffffff) & 0xffffffff;
      }
      if (up_32 == 0) {
        return count;
      }
      int64_t high_20 = (up_32 + 0x800) >> 12;
      int64_t low_12 = up_32 & 0xfff;
      if (high_20) {
        // Adjust to 20 bits for the case of overflow
        high_20 &= 0xfffff;
        count++;
        if (low_12) {
          count++;
        }
      } else {
        count++;
      }
      // Put it at the bgining of register
      count++;
      if (low_32 != 0) {
        count++;
      }
      return count;
    }
    // No temp register. Build imm in rd.
    // Build upper 32 bits first in rd. Divide lower 32 bits parts and add
    // parts to the upper part by doing shift and add.
    // First build upper part in rd.
    int64_t high_20 = (up_32 + 0x800) >> 12;
    int64_t low_12 = up_32 & 0xfff;
    if (high_20) {
      // Adjust to 20 bits for the case of overflow
      high_20 &= 0xfffff;
      count++;
      if (low_12) {
        count++;
      }
    } else {
      count++;
    }
    // upper part already in rd. Each part to be added to rd, has maximum of 11
    // bits, and always starts with a 1. rd is shifted by the size of the part
    // plus the number of zeros between the parts. Each part is added after the
    // left shift.
    uint32_t mask = 0x80000000;
    int32_t i;
    for (i = 0; i < 32; i++) {
      if ((low_32 & mask) == 0) {
        mask >>= 1;
        if (i == 31) {
          // rest is zero
          count++;
        }
        continue;
      }
      // The first 1 seen
      if ((i + 11) < 32) {
        // Pick 11 bits
        count++;
        count++;
        i += 10;
        mask >>= 11;
      } else {
        count++;
        count++;
        break;
      }
    }
  }
  return count;
}

void Assembler::li_ptr(Register rd, int64_t imm) {
  m_buffer.enterNoNops();
  m_buffer.assertNoPoolAndNoNops();
  // Initialize rd with an address
  // Pointers are 48 bits
  // 6 fixed instructions are generated
  DEBUG_PRINTF("li_ptr(%d, %lx <%ld>)\n", ToNumber(rd), imm, imm);
  MOZ_ASSERT((imm & 0xfff0000000000000ll) == 0);
  int64_t a6 = imm & 0x3f;                      // bits 0:5. 6 bits
  int64_t b11 = (imm >> 6) & 0x7ff;             // bits 6:11. 11 bits
  int64_t high_31 = (imm >> 17) & 0x7fffffff;   // 31 bits
  int64_t high_20 = ((high_31 + 0x800) >> 12);  // 19 bits
  int64_t low_12 = high_31 & 0xfff;             // 12 bits
  lui(rd, (int32_t)high_20);
  addi(rd, rd, low_12);  // 31 bits in rd.
  slli(rd, rd, 11);      // Space for next 11 bis
  ori(rd, rd, b11);      // 11 bits are put in. 42 bit in rd
  slli(rd, rd, 6);       // Space for next 6 bits
  ori(rd, rd, a6);       // 6 bits are put in. 48 bis in rd
  m_buffer.leaveNoNops();
}

void Assembler::li_constant(Register rd, int64_t imm) {
  m_buffer.enterNoNops();
  m_buffer.assertNoPoolAndNoNops();
  DEBUG_PRINTF("li_constant(%d, %lx <%ld>)\n", ToNumber(rd), imm, imm);
  lui(rd, (imm + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >>
              48);  // Bits 63:48
  addiw(rd, rd,
        (imm + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >>
            52);  // Bits 47:36
  slli(rd, rd, 12);
  addi(rd, rd, (imm + (1LL << 23) + (1LL << 11)) << 28 >> 52);  // Bits 35:24
  slli(rd, rd, 12);
  addi(rd, rd, (imm + (1LL << 11)) << 40 >> 52);  // Bits 23:12
  slli(rd, rd, 12);
  addi(rd, rd, imm << 52 >> 52);  // Bits 11:0
  m_buffer.leaveNoNops();
}

ABIArg ABIArgGenerator::next(MIRType type) {
  switch (type) {
    case MIRType::Int32:
    case MIRType::Int64:
    case MIRType::Pointer:
    case MIRType::WasmAnyRef:
    case MIRType::WasmArrayData:
    case MIRType::StackResults: {
      if (intRegIndex_ == NumIntArgRegs) {
        current_ = ABIArg(stackOffset_);
        stackOffset_ += sizeof(uintptr_t);
        break;
      }
      current_ = ABIArg(Register::FromCode(intRegIndex_ + a0.encoding()));
      intRegIndex_++;
      break;
    }
    case MIRType::Float32:
    case MIRType::Double: {
      if (floatRegIndex_ == NumFloatArgRegs) {
        current_ = ABIArg(stackOffset_);
        stackOffset_ += sizeof(double);
        break;
      }
      current_ = ABIArg(FloatRegister(
          FloatRegisters::Encoding(floatRegIndex_ + fa0.encoding()),
          type == MIRType::Double ? FloatRegisters::Double
                                  : FloatRegisters::Single));
      floatRegIndex_++;
      break;
    }
    case MIRType::Simd128: {
      MOZ_CRASH("RISCV64 does not support simd yet.");
      break;
    }
    default:
      MOZ_CRASH("Unexpected argument type");
  }
  return current_;
}

bool Assembler::oom() const {
  return AssemblerShared::oom() || m_buffer.oom() || jumpRelocations_.oom() ||
         dataRelocations_.oom() || !enoughLabelCache_;
}

int Assembler::disassembleInstr(Instr instr, bool enable_spew) {
  if (!FLAG_riscv_debug && !enable_spew) return -1;
  disasm::NameConverter converter;
  disasm::Disassembler disasm(converter);
  EmbeddedVector<char, 128> disasm_buffer;

  int size =
      disasm.InstructionDecode(disasm_buffer, reinterpret_cast<byte*>(&instr));
  DEBUG_PRINTF("%s\n", disasm_buffer.start());
  if (enable_spew) {
    JitSpew(JitSpew_Codegen, "%s", disasm_buffer.start());
  }
  return size;
}

uintptr_t Assembler::target_address_at(Instruction* pc) {
  Instruction* instr0 = pc;
  DEBUG_PRINTF("target_address_at: pc: 0x%p\t", instr0);
  Instruction* instr1 = pc + 1 * kInstrSize;
  Instruction* instr2 = pc + 2 * kInstrSize;
  Instruction* instr3 = pc + 3 * kInstrSize;
  Instruction* instr4 = pc + 4 * kInstrSize;
  Instruction* instr5 = pc + 5 * kInstrSize;

  // Interpret instructions for address generated by li: See listing in
  // Assembler::set_target_address_at() just below.
  if (IsLui(*reinterpret_cast<Instr*>(instr0)) &&
      IsAddi(*reinterpret_cast<Instr*>(instr1)) &&
      IsSlli(*reinterpret_cast<Instr*>(instr2)) &&
      IsOri(*reinterpret_cast<Instr*>(instr3)) &&
      IsSlli(*reinterpret_cast<Instr*>(instr4)) &&
      IsOri(*reinterpret_cast<Instr*>(instr5))) {
    // Assemble the 64 bit value.
    int64_t addr = (int64_t)(instr0->Imm20UValue() << kImm20Shift) +
                   (int64_t)instr1->Imm12Value();
    MOZ_ASSERT(instr2->Imm12Value() == 11);
    addr <<= 11;
    addr |= (int64_t)instr3->Imm12Value();
    MOZ_ASSERT(instr4->Imm12Value() == 6);
    addr <<= 6;
    addr |= (int64_t)instr5->Imm12Value();

    DEBUG_PRINTF("addr: %lx\n", addr);
    return static_cast<uintptr_t>(addr);
  }
  // We should never get here, force a bad address if we do.
  MOZ_CRASH("RISC-V UNREACHABLE");
}

void Assembler::PatchDataWithValueCheck(CodeLocationLabel label,
                                        ImmPtr newValue, ImmPtr expectedValue) {
  PatchDataWithValueCheck(label, PatchedImmPtr(newValue.value),
                          PatchedImmPtr(expectedValue.value));
}

void Assembler::PatchDataWithValueCheck(CodeLocationLabel label,
                                        PatchedImmPtr newValue,
                                        PatchedImmPtr expectedValue) {
  Instruction* inst = (Instruction*)label.raw();

  // Extract old Value
  DebugOnly<uint64_t> value = Assembler::ExtractLoad64Value(inst);
  MOZ_ASSERT(value == uint64_t(expectedValue.value));

  // Replace with new value
  Assembler::UpdateLoad64Value(inst, uint64_t(newValue.value));
}

uint64_t Assembler::ExtractLoad64Value(Instruction* inst0) {
  DEBUG_PRINTF("\tExtractLoad64Value: \tpc:%p ", inst0);
  if (IsJal(*reinterpret_cast<Instr*>(inst0))) {
    int offset = inst0->Imm20JValue();
    inst0 = inst0 + offset;
  }
  Instruction* instr1 = inst0 + 1 * kInstrSize;
  if (IsAddiw(*reinterpret_cast<Instr*>(instr1))) {
    // Li64
    Instruction* instr2 = inst0 + 2 * kInstrSize;
    Instruction* instr3 = inst0 + 3 * kInstrSize;
    Instruction* instr4 = inst0 + 4 * kInstrSize;
    Instruction* instr5 = inst0 + 5 * kInstrSize;
    Instruction* instr6 = inst0 + 6 * kInstrSize;
    Instruction* instr7 = inst0 + 7 * kInstrSize;
    if (IsLui(*reinterpret_cast<Instr*>(inst0)) &&
        IsAddiw(*reinterpret_cast<Instr*>(instr1)) &&
        IsSlli(*reinterpret_cast<Instr*>(instr2)) &&
        IsAddi(*reinterpret_cast<Instr*>(instr3)) &&
        IsSlli(*reinterpret_cast<Instr*>(instr4)) &&
        IsAddi(*reinterpret_cast<Instr*>(instr5)) &&
        IsSlli(*reinterpret_cast<Instr*>(instr6)) &&
        IsAddi(*reinterpret_cast<Instr*>(instr7))) {
      int64_t imm = (int64_t)(inst0->Imm20UValue() << kImm20Shift) +
                    (int64_t)instr1->Imm12Value();
      MOZ_ASSERT(instr2->Imm12Value() == 12);
      imm <<= 12;
      imm += (int64_t)instr3->Imm12Value();
      MOZ_ASSERT(instr4->Imm12Value() == 12);
      imm <<= 12;
      imm += (int64_t)instr5->Imm12Value();
      MOZ_ASSERT(instr6->Imm12Value() == 12);
      imm <<= 12;
      imm += (int64_t)instr7->Imm12Value();
      DEBUG_PRINTF("imm:%lx\n", imm);
      return imm;
    } else {
      FLAG_riscv_debug = true;
      disassembleInstr(inst0->InstructionBits());
      disassembleInstr(instr1->InstructionBits());
      disassembleInstr(instr2->InstructionBits());
      disassembleInstr(instr3->InstructionBits());
      disassembleInstr(instr4->InstructionBits());
      disassembleInstr(instr5->InstructionBits());
      disassembleInstr(instr6->InstructionBits());
      disassembleInstr(instr7->InstructionBits());
      MOZ_CRASH();
    }
  } else {
    DEBUG_PRINTF("\n");
    Instruction* instrf1 = (inst0 - 1 * kInstrSize);
    Instruction* instr2 = inst0 + 2 * kInstrSize;
    Instruction* instr3 = inst0 + 3 * kInstrSize;
    Instruction* instr4 = inst0 + 4 * kInstrSize;
    Instruction* instr5 = inst0 + 5 * kInstrSize;
    Instruction* instr6 = inst0 + 6 * kInstrSize;
    Instruction* instr7 = inst0 + 7 * kInstrSize;
    disassembleInstr(instrf1->InstructionBits());
    disassembleInstr(inst0->InstructionBits());
    disassembleInstr(instr1->InstructionBits());
    disassembleInstr(instr2->InstructionBits());
    disassembleInstr(instr3->InstructionBits());
    disassembleInstr(instr4->InstructionBits());
    disassembleInstr(instr5->InstructionBits());
    disassembleInstr(instr6->InstructionBits());
    disassembleInstr(instr7->InstructionBits());
    MOZ_ASSERT(IsAddi(*reinterpret_cast<Instr*>(instr1)));
    // Li48
    return target_address_at(inst0);
  }
}

void Assembler::UpdateLoad64Value(Instruction* pc, uint64_t value) {
  DEBUG_PRINTF("\tUpdateLoad64Value: pc: %p\tvalue: %lx\n", pc, value);
  Instruction* instr1 = pc + 1 * kInstrSize;
  if (IsJal(*reinterpret_cast<Instr*>(pc))) {
    pc = pc + pc->Imm20JValue();
    instr1 = pc + 1 * kInstrSize;
  }
  if (IsAddiw(*reinterpret_cast<Instr*>(instr1))) {
    Instruction* instr0 = pc;
    Instruction* instr2 = pc + 2 * kInstrSize;
    Instruction* instr3 = pc + 3 * kInstrSize;
    Instruction* instr4 = pc + 4 * kInstrSize;
    Instruction* instr5 = pc + 5 * kInstrSize;
    Instruction* instr6 = pc + 6 * kInstrSize;
    Instruction* instr7 = pc + 7 * kInstrSize;
    MOZ_ASSERT(IsLui(*reinterpret_cast<Instr*>(pc)) &&
               IsAddiw(*reinterpret_cast<Instr*>(instr1)) &&
               IsSlli(*reinterpret_cast<Instr*>(instr2)) &&
               IsAddi(*reinterpret_cast<Instr*>(instr3)) &&
               IsSlli(*reinterpret_cast<Instr*>(instr4)) &&
               IsAddi(*reinterpret_cast<Instr*>(instr5)) &&
               IsSlli(*reinterpret_cast<Instr*>(instr6)) &&
               IsAddi(*reinterpret_cast<Instr*>(instr7)));
    // lui(rd, (imm + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >>
    //             48);  // Bits 63:48
    // addiw(rd, rd,
    //       (imm + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >>
    //           52);  // Bits 47:36
    // slli(rd, rd, 12);
    // addi(rd, rd, (imm + (1LL << 23) + (1LL << 11)) << 28 >> 52);  // Bits
    // 35:24 slli(rd, rd, 12); addi(rd, rd, (imm + (1LL << 11)) << 40 >> 52); //
    // Bits 23:12 slli(rd, rd, 12); addi(rd, rd, imm << 52 >> 52);  // Bits 11:0
    *reinterpret_cast<Instr*>(instr0) &= 0xfff;
    *reinterpret_cast<Instr*>(instr0) |=
        (((value + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >> 48)
         << 12);
    *reinterpret_cast<Instr*>(instr1) &= 0xfffff;
    *reinterpret_cast<Instr*>(instr1) |=
        (((value + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >> 52) << 20);
    *reinterpret_cast<Instr*>(instr3) &= 0xfffff;
    *reinterpret_cast<Instr*>(instr3) |=
        (((value + (1LL << 23) + (1LL << 11)) << 28 >> 52) << 20);
    *reinterpret_cast<Instr*>(instr5) &= 0xfffff;
    *reinterpret_cast<Instr*>(instr5) |=
        (((value + (1LL << 11)) << 40 >> 52) << 20);
    *reinterpret_cast<Instr*>(instr7) &= 0xfffff;
    *reinterpret_cast<Instr*>(instr7) |= ((value << 52 >> 52) << 20);
    disassembleInstr(instr0->InstructionBits());
    disassembleInstr(instr1->InstructionBits());
    disassembleInstr(instr2->InstructionBits());
    disassembleInstr(instr3->InstructionBits());
    disassembleInstr(instr4->InstructionBits());
    disassembleInstr(instr5->InstructionBits());
    disassembleInstr(instr6->InstructionBits());
    disassembleInstr(instr7->InstructionBits());
    MOZ_ASSERT(ExtractLoad64Value(pc) == value);
  } else {
    Instruction* instr0 = pc;
    Instruction* instr2 = pc + 2 * kInstrSize;
    Instruction* instr3 = pc + 3 * kInstrSize;
    Instruction* instr4 = pc + 4 * kInstrSize;
    Instruction* instr5 = pc + 5 * kInstrSize;
    Instruction* instr6 = pc + 6 * kInstrSize;
    Instruction* instr7 = pc + 7 * kInstrSize;
    disassembleInstr(instr0->InstructionBits());
    disassembleInstr(instr1->InstructionBits());
    disassembleInstr(instr2->InstructionBits());
    disassembleInstr(instr3->InstructionBits());
    disassembleInstr(instr4->InstructionBits());
    disassembleInstr(instr5->InstructionBits());
    disassembleInstr(instr6->InstructionBits());
    disassembleInstr(instr7->InstructionBits());
    MOZ_ASSERT(IsAddi(*reinterpret_cast<Instr*>(instr1)));
    set_target_value_at(pc, value);
  }
}

void Assembler::set_target_value_at(Instruction* pc, uint64_t target) {
  DEBUG_PRINTF("\tset_target_value_at: pc: %p\ttarget: %lx\n", pc, target);
  uint32_t* p = reinterpret_cast<uint32_t*>(pc);
  MOZ_ASSERT((target & 0xffff000000000000ll) == 0);
#ifdef DEBUG
  // Check we have the result from a li macro-instruction.
  Instruction* instr0 = pc;
  Instruction* instr1 = pc + 1 * kInstrSize;
  Instruction* instr3 = pc + 3 * kInstrSize;
  Instruction* instr5 = pc + 5 * kInstrSize;
  MOZ_ASSERT(IsLui(*reinterpret_cast<Instr*>(instr0)) &&
             IsAddi(*reinterpret_cast<Instr*>(instr1)) &&
             IsOri(*reinterpret_cast<Instr*>(instr3)) &&
             IsOri(*reinterpret_cast<Instr*>(instr5)));
#endif
  int64_t a6 = target & 0x3f;                     // bits 0:6. 6 bits
  int64_t b11 = (target >> 6) & 0x7ff;            // bits 6:11. 11 bits
  int64_t high_31 = (target >> 17) & 0x7fffffff;  // 31 bits
  int64_t high_20 = ((high_31 + 0x800) >> 12);    // 19 bits
  int64_t low_12 = high_31 & 0xfff;               // 12 bits
  *p = *p & 0xfff;
  *p = *p | ((int32_t)high_20 << 12);
  *(p + 1) = *(p + 1) & 0xfffff;
  *(p + 1) = *(p + 1) | ((int32_t)low_12 << 20);
  *(p + 2) = *(p + 2) & 0xfffff;
  *(p + 2) = *(p + 2) | (11 << 20);
  *(p + 3) = *(p + 3) & 0xfffff;
  *(p + 3) = *(p + 3) | ((int32_t)b11 << 20);
  *(p + 4) = *(p + 4) & 0xfffff;
  *(p + 4) = *(p + 4) | (6 << 20);
  *(p + 5) = *(p + 5) & 0xfffff;
  *(p + 5) = *(p + 5) | ((int32_t)a6 << 20);
  MOZ_ASSERT(target_address_at(pc) == target);
}

void Assembler::WriteLoad64Instructions(Instruction* inst0, Register reg,
                                        uint64_t value) {
  DEBUG_PRINTF("\tWriteLoad64Instructions\n");
  // Initialize rd with an address
  // Pointers are 48 bits
  // 6 fixed instructions are generated
  MOZ_ASSERT((value & 0xfff0000000000000ll) == 0);
  int64_t a6 = value & 0x3f;                     // bits 0:5. 6 bits
  int64_t b11 = (value >> 6) & 0x7ff;            // bits 6:11. 11 bits
  int64_t high_31 = (value >> 17) & 0x7fffffff;  // 31 bits
  int64_t high_20 = ((high_31 + 0x800) >> 12);   // 19 bits
  int64_t low_12 = high_31 & 0xfff;              // 12 bits
  Instr lui_ = LUI | (reg.code() << kRdShift) |
               ((int32_t)high_20 << kImm20Shift);  // lui(rd, (int32_t)high_20);
  *reinterpret_cast<Instr*>(inst0) = lui_;

  Instr addi_ =
      OP_IMM | (reg.code() << kRdShift) | (0b000 << kFunct3Shift) |
      (reg.code() << kRs1Shift) |
      (low_12 << kImm12Shift);  // addi(rd, rd, low_12);  // 31 bits in rd.
  *reinterpret_cast<Instr*>(inst0 + 1 * kInstrSize) = addi_;

  Instr slli_ =
      OP_IMM | (reg.code() << kRdShift) | (0b001 << kFunct3Shift) |
      (reg.code() << kRs1Shift) |
      (11 << kImm12Shift);  // slli(rd, rd, 11);      // Space for next 11 bis
  *reinterpret_cast<Instr*>(inst0 + 2 * kInstrSize) = slli_;

  Instr ori_b11 = OP_IMM | (reg.code() << kRdShift) | (0b110 << kFunct3Shift) |
                  (reg.code() << kRs1Shift) |
                  (b11 << kImm12Shift);  // ori(rd, rd, b11);      // 11 bits
                                         // are put in. 42 bit in rd
  *reinterpret_cast<Instr*>(inst0 + 3 * kInstrSize) = ori_b11;

  slli_ = OP_IMM | (reg.code() << kRdShift) | (0b001 << kFunct3Shift) |
          (reg.code() << kRs1Shift) |
          (6 << kImm12Shift);  // slli(rd, rd, 6);      // Space for next 11 bis
  *reinterpret_cast<Instr*>(inst0 + 4 * kInstrSize) =
      slli_;  // slli(rd, rd, 6);       // Space for next 6 bits

  Instr ori_a6 = OP_IMM | (reg.code() << kRdShift) | (0b110 << kFunct3Shift) |
                 (reg.code() << kRs1Shift) |
                 (a6 << kImm12Shift);  // ori(rd, rd, a6);       // 6 bits are
                                       // put in. 48 bis in rd
  *reinterpret_cast<Instr*>(inst0 + 5 * kInstrSize) = ori_a6;
  disassembleInstr((inst0 + 0 * kInstrSize)->InstructionBits());
  disassembleInstr((inst0 + 1 * kInstrSize)->InstructionBits());
  disassembleInstr((inst0 + 2 * kInstrSize)->InstructionBits());
  disassembleInstr((inst0 + 3 * kInstrSize)->InstructionBits());
  disassembleInstr((inst0 + 4 * kInstrSize)->InstructionBits());
  disassembleInstr((inst0 + 5 * kInstrSize)->InstructionBits());
  disassembleInstr((inst0 + 6 * kInstrSize)->InstructionBits());
  MOZ_ASSERT(ExtractLoad64Value(inst0) == value);
}

// This just stomps over memory with 32 bits of raw data. Its purpose is to
// overwrite the call of JITed code with 32 bits worth of an offset. This will
// is only meant to function on code that has been invalidated, so it should
// be totally safe. Since that instruction will never be executed again, a
// ICache flush should not be necessary
void Assembler::PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm) {
  // Raw is going to be the return address.
  uint32_t* raw = (uint32_t*)label.raw();
  // Overwrite the 4 bytes before the return address, which will
  // end up being the call instruction.
  *(raw - 1) = imm.value;
}

void Assembler::target_at_put(BufferOffset pos, BufferOffset target_pos,
                              bool trampoline) {
  if (m_buffer.oom()) {
    return;
  }
  DEBUG_PRINTF("\ttarget_at_put: %p (%d) to %p (%d)\n",
               reinterpret_cast<Instr*>(editSrc(pos)), pos.getOffset(),
               reinterpret_cast<Instr*>(editSrc(pos)) + target_pos.getOffset() -
                   pos.getOffset(),
               target_pos.getOffset());
  Instruction* instruction = editSrc(pos);
  Instr instr = instruction->InstructionBits();
  switch (instruction->InstructionOpcodeType()) {
    case BRANCH: {
      instr = SetBranchOffset(pos.getOffset(), target_pos.getOffset(), instr);
      instr_at_put(pos, instr);
    } break;
    case JAL: {
      MOZ_ASSERT(IsJal(instr));
      instr = SetJalOffset(pos.getOffset(), target_pos.getOffset(), instr);
      instr_at_put(pos, instr);
    } break;
    case LUI: {
      set_target_value_at(instruction,
                          reinterpret_cast<uintptr_t>(editSrc(target_pos)));
    } break;
    case AUIPC: {
      Instr instr_auipc = instr;
      Instr instr_I =
          editSrc(BufferOffset(pos.getOffset() + 4))->InstructionBits();
      MOZ_ASSERT(IsJalr(instr_I) || IsAddi(instr_I));

      intptr_t offset = target_pos.getOffset() - pos.getOffset();
      if (is_int21(offset) && IsJalr(instr_I) && trampoline) {
        MOZ_ASSERT(is_int21(offset) && ((offset & 1) == 0));
        Instr instr = JAL;
        instr = SetJalOffset(pos.getOffset(), target_pos.getOffset(), instr);
        MOZ_ASSERT(IsJal(instr));
        MOZ_ASSERT(JumpOffset(instr) == offset);
        instr_at_put(pos, instr);
        instr_at_put(BufferOffset(pos.getOffset() + 4), kNopByte);
      } else {
        MOZ_RELEASE_ASSERT(is_int32(offset + 0x800));
        MOZ_ASSERT(instruction->RdValue() ==
                   editSrc(BufferOffset(pos.getOffset() + 4))->Rs1Value());
        int32_t Hi20 = (((int32_t)offset + 0x800) >> 12);
        int32_t Lo12 = (int32_t)offset << 20 >> 20;

        instr_auipc =
            (instr_auipc & ~kImm31_12Mask) | ((Hi20 & kImm19_0Mask) << 12);
        instr_at_put(pos, instr_auipc);

        const int kImm31_20Mask = ((1 << 12) - 1) << 20;
        const int kImm11_0Mask = ((1 << 12) - 1);
        instr_I = (instr_I & ~kImm31_20Mask) | ((Lo12 & kImm11_0Mask) << 20);
        instr_at_put(BufferOffset(pos.getOffset() + 4), instr_I);
      }
    } break;
    default:
      UNIMPLEMENTED_RISCV();
      break;
  }
}

const int kEndOfChain = -1;
const int32_t kEndOfJumpChain = 0;

int Assembler::target_at(BufferOffset pos, bool is_internal) {
  if (oom()) {
    return kEndOfChain;
  }
  Instruction* instruction = editSrc(pos);
  Instruction* instruction2 = nullptr;
  if (IsAuipc(instruction->InstructionBits())) {
    instruction2 = editSrc(BufferOffset(pos.getOffset() + kInstrSize));
  }
  return target_at(instruction, pos, is_internal, instruction2);
}

int Assembler::target_at(Instruction* instruction, BufferOffset pos,
                         bool is_internal, Instruction* instruction2) {
  DEBUG_PRINTF("\t target_at: %p(%x)\n\t",
               reinterpret_cast<Instr*>(instruction), pos.getOffset());
  disassembleInstr(instruction->InstructionBits());
  Instr instr = instruction->InstructionBits();
  switch (instruction->InstructionOpcodeType()) {
    case BRANCH: {
      int32_t imm13 = BranchOffset(instr);
      if (imm13 == kEndOfJumpChain) {
        // EndOfChain sentinel is returned directly, not relative to pc or pos.
        return kEndOfChain;
      } else {
        DEBUG_PRINTF("\t target_at: %d %d\n", imm13, pos.getOffset() + imm13);
        return pos.getOffset() + imm13;
      }
    }
    case JAL: {
      int32_t imm21 = JumpOffset(instr);
      if (imm21 == kEndOfJumpChain) {
        // EndOfChain sentinel is returned directly, not relative to pc or pos.
        return kEndOfChain;
      } else {
        DEBUG_PRINTF("\t target_at: %d %d\n", imm21, pos.getOffset() + imm21);
        return pos.getOffset() + imm21;
      }
    }
    case JALR: {
      int32_t imm12 = instr >> 20;
      if (imm12 == kEndOfJumpChain) {
        // EndOfChain sentinel is returned directly, not relative to pc or pos.
        return kEndOfChain;
      } else {
        DEBUG_PRINTF("\t target_at: %d %d\n", imm12, pos.getOffset() + imm12);
        return pos.getOffset() + imm12;
      }
    }
    case LUI: {
      uintptr_t imm = target_address_at(instruction);
      uintptr_t instr_address = reinterpret_cast<uintptr_t>(instruction);
      if (imm == kEndOfJumpChain) {
        return kEndOfChain;
      } else {
        MOZ_ASSERT(instr_address - imm < INT_MAX);
        int32_t delta = static_cast<int32_t>(instr_address - imm);
        MOZ_ASSERT(pos.getOffset() > delta);
        return pos.getOffset() - delta;
      }
    }
    case AUIPC: {
      MOZ_ASSERT(instruction2 != nullptr);
      Instr instr_auipc = instr;
      Instr instr_I = instruction2->InstructionBits();
      MOZ_ASSERT(IsJalr(instr_I) || IsAddi(instr_I));
      int32_t offset = BrachlongOffset(instr_auipc, instr_I);
      if (offset == kEndOfJumpChain) return kEndOfChain;
      DEBUG_PRINTF("\t target_at: %d %d\n", offset, pos.getOffset() + offset);
      return offset + pos.getOffset();
    }
    default: {
      UNIMPLEMENTED_RISCV();
    }
  }
}

uint32_t Assembler::next_link(Label* L, bool is_internal) {
  MOZ_ASSERT(L->used());
  BufferOffset pos(L);
  int link = target_at(pos, is_internal);
  if (link == kEndOfChain) {
    L->reset();
    return LabelBase::INVALID_OFFSET;
  } else {
    MOZ_ASSERT(link >= 0);
    DEBUG_PRINTF("next: %p to offset %d\n", L, link);
    L->use(link);
    return link;
  }
}

void Assembler::bind(Label* label, BufferOffset boff) {
  JitSpew(JitSpew_Codegen, ".set Llabel %p %d", label, currentOffset());
  DEBUG_PRINTF(".set Llabel %p\n", label);
  // If our caller didn't give us an explicit target to bind to
  // then we want to bind to the location of the next instruction
  BufferOffset dest = boff.assigned() ? boff : nextOffset();
  if (label->used()) {
    uint32_t next;

    // A used label holds a link to branch that uses it.
    do {
      BufferOffset b(label);
      DEBUG_PRINTF("\tbind next:%d\n", b.getOffset());
      // Even a 0 offset may be invalid if we're out of memory.
      if (oom()) {
        return;
      }
      int fixup_pos = b.getOffset();
      int dist = dest.getOffset() - fixup_pos;
      next = next_link(label, false);
      DEBUG_PRINTF("\t%p fixup: %d next: %d\n", label, fixup_pos, next);
      DEBUG_PRINTF("\t fixup: %d dest: %d dist: %d %d %d\n", fixup_pos,
                   dest.getOffset(), dist, nextOffset().getOffset(),
                   currentOffset());
      Instruction* instruction = editSrc(b);
      Instr instr = instruction->InstructionBits();
      if (IsBranch(instr)) {
        if (dist > kMaxBranchOffset) {
          MOZ_ASSERT(next != LabelBase::INVALID_OFFSET);
          MOZ_RELEASE_ASSERT((next - fixup_pos) <= kMaxBranchOffset);
          MOZ_ASSERT(IsAuipc(editSrc(BufferOffset(next))->InstructionBits()));
          MOZ_ASSERT(
              IsJalr(editSrc(BufferOffset(next + 4))->InstructionBits()));
          DEBUG_PRINTF("\t\ttrampolining: %d\n", next);
        } else {
          target_at_put(b, dest);
          BufferOffset deadline(b.getOffset() +
                                ImmBranchMaxForwardOffset(CondBranchRangeType));
          m_buffer.unregisterBranchDeadline(CondBranchRangeType, deadline);
        }
      } else if (IsJal(instr)) {
        if (dist > kMaxJumpOffset) {
          MOZ_ASSERT(next != LabelBase::INVALID_OFFSET);
          MOZ_RELEASE_ASSERT((next - fixup_pos) <= kMaxJumpOffset);
          MOZ_ASSERT(IsAuipc(editSrc(BufferOffset(next))->InstructionBits()));
          MOZ_ASSERT(
              IsJalr(editSrc(BufferOffset(next + 4))->InstructionBits()));
          DEBUG_PRINTF("\t\ttrampolining: %d\n", next);
        } else {
          target_at_put(b, dest);
          BufferOffset deadline(
              b.getOffset() + ImmBranchMaxForwardOffset(UncondBranchRangeType));
          m_buffer.unregisterBranchDeadline(UncondBranchRangeType, deadline);
        }
      } else {
        MOZ_ASSERT(IsAuipc(instr));
        target_at_put(b, dest);
      }
    } while (next != LabelBase::INVALID_OFFSET);
  }
  label->bind(dest.getOffset());
}

void Assembler::Bind(uint8_t* rawCode, const CodeLabel& label) {
  if (label.patchAt().bound()) {
    auto mode = label.linkMode();
    intptr_t offset = label.patchAt().offset();
    intptr_t target = label.target().offset();

    if (mode == CodeLabel::RawPointer) {
      *reinterpret_cast<const void**>(rawCode + offset) = rawCode + target;
    } else {
      MOZ_ASSERT(mode == CodeLabel::MoveImmediate ||
                 mode == CodeLabel::JumpImmediate);
      Instruction* inst = (Instruction*)(rawCode + offset);
      Assembler::UpdateLoad64Value(inst, (uint64_t)(rawCode + target));
    }
  }
}

bool Assembler::is_near(Label* L) {
  MOZ_ASSERT(L->bound());
  return is_intn((currentOffset() - L->offset()), kJumpOffsetBits);
}

bool Assembler::is_near(Label* L, OffsetSize bits) {
  if (L == nullptr || !L->bound()) return true;
  return is_intn((currentOffset() - L->offset()), bits);
}

bool Assembler::is_near_branch(Label* L) {
  MOZ_ASSERT(L->bound());
  return is_intn((currentOffset() - L->offset()), kBranchOffsetBits);
}

int32_t Assembler::branch_long_offset(Label* L) {
  if (oom()) {
    return kEndOfJumpChain;
  }
  intptr_t target_pos;
  BufferOffset next_instr_offset = nextInstrOffset(2);
  DEBUG_PRINTF("\tbranch_long_offset: %p to (%d)\n", L,
               next_instr_offset.getOffset());
  if (L->bound()) {
    JitSpew(JitSpew_Codegen, ".use Llabel %p on %d", L,
            next_instr_offset.getOffset());
    target_pos = L->offset();
  } else {
    if (L->used()) {
      LabelCahe::Ptr p = label_cache_.lookup(L->offset());
      MOZ_ASSERT(p);
      MOZ_ASSERT(p->key() == L->offset());
      target_pos = p->value().getOffset();
      target_at_put(BufferOffset(target_pos), next_instr_offset);
      DEBUG_PRINTF("\tLabel %p added to link: %d\n", L,
                   next_instr_offset.getOffset());
      bool ok = label_cache_.put(L->offset(), next_instr_offset);
      if (!ok) {
        NoEnoughLabelCache();
      }
      return kEndOfJumpChain;
    } else {
      JitSpew(JitSpew_Codegen, ".use Llabel %p on %d", L,
              next_instr_offset.getOffset());
      L->use(next_instr_offset.getOffset());
      DEBUG_PRINTF("\tLabel %p added to link: %d\n", L,
                   next_instr_offset.getOffset());
      bool ok = label_cache_.putNew(L->offset(), next_instr_offset);
      if (!ok) {
        NoEnoughLabelCache();
      }
      return kEndOfJumpChain;
    }
  }
  intptr_t offset = target_pos - next_instr_offset.getOffset();
  MOZ_ASSERT((offset & 3) == 0);
  MOZ_ASSERT(is_int32(offset));
  return static_cast<int32_t>(offset);
}

int32_t Assembler::branch_offset_helper(Label* L, OffsetSize bits) {
  if (oom()) {
    return kEndOfJumpChain;
  }
  int32_t target_pos;
  BufferOffset next_instr_offset = nextInstrOffset();
  DEBUG_PRINTF("\tbranch_offset_helper: %p to %d\n", L,
               next_instr_offset.getOffset());
  // This is the last possible branch target.
  if (L->bound()) {
    JitSpew(JitSpew_Codegen, ".use Llabel %p on %d", L,
            next_instr_offset.getOffset());
    target_pos = L->offset();
  } else {
    BufferOffset deadline(next_instr_offset.getOffset() +
                          ImmBranchMaxForwardOffset(bits));
    DEBUG_PRINTF("\tregisterBranchDeadline %d type %d\n", deadline.getOffset(),
                 OffsetSizeToImmBranchRangeType(bits));
    m_buffer.registerBranchDeadline(OffsetSizeToImmBranchRangeType(bits),
                                    deadline);
    if (L->used()) {
      LabelCahe::Ptr p = label_cache_.lookup(L->offset());
      MOZ_ASSERT(p);
      MOZ_ASSERT(p->key() == L->offset());
      target_pos = p->value().getOffset();
      target_at_put(BufferOffset(target_pos), next_instr_offset);
      DEBUG_PRINTF("\tLabel %p added to link: %d\n", L,
                   next_instr_offset.getOffset());
      bool ok = label_cache_.put(L->offset(), next_instr_offset);
      if (!ok) {
        NoEnoughLabelCache();
      }
      return kEndOfJumpChain;
    } else {
      JitSpew(JitSpew_Codegen, ".use Llabel %p on %d", L,
              next_instr_offset.getOffset());
      L->use(next_instr_offset.getOffset());
      bool ok = label_cache_.putNew(L->offset(), next_instr_offset);
      if (!ok) {
        NoEnoughLabelCache();
      }
      DEBUG_PRINTF("\tLabel %p added to link: %d\n", L,
                   next_instr_offset.getOffset());
      return kEndOfJumpChain;
    }
  }

  int32_t offset = target_pos - next_instr_offset.getOffset();
  DEBUG_PRINTF("\toffset = %d\n", offset);
  MOZ_ASSERT(is_intn(offset, bits));
  MOZ_ASSERT((offset & 1) == 0);
  return offset;
}

Assembler::Condition Assembler::InvertCondition(Condition cond) {
  switch (cond) {
    case Equal:
      return NotEqual;
    case NotEqual:
      return Equal;
    case Zero:
      return NonZero;
    case NonZero:
      return Zero;
    case LessThan:
      return GreaterThanOrEqual;
    case LessThanOrEqual:
      return GreaterThan;
    case GreaterThan:
      return LessThanOrEqual;
    case GreaterThanOrEqual:
      return LessThan;
    case Above:
      return BelowOrEqual;
    case AboveOrEqual:
      return Below;
    case Below:
      return AboveOrEqual;
    case BelowOrEqual:
      return Above;
    case Signed:
      return NotSigned;
    case NotSigned:
      return Signed;
    default:
      MOZ_CRASH("unexpected condition");
  }
}

Assembler::DoubleCondition Assembler::InvertCondition(DoubleCondition cond) {
  switch (cond) {
    case DoubleOrdered:
      return DoubleUnordered;
    case DoubleEqual:
      return DoubleNotEqualOrUnordered;
    case DoubleNotEqual:
      return DoubleEqualOrUnordered;
    case DoubleGreaterThan:
      return DoubleLessThanOrEqualOrUnordered;
    case DoubleGreaterThanOrEqual:
      return DoubleLessThanOrUnordered;
    case DoubleLessThan:
      return DoubleGreaterThanOrEqualOrUnordered;
    case DoubleLessThanOrEqual:
      return DoubleGreaterThanOrUnordered;
    case DoubleUnordered:
      return DoubleOrdered;
    case DoubleEqualOrUnordered:
      return DoubleNotEqual;
    case DoubleNotEqualOrUnordered:
      return DoubleEqual;
    case DoubleGreaterThanOrUnordered:
      return DoubleLessThanOrEqual;
    case DoubleGreaterThanOrEqualOrUnordered:
      return DoubleLessThan;
    case DoubleLessThanOrUnordered:
      return DoubleGreaterThanOrEqual;
    case DoubleLessThanOrEqualOrUnordered:
      return DoubleGreaterThan;
    default:
      MOZ_CRASH("unexpected condition");
  }
}

// Break / Trap instructions.
void Assembler::break_(uint32_t code, bool break_as_stop) {
  // We need to invalidate breaks that could be stops as well because the
  // simulator expects a char pointer after the stop instruction.
  // See constants-mips.h for explanation.
  MOZ_ASSERT(
      (break_as_stop && code <= kMaxStopCode && code > kMaxTracepointCode) ||
      (!break_as_stop && (code > kMaxStopCode || code <= kMaxTracepointCode)));

  // since ebreak does not allow additional immediate field, we use the
  // immediate field of lui instruction immediately following the ebreak to
  // encode the "code" info
  ebreak();
  MOZ_ASSERT(is_uint20(code));
  lui(zero_reg, code);
}

void Assembler::ToggleToJmp(CodeLocationLabel inst_) {
  Instruction* inst = (Instruction*)inst_.raw();
  MOZ_ASSERT(IsAddi(inst->InstructionBits()));
  int32_t offset = inst->Imm12Value();
  MOZ_ASSERT(is_int12(offset));
  Instr jal_ = JAL | (0b000 << kFunct3Shift) |
               (offset & 0xff000) |          // bits 19-12
               ((offset & 0x800) << 9) |     // bit  11
               ((offset & 0x7fe) << 20) |    // bits 10-1
               ((offset & 0x100000) << 11);  // bit  20
  // jal(zero, offset);
  *reinterpret_cast<Instr*>(inst) = jal_;
}

void Assembler::ToggleToCmp(CodeLocationLabel inst_) {
  Instruction* inst = (Instruction*)inst_.raw();

  // toggledJump is allways used for short jumps.
  MOZ_ASSERT(IsJal(inst->InstructionBits()));
  // Replace "jal zero_reg, offset" with "addi $zero, $zero, offset"
  int32_t offset = inst->Imm20JValue();
  MOZ_ASSERT(is_int12(offset));
  Instr addi_ = OP_IMM | (0b000 << kFunct3Shift) |
                (offset << kImm12Shift);  // addi(zero, zero, low_12);
  *reinterpret_cast<Instr*>(inst) = addi_;
}

bool Assembler::reserve(size_t size) {
  // This buffer uses fixed-size chunks so there's no point in reserving
  // now vs. on-demand.
  return !oom();
}

static JitCode* CodeFromJump(Instruction* jump) {
  uint8_t* target = (uint8_t*)Assembler::ExtractLoad64Value(jump);
  return JitCode::FromExecutable(target);
}

void Assembler::TraceJumpRelocations(JSTracer* trc, JitCode* code,
                                     CompactBufferReader& reader) {
  while (reader.more()) {
    JitCode* child =
        CodeFromJump((Instruction*)(code->raw() + reader.readUnsigned()));
    TraceManuallyBarrieredEdge(trc, &child, "rel32");
  }
}

static void TraceOneDataRelocation(JSTracer* trc,
                                   mozilla::Maybe<AutoWritableJitCode>& awjc,
                                   JitCode* code, Instruction* inst) {
  void* ptr = (void*)Assembler::ExtractLoad64Value(inst);
  void* prior = ptr;

  // Data relocations can be for Values or for raw pointers. If a Value is
  // zero-tagged, we can trace it as if it were a raw pointer. If a Value
  // is not zero-tagged, we have to interpret it as a Value to ensure that the
  // tag bits are masked off to recover the actual pointer.
  uintptr_t word = reinterpret_cast<uintptr_t>(ptr);
  if (word >> JSVAL_TAG_SHIFT) {
    // This relocation is a Value with a non-zero tag.
    Value v = Value::fromRawBits(word);
    TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value");
    ptr = (void*)v.bitsAsPunboxPointer();
  } else {
    // This relocation is a raw pointer or a Value with a zero tag.
    // No barrier needed since these are constants.
    TraceManuallyBarrieredGenericPointerEdge(
        trc, reinterpret_cast<gc::Cell**>(&ptr), "jit-masm-ptr");
  }

  if (ptr != prior) {
    if (awjc.isNothing()) {
      awjc.emplace(code);
    }
    Assembler::UpdateLoad64Value(inst, uint64_t(ptr));
  }
}

/* static */
void Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code,
                                     CompactBufferReader& reader) {
  mozilla::Maybe<AutoWritableJitCode> awjc;
  while (reader.more()) {
    size_t offset = reader.readUnsigned();
    Instruction* inst = (Instruction*)(code->raw() + offset);
    TraceOneDataRelocation(trc, awjc, code, inst);
  }
}

UseScratchRegisterScope::UseScratchRegisterScope(Assembler* assembler)
    : available_(assembler->GetScratchRegisterList()),
      old_available_(*available_) {}

UseScratchRegisterScope::~UseScratchRegisterScope() {
  *available_ = old_available_;
}

Register UseScratchRegisterScope::Acquire() {
  MOZ_ASSERT(available_ != nullptr);
  MOZ_ASSERT(!available_->empty());
  Register index = GeneralRegisterSet::FirstRegister(available_->bits());
  available_->takeRegisterIndex(index);
  return index;
}

bool UseScratchRegisterScope::hasAvailable() const {
  return (available_->size()) != 0;
}

void Assembler::retarget(Label* label, Label* target) {
  spew("retarget %p -> %p", label, target);
  if (label->used() && !oom()) {
    if (target->bound()) {
      bind(label, BufferOffset(target));
    } else if (target->used()) {
      // The target is not bound but used. Prepend label's branch list
      // onto target's.
      int32_t next;
      BufferOffset labelBranchOffset(label);

      // Find the head of the use chain for label.
      do {
        next = next_link(label, false);
        labelBranchOffset = BufferOffset(next);
      } while (next != LabelBase::INVALID_OFFSET);

      // Then patch the head of label's use chain to the tail of
      // target's use chain, prepending the entire use chain of target.
      target->use(label->offset());
      target_at_put(labelBranchOffset, BufferOffset(target));
      MOZ_CRASH("check");
    } else {
      // The target is unbound and unused.  We can just take the head of
      // the list hanging off of label, and dump that into target.
      target->use(label->offset());
    }
  }
  label->reset();
}

bool Assembler::appendRawCode(const uint8_t* code, size_t numBytes) {
  if (m_buffer.oom()) {
    return false;
  }
  while (numBytes > SliceSize) {
    m_buffer.putBytes(SliceSize, code);
    numBytes -= SliceSize;
    code += SliceSize;
  }
  m_buffer.putBytes(numBytes, code);
  return !m_buffer.oom();
}

void Assembler::ToggleCall(CodeLocationLabel inst_, bool enabled) {
  Instruction* i0 = (Instruction*)inst_.raw();
  Instruction* i1 = (Instruction*)(inst_.raw() + 1 * kInstrSize);
  Instruction* i2 = (Instruction*)(inst_.raw() + 2 * kInstrSize);
  Instruction* i3 = (Instruction*)(inst_.raw() + 3 * kInstrSize);
  Instruction* i4 = (Instruction*)(inst_.raw() + 4 * kInstrSize);
  Instruction* i5 = (Instruction*)(inst_.raw() + 5 * kInstrSize);
  Instruction* i6 = (Instruction*)(inst_.raw() + 6 * kInstrSize);

  MOZ_ASSERT(IsLui(i0->InstructionBits()));
  MOZ_ASSERT(IsAddi(i1->InstructionBits()));
  MOZ_ASSERT(IsSlli(i2->InstructionBits()));
  MOZ_ASSERT(IsOri(i3->InstructionBits()));
  MOZ_ASSERT(IsSlli(i4->InstructionBits()));
  MOZ_ASSERT(IsOri(i5->InstructionBits()));
  if (enabled) {
    Instr jalr_ = JALR | (ra.code() << kRdShift) | (0x0 << kFunct3Shift) |
                  (i5->RdValue() << kRs1Shift) | (0x0 << kImm12Shift);
    *((Instr*)i6) = jalr_;
  } else {
    *((Instr*)i6) = kNopByte;
  }
}

void Assembler::PatchShortRangeBranchToVeneer(Buffer* buffer, unsigned rangeIdx,
                                              BufferOffset deadline,
                                              BufferOffset veneer) {
  if (buffer->oom()) {
    return;
  }
  DEBUG_PRINTF("\tPatchShortRangeBranchToVeneer\n");
  // Reconstruct the position of the branch from (rangeIdx, deadline).
  ImmBranchRangeType branchRange = static_cast<ImmBranchRangeType>(rangeIdx);
  BufferOffset branch(deadline.getOffset() -
                      ImmBranchMaxForwardOffset(branchRange));
  Instruction* branchInst = buffer->getInst(branch);
  Instruction* veneerInst_1 = buffer->getInst(veneer);
  Instruction* veneerInst_2 =
      buffer->getInst(BufferOffset(veneer.getOffset() + 4));
  // Verify that the branch range matches what's encoded.
  DEBUG_PRINTF("\t%p(%x): ", branchInst, branch.getOffset());
  disassembleInstr(branchInst->InstructionBits(), JitSpew_Codegen);
  DEBUG_PRINTF("\t instert veneer %x, branch:%x deadline: %x\n",
               veneer.getOffset(), branch.getOffset(), deadline.getOffset());
  MOZ_ASSERT(branchRange <= UncondBranchRangeType);
  MOZ_ASSERT(branchInst->GetImmBranchRangeType() == branchRange);
  // emit a long jump slot
  Instr auipc = AUIPC | (t6.code() << kRdShift) | (0x0 << kImm20Shift);
  Instr jalr = JALR | (zero_reg.code() << kRdShift) | (0x0 << kFunct3Shift) |
               (t6.code() << kRs1Shift) | (0x0 << kImm12Shift);

  // We want to insert veneer after branch in the linked list of instructions
  // that use the same unbound label.
  // The veneer should be an unconditional branch.
  int32_t nextElemOffset = target_at(buffer->getInst(branch), branch, false);
  int32_t dist;
  // If offset is 0, this is the end of the linked list.
  if (nextElemOffset != kEndOfChain) {
    // Make the offset relative to veneer so it targets the same instruction
    // as branchInst.
    dist = nextElemOffset - veneer.getOffset();
  } else {
    dist = 0;
  }
  int32_t Hi20 = (((int32_t)dist + 0x800) >> 12);
  int32_t Lo12 = (int32_t)dist << 20 >> 20;
  auipc = SetAuipcOffset(Hi20, auipc);
  jalr = SetJalrOffset(Lo12, jalr);
  // insert veneer
  veneerInst_1->SetInstructionBits(auipc);
  veneerInst_2->SetInstructionBits(jalr);
  // Now link branchInst to veneer.
  if (IsBranch(branchInst->InstructionBits())) {
    branchInst->SetInstructionBits(SetBranchOffset(
        branch.getOffset(), veneer.getOffset(), branchInst->InstructionBits()));
  } else {
    MOZ_ASSERT(IsJal(branchInst->InstructionBits()));
    branchInst->SetInstructionBits(SetJalOffset(
        branch.getOffset(), veneer.getOffset(), branchInst->InstructionBits()));
  }
  DEBUG_PRINTF("\tfix to veneer:");
  disassembleInstr(branchInst->InstructionBits());
}
}  // namespace jit
}  // namespace js

Messung V0.5
C=86 H=97 G=91

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.38Angebot  Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.