<commit_before>//===-- llvm/CodeGen/VirtRegMap.cpp - Virtual Register Map ----------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the VirtRegMap class.
//
// It also contains implementations of the the Spiller interface, which, given a
// virtual register map and a machine function, eliminates all virtual
// references by replacing them with physical register references - adding spill
// code as necessary.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "spiller"
#include "VirtRegMap.h"
#include "llvm/Function.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/SSARegMap.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm;

namespace {
  Statistic<> NumSpills("spiller", "Number of register spills");
  Statistic<> NumStores("spiller", "Number of stores added");
  Statistic<> NumLoads ("spiller", "Number of loads added");

  enum SpillerName { simple, local };

  cl::opt<SpillerName>
  SpillerOpt("spiller",
             cl::desc("Spiller to use: (default: local)"),
             cl::Prefix,
             cl::values(clEnumVal(simple, "  simple spiller"),
                        clEnumVal(local,  "  local spiller"),
                        clEnumValEnd),
             cl::init(local));
}

//===----------------------------------------------------------------------===//
//  VirtRegMap implementation
//===----------------------------------------------------------------------===//

void VirtRegMap::grow() {
  Virt2PhysMap.grow(MF.getSSARegMap()->getLastVirtReg());
  Virt2StackSlotMap.grow(MF.getSSARegMap()->getLastVirtReg());
}

int VirtRegMap::assignVirt2StackSlot(unsigned virtReg) {
  assert(MRegisterInfo::isVirtualRegister(virtReg));
  assert(Virt2StackSlotMap[virtReg] == NO_STACK_SLOT &&
         "attempt to assign stack slot to already spilled register");
  const TargetRegisterClass* RC = MF.getSSARegMap()->getRegClass(virtReg);
  int frameIndex = MF.getFrameInfo()->CreateStackObject(RC->getSize(),
                                                        RC->getAlignment());
  Virt2StackSlotMap[virtReg] = frameIndex;
  ++NumSpills;
  return frameIndex;
}

void VirtRegMap::assignVirt2StackSlot(unsigned virtReg, int frameIndex) {
  assert(MRegisterInfo::isVirtualRegister(virtReg));
  assert(Virt2StackSlotMap[virtReg] == NO_STACK_SLOT &&
         "attempt to assign stack slot to already spilled register");
  Virt2StackSlotMap[virtReg] = frameIndex;
}

void VirtRegMap::virtFolded(unsigned virtReg,
                            MachineInstr* oldMI,
                            MachineInstr* newMI) {
  // move previous memory references folded to new instruction
  MI2VirtMapTy::iterator i, e;
  std::vector<MI2VirtMapTy::mapped_type> regs;
  for (tie(i, e) = MI2VirtMap.equal_range(oldMI); i != e; ) {
    regs.push_back(i->second);
    MI2VirtMap.erase(i++);
  }
  for (unsigned i = 0, e = regs.size(); i != e; ++i)
    MI2VirtMap.insert(std::make_pair(newMI, i));

  // add new memory reference
  MI2VirtMap.insert(std::make_pair(newMI, virtReg));
}

void VirtRegMap::print(std::ostream &OS) const {
  const MRegisterInfo* MRI = MF.getTarget().getRegisterInfo();

  OS << "********** REGISTER MAP **********\n";
  for (unsigned i = MRegisterInfo::FirstVirtualRegister,
         e = MF.getSSARegMap()->getLastVirtReg(); i <= e; ++i) {
    if (Virt2PhysMap[i] != (unsigned)VirtRegMap::NO_PHYS_REG)
      OS << "[reg" << i << " -> " << MRI->getName(Virt2PhysMap[i]) << "]\n";

  }

  for (unsigned i = MRegisterInfo::FirstVirtualRegister,
         e = MF.getSSARegMap()->getLastVirtReg(); i <= e; ++i)
    if (Virt2StackSlotMap[i] != VirtRegMap::NO_STACK_SLOT)
      OS << "[reg" << i << " -> fi#" << Virt2StackSlotMap[i] << "]\n";
  OS << '\n';
}

void VirtRegMap::dump() const { print(std::cerr); }

//===----------------------------------------------------------------------===//
// Simple Spiller Implementation
//===----------------------------------------------------------------------===//

Spiller::~Spiller() {}

namespace {
  struct SimpleSpiller : public Spiller {
    bool runOnMachineFunction(MachineFunction& mf, const VirtRegMap &VRM);
  };
}

bool SimpleSpiller::runOnMachineFunction(MachineFunction& MF,
                                         const VirtRegMap& VRM) {
  DEBUG(std::cerr << "********** REWRITE MACHINE CODE **********\n");
  DEBUG(std::cerr << "********** Function: "
                  << MF.getFunction()->getName() << '\n');
  const TargetMachine& TM = MF.getTarget();
  const MRegisterInfo& MRI = *TM.getRegisterInfo();

  // LoadedRegs - Keep track of which vregs are loaded, so that we only load
  // each vreg once (in the case where a spilled vreg is used by multiple
  // operands).  This is always smaller than the number of operands to the
  // current machine instr, so it should be small.
  std::vector<unsigned> LoadedRegs;

  for (MachineFunction::iterator mbbi = MF.begin(), E = MF.end();
       mbbi != E; ++mbbi) {
    DEBUG(std::cerr << mbbi->getBasicBlock()->getName() << ":\n");
    for (MachineBasicBlock::iterator mii = mbbi->begin(),
           mie = mbbi->end(); mii != mie; ++mii) {
      for (unsigned i = 0, e = mii->getNumOperands(); i != e; ++i) {
        MachineOperand& mop = mii->getOperand(i);
        if (mop.isRegister() && mop.getReg() &&
            MRegisterInfo::isVirtualRegister(mop.getReg())) {
          unsigned virtReg = mop.getReg();
          unsigned physReg = VRM.getPhys(virtReg);
          if (mop.isUse() && VRM.hasStackSlot(mop.getReg()) &&
              std::find(LoadedRegs.begin(), LoadedRegs.end(),
                        virtReg) == LoadedRegs.end()) {
            MRI.loadRegFromStackSlot(*mbbi, mii, physReg,
                                     VRM.getStackSlot(virtReg));
            LoadedRegs.push_back(virtReg);
            DEBUG(std::cerr << '\t';
                  prior(mii)->print(std::cerr, &TM));
            ++NumLoads;
          }

          if (mop.isDef() && VRM.hasStackSlot(mop.getReg())) {
            MRI.storeRegToStackSlot(*mbbi, next(mii), physReg,
                                    VRM.getStackSlot(virtReg));
            ++NumStores;
          }
          mii->SetMachineOperandReg(i, physReg);
        }
      }
      DEBUG(std::cerr << '\t'; mii->print(std::cerr, &TM));
      LoadedRegs.clear();
    }
  }
  return true;
}

//===----------------------------------------------------------------------===//
//  Local Spiller Implementation
//===----------------------------------------------------------------------===//

namespace {
  class LocalSpiller : public Spiller {
    typedef std::vector<unsigned> Phys2VirtMap;
    typedef std::vector<bool> PhysFlag;
    typedef DenseMap<MachineInstr*, VirtReg2IndexFunctor> Virt2MI;

    MachineFunction *MF;
    const TargetMachine *TM;
    const TargetInstrInfo *TII;
    const MRegisterInfo *MRI;
    const VirtRegMap *VRM;
    Phys2VirtMap p2vMap_;
    PhysFlag dirty_;
    Virt2MI lastDef_;

  public:
    bool runOnMachineFunction(MachineFunction &MF, const VirtRegMap &VRM);

  private:
    void vacateJustPhysReg(MachineBasicBlock& mbb,
                           MachineBasicBlock::iterator mii,
                           unsigned physReg);

    void vacatePhysReg(MachineBasicBlock& mbb,
                       MachineBasicBlock::iterator mii,
                       unsigned physReg) {
      vacateJustPhysReg(mbb, mii, physReg);
      for (const unsigned* as = MRI->getAliasSet(physReg); *as; ++as)
        vacateJustPhysReg(mbb, mii, *as);
    }

    void handleUse(MachineBasicBlock& mbb,
                   MachineBasicBlock::iterator mii,
                   unsigned virtReg,
                   unsigned physReg) {
      // check if we are replacing a previous mapping
      if (p2vMap_[physReg] != virtReg) {
        vacatePhysReg(mbb, mii, physReg);
        p2vMap_[physReg] = virtReg;
        // load if necessary
        if (VRM->hasStackSlot(virtReg)) {
          MRI->loadRegFromStackSlot(mbb, mii, physReg,
                                     VRM->getStackSlot(virtReg));
          ++NumLoads;
          DEBUG(std::cerr << "added: ";
                prior(mii)->print(std::cerr, TM));
          lastDef_[virtReg] = mii;
        }
      }
    }

    void handleDef(MachineBasicBlock& mbb,
                   MachineBasicBlock::iterator mii,
                   unsigned virtReg,
                   unsigned physReg) {
      // check if we are replacing a previous mapping
      if (p2vMap_[physReg] != virtReg)
        vacatePhysReg(mbb, mii, physReg);

      p2vMap_[physReg] = virtReg;
      dirty_[physReg] = true;
      lastDef_[virtReg] = mii;
    }

    void eliminateVirtRegsInMbb(MachineBasicBlock& mbb);
  };
}

bool LocalSpiller::runOnMachineFunction(MachineFunction &mf,
                                        const VirtRegMap &vrm) {
  MF = &mf;
  TM = &MF->getTarget();
  TII = TM->getInstrInfo();
  MRI = TM->getRegisterInfo();
  VRM = &vrm;
  p2vMap_.assign(MRI->getNumRegs(), 0);
  dirty_.assign(MRI->getNumRegs(), false);

  DEBUG(std::cerr << "********** REWRITE MACHINE CODE **********\n");
  DEBUG(std::cerr << "********** Function: "
        << MF->getFunction()->getName() << '\n');

  for (MachineFunction::iterator mbbi = MF->begin(),
         mbbe = MF->end(); mbbi != mbbe; ++mbbi) {
    lastDef_.grow(MF->getSSARegMap()->getLastVirtReg());
    DEBUG(std::cerr << mbbi->getBasicBlock()->getName() << ":\n");
    eliminateVirtRegsInMbb(*mbbi);
    // clear map, dirty flag and last ref
    p2vMap_.assign(p2vMap_.size(), 0);
    dirty_.assign(dirty_.size(), false);
    lastDef_.clear();
  }
  return true;
}

void LocalSpiller::vacateJustPhysReg(MachineBasicBlock& mbb,
                                     MachineBasicBlock::iterator mii,
                                     unsigned physReg) {
  unsigned virtReg = p2vMap_[physReg];
  if (dirty_[physReg] && VRM->hasStackSlot(virtReg)) {
    assert(lastDef_[virtReg] && "virtual register is mapped "
           "to a register and but was not defined!");
    MachineBasicBlock::iterator lastDef = lastDef_[virtReg];
    MachineBasicBlock::iterator nextLastRef = next(lastDef);
    MRI->storeRegToStackSlot(*lastDef->getParent(),
                              nextLastRef,
                              physReg,
                              VRM->getStackSlot(virtReg));
    ++NumStores;
    DEBUG(std::cerr << "added: ";
          prior(nextLastRef)->print(std::cerr, TM);
          std::cerr << "after: ";
          lastDef->print(std::cerr, TM));
    lastDef_[virtReg] = 0;
  }
  p2vMap_[physReg] = 0;
  dirty_[physReg] = false;
}

