Skip to content

Commit

Permalink
[DPWBS-1232] feat(RegisterInfo): add new pseudo CarryReg regclass, St…
Browse files Browse the repository at this point in the history
…atusRegBank regbank

To properly model the carry-bit register of overflow ops we need to have
a proper register bank and class. This commit adds the unallocatable
pseudo register class CarryReg, and the register bank StatusRegBank.

Furthermore, this commit marks the PSW_C register as reserved. This is
required since it is a system register and because the super register
PSW is also reserved.

StatusRegBank is only used by carry arithmetics. For TriCore this mean
only add/sub with carry (G_UADDO/UADDE/USUBO/USUBE). The mapping is only
supported for 32-bit non-carry and 1-bit carry operands. The non-carry
operands are mapped to the DataRegBank while the carry operands are
mapped to the StatusRegBank.

During instruction selection, carry-bit operands are constrained to the
CarryReg class

Since PSW_C is marked as a reserved register, a StatusRegBank COPY with
it as an operand cannot be emitted, as the register allocator would not
have a non-reserved register to allocate. Therefore the virtual
carry-out registers must be replaced with $psw_c directly.

To do this, this commit adds a check during the selection of carry-out
producers if it is safe to replace the virtual carry-out register with
the physical $psw_c register. Only if this check succeeds is selection
performed and the virtual register replaced with the physical one.

update tests for add/sub with carry.
  • Loading branch information
gargaroff authored and konstantinschwarz committed Mar 18, 2020
1 parent ebcee19 commit 5a4494a
Show file tree
Hide file tree
Showing 13 changed files with 665 additions and 138 deletions.
41 changes: 37 additions & 4 deletions llvm/lib/Target/TriCore/TriCoreGenRegisterBankInfo.def
Expand Up @@ -21,6 +21,8 @@ RegisterBankInfo::PartialMapping TriCoreGenRegisterBankInfo::PartMappings[]{
{0, 32, TriCore::AddrRegBank},
// 3: ExtAddrReg 64-bit value.
{0, 64, TriCore::AddrRegBank},
// 4: CarryReg 1-bit carry value
{0, 1, TriCore::StatusRegBank},
};

