mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
[globalisel][tablegen] Add support for (set $dst, 1) and test X86's OptForSize predicate.
Summary: It's rare but a small number of patterns use IntInit's at the root of the match. On X86, one such rule is enabled by the OptForSize predicate and causes the compiler to use the smaller: %0 = MOV32r1 instead of the usual: %0 = MOV32ri 1 This patch adds support for matching IntInit's at the root and uses this as a test case for the optsize attribute that was implemented in r301750 Reviewers: qcolombet, ab, t.p.northover, rovka, kristof.beyls, aditya_nandakumar Reviewed By: qcolombet Subscribers: igorb, llvm-commits Differential Revision: https://reviews.llvm.org/D32791 llvm-svn: 303678
This commit is contained in:
parent
8d54831bad
commit
fa3e6b4d4e
96
test/CodeGen/X86/GlobalISel/select-leaf-constant.mir
Normal file
96
test/CodeGen/X86/GlobalISel/select-leaf-constant.mir
Normal file
@ -0,0 +1,96 @@
|
||||
# RUN: llc -mtriple=i586-linux-gnu -global-isel -run-pass=instruction-select %s -o - | FileCheck %s --check-prefix=CHECK
|
||||
#
|
||||
# This is necessary to test that attribute-based rule predicates work and that
|
||||
# they properly reset between functions.
|
||||
|
||||
--- |
|
||||
define i32 @const_i32_1() {
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
define i32 @const_i32_1_optsize() #0 {
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
define i32 @const_i32_1b() {
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
define i32 @const_i32_1_optsizeb() #0 {
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
attributes #0 = { optsize }
|
||||
...
|
||||
---
|
||||
name: const_i32_1
|
||||
legalized: true
|
||||
regBankSelected: true
|
||||
selected: false
|
||||
# CHECK-LABEL: name: const_i32_1
|
||||
# CHECK: registers:
|
||||
# CHECK-NEXT: - { id: 0, class: gr32 }
|
||||
registers:
|
||||
- { id: 0, class: gpr }
|
||||
# CHECK: body:
|
||||
# CHECK: %0 = MOV32ri 1
|
||||
body: |
|
||||
bb.1 (%ir-block.0):
|
||||
%0(s32) = G_CONSTANT i32 1
|
||||
%eax = COPY %0(s32)
|
||||
RET 0, implicit %eax
|
||||
...
|
||||
---
|
||||
name: const_i32_1_optsize
|
||||
legalized: true
|
||||
regBankSelected: true
|
||||
selected: false
|
||||
# CHECK-LABEL: name: const_i32_1_optsize
|
||||
# CHECK: registers:
|
||||
# CHECK-NEXT: - { id: 0, class: gr32 }
|
||||
registers:
|
||||
- { id: 0, class: gpr }
|
||||
# CHECK: body:
|
||||
# CHECK: %0 = MOV32r1
|
||||
body: |
|
||||
bb.1 (%ir-block.0):
|
||||
%0(s32) = G_CONSTANT i32 1
|
||||
%eax = COPY %0(s32)
|
||||
RET 0, implicit %eax
|
||||
...
|
||||
---
|
||||
name: const_i32_1b
|
||||
legalized: true
|
||||
regBankSelected: true
|
||||
selected: false
|
||||
# CHECK-LABEL: name: const_i32_1b
|
||||
# CHECK: registers:
|
||||
# CHECK-NEXT: - { id: 0, class: gr32 }
|
||||
registers:
|
||||
- { id: 0, class: gpr }
|
||||
# CHECK: body:
|
||||
# CHECK: %0 = MOV32ri 1
|
||||
body: |
|
||||
bb.1 (%ir-block.0):
|
||||
%0(s32) = G_CONSTANT i32 1
|
||||
%eax = COPY %0(s32)
|
||||
RET 0, implicit %eax
|
||||
...
|
||||
---
|
||||
name: const_i32_1_optsizeb
|
||||
legalized: true
|
||||
regBankSelected: true
|
||||
selected: false
|
||||
# CHECK-LABEL: name: const_i32_1_optsizeb
|
||||
# CHECK: registers:
|
||||
# CHECK-NEXT: - { id: 0, class: gr32 }
|
||||
registers:
|
||||
- { id: 0, class: gpr }
|
||||
# CHECK: body:
|
||||
# CHECK: %0 = MOV32r1
|
||||
body: |
|
||||
bb.1 (%ir-block.0):
|
||||
%0(s32) = G_CONSTANT i32 1
|
||||
%eax = COPY %0(s32)
|
||||
RET 0, implicit %eax
|
||||
...
|
@ -462,6 +462,32 @@ def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1)
|
||||
def ORN : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>;
|
||||
def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
|
||||
|
||||
//===- Test a simple pattern with just a leaf immediate. ------------------===//
|
||||
|
||||
// CHECK-LABEL: if ([&]() {
|
||||
// CHECK-NEXT: MachineInstr &MI0 = I;
|
||||
// CHECK-NEXT: if (MI0.getNumOperands() < 2)
|
||||
// CHECK-NEXT: return false;
|
||||
// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_CONSTANT) &&
|
||||
// CHECK-NEXT: ((/* dst */ (MRI.getType(MI0.getOperand(0).getReg()) == (LLT::scalar(32))) &&
|
||||
// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(0).getReg(), MRI, TRI))))) &&
|
||||
// CHECK-NEXT: ((/* Operand 1 */ (MI0.getOperand(1).isCImm() && MI0.getOperand(1).getCImm()->equalsInt(1))))) {
|
||||
// CHECK-NEXT: // 1:i32 => (MOV1:i32)
|
||||
// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::MOV1));
|
||||
// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/);
|
||||
// CHECK-NEXT: for (const auto *FromMI : {&MI0, })
|
||||
// CHECK-NEXT: for (const auto &MMO : FromMI->memoperands())
|
||||
// CHECK-NEXT: MIB.addMemOperand(MMO);
|
||||
// CHECK-NEXT: I.eraseFromParent();
|
||||
// CHECK-NEXT: MachineInstr &NewI = *MIB;
|
||||
// CHECK-NEXT: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI);
|
||||
// CHECK-NEXT: return true;
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: return false;
|
||||
// CHECK-NEXT: }()) { return true; }
|
||||
|
||||
def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
|
||||
|
||||
//===- Test a pattern with an MBB operand. --------------------------------===//
|
||||
|
||||
// CHECK-LABEL: if ([&]() {
|
||||
|
@ -135,6 +135,9 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) {
|
||||
std::string Explanation = "";
|
||||
std::string Separator = "";
|
||||
if (N->isLeaf()) {
|
||||
if (IntInit *Int = dyn_cast<IntInit>(N->getLeafValue()))
|
||||
return Error::success();
|
||||
|
||||
Explanation = "Is a leaf";
|
||||
Separator = ", ";
|
||||
}
|
||||
@ -272,6 +275,7 @@ public:
|
||||
OPM_ComplexPattern,
|
||||
OPM_Instruction,
|
||||
OPM_Int,
|
||||
OPM_LiteralInt,
|
||||
OPM_LLT,
|
||||
OPM_RegBank,
|
||||
OPM_MBB,
|
||||
@ -406,13 +410,14 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Generates code to check that an operand is a particular int.
|
||||
class IntOperandMatcher : public OperandPredicateMatcher {
|
||||
/// Generates code to check that an operand is a G_CONSTANT with a particular
|
||||
/// int.
|
||||
class ConstantIntOperandMatcher : public OperandPredicateMatcher {
|
||||
protected:
|
||||
int64_t Value;
|
||||
|
||||
public:
|
||||
IntOperandMatcher(int64_t Value)
|
||||
ConstantIntOperandMatcher(int64_t Value)
|
||||
: OperandPredicateMatcher(OPM_Int), Value(Value) {}
|
||||
|
||||
static bool classof(const OperandPredicateMatcher *P) {
|
||||
@ -425,6 +430,27 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// Generates code to check that an operand is a raw int (where MO.isImm() or
|
||||
/// MO.isCImm() is true).
|
||||
class LiteralIntOperandMatcher : public OperandPredicateMatcher {
|
||||
protected:
|
||||
int64_t Value;
|
||||
|
||||
public:
|
||||
LiteralIntOperandMatcher(int64_t Value)
|
||||
: OperandPredicateMatcher(OPM_LiteralInt), Value(Value) {}
|
||||
|
||||
static bool classof(const OperandPredicateMatcher *P) {
|
||||
return P->getKind() == OPM_LiteralInt;
|
||||
}
|
||||
|
||||
void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule,
|
||||
StringRef OperandExpr) const override {
|
||||
OS << OperandExpr << ".isCImm() && " << OperandExpr
|
||||
<< ".getCImm()->equalsInt(" << Value << ")";
|
||||
}
|
||||
};
|
||||
|
||||
/// Generates code to check that a set of predicates match for a particular
|
||||
/// operand.
|
||||
class OperandMatcher : public PredicateListMatcher<OperandPredicateMatcher> {
|
||||
@ -1236,7 +1262,7 @@ private:
|
||||
createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher,
|
||||
const TreePatternNode *Src) const;
|
||||
Error importChildMatcher(InstructionMatcher &InsnMatcher,
|
||||
TreePatternNode *SrcChild, unsigned OpIdx,
|
||||
const TreePatternNode *SrcChild, unsigned OpIdx,
|
||||
unsigned &TempOpIdx) const;
|
||||
Expected<BuildMIAction &> createAndImportInstructionRenderer(
|
||||
RuleMatcher &M, const TreePatternNode *Dst,
|
||||
@ -1299,14 +1325,23 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
|
||||
if (Src->getExtTypes().size() > 1)
|
||||
return failedImport("Src pattern has multiple results");
|
||||
|
||||
auto SrcGIOrNull = findNodeEquiv(Src->getOperator());
|
||||
if (!SrcGIOrNull)
|
||||
return failedImport("Pattern operator lacks an equivalent Instruction" +
|
||||
explainOperator(Src->getOperator()));
|
||||
auto &SrcGI = *SrcGIOrNull;
|
||||
if (Src->isLeaf()) {
|
||||
Init *SrcInit = Src->getLeafValue();
|
||||
if (IntInit *SrcIntInit = dyn_cast<IntInit>(SrcInit)) {
|
||||
InsnMatcher.addPredicate<InstructionOpcodeMatcher>(
|
||||
&Target.getInstruction(RK.getDef("G_CONSTANT")));
|
||||
} else
|
||||
return failedImport("Unable to deduce gMIR opcode to handle Src (which is a leaf)");
|
||||
} else {
|
||||
auto SrcGIOrNull = findNodeEquiv(Src->getOperator());
|
||||
if (!SrcGIOrNull)
|
||||
return failedImport("Pattern operator lacks an equivalent Instruction" +
|
||||
explainOperator(Src->getOperator()));
|
||||
auto &SrcGI = *SrcGIOrNull;
|
||||
|
||||
// The operators look good: match the opcode and mutate it to the new one.
|
||||
InsnMatcher.addPredicate<InstructionOpcodeMatcher>(&SrcGI);
|
||||
// The operators look good: match the opcode
|
||||
InsnMatcher.addPredicate<InstructionOpcodeMatcher>(&SrcGI);
|
||||
}
|
||||
|
||||
unsigned OpIdx = 0;
|
||||
unsigned TempOpIdx = 0;
|
||||
@ -1323,18 +1358,27 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
|
||||
OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
|
||||
}
|
||||
|
||||
// Match the used operands (i.e. the children of the operator).
|
||||
for (unsigned i = 0, e = Src->getNumChildren(); i != e; ++i) {
|
||||
if (auto Error = importChildMatcher(InsnMatcher, Src->getChild(i), OpIdx++,
|
||||
TempOpIdx))
|
||||
return std::move(Error);
|
||||
if (Src->isLeaf()) {
|
||||
Init *SrcInit = Src->getLeafValue();
|
||||
if (IntInit *SrcIntInit = dyn_cast<IntInit>(SrcInit)) {
|
||||
OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, "", TempOpIdx);
|
||||
OM.addPredicate<LiteralIntOperandMatcher>(SrcIntInit->getValue());
|
||||
} else
|
||||
return failedImport("Unable to deduce gMIR opcode to handle Src (which is a leaf)");
|
||||
} else {
|
||||
// Match the used operands (i.e. the children of the operator).
|
||||
for (unsigned i = 0, e = Src->getNumChildren(); i != e; ++i) {
|
||||
if (auto Error = importChildMatcher(InsnMatcher, Src->getChild(i),
|
||||
OpIdx++, TempOpIdx))
|
||||
return std::move(Error);
|
||||
}
|
||||
}
|
||||
|
||||
return InsnMatcher;
|
||||
}
|
||||
|
||||
Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
|
||||
TreePatternNode *SrcChild,
|
||||
const TreePatternNode *SrcChild,
|
||||
unsigned OpIdx,
|
||||
unsigned &TempOpIdx) const {
|
||||
OperandMatcher &OM =
|
||||
@ -1379,7 +1423,7 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
|
||||
|
||||
// Check for constant immediates.
|
||||
if (auto *ChildInt = dyn_cast<IntInit>(SrcChild->getLeafValue())) {
|
||||
OM.addPredicate<IntOperandMatcher>(ChildInt->getValue());
|
||||
OM.addPredicate<ConstantIntOperandMatcher>(ChildInt->getValue());
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
@ -1605,6 +1649,9 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
return failedImport("Src pattern root isn't a trivial operator (" +
|
||||
toString(std::move(Err)) + ")");
|
||||
|
||||
if (Dst->isLeaf())
|
||||
return failedImport("Dst pattern root isn't a known leaf");
|
||||
|
||||
// Start with the defined operands (i.e., the results of the root operator).
|
||||
Record *DstOp = Dst->getOperator();
|
||||
if (!DstOp->isSubClassOf("Instruction"))
|
||||
|
Loading…
Reference in New Issue
Block a user