void LocalSpiller::eliminateVirtRegsInMbb(MachineBasicBlock &MBB) {
  for (MachineBasicBlock::iterator MI = MBB.begin(), E = MBB.end();
       MI != E; ++MI) {

    // if we have references to memory operands make sure
    // we clear all physical registers that may contain
    // the value of the spilled virtual register
    VirtRegMap::MI2VirtMapTy::const_iterator i, e;
    for (tie(i, e) = VRM->getFoldedVirts(MI); i != e; ++i) {
      if (VRM->hasPhys(i->second))
        vacateJustPhysReg(MBB, MI, VRM->getPhys(i->second));
    }

    // rewrite all used operands
    for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
      MachineOperand& op = MI->getOperand(i);
      if (op.isRegister() && op.getReg() && op.isUse() &&
          MRegisterInfo::isVirtualRegister(op.getReg())) {
        unsigned virtReg = op.getReg();
        unsigned physReg = VRM->getPhys(virtReg);
        handleUse(MBB, MI, virtReg, physReg);
        MI->SetMachineOperandReg(i, physReg);
        // mark as dirty if this is def&use
        if (op.isDef()) {
          dirty_[physReg] = true;
          lastDef_[virtReg] = MI;
        }
      }
    }

    // spill implicit physical register defs
    const TargetInstrDescriptor& tid = TII->get(MI->getOpcode());
    for (const unsigned* id = tid.ImplicitDefs; *id; ++id)
      vacatePhysReg(MBB, MI, *id);

    // spill explicit physical register defs
    for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
      MachineOperand& op = MI->getOperand(i);
      if (op.isRegister() && op.getReg() && !op.isUse() &&
          MRegisterInfo::isPhysicalRegister(op.getReg()))
        vacatePhysReg(MBB, MI, op.getReg());
    }

    // rewrite def operands (def&use was handled with the
    // uses so don't check for those here)
    for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
      MachineOperand& op = MI->getOperand(i);
      if (op.isRegister() && op.getReg() && !op.isUse())
        if (MRegisterInfo::isPhysicalRegister(op.getReg()))
          vacatePhysReg(MBB, MI, op.getReg());
        else {
          unsigned physReg = VRM->getPhys(op.getReg());
          handleDef(MBB, MI, op.getReg(), physReg);
          MI->SetMachineOperandReg(i, physReg);
        }
    }

    DEBUG(std::cerr << '\t'; MI->print(std::cerr, TM));
  }

  for (unsigned i = 1, e = p2vMap_.size(); i != e; ++i)
    vacateJustPhysReg(MBB, MBB.getFirstTerminator(), i);
}

llvm::Spiller* llvm::createSpiller() {
  switch (SpillerOpt) {
  default: assert(0 && "Unreachable!");
  case local:
    return new LocalSpiller();
  case simple:
    return new SimpleSpiller();
  }
}
<commit_msg>Switch from defaulting to the 'local' spiller to the 'simple' spiller.  The two spillers produce perfectly identical code (at least on povray and eon), but the simple spiller is substantially faster than the local spiller. Once the local spiller is improved, we can switch back.<commit_after>//===-- llvm/CodeGen/VirtRegMap.cpp - Virtual Register Map ----------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the VirtRegMap class.
//
// It also contains implementations of the the Spiller interface, which, given a
// virtual register map and a machine function, eliminates all virtual
// references by replacing them with physical register references - adding spill
// code as necessary.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "spiller"
#include "VirtRegMap.h"
#include "llvm/Function.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/SSARegMap.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm;

namespace {
  Statistic<> NumSpills("spiller", "Number of register spills");
  Statistic<> NumStores("spiller", "Number of stores added");
  Statistic<> NumLoads ("spiller", "Number of loads added");

  enum SpillerName { simple, local };

  cl::opt<SpillerName>
  SpillerOpt("spiller",
             cl::desc("Spiller to use: (default: simple)"),
             cl::Prefix,
             cl::values(clEnumVal(simple, "  simple spiller"),
                        clEnumVal(local,  "  local spiller"),
                        clEnumValEnd),
             cl::init(simple));
}

//===----------------------------------------------------------------------===//
//  VirtRegMap implementation
//===----------------------------------------------------------------------===//

void VirtRegMap::grow() {
  Virt2PhysMap.grow(MF.getSSARegMap()->getLastVirtReg());
  Virt2StackSlotMap.grow(MF.getSSARegMap()->getLastVirtReg());
}

int VirtRegMap::assignVirt2StackSlot(unsigned virtReg) {
  assert(MRegisterInfo::isVirtualRegister(virtReg));
  assert(Virt2StackSlotMap[virtReg] == NO_STACK_SLOT &&
         "attempt to assign stack slot to already spilled register");
  const TargetRegisterClass* RC = MF.getSSARegMap()->getRegClass(virtReg);
  int frameIndex = MF.getFrameInfo()->CreateStackObject(RC->getSize(),
                                                        RC->getAlignment());
  Virt2StackSlotMap[virtReg] = frameIndex;
  ++NumSpills;
  return frameIndex;
}

void VirtRegMap::assignVirt2StackSlot(unsigned virtReg, int frameIndex) {
  assert(MRegisterInfo::isVirtualRegister(virtReg));
  assert(Virt2StackSlotMap[virtReg] == NO_STACK_SLOT &&
         "attempt to assign stack slot to already spilled register");
  Virt2StackSlotMap[virtReg] = frameIndex;
}

void VirtRegMap::virtFolded(unsigned virtReg,
                            MachineInstr* oldMI,
                            MachineInstr* newMI) {
  // move previous memory references folded to new instruction
  MI2VirtMapTy::iterator i, e;
  std::vector<MI2VirtMapTy::mapped_type> regs;
  for (tie(i, e) = MI2VirtMap.equal_range(oldMI); i != e; ) {
    regs.push_back(i->second);
    MI2VirtMap.erase(i++);
  }
  for (unsigned i = 0, e = regs.size(); i != e; ++i)
    MI2VirtMap.insert(std::make_pair(newMI, i));

  // add new memory reference
  MI2VirtMap.insert(std::make_pair(newMI, virtReg));
}

void VirtRegMap::print(std::ostream &OS) const {
  const MRegisterInfo* MRI = MF.getTarget().getRegisterInfo();

  OS << "********** REGISTER MAP **********\n";
  for (unsigned i = MRegisterInfo::FirstVirtualRegister,
         e = MF.getSSARegMap()->getLastVirtReg(); i <= e; ++i) {
    if (Virt2PhysMap[i] != (unsigned)VirtRegMap::NO_PHYS_REG)
      OS << "[reg" << i << " -> " << MRI->getName(Virt2PhysMap[i]) << "]\n";

  }

  for (unsigned i = MRegisterInfo::FirstVirtualRegister,
         e = MF.getSSARegMap()->getLastVirtReg(); i <= e; ++i)
    if (Virt2StackSlotMap[i] != VirtRegMap::NO_STACK_SLOT)
      OS << "[reg" << i << " -> fi#" << Virt2StackSlotMap[i] << "]\n";
  OS << '\n';
}

void VirtRegMap::dump() const { print(std::cerr); }

//===----------------------------------------------------------------------===//
// Simple Spiller Implementation
//===----------------------------------------------------------------------===//

Spiller::~Spiller() {}

namespace {
  struct SimpleSpiller : public Spiller {
    bool runOnMachineFunction(MachineFunction& mf, const VirtRegMap &VRM);
  };
}

bool SimpleSpiller::runOnMachineFunction(MachineFunction& MF,
                                         const VirtRegMap& VRM) {
  DEBUG(std::cerr << "********** REWRITE MACHINE CODE **********\n");
  DEBUG(std::cerr << "********** Function: "
                  << MF.getFunction()->getName() << '\n');
  const TargetMachine& TM = MF.getTarget();
  const MRegisterInfo& MRI = *TM.getRegisterInfo();

  // LoadedRegs - Keep track of which vregs are loaded, so that we only load
  // each vreg once (in the case where a spilled vreg is used by multiple
  // operands).  This is always smaller than the number of operands to the
  // current machine instr, so it should be small.
  std::vector<unsigned> LoadedRegs;

  for (MachineFunction::iterator mbbi = MF.begin(), E = MF.end();
       mbbi != E; ++mbbi) {
    DEBUG(std::cerr << mbbi->getBasicBlock()->getName() << ":\n");
    for (MachineBasicBlock::iterator mii = mbbi->begin(),
           mie = mbbi->end(); mii != mie; ++mii) {
      for (unsigned i = 0, e = mii->getNumOperands(); i != e; ++i) {
        MachineOperand& mop = mii->getOperand(i);
        if (mop.isRegister() && mop.getReg() &&
            MRegisterInfo::isVirtualRegister(mop.getReg())) {
          unsigned virtReg = mop.getReg();
          unsigned physReg = VRM.getPhys(virtReg);
          if (mop.isUse() && VRM.hasStackSlot(mop.getReg()) &&
              std::find(LoadedRegs.begin(), LoadedRegs.end(),
                        virtReg) == LoadedRegs.end()) {
            MRI.loadRegFromStackSlot(*mbbi, mii, physReg,
                                     VRM.getStackSlot(virtReg));
            LoadedRegs.push_back(virtReg);
            DEBUG(std::cerr << '\t';
                  prior(mii)->print(std::cerr, &TM));
            ++NumLoads;
          }

          if (mop.isDef() && VRM.hasStackSlot(mop.getReg())) {
            MRI.storeRegToStackSlot(*mbbi, next(mii), physReg,
                                    VRM.getStackSlot(virtReg));
            ++NumStores;
          }
          mii->SetMachineOperandReg(i, physReg);
        }
      }
      DEBUG(std::cerr << '\t'; mii->print(std::cerr, &TM));
      LoadedRegs.clear();
    }
  }
  return true;
}

//===----------------------------------------------------------------------===//
//  Local Spiller Implementation
//===----------------------------------------------------------------------===//

namespace {
  class LocalSpiller : public Spiller {
    typedef std::vector<unsigned> Phys2VirtMap;
    typedef std::vector<bool> PhysFlag;
    typedef DenseMap<MachineInstr*, VirtReg2IndexFunctor> Virt2MI;

    MachineFunction *MF;
    const TargetMachine *TM;
    const TargetInstrInfo *TII;
    const MRegisterInfo *MRI;
    const VirtRegMap *VRM;
    Phys2VirtMap p2vMap_;
    PhysFlag dirty_;
    Virt2MI lastDef_;

  public:
    bool runOnMachineFunction(MachineFunction &MF, const VirtRegMap &VRM);

  private:
    void vacateJustPhysReg(MachineBasicBlock& mbb,
                           MachineBasicBlock::iterator mii,
                           unsigned physReg);

    void vacatePhysReg(MachineBasicBlock& mbb,
                       MachineBasicBlock::iterator mii,
                       unsigned physReg) {
      vacateJustPhysReg(mbb, mii, physReg);
      for (const unsigned* as = MRI->getAliasSet(physReg); *as; ++as)
        vacateJustPhysReg(mbb, mii, *as);
    }

    void handleUse(MachineBasicBlock& mbb,
                   MachineBasicBlock::iterator mii,
                   unsigned virtReg,
                   unsigned physReg) {
      // check if we are replacing a previous mapping
      if (p2vMap_[physReg] != virtReg) {
        vacatePhysReg(mbb, mii, physReg);
        p2vMap_[physReg] = virtReg;
        // load if necessary
        if (VRM->hasStackSlot(virtReg)) {
          MRI->loadRegFromStackSlot(mbb, mii, physReg,
                                     VRM->getStackSlot(virtReg));
          ++NumLoads;
          DEBUG(std::cerr << "added: ";
                prior(mii)->print(std::cerr, TM));
          lastDef_[virtReg] = mii;
        }
      }
    }

    void handleDef(MachineBasicBlock& mbb,
                   MachineBasicBlock::iterator mii,
                   unsigned virtReg,
                   unsigned physReg) {
      // check if we are replacing a previous mapping
      if (p2vMap_[physReg] != virtReg)
        vacatePhysReg(mbb, mii, physReg);

      p2vMap_[physReg] = virtReg;
      dirty_[physReg] = true;
      lastDef_[virtReg] = mii;
    }

    void eliminateVirtRegsInMbb(MachineBasicBlock& mbb);
  };
}

bool LocalSpiller::runOnMachineFunction(MachineFunction &mf,
                                        const VirtRegMap &vrm) {
  MF = &mf;
  TM = &MF->getTarget();
  TII = TM->getInstrInfo();
  MRI = TM->getRegisterInfo();
  VRM = &vrm;
  p2vMap_.assign(MRI->getNumRegs(), 0);
  dirty_.assign(MRI->getNumRegs(), false);

  DEBUG(std::cerr << "********** REWRITE MACHINE CODE **********\n");
  DEBUG(std::cerr << "********** Function: "
        << MF->getFunction()->getName() << '\n');

  for (MachineFunction::iterator mbbi = MF->begin(),
         mbbe = MF->end(); mbbi != mbbe; ++mbbi) {
    lastDef_.grow(MF->getSSARegMap()->getLastVirtReg());
    DEBUG(std::cerr << mbbi->getBasicBlock()->getName() << ":\n");
    eliminateVirtRegsInMbb(*mbbi);
    // clear map, dirty flag and last ref
    p2vMap_.assign(p2vMap_.size(), 0);
    dirty_.assign(dirty_.size(), false);
    lastDef_.clear();
  }
  return true;
}

