/**
   * Calculates the permutations to convert between source and destination indices (where source is
   * the generic instruction, destination is the target architecture specific instruction).
   *
   * @param intrinsic The intrinsic
   * @param rgSourceOps The array of source operands of the instruction
   * @param rgDestArgs The array of destination operands (the intrinsic arguments)
   * @param rgPermSourceToDest This array will contain the source → destination permutation on
   *     exit of the method
   * @param rgPermDestToSource This array will contain the destination → source permutation on
   *     exit of the method
   */
  private static void getArgumentPermutations(
      Intrinsic intrinsic,
      IOperand[] rgSourceOps,
      Argument[] rgDestArgs,
      int[] rgPermSourceToDest,
      int[] rgPermDestToSource) {
    Arrays.fill(rgPermSourceToDest, UNDEFINED);
    Arrays.fill(rgPermDestToSource, UNDEFINED);

    String[] rgArgNamesGeneric = Globals.getIntrinsicArguments(intrinsic.getBaseName());

    if (rgArgNamesGeneric != null) {
      for (int i = 0; i < rgSourceOps.length; i++) {
        rgPermSourceToDest[i] =
            i < rgSourceOps.length - 1
                ? InstructionListTranslator.getArgNum(rgDestArgs, rgArgNamesGeneric[i])
                : InstructionListTranslator.getOutputArgumentIndex(
                    rgDestArgs); // last argument is output

        if (0 <= rgPermSourceToDest[i] && rgPermSourceToDest[i] < rgPermDestToSource.length) {
          if (rgPermDestToSource[rgPermSourceToDest[i]] == UNDEFINED)
            rgPermDestToSource[rgPermSourceToDest[i]] = i;
        }
      }
    } else {
      // no intrinsic arguments defined for this intrinsic; just return the identity permutation
      for (int i = 0; i < rgPermSourceToDest.length; i++) rgPermSourceToDest[i] = i;
      for (int i = 0; i < rgPermDestToSource.length; i++) rgPermDestToSource[i] = i;
    }
  }
  /**
   * If instruction arguments are not compatible (e.g., an argument is passed by address, but the
   * intrinsic requires a register), this method tries to find operands which can be interchanged
   * and which make the operands compatible after interchanging.
   *
   * @param intrinsic The intrinsic to issue
   * @param rgSourceOps The array of operands of the original, generic instruction
   * @param rgDestArgs The array of intrinsic arguments
   * @param rgPermSourceToDest The source-to-destination permutation
   * @param rgPermDestToSource The destination-to-source permutation
   * @return
   */
  private static IOperand[] compatibilizeCommutatives(
      Intrinsic intrinsic,
      IOperand[] rgSourceOps,
      Argument[] rgDestArgs,
      int[] rgPermSourceToDest,
      int[] rgPermDestToSource,
      int nOutputArgDestIndex,
      boolean bIntrinsicHasSharedResult) {
    if (bIntrinsicHasSharedResult) {
      // if there is an input argument that is the same as the output argument
      // try to permute it to the "shared" position

      IOperand opShared = rgSourceOps[rgPermDestToSource[nOutputArgDestIndex]];
      int nCommonInOutIdx =
          InstructionListTranslator.getIndexOfNonSharedInputArgsResult(rgSourceOps, opShared);
      if (nCommonInOutIdx != -1) {
        int nOldPos = nCommonInOutIdx;
        int nNewPos = rgPermDestToSource[nOutputArgDestIndex];

        if (Globals.canSwapIntrinsicArguments(intrinsic.getBaseName(), nOldPos, nNewPos)
            && InstructionListTranslator.isCompatible(
                rgSourceOps[nOldPos], rgDestArgs[rgPermSourceToDest[nNewPos]])
            && InstructionListTranslator.isCompatible(
                rgSourceOps[nNewPos], rgDestArgs[rgPermSourceToDest[nOldPos]])) {
          // do the swap
          IOperand[] rgSourceOpsSwapped = new IOperand[rgSourceOps.length];
          for (int k = 0; k < rgSourceOpsSwapped.length; k++) {
            if (k == nOldPos) rgSourceOpsSwapped[k] = rgSourceOps[nNewPos];
            else if (k == nNewPos) rgSourceOpsSwapped[k] = rgSourceOps[nOldPos];
            else rgSourceOpsSwapped[k] = rgSourceOps[k];
          }

          return rgSourceOpsSwapped;
        }
      }
    }

    for (int i = 0; i < rgSourceOps.length; i++) {
      if (rgPermSourceToDest[i] != UNDEFINED) {
        if (!InstructionListTranslator.isCompatible(
            rgSourceOps[i], rgDestArgs[rgPermSourceToDest[i]])) {
          // try to find an argument which can be swapped with this non-compatible one
          // and check whether both operands are compatible after swapping

          for (int j = 0; j < rgSourceOps.length - 1; j++) {
            if (i == j) continue;

            if (Globals.canSwapIntrinsicArguments(intrinsic.getBaseName(), i, j)) {
              if (rgPermSourceToDest[j] != UNDEFINED
                  && InstructionListTranslator.isCompatible(
                      rgSourceOps[i], rgDestArgs[rgPermSourceToDest[j]])
                  && InstructionListTranslator.isCompatible(
                      rgSourceOps[j], rgDestArgs[rgPermSourceToDest[i]])) {
                // assume that only one swap is to be done, i.e., there are only
                // two arguments that can be interchanged

                // do the swap
                IOperand[] rgSourceOpsSwapped = new IOperand[rgSourceOps.length];
                for (int k = 0; k < rgSourceOpsSwapped.length; k++) {
                  if (k == i) rgSourceOpsSwapped[k] = rgSourceOps[j];
                  else if (k == j) rgSourceOpsSwapped[k] = rgSourceOps[i];
                  else rgSourceOpsSwapped[k] = rgSourceOps[k];
                }

                return rgSourceOpsSwapped;
              }
            }
          }
        }
      }
    }

    return rgSourceOps;
  }