MipsRegisterInfo.cpp revision e7338cd550a4ccde6796d2987b482ea9f0e239ef
1ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch//===-- MipsRegisterInfo.cpp - MIPS Register Information -== --------------===//
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//                     The LLVM Compiler Infrastructure
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file is distributed under the University of Illinois Open Source
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// License. See LICENSE.TXT for details.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//===----------------------------------------------------------------------===//
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file contains the MIPS implementation of the TargetRegisterInfo class.
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//===----------------------------------------------------------------------===//
13ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define DEBUG_TYPE "mips-reg-info"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "MipsRegisterInfo.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "Mips.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "MipsAnalyzeImmediate.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "MipsInstrInfo.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "MipsSubtarget.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "MipsMachineFunction.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "llvm/Constants.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "llvm/DebugInfo.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "llvm/Type.h"
25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "llvm/Function.h"
26eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "llvm/CodeGen/ValueTypes.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "llvm/CodeGen/MachineInstrBuilder.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "llvm/CodeGen/MachineFunction.h"
2968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "llvm/CodeGen/MachineFrameInfo.h"
3068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "llvm/Target/TargetFrameLowering.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "llvm/Target/TargetMachine.h"
328bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "llvm/Target/TargetOptions.h"
338bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "llvm/Target/TargetInstrInfo.h"
348bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "llvm/Support/CommandLine.h"
358bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "llvm/Support/Debug.h"
3668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "llvm/Support/ErrorHandling.h"
378bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "llvm/Support/raw_ostream.h"
3858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "llvm/ADT/BitVector.h"
3958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "llvm/ADT/STLExtras.h"
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#define GET_REGINFO_TARGET_DESC
428bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "MipsGenRegisterInfo.inc"
4358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
4458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using namespace llvm;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MipsRegisterInfo::MipsRegisterInfo(const MipsSubtarget &ST,
4768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                                   const TargetInstrInfo &tii)
4858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  : MipsGenRegisterInfo(Mips::RA), Subtarget(ST), TII(tii) {}
4958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
5058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)unsigned MipsRegisterInfo::getPICCallReg() { return Mips::T9; }
5158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)//===----------------------------------------------------------------------===//
538bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)// Callee Saved Registers methods
5458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)//===----------------------------------------------------------------------===//
5558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
5658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)/// Mips Callee Saved Registers
5758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)const uint16_t* MipsRegisterInfo::
5868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)getCalleeSavedRegs(const MachineFunction *MF) const {
5958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (Subtarget.isSingleFloat())
6058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return CSR_SingleFloatOnly_SaveList;
6158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  else if (!Subtarget.hasMips64())
6258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return CSR_O32_SaveList;
638bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  else if (Subtarget.isABI_N32())
648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return CSR_N32_SaveList;
6558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
6658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  assert(Subtarget.isABI_N64());
6758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return CSR_N64_SaveList;
6858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
6968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
7058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)const uint32_t*
7158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)MipsRegisterInfo::getCallPreservedMask(CallingConv::ID) const {
7258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (Subtarget.isSingleFloat())
7358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return CSR_SingleFloatOnly_RegMask;
748bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  else if (!Subtarget.hasMips64())
758bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    return CSR_O32_RegMask;
7658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  else if (Subtarget.isABI_N32())
7758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return CSR_N32_RegMask;
7858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
7958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  assert(Subtarget.isABI_N64());
8068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return CSR_N64_RegMask;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BitVector MipsRegisterInfo::
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)getReservedRegs(const MachineFunction &MF) const {
8558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  static const uint16_t ReservedCPURegs[] = {
8658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    Mips::ZERO, Mips::AT, Mips::K0, Mips::K1, Mips::SP
8768043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  };
8858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
8958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  static const uint16_t ReservedCPU64Regs[] = {
9058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    Mips::ZERO_64, Mips::AT_64, Mips::K0_64, Mips::K1_64, Mips::SP_64
9158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  };
9258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
9358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  BitVector Reserved(getNumRegs());
94a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  typedef TargetRegisterClass::const_iterator RegIter;
9568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (unsigned I = 0; I < array_lengthof(ReservedCPURegs); ++I)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Reserved.set(ReservedCPURegs[I]);
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (Subtarget.hasMips64()) {
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (unsigned I = 0; I < array_lengthof(ReservedCPU64Regs); ++I)
10168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      Reserved.set(ReservedCPU64Regs[I]);
10258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
10368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // Reserve all registers in AFGR64.
10458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    for (RegIter Reg = Mips::AFGR64RegClass.begin(),
10558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)         EReg = Mips::AFGR64RegClass.end(); Reg != EReg; ++Reg)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Reserved.set(*Reg);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
10868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    // Reserve all registers in CPU64Regs & FGR64.
10958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    for (RegIter Reg = Mips::CPU64RegsRegClass.begin(),
11058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)         EReg = Mips::CPU64RegsRegClass.end(); Reg != EReg; ++Reg)
11158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      Reserved.set(*Reg);
11258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
11358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    for (RegIter Reg = Mips::FGR64RegClass.begin(),
11458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)         EReg = Mips::FGR64RegClass.end(); Reg != EReg; ++Reg)
11568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      Reserved.set(*Reg);
11658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
11758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
11858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Reserve FP if this function should have a dedicated frame pointer register.
11958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (MF.getTarget().getFrameLowering()->hasFP(MF)) {
1208bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    Reserved.set(Mips::FP);
1218bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    Reserved.set(Mips::FP_64);
12258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
12358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Reserve hardware registers.
12558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  Reserved.set(Mips::HWR29);
12658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  Reserved.set(Mips::HWR29_64);
12758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
12868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  // Reserve RA if in mips16 mode.
12958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (Subtarget.inMips16Mode()) {
13058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    Reserved.set(Mips::RA);
13158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    Reserved.set(Mips::RA_64);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
134a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Reserve GP if small section is used.
13558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (Subtarget.useSmallSection()) {
13658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    Reserved.set(Mips::GP);
137a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    Reserved.set(Mips::GP_64);
13868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  }
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return Reserved;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool
14468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)MipsRegisterInfo::requiresRegisterScavenging(const MachineFunction &MF) const {
14558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return true;
14658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
14758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
14858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)bool
14958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)MipsRegisterInfo::trackLivenessAfterRegAlloc(const MachineFunction &MF) const {
15068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  return true;
15158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
15258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
15358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// FrameIndex represent objects inside a abstract stack.
15458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// We must replace FrameIndex with an stack/frame pointer
15558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// direct reference.
15668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void MipsRegisterInfo::
157a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj,
158a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                    RegScavenger *RS) const {
159a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  MachineInstr &MI = *II;
160a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  MachineFunction &MF = *MI.getParent()->getParent();
161a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
16268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  unsigned i = 0;
163a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  while (!MI.getOperand(i).isFI()) {
164a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    ++i;
165a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    assert(i < MI.getNumOperands() &&
166a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)           "Instr doesn't have FrameIndex operand!");
167a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
16858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
16958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DEBUG(errs() << "\nFunction : " << MF.getFunction()->getName() << "\n";
17058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        errs() << "<--------->\n" << MI);
17158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
17258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  int FrameIndex = MI.getOperand(i).getIndex();
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint64_t stackSize = MF.getFrameInfo()->getStackSize();
17458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  int64_t spOffset = MF.getFrameInfo()->getObjectOffset(FrameIndex);
17558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
17658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DEBUG(errs() << "FrameIndex : " << FrameIndex << "\n"
17758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)               << "spOffset   : " << spOffset << "\n"
178868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)               << "stackSize  : " << stackSize << "\n");
17958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
18058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  eliminateFI(MI, i, FrameIndex, stackSize, spOffset);
18158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
18258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
18358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)unsigned MipsRegisterInfo::
18458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)getFrameRegister(const MachineFunction &MF) const {
18558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  const TargetFrameLowering *TFI = MF.getTarget().getFrameLowering();
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool IsN64 = Subtarget.isABI_N64();
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TFI->hasFP(MF) ? (IsN64 ? Mips::FP_64 : Mips::FP) :
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          (IsN64 ? Mips::SP_64 : Mips::SP);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)unsigned MipsRegisterInfo::
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)getEHExceptionRegister() const {
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  llvm_unreachable("What is the exception register");
195eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)unsigned MipsRegisterInfo::
19858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)getEHHandlerRegister() const {
19958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  llvm_unreachable("What is the exception handler register");
20058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
20158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)