void LocalSpiller::vacateJustPhysReg(MachineBasicBlock& mbb,
                                     MachineBasicBlock::iterator mii,
                                     unsigned physReg) {
  unsigned virtReg = p2vMap_[physReg];
  if (dirty_[physReg] && VRM->hasStackSlot(virtReg)) {
    assert(lastDef_[virtReg] && "virtual register is mapped "
           "to a register and but was not defined!");
    MachineBasicBlock::iterator lastDef = lastDef_[virtReg];
    MachineBasicBlock::iterator nextLastRef = next(lastDef);
    MRI->storeRegToStackSlot(*lastDef->getParent(),
                              nextLastRef,
                              physReg,
                              VRM->getStackSlot(virtReg));
    ++NumStores;
    DEBUG(std::cerr << "added: ";
          prior(nextLastRef)->print(std::cerr, TM);
          std::cerr << "after: ";
          lastDef->print(std::cerr, TM));
    lastDef_[virtReg] = 0;
  }
  p2vMap_[physReg] = 0;
  dirty_[physReg] = false;
}

void LocalSpiller::eliminateVirtRegsInMbb(MachineBasicBlock &MBB) {
  for (MachineBasicBlock::iterator MI = MBB.begin(), E = MBB.end();
       MI != E; ++MI) {

    // if we have references to memory operands make sure
    // we clear all physical registers that may contain
    // the value of the spilled virtual register
    VirtRegMap::MI2VirtMapTy::const_iterator i, e;
    for (tie(i, e) = VRM->getFoldedVirts(MI); i != e; ++i) {
      if (VRM->hasPhys(i->second))
        vacateJustPhysReg(MBB, MI, VRM->getPhys(i->second));
    }

    // rewrite all used operands
    for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
      MachineOperand& op = MI->getOperand(i);
      if (op.isRegister() && op.getReg() && op.isUse() &&
          MRegisterInfo::isVirtualRegister(op.getReg())) {
        unsigned virtReg = op.getReg();
        unsigned physReg = VRM->getPhys(virtReg);
        handleUse(MBB, MI, virtReg, physReg);
        MI->SetMachineOperandReg(i, physReg);
        // mark as dirty if this is def&use
        if (op.isDef()) {
          dirty_[physReg] = true;
          lastDef_[virtReg] = MI;
        }
      }
    }

    // spill implicit physical register defs
    const TargetInstrDescriptor& tid = TII->get(MI->getOpcode());
    for (const unsigned* id = tid.ImplicitDefs; *id; ++id)
      vacatePhysReg(MBB, MI, *id);

    // spill explicit physical register defs
    for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
      MachineOperand& op = MI->getOperand(i);
      if (op.isRegister() && op.getReg() && !op.isUse() &&
          MRegisterInfo::isPhysicalRegister(op.getReg()))
        vacatePhysReg(MBB, MI, op.getReg());
    }

    // rewrite def operands (def&use was handled with the
    // uses so don't check for those here)
    for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
      MachineOperand& op = MI->getOperand(i);
      if (op.isRegister() && op.getReg() && !op.isUse())
        if (MRegisterInfo::isPhysicalRegister(op.getReg()))
          vacatePhysReg(MBB, MI, op.getReg());
        else {
          unsigned physReg = VRM->getPhys(op.getReg());
          handleDef(MBB, MI, op.getReg(), physReg);
          MI->SetMachineOperandReg(i, physReg);
        }
    }

    DEBUG(std::cerr << '\t'; MI->print(std::cerr, TM));
  }

  for (unsigned i = 1, e = p2vMap_.size(); i != e; ++i)
    vacateJustPhysReg(MBB, MBB.getFirstTerminator(), i);
}

llvm::Spiller* llvm::createSpiller() {
  switch (SpillerOpt) {
  default: assert(0 && "Unreachable!");
  case local:
    return new LocalSpiller();
  case simple:
    return new SimpleSpiller();
  }
}
<|endoftext|>


<commit_before>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2011 Sergey Lisitsyn
 * Copyright (C) 2011 Berlin Institute of Technology and Max-Planck-Society
 */

#include "preprocessor/LocallyLinearEmbedding.h"
#ifdef HAVE_LAPACK
#include "lib/lapack.h"
#include "lib/common.h"
#include "lib/Mathematics.h"
#include "lib/io.h"
#include "distance/EuclidianDistance.h"
#include "lib/Signal.h"

using namespace shogun;

CLocallyLinearEmbedding::CLocallyLinearEmbedding() : CSimplePreprocessor<float64_t>(), m_target_dim(1), m_k(1)
{
}

CLocallyLinearEmbedding::~CLocallyLinearEmbedding()
{
}

bool CLocallyLinearEmbedding::init(CFeatures* data)
{
	return true;
}

void CLocallyLinearEmbedding::cleanup()
{
}

float64_t* CLocallyLinearEmbedding::apply_to_feature_matrix(CFeatures* data)
{
	// shorthand for simplefeatures data
	CSimpleFeatures<float64_t>* pdata = (CSimpleFeatures<float64_t>*) data;
	ASSERT(pdata);

	// get dimensionality and number of vectors of data
	int32_t dim = pdata->get_num_features();
	ASSERT(m_target_dim<=dim);
	int32_t N = pdata->get_num_vectors();
	ASSERT(m_k<N);

	// loop variables
	int32_t i,j,k;

	// compute distance matrix
	CDistance* distance = new CEuclidianDistance(pdata,pdata);
	float64_t* distance_matrix;
	distance->get_distance_matrix(&distance_matrix,&N,&N);
	delete distance;

	// init matrices to be used
	int32_t* neighborhood_matrix = new int32_t[N*m_k];
	int32_t* local_neighbors_idxs = new int32_t[N];

	// construct neighborhood matrix (contains idxs of neighbors for
	// i-th object in i-th column)
	for (i=0; i<N; i++)
	{
		for (j=0; j<N; j++)
		{
			local_neighbors_idxs[j] = j;
		}

		CMath::qsort_index(distance_matrix+(i*N),local_neighbors_idxs,N);

		for (j=0; j<m_k; j++)
			neighborhood_matrix[j*N+i] = local_neighbors_idxs[j+1];
	}

	delete[] distance_matrix;
	delete[] local_neighbors_idxs;

	// init W (weight) matrix
	float64_t* W_matrix = new float64_t[N*N];
	for (i=0; i<N; i++)
		for (j=0; j<N; j++)
			W_matrix[i*N+j]=0.0;

	// init matrices to be used
	float64_t* z_matrix = new float64_t[N*m_k];
	float64_t* covariance_matrix = new float64_t[m_k*m_k];
	float64_t* feature_vector = new float64_t[dim];
	float64_t* id_vector = new float64_t[m_k];
	int32_t* ipiv = new int32_t[m_k];

	float64_t norming = 0.0;

	for (i=0; i<N; i++)
	{
		// compute local feature matrix containing neighbors of i-th vector
		for (j=0; j<m_k; j++)
		{
			pdata->get_feature_vector(&feature_vector, &dim, neighborhood_matrix[j*N+i]);
			for (k=0; k<dim; k++)
				z_matrix[j*dim+k] = feature_vector[k];
		}

		// get i-th feature vector
		pdata->get_feature_vector(&feature_vector, &dim, i);

		// center features by subtracting i-th feature column
		for (j=0; j<m_k; j++)
		{
			for (k=0; k<dim; k++)
				z_matrix[j*dim+k] -= feature_vector[k];
		}

		// compute local covariance matrix
		cblas_dgemm(CblasColMajor,CblasTrans,CblasNoTrans,
		            m_k,m_k,dim,
		            1.0,
		            z_matrix,dim,
		            z_matrix,dim,
		            0.0,
		            covariance_matrix,m_k);

		//CMath::display_matrix(covariance_matrix,m_k,m_k,"covar");

		for (j=0; j<m_k; j++)
			id_vector[j] = 1.0;

		// regularize in case of ill-posed system
		if (m_k>dim)
		{
			// compute tr(C)
			float64_t trace = 0.0;
			for (j=0; j<m_k; j++)
				trace += covariance_matrix[j*m_k+j];

			for (j=0; j<m_k; j++)
				covariance_matrix[j*m_k+j] += 1e-3*trace;
		}

		// solve system of linear equations: covariance_matrix * X = 1
		clapack_dgesv(CblasColMajor,
		              m_k,1,
		              covariance_matrix,m_k,
		              ipiv,
		              id_vector,m_k);

		// normalize weights
		norming=0.0;
		for (j=0; j<m_k; j++)
			norming += CMath::abs(id_vector[j]);

		for (j=0; j<m_k; j++)
			id_vector[j]/=norming;

		// put weights into W matrix
		for (j=0; j<m_k; j++)
			W_matrix[N*neighborhood_matrix[j*N+i]+i]=id_vector[j];

	}

	// clean
	delete[] ipiv;
	delete[] id_vector;
	delete[] neighborhood_matrix;
	delete[] feature_vector;
	delete[] z_matrix;
	delete[] covariance_matrix;

	// W=I-W
	for (i=0; i<N; i++)
	{
		for (j=0; j<N; j++)
			W_matrix[j*N+i] = (i==j) ? 1.0-W_matrix[j*N+i] : -W_matrix[j*N+i];
	}

	// compute M=(W-I)'*(W-I)
	float64_t* M_matrix = new float64_t[N*N];
	cblas_dgemm(CblasColMajor,CblasTrans, CblasNoTrans,
	            N,N,N,1.0,
	            W_matrix,N,
	            W_matrix,N,
	            0.0,
	            M_matrix,N);

	delete[] W_matrix;

	// compute eigenvectors
	float64_t* eigenvalues_vector = new float64_t[N];
	int32_t eigenproblem_status = 0;
	wrap_dsyev('V','U',
				N,M_matrix,
				N,eigenvalues_vector,
				&eigenproblem_status);
	ASSERT(eigenproblem_status==0);

	// replace features with bottom eigenvectos
	float64_t* new_feature_matrix = new float64_t[N*m_target_dim];

	for (i=0; i<m_target_dim; i++)
	{
		for (j=0; j<N; j++)
			new_feature_matrix[j*m_target_dim+i] = M_matrix[(i+1)*(N)+j];
	}

	SGMatrix<float64_t> features(new_feature_matrix,m_target_dim,N);
	pdata->set_feature_matrix(features);

	return features.matrix;
}

float64_t* CLocallyLinearEmbedding::apply_to_feature_vector(float64_t* f, int32_t &len)
{
	SG_NOTIMPLEMENTED;
}

#endif /* HAVE_LAPACK */
<commit_msg>Crucial fixes for drunk-made mistakes in LocallyLinearEmbedding<commit_after>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2011 Sergey Lisitsyn
 * Copyright (C) 2011 Berlin Institute of Technology and Max-Planck-Society
 */

#include "preprocessor/LocallyLinearEmbedding.h"
#ifdef HAVE_LAPACK
#include "lib/lapack.h"
#include "lib/common.h"
#include "lib/Mathematics.h"
#include "lib/io.h"
#include "distance/EuclidianDistance.h"
#include "lib/Signal.h"

using namespace shogun;

CLocallyLinearEmbedding::CLocallyLinearEmbedding() : CSimplePreprocessor<float64_t>(), m_target_dim(1), m_k(1)
{
}

CLocallyLinearEmbedding::~CLocallyLinearEmbedding()
{
}

bool CLocallyLinearEmbedding::init(CFeatures* data)
{
	return true;
}

void CLocallyLinearEmbedding::cleanup()
{
}