// ValueMappings.
Expand Down Expand Up @@ -55,6 +57,13 @@ RegisterBankInfo::ValueMapping TriCoreGenRegisterBankInfo::ValMappings[]{
// TruncAddrRegBankIdx.
{&TriCoreGenRegisterBankInfo::PartMappings[PMI_AddrReg - PMI_Min], 1},
{&TriCoreGenRegisterBankInfo::PartMappings[PMI_ExtAddrReg - PMI_Min], 1},
// Carry-bit arithmetic
// 17: Add/sub /w carry. <-- This must match CarryBitIdx.
{&TriCoreGenRegisterBankInfo::PartMappings[PMI_DataReg - PMI_Min], 1},
{&TriCoreGenRegisterBankInfo::PartMappings[PMI_CarryReg - PMI_Min], 1},
{&TriCoreGenRegisterBankInfo::PartMappings[PMI_DataReg - PMI_Min], 1},
{&TriCoreGenRegisterBankInfo::PartMappings[PMI_DataReg - PMI_Min], 1},
{&TriCoreGenRegisterBankInfo::PartMappings[PMI_CarryReg - PMI_Min], 1},
// TODO: Add mappings for cross register bank copies.
// Create FirstCrossRegCpyIdx and LastCrossRegCpyIdx.
// Create DistanceBetweenCrossRegCpy = <num operands>.
Expand Down Expand Up @@ -83,6 +92,16 @@ bool TriCoreGenRegisterBankInfo::checkValueMapImpl(unsigned Idx,
Map.NumBreakDowns == 1;
}

bool TriCoreGenRegisterBankInfo::checkCarryMapImpl(unsigned Idx,
unsigned Offset) {
// Check that the ValueMapping at the given index matches the expected values
const unsigned PartialMapBaseIdx = Idx - PartialMappingIdx::PMI_Min;
const ValueMapping &Map =
TriCoreGenRegisterBankInfo::getCarryMapping()[Offset];
return Map.BreakDown == &PartMappings[PartialMapBaseIdx] &&
Map.NumBreakDowns == 1;
}

bool TriCoreGenRegisterBankInfo::checkPartialMappingIdx(
PartialMappingIdx FirstAlias, PartialMappingIdx LastAlias,
ArrayRef<PartialMappingIdx> Order) {
Expand Down Expand Up @@ -112,14 +131,20 @@ bool TriCoreGenRegisterBankInfo::checkPartialMappingIdx(
unsigned TriCoreGenRegisterBankInfo::getRegBankBaseIdxOffset(unsigned RBIdx,
unsigned Size) {
// returned offset is relative to the register bank index
if (RBIdx == PMI_FirstDataReg || RBIdx == PMI_FirstAddrReg) {
switch (RBIdx) {
default:
return -1;
case PMI_FirstDataReg:
case PMI_FirstAddrReg:
if (Size <= 32)
return 0;
if (Size <= 64)
else if (Size <= 64)
return 1;
return -1;
else
return -1;
case PMI_FirstCarryReg:
return Size <= 1 ? 0 : -1;
}
return -1;
}

const RegisterBankInfo::ValueMapping *
Expand Down Expand Up @@ -148,6 +173,7 @@ TriCoreGenRegisterBankInfo::PartialMappingIdx
// order corresponds to the register bank ID enum
PMI_FirstAddrReg, // AddrReg
PMI_FirstDataReg, // DataReg
PMI_None, // StatusReg
};

const RegisterBankInfo::ValueMapping *
Expand Down Expand Up @@ -195,4 +221,11 @@ TriCoreGenRegisterBankInfo::getTruncMapping(unsigned BankID, unsigned DstSize,

return &ValMappings[TruncIdx];
}

const RegisterBankInfo::ValueMapping *
TriCoreGenRegisterBankInfo::getCarryMapping() {
// XXX: If TriCore ever support add/sub /w carry on address regs or 64-bit
// this function needs parameters and some more logic
return &ValMappings[CarryBitIdx];
}
} // namespace llvm
133 changes: 90 additions & 43 deletions llvm/lib/Target/TriCore/TriCoreInstructionSelector.cpp
Expand Up @@ -398,6 +398,51 @@ static bool isCarryOutProducer(const MachineInstr &I) {
}
}

static bool isCarryOutClobberedBeforeUse(const MachineInstr &MI,
const MachineRegisterInfo &MRI) {
assert(isCarryOutProducer(MI));

const Register &CarryOutReg = MI.getOperand(1).getReg();

assert(MRI.getType(CarryOutReg).getSizeInBits() == 1 &&
CarryOutReg.isVirtual() &&
"Expected a virtual carry-out register of 1-bit");

// To check whether a carry-out is clobbered before use we loop over all uses
// and then check all dominating instructions in reverse order up to the
// carry-out definition. If one of these instructions writes $psw_c, it
// clobbers the carry-out
const auto RE = MI.getReverseIterator();
for (auto I = MRI.use_instr_nodbg_begin(CarryOutReg),
E = MachineRegisterInfo::use_instr_nodbg_end();
I != E; ++I) {
const MachineInstr &Use = *I;

// Iterate from Use to MI, skipping Use itself
for (auto RI = std::next(Use.getReverseIterator()); RI != RE; ++RI) {
const MachineInstr &Inst = *RI;

// Stop if we reach the original instruction
if (&Inst == &MI)
break;

// Instruction selector works bottom up, so this instructions should have
// been selected already
assert(!isPreISelGenericOpcode(Inst.getOpcode()) &&
"Expected already-selected instruction");

// Check if this instruction clobbers $psw_c
if (Inst.definesRegister(TriCore::PSW_C, MRI.getTargetRegisterInfo())) {
LLVM_DEBUG(dbgs() << Inst << " clobbers carry-out register "
<< CarryOutReg << '\n');
return true;
}
}
}

return false;
}

bool TriCoreInstructionSelector::select(MachineInstr &I) {
assert(I.getParent() && "Instruction should be in a basic block!");
assert(I.getParent()->getParent() && "Instruction should be in a function!");
Expand Down Expand Up @@ -640,16 +685,30 @@ bool TriCoreInstructionSelector::selectAddSubE(MachineInstr &I,
// instruction. If that is not the case, the carry-in must be the constant
// 0, since we can use a simple ADDX/SUBX instruction to handle this case.
//
// We expect that there are no instructions clobbering the PSW are scheduled
// between this and the carry-out producing instruction. If that expectation
// turns out to be wrong, the COPY for the carry-in would get materialized,
// which is unsupported and fails in the TriCoreInstrInfo::copyPhysReg

// Look for the definition of the carry-in register, skipping truncations
const MachineInstr *Def = MRI.getVRegDef(CarryInReg);
while (Def->getOpcode() == TargetOpcode::G_TRUNC) {
// We also need to make sure that all usages of the carry-out are not
// clobbered by other instructions.
if (isCarryOutClobberedBeforeUse(I, MRI)) {
LLVM_DEBUG(dbgs() << "Carry-out register is clobbered before use\n");
return false;
}

// Also check if we can replace all uses of CarryOutReg with $psw_c
const RegisterBank *CarryOutRB = MRI.getRegBankOrNull(CarryOutReg);
if (!CarryOutRB || CarryOutRB->getID() != TriCore::StatusRegBankID) {
LLVM_DEBUG(dbgs() << "Cannot replace " << CarryOutReg << " with "
<< Register(TriCore::PSW_C)
<< "because it is not assigned to the StatusRegBank\n");
return false;
}

// Look for the definition of the carry-in register, skipping truncations and
// virtual copies
const MachineInstr *Def = getDefIgnoringCopies(CarryInReg, MRI);
while ((Def->getOpcode() == TargetOpcode::G_TRUNC ||
Def->getOpcode() == TargetOpcode::COPY) &&
Def->getOperand(1).getReg().isVirtual()) {
CarryInReg = Def->getOperand(1).getReg();
Def = MRI.getVRegDef(CarryInReg);
Def = getDefIgnoringCopies(CarryInReg, MRI);
}

MachineIRBuilder MIRBuilder(I);
Expand All @@ -658,19 +717,8 @@ bool TriCoreInstructionSelector::selectAddSubE(MachineInstr &I,
// instruction or is a constant
unsigned Opc;
if (isCarryOutProducer(*Def)) {
// Copy carry-in to pseudo-register PSW_C. This COPY should get eliminated
// in the register allocator
MIRBuilder.buildInstr(TargetOpcode::COPY)
.addDef(TriCore::PSW_C)
.addUse(CarryInReg);

if (!TriCoreRegisterBankInfo::constrainGenericRegister(
CarryInReg, TriCore::DataRegsRegClass, MRI)) {
LLVM_DEBUG(dbgs() << "Failed to constrain carry-in register for " << OpStr
<< '\n');
return false;
}

// Def is a carry-out producer, i.e. it sets $psw_c. Use ADDC_ddd/SUBC_ddd
// which read $psw_c.
Opc = IsAdd ? TriCore::ADDC_ddd : TriCore::SUBC_ddd;

} else if (auto CarryInVal = getConstantVRegVal(CarryInReg, MRI)) {
Expand All @@ -693,20 +741,11 @@ bool TriCoreInstructionSelector::selectAddSubE(MachineInstr &I,
return false;
}

// Build target instruction and copy PSW_C to carry-out
// Build target instruction and replace all uses of CarryOutReg with $psw_c
const auto AddSubMI =
MIRBuilder.buildInstr(Opc).addDef(DstReg).addUse(Src1Reg).addUse(Src2Reg);

MIRBuilder.buildInstr(TargetOpcode::COPY)
.addDef(CarryOutReg)
.addUse(TriCore::PSW_C);

if (!TriCoreRegisterBankInfo::constrainGenericRegister(
CarryOutReg, TriCore::DataRegsRegClass, MRI)) {
LLVM_DEBUG(dbgs() << "Failed to constrain carry-out register for " << OpStr
<< '\n');
return false;
}
MRI.replaceRegWith(CarryOutReg, TriCore::PSW_C);

constrainSelectedInstRegOperands(*AddSubMI, TII, TRI, RBI);

Expand Down Expand Up @@ -735,24 +774,32 @@ bool TriCoreInstructionSelector::selectAddSubO(MachineInstr &I,
!checkType(LLT::scalar(1), CarryTy, OpStr))
return false;

// Emit ADDX/SUBX and use COPY from PSW_C for the carry-out
// We need to make sure that all usages of the carry-out are not
// clobbered by other instructions.
if (isCarryOutClobberedBeforeUse(I, MRI)) {
LLVM_DEBUG(dbgs() << "Carry-out register is clobbered before use\n");
return false;
}

// Also check if we can replace all uses of CarryOutReg with $psw_c
const RegisterBank *CarryOutRB = MRI.getRegBankOrNull(CarryReg);
if (!CarryOutRB || CarryOutRB->getID() != TriCore::StatusRegBankID) {
LLVM_DEBUG(dbgs() << "Cannot replace " << CarryReg << " with "
<< Register(TriCore::PSW_C)
<< "because it is not assigned to the StatusRegBank\n");
return false;
}

// Emit ADDX/SUBX and replace CarryReg with $psw_c
MachineIRBuilder MIRBuilder(I);

const unsigned Opc = IsAdd ? TriCore::ADDX_ddd : TriCore::SUBX_ddd;
const auto AddSubMI =
MIRBuilder.buildInstr(Opc).addDef(DstReg).addUse(Src1Reg).addUse(Src2Reg);

MIRBuilder.buildInstr(TargetOpcode::COPY)
.addDef(CarryReg)
.addUse(TriCore::PSW_C);
MRI.replaceRegWith(CarryReg, TriCore::PSW_C);

constrainSelectedInstRegOperands(*AddSubMI, TII, TRI, RBI);
if (!TriCoreRegisterBankInfo::constrainGenericRegister(
CarryReg, TriCore::DataRegsRegClass, MRI)) {
LLVM_DEBUG(dbgs() << "Failed to constrain carry register for " << OpStr
<< '\n');
return false;
}

I.removeFromParent();
return true;
Expand Down

0 comments on commit 5a4494a

Please sign in to comment.