bool combineExtracts(MachineInstr &MI, MachineRegisterInfo &MRI,
const TargetInstrInfo &TII);
+ bool combineMerges(MachineInstr &MI, MachineRegisterInfo &MRI,
+ const TargetInstrInfo &TII);
+
bool runOnMachineFunction(MachineFunction &MF) override;
};
} // End namespace llvm.
ArrayRef<unsigned> Ops,
ArrayRef<uint64_t> Indices);
+ /// Build and insert \p Res<def> = G_MERGE_VALUES \p Op0, ...
+ ///
+ /// G_MERGE_VALUES combines the input elements contiguously into a larger
+ /// register.
+ ///
+ /// \pre setBasicBlock or setMI must have been called.
+ /// \pre The entire register \p Res (and no more) must be covered by the input
+ /// registers.
+ /// \pre The type of all \p Ops registers must be identical.
+ ///
+ /// \return a MachineInstrBuilder for the newly created instruction.
+ MachineInstrBuilder buildMerge(unsigned Res, ArrayRef<unsigned> Ops);
+
+ /// Build and insert \p Res0<def>, ... = G_UNMERGE_VALUES \p Op
+ ///
+ /// G_UNMERGE_VALUES splits contiguous bits of the input into multiple
+ ///
+ /// \pre setBasicBlock or setMI must have been called.
+ /// \pre The entire register \p Res (and no more) must be covered by the input
+ /// registers.
+ /// \pre The type of all \p Res registers must be identical.
+ ///
+ /// \return a MachineInstrBuilder for the newly created instruction.
+ MachineInstrBuilder buildUnmerge(ArrayRef<unsigned> Res, unsigned Op);
+
void addUsesWithIndices(MachineInstrBuilder MIB) {}
template <typename... ArgTys>
let hasSideEffects = 0;
}
+// Extract multiple registers specified size, starting from blocks given by
+// indexes. This will almost certainly be mapped to sub-register COPYs after
+// register banks have been selected.
+def G_UNMERGE_VALUES : Instruction {
+ let OutOperandList = (outs);
+ let InOperandList = (ins variable_ops);
+ let hasSideEffects = 0;
+}
+
// Insert a sequence of smaller registers into a larger one at the specified
// indices (interleaved with the values in the operand list "op0, bit0, op1,
// bit1, ...")).
let hasSideEffects = 0;
}
+def G_MERGE_VALUES : Instruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins variable_ops);
+ let hasSideEffects = 0;
+}
+
// Intrinsic without side effects.
def G_INTRINSIC : Instruction {
let OutOperandList = (outs);
/// (typically a sub-register COPY after instruction selection).
HANDLE_TARGET_OPCODE(G_EXTRACT)
+HANDLE_TARGET_OPCODE(G_UNMERGE_VALUES)
+
/// Generic instruction to insert blocks of bits from the registers given into
/// the source.
HANDLE_TARGET_OPCODE(G_INSERT)
/// larger register.
HANDLE_TARGET_OPCODE(G_SEQUENCE)
+HANDLE_TARGET_OPCODE(G_MERGE_VALUES)
+
/// Generic pointer to int conversion.
HANDLE_TARGET_OPCODE(G_PTRTOINT)
return Changed;
}
+bool Legalizer::combineMerges(MachineInstr &MI, MachineRegisterInfo &MRI,
+ const TargetInstrInfo &TII) {
+ if (MI.getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
+ return false;
+
+ unsigned NumDefs = MI.getNumOperands() - 1;
+ unsigned SrcReg = MI.getOperand(NumDefs).getReg();
+ MachineInstr &MergeI = *MRI.def_instr_begin(SrcReg);
+ if (MergeI.getOpcode() != TargetOpcode::G_MERGE_VALUES)
+ return false;
+
+ if (MergeI.getNumOperands() - 1 != NumDefs)
+ return false;
+
+ // FIXME: is a COPY appropriate if the types mismatch? We know both registers
+ // are allocatable by now.
+ if (MRI.getType(MI.getOperand(0).getReg()) !=
+ MRI.getType(MergeI.getOperand(1).getReg()))
+ return false;
+
+ for (unsigned Idx = 0; Idx < NumDefs; ++Idx)
+ MRI.replaceRegWith(MI.getOperand(Idx).getReg(),
+ MergeI.getOperand(Idx + 1).getReg());
+
+ MI.eraseFromParent();
+ if (MRI.use_empty(MergeI.getOperand(0).getReg()))
+ MergeI.eraseFromParent();
+ return true;
+}
+
bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
// If the ISel pipeline failed, do not bother running that pass.
if (MF.getProperties().hasProperty(
NextMI = std::next(MI);
Changed |= combineExtracts(*MI, MRI, TII);
+ Changed |= combineMerges(*MI, MRI, TII);
}
}
void LegalizerHelper::extractParts(unsigned Reg, LLT Ty, int NumParts,
SmallVectorImpl<unsigned> &VRegs) {
- unsigned Size = Ty.getSizeInBits();
- SmallVector<uint64_t, 4> Indexes;
- for (int i = 0; i < NumParts; ++i) {
+ for (int i = 0; i < NumParts; ++i)
VRegs.push_back(MRI.createGenericVirtualRegister(Ty));
- Indexes.push_back(i * Size);
- }
- MIRBuilder.buildExtract(VRegs, Indexes, Reg);
+ MIRBuilder.buildUnmerge(VRegs, Reg);
}
static RTLIB::Libcall getRTLibDesc(unsigned Opcode, unsigned Size) {
return UnableToLegalize;
case TargetOpcode::G_ADD: {
// Expand in terms of carry-setting/consuming G_ADDE instructions.
- unsigned NarrowSize = NarrowTy.getSizeInBits();
int NumParts = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits() /
NarrowTy.getSizeInBits();
SmallVector<unsigned, 2> Src1Regs, Src2Regs, DstRegs;
- SmallVector<uint64_t, 2> Indexes;
extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs);
extractParts(MI.getOperand(2).getReg(), NarrowTy, NumParts, Src2Regs);
Src2Regs[i], CarryIn);
DstRegs.push_back(DstReg);
- Indexes.push_back(i * NarrowSize);
CarryIn = CarryOut;
}
unsigned DstReg = MI.getOperand(0).getReg();
- MIRBuilder.buildSequence(DstReg, DstRegs, Indexes);
+ MIRBuilder.buildMerge(DstReg, DstRegs);
MI.eraseFromParent();
return Legalized;
}
for (int i = 0; i < NumParts; ++i) {
unsigned DstStart = i * NarrowSize;
unsigned DstReg = MRI.createGenericVirtualRegister(NarrowTy);
- Indexes.push_back(DstStart);
findInsertionsForRange(DstStart, DstStart + NarrowSize, CurOp, EndOp, MI);
}
assert(DstRegs.size() == (unsigned)NumParts && "not all parts covered");
- MIRBuilder.buildSequence(MI.getOperand(0).getReg(), DstRegs, Indexes);
+ MIRBuilder.buildMerge(MI.getOperand(0).getReg(), DstRegs);
MI.eraseFromParent();
return Legalized;
}
MRI.getType(MI.getOperand(1).getReg()).getAddressSpace(), NarrowSize);
SmallVector<unsigned, 2> DstRegs;
- SmallVector<uint64_t, 2> Indexes;
for (int i = 0; i < NumParts; ++i) {
unsigned DstReg = MRI.createGenericVirtualRegister(NarrowTy);
unsigned SrcReg = MRI.createGenericVirtualRegister(NarrowPtrTy);
MIRBuilder.buildLoad(DstReg, SrcReg, **MI.memoperands_begin());
DstRegs.push_back(DstReg);
- Indexes.push_back(i * NarrowSize);
}
unsigned DstReg = MI.getOperand(0).getReg();
- MIRBuilder.buildSequence(DstReg, DstRegs, Indexes);
+ MIRBuilder.buildMerge(DstReg, DstRegs);
MI.eraseFromParent();
return Legalized;
}
MIRBuilder.setInstr(MI);
SmallVector<unsigned, 2> Src1Regs, Src2Regs, DstRegs;
- SmallVector<uint64_t, 2> Indexes;
extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs);
extractParts(MI.getOperand(2).getReg(), NarrowTy, NumParts, Src2Regs);
unsigned DstReg = MRI.createGenericVirtualRegister(NarrowTy);
MIRBuilder.buildAdd(DstReg, Src1Regs[i], Src2Regs[i]);
DstRegs.push_back(DstReg);
- Indexes.push_back(i * NarrowSize);
}
- MIRBuilder.buildSequence(DstReg, DstRegs, Indexes);
+ MIRBuilder.buildMerge(DstReg, DstRegs);
MI.eraseFromParent();
return Legalized;
}
// FIXME: the long-term plan calls for expansion in terms of load/store (if
// they're not legal).
if (Aspect.Opcode == TargetOpcode::G_SEQUENCE ||
- Aspect.Opcode == TargetOpcode::G_EXTRACT)
+ Aspect.Opcode == TargetOpcode::G_EXTRACT ||
+ Aspect.Opcode == TargetOpcode::G_MERGE_VALUES ||
+ Aspect.Opcode == TargetOpcode::G_UNMERGE_VALUES)
return std::make_pair(Legal, Aspect.Type);
LegalizeAction Action = findInActions(Aspect);
return MIB;
}
+MachineInstrBuilder MachineIRBuilder::buildMerge(unsigned Res,
+ ArrayRef<unsigned> Ops) {
+
+#ifndef NDEBUG
+ assert(!Ops.empty() && "invalid trivial sequence");
+ LLT Ty = MRI->getType(Ops[0]);
+ for (auto Reg : Ops)
+ assert(MRI->getType(Reg) == Ty && "type mismatch in input list");
+ assert(Ops.size() * MRI->getType(Ops[0]).getSizeInBits() ==
+ MRI->getType(Res).getSizeInBits() &&
+ "input operands do not cover output register");
+#endif
+
+ MachineInstrBuilder MIB = buildInstr(TargetOpcode::G_MERGE_VALUES);
+ MIB.addDef(Res);
+ for (unsigned i = 0; i < Ops.size(); ++i)
+ MIB.addUse(Ops[i]);
+ return MIB;
+}
+
+MachineInstrBuilder MachineIRBuilder::buildUnmerge(ArrayRef<unsigned> Res,
+ unsigned Op) {
+
+#ifndef NDEBUG
+ assert(!Res.empty() && "invalid trivial sequence");
+ LLT Ty = MRI->getType(Res[0]);
+ for (auto Reg : Res)
+ assert(MRI->getType(Reg) == Ty && "type mismatch in input list");
+ assert(Res.size() * MRI->getType(Res[0]).getSizeInBits() ==
+ MRI->getType(Op).getSizeInBits() &&
+ "input operands do not cover output register");
+#endif
+
+ MachineInstrBuilder MIB = buildInstr(TargetOpcode::G_UNMERGE_VALUES);
+ for (unsigned i = 0; i < Res.size(); ++i)
+ MIB.addDef(Res[i]);
+ MIB.addUse(Op);
+ return MIB;
+}
+
MachineInstrBuilder MachineIRBuilder::buildIntrinsic(Intrinsic::ID ID,
unsigned Res,
bool HasSideEffects) {
bb.0.entry:
liveins: %x0, %x1, %x2, %x3
; CHECK-LABEL: name: test_scalar_add_big
- ; CHECK-NOT: G_EXTRACT
- ; CHECK-NOT: G_SEQUENCE
+ ; CHECK-NOT: G_MERGE_VALUES
+ ; CHECK-NOT: G_UNMERGE_VALUES
; CHECK-DAG: [[CARRY0_32:%.*]](s32) = G_CONSTANT i32 0
; CHECK-DAG: [[CARRY0:%[0-9]+]](s1) = G_TRUNC [[CARRY0_32]]
; CHECK: [[RES_LO:%.*]](s64), [[CARRY:%.*]](s1) = G_UADDE %0, %2, [[CARRY0]]
; CHECK: [[RES_HI:%.*]](s64), {{%.*}}(s1) = G_UADDE %1, %3, [[CARRY]]
- ; CHECK-NOT: G_EXTRACT
- ; CHECK-NOT: G_SEQUENCE
+ ; CHECK-NOT: G_MERGE_VALUES
+ ; CHECK-NOT: G_UNMERGE_VALUES
; CHECK: %x0 = COPY [[RES_LO]]
; CHECK: %x1 = COPY [[RES_HI]]
%1(s64) = COPY %x1
%2(s64) = COPY %x2
%3(s64) = COPY %x3
- %4(s128) = G_SEQUENCE %0, 0, %1, 64
- %5(s128) = G_SEQUENCE %2, 0, %3, 64
+ %4(s128) = G_MERGE_VALUES %0, %1
+ %5(s128) = G_MERGE_VALUES %2, %3
%6(s128) = G_ADD %4, %5
- %7(s64), %8(s64) = G_EXTRACT %6, 0, 64
+ %7(s64), %8(s64) = G_UNMERGE_VALUES %6
%x0 = COPY %7
%x1 = COPY %8
...
%1(<2 x s64>) = COPY %q1
%2(<2 x s64>) = COPY %q2
%3(<2 x s64>) = COPY %q3
- %4(<4 x s64>) = G_SEQUENCE %0, 0, %1, 128
- %5(<4 x s64>) = G_SEQUENCE %2, 0, %3, 128
+ %4(<4 x s64>) = G_MERGE_VALUES %0, %1
+ %5(<4 x s64>) = G_MERGE_VALUES %2, %3
%6(<4 x s64>) = G_ADD %4, %5
- %7(<2 x s64>), %8(<2 x s64>) = G_EXTRACT %6, 0, 128
+ %7(<2 x s64>), %8(<2 x s64>) = G_UNMERGE_VALUES %6
%q0 = COPY %7
%q1 = COPY %8
...
; CHECK: [[OFFSET1:%[0-9]+]](s64) = G_CONSTANT i64 8
; CHECK: [[GEP1:%[0-9]+]](p0) = G_GEP %0, [[OFFSET1]](s64)
; CHECK: [[LOAD1:%[0-9]+]](s64) = G_LOAD [[GEP1]](p0) :: (load 16 from %ir.addr)
- ; CHECK: %8(s128) = G_SEQUENCE [[LOAD0]](s64), 0, [[LOAD1]](s64), 64
+ ; CHECK: %8(s128) = G_MERGE_VALUES [[LOAD0]](s64), [[LOAD1]](s64)
%8(s128) = G_LOAD %0(p0) :: (load 16 from %ir.addr)
...
; CHECK: [[GEP1:%[0-9]+]](p0) = G_GEP %0, [[OFFSET1]](s64)
; CHECK: G_STORE %6(s64), [[GEP1]](p0) :: (store 16 into %ir.addr)
%6(s64) = G_PTRTOINT %0(p0)
- %7(s128) = G_SEQUENCE %5, 0, %6, 64
+ %7(s128) = G_MERGE_VALUES %5, %6
G_STORE %7, %0 :: (store 16 into %ir.addr)
...