131d157ae1ac2cd9c787dc3c1d28e64c682803844Jia Liu//===-- XCoreFrameLowering.cpp - Frame info for XCore Target --------------===// 2b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// 3b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// The LLVM Compiler Infrastructure 4b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// 5b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// This file is distributed under the University of Illinois Open Source 6b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// License. See LICENSE.TXT for details. 7b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// 8b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne//===----------------------------------------------------------------------===// 9b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// 10b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// This file contains XCore frame information that doesn't fit anywhere else 11b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// cleanly... 12b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne// 13b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne//===----------------------------------------------------------------------===// 14b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne 1516c29b5f285f375be53dabaa73e3e91107485fe4Anton Korobeynikov#include "XCoreFrameLowering.h" 1679aa3417eb6f58d668aadfedf075240a41d35a26Craig Topper#include "XCore.h" 1733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov#include "XCoreInstrInfo.h" 1833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov#include "XCoreMachineFunctionInfo.h" 1933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov#include "llvm/CodeGen/MachineFrameInfo.h" 2033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov#include "llvm/CodeGen/MachineFunction.h" 2133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov#include "llvm/CodeGen/MachineInstrBuilder.h" 2233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov#include "llvm/CodeGen/MachineModuleInfo.h" 2333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov#include "llvm/CodeGen/MachineRegisterInfo.h" 2494c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov#include "llvm/CodeGen/RegisterScavenging.h" 250b8c9a80f20772c3793201ab5b251d3520b9cea3Chandler Carruth#include "llvm/IR/DataLayout.h" 260b8c9a80f20772c3793201ab5b251d3520b9cea3Chandler Carruth#include "llvm/IR/Function.h" 2733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov#include "llvm/Support/ErrorHandling.h" 28d04a8d4b33ff316ca4cf961e06c9e312eff8e64fChandler Carruth#include "llvm/Target/TargetOptions.h" 2933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 30b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborneusing namespace llvm; 31b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne 3233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov// helper functions. FIXME: Eliminate. 3333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikovstatic inline bool isImmUs(unsigned val) { 3433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov return val <= 11; 3533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov} 3633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 3733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikovstatic inline bool isImmU6(unsigned val) { 3833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov return val < (1 << 6); 3933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov} 4033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 4133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikovstatic inline bool isImmU16(unsigned val) { 4233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov return val < (1 << 16); 4333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov} 4433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 4533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikovstatic void loadFromStack(MachineBasicBlock &MBB, 4633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MachineBasicBlock::iterator I, 4733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov unsigned DstReg, int Offset, DebugLoc dl, 4833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov const TargetInstrInfo &TII) { 4933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov assert(Offset%4 == 0 && "Misaligned stack offset"); 5033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov Offset/=4; 5133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov bool isU6 = isImmU6(Offset); 5233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (!isU6 && !isImmU16(Offset)) 5333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov report_fatal_error("loadFromStack offset too big " + Twine(Offset)); 5433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov int Opcode = isU6 ? XCore::LDWSP_ru6 : XCore::LDWSP_lru6; 5533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, I, dl, TII.get(Opcode), DstReg) 5633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov .addImm(Offset); 5733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov} 5833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 5933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 6033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikovstatic void storeToStack(MachineBasicBlock &MBB, 6133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MachineBasicBlock::iterator I, 6233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov unsigned SrcReg, int Offset, DebugLoc dl, 6333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov const TargetInstrInfo &TII) { 6433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov assert(Offset%4 == 0 && "Misaligned stack offset"); 6533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov Offset/=4; 6633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov bool isU6 = isImmU6(Offset); 6733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (!isU6 && !isImmU16(Offset)) 6833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov report_fatal_error("storeToStack offset too big " + Twine(Offset)); 6933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov int Opcode = isU6 ? XCore::STWSP_ru6 : XCore::STWSP_lru6; 7033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, I, dl, TII.get(Opcode)) 7133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov .addReg(SrcReg) 7233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov .addImm(Offset); 7333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov} 7433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 7533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 76b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne//===----------------------------------------------------------------------===// 7716c29b5f285f375be53dabaa73e3e91107485fe4Anton Korobeynikov// XCoreFrameLowering: 78b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne//===----------------------------------------------------------------------===// 79b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne 8016c29b5f285f375be53dabaa73e3e91107485fe4Anton KorobeynikovXCoreFrameLowering::XCoreFrameLowering(const XCoreSubtarget &sti) 8195a9d937728ca9cf2bf44f86ff1184df318b3bd7Benjamin Kramer : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, 4, 0) { 82b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne // Do nothing 83b25baef26f03b9909b65dd5f762b38f93000445dRichard Osborne} 8433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 8516c29b5f285f375be53dabaa73e3e91107485fe4Anton Korobeynikovbool XCoreFrameLowering::hasFP(const MachineFunction &MF) const { 868a8d479214745c82ef00f08d4e4f1c173b5f9ce2Nick Lewycky return MF.getTarget().Options.DisableFramePointerElim(MF) || 878a8d479214745c82ef00f08d4e4f1c173b5f9ce2Nick Lewycky MF.getFrameInfo()->hasVarSizedObjects(); 88d0c38176690e9602a93a20a43f1bd084564a8116Anton Korobeynikov} 89d0c38176690e9602a93a20a43f1bd084564a8116Anton Korobeynikov 9016c29b5f285f375be53dabaa73e3e91107485fe4Anton Korobeynikovvoid XCoreFrameLowering::emitPrologue(MachineFunction &MF) const { 9133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MachineBasicBlock &MBB = MF.front(); // Prolog goes in entry BB 9233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MachineBasicBlock::iterator MBBI = MBB.begin(); 9333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MachineFrameInfo *MFI = MF.getFrameInfo(); 9433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MachineModuleInfo *MMI = &MF.getMMI(); 9533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov const XCoreInstrInfo &TII = 9633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov *static_cast<const XCoreInstrInfo*>(MF.getTarget().getInstrInfo()); 9733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov XCoreFunctionInfo *XFI = MF.getInfo<XCoreFunctionInfo>(); 9833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov DebugLoc dl = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); 9933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 100d0c38176690e9602a93a20a43f1bd084564a8116Anton Korobeynikov bool FP = hasFP(MF); 10199faa3b4ec6d03ac7808fe4ff3fbf3d04e375502Bill Wendling const AttributeSet &PAL = MF.getFunction()->getAttributes(); 102ff0c5014b2127b16815121d9e723dc85bd164a79Richard Osborne 1039d30e7208e6b2bc3fa48305e3ae371188f643425Bill Wendling if (PAL.hasAttrSomewhere(Attribute::Nest)) 1049d30e7208e6b2bc3fa48305e3ae371188f643425Bill Wendling loadFromStack(MBB, MBBI, XCore::R11, 0, dl, TII); 10533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 10633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Work out frame sizes. 10733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov int FrameSize = MFI->getStackSize(); 10833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov assert(FrameSize%4 == 0 && "Misaligned frame size"); 10933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov FrameSize/=4; 11033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 11133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov bool isU6 = isImmU6(FrameSize); 11233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 11333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (!isU6 && !isImmU16(FrameSize)) { 11433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // FIXME could emit multiple instructions. 11533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov report_fatal_error("emitPrologue Frame size too big: " + Twine(FrameSize)); 11633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 11719411101a1c6e252af9a425793221ecec722c35cRichard Osborne bool emitFrameMoves = XCoreRegisterInfo::needsFrameMoves(MF); 11833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 1199bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne bool saveLR = XFI->getUsesLR(); 12033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Do we need to allocate space on the stack? 12133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (FrameSize) { 12233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov int Opcode; 12333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (saveLR && (MFI->getObjectOffset(XFI->getLRSpillSlot()) == 0)) { 12433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov Opcode = (isU6) ? XCore::ENTSP_u6 : XCore::ENTSP_lu6; 12533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MBB.addLiveIn(XCore::LR); 12633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov saveLR = false; 12733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } else { 12833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov Opcode = (isU6) ? XCore::EXTSP_u6 : XCore::EXTSP_lu6; 12933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 13033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, MBBI, dl, TII.get(Opcode)).addImm(FrameSize); 13133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 13233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (emitFrameMoves) { 13333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 13433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Show update of SP. 13533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MCSymbol *FrameLabel = MMI->getContext().CreateTempSymbol(); 13633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, MBBI, dl, TII.get(XCore::PROLOG_LABEL)).addSym(FrameLabel); 13733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 1389bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne } 1399bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne if (saveLR) { 1409bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne int LRSpillOffset = MFI->getObjectOffset(XFI->getLRSpillSlot()); 1419bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne storeToStack(MBB, MBBI, XCore::LR, LRSpillOffset + FrameSize*4, dl, TII); 1429bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne MBB.addLiveIn(XCore::LR); 14333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 1449bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne if (emitFrameMoves) { 1459bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne MCSymbol *SaveLRLabel = MMI->getContext().CreateTempSymbol(); 1469bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne BuildMI(MBB, MBBI, dl, TII.get(XCore::PROLOG_LABEL)).addSym(SaveLRLabel); 14733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 14833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 14933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 15033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (FP) { 15133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Save R10 to the stack. 15233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov int FPSpillOffset = MFI->getObjectOffset(XFI->getFPSpillSlot()); 15333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov storeToStack(MBB, MBBI, XCore::R10, FPSpillOffset + FrameSize*4, dl, TII); 15433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // R10 is live-in. It is killed at the spill. 15533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MBB.addLiveIn(XCore::R10); 15633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (emitFrameMoves) { 15733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MCSymbol *SaveR10Label = MMI->getContext().CreateTempSymbol(); 15833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, MBBI, dl, TII.get(XCore::PROLOG_LABEL)).addSym(SaveR10Label); 15933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 16033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Set the FP from the SP. 16133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov unsigned FramePtr = XCore::R10; 16233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, MBBI, dl, TII.get(XCore::LDAWSP_ru6), FramePtr) 16333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov .addImm(0); 16433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (emitFrameMoves) { 16533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Show FP is now valid. 16633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MCSymbol *FrameLabel = MMI->getContext().CreateTempSymbol(); 16733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, MBBI, dl, TII.get(XCore::PROLOG_LABEL)).addSym(FrameLabel); 16833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 16933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 17033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov} 17133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 17216c29b5f285f375be53dabaa73e3e91107485fe4Anton Korobeynikovvoid XCoreFrameLowering::emitEpilogue(MachineFunction &MF, 17333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MachineBasicBlock &MBB) const { 17433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MachineFrameInfo *MFI = MF.getFrameInfo(); 175f7ca976e74eafeeab0e9097f0fb07d6bb447415bJakob Stoklund Olesen MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); 17633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov const XCoreInstrInfo &TII = 17733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov *static_cast<const XCoreInstrInfo*>(MF.getTarget().getInstrInfo()); 1789bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne XCoreFunctionInfo *XFI = MF.getInfo<XCoreFunctionInfo>(); 17933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov DebugLoc dl = MBBI->getDebugLoc(); 18033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 181d0c38176690e9602a93a20a43f1bd084564a8116Anton Korobeynikov bool FP = hasFP(MF); 18233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (FP) { 18333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Restore the stack pointer. 18433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov unsigned FramePtr = XCore::R10; 18533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, MBBI, dl, TII.get(XCore::SETSP_1r)) 18633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov .addReg(FramePtr); 18733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 18833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 18933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Work out frame sizes. 19033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov int FrameSize = MFI->getStackSize(); 19133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 19233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov assert(FrameSize%4 == 0 && "Misaligned frame size"); 19333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 19433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov FrameSize/=4; 19533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 19633464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov bool isU6 = isImmU6(FrameSize); 19733464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 19833464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (!isU6 && !isImmU16(FrameSize)) { 19933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // FIXME could emit multiple instructions. 20033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov report_fatal_error("emitEpilogue Frame size too big: " + Twine(FrameSize)); 20133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 20233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 2039bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne if (FP) { 2049bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne // Restore R10 2059bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne int FPSpillOffset = MFI->getObjectOffset(XFI->getFPSpillSlot()); 2069bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne FPSpillOffset += FrameSize*4; 2079bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne loadFromStack(MBB, MBBI, XCore::R10, FPSpillOffset, dl, TII); 2089bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne } 20933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov 2109bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne bool restoreLR = XFI->getUsesLR(); 2119bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne if (restoreLR && 2129bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne (FrameSize == 0 || MFI->getObjectOffset(XFI->getLRSpillSlot()) != 0)) { 2139bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne int LRSpillOffset = MFI->getObjectOffset(XFI->getLRSpillSlot()); 2149bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne LRSpillOffset += FrameSize*4; 2159bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne loadFromStack(MBB, MBBI, XCore::LR, LRSpillOffset, dl, TII); 2169bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne restoreLR = false; 2179bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne } 2189bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne 2199bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne if (FrameSize) { 22033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov if (restoreLR) { 22133464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov // Fold prologue into return instruction 2229bd913c4c1f350562b5a31e79a82dcaf143b06e0Richard Osborne assert(MFI->getObjectOffset(XFI->getLRSpillSlot()) == 0); 22333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov assert(MBBI->getOpcode() == XCore::RETSP_u6 22433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov || MBBI->getOpcode() == XCore::RETSP_lu6); 22533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov int Opcode = (isU6) ? XCore::RETSP_u6 : XCore::RETSP_lu6; 226dcc4207a00b31687018f87de75846579bbdb9c77Richard Osborne MachineInstrBuilder MIB = BuildMI(MBB, MBBI, dl, TII.get(Opcode)).addImm(FrameSize); 227dcc4207a00b31687018f87de75846579bbdb9c77Richard Osborne for (unsigned i = 3, e = MBBI->getNumOperands(); i < e; ++i) 228dcc4207a00b31687018f87de75846579bbdb9c77Richard Osborne MIB->addOperand(MBBI->getOperand(i)); // copy any variadic operands 22933464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov MBB.erase(MBBI); 23033464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } else { 231c6ff29713d69b4a41c225cbde9c82e4a350dbfacRichard Osborne int Opcode = (isU6) ? XCore::LDAWSP_ru6 : XCore::LDAWSP_lru6; 23233464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov BuildMI(MBB, MBBI, dl, TII.get(Opcode), XCore::SP).addImm(FrameSize); 23333464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 23433464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov } 23533464912237efaa0ed7060829e66b59055bdd48bAnton Korobeynikov} 236d9e3385ced2dc887e2fe8e1c071bd2611e4d3edeAnton Korobeynikov 23716c29b5f285f375be53dabaa73e3e91107485fe4Anton Korobeynikovbool XCoreFrameLowering::spillCalleeSavedRegisters(MachineBasicBlock &MBB, 238cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MachineBasicBlock::iterator MI, 239cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov const std::vector<CalleeSavedInfo> &CSI, 240cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov const TargetRegisterInfo *TRI) const { 241cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov if (CSI.empty()) 242cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov return true; 243cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov 244cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MachineFunction *MF = MBB.getParent(); 245cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov const TargetInstrInfo &TII = *MF->getTarget().getInstrInfo(); 246cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov 247cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov XCoreFunctionInfo *XFI = MF->getInfo<XCoreFunctionInfo>(); 248cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov bool emitFrameMoves = XCoreRegisterInfo::needsFrameMoves(*MF); 249cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov 250cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov DebugLoc DL; 251cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov if (MI != MBB.end()) DL = MI->getDebugLoc(); 252cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov 253cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov for (std::vector<CalleeSavedInfo>::const_iterator it = CSI.begin(); 254cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov it != CSI.end(); ++it) { 255cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov // Add the callee-saved register as live-in. It's killed at the spill. 256cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MBB.addLiveIn(it->getReg()); 257cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov 258cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov unsigned Reg = it->getReg(); 259cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); 260cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov TII.storeRegToStackSlot(MBB, MI, Reg, true, 261cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov it->getFrameIdx(), RC, TRI); 262cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov if (emitFrameMoves) { 263cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MCSymbol *SaveLabel = MF->getContext().CreateTempSymbol(); 264cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov BuildMI(MBB, MI, DL, TII.get(XCore::PROLOG_LABEL)).addSym(SaveLabel); 265cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov XFI->getSpillLabels().push_back(std::make_pair(SaveLabel, *it)); 266cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov } 267cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov } 268cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov return true; 269cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov} 270cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov 27116c29b5f285f375be53dabaa73e3e91107485fe4Anton Korobeynikovbool XCoreFrameLowering::restoreCalleeSavedRegisters(MachineBasicBlock &MBB, 272cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MachineBasicBlock::iterator MI, 273cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov const std::vector<CalleeSavedInfo> &CSI, 274cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov const TargetRegisterInfo *TRI) const{ 275cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MachineFunction *MF = MBB.getParent(); 276cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov const TargetInstrInfo &TII = *MF->getTarget().getInstrInfo(); 277cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov 278cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov bool AtStart = MI == MBB.begin(); 279cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MachineBasicBlock::iterator BeforeI = MI; 280cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov if (!AtStart) 281cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov --BeforeI; 282cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov for (std::vector<CalleeSavedInfo>::const_iterator it = CSI.begin(); 283cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov it != CSI.end(); ++it) { 284cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov unsigned Reg = it->getReg(); 285cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); 286cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov TII.loadRegFromStackSlot(MBB, MI, it->getReg(), it->getFrameIdx(), 287cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov RC, TRI); 288cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov assert(MI != MBB.begin() && 289cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov "loadRegFromStackSlot didn't insert any code!"); 290cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov // Insert in reverse order. loadRegFromStackSlot can insert multiple 291cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov // instructions. 292cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov if (AtStart) 293cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MI = MBB.begin(); 294cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov else { 295cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov MI = BeforeI; 296cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov ++MI; 297cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov } 298cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov } 299cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov return true; 300cd775ceff0b25a0b026f643a7990c2924bd310a3Anton Korobeynikov} 30194c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov 302700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky// This function eliminates ADJCALLSTACKDOWN, 303700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky// ADJCALLSTACKUP pseudo instructions 304700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Benderskyvoid XCoreFrameLowering:: 305700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli BenderskyeliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, 306700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky MachineBasicBlock::iterator I) const { 307700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky const XCoreInstrInfo &TII = 308700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky *static_cast<const XCoreInstrInfo*>(MF.getTarget().getInstrInfo()); 309700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky if (!hasReservedCallFrame(MF)) { 310700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky // Turn the adjcallstackdown instruction into 'extsp <amt>' and the 311700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky // adjcallstackup instruction into 'ldaw sp, sp[<amt>]' 312700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky MachineInstr *Old = I; 313700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky uint64_t Amount = Old->getOperand(0).getImm(); 314700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky if (Amount != 0) { 315700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky // We need to keep the stack aligned properly. To do this, we round the 316700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky // amount of space needed for the outgoing arguments up to the next 317700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky // alignment boundary. 318700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky unsigned Align = getStackAlignment(); 319700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky Amount = (Amount+Align-1)/Align*Align; 320700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky 321700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky assert(Amount%4 == 0); 322700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky Amount /= 4; 323700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky 324700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky bool isU6 = isImmU6(Amount); 325700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky if (!isU6 && !isImmU16(Amount)) { 326700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky // FIX could emit multiple instructions in this case. 327700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky#ifndef NDEBUG 328700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky errs() << "eliminateCallFramePseudoInstr size too big: " 329700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky << Amount << "\n"; 330700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky#endif 331700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky llvm_unreachable(0); 332700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky } 333700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky 334700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky MachineInstr *New; 335700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky if (Old->getOpcode() == XCore::ADJCALLSTACKDOWN) { 336700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky int Opcode = isU6 ? XCore::EXTSP_u6 : XCore::EXTSP_lu6; 337700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky New=BuildMI(MF, Old->getDebugLoc(), TII.get(Opcode)) 338700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky .addImm(Amount); 339700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky } else { 340700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky assert(Old->getOpcode() == XCore::ADJCALLSTACKUP); 341c6ff29713d69b4a41c225cbde9c82e4a350dbfacRichard Osborne int Opcode = isU6 ? XCore::LDAWSP_ru6 : XCore::LDAWSP_lru6; 342700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky New=BuildMI(MF, Old->getDebugLoc(), TII.get(Opcode), XCore::SP) 343700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky .addImm(Amount); 344700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky } 345700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky 346700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky // Replace the pseudo instruction with a new instruction... 347700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky MBB.insert(I, New); 348700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky } 349700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky } 350700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky 351700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky MBB.erase(I); 352700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky} 353700ed80d3da5e98e05ceb90e9bfb66058581a6dbEli Bendersky 35494c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikovvoid 35516c29b5f285f375be53dabaa73e3e91107485fe4Anton KorobeynikovXCoreFrameLowering::processFunctionBeforeCalleeSavedScan(MachineFunction &MF, 35694c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov RegScavenger *RS) const { 35794c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov MachineFrameInfo *MFI = MF.getFrameInfo(); 35894c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov const TargetRegisterInfo *RegInfo = MF.getTarget().getRegisterInfo(); 35994c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov bool LRUsed = MF.getRegInfo().isPhysRegUsed(XCore::LR); 360420761a0f193e87d08ee1c51b26bba23ab4bac7fCraig Topper const TargetRegisterClass *RC = &XCore::GRRegsRegClass; 36194c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov XCoreFunctionInfo *XFI = MF.getInfo<XCoreFunctionInfo>(); 36294c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov if (LRUsed) { 36394c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov MF.getRegInfo().setPhysRegUnused(XCore::LR); 36494c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov 36594c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov bool isVarArg = MF.getFunction()->isVarArg(); 36694c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov int FrameIdx; 36794c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov if (! isVarArg) { 36894c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov // A fixed offset of 0 allows us to save / restore LR using entsp / retsp. 36994c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov FrameIdx = MFI->CreateFixedObject(RC->getSize(), 0, true); 37094c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov } else { 37194c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov FrameIdx = MFI->CreateStackObject(RC->getSize(), RC->getAlignment(), 37294c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov false); 37394c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov } 37494c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov XFI->setUsesLR(FrameIdx); 37594c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov XFI->setLRSpillSlot(FrameIdx); 37694c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov } 37794c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov if (RegInfo->requiresRegisterScavenging(MF)) { 37894c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov // Reserve a slot close to SP or frame pointer. 379dc3beb90178fc316f63790812b22201884eaa017Hal Finkel RS->addScavengingFrameIndex(MFI->CreateStackObject(RC->getSize(), 38094c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov RC->getAlignment(), 38194c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov false)); 38294c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov } 38394c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov if (hasFP(MF)) { 38494c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov // A callee save register is used to hold the FP. 38594c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov // This needs saving / restoring in the epilogue / prologue. 38694c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov XFI->setFPSpillSlot(MFI->CreateStackObject(RC->getSize(), 38794c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov RC->getAlignment(), 38894c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov false)); 38994c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov } 39094c5ae08750f314bc3cf1bf882b686244a3927d9Anton Korobeynikov} 391