float64_t* CLocallyLinearEmbedding::apply_to_feature_matrix(CFeatures* data)
{
	// shorthand for simplefeatures data
	CSimpleFeatures<float64_t>* pdata = (CSimpleFeatures<float64_t>*) data;
	ASSERT(pdata);

	// get dimensionality and number of vectors of data
	int32_t dim = pdata->get_num_features();
	ASSERT(m_target_dim<=dim);
	int32_t N = pdata->get_num_vectors();
	ASSERT(m_k<N);

	// loop variables
	int32_t i,j,k;

	// compute distance matrix
	CDistance* distance = new CEuclidianDistance(pdata,pdata);
	float64_t* distance_matrix;
	distance->get_distance_matrix(&distance_matrix,&N,&N);
	delete distance;

	// init matrices to be used
	int32_t* neighborhood_matrix = new int32_t[N*m_k];
	int32_t* local_neighbors_idxs = new int32_t[N];

	// construct neighborhood matrix (contains idxs of neighbors for
	// i-th object in i-th column)
	for (i=0; i<N; i++)
	{
		for (j=0; j<N; j++)
		{
			local_neighbors_idxs[j] = j;
		}

		CMath::qsort_index(distance_matrix+(i*N),local_neighbors_idxs,N);

		for (j=0; j<m_k; j++)
			neighborhood_matrix[j*N+i] = local_neighbors_idxs[j+1];
	}

	delete[] distance_matrix;
	delete[] local_neighbors_idxs;

	// init W (weight) matrix
	float64_t* W_matrix = new float64_t[N*N];
	for (i=0; i<N; i++)
		for (j=0; j<N; j++)
			W_matrix[i*N+j]=0.0;

	// init matrices and norm factor to be used
	float64_t* z_matrix = new float64_t[N*dim];
	float64_t* covariance_matrix = new float64_t[m_k*m_k];
	float64_t* id_vector = new float64_t[m_k];
	int32_t* ipiv = new int32_t[m_k];
	float64_t norming = 0.0;

	for (i=0; i<N; i++)
	{
		// get feature matrix
		SGMatrix<float64_t> feature_matrix = pdata->get_feature_matrix();

		// compute local feature matrix containing neighbors of i-th vector
		for (j=0; j<m_k; j++)
		{
			for (k=0; k<dim; k++)
				z_matrix[j*dim+k] = feature_matrix.matrix[neighborhood_matrix[j*N+i]*dim+k];
		}

		// center features by subtracting i-th feature column
		for (j=0; j<m_k; j++)
		{
			for (k=0; k<dim; k++)
				z_matrix[j*dim+k] -= feature_matrix.matrix[i*dim+k];
		}

		// compute local covariance matrix
		cblas_dgemm(CblasColMajor,CblasTrans,CblasNoTrans,
		            m_k,m_k,dim,
		            1.0,
		            z_matrix,dim,
		            z_matrix,dim,
		            0.0,
		            covariance_matrix,m_k);

		for (j=0; j<m_k; j++)
			id_vector[j] = 1.0;

		// regularize in case of ill-posed system
		if (m_k>dim)
		{
			// compute tr(C)
			float64_t trace = 0.0;
			for (j=0; j<m_k; j++)
				trace += covariance_matrix[j*m_k+j];

			for (j=0; j<m_k; j++)
				covariance_matrix[j*m_k+j] += 1e-3*trace;
		}

		// solve system of linear equations: covariance_matrix * X = 1
		clapack_dgesv(CblasColMajor,
		              m_k,1,
		              covariance_matrix,m_k,
		              ipiv,
		              id_vector,m_k);

		// normalize weights
		norming=0.0;
		for (j=0; j<m_k; j++)
			norming += CMath::abs(id_vector[j]);

		for (j=0; j<m_k; j++)
			id_vector[j]/=norming;

		// put weights into W matrix
		for (j=0; j<m_k; j++)
			W_matrix[N*neighborhood_matrix[j*N+i]+i]=id_vector[j];

	}

	// clean
	delete[] ipiv;
	delete[] id_vector;
	delete[] neighborhood_matrix;
	delete[] z_matrix;
	delete[] covariance_matrix;

	// W=I-W
	for (i=0; i<N; i++)
	{
		for (j=0; j<N; j++)
			W_matrix[j*N+i] = (i==j) ? 1.0-W_matrix[j*N+i] : -W_matrix[j*N+i];
	}

	// compute M=(W-I)'*(W-I)
	float64_t* M_matrix = new float64_t[N*N];
	cblas_dgemm(CblasColMajor,CblasTrans, CblasNoTrans,
	            N,N,N,1.0,
	            W_matrix,N,
	            W_matrix,N,
	            0.0,
	            M_matrix,N);

	delete[] W_matrix;

	// compute eigenvectors
	float64_t* eigenvalues_vector = new float64_t[N];
	int32_t eigenproblem_status = 0;
	wrap_dsyev('V','U',
				N,M_matrix,
				N,eigenvalues_vector,
				&eigenproblem_status);
	ASSERT(eigenproblem_status==0);

	// replace features with bottom eigenvectos
	float64_t* new_feature_matrix = new float64_t[N*m_target_dim];

	for (i=0; i<m_target_dim; i++)
	{
		for (j=0; j<N; j++)
			new_feature_matrix[j*m_target_dim+i] = M_matrix[(i+1)*(N)+j];
	}

	// clean
	delete[] eigenvalues_vector;
	delete[] M_matrix;

	SGMatrix<float64_t> features(new_feature_matrix,m_target_dim,N);
	pdata->set_feature_matrix(features);

	return features.matrix;
}

float64_t* CLocallyLinearEmbedding::apply_to_feature_vector(float64_t* f, int32_t &len)
{
	SG_NOTIMPLEMENTED;
}

#endif /* HAVE_LAPACK */
<|endoftext|>


<commit_before>
#include "builder.h"
#include "window.h"
#include "application.h"
#include "style.h"
#include <view/cocoa/button.h>
#include <react/button.h>

namespace gui
{

////////////////////////////////////////

builder::builder( const std::shared_ptr<window> &w )
	: _container( w->get_container() )
{
	auto sty = application::current()->get_style();
	auto bg = sty->bg_frame( _container->area() );
	_container->add_view( bg );
}

////////////////////////////////////////

builder::~builder( void )
{
}

////////////////////////////////////////

void builder::make_label( const std::shared_ptr<draw::area> &a, const std::string &txt )
{
	auto sty = application::current()->get_style();
	auto text = sty->label_text( a, txt );
	_container->add_view( text );
}

////////////////////////////////////////

void builder::make_button( const std::shared_ptr<draw::area> &a, const std::string &txt )
{
	auto sty = application::current()->get_style();

	auto frame = sty->button_frame( a );
	_container->add_view( frame );

	auto text = sty->button_text( a, txt );
	_container->add_view( text );

	auto react = std::make_shared<react::button>( a );
	_container->add_reactor( react );

	auto cbutton = std::dynamic_pointer_cast<cocoa::button>( frame );
	if ( !cbutton )
		throw std::runtime_error( "not a cocoa button" );

	react->when_pressed( [=]( bool p ) { cbutton->set_pressed( p ); } );
}

////////////////////////////////////////

}

<commit_msg>Switch builder to use action.<commit_after>
#include "builder.h"
#include "window.h"
#include "application.h"
#include "style.h"
#include <view/cocoa/button.h>
#include <react/button.h>

namespace gui
{

////////////////////////////////////////

builder::builder( const std::shared_ptr<window> &w )
	: _container( w->get_container() )
{
	auto sty = application::current()->get_style();
	auto bg = sty->bg_frame( _container->area() );
	_container->add_view( bg );
}

////////////////////////////////////////

builder::~builder( void )
{
}

////////////////////////////////////////

void builder::make_label( const std::shared_ptr<draw::area> &a, const std::string &txt )
{
	auto sty = application::current()->get_style();
	auto text = sty->label_text( a, txt );
	_container->add_view( text );
}

////////////////////////////////////////

void builder::make_button( const std::shared_ptr<draw::area> &a, const std::string &txt )
{
	auto react = std::make_shared<react::button>( a );
	_container->add_reactor( react );

	auto sty = application::current()->get_style();

	auto frame = sty->button_frame( a );
	_container->add_view( frame );

	auto text = sty->button_text( a, txt );
	_container->add_view( text );

	auto cbutton = std::dynamic_pointer_cast<cocoa::button>( frame );
	if ( !cbutton )
		throw std::runtime_error( "not a cocoa button" );

	react->pressed.callback( [=]( bool p ) { cbutton->set_pressed( p ); } );
}

////////////////////////////////////////

}

<|endoftext|>


<commit_before>#include "Auth.hpp"

#include <QDebug>
#include <QDateTime>

namespace cutehmi {
namespace lockscreen {

Auth::Auth(QObject * parent) : QObject(parent),
    m_lowerBoundOfHashes(9000), m_upperBoundOfHashes(10000), m_locked(false)
{
    m_settings = new QSettings(this);
	m_settings->beginGroup("cutehmi_lockscreen_1");
    qsrand(static_cast<uint>(QTime::currentTime().msec()));
}

bool Auth::checkPassword(const QString & password)
{
    if (validatePassword(password) != true)
        return false;
    QString passwordHash = m_settings->value("lockScreenPassword").toString();
    QByteArray hash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha3_512).toHex();
    for (int i = 0; i < m_upperBoundOfHashes; ++i) {
        if (i >= m_lowerBoundOfHashes && hash == passwordHash)
            return true;
        hash = QCryptographicHash::hash(hash, QCryptographicHash::Sha3_512).toHex();
    }
    return false;
}

bool Auth::validatePassword(const QString & password)
{
    if (password.length() != 0)
        return true;
    return false;
}

bool Auth::changePassword(const QString & newPassword, const QString & oldPassword)
{
    if (checkPassword(oldPassword)) {
        changePassword(newPassword);
        return true;
    }
    return false;
}

void Auth::changePassword(const QString & password)
{
    if (validatePassword(password) != true)
        return;
    QByteArray hash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha3_512).toHex();
    for (int i = 0; i < getNumberOfHashes(); ++i)
        hash = QCryptographicHash::hash(hash, QCryptographicHash::Sha3_512).toHex();
    QString passwordHash = hash;
    m_settings->setValue("lockScreenPassword", passwordHash);
}

int Auth::getNumberOfHashes()
{
    return qrand() % (m_upperBoundOfHashes - m_lowerBoundOfHashes) + m_lowerBoundOfHashes;
}

}
}
<commit_msg>Change name of LockScreenPassword setting to just password.<commit_after>#include "Auth.hpp"

#include <QDebug>
#include <QDateTime>

namespace cutehmi {
namespace lockscreen {

Auth::Auth(QObject * parent) : QObject(parent),
    m_lowerBoundOfHashes(9000), m_upperBoundOfHashes(10000), m_locked(false)
{
    m_settings = new QSettings(this);
	m_settings->beginGroup("cutehmi_lockscreen_1");
    qsrand(static_cast<uint>(QTime::currentTime().msec()));
}

bool Auth::checkPassword(const QString & password)
{
    if (validatePassword(password) != true)
        return false;
	QString passwordHash = m_settings->value("password").toString();
    QByteArray hash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha3_512).toHex();
    for (int i = 0; i < m_upperBoundOfHashes; ++i) {
        if (i >= m_lowerBoundOfHashes && hash == passwordHash)
            return true;
        hash = QCryptographicHash::hash(hash, QCryptographicHash::Sha3_512).toHex();
    }
    return false;
}

bool Auth::validatePassword(const QString & password)
{
    if (password.length() != 0)
        return true;
    return false;
}

bool Auth::changePassword(const QString & newPassword, const QString & oldPassword)
{
    if (checkPassword(oldPassword)) {
        changePassword(newPassword);
        return true;
    }
    return false;
}

void Auth::changePassword(const QString & password)
{
    if (validatePassword(password) != true)
        return;
    QByteArray hash = QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha3_512).toHex();
    for (int i = 0; i < getNumberOfHashes(); ++i)
        hash = QCryptographicHash::hash(hash, QCryptographicHash::Sha3_512).toHex();
    QString passwordHash = hash;
	m_settings->setValue("password", passwordHash);
}

int Auth::getNumberOfHashes()
{
    return qrand() % (m_upperBoundOfHashes - m_lowerBoundOfHashes) + m_lowerBoundOfHashes;
}

}
}
<|endoftext|>


<commit_before>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/compiler/mlir/tensorflow/transforms/constant_fold.h"

#include <algorithm>

#include "mlir/IR/BuiltinAttributes.h"  // from @llvm-project
#include "mlir/IR/OpDefinition.h"  // from @llvm-project
#include "mlir/Interfaces/SideEffectInterfaces.h"  // from @llvm-project
#include "mlir/Support/LogicalResult.h"  // from @llvm-project
#include "tensorflow/c/tf_status.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_ops.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_traits.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_types.h"
#include "tensorflow/compiler/mlir/tensorflow/utils/eval_util.h"
#include "tensorflow/core/platform/mutex.h"

