bool X86MachineInstructionRaiser::raiseBinaryOpMemToRegInstr()

in X86/X86MachineInstructionRaiser.cpp [1558:1821]


bool X86MachineInstructionRaiser::raiseBinaryOpMemToRegInstr(
    const MachineInstr &MI, Value *MemRefValue) {
  unsigned int Opcode = MI.getOpcode();
  const MCInstrDesc &MIDesc = MI.getDesc();
  std::set<unsigned> AffectedEFlags;
  std::set<unsigned> ClearedEFlags;

  assert((MIDesc.getNumDefs() == 1) &&
         "Encountered memory load instruction with more than 1 defs");
  unsigned int DestIndex = 0;
  const MachineOperand &DestOp = MI.getOperand(DestIndex);
  assert(DestOp.isReg() &&
         "Expect destination register operand in binary reg/mem instruction");

  // Get the BasicBlock corresponding to MachineBasicBlock of MI.
  // Raised instruction is added to this BasicBlock.
  BasicBlock *RaisedBB = getRaisedBasicBlock(MI.getParent());

  unsigned int DestPReg = DestOp.getReg();
  unsigned int MemAlignment = getInstructionMemOpSize(Opcode);
  Type *DestopTy = getPhysRegOperandType(MI, DestIndex);
  Value *DestValue = getRegOrArgValue(DestPReg, MI.getParent()->getNumber());

  // Verify sanity of the instruction.
  // SSE2 registers are always of a fixed size and might not be equal to
  // MemAlignment
  assert((isSSE2Reg(DestOp.getReg()) ||
          getPhysRegOperandSize(MI, DestIndex) == MemAlignment) &&
         "Mismatched destination register size and instruction size of binary "
         "op instruction");

  unsigned int MemoryRefOpIndex = getMemoryRefOpIndex(MI);
  Value *LoadValue =
      loadMemoryRefValue(MI, MemRefValue, MemoryRefOpIndex, DestopTy);
  if (DestValue != nullptr) {
    // Cast DestValue to the DestopTy, as for single-precision FP ops
    // DestValue type and DestopTy might be different.
    if (isSSE2Reg(DestPReg)) {
      DestValue = getRaisedValues()->reinterpretSSERegValue(DestValue, DestopTy,
                                                            RaisedBB);
    } else {
      DestValue = getRaisedValues()->castValue(DestValue, DestopTy, RaisedBB);
    }
  }
  Instruction *BinOpInst = nullptr;

  switch (Opcode) {
  case X86::ADD64rm:
  case X86::ADD32rm:
  case X86::ADD16rm:
  case X86::ADD8rm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    // Create add instruction
    BinOpInst = BinaryOperator::CreateAdd(DestValue, LoadValue);
    AffectedEFlags.insert(EFLAGS::PF);
  } break;
  case X86::AND64rm:
  case X86::AND32rm:
  case X86::AND16rm:
  case X86::AND8rm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    // Create and instruction
    BinOpInst = BinaryOperator::CreateAnd(DestValue, LoadValue);
    AffectedEFlags.insert(EFLAGS::PF);
  } break;
  case X86::OR32rm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    // Create or instruction
    BinOpInst = BinaryOperator::CreateOr(DestValue, LoadValue);
    AffectedEFlags.insert(EFLAGS::PF);
  } break;
  case X86::IMUL16rm:
  case X86::IMUL32rm:
  case X86::IMUL64rm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    // One-operand form of IMUL
    // Create mul instruction
    BinOpInst = BinaryOperator::CreateMul(DestValue, LoadValue);
  } break;
  case X86::IMUL16rmi:
  case X86::IMUL16rmi8:
  case X86::IMUL32rmi:
  case X86::IMUL32rmi8:
  case X86::IMUL64rmi8:
  case X86::IMUL64rmi32: {
    // We don't read from DestValue, so we don't need to check if it is null
    // Three-operand form of IMUL
    // Get index of memory reference in the instruction.
    int MemoryRefOpIndex = getMemoryRefOpIndex(MI);
    // The index of the memory reference operand should be 1
    assert(MemoryRefOpIndex == 1 &&
           "Unexpected memory reference operand index in imul instruction");
    const MachineOperand &SecondSourceOp =
        MI.getOperand(MemoryRefOpIndex + X86::AddrNumOperands);
    // Second source should be an immediate.
    assert(SecondSourceOp.isImm() &&
           "Expect immediate operand in imul instruction");
    // Construct the value corresponding to immediate operand
    Value *SecondSourceVal =
        ConstantInt::get(LoadValue->getType(), SecondSourceOp.getImm());
    // Create mul instruction
    BinOpInst = BinaryOperator::CreateMul(SecondSourceVal, LoadValue);
  } break;
  case X86::XOR8rm:
  case X86::XOR16rm:
  case X86::XOR32rm:
  case X86::XOR64rm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    // Create xor instruction
    BinOpInst = BinaryOperator::CreateXor(DestValue, LoadValue);
    ClearedEFlags.insert(EFLAGS::OF);
    ClearedEFlags.insert(EFLAGS::CF);
    AffectedEFlags.insert(EFLAGS::SF);
    AffectedEFlags.insert(EFLAGS::ZF);
    AffectedEFlags.insert(EFLAGS::PF);
  } break;
  case X86::ADDSSrm_Int:
  case X86::ADDSDrm_Int: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    BinOpInst = BinaryOperator::CreateFAdd(DestValue, LoadValue);
  } break;
  case X86::SUBSSrm_Int:
  case X86::SUBSDrm_Int: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    BinOpInst = BinaryOperator::CreateFSub(DestValue, LoadValue);
  } break;
  case X86::MULSDrm_Int:
  case X86::MULSSrm_Int: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    BinOpInst = BinaryOperator::CreateFMul(DestValue, LoadValue);
  } break;
  case X86::DIVSDrm_Int:
  case X86::DIVSSrm_Int: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    BinOpInst = BinaryOperator::CreateFDiv(DestValue, LoadValue);
  } break;
  case X86::MAXSDrm_Int:
  case X86::MAXSSrm_Int:
  case X86::MINSDrm_Int:
  case X86::MINSSrm_Int: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    bool isMax = instrNameStartsWith(MI, "MAX");
    std::string nameString = isMax ? "max" : "min";

    auto CmpType = isMax ? CmpInst::FCMP_OGT : CmpInst::FCMP_OLT;
    Instruction *CmpInst =
        new FCmpInst(*RaisedBB, CmpType, DestValue, LoadValue, "cmp");

    BinOpInst = SelectInst::Create(CmpInst, DestValue, LoadValue, nameString);
  } break;
  case X86::ANDPDrm:
  case X86::ANDPSrm:
  case X86::ANDNPDrm:
  case X86::ANDNPSrm:
  case X86::ORPDrm:
  case X86::ORPSrm:
  case X86::XORPDrm:
  case X86::XORPSrm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    LLVMContext &Ctx(MF.getFunction().getContext());
    auto Int128Ty = Type::getInt128Ty(Ctx);
    // BitCast operands to integer types to perform and/or/xor operation
    auto DestValueInt = new BitCastInst(DestValue, Int128Ty, "", RaisedBB);
    auto LoadValueInt = new BitCastInst(LoadValue, Int128Ty, "", RaisedBB);

    Value *Result;
    switch (Opcode) {
    case X86::ANDPDrm:
    case X86::ANDPSrm:
      Result = BinaryOperator::CreateAnd(DestValueInt, LoadValueInt, "", RaisedBB);
      break;
    case X86::ANDNPDrm:
    case X86::ANDNPSrm: {
      auto NotVal = BinaryOperator::CreateNot(DestValueInt, "", RaisedBB);
      Result = BinaryOperator::CreateAnd(NotVal, LoadValueInt, "", RaisedBB);
    } break;
    case X86::ORPDrm:
    case X86::ORPSrm:
      Result = BinaryOperator::CreateOr(DestValueInt, LoadValueInt, "", RaisedBB);
      break;
    case X86::XORPDrm:
    case X86::XORPSrm:
      Result = BinaryOperator::CreateXor(DestValueInt, LoadValueInt, "", RaisedBB);
      break;
    default:
      llvm_unreachable("Unhandled opcode for packed bitwise instruction");
    }
    // Cast back to operand type
    BinOpInst = new BitCastInst(Result, DestopTy);
  } break;
  case X86::PANDrm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    BinOpInst = BinaryOperator::CreateAnd(DestValue, LoadValue);
  } break;
  case X86::PANDNrm: {
    DestValue = BinaryOperator::CreateNot(DestValue, "", RaisedBB);
    BinOpInst = BinaryOperator::CreateAnd(DestValue, LoadValue);
  } break;
  case X86::PORrm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    BinOpInst = BinaryOperator::CreateOr(DestValue, LoadValue);
  } break;
  case X86::PXORrm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    BinOpInst = BinaryOperator::CreateXor(DestValue, LoadValue);
  } break;
  case X86::SBB16rm:
  case X86::SBB32rm:
  case X86::SBB64rm:
  case X86::SBB8rm: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    auto CFValue = getRegOrArgValue(EFLAGS::CF, MI.getParent()->getNumber());
    assert(CFValue && "Expected CF to be set for sbb instruction");

    auto CFExtended = CastInst::Create(Instruction::ZExt, CFValue,
                                       LoadValue->getType(), "", RaisedBB);

    auto LoadAndCFValue =
        BinaryOperator::CreateAdd(LoadValue, CFExtended, "", RaisedBB);

    BinOpInst = BinaryOperator::CreateSub(DestValue, LoadAndCFValue);

    AffectedEFlags.insert(EFLAGS::OF);
    AffectedEFlags.insert(EFLAGS::CF);
  } break;
  case X86::SQRTSDm:
  case X86::SQRTSDm_Int:
  case X86::SQRTSSm:
  case X86::SQRTSSm_Int: {
    assert(DestValue != nullptr && "Encountered instruction with undefined register");
    LLVMContext &Ctx(MF.getFunction().getContext());

    Type *InstrTy = getRaisedValues()->getSSEInstructionType(MI, Ctx);
    assert(LoadValue->getType() == InstrTy &&
           "Unexpected value type for sqrtsd/sqrtss instruction");

    Module *M = MR->getModule();
    auto IntrinsicFunc = Intrinsic::getDeclaration(M, Intrinsic::sqrt, InstrTy);

    Value *IntrinsicCallArgs[] = {LoadValue};
    BinOpInst = CallInst::Create(
        IntrinsicFunc, ArrayRef<Value *>(IntrinsicCallArgs));
  } break;
  default:
    assert(false && "Unhandled binary op mem to reg instruction ");
  }
  // Add instruction to block
  getRaisedValues()->setInstMetadataRODataIndex(LoadValue, BinOpInst);
  RaisedBB->getInstList().push_back(BinOpInst);

  // Clear EFLAGS if any
  int MMBNo = MI.getParent()->getNumber();
  for (auto F : ClearedEFlags)
    raisedValues->setEflagBoolean(F, MMBNo, false);

  // Test and set affected flags
  for (auto Flag : AffectedEFlags)
    raisedValues->testAndSetEflagSSAValue(Flag, MI, BinOpInst);

  // Update PhysReg to Value map
  raisedValues->setPhysRegSSAValue(DestPReg, MI.getParent()->getNumber(),
                                   BinOpInst);
  return true;
}