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

Quelle  MoveEmitter-arm.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/. */


#include "jit/arm/MoveEmitter-arm.h"

#include "jit/MacroAssembler-inl.h"

using namespace js;
using namespace js::jit;

MoveEmitterARM::MoveEmitterARM(MacroAssembler& masm)
    : inCycle_(0),
      masm(masm),
      pushedAtCycle_(-1),
      pushedAtSpill_(-1),
      spilledReg_(InvalidReg),
      spilledFloatReg_(InvalidFloatReg) {
  pushedAtStart_ = masm.framePushed();
}

void MoveEmitterARM::emit(const MoveResolver& moves) {
  if (moves.numCycles()) {
    // Reserve stack for cycle resolution
    static_assert(SpillSlotSize == 8);
    masm.reserveStack(moves.numCycles() * SpillSlotSize);
    pushedAtCycle_ = masm.framePushed();
  }

  for (size_t i = 0; i < moves.numMoves(); i++) {
    emit(moves.getMove(i));
  }
}

MoveEmitterARM::~MoveEmitterARM() { assertDone(); }

Address MoveEmitterARM::cycleSlot(uint32_t slot, uint32_t subslot) const {
  int32_t offset = masm.framePushed() - pushedAtCycle_;
  MOZ_ASSERT(offset < 4096 && offset > -4096);
  return Address(StackPointer, offset + slot * sizeof(double) + subslot);
}

Address MoveEmitterARM::spillSlot() const {
  int32_t offset = masm.framePushed() - pushedAtSpill_;
  MOZ_ASSERT(offset < 4096 && offset > -4096);
  return Address(StackPointer, offset);
}

Address MoveEmitterARM::toAddress(const MoveOperand& operand) const {
  MOZ_ASSERT(operand.isMemoryOrEffectiveAddress());

  if (operand.base() != StackPointer) {
    return Address(operand.base(), operand.disp());
  }

  MOZ_ASSERT(operand.disp() >= 0);

  // Otherwise, the stack offset may need to be adjusted.
  return Address(StackPointer,
                 operand.disp() + (masm.framePushed() - pushedAtStart_));
}

Register MoveEmitterARM::tempReg() {
  if (spilledReg_ != InvalidReg) {
    return spilledReg_;
  }

  // For now, just pick r12/ip as the eviction point. This is totally random,
  // and if it ends up being bad, we can use actual heuristics later. r12 is
  // actually a bad choice. It is the scratch register, which is frequently
  // used for address computations, such as those found when we attempt to
  // access values more than 4096 off of the stack pointer. Instead, use lr,
  // the LinkRegister.
  spilledReg_ = r14;
  if (pushedAtSpill_ == -1) {
    masm.Push(spilledReg_);
    pushedAtSpill_ = masm.framePushed();
  } else {
    ScratchRegisterScope scratch(masm);
    masm.ma_str(spilledReg_, spillSlot(), scratch);
  }
  return spilledReg_;
}