namespace mlir {
namespace TF {

// Implements a TF specific policy on when constant folding is allowed.
// Policy: Always allow folding if we do not know the shape of an operand or
// result i.e., one of these values has non-static shape. If we know all the
// shapes, find the total size of the operands and results. Folding of the op is
// allowed if one of the following conditions are met:
// 1. size of results is less than a certain threshold (`kSizeThreshold`), or
// 2. size of results is within a factor (`kSizeFactor`) of size of operands, or
// TODO(b/157226221): Look into other heuristics for constant fold policy.
// LINT.IfChange(folding-policy)
static bool ShouldBeFolded(Operation* inst) {
  constexpr int kSizeFactor = 2;
  constexpr int64_t kSizeThreshold = (1 << 21);  // 256 KB
  bool has_unknown_shape = false;
  auto get_size = [&](TypeRange types) {
    int64_t size = 0;
    for (auto t : types) {
      auto tensor_type = t.cast<TensorType>();
      // Ignore types with undefined bit widths.
      if (!tensor_type.getElementType().isIntOrFloat()) continue;
      if (!tensor_type.hasStaticShape()) {
        has_unknown_shape = true;
        return size;
      }
      size += tensor_type.getNumElements() *
              tensor_type.getElementType().getIntOrFloatBitWidth();
    }
    return size;
  };

  int64_t results_size = get_size(inst->getResultTypes());
  int64_t operands_size = get_size(inst->getOperandTypes());

  return has_unknown_shape || (results_size <= kSizeThreshold) ||
         (results_size <= kSizeFactor * operands_size);
}
// LINT.ThenChange(../tests/constant-fold.mlir:folding-policy-test)

LogicalResult ConstantFoldFallbackHook(
    Operation* inst, ArrayRef<Attribute> operands,
    SmallVectorImpl<OpFoldResult>& results) {  // NOLINT
  // Instructions with side effects should not be constant folded to preserve
  // the original semantics. Ops that have no side effect and zero results but
  // could be folded should have a custom folder instead of relying on the
  // TensorFlow folding hook.
  if (inst->getNumResults() == 0 ||
      inst->hasTrait<OpTrait::TF::NoConstantFold>() ||
      inst->getNumRegions() != 0 || !MemoryEffectOpInterface::hasNoEffect(inst))
    return failure();

  // If any of the result types are variants, don't try to constant fold them.
  // This creates opaque variant constants which lose information and would
  // require "raising" later.
  for (auto type : inst->getResultTypes()) {
    if (auto tensor_type = type.dyn_cast<TensorType>()) {
      if (tensor_type.getElementType().isa<VariantType>()) {
        return failure();
      }
    }
  }

  // If all the results are empty and has numerical element types, set results
  // to empty elements attribute. This is restricted to the numerical element
  // types as the DenseElementsAttr only supports numerical and string types.
  // TODO(hinsu): Handle ops that have one of the results empty for constant
  // propagation.
  bool has_empty_numerical_results =
      llvm::all_of(inst->getResultTypes(), [](Type ty) {
        ShapedType shaped_ty = ty.cast<ShapedType>();
        Type element_ty = shaped_ty.getElementType();
        return shaped_ty.hasStaticShape() && shaped_ty.getNumElements() == 0 &&
               element_ty.isIntOrFloat();
      });
  if (has_empty_numerical_results &&
      // TODO(jpienaar): Remove this once some unmodeled op behavior is
      // addressed.
      inst->getAbstractOperation()) {
    for (Type ty : inst->getResultTypes()) {
      auto shaped_ty = ty.cast<ShapedType>();
      results.push_back(
          DenseElementsAttr::get(shaped_ty, llvm::ArrayRef<Attribute>()));
    }
    return success();
  }

  // Do not execute function calls.
  if (llvm::isa<TF::WhileOp, TF::CaseOp, TF::IfOp, CallOpInterface>(inst)) {
    return failure();
  }

  // Determine if we should attempt to fold this operation by considering the
  // size/size increase due to folding.
  if (!ShouldBeFolded(inst)) return failure();

  // TODO(jpienaar): Currently this persists the entire program execution. This
  // should instead be per module/set from the Graph being executed in TF (if
  // any) so that the value of variables in the context could be read.
  // Note: Sharing the context is fine as ops are side-effect free.
  auto initialize = []() {
    TF_Status* status = TF_NewStatus();
    // The TFE_Context is created without an accompanying delete due to current
    // lifetime. This does not result in memory leaks reported (see totw/110).
    TFE_ContextOptions* opts = TFE_NewContextOptions();
    // Input tensors are placed on the host CPU so use the explicit device
    // policy to fail if no CPU kernels are available for the op.
    TFE_ContextOptionsSetDevicePlacementPolicy(opts,
                                               TFE_DEVICE_PLACEMENT_EXPLICIT);
    auto ctx = TFE_NewContext(opts, status);
    TFE_DeleteContextOptions(opts);
    TF_DeleteStatus(status);
    return ctx;
  };
  static TFE_Context* ctx = initialize();

  // Returns directly if any of the operands is not an elements attributes.
  if (std::any_of(operands.begin(), operands.end(), [](Attribute attr) {
        return !attr || !attr.isa<ElementsAttr>();
      }))
    return failure();

  SmallVector<ElementsAttr, 4> inputs;
  inputs.reserve(operands.size());
  for (auto input : operands) {
    inputs.push_back(input.cast<ElementsAttr>());
  }

  // Avoid overlapping folds with the same context.
  // TODO(jpienaar): Avoid using global context & mutex here.
  static auto* mu = new tensorflow::mutex();
  tensorflow::mutex_lock l(*mu);
  SmallVector<Attribute, 8> constants;
  LogicalResult status =
      tensorflow::EvaluateOperation(inst, inputs, ctx, &constants);
  results.assign(constants.begin(), constants.end());
  return status;
}

static bool init_hooks = ([] () {
  TensorFlowDialect::RegisterConstantFoldHook(ConstantFoldFallbackHook);
}(), true);

}  // namespace TF
}  // namespace mlir
<commit_msg>Only initialize a single CPU as needed for eager context in folding<commit_after>/* Copyright 2019 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/compiler/mlir/tensorflow/transforms/constant_fold.h"

#include <algorithm>

#include "mlir/IR/BuiltinAttributes.h"  // from @llvm-project
#include "mlir/IR/OpDefinition.h"  // from @llvm-project
#include "mlir/Interfaces/SideEffectInterfaces.h"  // from @llvm-project
#include "mlir/Support/LogicalResult.h"  // from @llvm-project
#include "tensorflow/c/tf_status.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_ops.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_traits.h"
#include "tensorflow/compiler/mlir/tensorflow/ir/tf_types.h"
#include "tensorflow/compiler/mlir/tensorflow/utils/eval_util.h"
#include "tensorflow/core/platform/mutex.h"

namespace mlir {
namespace TF {

// Implements a TF specific policy on when constant folding is allowed.
// Policy: Always allow folding if we do not know the shape of an operand or
// result i.e., one of these values has non-static shape. If we know all the
// shapes, find the total size of the operands and results. Folding of the op is
// allowed if one of the following conditions are met:
// 1. size of results is less than a certain threshold (`kSizeThreshold`), or
// 2. size of results is within a factor (`kSizeFactor`) of size of operands, or
// TODO(b/157226221): Look into other heuristics for constant fold policy.
// LINT.IfChange(folding-policy)
static bool ShouldBeFolded(Operation* inst) {
  constexpr int kSizeFactor = 2;
  constexpr int64_t kSizeThreshold = (1 << 21);  // 256 KB
  bool has_unknown_shape = false;
  auto get_size = [&](TypeRange types) {
    int64_t size = 0;
    for (auto t : types) {
      auto tensor_type = t.cast<TensorType>();
      // Ignore types with undefined bit widths.
      if (!tensor_type.getElementType().isIntOrFloat()) continue;
      if (!tensor_type.hasStaticShape()) {
        has_unknown_shape = true;
        return size;
      }
      size += tensor_type.getNumElements() *
              tensor_type.getElementType().getIntOrFloatBitWidth();
    }
    return size;
  };

  int64_t results_size = get_size(inst->getResultTypes());
  int64_t operands_size = get_size(inst->getOperandTypes());

  return has_unknown_shape || (results_size <= kSizeThreshold) ||
         (results_size <= kSizeFactor * operands_size);
}
// LINT.ThenChange(../tests/constant-fold.mlir:folding-policy-test)

LogicalResult ConstantFoldFallbackHook(
    Operation* inst, ArrayRef<Attribute> operands,
    SmallVectorImpl<OpFoldResult>& results) {  // NOLINT
  // Instructions with side effects should not be constant folded to preserve
  // the original semantics. Ops that have no side effect and zero results but
  // could be folded should have a custom folder instead of relying on the
  // TensorFlow folding hook.
  if (inst->getNumResults() == 0 ||
      inst->hasTrait<OpTrait::TF::NoConstantFold>() ||
      inst->getNumRegions() != 0 || !MemoryEffectOpInterface::hasNoEffect(inst))
    return failure();

  // If any of the result types are variants, don't try to constant fold them.
  // This creates opaque variant constants which lose information and would
  // require "raising" later.
  for (auto type : inst->getResultTypes()) {
    if (auto tensor_type = type.dyn_cast<TensorType>()) {
      if (tensor_type.getElementType().isa<VariantType>()) {
        return failure();
      }
    }
  }

  // If all the results are empty and has numerical element types, set results
  // to empty elements attribute. This is restricted to the numerical element
  // types as the DenseElementsAttr only supports numerical and string types.
  // TODO(hinsu): Handle ops that have one of the results empty for constant
  // propagation.
  bool has_empty_numerical_results =
      llvm::all_of(inst->getResultTypes(), [](Type ty) {
        ShapedType shaped_ty = ty.cast<ShapedType>();
        Type element_ty = shaped_ty.getElementType();
        return shaped_ty.hasStaticShape() && shaped_ty.getNumElements() == 0 &&
               element_ty.isIntOrFloat();
      });
  if (has_empty_numerical_results &&
      // TODO(jpienaar): Remove this once some unmodeled op behavior is
      // addressed.
      inst->getAbstractOperation()) {
    for (Type ty : inst->getResultTypes()) {
      auto shaped_ty = ty.cast<ShapedType>();
      results.push_back(
          DenseElementsAttr::get(shaped_ty, llvm::ArrayRef<Attribute>()));
    }
    return success();
  }

  // Do not execute function calls.
  if (llvm::isa<TF::WhileOp, TF::CaseOp, TF::IfOp, CallOpInterface>(inst)) {
    return failure();
  }

  // Determine if we should attempt to fold this operation by considering the
  // size/size increase due to folding.
  if (!ShouldBeFolded(inst)) return failure();

  // TODO(jpienaar): Currently this persists the entire program execution. This
  // should instead be per module/set from the Graph being executed in TF (if
  // any) so that the value of variables in the context could be read.
  // Note: Sharing the context is fine as ops are side-effect free.
  auto initialize = []() -> TFE_Context* {
    std::unique_ptr<TF_Status, decltype(&TF_DeleteStatus)> status(
        TF_NewStatus(), TF_DeleteStatus);
    std::unique_ptr<TFE_ContextOptions, decltype(&TFE_DeleteContextOptions)>
        opts(TFE_NewContextOptions(), TFE_DeleteContextOptions);
    // Only initialize single CPU.
    tensorflow::ConfigProto config_proto;
    // This is conceptually equal to what we do in python/eager/context.py but
    // with all GPU devices ignored and CPU only set to 1.
    (*config_proto.mutable_device_count())["CPU"] = 1;
    (*config_proto.mutable_device_count())["GPU"] = 0;
    std::unique_ptr<TF_Buffer, decltype(&TF_DeleteBuffer)> config(
        TF_NewBuffer(), TF_DeleteBuffer);
    DCHECK(config->data == nullptr);

    // Copy config_proto into config.
    {
      const size_t proto_size = config_proto.ByteSizeLong();
      void* buf = tensorflow::port::Malloc(proto_size);
      if (buf == nullptr) {
        LOG(ERROR) << "Failed to allocate memory to serialize ConfigProto "
                      "while creating context options for constant folding";
        return nullptr;
      }
      if (!config_proto.SerializeWithCachedSizesToArray(
              static_cast<uint8_t*>(buf))) {
        tensorflow::port::Free(buf);
        LOG(ERROR) << "Unable to serialize ConfigProto while creating context "
                      "options for constant folding";
        return nullptr;
      }
      config->data = buf;
      config->length = proto_size;
      config->data_deallocator = [](void* data, size_t length) {
        tensorflow::port::Free(data);
      };
    }

    TFE_ContextOptionsSetConfig(opts.get(), config->data, config->length,
                                status.get());
    if (TF_GetCode(status.get()) != TF_OK) {
      LOG(ERROR) << "Failed to set context options for constant folding: "
                 << status.get();
      return nullptr;
    }

    // Input tensors are placed on the host CPU so use the explicit device
    // policy to fail if no CPU kernels are available for the op.
    TFE_ContextOptionsSetDevicePlacementPolicy(opts.get(),
                                               TFE_DEVICE_PLACEMENT_EXPLICIT);
    auto ctx = TFE_NewContext(opts.get(), status.get());
    if (TF_GetCode(status.get()) != TF_OK) {
      LOG(ERROR) << "Failed to create context for constant folding: "
                 << status.get();
      return nullptr;
    }
    return ctx;
  };
  static TFE_Context* ctx = initialize();
  if (!ctx) return failure();

  // Returns directly if any of the operands is not an elements attributes.
  if (std::any_of(operands.begin(), operands.end(), [](Attribute attr) {
        return !attr || !attr.isa<ElementsAttr>();
      }))
    return failure();

  SmallVector<ElementsAttr, 4> inputs;
  inputs.reserve(operands.size());
  for (auto input : operands) {
    inputs.push_back(input.cast<ElementsAttr>());
  }

  // Avoid overlapping folds with the same context.
  // TODO(jpienaar): Avoid using global context & mutex here.
  static auto* mu = new tensorflow::mutex();
  tensorflow::mutex_lock l(*mu);
  SmallVector<Attribute, 8> constants;
  LogicalResult status =
      tensorflow::EvaluateOperation(inst, inputs, ctx, &constants);
  results.assign(constants.begin(), constants.end());
  return status;
}

static bool init_hooks = ([] () {
  TensorFlowDialect::RegisterConstantFoldHook(ConstantFoldFallbackHook);
}(), true);

}  // namespace TF
}  // namespace mlir
<|endoftext|>


<commit_before>// Copyright(c) 2016 Jounayd Id Salah
// Distributed under the MIT License (See accompanying file LICENSE.md file or copy at http://opensource.org/licenses/MIT).
#include "neural/pch.h"
#include "neural/process/training/coNeuralTrainingNet_f.h"
#include "neural/process/training/coNeuralTrainingNet.h"
#include "neural/process/training/coNeuralTrainingLayer.h"
#include "neural/process/training/coNeuralDataSet.h"
#include "neural/process/coNeuralLayer_f.h"
#include "neural/process/coNeuralNet_f.h"
#include "neural/process/coNeuralSigmoid_f.h"
#include "neural/network/coNeuralNet.h"
#include "neural/network/coNeuralLayer.h"
#include "lang/result/coResult_f.h"

void coComputeForwardPass(coNeuralTrainingNet& _trainingNet, const coArray<coFloat>& _targetInputs)
{
	coArray<coFloat> inputs = _targetInputs;
	for (coNeuralTrainingLayer* trainingLayer : _trainingNet.GetTrainingLayers())
	{
		coNeuralLayer* layer = trainingLayer->layer;
		coComputeNeuralOutputs(*layer, inputs, trainingLayer->outputs);
		inputs = trainingLayer->outputs;
	}
}

void coComputeGradients(coNeuralTrainingNet& _trainingNet, const coArray<coFloat>& _targetOutputs)
{
	const coArray<coNeuralTrainingLayer*>& trainingLayers = _trainingNet.GetTrainingLayers();

	// Last layer
	{
		coNeuralTrainingLayer* trainingLayer = coBack(trainingLayers);
		const coUint nbOutputs = trainingLayer->outputs.count;
		coArray<coFloat>& gradients = trainingLayer->gradients;
		const coArray<coFloat>& values = trainingLayer->outputs;
		for (coUint i = 0; i < nbOutputs; ++i)
		{
			const coFloat value = values[i];
			const coFloat targetValue = _targetOutputs[i];
			const coFloat error = targetValue - value;
			gradients[i] = error * coComputeNeuralSigmoidDerivative(value);
		}
	}

	// Rest of the layers
	for (coInt j = trainingLayers.count - 2; j >= 0; --j)
	{
		coNeuralTrainingLayer* curTrainingLayer = trainingLayers[j];
		coNeuralTrainingLayer* nextTrainingLayer = trainingLayers[j + 1];
		coArray<coFloat>& curGradients = curTrainingLayer->gradients;
		const coArray<coFloat>& nextGradients = nextTrainingLayer->gradients;
		const coArray<coFloat>& curOutputs = curTrainingLayer->outputs;

		const coNeuralLayer* curData = curTrainingLayer->layer;
		const coNeuralLayer* nextData = nextTrainingLayer->layer;
		const coArray<coFloat>& nextWeights = nextData->GetWeightBuffer();
		const coUint nbCurOutputs = curData->GetNbOutputs();
		const coUint nbNextOutputs = nextData->GetNbOutputs();
		for (coUint k = 0; k < nbCurOutputs; ++k)
		{
			coFloat sum = 0.0f;
			for (coUint l = 0; l < nbNextOutputs; ++l)
			{
				const coUint weightIndex = l * nbCurOutputs + k;
				sum += nextGradients[l] * nextWeights[weightIndex];
			}
			const coFloat output = curOutputs[k];
			curGradients[k] = sum * coComputeNeuralSigmoidDerivative(output);
		}
	}
}

void coUpdateWeights(coNeuralTrainingNet& _trainingNet, coFloat _learnRate, coFloat _momentum, const coArray<coFloat>& _inputs)
{
	const coArray<coNeuralTrainingLayer*>& trainingLayers = _trainingNet.GetTrainingLayers();
	coArray<coFloat> layerInputs(_inputs);
	for (coNeuralTrainingLayer* trainingLayer : trainingLayers)
	{
		coNeuralLayer* data = trainingLayer->layer;
		const coUint nbInputs = data->GetNbInputs();
		const coUint nbOutputs = data->GetNbOutputs();
		coArray<coFloat>& biasBuffer = data->GetBiasBuffer();
		coArray<coFloat>& weightBuffer = data->GetWeightBuffer();
		coArray<coFloat>& biasDeltas = trainingLayer->biasDeltas;
		coArray<coFloat>& weightDeltas = trainingLayer->weightDeltas;
		const coArray<coFloat>& gradients = trainingLayer->gradients;
		coUint weightIndex = 0;
		for (coUint k = 0; k < nbOutputs; ++k)
		{
			const coFloat gradient = gradients[k];

			// Biases
			{
				coFloat& curDelta = biasDeltas[k];
				const coFloat prevDelta = curDelta;
				curDelta = _learnRate * gradient;
				biasBuffer[k] += curDelta + _momentum * prevDelta;
			}

			// Weights
			for (coUint l = 0; l < nbInputs; ++l)
			{
				coFloat& curDelta = weightDeltas[weightIndex];
				const coFloat prevDelta = curDelta;
				curDelta = _learnRate * gradient * layerInputs[l];
				weightBuffer[weightIndex] += curDelta + _momentum * prevDelta;
				++weightIndex;
			}
		}

		layerInputs = trainingLayer->outputs;
	}
}

coResult coTrain(coNeuralTrainingNet& _trainingNet, const coNeuralDataSet& _dataSet, coFloat _targetError, coUint _nbMaxEpochs)
{
	// References:
	// - "On Large-Batch Training for Deep Learning: Generalization Gap and Sharp Minima" (arXiv:1609.04836).

	coTRY(_targetError >= 0.0f, nullptr);

	const coFloat learnRate = 0.4f;
	const coFloat momentum = 0.9f;

	//coTRY(learnRate < 1.0f, nullptr);

	coNeuralNet* net = _trainingNet.GetNet();
	coTRY(net, nullptr);

	const coUint nbNetInputs = net->GetNbInputs();
	const coUint nbNetOutputs = net->GetNbOutputs();

	// Init weights
	coUint32 seed = 777777777;
	coRandomizeWeightsAndBiases(*net, seed);

	// Training layers
	const coArray<coNeuralTrainingLayer*>& trainingLayers = _trainingNet.GetTrainingLayers();

	coFloat err;
	coUint nbEpochs = 0;
	do
	{
		err = 0.0f;
		for (coUint s = 0; s < _dataSet.nbSamples; ++s)
		{
			coArray<coFloat> sampleInputs(_dataSet.inputs.data + s * nbNetInputs, nbNetInputs);
			coArray<coFloat> sampleOutputs(_dataSet.outputs.data + s * nbNetOutputs, nbNetOutputs);

			coComputeForwardPass(_trainingNet, sampleInputs);
			coComputeGradients(_trainingNet, sampleOutputs);
			coUpdateWeights(_trainingNet, learnRate, momentum, sampleInputs);

			// Compute error
			{
				coNeuralTrainingLayer* lastTrainingLayer = coBack(trainingLayers);
				const coArray<coFloat>& outputs = lastTrainingLayer->outputs;
				for (coUint i = 0; i < nbNetOutputs; ++i)
				{
					err += coPow2(sampleOutputs[i] - outputs[i]);
				}
			}
		}

		coASSERT(_dataSet.nbSamples);
		err /= coFloat(_dataSet.nbSamples);
		err *= 0.5f;

		++nbEpochs;
	} while (err > _targetError && nbEpochs < _nbMaxEpochs);

	return true;
}
<commit_msg>Refactored error computing.<commit_after>// Copyright(c) 2016 Jounayd Id Salah
// Distributed under the MIT License (See accompanying file LICENSE.md file or copy at http://opensource.org/licenses/MIT).
#include "neural/pch.h"
#include "neural/process/training/coNeuralTrainingNet_f.h"
#include "neural/process/training/coNeuralTrainingNet.h"
#include "neural/process/training/coNeuralTrainingLayer.h"
#include "neural/process/training/coNeuralDataSet.h"
#include "neural/process/coNeuralLayer_f.h"
#include "neural/process/coNeuralNet_f.h"
#include "neural/process/coNeuralSigmoid_f.h"
#include "neural/network/coNeuralNet.h"
#include "neural/network/coNeuralLayer.h"
#include "lang/result/coResult_f.h"

void coComputeForwardPass(coNeuralTrainingNet& _trainingNet, const coArray<coFloat>& _targetInputs)
{
	coArray<coFloat> inputs = _targetInputs;
	for (coNeuralTrainingLayer* trainingLayer : _trainingNet.GetTrainingLayers())
	{
		coNeuralLayer* layer = trainingLayer->layer;
		coComputeNeuralOutputs(*layer, inputs, trainingLayer->outputs);
		inputs = trainingLayer->outputs;
	}
}

void coComputeGradients(coNeuralTrainingNet& _trainingNet, const coArray<coFloat>& _targetOutputs)
{
	const coArray<coNeuralTrainingLayer*>& trainingLayers = _trainingNet.GetTrainingLayers();

	// Last layer
	{
		coNeuralTrainingLayer* trainingLayer = coBack(trainingLayers);
		const coUint nbOutputs = trainingLayer->outputs.count;
		coArray<coFloat>& gradients = trainingLayer->gradients;
		const coArray<coFloat>& values = trainingLayer->outputs;
		for (coUint i = 0; i < nbOutputs; ++i)
		{
			const coFloat value = values[i];
			const coFloat targetValue = _targetOutputs[i];
			const coFloat error = targetValue - value;
			gradients[i] = error * coComputeNeuralSigmoidDerivative(value);
		}
	}

	// Rest of the layers
	for (coInt j = trainingLayers.count - 2; j >= 0; --j)
	{
		coNeuralTrainingLayer* curTrainingLayer = trainingLayers[j];
		coNeuralTrainingLayer* nextTrainingLayer = trainingLayers[j + 1];
		coArray<coFloat>& curGradients = curTrainingLayer->gradients;
		const coArray<coFloat>& nextGradients = nextTrainingLayer->gradients;
		const coArray<coFloat>& curOutputs = curTrainingLayer->outputs;

		const coNeuralLayer* curData = curTrainingLayer->layer;
		const coNeuralLayer* nextData = nextTrainingLayer->layer;
		const coArray<coFloat>& nextWeights = nextData->GetWeightBuffer();
		const coUint nbCurOutputs = curData->GetNbOutputs();
		const coUint nbNextOutputs = nextData->GetNbOutputs();
		for (coUint k = 0; k < nbCurOutputs; ++k)
		{
			coFloat sum = 0.0f;
			for (coUint l = 0; l < nbNextOutputs; ++l)
			{
				const coUint weightIndex = l * nbCurOutputs + k;
				sum += nextGradients[l] * nextWeights[weightIndex];
			}
			const coFloat output = curOutputs[k];
			curGradients[k] = sum * coComputeNeuralSigmoidDerivative(output);
		}
	}
}

void coUpdateWeights(coNeuralTrainingNet& _trainingNet, coFloat _learnRate, coFloat _momentum, const coArray<coFloat>& _inputs)
{
	const coArray<coNeuralTrainingLayer*>& trainingLayers = _trainingNet.GetTrainingLayers();
	coArray<coFloat> layerInputs(_inputs);
	for (coNeuralTrainingLayer* trainingLayer : trainingLayers)
	{
		coNeuralLayer* data = trainingLayer->layer;
		const coUint nbInputs = data->GetNbInputs();
		const coUint nbOutputs = data->GetNbOutputs();
		coArray<coFloat>& biasBuffer = data->GetBiasBuffer();
		coArray<coFloat>& weightBuffer = data->GetWeightBuffer();
		coArray<coFloat>& biasDeltas = trainingLayer->biasDeltas;
		coArray<coFloat>& weightDeltas = trainingLayer->weightDeltas;
		const coArray<coFloat>& gradients = trainingLayer->gradients;
		coUint weightIndex = 0;
		for (coUint k = 0; k < nbOutputs; ++k)
		{
			const coFloat gradient = gradients[k];

			// Biases
			{
				coFloat& curDelta = biasDeltas[k];
				const coFloat prevDelta = curDelta;
				curDelta = _learnRate * gradient;
				biasBuffer[k] += curDelta + _momentum * prevDelta;
			}

			// Weights
			for (coUint l = 0; l < nbInputs; ++l)
			{
				coFloat& curDelta = weightDeltas[weightIndex];
				const coFloat prevDelta = curDelta;
				curDelta = _learnRate * gradient * layerInputs[l];
				weightBuffer[weightIndex] += curDelta + _momentum * prevDelta;
				++weightIndex;
			}
		}

		layerInputs = trainingLayer->outputs;
	}
}

coFloat coComputeErrors(const coNeuralTrainingNet& _trainingNet, const coArray<coFloat>& _targetOutputs)
{
	coFloat error = 0.0f;
	const coArray<coNeuralTrainingLayer*>& trainingLayers = _trainingNet.GetTrainingLayers();
	coNeuralTrainingLayer* lastTrainingLayer = coBack(trainingLayers);
	const coArray<coFloat>& outputs = lastTrainingLayer->outputs;
	coASSERT(outputs.count == _targetOutputs.count);
	const coUint nbOutputs = lastTrainingLayer->outputs.count;
	for (coUint i = 0; i < nbOutputs; ++i)
	{
		error += coPow2(_targetOutputs[i] - outputs[i]);
	}
	return error;
}

coResult coTrain(coNeuralTrainingNet& _trainingNet, const coNeuralDataSet& _dataSet, coFloat _targetError, coUint _nbMaxEpochs)
{
	// References:
	// - "On Large-Batch Training for Deep Learning: Generalization Gap and Sharp Minima" (arXiv:1609.04836).

	coTRY(_targetError >= 0.0f, nullptr);

	const coFloat learnRate = 0.4f;
	const coFloat momentum = 0.9f;

	//coTRY(learnRate < 1.0f, nullptr);

	coNeuralNet* net = _trainingNet.GetNet();
	coTRY(net, nullptr);

	const coUint nbNetInputs = net->GetNbInputs();
	const coUint nbNetOutputs = net->GetNbOutputs();

	coFloat err;
	coUint nbEpochs = 0;
	do
	{
		err = 0.0f;
		for (coUint s = 0; s < _dataSet.nbSamples; ++s)
		{
			coArray<coFloat> sampleInputs(_dataSet.inputs.data + s * nbNetInputs, nbNetInputs);
			coArray<coFloat> sampleOutputs(_dataSet.outputs.data + s * nbNetOutputs, nbNetOutputs);

			coComputeForwardPass(_trainingNet, sampleInputs);
			coComputeGradients(_trainingNet, sampleOutputs);
			coUpdateWeights(_trainingNet, learnRate, momentum, sampleInputs);
			err += coComputeErrors(_trainingNet, sampleOutputs);
		}

		coASSERT(_dataSet.nbSamples);
		err /= coFloat(_dataSet.nbSamples);
		err *= 0.5f;

		++nbEpochs;
	} while (err > _targetError && nbEpochs < _nbMaxEpochs);

	return true;
}
<|endoftext|>


<commit_before>/**
 *  @author Koen Wolters <koen.wolters@cern.ch>
 */
