15#ifndef LLVM_CODEGEN_GLOBALISEL_GIMATCHTABLEEXECUTORIMPL_H
16#define LLVM_CODEGEN_GLOBALISEL_GIMATCHTABLEEXECUTORIMPL_H
44template <
class TgtExecutor,
class PredicateBitset,
class ComplexMatcherMemFn,
45 class CustomRendererFn>
53 const PredicateBitset &AvailableFeatures,
62 bool NoFPException = !State.
MIs[0]->getDesc().mayRaiseFPException();
66 enum RejectAction { RejectAndGiveUp, RejectAndResume };
67 auto handleReject = [&]() -> RejectAction {
69 dbgs() << CurrentIdx <<
": Rejected\n");
70 if (OnFailResumeAt.
empty())
71 return RejectAndGiveUp;
74 dbgs() << CurrentIdx <<
": Resume at " << CurrentIdx <<
" ("
75 << OnFailResumeAt.
size() <<
" try-blocks remain)\n");
76 return RejectAndResume;
79 const auto propagateFlags = [&]() {
80 for (
auto MIB : OutMIs) {
84 if (NoFPException && MIB->mayRaiseFPException())
88 MIB.setMIFlags(MIBFlags);
96 const auto getTypeFromIdx = [&](int64_t
Idx) ->
LLT {
102 const auto readULEB = [&]() {
114 const auto readS8 = [&]() {
return (int8_t)MatchTable[CurrentIdx++]; };
116 const auto readU16 = [&]() {
117 auto V = readBytesAs<uint16_t>(MatchTable + CurrentIdx);
122 const auto readU32 = [&]() {
123 auto V = readBytesAs<uint32_t>(MatchTable + CurrentIdx);
128 const auto readU64 = [&]() {
129 auto V = readBytesAs<uint64_t>(MatchTable + CurrentIdx);
141 MI->eraseFromParent();
145 assert(CurrentIdx != ~0u &&
"Invalid MatchTable index");
146 uint8_t MatcherOpcode = MatchTable[CurrentIdx++];
147 switch (MatcherOpcode) {
150 dbgs() << CurrentIdx <<
": Begin try-block\n");
163 assert(NewInsnID != 0 &&
"Refusing to modify MIs[0]");
168 dbgs() << CurrentIdx <<
": Not a register\n");
169 if (handleReject() == RejectAndGiveUp)
175 dbgs() << CurrentIdx <<
": Is a physical register\n");
176 if (handleReject() == RejectAndGiveUp)
187 if ((
size_t)NewInsnID < State.
MIs.
size())
188 State.
MIs[NewInsnID] = NewMI;
191 "Expected to store MIs in order");
195 dbgs() << CurrentIdx <<
": MIs[" << NewInsnID
196 <<
"] = GIM_RecordInsn(" << InsnID <<
", " << OpIdx
202 uint16_t ExpectedBitsetID = readU16();
205 <<
": GIM_CheckFeatures(ExpectedBitsetID="
206 << ExpectedBitsetID <<
")\n");
207 if ((AvailableFeatures & ExecInfo.
FeatureBitsets[ExpectedBitsetID]) !=
209 if (handleReject() == RejectAndGiveUp)
220 Expected1 = readU16();
222 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
223 unsigned Opcode = State.
MIs[InsnID]->getOpcode();
226 dbgs() << CurrentIdx <<
": GIM_CheckOpcode(MIs[" << InsnID
227 <<
"], ExpectedOpcode=" << Expected0;
229 <<
" || " << Expected1;
230 dbgs() <<
") // Got=" << Opcode <<
"\n";);
232 if (Opcode != Expected0 && Opcode != Expected1) {
233 if (handleReject() == RejectAndGiveUp)
244 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
245 const int64_t Opcode = State.
MIs[InsnID]->getOpcode();
248 dbgs() << CurrentIdx <<
": GIM_SwitchOpcode(MIs[" << InsnID <<
"], ["
249 << LowerBound <<
", " << UpperBound <<
"), Default=" <<
Default
250 <<
", JumpTable...) // Got=" << Opcode <<
"\n";
252 if (Opcode < LowerBound || UpperBound <= Opcode) {
256 const auto EntryIdx = (Opcode - LowerBound);
259 readBytesAs<uint32_t>(MatchTable + CurrentIdx + (EntryIdx * 4));
275 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
279 dbgs() << CurrentIdx <<
": GIM_SwitchType(MIs[" << InsnID
280 <<
"]->getOperand(" << OpIdx <<
"), [" << LowerBound <<
", "
281 << UpperBound <<
"), Default=" <<
Default
282 <<
", JumpTable...) // Got=";
284 dbgs() <<
"Not a VReg\n";
293 const auto TyI = ExecInfo.
TypeIDMap.find(Ty);
298 const int64_t
TypeID = TyI->second;
303 const auto NumEntry = (
TypeID - LowerBound);
306 readBytesAs<uint32_t>(MatchTable + CurrentIdx + (NumEntry * 4));
319 dbgs() << CurrentIdx <<
": GIM_CheckNumOperands(MIs["
320 << InsnID <<
"], Expected=" <<
Expected <<
")\n");
321 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
322 if (State.
MIs[InsnID]->getNumOperands() !=
Expected) {
323 if (handleReject() == RejectAndGiveUp)
335 dbgs() << CurrentIdx <<
": GIM_CheckImmPredicate(MIs["
336 << InsnID <<
"]->getOperand(" << OpIdx
337 <<
"), Predicate=" << Predicate <<
")\n");
338 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
339 assert((State.
MIs[InsnID]->getOperand(OpIdx).isImm() ||
340 State.
MIs[InsnID]->getOperand(OpIdx).isCImm()) &&
341 "Expected immediate operand");
344 if (State.
MIs[InsnID]->getOperand(OpIdx).isCImm())
345 Value = State.
MIs[InsnID]->getOperand(OpIdx).getCImm()->getSExtValue();
346 else if (State.
MIs[InsnID]->getOperand(OpIdx).isImm())
347 Value = State.
MIs[InsnID]->getOperand(OpIdx).getImm();
352 if (handleReject() == RejectAndGiveUp)
361 << CurrentIdx <<
": GIM_CheckAPIntImmPredicate(MIs["
362 << InsnID <<
"], Predicate=" << Predicate <<
")\n");
363 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
364 assert(State.
MIs[InsnID]->getOpcode() == TargetOpcode::G_CONSTANT &&
365 "Expected G_CONSTANT");
367 if (!State.
MIs[InsnID]->getOperand(1).isCImm())
371 State.
MIs[InsnID]->getOperand(1).getCImm()->getValue();
373 if (handleReject() == RejectAndGiveUp)
382 << CurrentIdx <<
": GIM_CheckAPFloatImmPredicate(MIs["
383 << InsnID <<
"], Predicate=" << Predicate <<
")\n");
384 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
385 assert(State.
MIs[InsnID]->getOpcode() == TargetOpcode::G_FCONSTANT &&
386 "Expected G_FCONSTANT");
387 assert(State.
MIs[InsnID]->getOperand(1).isFPImm() &&
388 "Expected FPImm operand");
391 State.
MIs[InsnID]->getOperand(1).getFPImm()->getValueAPF();
394 if (handleReject() == RejectAndGiveUp)
404 <<
": GIM_CheckBuildVectorAll{Zeros|Ones}(MIs["
405 << InsnID <<
"])\n");
406 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
409 assert((
MI->getOpcode() == TargetOpcode::G_BUILD_VECTOR ||
410 MI->getOpcode() == TargetOpcode::G_BUILD_VECTOR_TRUNC) &&
411 "Expected G_BUILD_VECTOR or G_BUILD_VECTOR_TRUNC");
415 if (handleReject() == RejectAndGiveUp)
420 if (handleReject() == RejectAndGiveUp)
434 <<
": GIM_CheckSimplePredicate(Predicate="
435 << Predicate <<
")\n");
438 if (handleReject() == RejectAndGiveUp)
448 << CurrentIdx <<
": GIM_CheckCxxPredicate(MIs["
449 << InsnID <<
"], Predicate=" << Predicate <<
")\n");
450 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
454 if (handleReject() == RejectAndGiveUp)
462 dbgs() << CurrentIdx <<
": GIM_CheckHasNoUse(MIs["
466 assert(
MI &&
"Used insn before defined");
467 assert(
MI->getNumDefs() > 0 &&
"No defs");
468 const Register Res =
MI->getOperand(0).getReg();
470 if (!
MRI.use_nodbg_empty(Res)) {
471 if (handleReject() == RejectAndGiveUp)
481 dbgs() << CurrentIdx <<
": GIM_CheckAtomicOrdering(MIs["
482 << InsnID <<
"], " << (
uint64_t)Ordering <<
")\n");
483 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
484 if (!State.
MIs[InsnID]->hasOneMemOperand())
485 if (handleReject() == RejectAndGiveUp)
488 for (
const auto &MMO : State.
MIs[InsnID]->memoperands())
489 if (MMO->getMergedOrdering() != Ordering)
490 if (handleReject() == RejectAndGiveUp)
499 <<
": GIM_CheckAtomicOrderingOrStrongerThan(MIs["
500 << InsnID <<
"], " << (
uint64_t)Ordering <<
")\n");
501 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
502 if (!State.
MIs[InsnID]->hasOneMemOperand())
503 if (handleReject() == RejectAndGiveUp)
506 for (
const auto &MMO : State.
MIs[InsnID]->memoperands())
508 if (handleReject() == RejectAndGiveUp)
517 <<
": GIM_CheckAtomicOrderingWeakerThan(MIs["
518 << InsnID <<
"], " << (
uint64_t)Ordering <<
")\n");
519 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
520 if (!State.
MIs[InsnID]->hasOneMemOperand())
521 if (handleReject() == RejectAndGiveUp)
524 for (
const auto &MMO : State.
MIs[InsnID]->memoperands())
526 if (handleReject() == RejectAndGiveUp)
534 const uint64_t NumAddrSpace = readULEB();
536 if (State.
MIs[InsnID]->getNumMemOperands() <= MMOIdx) {
537 if (handleReject() == RejectAndGiveUp)
544 const uint64_t LastIdx = CurrentIdx + NumAddrSpace;
547 *(State.
MIs[InsnID]->memoperands_begin() + MMOIdx);
551 for (
unsigned I = 0;
I != NumAddrSpace; ++
I) {
554 dbgs() <<
"addrspace(" << MMOAddrSpace <<
") vs "
555 << AddrSpace <<
'\n');
557 if (AddrSpace == MMOAddrSpace) {
563 CurrentIdx = LastIdx;
564 if (!
Success && handleReject() == RejectAndGiveUp)
573 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
575 if (State.
MIs[InsnID]->getNumMemOperands() <= MMOIdx) {
576 if (handleReject() == RejectAndGiveUp)
582 *(State.
MIs[InsnID]->memoperands_begin() + MMOIdx);
584 dbgs() << CurrentIdx <<
": GIM_CheckMemoryAlignment"
585 <<
"(MIs[" << InsnID <<
"]->memoperands() + "
586 << MMOIdx <<
")->getAlignment() >= " <<
MinAlign
599 dbgs() << CurrentIdx <<
": GIM_CheckMemorySizeEqual(MIs["
600 << InsnID <<
"]->memoperands() + " << MMOIdx
601 <<
", Size=" <<
Size <<
")\n");
602 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
604 if (State.
MIs[InsnID]->getNumMemOperands() <= MMOIdx) {
605 if (handleReject() == RejectAndGiveUp)
611 *(State.
MIs[InsnID]->memoperands_begin() + MMOIdx);
614 <<
" bytes vs " <<
Size
617 if (handleReject() == RejectAndGiveUp)
630 TgtExecutor::getName(),
631 dbgs() << CurrentIdx <<
": GIM_CheckMemorySize"
636 <<
"LLT(MIs[" << InsnID <<
"]->memoperands() + " << MMOIdx
637 <<
", OpIdx=" << OpIdx <<
")\n");
638 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
643 dbgs() << CurrentIdx <<
": Not a register\n");
644 if (handleReject() == RejectAndGiveUp)
649 if (State.
MIs[InsnID]->getNumMemOperands() <= MMOIdx) {
650 if (handleReject() == RejectAndGiveUp)
656 *(State.
MIs[InsnID]->memoperands_begin() + MMOIdx);
661 if (handleReject() == RejectAndGiveUp)
665 if (handleReject() == RejectAndGiveUp)
669 if (handleReject() == RejectAndGiveUp)
680 dbgs() << CurrentIdx <<
": GIM_CheckType(MIs[" << InsnID
681 <<
"]->getOperand(" << OpIdx
682 <<
"), TypeID=" <<
TypeID <<
")\n");
683 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
686 if (handleReject() == RejectAndGiveUp)
697 dbgs() << CurrentIdx <<
": GIM_CheckPointerToAny(MIs["
698 << InsnID <<
"]->getOperand(" << OpIdx
699 <<
"), SizeInBits=" << SizeInBits <<
")\n");
700 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
705 if (SizeInBits == 0) {
711 assert(SizeInBits != 0 &&
"Pointer size must be known");
715 if (handleReject() == RejectAndGiveUp)
717 }
else if (handleReject() == RejectAndGiveUp)
728 dbgs() << CurrentIdx <<
": GIM_RecordNamedOperand(MIs["
729 << InsnID <<
"]->getOperand(" << OpIdx
730 <<
"), StoreIdx=" << StoreIdx <<
")\n");
731 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
739 int TypeIdx = readS8();
742 dbgs() << CurrentIdx <<
": GIM_RecordRegType(MIs["
743 << InsnID <<
"]->getOperand(" << OpIdx
744 <<
"), TypeIdx=" << TypeIdx <<
")\n");
745 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
746 assert(TypeIdx < 0 &&
"Temp types always have negative indexes!");
748 TypeIdx = 1 - TypeIdx;
749 const auto &
Op = State.
MIs[InsnID]->getOperand(OpIdx);
763 dbgs() << CurrentIdx <<
": GIM_CheckRegBankForClass(MIs["
764 << InsnID <<
"]->getOperand(" << OpIdx
765 <<
"), RCEnum=" << RCEnum <<
")\n");
766 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
772 if (handleReject() == RejectAndGiveUp)
782 uint16_t ComplexPredicateID = readU16();
784 dbgs() << CurrentIdx <<
": State.Renderers[" << RendererID
785 <<
"] = GIM_CheckComplexPattern(MIs[" << InsnID
786 <<
"]->getOperand(" << OpIdx
787 <<
"), ComplexPredicateID=" << ComplexPredicateID
789 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
793 State.
MIs[InsnID]->getOperand(OpIdx));
796 else if (handleReject() == RejectAndGiveUp)
809 dbgs() << CurrentIdx <<
": GIM_CheckConstantInt(MIs["
810 << InsnID <<
"]->getOperand(" << OpIdx
811 <<
"), Value=" <<
Value <<
")\n");
812 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
820 if (handleReject() == RejectAndGiveUp)
827 if (handleReject() == RejectAndGiveUp)
830 }
else if (handleReject() == RejectAndGiveUp)
839 int64_t
Value = readU64();
841 dbgs() << CurrentIdx <<
": GIM_CheckLiteralInt(MIs["
842 << InsnID <<
"]->getOperand(" << OpIdx
843 <<
"), Value=" <<
Value <<
")\n");
844 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
852 if (handleReject() == RejectAndGiveUp)
863 dbgs() << CurrentIdx <<
": GIM_CheckIntrinsicID(MIs["
864 << InsnID <<
"]->getOperand(" << OpIdx
865 <<
"), Value=" <<
Value <<
")\n");
866 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
869 if (handleReject() == RejectAndGiveUp)
878 dbgs() << CurrentIdx <<
": GIM_CheckCmpPredicate(MIs["
879 << InsnID <<
"]->getOperand(" << OpIdx
880 <<
"), Value=" <<
Value <<
")\n");
881 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
884 if (handleReject() == RejectAndGiveUp)
892 dbgs() << CurrentIdx <<
": GIM_CheckIsMBB(MIs[" << InsnID
893 <<
"]->getOperand(" << OpIdx <<
"))\n");
894 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
895 if (!State.
MIs[InsnID]->getOperand(OpIdx).isMBB()) {
896 if (handleReject() == RejectAndGiveUp)
905 dbgs() << CurrentIdx <<
": GIM_CheckIsImm(MIs[" << InsnID
906 <<
"]->getOperand(" << OpIdx <<
"))\n");
907 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
908 if (!State.
MIs[InsnID]->getOperand(OpIdx).isImm()) {
909 if (handleReject() == RejectAndGiveUp)
915 uint64_t NumInsn = MatchTable[CurrentIdx++];
917 dbgs() << CurrentIdx <<
": GIM_CheckIsSafeToFold(N = "
918 << NumInsn <<
")\n");
920 for (
unsigned K = 1,
E = NumInsn + 1; K <
E; ++K) {
922 if (handleReject() == RejectAndGiveUp)
935 dbgs() << CurrentIdx <<
": GIM_CheckIsSameOperand(MIs["
936 << InsnID <<
"][" << OpIdx <<
"], MIs["
937 << OtherInsnID <<
"][" << OtherOpIdx <<
"])\n");
938 assert(State.
MIs[InsnID] !=
nullptr &&
"Used insn before defined");
939 assert(State.
MIs[OtherInsnID] !=
nullptr &&
"Used insn before defined");
945 if (
Op.isReg() && OtherOp.
isReg()) {
952 if (!
Op.isIdenticalTo(OtherOp)) {
953 if (handleReject() == RejectAndGiveUp)
965 dbgs() << CurrentIdx <<
": GIM_CheckCanReplaceReg(MIs["
966 << OldInsnID <<
"][" << OldOpIdx <<
"] = MIs["
967 << NewInsnID <<
"][" << NewOpIdx <<
"])\n");
969 Register Old = State.
MIs[OldInsnID]->getOperand(OldOpIdx).getReg();
970 Register New = State.
MIs[NewInsnID]->getOperand(NewOpIdx).getReg();
972 if (handleReject() == RejectAndGiveUp)
982 dbgs() << CurrentIdx <<
": GIM_MIFlags(MIs[" << InsnID
983 <<
"], " << Flags <<
")\n");
984 if ((State.
MIs[InsnID]->getFlags() & Flags) != Flags) {
985 if (handleReject() == RejectAndGiveUp)
995 dbgs() << CurrentIdx <<
": GIM_MIFlagsNot(MIs[" << InsnID
996 <<
"], " << Flags <<
")\n");
997 if ((State.
MIs[InsnID]->getFlags() & Flags)) {
998 if (handleReject() == RejectAndGiveUp)
1005 dbgs() << CurrentIdx <<
": GIM_Reject\n");
1006 if (handleReject() == RejectAndGiveUp)
1013 if (NewInsnID >= OutMIs.
size())
1014 OutMIs.
resize(NewInsnID + 1);
1020 OutMIs[NewInsnID]->setDesc(
TII.get(NewOpcode));
1024 dbgs() << CurrentIdx <<
": GIR_MutateOpcode(OutMIs["
1025 << NewInsnID <<
"], MIs[" << OldInsnID <<
"], "
1026 << NewOpcode <<
")\n");
1034 if (NewInsnID >= OutMIs.
size())
1035 OutMIs.
resize(NewInsnID + 1);
1037 OutMIs[NewInsnID] = Builder.
buildInstr(Opcode);
1039 dbgs() << CurrentIdx <<
": GIR_BuildMI(OutMIs["
1040 << NewInsnID <<
"], " << Opcode <<
")\n");
1049 dbgs() << CurrentIdx <<
": GIR_BuildConstant(TempReg["
1050 << TempRegID <<
"], Imm=" << Imm <<
")\n");
1061 assert(OutMIs[NewInsnID] &&
"Attempted to add to undefined instruction");
1062 OutMIs[NewInsnID].add(State.
MIs[OldInsnID]->getOperand(OpIdx));
1065 << CurrentIdx <<
": GIR_Copy(OutMIs[" << NewInsnID
1066 <<
"], MIs[" << OldInsnID <<
"], " << OpIdx <<
")\n");
1075 assert(OutMIs[NewInsnID] &&
"Attempted to add to undefined instruction");
1078 OutMIs[NewInsnID].addReg(ZeroReg);
1080 OutMIs[NewInsnID].add(MO);
1082 dbgs() << CurrentIdx <<
": GIR_CopyOrAddZeroReg(OutMIs["
1083 << NewInsnID <<
"], MIs[" << OldInsnID <<
"], "
1084 << OpIdx <<
", " << ZeroReg <<
")\n");
1093 assert(OutMIs[NewInsnID] &&
"Attempted to add to undefined instruction");
1094 OutMIs[NewInsnID].addReg(State.
MIs[OldInsnID]->getOperand(OpIdx).getReg(),
1097 dbgs() << CurrentIdx <<
": GIR_CopySubReg(OutMIs["
1098 << NewInsnID <<
"], MIs[" << OldInsnID <<
"], "
1099 << OpIdx <<
", " << SubRegIdx <<
")\n");
1107 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1109 OutMIs[InsnID].addDef(RegNum, Flags);
1111 dbgs() << CurrentIdx <<
": GIR_AddImplicitDef(OutMIs["
1112 << InsnID <<
"], " << RegNum <<
")\n");
1119 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1122 dbgs() << CurrentIdx <<
": GIR_AddImplicitUse(OutMIs["
1123 << InsnID <<
"], " << RegNum <<
")\n");
1131 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1132 OutMIs[InsnID].addReg(RegNum, RegFlags);
1135 << CurrentIdx <<
": GIR_AddRegister(OutMIs[" << InsnID
1136 <<
"], " << RegNum <<
", " << RegFlags <<
")\n");
1142 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1145 dbgs() << CurrentIdx <<
": GIR_AddIntrinsicID(OutMIs["
1146 << InsnID <<
"], " <<
Value <<
")\n");
1153 dbgs() << CurrentIdx <<
": GIR_SetImplicitDefDead(OutMIs["
1154 << InsnID <<
"], OpIdx=" << OpIdx <<
")\n");
1156 assert(
MI &&
"Modifying undefined instruction");
1157 MI->getOperand(
MI->getNumExplicitOperands() + OpIdx).setIsDead();
1165 dbgs() << CurrentIdx <<
": GIR_SetMIFlags(OutMIs["
1166 << InsnID <<
"], " << Flags <<
")\n");
1168 MI->setFlags(
MI->getFlags() | Flags);
1176 dbgs() << CurrentIdx <<
": GIR_UnsetMIFlags(OutMIs["
1177 << InsnID <<
"], " << Flags <<
")\n");
1179 MI->setFlags(
MI->getFlags() & ~Flags);
1187 dbgs() << CurrentIdx <<
": GIR_CopyMIFlags(OutMIs["
1188 << InsnID <<
"], MIs[" << OldInsnID <<
"])\n");
1190 MI->setFlags(
MI->getFlags() | State.
MIs[OldInsnID]->getFlags());
1200 TempRegFlags = readU16();
1205 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1207 OutMIs[InsnID].addReg(State.
TempRegisters[TempRegID], TempRegFlags,
1210 TgtExecutor::getName(),
1211 dbgs() << CurrentIdx <<
": GIR_AddTempRegister(OutMIs[" << InsnID
1212 <<
"], TempRegisters[" << TempRegID <<
"]";
1214 dbgs() <<
", " << TempRegFlags <<
")\n");
1220 const bool IsAdd8 = (MatcherOpcode ==
GIR_AddImm8);
1222 uint64_t Imm = IsAdd8 ? (int64_t)readS8() : readU64();
1223 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1224 OutMIs[InsnID].addImm(Imm);
1226 dbgs() << CurrentIdx <<
": GIR_AddImm(OutMIs[" << InsnID
1227 <<
"], " << Imm <<
")\n");
1235 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1239 OutMIs[InsnID].addCImm(
1242 dbgs() << CurrentIdx <<
": GIR_AddCImm(OutMIs[" << InsnID
1243 <<
"], TypeID=" <<
TypeID <<
", Imm=" << Imm
1251 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1252 for (
const auto &RenderOpFn : State.
Renderers[RendererID])
1253 RenderOpFn(OutMIs[InsnID]);
1255 dbgs() << CurrentIdx <<
": GIR_ComplexRenderer(OutMIs["
1256 << InsnID <<
"], " << RendererID <<
")\n");
1263 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1264 State.
Renderers[RendererID][RenderOpID](OutMIs[InsnID]);
1266 dbgs() << CurrentIdx
1267 <<
": GIR_ComplexSubOperandRenderer(OutMIs["
1268 << InsnID <<
"], " << RendererID <<
", "
1269 << RenderOpID <<
")\n");
1278 assert(
MI &&
"Attempted to add to undefined instruction");
1280 MI->getOperand(
MI->getNumOperands() - 1).setSubReg(SubRegIdx);
1282 dbgs() << CurrentIdx
1283 <<
": GIR_ComplexSubOperandSubRegRenderer(OutMIs["
1284 << InsnID <<
"], " << RendererID <<
", "
1285 << RenderOpID <<
", " << SubRegIdx <<
")\n");
1292 assert(OutMIs[NewInsnID] &&
"Attempted to add to undefined instruction");
1293 assert(State.
MIs[OldInsnID]->getOpcode() == TargetOpcode::G_CONSTANT &&
1294 "Expected G_CONSTANT");
1295 if (State.
MIs[OldInsnID]->getOperand(1).isCImm()) {
1296 OutMIs[NewInsnID].addImm(
1297 State.
MIs[OldInsnID]->getOperand(1).getCImm()->getSExtValue());
1298 }
else if (State.
MIs[OldInsnID]->getOperand(1).isImm())
1299 OutMIs[NewInsnID].add(State.
MIs[OldInsnID]->getOperand(1));
1303 dbgs() << CurrentIdx <<
": GIR_CopyConstantAsSImm(OutMIs["
1304 << NewInsnID <<
"], MIs[" << OldInsnID <<
"])\n");
1312 assert(OutMIs[NewInsnID] &&
"Attempted to add to undefined instruction");
1313 assert(State.
MIs[OldInsnID]->getOpcode() == TargetOpcode::G_FCONSTANT &&
1314 "Expected G_FCONSTANT");
1315 if (State.
MIs[OldInsnID]->getOperand(1).isFPImm())
1316 OutMIs[NewInsnID].addFPImm(
1317 State.
MIs[OldInsnID]->getOperand(1).getFPImm());
1322 << CurrentIdx <<
": GIR_CopyFPConstantAsFPImm(OutMIs["
1323 << NewInsnID <<
"], MIs[" << OldInsnID <<
"])\n");
1331 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1333 dbgs() << CurrentIdx <<
": GIR_CustomRenderer(OutMIs["
1334 << InsnID <<
"], MIs[" << OldInsnID <<
"], "
1335 << RendererFnID <<
")\n");
1337 OutMIs[InsnID], *State.
MIs[OldInsnID],
1344 dbgs() << CurrentIdx <<
": GIR_CustomAction(FnID=" << FnID
1355 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1358 dbgs() << CurrentIdx
1359 <<
": GIR_CustomOperandRenderer(OutMIs[" << InsnID
1360 <<
"], MIs[" << OldInsnID <<
"]->getOperand("
1361 << OpIdx <<
"), " << RendererFnID <<
")\n");
1363 OutMIs[InsnID], *State.
MIs[OldInsnID], OpIdx);
1370 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1378 dbgs() << CurrentIdx <<
": GIR_ConstrainOperandRC(OutMIs["
1379 << InsnID <<
"], " << OpIdx <<
", " << RCEnum
1389 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1393 dbgs() << CurrentIdx
1394 <<
": GIR_ConstrainSelectedInstOperands(OutMIs["
1395 << InsnID <<
"])\n");
1400 uint64_t NumInsn = MatchTable[CurrentIdx++];
1401 assert(OutMIs[InsnID] &&
"Attempted to add to undefined instruction");
1404 dbgs() << CurrentIdx <<
": GIR_MergeMemOperands(OutMIs["
1406 for (
unsigned K = 0; K < NumInsn; ++K) {
1409 dbgs() <<
", MIs[" << NextID <<
"]");
1410 for (
const auto &MMO : State.
MIs[NextID]->memoperands())
1411 OutMIs[InsnID].addMemOperand(MMO);
1419 assert(
MI &&
"Attempted to erase an undefined instruction");
1421 dbgs() << CurrentIdx <<
": GIR_EraseFromParent(MIs["
1422 << InsnID <<
"])\n");
1429 << CurrentIdx <<
": GIR_EraseRootFromParent_Done\n");
1430 eraseImpl(State.
MIs[0]);
1439 MRI.createGenericVirtualRegister(getTypeFromIdx(
TypeID));
1441 dbgs() << CurrentIdx <<
": TempRegs[" << TempRegID
1442 <<
"] = GIR_MakeTempReg(" <<
TypeID <<
")\n");
1452 dbgs() << CurrentIdx <<
": GIR_ReplaceReg(MIs["
1453 << OldInsnID <<
"][" << OldOpIdx <<
"] = MIs["
1454 << NewInsnID <<
"][" << NewOpIdx <<
"])\n");
1456 Register Old = State.
MIs[OldInsnID]->getOperand(OldOpIdx).getReg();
1457 Register New = State.
MIs[NewInsnID]->getOperand(NewOpIdx).getReg();
1460 MRI.replaceRegWith(Old, New);
1471 dbgs() << CurrentIdx <<
": GIR_ReplaceRegWithTempReg(MIs["
1472 << OldInsnID <<
"][" << OldOpIdx <<
"] = TempRegs["
1473 << TempRegID <<
"])\n");
1475 Register Old = State.
MIs[OldInsnID]->getOperand(OldOpIdx).getReg();
1479 MRI.replaceRegWith(Old, New);
1490 <<
": GIR_Coverage("
1497 dbgs() << CurrentIdx <<
": GIR_Done\n");
unsigned const MachineRegisterInfo * MRI
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Returns the sub type a function will return at a given Idx Should correspond to the result type of an ExtractValue instruction executed with just that one unsigned Idx
#define DEBUG_WITH_TYPE(TYPE, X)
DEBUG_WITH_TYPE macro - This macro should be used by passes to emit debug information.
This contains common code to allow clients to notify changes to machine instr.
const HexagonInstrInfo * TII
This file declares the MachineIRBuilder class.
unsigned const TargetRegisterInfo * TRI
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file defines the SmallVector class.
Class for arbitrary precision integers.
void setCovered(uint64_t RuleID)
bool equalsInt(uint64_t V) const
A helper method that can be used to determine if the constant contained within is equal to a constant...
This class represents an Operation in the Expression.
unsigned getPointerSizeInBits(unsigned AS=0) const
Layout pointer size, in bits FIXME: The defaults need to be removed once all of the backends/clients ...
Tagged union holding either a T or a Error.
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
virtual bool testSimplePredicate(unsigned) const
bool executeMatchTable(TgtExecutor &Exec, MatcherState &State, const ExecInfoTy< PredicateBitset, ComplexMatcherMemFn, CustomRendererFn > &ExecInfo, MachineIRBuilder &Builder, const uint8_t *MatchTable, const TargetInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures, CodeGenCoverage *CoverageInfo) const
Execute a given matcher table and return true if the match was successful and false otherwise.
virtual bool testImmPredicate_APFloat(unsigned, const APFloat &) const
virtual bool testImmPredicate_APInt(unsigned, const APInt &) const
virtual bool testMIPredicate_MI(unsigned, const MachineInstr &, const MatcherState &State) const
virtual bool testImmPredicate_I64(unsigned, int64_t) const
std::optional< SmallVector< std::function< void(MachineInstrBuilder &)>, 4 > > ComplexRendererFns
bool isOperandImmEqual(const MachineOperand &MO, int64_t Value, const MachineRegisterInfo &MRI, bool Splat=false) const
virtual void runCustomAction(unsigned, const MatcherState &State, NewMIVector &OutMIs) const
bool isObviouslySafeToFold(MachineInstr &MI, MachineInstr &IntoMI) const
Return true if MI can obviously be folded into IntoMI.
CodeGenCoverage * CoverageInfo
Abstract class that contains various methods for clients to notify about changes.
virtual void changingInstr(MachineInstr &MI)=0
This instruction is about to be mutated in some way.
void finishedChangingAllUsesOfReg()
All instructions reported as changing by changingAllUsesOfReg() have finished being changed.
virtual void changedInstr(MachineInstr &MI)=0
This instruction was mutated in some way.
virtual void erasingInstr(MachineInstr &MI)=0
An instruction is about to be erased.
void changingAllUsesOfReg(const MachineRegisterInfo &MRI, Register Reg)
All the instructions using the given register are being changed.
static IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
constexpr unsigned getScalarSizeInBits() const
constexpr TypeSize getSizeInBits() const
Returns the total size of the type. Must only be called on sized types.
constexpr bool isPointer() const
constexpr unsigned getAddressSpace() const
This is an important class for using LLVM in a threaded context.
TypeSize getValue() const
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
const DataLayout & getDataLayout() const
Return the DataLayout attached to the Module associated to this MF.
Function & getFunction()
Return the LLVM function that this machine code represents.
Helper class to build MachineInstr.
GISelChangeObserver * getObserver()
void setInsertPt(MachineBasicBlock &MBB, MachineBasicBlock::iterator II)
Set the insertion point before the specified position.
MachineBasicBlock::iterator getInsertPt()
Current insertion point for new instructions.
MachineInstrBuilder buildInstr(unsigned Opcode)
Build and insert <empty> = Opcode <empty>.
virtual MachineInstrBuilder buildConstant(const DstOp &Res, const ConstantInt &Val)
Build and insert Res = G_CONSTANT Val.
Representation of each machine instruction.
const MachineFunction * getMF() const
Return the function that contains the basic block that this instruction belongs to.
A description of a memory reference used in the backend.
LocationSize getSize() const
Return the size in bytes of the memory reference.
unsigned getAddrSpace() const
Align getAlign() const
Return the minimum known alignment in bytes of the actual memory reference.
LocationSize getSizeInBits() const
Return the size in bits of the memory reference.
MachineOperand class - Representation of each machine instruction operand.
const ConstantInt * getCImm() const
bool isCImm() const
isCImm - Test if this is a MO_CImmediate operand.
bool isIntrinsicID() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
bool isImm() const
isImm - Tests if this is a MO_Immediate operand.
Register getReg() const
getReg - Returns the register number.
Intrinsic::ID getIntrinsicID() const
unsigned getPredicate() const
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
Holds all the information related to register banks.
const RegisterBank & getRegBank(unsigned ID)
Get the register bank identified by ID.
virtual const RegisterBank & getRegBankFromRegClass(const TargetRegisterClass &RC, LLT Ty) const
Get a register bank that covers RC.
Wrapper class representing virtual and physical registers.
constexpr bool isPhysical() const
Return true if the specified register number is in the physical register namespace.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
TargetInstrInfo - Interface to description of machine instruction set.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
LLVM Value Representation.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ Implicit
Not emitted register (e.g. carry, or temporary result).
This is an optimization pass for GlobalISel generic memory operations.
bool isBuildVectorAllZeros(const MachineInstr &MI, const MachineRegisterInfo &MRI, bool AllowUndef=false)
Return true if the specified instruction is a G_BUILD_VECTOR or G_BUILD_VECTOR_TRUNC where all of the...
Register constrainOperandRegClass(const MachineFunction &MF, const TargetRegisterInfo &TRI, MachineRegisterInfo &MRI, const TargetInstrInfo &TII, const RegisterBankInfo &RBI, MachineInstr &InsertPt, const TargetRegisterClass &RegClass, MachineOperand &RegMO)
Constrain the Register operand OpIdx, so that it is now constrained to the TargetRegisterClass passed...
@ GICXXCustomAction_Invalid
bool constrainSelectedInstRegOperands(MachineInstr &I, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI)
Mutate the newly-selected instruction I to constrain its (possibly generic) virtual register operands...
uint64_t decodeULEB128(const uint8_t *p, unsigned *n=nullptr, const uint8_t *end=nullptr, const char **error=nullptr)
Utility function to decode a ULEB128 value.
MachineInstr * getDefIgnoringCopies(Register Reg, const MachineRegisterInfo &MRI)
Find the def instruction for Reg, folding away any trivial copies.
@ GIR_AddIntrinsicID
Adds an intrinsic ID to the specified instruction.
@ GIR_ComplexRenderer
Render complex operands to the specified instruction.
@ GIR_ReplaceRegWithTempReg
Replaces all references to a register with a temporary register.
@ GIR_ComplexSubOperandRenderer
Render sub-operands of complex operands to the specified instruction.
@ GIR_MakeTempReg
Create a new temporary register that's not constrained.
@ GIM_CheckMemorySizeEqualTo
Check the size of the memory access for the given machine memory operand.
@ GIM_RootCheckType
GIM_CheckType but InsnID is omitted and defaults to zero.
@ GIM_RootCheckRegBankForClass
GIM_CheckRegBankForClass but InsnID is omitted and defaults to zero.
@ GIR_Done
A successful emission.
@ GIM_RecordNamedOperand
Predicates with 'let PredicateCodeUsesOperands = 1' need to examine some named operands that will be ...
@ GIM_Try
Begin a try-block to attempt a match and jump to OnFail if it is unsuccessful.
@ GIR_RootConstrainSelectedInstOperands
GIR_ConstrainSelectedInstOperands but InsnID is omitted and defaults to zero.
@ GIM_CheckIsBuildVectorAllOnes
Check if this is a vector that can be treated as a vector splat constant.
@ GIM_CheckNumOperands
Check the instruction has the right number of operands.
@ GIR_AddCImm
Add an CImm to the specified instruction.
@ GIR_ConstrainOperandRC
Constrain an instruction operand to a register class.
@ GIM_CheckI64ImmPredicate
Check an immediate predicate on the specified instruction.
@ GIR_AddImplicitDef
Add an implicit register def to the specified instruction.
@ GIM_CheckAPIntImmPredicate
Check an immediate predicate on the specified instruction via an APInt.
@ GIM_CheckHasNoUse
Check if there's no use of the first result.
@ GIM_CheckPointerToAny
Check the type of a pointer to any address space.
@ GIM_CheckMemorySizeEqualToLLT
Check the size of the memory access for the given machine memory operand against the size of an opera...
@ GIM_CheckComplexPattern
Check the operand matches a complex predicate.
@ GIR_CopyConstantAsSImm
Render a G_CONSTANT operator as a sign-extended immediate.
@ GIR_EraseFromParent
Erase from parent.
@ GIR_CustomAction
Calls a C++ function to perform an action when a match is complete.
@ GIM_SwitchType
Switch over the LLT on the specified instruction operand.
@ GIR_CopySubReg
Copy an operand to the specified instruction.
@ GIR_MutateOpcode
Mutate an instruction.
@ GIM_CheckIsBuildVectorAllZeros
@ GIM_CheckAtomicOrderingOrStrongerThan
@ GIR_AddRegister
Add an register to the specified instruction.
@ GIR_AddTempSubRegister
Add a temporary register to the specified instruction.
@ GIM_CheckIsSafeToFold
Checks if the matched instructions numbered [1, 1+N) can be folded into the root (inst 0).
@ GIM_CheckOpcode
Check the opcode on the specified instruction.
@ GIR_ReplaceReg
Replaces all references to a register from an instruction with another register from another instruct...
@ GIM_SwitchOpcode
Switch over the opcode on the specified instruction.
@ GIM_CheckAPFloatImmPredicate
Check a floating point immediate predicate on the specified instruction.
@ GIM_Reject
Fail the current try-block, or completely fail to match if there is no current try-block.
@ GIR_AddSimpleTempRegister
Add a temporary register to the specified instruction without setting any flags.
@ GIR_AddTempRegister
Add a temporary register to the specified instruction.
@ GIR_Copy
Copy an operand to the specified instruction.
@ GIR_AddImm
Add an immediate to the specified instruction.
@ GIR_CopyFConstantAsFPImm
Render a G_FCONSTANT operator as a sign-extended immediate.
@ GIM_MIFlags
Check that a matched instruction has, or doesn't have a MIFlag.
@ GIR_CopyOrAddZeroReg
Copy an operand to the specified instruction or add a zero register if the operand is a zero immediat...
@ GIM_CheckMemoryAlignment
Check the minimum alignment of the memory access for the given machine memory operand.
@ GIM_CheckIsSameOperand
Check the specified operands are identical.
@ GIR_AddImm8
Add signed 8 bit immediate to the specified instruction.
@ GIM_CheckIsSameOperandIgnoreCopies
@ GIM_CheckIsMBB
Check the specified operand is an MBB.
@ GIM_CheckMemorySizeGreaterThanLLT
@ GIM_CheckRegBankForClass
Check the register bank for the specified operand.
@ GIM_CheckLiteralInt
Check the operand is a specific literal integer (i.e.
@ GIM_CheckMemorySizeLessThanLLT
@ GIM_RecordRegType
Records an operand's register type into the set of temporary types.
@ GIR_EraseRootFromParent_Done
Combines both a GIR_EraseFromParent 0 + GIR_Done.
@ GIR_CopyMIFlags
Copy the MIFlags of a matched instruction into an output instruction.
@ GIR_BuildMI
Build a new instruction.
@ GIM_RecordInsn
Record the specified instruction.
@ GIM_CheckIsImm
Check the specified operand is an Imm.
@ GIR_BuildRootMI
GIR_BuildMI but InsnID is omitted and defaults to zero.
@ GIM_CheckFeatures
Check the feature bits Feature(2) - Expected features.
@ GIM_CheckCanReplaceReg
Check we can replace all uses of a register with another.
@ GIM_CheckMemoryAddressSpace
Check the address space of the memory access for the given machine memory operand.
@ GIR_CustomRenderer
Render operands to the specified instruction using a custom function.
@ GIM_CheckAtomicOrdering
Check a memory operation has the specified atomic ordering.
@ GIM_CheckType
Check the type for the specified operand.
@ GIM_CheckConstantInt8
Check the operand is a specific 8-bit signed integer.
@ GIM_CheckCmpPredicate
Check the operand is a specific predicate.
@ GIM_CheckOpcodeIsEither
Check the opcode on the specified instruction, checking 2 acceptable alternatives.
@ GIR_SetImplicitDefDead
Marks the implicit def of a register as dead.
@ GIR_BuildConstant
Builds a constant and stores its result in a TempReg.
@ GIR_AddImplicitUse
Add an implicit register use to the specified instruction.
@ GIR_Coverage
Increment the rule coverage counter.
@ GIR_MergeMemOperands
Merge all memory operands into instruction.
@ GIM_CheckImmOperandPredicate
Check an immediate predicate on the specified instruction.
@ GIM_CheckAtomicOrderingWeakerThan
@ GIR_SetMIFlags
Set or unset a MIFlag on an instruction.
@ GIM_CheckIntrinsicID
Check the operand is a specific intrinsic ID.
@ GIM_CheckConstantInt
Check the operand is a specific integer.
@ GIR_RootToRootCopy
GIR_Copy but with both New/OldInsnIDs omitted and defaulting to zero.
@ GIR_ComplexSubOperandSubRegRenderer
Render subregisters of suboperands of complex operands to the specified instruction.
@ GIM_RecordInsnIgnoreCopies
@ GIR_CustomOperandRenderer
Render operands to the specified instruction using a custom function, reading from a specific operand...
@ GIR_ConstrainSelectedInstOperands
Constrain an instructions operands according to the instruction description.
@ GIM_CheckCxxInsnPredicate
Check a generic C++ instruction predicate.
@ GIM_CheckSimplePredicate
Check a trivial predicate which takes no arguments.
bool canReplaceReg(Register DstReg, Register SrcReg, MachineRegisterInfo &MRI)
Check if DstReg can be replaced with SrcReg depending on the register constraints.
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
bool isAtLeastOrStrongerThan(AtomicOrdering AO, AtomicOrdering Other)
bool isBuildVectorAllOnes(const MachineInstr &MI, const MachineRegisterInfo &MRI, bool AllowUndef=false)
Return true if the specified instruction is a G_BUILD_VECTOR or G_BUILD_VECTOR_TRUNC where all of the...
AtomicOrdering
Atomic ordering for LLVM's memory model.
constexpr uint64_t MinAlign(uint64_t A, uint64_t B)
A and B are either alignments or offsets.
constexpr int64_t SignExtend64(uint64_t x)
Sign-extend the number in the bottom B bits of X to a 64-bit integer.
Register getSrcRegIgnoringCopies(Register Reg, const MachineRegisterInfo &MRI)
Find the source register for Reg, folding away any trivial copies.
@ Default
The result values are uniform if and only if all operands are uniform.
bool isStrongerThan(AtomicOrdering AO, AtomicOrdering Other)
Returns true if ao is stronger than other as defined by the AtomicOrdering lattice,...
const CustomRendererFn * CustomRenderers
SmallDenseMap< LLT, unsigned, 64 > TypeIDMap
const ComplexMatcherMemFn * ComplexPredicates
const PredicateBitset * FeatureBitsets
std::array< const MachineOperand *, 3 > RecordedOperands
Named operands that predicate with 'let PredicateCodeUsesOperands = 1' referenced in its argument lis...
SmallVector< LLT, 4 > RecordedTypes
Types extracted from an instruction's operand.
DenseMap< unsigned, unsigned > TempRegisters
std::vector< ComplexRendererFns::value_type > Renderers