ASMJIT_FAVOR_SIZE Error X86Internal::emitArgMove()

in erts/emulator/asmjit/x86/x86internal.cpp [1063:1282]


ASMJIT_FAVOR_SIZE Error X86Internal::emitArgMove(Emitter* emitter,
  const Reg& dst_, uint32_t dstTypeId,
  const Operand_& src_, uint32_t srcTypeId, bool avxEnabled, const char* comment) {

  // Deduce optional `dstTypeId`, which may be `Type::kIdVoid` in some cases.
  if (!dstTypeId)
    dstTypeId = opData.archRegs.regTypeToTypeId[dst_.type()];

  // Invalid or abstract TypeIds are not allowed.
  ASMJIT_ASSERT(Type::isValid(dstTypeId) && !Type::isAbstract(dstTypeId));
  ASMJIT_ASSERT(Type::isValid(srcTypeId) && !Type::isAbstract(srcTypeId));

  Reg dst(dst_);
  Operand src(src_);

  uint32_t dstSize = Type::sizeOf(dstTypeId);
  uint32_t srcSize = Type::sizeOf(srcTypeId);

  uint32_t instId = Inst::kIdNone;

  // Not a real loop, just 'break' is nicer than 'goto'.
  for (;;) {
    if (Type::isInt(dstTypeId)) {
      if (Type::isInt(srcTypeId)) {
        instId = Inst::kIdMovsx;
        uint32_t typeOp = (dstTypeId << 8) | srcTypeId;

        // Sign extend by using 'movsx'.
        if (typeOp == ((Type::kIdI16 << 8) | Type::kIdI8 ) ||
            typeOp == ((Type::kIdI32 << 8) | Type::kIdI8 ) ||
            typeOp == ((Type::kIdI32 << 8) | Type::kIdI16) ||
            typeOp == ((Type::kIdI64 << 8) | Type::kIdI8 ) ||
            typeOp == ((Type::kIdI64 << 8) | Type::kIdI16))
          break;

        // Sign extend by using 'movsxd'.
        instId = Inst::kIdMovsxd;
        if (typeOp == ((Type::kIdI64 << 8) | Type::kIdI32))
          break;
      }

      if (Type::isInt(srcTypeId) || src_.isMem()) {
        // Zero extend by using 'movzx' or 'mov'.
        if (dstSize <= 4 && srcSize < 4) {
          instId = Inst::kIdMovzx;
          dst.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
        }
        else {
          // We should have caught all possibilities where `srcSize` is less
          // than 4, so we don't have to worry about 'movzx' anymore. Minimum
          // size is enough to determine if we want 32-bit or 64-bit move.
          instId = Inst::kIdMov;
          srcSize = Support::min(srcSize, dstSize);

          dst.setSignature(srcSize == 4 ? Reg::signatureOfT<Reg::kTypeGpd>()
                                        : Reg::signatureOfT<Reg::kTypeGpq>());
          if (src.isReg())
            src.setSignature(dst.signature());
        }
        break;
      }

      // NOTE: The previous branch caught all memory sources, from here it's
      // always register to register conversion, so catch the remaining cases.
      srcSize = Support::min(srcSize, dstSize);

      if (Type::isMmx(srcTypeId)) {
        // 64-bit move.
        instId = Inst::kIdMovq;
        if (srcSize == 8)
          break;

        // 32-bit move.
        instId = Inst::kIdMovd;
        dst.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
        break;
      }

      if (Type::isMask(srcTypeId)) {
        instId = x86KmovFromSize(srcSize);
        dst.setSignature(srcSize <= 4 ? Reg::signatureOfT<Reg::kTypeGpd>()
                                      : Reg::signatureOfT<Reg::kTypeGpq>());
        break;
      }

      if (Type::isVec(srcTypeId)) {
        // 64-bit move.
        instId = avxEnabled ? Inst::kIdVmovq : Inst::kIdMovq;
        if (srcSize == 8)
          break;

        // 32-bit move.
        instId = avxEnabled ? Inst::kIdVmovd : Inst::kIdMovd;
        dst.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
        break;
      }
    }

    if (Type::isMmx(dstTypeId)) {
      instId = Inst::kIdMovq;
      srcSize = Support::min(srcSize, dstSize);

      if (Type::isInt(srcTypeId) || src.isMem()) {
        // 64-bit move.
        if (srcSize == 8)
          break;

        // 32-bit move.
        instId = Inst::kIdMovd;
        if (src.isReg())
          src.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
        break;
      }

      if (Type::isMmx(srcTypeId))
        break;

      // This will hurt if `avxEnabled`.
      instId = Inst::kIdMovdq2q;
      if (Type::isVec(srcTypeId))
break;
    }

    if (Type::isMask(dstTypeId)) {
      srcSize = Support::min(srcSize, dstSize);

      if (Type::isInt(srcTypeId) || Type::isMask(srcTypeId) || src.isMem()) {
        instId = x86KmovFromSize(srcSize);
        if (Reg::isGp(src) && srcSize <= 4)
          src.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
        break;
      }
    }

    if (Type::isVec(dstTypeId)) {
      // By default set destination to XMM, will be set to YMM|ZMM if needed.
      dst.setSignature(Reg::signatureOfT<Reg::kTypeXmm>());

      // This will hurt if `avxEnabled`.
      if (Reg::isMm(src)) {
        // 64-bit move.
        instId = Inst::kIdMovq2dq;
        break;
      }

      // Argument conversion.
      uint32_t dstElement = Type::baseOf(dstTypeId);
      uint32_t srcElement = Type::baseOf(srcTypeId);

      if (dstElement == Type::kIdF32 && srcElement == Type::kIdF64) {
        srcSize = Support::min(dstSize * 2, srcSize);
        dstSize = srcSize / 2;

        if (srcSize <= 8)
          instId = avxEnabled ? Inst::kIdVcvtss2sd : Inst::kIdCvtss2sd;
        else
          instId = avxEnabled ? Inst::kIdVcvtps2pd : Inst::kIdCvtps2pd;

        if (dstSize == 32)
          dst.setSignature(Reg::signatureOfT<Reg::kTypeYmm>());
        if (src.isReg())
          src.setSignature(Reg::signatureOfVecBySize(srcSize));
        break;
      }

      if (dstElement == Type::kIdF64 && srcElement == Type::kIdF32) {
        srcSize = Support::min(dstSize, srcSize * 2) / 2;
        dstSize = srcSize * 2;

        if (srcSize <= 4)
          instId = avxEnabled ? Inst::kIdVcvtsd2ss : Inst::kIdCvtsd2ss;
        else
          instId = avxEnabled ? Inst::kIdVcvtpd2ps : Inst::kIdCvtpd2ps;

        dst.setSignature(Reg::signatureOfVecBySize(dstSize));
        if (src.isReg() && srcSize >= 32)
          src.setSignature(Reg::signatureOfT<Reg::kTypeYmm>());
        break;
      }

      srcSize = Support::min(srcSize, dstSize);
      if (Reg::isGp(src) || src.isMem()) {
        // 32-bit move.
        if (srcSize <= 4) {
          instId = avxEnabled ? Inst::kIdVmovd : Inst::kIdMovd;
          if (src.isReg())
            src.setSignature(Reg::signatureOfT<Reg::kTypeGpd>());
          break;
        }

        // 64-bit move.
        if (srcSize == 8) {
          instId = avxEnabled ? Inst::kIdVmovq : Inst::kIdMovq;
          break;
        }
      }

      if (Reg::isVec(src) || src.isMem()) {
        instId = avxEnabled ? Inst::kIdVmovaps : Inst::kIdMovaps;

        if (src.isMem() && srcSize < emitter->environment().stackAlignment())
          instId = avxEnabled ? Inst::kIdVmovups : Inst::kIdMovups;

        uint32_t signature = Reg::signatureOfVecBySize(srcSize);
        dst.setSignature(signature);
        if (src.isReg())
          src.setSignature(signature);
        break;
      }
    }

    return DebugUtils::errored(kErrorInvalidState);
  }

  if (src.isMem())
    src.as<Mem>().setSize(srcSize);

  emitter->setInlineComment(comment);
  return emitter->emit(instId, dst, src);
}