#ifndef ALLPIX_MODULE_GEOMETRY_CONSTRUCTION_DETECTOR_MODEL_G4_H
#define ALLPIX_MODULE_GEOMETRY_CONSTRUCTION_DETECTOR_MODEL_G4_H

#include <G4LogicalVolume.hh>
#include <G4VPhysicalVolume.hh>

#include "BumpsParameterizationG4.hpp"

namespace allpix {

    class DetectorModelG4 {
    public:
        // FIXME: should use smart pointer instead of deleting here
        ~DetectorModelG4() {
            delete wrapper_log;
            delete wrapper_phys;
            delete PCB_log;
            delete PCB_phys;
            delete chip_log;
            delete chip_phys;
            delete bumps_log;
            delete bumps_phys;
            delete bumps_cell_log;
            delete guard_rings_log;
            delete guard_rings_phys;
            delete slice_log;
        }

        // Wrapper for the whole detector in the world model (invisible)
        G4LogicalVolume* wrapper_log;
        G4VPhysicalVolume* wrapper_phys;

        // Volume containing the PCB for all pixels (green)
        G4LogicalVolume* PCB_log;
        G4VPhysicalVolume* PCB_phys;

        // Volume containing the sensitive pixels (blue)
        G4LogicalVolume* box_log;
        G4VPhysicalVolume* box_phys;

