MSP430ISelDAGToDAG.cpp revision 36e3a6e235ee8b21eba777686b4508f71248b869
137171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov//===-- MSP430ISelDAGToDAG.cpp - A dag to dag inst selector for MSP430 ----===// 237171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// 337171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// The LLVM Compiler Infrastructure 437171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// 537171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// This file is distributed under the University of Illinois Open Source 637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// License. See LICENSE.TXT for details. 737171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// 837171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov//===----------------------------------------------------------------------===// 937171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// 1037171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// This file defines an instruction selector for the MSP430 target. 1137171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov// 1237171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov//===----------------------------------------------------------------------===// 1337171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 1437171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "MSP430.h" 1537171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "MSP430ISelLowering.h" 1637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "MSP430TargetMachine.h" 1737171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/DerivedTypes.h" 1837171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/Function.h" 1937171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/Intrinsics.h" 2037171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/CallingConv.h" 2137171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/Constants.h" 2237171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/CodeGen/MachineFrameInfo.h" 2337171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/CodeGen/MachineFunction.h" 2437171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/CodeGen/MachineInstrBuilder.h" 2537171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/CodeGen/MachineRegisterInfo.h" 2637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/CodeGen/SelectionDAG.h" 2737171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/CodeGen/SelectionDAGISel.h" 2837171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/Target/TargetLowering.h" 2937171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/Support/Compiler.h" 3037171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov#include "llvm/Support/Debug.h" 314d9756a9843862edb9daddfaa0d8c78ac1c52b32Edwin Török#include "llvm/Support/ErrorHandling.h" 324d9756a9843862edb9daddfaa0d8c78ac1c52b32Edwin Török#include "llvm/Support/raw_ostream.h" 3337171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikovusing namespace llvm; 3437171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 3537171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov/// MSP430DAGToDAGISel - MSP430 specific code to select MSP430 machine 3637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov/// instructions for SelectionDAG operations. 3737171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov/// 3837171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikovnamespace { 3937171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov class MSP430DAGToDAGISel : public SelectionDAGISel { 4037171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov MSP430TargetLowering &Lowering; 4137171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov const MSP430Subtarget &Subtarget; 4237171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 4337171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov public: 440d370dcf2d8b7092355935d7d52e7236dd745345Anton Korobeynikov MSP430DAGToDAGISel(MSP430TargetMachine &TM, CodeGenOpt::Level OptLevel) 450d370dcf2d8b7092355935d7d52e7236dd745345Anton Korobeynikov : SelectionDAGISel(TM, OptLevel), 4637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov Lowering(*TM.getTargetLowering()), 4737171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov Subtarget(*TM.getSubtargetImpl()) { } 4837171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 4937171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov virtual void InstructionSelect(); 5037171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 5137171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov virtual const char *getPassName() const { 5237171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov return "MSP430 DAG->DAG Pattern Instruction Selection"; 5337171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov } 5437171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 5537171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov // Include the pieces autogenerated from the target description. 5637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov #include "MSP430GenDAGISel.inc" 5737171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 5837171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov private: 5937171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov SDNode *Select(SDValue Op); 60a6e3669f02a049e1fc0e5732d5ffbbe4a19027fdAnton Korobeynikov bool SelectAddr(SDValue Op, SDValue Addr, SDValue &Base, SDValue &Disp); 6181e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov 6281e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov #ifndef NDEBUG 6381e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov unsigned Indent; 6481e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov #endif 6537171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov }; 6637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov} // end anonymous namespace 6737171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 6837171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov/// createMSP430ISelDag - This pass converts a legalized DAG into a 6937171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov/// MSP430-specific DAG, ready for instruction scheduling. 7037171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov/// 710d370dcf2d8b7092355935d7d52e7236dd745345Anton KorobeynikovFunctionPass *llvm::createMSP430ISelDag(MSP430TargetMachine &TM, 720d370dcf2d8b7092355935d7d52e7236dd745345Anton Korobeynikov CodeGenOpt::Level OptLevel) { 730d370dcf2d8b7092355935d7d52e7236dd745345Anton Korobeynikov return new MSP430DAGToDAGISel(TM, OptLevel); 7437171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov} 7537171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 760a4985b79ec879d0267b6e7d64fc551249c086c8Anton Korobeynikov// FIXME: This is pretty dummy routine and needs to be rewritten in the future. 774c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikovbool MSP430DAGToDAGISel::SelectAddr(SDValue Op, SDValue Addr, 78a6e3669f02a049e1fc0e5732d5ffbbe4a19027fdAnton Korobeynikov SDValue &Base, SDValue &Disp) { 799dbaeaa03759d55f35edaf98d6078d7076b99882Anton Korobeynikov // Try to match frame address first. 809dbaeaa03759d55f35edaf98d6078d7076b99882Anton Korobeynikov if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) { 8136e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i16); 8236e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson Disp = CurDAG->getTargetConstant(0, MVT::i16); 839dbaeaa03759d55f35edaf98d6078d7076b99882Anton Korobeynikov return true; 849dbaeaa03759d55f35edaf98d6078d7076b99882Anton Korobeynikov } 854c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov 869841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov switch (Addr.getOpcode()) { 879841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov case ISD::ADD: 8876f578d8a41309e9a74e1c87476b9a19693cbb93Anton Korobeynikov // Operand is a result from ADD with constant operand which fits into i16. 8976f578d8a41309e9a74e1c87476b9a19693cbb93Anton Korobeynikov if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1))) { 904c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov uint64_t CVal = CN->getZExtValue(); 914c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov // Offset should fit into 16 bits. 924c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov if (((CVal << 48) >> 48) == CVal) { 939dbaeaa03759d55f35edaf98d6078d7076b99882Anton Korobeynikov SDValue N0 = Addr.getOperand(0); 949dbaeaa03759d55f35edaf98d6078d7076b99882Anton Korobeynikov if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(N0)) 9536e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i16); 969dbaeaa03759d55f35edaf98d6078d7076b99882Anton Korobeynikov else 979dbaeaa03759d55f35edaf98d6078d7076b99882Anton Korobeynikov Base = N0; 984c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov 9936e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson Disp = CurDAG->getTargetConstant(CVal, MVT::i16); 1004c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov return true; 1014c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov } 1024c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov } 1039841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov break; 1049841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov case MSP430ISD::Wrapper: 1059841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov SDValue N0 = Addr.getOperand(0); 1069841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(N0)) { 107a6e3669f02a049e1fc0e5732d5ffbbe4a19027fdAnton Korobeynikov Base = CurDAG->getTargetGlobalAddress(G->getGlobal(), 10836e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson MVT::i16, G->getOffset()); 10936e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson Disp = CurDAG->getTargetConstant(0, MVT::i16); 1100a4985b79ec879d0267b6e7d64fc551249c086c8Anton Korobeynikov return true; 111165bbe3b7517003c363e54f3187f5dcc1218e2ddAnton Korobeynikov } else if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(N0)) { 11236e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson Base = CurDAG->getTargetExternalSymbol(E->getSymbol(), MVT::i16); 11336e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson Disp = CurDAG->getTargetConstant(0, MVT::i16); 1149841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov } 1159841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov break; 1169841d21a31cbc34a0915671ac89116215568ace7Anton Korobeynikov }; 1174c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov 118a6e3669f02a049e1fc0e5732d5ffbbe4a19027fdAnton Korobeynikov Base = Addr; 11936e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson Disp = CurDAG->getTargetConstant(0, MVT::i16); 1204c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov 1214c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov return true; 1224c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov} 1234c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov 1244c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov 1254c88f11c8739ff1395bc5b07b223eb84894d50e9Anton Korobeynikov 12637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov/// InstructionSelect - This callback is invoked by 12737171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov/// SelectionDAGISel when it has created a SelectionDAG for us to codegen. 12810a5a3c82145edc6fe8e9b030b0ccb27b625adb1Anton Korobeynikovvoid MSP430DAGToDAGISel::InstructionSelect() { 12937171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov DEBUG(BB->dump()); 13037171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 1317a872e95a74b53a5360b559e4096dcb6753263daAnton Korobeynikov // Codegen the basic block. 1327a872e95a74b53a5360b559e4096dcb6753263daAnton Korobeynikov#ifndef NDEBUG 1337a872e95a74b53a5360b559e4096dcb6753263daAnton Korobeynikov DOUT << "===== Instruction selection begins:\n"; 1347a872e95a74b53a5360b559e4096dcb6753263daAnton Korobeynikov Indent = 0; 1357a872e95a74b53a5360b559e4096dcb6753263daAnton Korobeynikov#endif 13637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov SelectRoot(*CurDAG); 1377a872e95a74b53a5360b559e4096dcb6753263daAnton Korobeynikov#ifndef NDEBUG 1387a872e95a74b53a5360b559e4096dcb6753263daAnton Korobeynikov DOUT << "===== Instruction selection ends:\n"; 1397a872e95a74b53a5360b559e4096dcb6753263daAnton Korobeynikov#endif 14037171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 14137171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov CurDAG->RemoveDeadNodes(); 14237171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov} 14337171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov 14437171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton KorobeynikovSDNode *MSP430DAGToDAGISel::Select(SDValue Op) { 14581e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov SDNode *Node = Op.getNode(); 1463c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov DebugLoc dl = Op.getDebugLoc(); 14781e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov 14881e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov // Dump information about the Node being selected 14981e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov #ifndef NDEBUG 15081e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DOUT << std::string(Indent, ' ') << "Selecting: "; 15181e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DEBUG(Node->dump(CurDAG)); 15281e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DOUT << "\n"; 15381e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov Indent += 2; 15481e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov #endif 15581e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov 15681e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov // If we have a custom node, we already have selected! 15781e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov if (Node->isMachineOpcode()) { 15881e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov #ifndef NDEBUG 15981e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DOUT << std::string(Indent-2, ' ') << "== "; 16081e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DEBUG(Node->dump(CurDAG)); 16181e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DOUT << "\n"; 16281e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov Indent -= 2; 16381e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov #endif 16481e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov return NULL; 16581e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov } 16681e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov 1673c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov // Few custom selection stuff. 1683c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov switch (Node->getOpcode()) { 1693c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov default: break; 1703c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov case ISD::FrameIndex: { 17136e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson assert(Op.getValueType() == MVT::i16); 1723c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov int FI = cast<FrameIndexSDNode>(Node)->getIndex(); 17336e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i16); 1743c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov if (Node->hasOneUse()) 17536e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson return CurDAG->SelectNodeTo(Node, MSP430::ADD16ri, MVT::i16, 17636e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson TFI, CurDAG->getTargetConstant(0, MVT::i16)); 17736e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson return CurDAG->getTargetNode(MSP430::ADD16ri, dl, MVT::i16, 17836e3a6e235ee8b21eba777686b4508f71248b869Owen Anderson TFI, CurDAG->getTargetConstant(0, MVT::i16)); 1793c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov } 1803c10ef5a963c736849a7bb453238ede34be9eb77Anton Korobeynikov } 18181e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov 18281e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov // Select the default instruction 18381e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov SDNode *ResNode = SelectCode(Op); 18481e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov 18581e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov #ifndef NDEBUG 18681e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DOUT << std::string(Indent-2, ' ') << "=> "; 18781e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov if (ResNode == NULL || ResNode == Op.getNode()) 18881e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DEBUG(Op.getNode()->dump(CurDAG)); 18981e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov else 19081e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DEBUG(ResNode->dump(CurDAG)); 19181e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov DOUT << "\n"; 19281e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov Indent -= 2; 19381e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov #endif 19481e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov 19581e1f97bd3da47ff1b0cb85336a104a7a654f310Anton Korobeynikov return ResNode; 19637171571716b9cb7c5aeb5b45d95b1fbd0716d03Anton Korobeynikov} 197