void MoveEmitterARM::breakCycle(const MoveOperand& from, const MoveOperand& to,
                                MoveOp::Type type, uint32_t slotId) {
  // There is some pattern:
  //   (A -> B)
  //   (B -> A)
  //
  // This case handles (A -> B), which we reach first. We save B, then allow
  // the original move to continue.

  ScratchRegisterScope scratch(masm);

  switch (type) {
    case MoveOp::FLOAT32:
      if (to.isMemory()) {
        ScratchFloat32Scope scratchFloat32(masm);
        masm.ma_vldr(toAddress(to), scratchFloat32, scratch);
        // Since it is uncertain if the load will be aligned or not
        // just fill both of them with the same value.
        masm.ma_vstr(scratchFloat32, cycleSlot(slotId, 0), scratch);
        masm.ma_vstr(scratchFloat32, cycleSlot(slotId, 4), scratch);
      } else if (to.isGeneralReg()) {
        // Since it is uncertain if the load will be aligned or not
        // just fill both of them with the same value.
        masm.ma_str(to.reg(), cycleSlot(slotId, 0), scratch);
        masm.ma_str(to.reg(), cycleSlot(slotId, 4), scratch);
      } else {
        FloatRegister src = to.floatReg();
        // Just always store the largest possible size. Currently, this is
        // a double. When SIMD is added, two doubles will need to be stored.
        masm.ma_vstr(src.doubleOverlay(), cycleSlot(slotId, 0), scratch);
      }
      break;
    case MoveOp::DOUBLE:
      if (to.isMemory()) {
        ScratchDoubleScope scratchDouble(masm);
        masm.ma_vldr(toAddress(to), scratchDouble, scratch);
        masm.ma_vstr(scratchDouble, cycleSlot(slotId, 0), scratch);
      } else if (to.isGeneralRegPair()) {
        ScratchDoubleScope scratchDouble(masm);
        masm.ma_vxfer(to.evenReg(), to.oddReg(), scratchDouble);
        masm.ma_vstr(scratchDouble, cycleSlot(slotId, 0), scratch);
      } else {
        masm.ma_vstr(to.floatReg().doubleOverlay(), cycleSlot(slotId, 0),
                     scratch);
      }
      break;
    case MoveOp::INT32:
    case MoveOp::GENERAL:
      // an non-vfp value
      if (to.isMemory()) {
        Register temp = tempReg();
        masm.ma_ldr(toAddress(to), temp, scratch);
        masm.ma_str(temp, cycleSlot(0, 0), scratch);
      } else {
        if (to.reg() == spilledReg_) {
          // If the destination was spilled, restore it first.
          masm.ma_ldr(spillSlot(), spilledReg_, scratch);
          spilledReg_ = InvalidReg;
        }
        masm.ma_str(to.reg(), cycleSlot(0, 0), scratch);
      }
      break;
    default:
      MOZ_CRASH("Unexpected move type");
  }
}

void MoveEmitterARM::completeCycle(const MoveOperand& from,
                                   const MoveOperand& to, MoveOp::Type type,
                                   uint32_t slotId) {
  // There is some pattern:
  //   (A -> B)
  //   (B -> A)
  //
  // This case handles (B -> A), which we reach last. We emit a move from the
  // saved value of B, to A.

  ScratchRegisterScope scratch(masm);

  switch (type) {
    case MoveOp::FLOAT32:
      MOZ_ASSERT(!to.isGeneralRegPair());
      if (to.isMemory()) {
        ScratchFloat32Scope scratchFloat32(masm);
        masm.ma_vldr(cycleSlot(slotId, 0), scratchFloat32, scratch);
        masm.ma_vstr(scratchFloat32, toAddress(to), scratch);
      } else if (to.isGeneralReg()) {
        MOZ_ASSERT(type == MoveOp::FLOAT32);
        masm.ma_ldr(toAddress(from), to.reg(), scratch);
      } else {
        uint32_t offset = 0;
        if ((!from.isMemory()) && from.floatReg().numAlignedAliased() == 1) {
          offset = sizeof(float);
        }
        masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg(), scratch);
      }
      break;
    case MoveOp::DOUBLE:
      MOZ_ASSERT(!to.isGeneralReg());
      if (to.isMemory()) {
        ScratchDoubleScope scratchDouble(masm);
        masm.ma_vldr(cycleSlot(slotId, 0), scratchDouble, scratch);
        masm.ma_vstr(scratchDouble, toAddress(to), scratch);
      } else if (to.isGeneralRegPair()) {
        MOZ_ASSERT(type == MoveOp::DOUBLE);
        ScratchDoubleScope scratchDouble(masm);
        masm.ma_vldr(toAddress(from), scratchDouble, scratch);
        masm.ma_vxfer(scratchDouble, to.evenReg(), to.oddReg());
      } else {
        uint32_t offset = 0;
        if ((!from.isMemory()) && from.floatReg().numAlignedAliased() == 1) {
          offset = sizeof(float);
        }
        masm.ma_vldr(cycleSlot(slotId, offset), to.floatReg(), scratch);
      }
      break;
    case MoveOp::INT32:
    case MoveOp::GENERAL:
      MOZ_ASSERT(slotId == 0);
      if (to.isMemory()) {
        Register temp = tempReg();
        masm.ma_ldr(cycleSlot(slotId, 0), temp, scratch);
        masm.ma_str(temp, toAddress(to), scratch);
      } else {
        if (to.reg() == spilledReg_) {
          // Make sure we don't re-clobber the spilled register later.
          spilledReg_ = InvalidReg;
        }
        masm.ma_ldr(cycleSlot(slotId, 0), to.reg(), scratch);
      }
      break;
    default:
      MOZ_CRASH("Unexpected move type");
  }
}