        // Volume containing the chips for each sensor (gray)
        G4LogicalVolume* chip_log;
        G4VPhysicalVolume* chip_phys;

        // Volume box containing the bumps between the pixel and the chip (yellow)
        G4LogicalVolume* bumps_log;
        G4VPhysicalVolume* bumps_phys;

        // G4LogicalVolume*    bumps_slice_log;
        // Link to replicas of the individual bonds in bumps volume box (used with the parameterization)
        G4LogicalVolume* bumps_cell_log;

        // Volume containing the guard rings around the sensor (green)
        G4LogicalVolume* guard_rings_log;
        G4VPhysicalVolume* guard_rings_phys;

        // G4LogicalVolume*    coverlayer_log;
        // G4VPhysicalVolume*  coverlayer_phys;

        // A row containing multiple replicas of pixels in a set of slices
        G4LogicalVolume* slice_log;
        // A list of cells containing a single pixel in in a slice (in the sensitive sensor box)
        G4LogicalVolume* pixel_log;

        // FIXME: do we want to save this one?
        std::unique_ptr<BumpsParameterizationG4> parameterization_;
    };
} // namespace allpix

#endif
<commit_msg>do not delete object yet as it causes problems<commit_after>/**
 *  @author Koen Wolters <koen.wolters@cern.ch>
 */
#ifndef ALLPIX_MODULE_GEOMETRY_CONSTRUCTION_DETECTOR_MODEL_G4_H
#define ALLPIX_MODULE_GEOMETRY_CONSTRUCTION_DETECTOR_MODEL_G4_H

#include <G4LogicalVolume.hh>
#include <G4VPhysicalVolume.hh>

#include "BumpsParameterizationG4.hpp"

namespace allpix {

    class DetectorModelG4 {
    public:
        // FIXME: should use smart pointer instead of deleting here
        ~DetectorModelG4() {
            // FIXME: causes problems, investigate better
            /*delete wrapper_log;
            delete wrapper_phys;
            delete PCB_log;
            delete PCB_phys;
            delete chip_log;
            delete chip_phys;
            delete bumps_log;
            delete bumps_phys;
            delete bumps_cell_log;
            delete guard_rings_log;
            delete guard_rings_phys;
            delete slice_log;*/
        }

        // Wrapper for the whole detector in the world model (invisible)
        G4LogicalVolume* wrapper_log;
        G4VPhysicalVolume* wrapper_phys;

        // Volume containing the PCB for all pixels (green)
        G4LogicalVolume* PCB_log;
        G4VPhysicalVolume* PCB_phys;

        // Volume containing the sensitive pixels (blue)
        G4LogicalVolume* box_log;
        G4VPhysicalVolume* box_phys;

        // Volume containing the chips for each sensor (gray)
        G4LogicalVolume* chip_log;
        G4VPhysicalVolume* chip_phys;

        // Volume box containing the bumps between the pixel and the chip (yellow)
        G4LogicalVolume* bumps_log;
        G4VPhysicalVolume* bumps_phys;

        // G4LogicalVolume*    bumps_slice_log;
        // Link to replicas of the individual bonds in bumps volume box (used with the parameterization)
        G4LogicalVolume* bumps_cell_log;

        // Volume containing the guard rings around the sensor (green)
        G4LogicalVolume* guard_rings_log;
        G4VPhysicalVolume* guard_rings_phys;

        // G4LogicalVolume*    coverlayer_log;
        // G4VPhysicalVolume*  coverlayer_phys;

        // A row containing multiple replicas of pixels in a set of slices
        G4LogicalVolume* slice_log;
        // A list of cells containing a single pixel in in a slice (in the sensitive sensor box)
        G4LogicalVolume* pixel_log;

        // FIXME: do we want to save this one?
        std::unique_ptr<BumpsParameterizationG4> parameterization_;
    };
} // namespace allpix

#endif
<|endoftext|>


<commit_before>#include "dns_communicator_factory.h"
#include "../../connector.h"

#include <chrono>

namespace network
{

dns_connector_factory::dns_connector_factory(boost::asio::io_service& io, std::chrono::milliseconds connector_timeout)
	: io{io}
	, conn_timeout{connector_timeout}
	, dead{std::make_shared<bool>(false)}
{}

dns_connector_factory::~dns_connector_factory()
{
	*dead = true;
}

void dns_connector_factory::get_connector(const std::string& address, uint16_t port, bool tls,
	connector_callback_t connector_cb, error_callback_t error_cb)
{
	if ( stopping ) return error_cb(1);

	dns_resolver(address, port, tls, std::move(connector_cb), std::move(error_cb));
}

void dns_connector_factory::dns_resolver(const std::string& address, uint16_t port, bool tls,
	connector_callback_t connector_cb, error_callback_t error_cb)
{
	std::shared_ptr<boost::asio::ip::tcp::resolver> r = std::make_shared<boost::asio::ip::tcp::resolver>(io);
	if(!port)
		port = tls ? 443 : 80;

	LOGTRACE("resolving ", address, "with port ", port);
	boost::asio::ip::tcp::resolver::query q( address,  std::to_string(port) );
	auto resolve_timer =
		std::make_shared<boost::asio::deadline_timer>(io);
	resolve_timer->expires_from_now(boost::posix_time::milliseconds(resolve_timeout));
	resolve_timer->async_wait([r, resolve_timer](const boost::system::error_code &ec)
	{
		if ( ! ec ) r->cancel();
	});

	r->async_resolve(q,
		[this, r, tls, connector_cb = std::move(connector_cb), error_cb = std::move(error_cb), resolve_timer, dead=dead]
		(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator iterator)
		{
			if(*dead)
				return;

			resolve_timer->cancel();
			if(ec || iterator == boost::asio::ip::tcp::resolver::iterator())
			{
				LOGERROR(ec.message());
				return error_cb(3);
			}
			/** No error: go on connecting */
			LOGTRACE( "tls is: ", tls );
			if ( tls )
			{
				return endpoint_connect(std::move(iterator),
					std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>
						(io, ctx ),
							std::move(connector_cb), std::move(error_cb));
			}
			else
				return endpoint_connect(std::move(iterator),
					std::make_shared<boost::asio::ip::tcp::socket>
						(io),
							std::move(connector_cb), std::move(error_cb));
		});
}

void dns_connector_factory::endpoint_connect(boost::asio::ip::tcp::resolver::iterator it,
	std::shared_ptr<boost::asio::ip::tcp::socket> socket, connector_callback_t connector_cb, error_callback_t error_cb)
{
	if(it == boost::asio::ip::tcp::resolver::iterator() || stopping) //finished
		return error_cb(3);
	auto&& endpoint = *it;
	auto connect_timer =
		std::make_shared<boost::asio::deadline_timer>(io);
	connect_timer->expires_from_now(boost::posix_time::milliseconds(connect_timeout));
	connect_timer->async_wait([socket, connect_timer](const boost::system::error_code &ec)
	{
		if(!ec) socket->cancel();
	});
	++it;
	socket->async_connect(endpoint,
		[this, it = std::move(it), socket, connector_cb = std::move(connector_cb),
			error_cb = std::move(error_cb), connect_timer, dead=dead]
		(const boost::system::error_code &ec)
		{
			if(*dead)
				return;

			connect_timer->cancel();
			if ( ec )
			{
				LOGERROR(ec.message());
				return endpoint_connect(std::move(it), std::move(socket), std::move(connector_cb), std::move(error_cb));
			}

			auto connector = std::make_shared<server::connector<server::tcp_socket>>(std::move(socket));
			connector->set_timeout(conn_timeout);
			connector_cb(std::move(connector));
		});
}

void dns_connector_factory::endpoint_connect(boost::asio::ip::tcp::resolver::iterator it,
	std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> stream,
	connector_callback_t connector_cb, error_callback_t error_cb)
{
	if ( it == boost::asio::ip::tcp::resolver::iterator() || stopping ) //finished
		return error_cb(3);

	stream->set_verify_mode( boost::asio::ssl::verify_none );

	auto connect_timer =
		std::make_shared<boost::asio::deadline_timer>(io);
	connect_timer->expires_from_now(boost::posix_time::milliseconds(connect_timeout));
	connect_timer->async_wait([stream, connect_timer](const boost::system::error_code &ec)
	{
		if ( !ec ) //stream->cancel();
		{
			boost::system::error_code sec;
			stream->shutdown( sec );
		}
	});

	boost::asio::async_connect( stream->lowest_layer(), it, [ this, stream, connector_cb = std::move(connector_cb),
		error_cb = std::move(error_cb), connect_timer=std::move(connect_timer), dead = dead ]( const boost::system::error_code &ec,
			boost::asio::ip::tcp::resolver::iterator it )
	{
		if(*dead)
			return;

		if ( ec )
		{
			LOGERROR(ec.message());
			//++it ?
			return endpoint_connect(std::move(it), std::move(stream), std::move(connector_cb), std::move(error_cb));
		}
		stream->async_handshake( boost::asio::ssl::stream_base::client,
			[ this, stream, connector_cb = std::move(connector_cb), error_cb = std::move(error_cb), connect_timer=std::move(connect_timer) ]
				( const boost::system::error_code &ec )
				{
					connect_timer->cancel();
					if ( ec )
					{
						LOGERROR( ec.message() );
						return;
					}
					auto connector = std::make_shared<server::connector<server::ssl_socket>>(std::move(stream));
					connector->set_timeout(conn_timeout);
					connector_cb(std::move(connector));
				});
	});
}

namespace
{
static const std::string NGHTTP2_H2_ALPN = "\x2h2";
static const std::string NGHTTP2_H2_14_ALPN = "\x5h2-14";
static const std::string NGHTTP2_H2_16_ALPN = "\x5h2-16";

bool select_proto(const unsigned char **out, unsigned char *outlen,
				  const unsigned char *in, unsigned int inlen,
				  const std::string &key) {
  for (auto p = in, end = in + inlen; p + key.size() <= end; p += *p + 1) {
	if (std::equal(std::begin(key), std::end(key), p)) {
	  *out = p + 1;
	  *outlen = *p;
	  return true;
	}
  }
  return false;
}

bool select_h2(const unsigned char **out, unsigned char *outlen,
			   const unsigned char *in, unsigned int inlen) {
  return select_proto(out, outlen, in, inlen, NGHTTP2_H2_ALPN) ||
		 select_proto(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN) ||
		 select_proto(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN);
}

int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
								unsigned char *outlen, const unsigned char *in,
								unsigned int inlen, void *arg) {
  if (!select_h2(const_cast<const unsigned char **>(out), outlen, in,
					   inlen)) {
	return SSL_TLSEXT_ERR_NOACK;
  }
  return SSL_TLSEXT_ERR_OK;
}

std::vector<unsigned char> get_default_alpn() {

  auto res = std::vector<unsigned char>(NGHTTP2_H2_ALPN.size() +
										NGHTTP2_H2_16_ALPN.size() +
										NGHTTP2_H2_14_ALPN.size());
  auto p = std::begin(res);

  p = std::copy_n(std::begin(NGHTTP2_H2_ALPN), NGHTTP2_H2_ALPN.size(), p);
  p = std::copy_n(std::begin(NGHTTP2_H2_16_ALPN), NGHTTP2_H2_16_ALPN.size(), p);
  p = std::copy_n(std::begin(NGHTTP2_H2_14_ALPN), NGHTTP2_H2_14_ALPN.size(), p);

  return res;
}

} // unnamed namespace

