content: /*
   * Copyright (C) 2007 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.taobao.android.dx.dex.code;
  
  import com.taobao.android.dx.dex.DexOptions;
  import com.taobao.android.dx.io.Opcodes;
  import com.taobao.android.dx.rop.code.LocalItem;
  import com.taobao.android.dx.rop.code.RegisterSpec;
  import com.taobao.android.dx.rop.code.RegisterSpecList;
  import com.taobao.android.dx.rop.code.RegisterSpecSet;
  import com.taobao.android.dx.rop.code.SourcePosition;
  import com.taobao.android.dx.rop.cst.Constant;
  import com.taobao.android.dx.rop.cst.CstMemberRef;
  import com.taobao.android.dx.rop.cst.CstString;
  import com.taobao.android.dx.rop.cst.CstType;
  import com.taobao.android.dx.rop.type.Type;
  import com.taobao.android.dx.ssa.BasicRegisterMapper;
  
  import com.taobao.android.dex.DexException;
  import java.util.ArrayList;
  import java.util.BitSet;
  import java.util.HashSet;
  
  /**
   * Processor for instruction lists, which takes a "first cut" of
   * instruction selection as a basis and produces a "final cut" in the
   * form of a {@link DalvInsnList} instance.
   */
  public final class OutputFinisher {
      /** {@code non-null;} options for dex output */
      private final DexOptions dexOptions;
  
      /**
       * {@code >= 0;} register count for the method, not including any extra
       * "reserved" registers needed to translate "difficult" instructions
       */
      private final int unreservedRegCount;
  
      /** {@code non-null;} the list of instructions, per se */
      private ArrayList<DalvInsn> insns;
  
      /** whether any instruction has position info */
      private boolean hasAnyPositionInfo;
  
      /** whether any instruction has local variable info */
      private boolean hasAnyLocalInfo;
  
      /**
       * {@code >= 0;} the count of reserved registers (low-numbered
       * registers used when expanding instructions that can't be
       * represented simply); becomes valid after a call to {@link
       * #massageInstructions}
       */
      private int reservedCount;
  
      /**
       * {@code >= 0;} the count of reserved registers just before parameters in order to align them.
       */
      private int reservedParameterCount;
  
      /**
       * Size, in register units, of all the parameters to this method
       */
      private final int paramSize;
  
      /**
       * Constructs an instance. It initially contains no instructions.
       *
       * @param dexOptions {@code non-null;} options for dex output
       * @param initialCapacity {@code >= 0;} initial capacity of the
       * instructions list
       * @param regCount {@code >= 0;} register count for the method
       * @param paramSize size, in register units, of all the parameters for this method
       */
      public OutputFinisher(DexOptions dexOptions, int initialCapacity, int regCount, int paramSize) {
          this.dexOptions = dexOptions;
          this.unreservedRegCount = regCount;
          this.insns = new ArrayList<DalvInsn>(initialCapacity);
          this.reservedCount = -1;
          this.hasAnyPositionInfo = false;
          this.hasAnyLocalInfo = false;
          this.paramSize = paramSize;
      }
  
      /**
       * Returns whether any of the instructions added to this instance
       * come with position info.
       *
       * @return whether any of the instructions added to this instance
       * come with position info
       */
      public boolean hasAnyPositionInfo() {
          return hasAnyPositionInfo;
      }
  
      /**
       * Returns whether this instance has any local variable information.
       *
       * @return whether this instance has any local variable information
       */
      public boolean hasAnyLocalInfo() {
          return hasAnyLocalInfo;
      }
  
      /**
       * Helper for {@link #add} which scrutinizes a single
       * instruction for local variable information.
       *
       * @param insn {@code non-null;} instruction to scrutinize
       * @return {@code true} iff the instruction refers to any
       * named locals
       */
      private static boolean hasLocalInfo(DalvInsn insn) {
          if (insn instanceof LocalSnapshot) {
              RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
              int size = specs.size();
              for (int i = 0; i < size; i++) {
                  if (hasLocalInfo(specs.get(i))) {
                      return true;
                  }
              }
          } else if (insn instanceof LocalStart) {
              RegisterSpec spec = ((LocalStart) insn).getLocal();
              if (hasLocalInfo(spec)) {
                  return true;
              }
          }
  
          return false;
      }
  
      /**
       * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single
       * register spec.
       *
       * @param spec {@code non-null;} spec to scrutinize
       * @return {@code true} iff the spec refers to any
       * named locals
       */
      private static boolean hasLocalInfo(RegisterSpec spec) {
          return (spec != null)
              && (spec.getLocalItem().getName() != null);
      }
  
      /**
       * Returns the set of all constants referred to by instructions added
       * to this instance.
       *
       * @return {@code non-null;} the set of constants
       */
      public HashSet<Constant> getAllConstants() {
          HashSet<Constant> result = new HashSet<Constant>(20);
  
          for (DalvInsn insn : insns) {
              addConstants(result, insn);
          }
  
          return result;
      }
  
      /**
       * Helper for {@link #getAllConstants} which adds all the info for
       * a single instruction.
       *
       * @param result {@code non-null;} result set to add to
       * @param insn {@code non-null;} instruction to scrutinize
       */
      private static void addConstants(HashSet<Constant> result,
              DalvInsn insn) {
          if (insn instanceof CstInsn) {
              Constant cst = ((CstInsn) insn).getConstant();
              result.add(cst);
          } else if (insn instanceof LocalSnapshot) {
              RegisterSpecSet specs = ((LocalSnapshot) insn).getLocals();
              int size = specs.size();
              for (int i = 0; i < size; i++) {
                  addConstants(result, specs.get(i));
              }
          } else if (insn instanceof LocalStart) {
              RegisterSpec spec = ((LocalStart) insn).getLocal();
              addConstants(result, spec);
          }
      }
  
      /**
       * Helper for {@link #getAllConstants} which adds all the info for
       * a single {@code RegisterSpec}.
       *
       * @param result {@code non-null;} result set to add to
       * @param spec {@code null-ok;} register spec to add
       */
      private static void addConstants(HashSet<Constant> result,
              RegisterSpec spec) {
          if (spec == null) {
              return;
          }
  
          LocalItem local = spec.getLocalItem();
          CstString name = local.getName();
          CstString signature = local.getSignature();
          Type type = spec.getType();
  
          if (type != Type.KNOWN_NULL) {
              result.add(CstType.intern(type));
          }
  
          if (name != null) {
              result.add(name);
          }
  
          if (signature != null) {
              result.add(signature);
          }
      }
  
      /**
       * Adds an instruction to the output.
       *
       * @param insn {@code non-null;} the instruction to add
       */
      public void add(DalvInsn insn) {
          insns.add(insn);
          updateInfo(insn);
      }
  
      /**
       * Inserts an instruction in the output at the given offset.
       *
       * @param at {@code >= 0;} what index to insert at
       * @param insn {@code non-null;} the instruction to insert
       */
      public void insert(int at, DalvInsn insn) {
          insns.add(at, insn);
          updateInfo(insn);
      }
  
      /**
       * Helper for {@link #add} and {@link #insert},
       * which updates the position and local info flags.
       *
       * @param insn {@code non-null;} an instruction that was just introduced
       */
      private void updateInfo(DalvInsn insn) {
          if (! hasAnyPositionInfo) {
              SourcePosition pos = insn.getPosition();
              if (pos.getLine() >= 0) {
                  hasAnyPositionInfo = true;
              }
          }
  
          if (! hasAnyLocalInfo) {
              if (hasLocalInfo(insn)) {
                  hasAnyLocalInfo = true;
              }
          }
      }
  
      /**
       * Reverses a branch which is buried a given number of instructions
       * backward in the output. It is illegal to call this unless the
       * indicated instruction really is a reversible branch.
       *
       * @param which how many instructions back to find the branch;
       * {@code 0} is the most recently added instruction,
       * {@code 1} is the instruction before that, etc.
       * @param newTarget {@code non-null;} the new target for the
       * reversed branch
       */
      public void reverseBranch(int which, CodeAddress newTarget) {
          int size = insns.size();
          int index = size - which - 1;
          TargetInsn targetInsn;
  
          try {
              targetInsn = (TargetInsn) insns.get(index);
          } catch (IndexOutOfBoundsException ex) {
              // Translate the exception.
              throw new IllegalArgumentException("too few instructions");
          } catch (ClassCastException ex) {
              // Translate the exception.
              throw new IllegalArgumentException("non-reversible instruction");
          }
  
          /*
           * No need to call this.set(), since the format and other info
           * are the same.
           */
          insns.set(index, targetInsn.withNewTargetAndReversed(newTarget));
      }
  
      /**
       * Assigns indices in all instructions that need them, using the
       * given callback to perform lookups. This should be called before
       * calling {@link #finishProcessingAndGetList}.
       *
       * @param callback {@code non-null;} callback object
       */
      public void assignIndices(DalvCode.AssignIndicesCallback callback) {
          for (DalvInsn insn : insns) {
              if (insn instanceof CstInsn) {
                  assignIndices((CstInsn) insn, callback);
              }
          }
      }
  
      /**
       * Helper for {@link #assignIndices} which does assignment for one
       * instruction.
       *
       * @param insn {@code non-null;} the instruction
       * @param callback {@code non-null;} the callback
       */
      private static void assignIndices(CstInsn insn,
              DalvCode.AssignIndicesCallback callback) {
          Constant cst = insn.getConstant();
          int index = callback.getIndex(cst);
  
          if (index >= 0) {
              insn.setIndex(index);
          }
  
          if (cst instanceof CstMemberRef) {
              CstMemberRef member = (CstMemberRef) cst;
              CstType definer = member.getDefiningClass();
              index = callback.getIndex(definer);
              if (index >= 0) {
                  insn.setClassIndex(index);
              }
          }
      }
  
      /**
       * Does final processing on this instance and gets the output as
       * a {@link DalvInsnList}. Final processing consists of:
       *
       * <ul>
       *   <li>optionally renumbering registers (to make room as needed for
       *   expanded instructions)</li>
       *   <li>picking a final opcode for each instruction</li>
       *   <li>rewriting instructions, because of register number,
       *   constant pool index, or branch target size issues</li>
       *   <li>assigning final addresses</li>
       * </ul>
       *
       * <p><b>Note:</b> This method may only be called once per instance
       * of this class.</p>
       *
       * @return {@code non-null;} the output list
       * @throws UnsupportedOperationException if this method has
       * already been called
       */
      public DalvInsnList finishProcessingAndGetList() {
          if (reservedCount >= 0) {
              throw new UnsupportedOperationException("already processed");
          }
  
          Dop[] opcodes = makeOpcodesArray();
          reserveRegisters(opcodes);
          if (dexOptions.ALIGN_64BIT_REGS_IN_OUTPUT_FINISHER) {
            align64bits(opcodes);
          }
          massageInstructions(opcodes);
          assignAddressesAndFixBranches();
  
          return DalvInsnList.makeImmutable(insns, reservedCount + unreservedRegCount
              + reservedParameterCount);
      }
  
      /**
       * Helper for {@link #finishProcessingAndGetList}, which extracts
       * the opcode out of each instruction into a separate array, to be
       * further manipulated as things progress.
       *
       * @return {@code non-null;} the array of opcodes
       */
      private Dop[] makeOpcodesArray() {
          int size = insns.size();
          Dop[] result = new Dop[size];
  
          for (int i = 0; i < size; i++) {
              result[i] = insns.get(i).getOpcode();
          }
  
          return result;
      }
  
      /**
       * Helper for {@link #finishProcessingAndGetList}, which figures
       * out how many reserved registers are required and then reserving
       * them. It also updates the given {@code opcodes} array so
       * as to avoid extra work when constructing the massaged
       * instruction list.
       *
       * @param opcodes {@code non-null;} array of per-instruction
       * opcode selections
       * @return true if reservedCount is expanded, false otherwise
       */
      private boolean reserveRegisters(Dop[] opcodes) {
          boolean reservedCountExpanded = false;
          int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount;
  
          /*
           * Call calculateReservedCount() and then perform register
           * reservation, repeatedly until no new reservations happen.
           */
          for (;;) {
              int newReservedCount = calculateReservedCount(opcodes);
              if (oldReservedCount >= newReservedCount) {
                  break;
              }
  
              reservedCountExpanded = true;
  
              int reservedDifference = newReservedCount - oldReservedCount;
              int size = insns.size();
  
              for (int i = 0; i < size; i++) {
                  /*
                   * CodeAddress instance identity is used to link
                   * TargetInsns to their targets, so it is
                   * inappropriate to make replacements, and they don't
                   * have registers in any case. Hence, the instanceof
                   * test below.
                   */
                  DalvInsn insn = insns.get(i);
                  if (!(insn instanceof CodeAddress)) {
                      /*
                       * No need to call this.set() since the format and
                       * other info are the same.
                       */
                      insns.set(i, insn.withRegisterOffset(reservedDifference));
                  }
              }
  
              oldReservedCount = newReservedCount;
          }
  
          reservedCount = oldReservedCount;
  
          return reservedCountExpanded;
      }
  
      /**
       * Helper for {@link #reserveRegisters}, which does one
       * pass over the instructions, calculating the number of
       * registers that need to be reserved. It also updates the
       * {@code opcodes} list to help avoid extra work in future
       * register reservation passes.
       *
       * @param opcodes {@code non-null;} array of per-instruction
       * opcode selections
       * @return {@code >= 0;} the count of reserved registers
       */
      private int calculateReservedCount(Dop[] opcodes) {
          int size = insns.size();
  
          /*
           * Potential new value of reservedCount, which gets updated in the
           * following loop. It starts out with the existing reservedCount
           * and gets increased if it turns out that additional registers
           * need to be reserved.
           */
          int newReservedCount = reservedCount;
  
          for (int i = 0; i < size; i++) {
              DalvInsn insn = insns.get(i);
              Dop originalOpcode = opcodes[i];
              Dop newOpcode = findOpcodeForInsn(insn, originalOpcode);
  
              if (newOpcode == null) {
                  /*
                   * The instruction will need to be expanded, so find the
                   * expanded opcode and reserve registers for it.
                   */
                  Dop expandedOp = findExpandedOpcodeForInsn(insn);
                  BitSet compatRegs = expandedOp.getFormat().compatibleRegs(insn);
                  int reserve = insn.getMinimumRegisterRequirement(compatRegs);
                  if (reserve > newReservedCount) {
                      newReservedCount = reserve;
                  }
              } else if (originalOpcode == newOpcode) {
                  continue;
              }
  
              opcodes[i] = newOpcode;
          }
  
          return newReservedCount;
      }
  
      /**
       * Attempts to fit the given instruction into a specific opcode,
       * returning the opcode whose format that the instruction fits
       * into or {@code null} to indicate that the instruction will need
       * to be expanded. This fitting process starts with the given
       * opcode as a first "best guess" and then pessimizes from there
       * if necessary.
       *
       * @param insn {@code non-null;} the instruction in question
       * @param guess {@code null-ok;} the current guess as to the best
       * opcode; {@code null} means that no simple opcode fits
       * @return {@code null-ok;} a possibly-different opcode; either a
       * {@code non-null} good fit or {@code null} to indicate that no
       * simple opcode fits
       */
      private Dop findOpcodeForInsn(DalvInsn insn, Dop guess) {
          /*
           * Note: The initial guess might be null, meaning that an
           * earlier call to this method already determined that there
           * was no possible simple opcode fit.
           */
  
          while (guess != null) {
              if (guess.getFormat().isCompatible(insn)) {
                  /*
                   * Don't break out for const_string to generate jumbo version
                   * when option is enabled.
                   */
                  if (!dexOptions.forceJumbo ||
                      guess.getOpcode() != Opcodes.CONST_STRING) {
                      break;
                  }
              }
  
              guess = Dops.getNextOrNull(guess, dexOptions);
          }
  
          return guess;
      }
  
      /**
       * Finds the proper opcode for the given instruction, ignoring
       * register constraints.
       *
       * @param insn {@code non-null;} the instruction in question
       * @return {@code non-null;} the opcode that fits
       */
      private Dop findExpandedOpcodeForInsn(DalvInsn insn) {
          Dop result = findOpcodeForInsn(insn.getLowRegVersion(), insn.getOpcode());
          if (result == null) {
              throw new DexException("No expanded opcode for " + insn);
          }
          return result;
      }
  
      /**
       * Helper for {@link #finishProcessingAndGetList}, which goes
       * through each instruction in the output, making sure its opcode
       * can accomodate its arguments. In cases where the opcode is
       * unable to do so, this replaces the instruction with a larger
       * instruction with identical semantics that <i>will</i> work.
       *
       * <p>This method may also reserve a number of low-numbered
       * registers, renumbering the instructions' original registers, in
       * order to have register space available in which to move
       * very-high registers when expanding instructions into
       * multi-instruction sequences. This expansion is done when no
       * simple instruction format can be found for a given instruction that
       * is able to accomodate that instruction's registers.</p>
       *
       * <p>This method ignores issues of branch target size, since
       * final addresses aren't known at the point that this method is
       * called.</p>
       *
       * @param opcodes {@code non-null;} array of per-instruction
       * opcode selections
       */
      private void massageInstructions(Dop[] opcodes) {
          if (reservedCount == 0) {
              /*
               * The easy common case: No registers were reserved, so we
               * merely need to replace any instructions whose format
               * (and hence whose opcode) changed during the reservation
               * pass, but all instructions will stay at their original
               * indices, and the instruction list doesn't grow.
               */
              int size = insns.size();
  
              for (int i = 0; i < size; i++) {
                  DalvInsn insn = insns.get(i);
                  Dop originalOpcode = insn.getOpcode();
                  Dop currentOpcode = opcodes[i];
  
                  if (originalOpcode != currentOpcode) {
                      insns.set(i, insn.withOpcode(currentOpcode));
                  }
              }
          } else {
              /*
               * The difficult uncommon case: Some instructions have to be
               * expanded to deal with high registers.
               */
              insns = performExpansion(opcodes);
          }
      }
  
      /**
       * Helper for {@link #massageInstructions}, which constructs a
       * replacement list, where each {link DalvInsn} instance that
       * couldn't be represented simply (due to register representation
       * problems) is expanded into a series of instances that together
       * perform the proper function.
       *
       * @param opcodes {@code non-null;} array of per-instruction
       * opcode selections
       * @return {@code non-null;} the replacement list
       */
      private ArrayList<DalvInsn> performExpansion(Dop[] opcodes) {
          int size = insns.size();
          ArrayList<DalvInsn> result = new ArrayList<DalvInsn>(size * 2);
  
          ArrayList<CodeAddress> closelyBoundAddresses = new ArrayList<CodeAddress>();
  
          for (int i = 0; i < size; i++) {
              DalvInsn insn = insns.get(i);
              Dop originalOpcode = insn.getOpcode();
              Dop currentOpcode = opcodes[i];
              DalvInsn prefix;
              DalvInsn suffix;
  
              if (currentOpcode != null) {
                  // No expansion is necessary.
                  prefix = null;
                  suffix = null;
              } else {
                  // Expansion is required.
                  currentOpcode = findExpandedOpcodeForInsn(insn);
                  BitSet compatRegs =
                      currentOpcode.getFormat().compatibleRegs(insn);
                  prefix = insn.expandedPrefix(compatRegs);
                  suffix = insn.expandedSuffix(compatRegs);
  
                  // Expand necessary registers to fit the new format
                  insn = insn.expandedVersion(compatRegs);
              }
  
              if (insn instanceof CodeAddress) {
                  // If we have a closely bound address, don't add it yet,
                  // because we need to add it after the prefix for the
                  // instruction it is bound to.
                  if (((CodeAddress) insn).getBindsClosely()) {
                      closelyBoundAddresses.add((CodeAddress)insn);
                      continue;
                  }
              }
  
              if (prefix != null) {
                  result.add(prefix);
              }
  
              // Add any pending closely bound addresses
              if (!(insn instanceof ZeroSizeInsn) && closelyBoundAddresses.size() > 0) {
                  for (CodeAddress codeAddress: closelyBoundAddresses) {
                      result.add(codeAddress);
                  }
                  closelyBoundAddresses.clear();
              }
  
              if (currentOpcode != originalOpcode) {
                  insn = insn.withOpcode(currentOpcode);
              }
              result.add(insn);
  
              if (suffix != null) {
                  result.add(suffix);
              }
          }
  
          return result;
      }
  
      /**
       * Helper for {@link #finishProcessingAndGetList}, which assigns
       * addresses to each instruction, possibly rewriting branches to
       * fix ones that wouldn't otherwise be able to reach their
       * targets.
       */
      private void assignAddressesAndFixBranches() {
          for (;;) {
              assignAddresses();
              if (!fixBranches()) {
                  break;
              }
          }
      }
  
      /**
       * Helper for {@link #assignAddressesAndFixBranches}, which
       * assigns an address to each instruction, in order.
       */
      private void assignAddresses() {
          int address = 0;
          int size = insns.size();
  
          for (int i = 0; i < size; i++) {
              DalvInsn insn = insns.get(i);
              insn.setAddress(address);
              address += insn.codeSize();
          }
      }
  
      /**
       * Helper for {@link #assignAddressesAndFixBranches}, which checks
       * the branch target size requirement of each branch instruction
       * to make sure it fits. For instructions that don't fit, this
       * rewrites them to use a {@code goto} of some sort. In the
       * case of a conditional branch that doesn't fit, the sense of the
       * test is reversed in order to branch around a {@code goto}
       * to the original target.
       *
       * @return whether any branches had to be fixed
       */
      private boolean fixBranches() {
          int size = insns.size();
          boolean anyFixed = false;
  
          for (int i = 0; i < size; i++) {
              DalvInsn insn = insns.get(i);
              if (!(insn instanceof TargetInsn)) {
                  // This loop only needs to inspect TargetInsns.
                  continue;
              }
  
              Dop opcode = insn.getOpcode();
              TargetInsn target = (TargetInsn) insn;
  
              if (opcode.getFormat().branchFits(target)) {
                  continue;
              }
  
              if (opcode.getFamily() == Opcodes.GOTO) {
                  // It is a goto; widen it if possible.
                  opcode = findOpcodeForInsn(insn, opcode);
                  if (opcode == null) {
                      /*
                       * The branch is already maximally large. This should
                       * only be possible if a method somehow manages to have
                       * more than 2^31 code units.
                       */
                      throw new UnsupportedOperationException("method too long");
                  }
                  insns.set(i, insn.withOpcode(opcode));
              } else {
                  /*
                   * It is a conditional: Reverse its sense, and arrange for
                   * it to branch around an absolute goto to the original
                   * branch target.
                   *
                   * Note: An invariant of the list being processed is
                   * that every TargetInsn is followed by a CodeAddress.
                   * Hence, it is always safe to get the next element
                   * after a TargetInsn and cast it to CodeAddress, as
                   * is happening a few lines down.
                   *
                   * Also note: Size gets incremented by one here, as we
                   * have -- in the net -- added one additional element
                   * to the list, so we increment i to match. The added
                   * and changed elements will be inspected by a repeat
                   * call to this method after this invocation returns.
                   */
                  CodeAddress newTarget;
                  try {
                      newTarget = (CodeAddress) insns.get(i + 1);
                  } catch (IndexOutOfBoundsException ex) {
                      // The TargetInsn / CodeAddress invariant was violated.
                      throw new IllegalStateException(
                              "unpaired TargetInsn (dangling)");
                  } catch (ClassCastException ex) {
                      // The TargetInsn / CodeAddress invariant was violated.
                      throw new IllegalStateException("unpaired TargetInsn");
                  }
                  TargetInsn gotoInsn =
                      new TargetInsn(Dops.GOTO, target.getPosition(),
                              RegisterSpecList.EMPTY, target.getTarget());
                  insns.set(i, gotoInsn);
                  insns.add(i, target.withNewTargetAndReversed(newTarget));
                  size++;
                  i++;
              }
  
              anyFixed = true;
          }
  
          return anyFixed;
      }
  
      private void align64bits(Dop[] opcodes) {
        while (true) {
          int notAligned64bitRegAccess = 0;
          int aligned64bitRegAccess = 0;
          int notAligned64bitParamAccess = 0;
          int aligned64bitParamAccess = 0;
          int lastParameter = unreservedRegCount + reservedCount + reservedParameterCount;
          int firstParameter = lastParameter - paramSize;
  
          // Collects the number of time that 64-bit registers are accessed aligned or not.
          for (DalvInsn insn : insns) {
            RegisterSpecList regs = insn.getRegisters();
            for (int usedRegIdx = 0; usedRegIdx < regs.size(); usedRegIdx++) {
              RegisterSpec reg = regs.get(usedRegIdx);
              if (reg.isCategory2()) {
                boolean isParameter = reg.getReg() >= firstParameter;
                if (reg.isEvenRegister()) {
                  if (isParameter) {
                    aligned64bitParamAccess++;
                  } else {
                    aligned64bitRegAccess++;
                  }
                } else {
                  if (isParameter) {
                    notAligned64bitParamAccess++;
                  } else {
                    notAligned64bitRegAccess++;
                  }
                }
              }
            }
          }
  
          if (notAligned64bitParamAccess > aligned64bitParamAccess
              && notAligned64bitRegAccess > aligned64bitRegAccess) {
            addReservedRegisters(1);
          } else if (notAligned64bitParamAccess > aligned64bitParamAccess) {
            addReservedParameters(1);
          } else if (notAligned64bitRegAccess > aligned64bitRegAccess) {
            addReservedRegisters(1);
  
            // Need to shift parameters if they exist and if number of unaligned is greater than
            // aligned. We test the opposite because we previously shift all registers by one,
            // so the number of aligned become the number of unaligned.
            if (paramSize != 0 && aligned64bitParamAccess > notAligned64bitParamAccess) {
              addReservedParameters(1);
            }
          } else {
            break;
          }
  
          if (!reserveRegisters(opcodes)) {
            break;
          }
        }
      }
  
      private void addReservedParameters(int delta) {
        shiftParameters(delta);
        reservedParameterCount += delta;
      }
  
      private void addReservedRegisters(int delta) {
        shiftAllRegisters(delta);
        reservedCount += delta;
      }
  
      private void shiftAllRegisters(int delta) {
        int insnSize = insns.size();
  
        for (int i = 0; i < insnSize; i++) {
          DalvInsn insn = insns.get(i);
          // Since there is no need to replace CodeAddress since it does not use registers, skips it to
          // avoid to update all TargetInsn that contain a reference to CodeAddress
          if (!(insn instanceof CodeAddress)) {
            insns.set(i, insn.withRegisterOffset(delta));
          }
        }
      }
  
      private void shiftParameters(int delta) {
        int insnSize = insns.size();
        int lastParameter = unreservedRegCount + reservedCount + reservedParameterCount;
        int firstParameter = lastParameter - paramSize;
  
        BasicRegisterMapper mapper = new BasicRegisterMapper(lastParameter);
        for (int i = 0; i < lastParameter; i++) {
          if (i >= firstParameter) {
            mapper.addMapping(i, i + delta, 1);
          } else {
            mapper.addMapping(i, i, 1);
          }
        }
  
        for (int i = 0; i < insnSize; i++) {
          DalvInsn insn = insns.get(i);
          // Since there is no need to replace CodeAddress since it does not use registers, skips it to
          // avoid to update all TargetInsn that contain a reference to CodeAddress
          if (!(insn instanceof CodeAddress)) {
            insns.set(i, insn.withMapper(mapper));
          }
        }
      }
  }
  