void MoveEmitterARM::emitMove(const MoveOperand& from, const MoveOperand& to) {
  // Register pairs are used to store Double values during calls.
  MOZ_ASSERT(!from.isGeneralRegPair());
  MOZ_ASSERT(!to.isGeneralRegPair());

  ScratchRegisterScope scratch(masm);

  if (to.isGeneralReg() && to.reg() == spilledReg_) {
    // If the destination is the spilled register, make sure we
    // don't re-clobber its value.
    spilledReg_ = InvalidReg;
  }

  if (from.isGeneralReg()) {
    if (from.reg() == spilledReg_) {
      // If the source is a register that has been spilled, make sure
      // to load the source back into that register.
      masm.ma_ldr(spillSlot(), spilledReg_, scratch);
      spilledReg_ = InvalidReg;
    }
    if (to.isMemoryOrEffectiveAddress()) {
      masm.ma_str(from.reg(), toAddress(to), scratch);
    } else {
      masm.ma_mov(from.reg(), to.reg());
    }
  } else if (to.isGeneralReg()) {
    MOZ_ASSERT(from.isMemoryOrEffectiveAddress());
    if (from.isMemory()) {
      masm.ma_ldr(toAddress(from), to.reg(), scratch);
    } else {
      masm.ma_add(from.base(), Imm32(from.disp()), to.reg(), scratch);
    }
  } else {
    // Memory to memory gpr move.
    Register reg = tempReg();

    MOZ_ASSERT(from.isMemoryOrEffectiveAddress());
    if (from.isMemory()) {
      masm.ma_ldr(toAddress(from), reg, scratch);
    } else {
      masm.ma_add(from.base(), Imm32(from.disp()), reg, scratch);
    }
    MOZ_ASSERT(to.base() != reg);
    masm.ma_str(reg, toAddress(to), scratch);
  }
}

void MoveEmitterARM::emitFloat32Move(const MoveOperand& from,
                                     const MoveOperand& to) {
  // Register pairs are used to store Double values during calls.
  MOZ_ASSERT(!from.isGeneralRegPair());
  MOZ_ASSERT(!to.isGeneralRegPair());

  ScratchRegisterScope scratch(masm);

  if (from.isFloatReg()) {
    if (to.isFloatReg()) {
      masm.ma_vmov_f32(from.floatReg(), to.floatReg());
    } else if (to.isGeneralReg()) {
      masm.ma_vxfer(from.floatReg(), to.reg());
    } else {
      masm.ma_vstr(VFPRegister(from.floatReg()).singleOverlay(), toAddress(to),
                   scratch);
    }
  } else if (from.isGeneralReg()) {
    if (to.isFloatReg()) {
      masm.ma_vxfer(from.reg(), to.floatReg());
    } else if (to.isGeneralReg()) {
      masm.ma_mov(from.reg(), to.reg());
    } else {
      masm.ma_str(from.reg(), toAddress(to), scratch);
    }
  } else if (to.isFloatReg()) {
    masm.ma_vldr(toAddress(from), VFPRegister(to.floatReg()).singleOverlay(),
                 scratch);
  } else if (to.isGeneralReg()) {
    masm.ma_ldr(toAddress(from), to.reg(), scratch);
  } else {
    // Memory to memory move.
    MOZ_ASSERT(from.isMemory());
    ScratchFloat32Scope scratchFloat32(masm);
    masm.ma_vldr(toAddress(from), scratchFloat32, scratch);
    masm.ma_vstr(scratchFloat32, toAddress(to), scratch);
  }
}