boost::asio::ssl::context dns_connector_factory::init_ssl_ctx()
{
	boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
	auto ctx_h = ctx.native_handle();
	SSL_CTX_set_next_proto_select_cb(ctx_h, client_select_next_proto_cb, nullptr);
	auto proto_list = get_default_alpn();
	SSL_CTX_set_alpn_protos(ctx_h, proto_list.data(), proto_list.size());
	return ctx;
}

thread_local boost::asio::ssl::context dns_connector_factory::ctx{dns_connector_factory::init_ssl_ctx()};

}
<commit_msg>added a comment<commit_after>#include "dns_communicator_factory.h"
#include "../../connector.h"

#include <chrono>

namespace network
{

dns_connector_factory::dns_connector_factory(boost::asio::io_service& io, std::chrono::milliseconds connector_timeout)
	: io{io}
	, conn_timeout{connector_timeout}
	, dead{std::make_shared<bool>(false)}
{}

dns_connector_factory::~dns_connector_factory()
{
	*dead = true;
}

void dns_connector_factory::get_connector(const std::string& address, uint16_t port, bool tls,
	connector_callback_t connector_cb, error_callback_t error_cb)
{
	if ( stopping ) return error_cb(1);

	dns_resolver(address, port, tls, std::move(connector_cb), std::move(error_cb));
}

void dns_connector_factory::dns_resolver(const std::string& address, uint16_t port, bool tls,
	connector_callback_t connector_cb, error_callback_t error_cb)
{
	std::shared_ptr<boost::asio::ip::tcp::resolver> r = std::make_shared<boost::asio::ip::tcp::resolver>(io);
	if(!port)
		port = tls ? 443 : 80;

	LOGTRACE("resolving ", address, "with port ", port);
	boost::asio::ip::tcp::resolver::query q( address,  std::to_string(port) );
	auto resolve_timer =
		std::make_shared<boost::asio::deadline_timer>(io);
	resolve_timer->expires_from_now(boost::posix_time::milliseconds(resolve_timeout));
	resolve_timer->async_wait([r, resolve_timer](const boost::system::error_code &ec)
	{
		if ( ! ec ) r->cancel();
	});

	r->async_resolve(q,
		[this, r, tls, connector_cb = std::move(connector_cb), error_cb = std::move(error_cb), resolve_timer, dead=dead]
		(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator iterator)
		{
			if(*dead)
				return;

			resolve_timer->cancel();
			if(ec || iterator == boost::asio::ip::tcp::resolver::iterator())
			{
				LOGERROR(ec.message());
				return error_cb(3);
			}
			/** No error: go on connecting */
			LOGTRACE( "tls is: ", tls );
			if ( tls )
			{
				return endpoint_connect(std::move(iterator),
					std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>
						(io, ctx ),
							std::move(connector_cb), std::move(error_cb));
			}
			else
				return endpoint_connect(std::move(iterator),
					std::make_shared<boost::asio::ip::tcp::socket>
						(io),
							std::move(connector_cb), std::move(error_cb));
		});
}

void dns_connector_factory::endpoint_connect(boost::asio::ip::tcp::resolver::iterator it,
	std::shared_ptr<boost::asio::ip::tcp::socket> socket, connector_callback_t connector_cb, error_callback_t error_cb)
{
	if(it == boost::asio::ip::tcp::resolver::iterator() || stopping) //finished
		return error_cb(3);
	auto&& endpoint = *it;
	auto connect_timer =
		std::make_shared<boost::asio::deadline_timer>(io);
	connect_timer->expires_from_now(boost::posix_time::milliseconds(connect_timeout));
	connect_timer->async_wait([socket, connect_timer](const boost::system::error_code &ec)
	{
		if(!ec) socket->cancel();
	});
	++it;
	socket->async_connect(endpoint,
		[this, it = std::move(it), socket, connector_cb = std::move(connector_cb),
			error_cb = std::move(error_cb), connect_timer, dead=dead]
		(const boost::system::error_code &ec)
		{
			if(*dead)
				return;

			connect_timer->cancel();
			if ( ec )
			{
				LOGERROR(ec.message());
				return endpoint_connect(std::move(it), std::move(socket), std::move(connector_cb), std::move(error_cb));
			}

			auto connector = std::make_shared<server::connector<server::tcp_socket>>(std::move(socket));
			connector->set_timeout(conn_timeout);
			connector_cb(std::move(connector));
		});
}

void dns_connector_factory::endpoint_connect(boost::asio::ip::tcp::resolver::iterator it,
	std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> stream,
	connector_callback_t connector_cb, error_callback_t error_cb)
{
	if ( it == boost::asio::ip::tcp::resolver::iterator() || stopping ) //finished
		return error_cb(3);

	stream->set_verify_mode( boost::asio::ssl::verify_none );

	auto connect_timer =
		std::make_shared<boost::asio::deadline_timer>(io);
	connect_timer->expires_from_now(boost::posix_time::milliseconds(connect_timeout));
	connect_timer->async_wait([stream, connect_timer](const boost::system::error_code &ec)
	{
		if ( !ec ) //stream->cancel();
		{
			boost::system::error_code sec;
			stream->shutdown( sec );
		}
	});

	boost::asio::async_connect( stream->lowest_layer(), it, [ this, stream, connector_cb = std::move(connector_cb),
		error_cb = std::move(error_cb), connect_timer=std::move(connect_timer), dead = dead ]( const boost::system::error_code &ec,
			boost::asio::ip::tcp::resolver::iterator it )
	{
		if(*dead)
			return;

		if ( ec )
		{
			LOGERROR(ec.message());
			//++it ?
			return endpoint_connect(std::move(it), std::move(stream), std::move(connector_cb), std::move(error_cb));
		}
		stream->async_handshake( boost::asio::ssl::stream_base::client,
			[ this, stream, connector_cb = std::move(connector_cb), error_cb = std::move(error_cb), connect_timer=std::move(connect_timer) ]
				( const boost::system::error_code &ec )
				{
					connect_timer->cancel();
					if ( ec )
					{
						LOGERROR( ec.message() );
						return;
					}
					auto connector = std::make_shared<server::connector<server::ssl_socket>>(std::move(stream));
					connector->set_timeout(conn_timeout);
					connector_cb(std::move(connector));
				});
	});
}

namespace
{
static const std::string NGHTTP2_H2_ALPN = "\x2h2";
static const std::string NGHTTP2_H2_14_ALPN = "\x5h2-14";
static const std::string NGHTTP2_H2_16_ALPN = "\x5h2-16";

bool select_proto(const unsigned char **out, unsigned char *outlen,
				  const unsigned char *in, unsigned int inlen,
				  const std::string &key) {
  for (auto p = in, end = in + inlen; p + key.size() <= end; p += *p + 1) {
	if (std::equal(std::begin(key), std::end(key), p)) {
	  *out = p + 1;
	  *outlen = *p;
	  return true;
	}
  }
  return false;
}

bool select_h2(const unsigned char **out, unsigned char *outlen,
			   const unsigned char *in, unsigned int inlen) {
  return select_proto(out, outlen, in, inlen, NGHTTP2_H2_ALPN) ||
		 select_proto(out, outlen, in, inlen, NGHTTP2_H2_16_ALPN) ||
		 select_proto(out, outlen, in, inlen, NGHTTP2_H2_14_ALPN);
}

int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
								unsigned char *outlen, const unsigned char *in,
								unsigned int inlen, void *arg) {
  if (!select_h2(const_cast<const unsigned char **>(out), outlen, in,
					   inlen)) {
	return SSL_TLSEXT_ERR_NOACK;
  }
  return SSL_TLSEXT_ERR_OK;
}

std::vector<unsigned char> get_default_alpn() {

  auto res = std::vector<unsigned char>(NGHTTP2_H2_ALPN.size() +
										NGHTTP2_H2_16_ALPN.size() +
										NGHTTP2_H2_14_ALPN.size());
  auto p = std::begin(res);

  p = std::copy_n(std::begin(NGHTTP2_H2_ALPN), NGHTTP2_H2_ALPN.size(), p);
  p = std::copy_n(std::begin(NGHTTP2_H2_16_ALPN), NGHTTP2_H2_16_ALPN.size(), p);
  p = std::copy_n(std::begin(NGHTTP2_H2_14_ALPN), NGHTTP2_H2_14_ALPN.size(), p);

  return res;
}

} // unnamed namespace

boost::asio::ssl::context dns_connector_factory::init_ssl_ctx()
{
	boost::asio::ssl::context ctx{boost::asio::ssl::context::sslv23};
	// enable NPN for http2
	auto ctx_h = ctx.native_handle();
	SSL_CTX_set_next_proto_select_cb(ctx_h, client_select_next_proto_cb, nullptr);
	// enable ALPN for http2
	auto proto_list = get_default_alpn();
	SSL_CTX_set_alpn_protos(ctx_h, proto_list.data(), proto_list.size());
	return ctx;
}

thread_local boost::asio::ssl::context dns_connector_factory::ctx{dns_connector_factory::init_ssl_ctx()};

}
<|endoftext|>