/// - TempRegFlags - The register flags to set
GIR_AddTempRegister,
+ /// Add a temporary register to the specified instruction
+ /// - InsnID - Instruction ID to modify
+ /// - TempRegID - The temporary register ID to add
+ /// - TempRegFlags - The register flags to set
+ /// - SubRegIndex - The subregister index to set
+ GIR_AddTempSubRegister,
+
/// Add an immediate to the specified instruction
/// - InsnID - Instruction ID to modify
/// - Imm - The immediate to add
break;
}
- case GIR_AddTempRegister: {
+ case GIR_AddTempRegister:
+ case GIR_AddTempSubRegister: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t TempRegID = MatchTable[CurrentIdx++];
uint64_t TempRegFlags = MatchTable[CurrentIdx++];
+ unsigned SubReg = 0;
+ if (MatcherOpcode == GIR_AddTempSubRegister)
+ SubReg = MatchTable[CurrentIdx++];
+
assert(OutMIs[InsnID] && "Attempted to add to undefined instruction");
- OutMIs[InsnID].addReg(State.TempRegisters[TempRegID], TempRegFlags);
+
+ OutMIs[InsnID].addReg(State.TempRegisters[TempRegID], TempRegFlags, SubReg);
DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
dbgs() << CurrentIdx << ": GIR_AddTempRegister(OutMIs["
<< InsnID << "], TempRegisters[" << TempRegID
- << "], " << TempRegFlags << ")\n");
+ << "]";
+ if (SubReg)
+ dbgs() << '.' << TRI.getSubRegIndexName(SubReg);
+ dbgs() << ", " << TempRegFlags << ")\n");
break;
}
regBankSelected: true
# ALL: registers:
# ALL-NEXT: - { id: 0, class: gr16[[ABCD:(_abcd)?]], preferred-register: '' }
-# ALL-NEXT: - { id: 1, class: gr8, preferred-register: '' }
+# X32-NEXT: - { id: 1, class: gr8_abcd_l, preferred-register: '' }
+# X64-NEXT: - { id: 1, class: gr8, preferred-register: '' }
# ALL-NEXT: - { id: 2, class: gr32, preferred-register: '' }
registers:
- { id: 0, class: gpr, preferred-register: '' }
regBankSelected: true
# ALL: registers:
# ALL-NEXT: - { id: 0, class: gr32[[ABCD:(_abcd)?]], preferred-register: '' }
-# ALL-NEXT: - { id: 1, class: gr8, preferred-register: '' }
+# X32-NEXT: - { id: 1, class: gr8_abcd_l, preferred-register: '' }
+# X64-NEXT: - { id: 1, class: gr8, preferred-register: '' }
# ALL-NEXT: - { id: 2, class: gr32, preferred-register: '' }
registers:
- { id: 0, class: gpr, preferred-register: '' }
legalized: true
regBankSelected: true
# X32: registers:
-# X32-NEXT: - { id: 0, class: gr32_abcd, preferred-register: '' }
-# X32-NEXT: - { id: 1, class: gr8, preferred-register: '' }
+# X32-NEXT: - { id: 0, class: gr32, preferred-register: '' }
+# X32-NEXT: - { id: 1, class: gr8_abcd_l, preferred-register: '' }
# X32-NEXT: - { id: 2, class: gr16, preferred-register: '' }
#
# X64: registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
-# X32: %0:gr32_abcd = COPY $edi
-# X64: %0:gr32 = COPY $edi
-# ALL-NEXT: %1:gr8 = COPY %0.sub_8bit
-# ALL-NEXT: %2:gr16 = SUBREG_TO_REG 0, %1, %subreg.sub_8bit
+# X32: %0:gr32 = COPY $edi
+# X32-NEXT: %4:gr32_abcd = COPY %0
+# X32-NEXT: %1:gr8_abcd_l = COPY %4.sub_8bit
+
+# X64: %0:gr32 = COPY $edi
+# X64-NEXT: %1:gr8 = COPY %0.sub_8bit
+
+# ALL-NEXT: %3:gr32 = MOVZX32rr8 %1
+# ALL-NEXT: %2:gr16 = COPY %3.sub_16bit
# ALL-NEXT: $ax = COPY %2
# ALL-NEXT: RET 0, implicit $ax
body: |
legalized: true
regBankSelected: true
# X32: registers:
-# X32-NEXT: - { id: 0, class: gr32_abcd, preferred-register: '' }
-# X32-NEXT: - { id: 1, class: gr8, preferred-register: '' }
+# X32-NEXT: - { id: 0, class: gr32, preferred-register: '' }
+# X32-NEXT: - { id: 1, class: gr8_abcd_l, preferred-register: '' }
# X32-NEXT: - { id: 2, class: gr32, preferred-register: '' }
#
# X64: registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
-# X32: %0:gr32_abcd = COPY $edi
+# X32: %0:gr32 = COPY $edi
+# X32-NEXT: %3:gr32_abcd = COPY %0
+# X32-NEXT: %1:gr8_abcd_l = COPY %3.sub_8bit
+
# X64: %0:gr32 = COPY $edi
-# ALL-NEXT: %1:gr8 = COPY %0.sub_8bit
+# X64-NEXT: %1:gr8 = COPY %0.sub_8bit
+
# ALL-NEXT: %2:gr32 = MOVZX32rr8 %1
# ALL-NEXT: $eax = COPY %2
# ALL-NEXT: RET 0, implicit $eax
; %b: 72 (0000 0000 0100 1000)
; X64-LABEL: test_shl_i4:
; X64: # %bb.0:
-; X64-NEXT: movl %edi, %eax
; X64-NEXT: # kill: def $esi killed $esi def $rsi
; X64-NEXT: # kill: def $edx killed $edx def $rdx
; X64-NEXT: leal (%rdx,%rsi), %ecx
; X64-NEXT: andb $15, %cl
; X64-NEXT: # kill: def $cl killed $cl killed $ecx
-; X64-NEXT: shlb %cl, %al
+; X64-NEXT: shlb %cl, %dil
+; X64-NEXT: movzbl %dil, %eax
; X64-NEXT: andw $15, %ax
; X64-NEXT: # kill: def $ax killed $ax killed $eax
; X64-NEXT: retq
; CHECK-LABEL: name: test_sdiv_i8
; CHECK: liveins: $edi, $esi
- ; CHECK: [[COPY:%[0-9]+]]:gr32_abcd = COPY $edi
- ; CHECK: [[COPY1:%[0-9]+]]:gr8 = COPY [[COPY]].sub_8bit
- ; CHECK: [[COPY2:%[0-9]+]]:gr32_abcd = COPY $esi
- ; CHECK: [[COPY3:%[0-9]+]]:gr8 = COPY [[COPY2]].sub_8bit
- ; CHECK: $ax = MOVSX16rr8 [[COPY1]]
- ; CHECK: IDIV8r [[COPY3]], implicit-def $al, implicit-def $ah, implicit-def $eflags, implicit $ax
- ; CHECK: [[COPY4:%[0-9]+]]:gr8 = COPY $al
- ; CHECK: $al = COPY [[COPY4]]
+ ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $edi
+ ; CHECK: [[COPY1:%[0-9]+]]:gr32_abcd = COPY [[COPY]]
+ ; CHECK: [[COPY2:%[0-9]+]]:gr8_abcd_l = COPY [[COPY1]].sub_8bit
+ ; CHECK: [[COPY3:%[0-9]+]]:gr32 = COPY $esi
+ ; CHECK: [[COPY4:%[0-9]+]]:gr32_abcd = COPY [[COPY3]]
+ ; CHECK: [[COPY5:%[0-9]+]]:gr8_abcd_l = COPY [[COPY4]].sub_8bit
+ ; CHECK: $ax = MOVSX16rr8 [[COPY2]]
+ ; CHECK: IDIV8r [[COPY5]], implicit-def $al, implicit-def $ah, implicit-def $eflags, implicit $ax
+ ; CHECK: [[COPY6:%[0-9]+]]:gr8 = COPY $al
+ ; CHECK: $al = COPY [[COPY6]]
; CHECK: RET 0, implicit $al
%2:gpr(s32) = COPY $edi
%0:gpr(s8) = G_TRUNC %2(s32)
; CHECK: liveins: $edi
; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $edi
; CHECK: [[COPY1:%[0-9]+]]:gr16 = COPY [[COPY]].sub_16bit
- ; CHECK: [[AND16ri:%[0-9]+]]:gr16 = AND16ri [[COPY1]], 255, implicit-def $eflags
- ; CHECK: $ax = COPY [[AND16ri]]
+ ; CHECK: [[COPY2:%[0-9]+]]:gr8 = COPY [[COPY1]].sub_8bit
+ ; CHECK: [[MOVZX32rr8_:%[0-9]+]]:gr32 = MOVZX32rr8 [[COPY2]]
+ ; CHECK: [[COPY3:%[0-9]+]]:gr16 = COPY [[MOVZX32rr8_]].sub_16bit
+ ; CHECK: $ax = COPY [[COPY3]]
; CHECK: RET 0, implicit $ax
%1:gpr(s32) = COPY $edi
%3:gpr(s16) = G_CONSTANT i16 255
def DOP : RegisterOperand<DRegs>;
def SOME_INSN : I<(outs DRegs:$dst), (ins DOP:$src), []>;
def SUBSOME_INSN : I<(outs SRegs:$dst), (ins SOP:$src), []>;
+def SUBSOME_INSN2 : I<(outs SRegs:$dst), (ins SOP:$src), []>;
// We should skip cases where we don't have a given register class for the
// subregister source.
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/1, /*RC DRegs*/1,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::SUBSOME_INSN,
+// Test an extract from an output instruction result (nonleaf)
+def : Pat<(i16 (trunc (bitreverse DOP:$src))),
+ (EXTRACT_SUBREG (SOME_INSN DOP:$src), sub0)>;
+// CHECK-LABEL: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_BITREVERSE,
+// CHECK-NEXT: GIM_CheckType, /*MI*/1, /*Op*/1, /*Type*/GILLT_s32,
+// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/1, /*Op*/1, /*RC*/Test::DRegsRegClassID,
+// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
+// CHECK-NEXT: // (trunc:{ *:[i16] } (bitreverse:{ *:[i32] } DOP:{ *:[i32] }:$src)) => (EXTRACT_SUBREG:{ *:[i16] } (SOME_INSN:{ *:[i32] } DOP:{ *:[i32] }:$src), sub0:{ *:[i32] })
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/MyTarget::SOME_INSN,
+// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/1, /*OpIdx*/1, // src
+// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/1,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::COPY,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_AddTempSubRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0, sub0,
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
+// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC SRegs*/0,
+// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/1, /*RC DRegs*/1,
+
+// EXTRACT_SUBREG is subinstruction, but also doesn't have a leaf input
+
+// CHECK-LABEL: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_CTPOP,
+// CHECK-NEXT: GIM_CheckType, /*MI*/1, /*Op*/1, /*Type*/GILLT_s32,
+// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/1, /*Op*/1, /*RC*/Test::DRegsRegClassID,
+// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
+// CHECK-NEXT: // (trunc:{ *:[i16] } (ctpop:{ *:[i32] } DOP:{ *:[i32] }:$src)) => (SUBSOME_INSN2:{ *:[i16] } (EXTRACT_SUBREG:{ *:[i16] } (SOME_INSN:{ *:[i32] } DOP:{ *:[i32] }:$src), sub0:{ *:[i32] }))
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16,
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/MyTarget::SOME_INSN,
+// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/RegState::Define,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/1, /*OpIdx*/1, // src
+// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::COPY,
+// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define,
+// CHECK-NEXT: GIR_AddTempSubRegister, /*InsnID*/1, /*TempRegID*/1, /*TempRegFlags*/0, sub0,
+// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, /*RC SRegs*/0,
+// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/1, /*RC DRegs*/1,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::SUBSOME_INSN2,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0,
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
+// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
+def : Pat<(i16 (trunc (ctpop DOP:$src))),
+ (SUBSOME_INSN2 (EXTRACT_SUBREG (SOME_INSN DOP:$src), sub0))>;
+
// Test an EXTRACT_SUBREG that is the final instruction.
def : Pat<(i16 (trunc DOP:$src)),
(EXTRACT_SUBREG DOP:$src, sub0)>;
protected:
unsigned InsnID;
unsigned TempRegID;
+ const CodeGenSubRegIndex *SubRegIdx;
bool IsDef;
public:
- TempRegRenderer(unsigned InsnID, unsigned TempRegID, bool IsDef = false)
+ TempRegRenderer(unsigned InsnID, unsigned TempRegID, bool IsDef = false,
+ const CodeGenSubRegIndex *SubReg = nullptr)
: OperandRenderer(OR_Register), InsnID(InsnID), TempRegID(TempRegID),
- IsDef(IsDef) {}
+ SubRegIdx(SubReg), IsDef(IsDef) {}
static bool classof(const OperandRenderer *R) {
return R->getKind() == OR_TempRegister;
}
void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
- Table << MatchTable::Opcode("GIR_AddTempRegister")
- << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
+ if (SubRegIdx) {
+ assert(!IsDef);
+ Table << MatchTable::Opcode("GIR_AddTempSubRegister");
+ } else
+ Table << MatchTable::Opcode("GIR_AddTempRegister");
+
+ Table << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
<< MatchTable::Comment("TempRegID") << MatchTable::IntValue(TempRegID)
<< MatchTable::Comment("TempRegFlags");
+
if (IsDef)
Table << MatchTable::NamedValue("RegState::Define");
else
Table << MatchTable::IntValue(0);
+
+ if (SubRegIdx)
+ Table << MatchTable::NamedValue(SubRegIdx->getQualifiedName());
Table << MatchTable::LineBreak;
}
};
//===- GlobalISelEmitter class --------------------------------------------===//
+static Expected<LLTCodeGen> getInstResultType(const TreePatternNode *Dst) {
+ ArrayRef<TypeSetByHwMode> ChildTypes = Dst->getExtTypes();
+ if (ChildTypes.size() != 1)
+ return failedImport("Dst pattern child has multiple results");
+
+ Optional<LLTCodeGen> MaybeOpTy;
+ if (ChildTypes.front().isMachineValueType()) {
+ MaybeOpTy =
+ MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy);
+ }
+
+ if (!MaybeOpTy)
+ return failedImport("Dst operand has an unsupported type");
+ return *MaybeOpTy;
+}
+
class GlobalISelEmitter {
public:
explicit GlobalISelEmitter(RecordKeeper &RK);
}
if (DstChild->getOperator()->isSubClassOf("Instruction")) {
- ArrayRef<TypeSetByHwMode> ChildTypes = DstChild->getExtTypes();
- if (ChildTypes.size() != 1)
- return failedImport("Dst pattern child has multiple results");
-
- Optional<LLTCodeGen> OpTyOrNone = None;
- if (ChildTypes.front().isMachineValueType())
- OpTyOrNone =
- MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy);
- if (!OpTyOrNone)
- return failedImport("Dst operand has an unsupported type");
+ auto OpTy = getInstResultType(DstChild);
+ if (!OpTy)
+ return OpTy.takeError();
unsigned TempRegID = Rule.allocateTempRegID();
InsertPt = Rule.insertAction<MakeTempRegisterAction>(
- InsertPt, OpTyOrNone.getValue(), TempRegID);
+ InsertPt, *OpTy, TempRegID);
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID);
auto InsertPtOrError = createAndImportSubInstructionRenderer(
// EXTRACT_SUBREG needs to use a subregister COPY.
if (Name == "EXTRACT_SUBREG") {
- if (!Dst->getChild(0)->isLeaf())
- return failedImport("EXTRACT_SUBREG child #1 is not a leaf");
-
- if (DefInit *SubRegInit =
- dyn_cast<DefInit>(Dst->getChild(1)->getLeafValue())) {
- Record *RCDef = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue());
- if (!RCDef)
- return failedImport("EXTRACT_SUBREG child #0 could not "
- "be coerced to a register class");
-
- CodeGenRegisterClass *RC = CGRegs.getRegClass(RCDef);
- CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef());
-
- const auto SrcRCDstRCPair =
- RC->getMatchingSubClassWithSubRegs(CGRegs, SubIdx);
- if (SrcRCDstRCPair.hasValue()) {
- assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass");
- if (SrcRCDstRCPair->first != RC)
- return failedImport("EXTRACT_SUBREG requires an additional COPY");
- }
+ DefInit *SubRegInit = dyn_cast<DefInit>(Dst->getChild(1)->getLeafValue());
+ if (!SubRegInit)
+ return failedImport("EXTRACT_SUBREG child #1 is not a subreg index");
+
+ CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef());
+ TreePatternNode *ValChild = Dst->getChild(0);
+ if (!ValChild->isLeaf()) {
+ // We really have to handle the source instruction, and then insert a
+ // copy from the subregister.
+ auto ExtractSrcTy = getInstResultType(ValChild);
+ if (!ExtractSrcTy)
+ return ExtractSrcTy.takeError();
- DstMIBuilder.addRenderer<CopySubRegRenderer>(Dst->getChild(0)->getName(),
- SubIdx);
+ unsigned TempRegID = M.allocateTempRegID();
+ InsertPt = M.insertAction<MakeTempRegisterAction>(
+ InsertPt, *ExtractSrcTy, TempRegID);
+
+ auto InsertPtOrError = createAndImportSubInstructionRenderer(
+ ++InsertPt, M, ValChild, TempRegID);
+ if (auto Error = InsertPtOrError.takeError())
+ return std::move(Error);
+
+ DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, false, SubIdx);
return InsertPt;
}
- return failedImport("EXTRACT_SUBREG child #1 is not a subreg index");
+ // If this is a source operand, this is just a subregister copy.
+ Record *RCDef = getInitValueAsRegClass(ValChild->getLeafValue());
+ if (!RCDef)
+ return failedImport("EXTRACT_SUBREG child #0 could not "
+ "be coerced to a register class");
+
+ CodeGenRegisterClass *RC = CGRegs.getRegClass(RCDef);
+
+ const auto SrcRCDstRCPair =
+ RC->getMatchingSubClassWithSubRegs(CGRegs, SubIdx);
+ if (SrcRCDstRCPair.hasValue()) {
+ assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass");
+ if (SrcRCDstRCPair->first != RC)
+ return failedImport("EXTRACT_SUBREG requires an additional COPY");
+ }
+
+ DstMIBuilder.addRenderer<CopySubRegRenderer>(Dst->getChild(0)->getName(),
+ SubIdx);
+ return InsertPt;
}
if (Name == "REG_SEQUENCE") {
if (DstIOpRec == nullptr)
return failedImport("REG_SEQUENCE operand #0 isn't a register class");
} else if (DstIName == "EXTRACT_SUBREG") {
- if (!Dst->getChild(0)->isLeaf())
- return failedImport("EXTRACT_SUBREG operand #0 isn't a leaf");
+ auto InferredClass = inferRegClassFromPattern(Dst->getChild(0));
+ if (!InferredClass)
+ return failedImport("Could not infer class for EXTRACT_SUBREG operand #0");
// We can assume that a subregister is in the same bank as it's super
// register.
- DstIOpRec = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue());
-
- if (DstIOpRec == nullptr)
- return failedImport("EXTRACT_SUBREG operand #0 isn't a register class");
+ DstIOpRec = (*InferredClass)->getDef();
} else if (DstIName == "INSERT_SUBREG") {
auto MaybeSuperClass = inferSuperRegisterClassForNode(
VTy, Dst->getChild(0), Dst->getChild(2));
const auto SrcRCDstRCPair =
(*SuperClass)->getMatchingSubClassWithSubRegs(CGRegs, *SubIdx);
+ if (!SrcRCDstRCPair) {
+ return failedImport("subreg index is incompatible "
+ "with inferred reg class");
+ }
+
assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass");
M.addAction<ConstrainOperandToRegClassAction>(0, 0, *SrcRCDstRCPair->second);
M.addAction<ConstrainOperandToRegClassAction>(0, 1, *SrcRCDstRCPair->first);