bool X86MachineInstructionRaiser::raiseCompareMachineInstr()

in X86/X86MachineInstructionRaiser.cpp [2864:3110]


bool X86MachineInstructionRaiser::raiseCompareMachineInstr(
    const MachineInstr &MI, bool isMemCompare, Value *MemRefValue) {
  // Ensure this is a compare instruction
  assert(MI.isCompare() && "Compare instruction expected");
  // Get index of memory reference in the instruction.
  int memoryRefOpIndex = getMemoryRefOpIndex(MI);
  int MBBNo = MI.getParent()->getNumber();
  unsigned int DestReg = X86::NoRegister;
  assert((((memoryRefOpIndex != -1) && isMemCompare) ||
          ((memoryRefOpIndex == -1) && !isMemCompare)) &&
         "Inconsistent memory reference operand information specified for "
         "compare instruction");
  MCInstrDesc MCIDesc = MI.getDesc();
  unsigned NumImplicitUses = MCIDesc.getNumImplicitUses();
  // Get the BasicBlock corresponding to MachineBasicBlock of MI.
  // Raised instruction is added to this BasicBlock.
  BasicBlock *RaisedBB = getRaisedBasicBlock(MI.getParent());

  // Three instructions viz., CMP*, SUB* and TEST* are classified by LLVM as
  // compare instructions Is this a sub instruction?
  bool isCMPInst = instrNameStartsWith(MI, "CMP");
  bool isSUBInst = instrNameStartsWith(MI, "SUB");
  bool isTESTInst = instrNameStartsWith(MI, "TEST");

  assert((isCMPInst || isSUBInst || isTESTInst) &&
         "Unhandled memory referencing compare instruction");
  SmallVector<Value *, 2> OpValues{nullptr, nullptr};

  // Get operand indices
  if (isMemCompare) {
    // This is a memory referencing instruction.
    Type *NonMemRefOpTy;
    const MachineOperand *NonMemRefOp;
    assert(memoryRefOpIndex >= 0 &&
           "Unexpected memory operand index in compare instruction");
    unsigned nonMemRefOpIndex =
        (memoryRefOpIndex == 0) ? X86::AddrNumOperands : 0;
    NonMemRefOp = &(MI.getOperand(nonMemRefOpIndex));
    if (NonMemRefOp->isReg()) {
      NonMemRefOpTy = getPhysRegOperandType(MI, nonMemRefOpIndex);
    } else if (NonMemRefOp->isImm()) {
      LLVMContext &Ctx(MF.getFunction().getContext());
      auto MemOpSize = getInstructionMemOpSize(MI.getOpcode());
      assert(MemOpSize != 0 && "Expected mem op size to be > 0");
      NonMemRefOpTy = Type::getIntNTy(Ctx, MemOpSize * 8);
    } else {
      LLVM_DEBUG(MI.dump());
      assert(false && "Unhandled second operand type in compare instruction");
    }

    assert(MemRefValue != nullptr && "Null memory reference value encountered "
                                     "while raising compare instruction");
    // Convert it to a pointer of type of non-memory operand
    if (isEffectiveAddrValue(MemRefValue)) {
      PointerType *PtrTy = PointerType::get(NonMemRefOpTy, 0);
      IntToPtrInst *convIntToPtr = new IntToPtrInst(MemRefValue, PtrTy);
      RaisedBB->getInstList().push_back(convIntToPtr);
      MemRefValue = convIntToPtr;
    }
    // Load the value from memory location
    auto align =
        MemRefValue->getPointerAlignment(MR->getModule()->getDataLayout());
    LoadInst *loadInst =
        new LoadInst(MemRefValue->getType()->getPointerElementType(),
                     MemRefValue, "", false, align, RaisedBB);
    // save it at the appropriate index of operand value array
    if (memoryRefOpIndex == 0) {
      OpValues[0] = loadInst;
    } else {
      OpValues[1] = loadInst;
    }

    // Get value for non-memory operand of compare.
    Value *NonMemRefVal = nullptr;
    if (NonMemRefOp->isReg()) {
      NonMemRefVal = getRegOrArgValue(NonMemRefOp->getReg(), MBBNo);
    } else if (NonMemRefOp->isImm()) {
      NonMemRefVal =
          ConstantInt::get(MemRefValue->getType()->getPointerElementType(),
                           NonMemRefOp->getImm());
    } else {
      LLVM_DEBUG(MI.dump());
      assert(false && "Unhandled first operand type in compare instruction");
    }
    // save non-memory reference value at the appropriate index of operand
    // value array
    if (memoryRefOpIndex == 0) {
      OpValues[1] = NonMemRefVal;
    } else {
      OpValues[0] = NonMemRefVal;
    }
  } else {
    // The instruction operands do not reference memory
    unsigned Op1Index, Op2Index;

    // Determine the appropriate operand indices of the instruction based on the
    // usage of implicit registers. Note that a cmp instruction is translated as
    // sub op1, op2 (i.e., op1 - op2).
    if (NumImplicitUses == 1) {
      // If an implicit operand is used, that is op1.
      MCPhysReg UseReg = MCIDesc.ImplicitUses[0];
      Op1Index = MI.findRegisterUseOperandIdx(UseReg, false, nullptr);
      Op2Index = MCIDesc.getNumDefs() == 0 ? 0 : 1;
    } else {
      // Explicit operands are used
      Op1Index = MCIDesc.getNumDefs() == 0 ? 0 : 1;
      Op2Index = Op1Index + 1;
    }

    MachineOperand CmpOp1 = MI.getOperand(Op1Index);
    MachineOperand CmpOp2 = MI.getOperand(Op2Index);

    assert((CmpOp1.isReg() || CmpOp1.isImm()) &&
           "Unhandled first operand type in compare instruction");

    assert((CmpOp2.isReg() || CmpOp2.isImm()) &&
           "Unhandled second operand type in compare instruction");

    if (CmpOp1.isReg()) {
      OpValues[0] = getRegOrArgValue(CmpOp1.getReg(), MBBNo);
    }

    if (CmpOp2.isReg()) {
      OpValues[1] = getRegOrArgValue(CmpOp2.getReg(), MBBNo);
    }

    // Construct value if either of the operands is an immediate
    if (CmpOp1.isImm()) {
      assert((OpValues[1] != nullptr) &&
             "At least one value expected while raising compare instruction");
      OpValues[0] = ConstantInt::get(OpValues[1]->getType(), CmpOp1.getImm());
    }

    if (CmpOp2.isImm()) {
      assert((OpValues[0] != nullptr) &&
             "At least one value expected while raising compare instruction");
      OpValues[1] = ConstantInt::get(OpValues[0]->getType(), CmpOp2.getImm());
    }
  }
  assert(OpValues[0] != nullptr && OpValues[1] != nullptr &&
         "Unable to materialize compare operand values");

  // If the first operand is register, make sure the source operand value types
  // are the same as destination register type.
  if (MI.getOperand(0).isReg()) {
    DestReg = MI.getOperand(0).getReg();
    if (DestReg != X86::NoRegister) {
      Type *DestTy = getPhysRegOperandType(MI, 0);
      for (int i = 0; i < 2; i++) {
        if (OpValues[i]->getType() != DestTy) {
          CastInst *CInst = CastInst::Create(
              CastInst::getCastOpcode(OpValues[i], false, DestTy, false),
              OpValues[i], DestTy);
          RaisedBB->getInstList().push_back(CInst);
          OpValues[i] = CInst;
        }
      }
    }
  }

  // If the number of implicit use operand is one, make sure the source operand
  // value type is the same as the implicit use operand value type.
  if (NumImplicitUses == 1) {
    if (OpValues[0]->getType() != OpValues[1]->getType()) {
      CastInst *CInst = CastInst::Create(
          CastInst::getCastOpcode(OpValues[0], false, OpValues[1]->getType(),
                                  false),
          OpValues[0], OpValues[1]->getType());
      RaisedBB->getInstList().push_back(CInst);
      OpValues[0] = CInst;
    }
  }

  assert((OpValues[0]->getType() == OpValues[1]->getType()) &&
         "Mis-matched operand types encountered while raising compare "
         "instruction");

  raisedValues->setEflagBoolean(EFLAGS::OF, MBBNo, false);
  raisedValues->setEflagBoolean(EFLAGS::CF, MBBNo, false);
  // CmpInst is of type Value * to allow for a potential need to pass it to
  // castValue(), if needed.
  // If MI is a test instruction, the compare instruction should be an and
  // instruction.
  Value *CmpInst = (isTESTInst)
                       ? BinaryOperator::CreateAnd(OpValues[0], OpValues[1])
                       : BinaryOperator::CreateSub(OpValues[0], OpValues[1]);
  // Casting CmpInst to instruction to be added to the raised basic
  // block is correct since it is known to be specifically of type Instruction.
  RaisedBB->getInstList().push_back(dyn_cast<Instruction>(CmpInst));

  if (isSUBInst) {
    switch (MI.getOpcode()) {
    case X86::SUB8mi:
    case X86::SUB8mi8:
    case X86::SUB8mr:
    case X86::SUB16mi:
    case X86::SUB16mi8:
    case X86::SUB16mr:
    case X86::SUB32mi:
    case X86::SUB32mi8:
    case X86::SUB32mr:
    case X86::SUB64mi8:
    case X86::SUB64mi32:
    case X86::SUB64mr: {
      // This instruction moves a source value to memory. So, if the types of
      // the source value and that of the memory pointer content are not the
      // same, it is the source value that needs to be cast to match the type of
      // destination (i.e., memory). It needs to be sign extended as needed.
      Type *MatchTy = MemRefValue->getType()->getPointerElementType();
      if (!MatchTy->isArrayTy()) {
        CmpInst = getRaisedValues()->castValue(CmpInst, MatchTy, RaisedBB);
      }

      // Store CmpInst to MemRefValue only if this is a sub MI or MR
      // instruction. Do not update if this is a cmp instruction.
      new StoreInst(CmpInst, MemRefValue, RaisedBB);
    } break;
    case X86::SUB32rr:
    case X86::SUB64rr:
    case X86::SUB8rm:
    case X86::SUB32rm:
    case X86::SUB64rm: {
      assert(MCIDesc.getNumDefs() == 1 &&
             "Unexpected number of def operands of sub instruction");
      // Update the DestReg only if this is a sub instruction. Do not update
      // if this is a cmp instruction
      raisedValues->setPhysRegSSAValue(DestReg, MBBNo, CmpInst);
    } break;
    default:
      assert(false && "Unhandled sub instruction found");
    }
  }
  // Now update EFLAGS
  assert(MCIDesc.getNumImplicitDefs() == 1 &&
         "Compare instruction does not have exactly one implicit def");
  MCPhysReg ImpDefReg = MCIDesc.ImplicitDefs[0];
  assert(ImpDefReg == X86::EFLAGS &&
         "Expected implicit EFLAGS def in compare instruction");
  // Create instructions to set CF, ZF, SF, PF, and OF flags according to the
  // result CmpInst. NOTE: Support for tracking AF not yet implemented.
  raisedValues->testAndSetEflagSSAValue(EFLAGS::CF, MI, CmpInst);
  raisedValues->testAndSetEflagSSAValue(EFLAGS::ZF, MI, CmpInst);
  raisedValues->testAndSetEflagSSAValue(EFLAGS::SF, MI, CmpInst);
  raisedValues->testAndSetEflagSSAValue(EFLAGS::OF, MI, CmpInst);
  raisedValues->testAndSetEflagSSAValue(EFLAGS::PF, MI, CmpInst);
  return true;
}