void MoveEmitterARM::emitDoubleMove(const MoveOperand& from,
                                    const MoveOperand& to) {
  // Registers are used to store pointers / int32 / float32 values.
  MOZ_ASSERT(!from.isGeneralReg());
  MOZ_ASSERT(!to.isGeneralReg());

  ScratchRegisterScope scratch(masm);

  if (from.isFloatReg()) {
    if (to.isFloatReg()) {
      masm.ma_vmov(from.floatReg(), to.floatReg());
    } else if (to.isGeneralRegPair()) {
      masm.ma_vxfer(from.floatReg(), to.evenReg(), to.oddReg());
    } else {
      masm.ma_vstr(from.floatReg(), toAddress(to), scratch);
    }
  } else if (from.isGeneralRegPair()) {
    if (to.isFloatReg()) {
      masm.ma_vxfer(from.evenReg(), from.oddReg(), to.floatReg());
    } else if (to.isGeneralRegPair()) {
      MOZ_ASSERT(!from.aliases(to));
      masm.ma_mov(from.evenReg(), to.evenReg());
      masm.ma_mov(from.oddReg(), to.oddReg());
    } else {
      ScratchDoubleScope scratchDouble(masm);
      masm.ma_vxfer(from.evenReg(), from.oddReg(), scratchDouble);
      masm.ma_vstr(scratchDouble, toAddress(to), scratch);
    }
  } else if (to.isFloatReg()) {
    masm.ma_vldr(toAddress(from), to.floatReg(), scratch);
  } else if (to.isGeneralRegPair()) {
    MOZ_ASSERT(from.isMemory());
    Address src = toAddress(from);
    // Note: We can safely use the MoveOperand's displacement here,
    // even if the base is SP: MoveEmitter::toOperand adjusts
    // SP-relative operands by the difference between the current
    // stack usage and stackAdjust, which emitter.finish() resets to
    // 0.
    //
    // Warning: if the offset isn't within [-255,+255] then this
    // will assert-fail (or, if non-debug, load the wrong words).
    // Nothing uses such an offset at the time of this writing.
    masm.ma_ldrd(EDtrAddr(src.base, EDtrOffImm(src.offset)), to.evenReg(),
                 to.oddReg());
  } else {
    // Memory to memory move.
    MOZ_ASSERT(from.isMemory());
    ScratchDoubleScope scratchDouble(masm);
    masm.ma_vldr(toAddress(from), scratchDouble, scratch);
    masm.ma_vstr(scratchDouble, toAddress(to), scratch);
  }
}

void MoveEmitterARM::emit(const MoveOp& move) {
  const MoveOperand& from = move.from();
  const MoveOperand& to = move.to();

  if (move.isCycleEnd() && move.isCycleBegin()) {
    // A fun consequence of aliased registers is you can have multiple
    // cycles at once, and one can end exactly where another begins.
    breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
    completeCycle(from, to, move.type(), move.cycleEndSlot());
    return;
  }

  if (move.isCycleEnd()) {
    MOZ_ASSERT(inCycle_);
    completeCycle(from, to, move.type(), move.cycleEndSlot());
    MOZ_ASSERT(inCycle_ > 0);
    inCycle_--;
    return;
  }

  if (move.isCycleBegin()) {
    breakCycle(from, to, move.endCycleType(), move.cycleBeginSlot());
    inCycle_++;
  }

  switch (move.type()) {
    case MoveOp::FLOAT32:
      emitFloat32Move(from, to);
      break;
    case MoveOp::DOUBLE:
      emitDoubleMove(from, to);
      break;
    case MoveOp::INT32:
    case MoveOp::GENERAL:
      emitMove(from, to);
      break;
    default:
      MOZ_CRASH("Unexpected move type");
  }
}

void MoveEmitterARM::assertDone() { MOZ_ASSERT(inCycle_ == 0); }

void MoveEmitterARM::finish() {
  assertDone();

  if (pushedAtSpill_ != -1 && spilledReg_ != InvalidReg) {
    ScratchRegisterScope scratch(masm);
    masm.ma_ldr(spillSlot(), spilledReg_, scratch);
  }
  masm.freeStack(masm.framePushed() - pushedAtStart_);
}

Messung V0.5
C=94 H=100 G=96

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






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.