<commit_before>//===-- X86FixupBWInsts.cpp - Fixup Byte or Word instructions -----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
/// This file defines the pass that looks through the machine instructions
/// late in the compilation, and finds byte or word instructions that
/// can be profitably replaced with 32 bit instructions that give equivalent
/// results for the bits of the results that are used. There are two possible
/// reasons to do this.
///
/// One reason is to avoid false-dependences on the upper portions
/// of the registers.  Only instructions that have a destination register
/// which is not in any of the source registers can be affected by this.
/// Any instruction where one of the source registers is also the destination
/// register is unaffected, because it has a true dependence on the source
/// register already.  So, this consideration primarily affects load
/// instructions and register-to-register moves.  It would
/// seem like cmov(s) would also be affected, but because of the way cmov is
/// really implemented by most machines as reading both the destination and
/// and source regsters, and then "merging" the two based on a condition,
/// it really already should be considered as having a true dependence on the
/// destination register as well.
///
/// The other reason to do this is for potential code size savings.  Word
/// operations need an extra override byte compared to their 32 bit
/// versions. So this can convert many word operations to their larger
/// size, saving a byte in encoding. This could introduce partial register
/// dependences where none existed however.  As an example take:
///   orw  ax, $0x1000
///   addw ax, $3
/// now if this were to get transformed into
///   orw  ax, $1000
///   addl eax, $3
/// because the addl encodes shorter than the addw, this would introduce
/// a use of a register that was only partially written earlier.  On older
/// Intel processors this can be quite a performance penalty, so this should
/// probably only be done when it can be proven that a new partial dependence
/// wouldn't be created, or when your know a newer processor is being
/// targeted, or when optimizing for minimum code size.
///
//===----------------------------------------------------------------------===//

#include "X86.h"
#include "X86InstrInfo.h"
#include "X86Subtarget.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetInstrInfo.h"
using namespace llvm;

#define DEBUG_TYPE "x86-fixup-bw-insts"

// Option to allow this optimization pass to have fine-grained control.
// This is turned off by default so as not to affect a large number of
// existing lit tests.
static cl::opt<bool>
    FixupBWInsts("fixup-byte-word-insts",
                 cl::desc("Change byte and word instructions to larger sizes"),
                 cl::init(false), cl::Hidden);

namespace {
class FixupBWInstPass : public MachineFunctionPass {
  static char ID;

  const char *getPassName() const override {
    return "X86 Byte/Word Instruction Fixup";
  }

  /// \brief Loop over all of the instructions in the basic block
  /// replacing applicable byte or word instructions with better
  /// alternatives.
  void processBasicBlock(MachineFunction &MF, MachineBasicBlock &MBB);

  /// \brief This sets the \p SuperDestReg to the 32 bit super reg
  /// of the original destination register of the MachineInstr
  /// passed in. It returns true if that super register is dead
  /// just prior to \p OrigMI, and false if not.
  /// \pre OrigDestSize must be 8 or 16.
  bool getSuperRegDestIfDead(MachineInstr *OrigMI, unsigned OrigDestSize,
                             unsigned &SuperDestReg) const;

  /// \brief Change the MachineInstr \p MI into the equivalent extending load
  /// to 32 bit register if it is safe to do so.  Return the replacement
  /// instruction if OK, otherwise return nullptr.
  /// \pre OrigDestSize must be 8 or 16.
  MachineInstr *tryReplaceLoad(unsigned New32BitOpcode, unsigned OrigDestSize,
                               MachineInstr *MI) const;

public:
  FixupBWInstPass() : MachineFunctionPass(ID) {}

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.addRequired<MachineLoopInfo>(); // Machine loop info is used to
                                       // guide some heuristics.
    MachineFunctionPass::getAnalysisUsage(AU);
  }

  /// \brief Loop over all of the basic blocks,
  /// replacing byte and word instructions by equivalent 32 bit instructions
  /// where performance or code size can be improved.
  bool runOnMachineFunction(MachineFunction &MF) override;

private:
  MachineFunction *MF;

  /// Machine instruction info used throughout the class.
  const X86InstrInfo *TII;

  /// Local member for function's OptForSize attribute.
  bool OptForSize;

  /// Machine loop info used for guiding some heruistics.
  MachineLoopInfo *MLI;

  /// Register Liveness information after the current instruction.
  LivePhysRegs LiveRegs;
};
char FixupBWInstPass::ID = 0;
}

FunctionPass *llvm::createX86FixupBWInsts() { return new FixupBWInstPass(); }

bool FixupBWInstPass::runOnMachineFunction(MachineFunction &MF) {
  if (!FixupBWInsts)
    return false;

  this->MF = &MF;
  TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
  OptForSize = MF.getFunction()->optForSize();
  MLI = &getAnalysis<MachineLoopInfo>();
  LiveRegs.init(&TII->getRegisterInfo());

  DEBUG(dbgs() << "Start X86FixupBWInsts\n";);

  // Process all basic blocks.
  for (auto &MBB : MF)
    processBasicBlock(MF, MBB);

  DEBUG(dbgs() << "End X86FixupBWInsts\n";);

  return true;
}

// TODO: This method of analysis can miss some legal cases, because the
// super-register could be live into the address expression for a memory
// reference for the instruction, and still be killed/last used by the
// instruction. However, the existing query interfaces don't seem to
// easily allow that to be checked.
//
// What we'd really like to know is whether after OrigMI, the
// only portion of SuperDestReg that is alive is the portion that
// was the destination register of OrigMI.
bool FixupBWInstPass::getSuperRegDestIfDead(MachineInstr *OrigMI,
                                            unsigned OrigDestSize,
                                            unsigned &SuperDestReg) const {

  unsigned OrigDestReg = OrigMI->getOperand(0).getReg();
  SuperDestReg = getX86SubSuperRegister(OrigDestReg, 32);

  // Make sure that the sub-register that this instruction has as its
  // destination is the lowest order sub-register of the super-register.
  // If it isn't, then the register isn't really dead even if the
  // super-register is considered dead.
  // This test works because getX86SubSuperRegister returns the low portion
  // register by default when getting a sub-register, so if that doesn't
  // match the original destination register, then the original destination
  // register must not have been the low register portion of that size.
  if (getX86SubSuperRegister(SuperDestReg, OrigDestSize) != OrigDestReg)
    return false;

  if (LiveRegs.contains(SuperDestReg))
    return false;

  if (OrigDestSize == 8) {
    // In the case of byte registers, we also have to check that the upper
    // byte register is also dead. That is considered to be independent of
    // whether the super-register is dead.
    unsigned UpperByteReg = getX86SubSuperRegister(SuperDestReg, 8, true);

    if (LiveRegs.contains(UpperByteReg))
      return false;
  }

  return true;
}

MachineInstr *FixupBWInstPass::tryReplaceLoad(unsigned New32BitOpcode,
                                              unsigned OrigDestSize,
                                              MachineInstr *MI) const {
  unsigned NewDestReg;

  // We are going to try to rewrite this load to a larger zero-extending
  // load.  This is safe if all portions of the 32 bit super-register
  // of the original destination register, except for the original destination
  // register are dead. getSuperRegDestIfDead checks that.
  if (!getSuperRegDestIfDead(MI, OrigDestSize, NewDestReg))
    return nullptr;

  // Safe to change the instruction.
  MachineInstrBuilder MIB =
      BuildMI(*MF, MI->getDebugLoc(), TII->get(New32BitOpcode), NewDestReg);

  unsigned NumArgs = MI->getNumOperands();
  for (unsigned i = 1; i < NumArgs; ++i)
    MIB.addOperand(MI->getOperand(i));

  MIB->setMemRefs(MI->memoperands_begin(), MI->memoperands_end());

  return MIB;
}

void FixupBWInstPass::processBasicBlock(MachineFunction &MF,
                                        MachineBasicBlock &MBB) {

  // This algorithm doesn't delete the instructions it is replacing
  // right away.  By leaving the existing instructions in place, the
  // register liveness information doesn't change, and this makes the
  // analysis that goes on be better than if the replaced instructions
  // were immediately removed.
  //
  // This algorithm always creates a replacement instruction
  // and notes that and the original in a data structure, until the
  // whole BB has been analyzed.  This keeps the replacement instructions
  // from making it seem as if the larger register might be live.
  SmallVector<std::pair<MachineInstr *, MachineInstr *>, 8> MIReplacements;

  // Start computing liveness for this block. We iterate from the end to be able
  // to update this for each instruction.
  LiveRegs.clear();
  // We run after PEI, so we need to AddPristinesAndCSRs.
  LiveRegs.addLiveOuts(&MBB, /*AddPristinesAndCSRs=*/true);

  for (auto I = MBB.rbegin(); I != MBB.rend(); ++I) {
    MachineInstr *NewMI = nullptr;
    MachineInstr *MI = &*I;

    // See if this is an instruction of the type we are currently looking for.
    switch (MI->getOpcode()) {

    case X86::MOV8rm:
      // Only replace 8 bit loads with the zero extending versions if
      // in an inner most loop and not optimizing for size. This takes
      // an extra byte to encode, and provides limited performance upside.
      if (MachineLoop *ML = MLI->getLoopFor(&MBB)) {
        if (ML->begin() == ML->end() && !OptForSize)
          NewMI = tryReplaceLoad(X86::MOVZX32rm8, 8, MI);
      }
      break;

    case X86::MOV16rm:
      // Always try to replace 16 bit load with 32 bit zero extending.
      // Code size is the same, and there is sometimes a perf advantage
      // from eliminating a false dependence on the upper portion of
      // the register.
      NewMI = tryReplaceLoad(X86::MOVZX32rm16, 16, MI);
      break;

    default:
      // nothing to do here.
      break;
    }

    if (NewMI)
      MIReplacements.push_back(std::make_pair(MI, NewMI));

    // We're done with this instruction, update liveness for the next one.
    LiveRegs.stepBackward(*MI);
  }

  while (!MIReplacements.empty()) {
    MachineInstr *MI = MIReplacements.back().first;
    MachineInstr *NewMI = MIReplacements.back().second;
    MIReplacements.pop_back();
    MBB.insert(MI, NewMI);
    MBB.erase(MI);
  }
}
<commit_msg>[X86] Simplify FixupBW sub_8bit_hi-related logic. NFC.<commit_after>//===-- X86FixupBWInsts.cpp - Fixup Byte or Word instructions -----------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
/// This file defines the pass that looks through the machine instructions
/// late in the compilation, and finds byte or word instructions that
/// can be profitably replaced with 32 bit instructions that give equivalent
/// results for the bits of the results that are used. There are two possible
/// reasons to do this.
///
/// One reason is to avoid false-dependences on the upper portions
/// of the registers.  Only instructions that have a destination register
/// which is not in any of the source registers can be affected by this.
/// Any instruction where one of the source registers is also the destination
/// register is unaffected, because it has a true dependence on the source
/// register already.  So, this consideration primarily affects load
/// instructions and register-to-register moves.  It would
/// seem like cmov(s) would also be affected, but because of the way cmov is
/// really implemented by most machines as reading both the destination and
/// and source regsters, and then "merging" the two based on a condition,
/// it really already should be considered as having a true dependence on the
/// destination register as well.
///
/// The other reason to do this is for potential code size savings.  Word
/// operations need an extra override byte compared to their 32 bit
/// versions. So this can convert many word operations to their larger
/// size, saving a byte in encoding. This could introduce partial register
/// dependences where none existed however.  As an example take:
///   orw  ax, $0x1000
///   addw ax, $3
/// now if this were to get transformed into
///   orw  ax, $1000
///   addl eax, $3
/// because the addl encodes shorter than the addw, this would introduce
/// a use of a register that was only partially written earlier.  On older
/// Intel processors this can be quite a performance penalty, so this should
/// probably only be done when it can be proven that a new partial dependence
/// wouldn't be created, or when your know a newer processor is being
/// targeted, or when optimizing for minimum code size.
///
//===----------------------------------------------------------------------===//

#include "X86.h"
#include "X86InstrInfo.h"
#include "X86Subtarget.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetInstrInfo.h"
using namespace llvm;

#define DEBUG_TYPE "x86-fixup-bw-insts"

// Option to allow this optimization pass to have fine-grained control.
// This is turned off by default so as not to affect a large number of
// existing lit tests.
static cl::opt<bool>
    FixupBWInsts("fixup-byte-word-insts",
                 cl::desc("Change byte and word instructions to larger sizes"),
                 cl::init(false), cl::Hidden);

namespace {
class FixupBWInstPass : public MachineFunctionPass {
  static char ID;

  const char *getPassName() const override {
    return "X86 Byte/Word Instruction Fixup";
  }

  /// \brief Loop over all of the instructions in the basic block
  /// replacing applicable byte or word instructions with better
  /// alternatives.
  void processBasicBlock(MachineFunction &MF, MachineBasicBlock &MBB);

  /// \brief This sets the \p SuperDestReg to the 32 bit super reg
  /// of the original destination register of the MachineInstr
  /// passed in. It returns true if that super register is dead
  /// just prior to \p OrigMI, and false if not.
  bool getSuperRegDestIfDead(MachineInstr *OrigMI,
                             unsigned &SuperDestReg) const;

  /// \brief Change the MachineInstr \p MI into the equivalent extending load
  /// to 32 bit register if it is safe to do so.  Return the replacement
  /// instruction if OK, otherwise return nullptr.
  MachineInstr *tryReplaceLoad(unsigned New32BitOpcode, MachineInstr *MI) const;

public:
  FixupBWInstPass() : MachineFunctionPass(ID) {}

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.addRequired<MachineLoopInfo>(); // Machine loop info is used to
                                       // guide some heuristics.
    MachineFunctionPass::getAnalysisUsage(AU);
  }

  /// \brief Loop over all of the basic blocks,
  /// replacing byte and word instructions by equivalent 32 bit instructions
  /// where performance or code size can be improved.
  bool runOnMachineFunction(MachineFunction &MF) override;

private:
  MachineFunction *MF;

  /// Machine instruction info used throughout the class.
  const X86InstrInfo *TII;

  /// Local member for function's OptForSize attribute.
  bool OptForSize;

  /// Machine loop info used for guiding some heruistics.
  MachineLoopInfo *MLI;

  /// Register Liveness information after the current instruction.
  LivePhysRegs LiveRegs;
};
char FixupBWInstPass::ID = 0;
}

FunctionPass *llvm::createX86FixupBWInsts() { return new FixupBWInstPass(); }

bool FixupBWInstPass::runOnMachineFunction(MachineFunction &MF) {
  if (!FixupBWInsts)
    return false;

  this->MF = &MF;
  TII = MF.getSubtarget<X86Subtarget>().getInstrInfo();
  OptForSize = MF.getFunction()->optForSize();
  MLI = &getAnalysis<MachineLoopInfo>();
  LiveRegs.init(&TII->getRegisterInfo());

  DEBUG(dbgs() << "Start X86FixupBWInsts\n";);

  // Process all basic blocks.
  for (auto &MBB : MF)
    processBasicBlock(MF, MBB);

  DEBUG(dbgs() << "End X86FixupBWInsts\n";);

  return true;
}

// TODO: This method of analysis can miss some legal cases, because the
// super-register could be live into the address expression for a memory
// reference for the instruction, and still be killed/last used by the
// instruction. However, the existing query interfaces don't seem to
// easily allow that to be checked.
//
// What we'd really like to know is whether after OrigMI, the
// only portion of SuperDestReg that is alive is the portion that
// was the destination register of OrigMI.
bool FixupBWInstPass::getSuperRegDestIfDead(MachineInstr *OrigMI,
                                            unsigned &SuperDestReg) const {
  auto *TRI = &TII->getRegisterInfo();

  unsigned OrigDestReg = OrigMI->getOperand(0).getReg();
  SuperDestReg = getX86SubSuperRegister(OrigDestReg, 32);

  const auto SubRegIdx = TRI->getSubRegIndex(SuperDestReg, OrigDestReg);

  // Make sure that the sub-register that this instruction has as its
  // destination is the lowest order sub-register of the super-register.
  // If it isn't, then the register isn't really dead even if the
  // super-register is considered dead.
  if (SubRegIdx == X86::sub_8bit_hi)
    return false;

  if (LiveRegs.contains(SuperDestReg))
    return false;

  if (SubRegIdx == X86::sub_8bit) {
    // In the case of byte registers, we also have to check that the upper
    // byte register is also dead. That is considered to be independent of
    // whether the super-register is dead.
    unsigned UpperByteReg =
        getX86SubSuperRegister(SuperDestReg, 8, /*High=*/true);

    if (LiveRegs.contains(UpperByteReg))
      return false;
  }

  return true;
}

MachineInstr *FixupBWInstPass::tryReplaceLoad(unsigned New32BitOpcode,
                                              MachineInstr *MI) const {
  unsigned NewDestReg;

  // We are going to try to rewrite this load to a larger zero-extending
  // load.  This is safe if all portions of the 32 bit super-register
  // of the original destination register, except for the original destination
  // register are dead. getSuperRegDestIfDead checks that.
  if (!getSuperRegDestIfDead(MI, NewDestReg))
    return nullptr;

  // Safe to change the instruction.
  MachineInstrBuilder MIB =
      BuildMI(*MF, MI->getDebugLoc(), TII->get(New32BitOpcode), NewDestReg);

  unsigned NumArgs = MI->getNumOperands();
  for (unsigned i = 1; i < NumArgs; ++i)
    MIB.addOperand(MI->getOperand(i));

  MIB->setMemRefs(MI->memoperands_begin(), MI->memoperands_end());

  return MIB;
}

void FixupBWInstPass::processBasicBlock(MachineFunction &MF,
                                        MachineBasicBlock &MBB) {

  // This algorithm doesn't delete the instructions it is replacing
  // right away.  By leaving the existing instructions in place, the
  // register liveness information doesn't change, and this makes the
  // analysis that goes on be better than if the replaced instructions
  // were immediately removed.
  //
  // This algorithm always creates a replacement instruction
  // and notes that and the original in a data structure, until the
  // whole BB has been analyzed.  This keeps the replacement instructions
  // from making it seem as if the larger register might be live.
  SmallVector<std::pair<MachineInstr *, MachineInstr *>, 8> MIReplacements;

  // Start computing liveness for this block. We iterate from the end to be able
  // to update this for each instruction.
  LiveRegs.clear();
  // We run after PEI, so we need to AddPristinesAndCSRs.
  LiveRegs.addLiveOuts(&MBB, /*AddPristinesAndCSRs=*/true);

  for (auto I = MBB.rbegin(); I != MBB.rend(); ++I) {
    MachineInstr *NewMI = nullptr;
    MachineInstr *MI = &*I;

    // See if this is an instruction of the type we are currently looking for.
    switch (MI->getOpcode()) {

    case X86::MOV8rm:
      // Only replace 8 bit loads with the zero extending versions if
      // in an inner most loop and not optimizing for size. This takes
      // an extra byte to encode, and provides limited performance upside.
      if (MachineLoop *ML = MLI->getLoopFor(&MBB)) {
        if (ML->begin() == ML->end() && !OptForSize)
          NewMI = tryReplaceLoad(X86::MOVZX32rm8, MI);
      }
      break;

    case X86::MOV16rm:
      // Always try to replace 16 bit load with 32 bit zero extending.
      // Code size is the same, and there is sometimes a perf advantage
      // from eliminating a false dependence on the upper portion of
      // the register.
      NewMI = tryReplaceLoad(X86::MOVZX32rm16, MI);
      break;

    default:
      // nothing to do here.
      break;
    }

    if (NewMI)
      MIReplacements.push_back(std::make_pair(MI, NewMI));

    // We're done with this instruction, update liveness for the next one.
    LiveRegs.stepBackward(*MI);
  }

  while (!MIReplacements.empty()) {
    MachineInstr *MI = MIReplacements.back().first;
    MachineInstr *NewMI = MIReplacements.back().second;
    MIReplacements.pop_back();
    MBB.insert(MI, NewMI);
    MBB.erase(MI);
  }
}
<|endoftext|>


<commit_before>#include "run-scenario.h"

#include <QApplication>
#include <QDir>
#include <QTextStream>
#include <QTimer>

RunScenario::RunScenario()
{
    _rootDir = QDir::currentPath() + QDir::separator() + "dirs";

    // Set up model.
    _model.setReadOnly(true);
    _model.setRootPath(_rootDir);

    // In the GUI, we cannot select a file or directory until its parent
    // directory has been loaded.  This is not a perfect imitation of that
    // scenario, but it is better than nothing.
    while(_model.needToReadSubdirs(_rootDir))
        QCoreApplication::processEvents(0, 100);
}

RunScenario::~RunScenario()
{
}

// The format of these lines in the scenario file is:
// X filename
// where X is a single character, followed by a space.
QString RunScenario::getRelname(const QString line)
{
    QString relname = line.right(line.size() - 2);
    return relname;
}

QModelIndex RunScenario::getIndex(const QString line)
{
    QString     relname  = getRelname(line);
    QString     filename = QDir(_rootDir).filePath(relname);
    QModelIndex index    = _model.index(filename);
    return index;
}

int RunScenario::getLineState(const QString line)
{
    int state = line[0].digitValue();
    return state;
}

int RunScenario::getCheckedStateInt(const QString line)
{
    QString     relname  = line.right(line.size() - 2);
    QString     filename = QDir(_rootDir).filePath(relname);
    QModelIndex index    = _model.index(filename);
    int         state    = _model.data(index, Qt::CheckStateRole).toInt();
    return state;
}

int RunScenario::processActions(QTextStream &in)
{
    while(!in.atEnd())
    {
        QString line = in.readLine();
        // Blank lines and comments.
        if((line.size() == 0) || (line[0] == QChar('#')))
            continue;
        else if(line == "actions:")
            continue;
        else if(line == "results:")
            break;
        else if(line[0] == QChar('+'))
            _model.setData(getIndex(line), Qt::Checked, Qt::CheckStateRole);
        else if(line[0] == QChar('-'))
            _model.setData(getIndex(line), Qt::Unchecked, Qt::CheckStateRole);
        else
        {
            QTextStream console(stdout);
            console << "ERROR: Scenario file is broken; parsing died on line:"
                    << endl
                    << line << endl;
            // Don't try to recover.
            QApplication::exit(1);
        }
    }
    return (0);
}

// Returns 0 if success, 1 if a model error, 2 if an emit error.
int RunScenario::processResults(QTextStream &in)
{
    while(!in.atEnd())
    {
        QString line = in.readLine();
        // Blank lines and comments.
        if((line.size() == 0) || (line[0] == QChar('#')))
            continue;
        else if(line == "results:")
            continue;
        else if((line[0] == QChar('0')) || (line[0] == QChar('1'))
                || (line[0] == QChar('2')))
        {
            QString lineRelname  = getRelname(line);
            int     desiredState = getLineState(line);
            int     modelState   = getCheckedStateInt(line);
            if(desiredState != modelState)
            {
                // Test failed!
                return (1);
            }
        }
        else
        {
            QTextStream console(stdout);
            console << "ERROR: Scenario file is broken; parsing died on line:"
                    << endl
                    << line << endl;
            // Don't try to recover.
            QApplication::exit(1);
        }
    }
    return (0);
}

int RunScenario::runScenario(const int num)
{
    int result = -1;
    _model.reset();

    QString scenarioFilename =
        QString("scenario-%1.txt").arg(num, 2, 10, QChar('0'));
    QFile inputFile(scenarioFilename);
    if(inputFile.open(QIODevice::ReadOnly))
    {
        QTextStream in(&inputFile);
        processActions(in);
        result = processResults(in);
        if(result != 0)
        {
            // Test failed!
            QTextStream console(stdout);
            console << "--" << endl
                    << "Test failed: " << scenarioFilename << endl;
            if(result == 1)
            {
                console << "Model internal state does not match "
                        << "desired state.  Model data:" << endl;
                printModel();
            }
            console << "--" << endl << endl;
        }
    }
    else
    {
        QTextStream console(stdout);
        console << "ERROR: could not read file: " << scenarioFilename << endl;
        // Don't try to recover.
        QApplication::exit(1);
    }
    inputFile.close();

    return (result);
}

void RunScenario::printDir(const QString dirname, const int depth)
{
    QTextStream console(stdout);
    QModelIndex index;
    QModelIndex dir;

    dir = _model.index(dirname);
    for(int i = 0; i < _model.rowCount(dir); i++)
    {
        index = dir.child(i, dir.column());
        console << _model.data(index, Qt::CheckStateRole).toInt() << "\t";
        // Add indents to show the directory structure.
        for(int j = 0; j < depth; j++)
            console << "\t";
        console << _model.fileName(index) << endl;
        // Recursively print the subdirectory.
        if(_model.isDir(index))
        {
            printDir(_model.filePath(index), depth + 1);
        }
    }
}

void RunScenario::printModel()
{
    printDir(_model.rootPath(), 0);
}
<commit_msg>tests/customfilesystemmodel: fix strict compiler warning<commit_after>#include "run-scenario.h"

#include <QApplication>
#include <QDir>
#include <QEventLoop>
#include <QTextStream>
#include <QTimer>

RunScenario::RunScenario()
{
    _rootDir = QDir::currentPath() + QDir::separator() + "dirs";

    // Set up model.
    _model.setReadOnly(true);
    _model.setRootPath(_rootDir);

    // In the GUI, we cannot select a file or directory until its parent
    // directory has been loaded.  This is not a perfect imitation of that
    // scenario, but it is better than nothing.
    while(_model.needToReadSubdirs(_rootDir))
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}

RunScenario::~RunScenario()
{
}

// The format of these lines in the scenario file is:
// X filename
// where X is a single character, followed by a space.
QString RunScenario::getRelname(const QString line)
{
    QString relname = line.right(line.size() - 2);
    return relname;
}

QModelIndex RunScenario::getIndex(const QString line)
{
    QString     relname  = getRelname(line);
    QString     filename = QDir(_rootDir).filePath(relname);
    QModelIndex index    = _model.index(filename);
    return index;
}

int RunScenario::getLineState(const QString line)
{
    int state = line[0].digitValue();
    return state;
}

int RunScenario::getCheckedStateInt(const QString line)
{
    QString     relname  = line.right(line.size() - 2);
    QString     filename = QDir(_rootDir).filePath(relname);
    QModelIndex index    = _model.index(filename);
    int         state    = _model.data(index, Qt::CheckStateRole).toInt();
    return state;
}

int RunScenario::processActions(QTextStream &in)
{
    while(!in.atEnd())
    {
        QString line = in.readLine();
        // Blank lines and comments.
        if((line.size() == 0) || (line[0] == QChar('#')))
            continue;
        else if(line == "actions:")
            continue;
        else if(line == "results:")
            break;
        else if(line[0] == QChar('+'))
            _model.setData(getIndex(line), Qt::Checked, Qt::CheckStateRole);
        else if(line[0] == QChar('-'))
            _model.setData(getIndex(line), Qt::Unchecked, Qt::CheckStateRole);
        else
        {
            QTextStream console(stdout);
            console << "ERROR: Scenario file is broken; parsing died on line:"
                    << endl
                    << line << endl;
            // Don't try to recover.
            QApplication::exit(1);
        }
    }
    return (0);
}

// Returns 0 if success, 1 if a model error, 2 if an emit error.
int RunScenario::processResults(QTextStream &in)
{
    while(!in.atEnd())
    {
        QString line = in.readLine();
        // Blank lines and comments.
        if((line.size() == 0) || (line[0] == QChar('#')))
            continue;
        else if(line == "results:")
            continue;
        else if((line[0] == QChar('0')) || (line[0] == QChar('1'))
                || (line[0] == QChar('2')))
        {
            QString lineRelname  = getRelname(line);
            int     desiredState = getLineState(line);
            int     modelState   = getCheckedStateInt(line);
            if(desiredState != modelState)
            {
                // Test failed!
                return (1);
            }
        }
        else
        {
            QTextStream console(stdout);
            console << "ERROR: Scenario file is broken; parsing died on line:"
                    << endl
                    << line << endl;
            // Don't try to recover.
            QApplication::exit(1);
        }
    }
    return (0);
}

int RunScenario::runScenario(const int num)
{
    int result = -1;
    _model.reset();

    QString scenarioFilename =
        QString("scenario-%1.txt").arg(num, 2, 10, QChar('0'));
    QFile inputFile(scenarioFilename);
    if(inputFile.open(QIODevice::ReadOnly))
    {
        QTextStream in(&inputFile);
        processActions(in);
        result = processResults(in);
        if(result != 0)
        {
            // Test failed!
            QTextStream console(stdout);
            console << "--" << endl
                    << "Test failed: " << scenarioFilename << endl;
            if(result == 1)
            {
                console << "Model internal state does not match "
                        << "desired state.  Model data:" << endl;
                printModel();
            }
            console << "--" << endl << endl;
        }
    }
    else
    {
        QTextStream console(stdout);
        console << "ERROR: could not read file: " << scenarioFilename << endl;
        // Don't try to recover.
        QApplication::exit(1);
    }
    inputFile.close();

    return (result);
}

void RunScenario::printDir(const QString dirname, const int depth)
{
    QTextStream console(stdout);
    QModelIndex index;
    QModelIndex dir;

    dir = _model.index(dirname);
    for(int i = 0; i < _model.rowCount(dir); i++)
    {
        index = dir.child(i, dir.column());
        console << _model.data(index, Qt::CheckStateRole).toInt() << "\t";
        // Add indents to show the directory structure.
        for(int j = 0; j < depth; j++)
            console << "\t";
        console << _model.fileName(index) << endl;
        // Recursively print the subdirectory.
        if(_model.isDir(index))
        {
            printDir(_model.filePath(index), depth + 1);
        }
    }
}

void RunScenario::printModel()
{
    printDir(_model.rootPath(), 0);
}
<|endoftext|>


<commit_before>// Fill out your copyright notice in the Description page of Project Settings.

#include "RoguelikeSurvival.h"
#include "ZombieCharacter.h"
#include "ZombieController.h"
#include "PlayerDamageType.h"
#include "RoguelikeChar.h"
#include "ZombieAnimInstance.h"
//#include "DrawDebugHelpers.h"
#include "RoguelikeGameInstance.h"

// Sets default values
AZombieCharacter::AZombieCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//Initializing stats and components.

	AudioComponent = CreateDefaultSubobject<UAudioComponent>(FName("Audio"));
	AudioComponent->AttachTo(GetRootComponent());

	ParticleSystemComponent = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ParticleComp"));
	ParticleSystemComponent->bAutoActivate = false;

	ParticleSystemComponent->AttachTo(GetRootComponent());

	//Combat stats
	MinHealth = 100.f;
	MaxHealth = 300.f;

	MinSpeed = 150.f;
	MaxSpeed = 250.f;

	MinDamage = 5.f;
	MaxDamage = 15.f;

	DotTickInSeconds = 1.f;
	SlowPercentage = 0.5f;
	SlowEffectDuration = 3.f;
	Damage = 15.f;

	MeleeDistanceThreshold = 150.f;
}

// Called when the game starts or when spawned
void AZombieCharacter::BeginPlay()
{
	Super::BeginPlay();

	//ParticleSystemComponent->SetWorldLocation(GetActorLocation());
	ParticleSystemComponent->SetRelativeLocation(FVector(90, 0, 40));
	AudioComponent->SetRelativeLocation(FVector(90, 0, 40));

	//Stats initialization
	StatsInit(MinHealth, MaxHealth, MinDamage, MaxDamage);
	CurrentSpeed = FMath::RandRange(MinSpeed, MaxSpeed);

	bHasDot = false;
	bHasSlow = false;

	InitialMaxWalkSpeed = CurrentSpeed;
	GetCharacterMovement()->MaxWalkSpeed = CurrentSpeed;

	//Animations initialization
	ZAnimInstance = Cast<UZombieAnimInstance>(GetMesh()->GetAnimInstance());
	ZAnimInstance->Speed = CurrentSpeed;

	//Setting up the player reference for future use
	OurCharacter = Cast<AZombieController>(GetController())->GetTarget();

	if(AlphaReductionCurveFloat && DeathMaterialInstance)
	{
		//Binds the functions for the progress and the finished function on the timeline
		FOnTimelineFloat TimelineFunctionProgress;

		TimelineFunctionProgress.BindUFunction(this, FName("HandleTimelineProgress"));

		FOnTimelineEvent CompletedTimelineEvent;
		CompletedTimelineEvent.BindUFunction(this, FName("Destroy"));

		//Binding...
		AlphaReductionTimeline.AddInterpFloat(AlphaReductionCurveFloat, TimelineFunctionProgress);
		AlphaReductionTimeline.SetTimelineFinishedFunc(CompletedTimelineEvent);
	}
}

// Called every frame
void AZombieCharacter::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );
	GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
	ZAnimInstance->Speed = InitialMaxWalkSpeed;

	//If the zombie is within the melee range cancel any movement animation so it's more realistic
	float Distance = FVector::Dist(GetActorLocation(),OurCharacter->GetActorLocation());
	if (Distance<= MeleeDistanceThreshold)
	{
		ZAnimInstance->Speed = 0;
	}

	//Enable the attack raycast sphere in zombies hand if the animation is valid
	if (ZAnimInstance->bEligibleForAttack)
	{
		Attack();
		if (GetCharacterMovement())
		{
			GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
		}
	}

	//Updates the alpha value if the zombie is dead to achieve over-time fade out
	if(!IsAlive())
	{
		//Ticks the alpha reduction timeline
		AlphaReductionTimeline.TickTimeline(DeltaTime);
	}

}

float AZombieCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	//Determines the damage amount and type in order to apply it to the zombie
	const float ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	OurCharacter = Cast<ARoguelikeChar>(DamageCauser);

	HandleDamage(DamageAmount, DamageEvent);

	return ActualDamage;
}

void AZombieCharacter::HandleDamage(float DamageAmount, FDamageEvent DamageEvent)
{
	//Handles the given damage amount and effect
	//Makes sures to apply the right debuff (if any - based on the bullet type the player used) and damage to the zombie
	//Takes into consideration the active debuff in order to deactivate it if necessary (ie dot cancels slow effect and vice versa)

	if(IsAlive())
	{

		//Determing the damage type based on the bullet that the player used
		//ie frost damage if frost ammo is equipped
		UPlayerDamageType* DamageType = Cast<UPlayerDamageType>(DamageEvent.DamageTypeClass.GetDefaultObject());
		switch (DamageType->PlayerDamageType)
		{
			case EPlayerDamageType::Dot:
			{
				DisableSlowEffect();
				ApplyDotEffect(DamageAmount);
				break;
			}
			case EPlayerDamageType::Slow:
			{
				DisableDotEffectIfNecessary(true);
				ApplyDamage(DamageAmount);
				ApplySlowEffect();
				break;
			}
			default:
			{
				ApplyDamage(DamageAmount);
				break;
			}
		}

		if (!IsAlive())
		{
			//Stop logic of the AI
			Die();

		}
	}
}

void AZombieCharacter::ApplyDamage(float DamageAmount)
{
	//Applies damage to the zombie

	//Updating the animation of the zombie
	ZAnimInstance->GetHurt();
	if (bHasDot)
	{
		DisableDotEffectIfNecessary();
	}
	CurrentHealth -= DamageAmount;
	if (!IsAlive())
	{
		Die();
	}
	GLog->Log("Zombie has took damage, current hp:" + FString::SanitizeFloat(CurrentHealth));
}

void AZombieCharacter::DisableDotEffectIfNecessary(bool ForceStop)
{
	//Disables the dot effect is necessary (ie dot timer has been depleted)
	//If ForceStop is active it forces the dot effect to stop even if the dot timer is still active
	UWorld* World = GetWorld();
	//Forced stop of dot effect
	if (ForceStop && World && OurCharacter && World->GetTimerManager().IsTimerActive(DotTimerHandle))
	{
		GLog->Log("Stopping Dot Effect...");
		World->GetTimerManager().ClearTimer(DotTimerHandle);

		ParticleSystemComponent->Deactivate();
		//ZombieDebuffComp->SetFireParticleEnabled(false);

		bHasDot = false;

	}
	else
	{	//Disable effect due to time out
		if (World && OurCharacter &&
			World->GetTimerManager().IsTimerActive(DotTimerHandle) &&
			World->GetTimeSeconds() - DotApplicationTime>=OurCharacter->DotDuration)
		{
			World->GetTimerManager().ClearTimer(DotTimerHandle);

			ParticleSystemComponent->Deactivate();
			//ZombieDebuffComp->SetFireParticleEnabled(false);

			bHasDot = false;
		}
	}

	//ParticleSystemComponent->Deactivate();

}

void AZombieCharacter::ApplyDotEffect(float DamageAmount)
{
	//Applies the damage over time effect on zombie and sets up the corresponding particle template
	//Moreover, initializes the dot timer handle so certain damage is applied over-time.
	if (!bHasDot)
	{
		AZombieController* ZCon = Cast<AZombieController>(GetController());
		if (ZCon)
		{

			//Calculating the tick damage that we're going to apply in the zombie
			//based on the given damage amount.
			float DotTimer = OurCharacter->DotDuration;

			float TickDamage = DamageAmount / DotTimer;

			//The delegate that is binded to the apply damage function
			FTimerDelegate Delegate;
			Delegate.BindUFunction<AZombieCharacter, float>(this, FName("ApplyDamage"), TickDamage);

			//Enabling the dot effect..
			UWorld* World = GetWorld();
			if (World && OurCharacter)
			{
				World->GetTimerManager().SetTimer(DotTimerHandle, Delegate, DotTickInSeconds, true);
				DotApplicationTime = World->GetTimeSeconds();
				GLog->Log("Dot application seconds:" + FString::SanitizeFloat(DotApplicationTime));
				bHasDot = true;

				//Resets the particle system template
				if(ParticleSystemComponent)
				{
					ParticleSystemComponent->SetTemplate(FireEffectParticleTemplate);
					ParticleSystemComponent->Activate(true);
					//Activate the fire particles
					/*ParticleSystemComponent->Deactivate();
					ParticleSystemComponent->Template = FireEffectParticleTemplate;
					ParticleSystemComponent->Activate(true);*/
					//ZombieDebuffComp->SetFireParticleEnabled(true);

				}

			}
		}
	}
}

void AZombieCharacter::ApplySlowEffect()
{
	//Applies the slow effect on zombie and sets up the slow timer
	//as well as the right particle template

	if (!bHasSlow)
	{
		DisableDotEffectIfNecessary(true);
		bHasSlow = true;

		float SlowSpeed = InitialMaxWalkSpeed * (1 - SlowPercentage);

		//Activating slow
		GetCharacterMovement()->MaxWalkSpeed = SlowSpeed;

		//Reseting the slow particle template
		ParticleSystemComponent->SetTemplate(SlowEffectParticleTemplate);
		ParticleSystemComponent->Activate(true);

		GLog->Log("Applied slow effect on zombie.");

		//Initializing the slow timer handle
		UWorld* World = GetWorld();
		if (World && OurCharacter)
		{
			World->GetTimerManager().SetTimer(SlowTimerHandle, this, &AZombieCharacter::DisableSlowEffect, SlowEffectDuration);
		}
	}
}

void AZombieCharacter::DisableSlowEffect()
{
	//Disables the slow effect on the zombie and applies its default movement speed
	bHasSlow = false;

	GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
	GLog->Log("Disabled slow effect on zombie.");

	UWorld* World=GetWorld();
	if (World)
	{
		//Clearing the slow timer so the effect can be applied again
		World->GetTimerManager().ClearTimer(SlowTimerHandle);
	}

	//Turning of the particle system
	ParticleSystemComponent->Deactivate();
}

void AZombieCharacter::StatsInit(float MinHealth, float MaxHealth, float MinDamage, float MaxDamage)
{
	//Initializes the stats of the zombie based on the given range of values
	CurrentHealth = FMath::RandRange(MinHealth, MaxHealth);
	Damage = FMath::RandRange(MinDamage, MaxDamage);
}

void AZombieCharacter::Attack()
{
	//This function is used only when it's permitted by the zombie's animation instance
	//It creates a raycast in a sphere shape and checks for possible hits
	//If the hits contain our player it makes sure it applies damage to him

	//Setting up the start and end location of the raycast
	FVector StartLocation = GetMesh()->GetSocketLocation(FName("MeleeStartSocket"));
	FVector EndLocation = GetMesh()->GetSocketLocation(FName("MeleeEndSocket"));

	//Raycasting in a sphere to detect collisions
	TArray<FHitResult> HitResults;

	//Setting up the shape of the raycast
	FCollisionShape CollisionShape;
	CollisionShape.ShapeType = ECollisionShape::Sphere;
	CollisionShape.SetSphere(AttackRaycastRadius);

	//Object query parameters
	FCollisionObjectQueryParams ObjectQueryParams;
	ObjectQueryParams.AllDynamicObjects;

	//Handling ignored actors
	FCollisionQueryParams QueryParams;
	QueryParams.AddIgnoredActor(this);

	UWorld* World = GetWorld();
	if (World && ZAnimInstance->bEligibleForAttack)
	{
		//Raycasting...
		bool bHit = World->SweepMultiByObjectType(HitResults, StartLocation, EndLocation, FQuat::Identity, ObjectQueryParams, CollisionShape, QueryParams);

		//Raycast visualization
		/*FVector Center = ((EndLocation - StartLocation) / 2) + StartLocation;
		DrawDebugSphere(World, Center, AttackRaycastRadius, 20, FColor::Green, false, 2.f);*/

		//Checking for possible hits
		if (bHit)
		{
			for (auto It = HitResults.CreateIterator(); It; It++)
			{
				ARoguelikeChar* Char = Cast<ARoguelikeChar>(It->GetActor());
				if (Char && ZAnimInstance && GetCharacterMovement())
				{
					//Calling the attack function from character
					Char->TakeDamageFromZombie(Damage);

					//Closing the flag which checks for the attack function
					ZAnimInstance->bEligibleForAttack = false;

					//Updating with new movement speed
					GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
					ZAnimInstance->Speed = InitialMaxWalkSpeed;
					break;
				}
			}
		}
	}

}

void AZombieCharacter::Die()
{
	//Disables the logic of the zombie and gradually destroys this Actor

	AZombieController* ZCon = Cast<AZombieController>(GetController());
	if (ZCon)
	{
		//Increasing the kills and wave count if necessary
		OurCharacter->Kills++;
		URoguelikeGameInstance* GameInstance = Cast<URoguelikeGameInstance>(GetGameInstance());
		GameInstance->IncreaseWaveCountIfNeeded();
		OurCharacter->PlayerController->UpdateUI();

		//Stopping the AI logic
		ZCon->UnPossess();

		//TODO: Play death animation and alpha channel
		AlphaChannelReduction();
		//AlphaReductionTimeline.PlayFromStart();

		//Handling the last animations
		ZAnimInstance->bIsDead = true;
		ZAnimInstance->Speed = 0;

		AudioComponent->Stop();

		//Disabling the collision to avoid false kill count!
		UCapsuleComponent* RootComp = Cast<UCapsuleComponent>(GetRootComponent());
		RootComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	}
}

void AZombieCharacter::HandleTimelineProgress(float AlphaReduction)
{
	//Gradually reducing the Alpha value of the material of the zombie
	//if(DeathMaterialInstance)
	//{
	//	//GLog->Log("Timeline tick");
	//	//GetMesh()->CreateAndSetMaterialInstanceDynamicFromMaterial(0, DeathMaterialInstance);
	//	GetMesh()->CreateDynamicMaterialInstance(0, DeathMaterialInstance);
	//	GLog->Log("Alpha reduction:" + FString::SanitizeFloat(AlphaReduction));

	//	//DeathMaterialInstance->OverrideScalarParameterDefault(FName("AlphaReduction"), AlphaReduction, true, ERHIFeatureLevel::Num);
	//	TArray<FScalarParameterValue> ZombieMatInstScalarParams;
	//	ZombieMatInstScalarParams = DeathMaterialInstance->ScalarParameterValues;
	//
	//	for (auto It = ZombieMatInstScalarParams.CreateIterator(); It;It++)
	//	{
	//		if((*It).ParameterName==FName("AlphaReduction"))
	//		{
	//			(*It).ParameterValue = AlphaReduction;
	//			break;
	//		}
	//	}

	//	FScalarParameterValue ZombieInstance;
	//	ZombieInstance.ParameterName = FName("AlphaReduction");
	//
	//	//int32 AlphaReductionPropertyIndex = ZombieMatInstScalarParams.Find(ZombieInstance.ParameterName);

	//	auto* Parameter=ZombieMatInstScalarParams.FindByPredicate( [&] (int32 i) {return ZombieMatInstScalarParams[i].ParameterName == FName("AlphaReduction"); });
	//	Parameter->ParameterValue = AlphaReduction;
	//

	//	//ZombieMatInstScalarParams[AlphaReductionPropertyIndex].ParameterValue = AlphaReduction;

	//}
}

//void AZombieCharacter::KillZombie()
//{
//	Destroy();
//}
<commit_msg>Update ZombieCharacter.cpp<commit_after>// Fill out your copyright notice in the Description page of Project Settings.

#include "RoguelikeSurvival.h"
#include "ZombieCharacter.h"
#include "ZombieController.h"
#include "PlayerDamageType.h"
#include "RoguelikeChar.h"
#include "ZombieAnimInstance.h"
//#include "DrawDebugHelpers.h"
#include "RoguelikeGameInstance.h"

// Sets default values
AZombieCharacter::AZombieCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//Initializing stats and components.

	AudioComponent = CreateDefaultSubobject<UAudioComponent>(FName("Audio"));
	AudioComponent->AttachTo(GetRootComponent());

	ParticleSystemComponent = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ParticleComp"));
	ParticleSystemComponent->bAutoActivate = false;

	ParticleSystemComponent->AttachTo(GetRootComponent());

	//Combat stats
	MinHealth = 100.f;
	MaxHealth = 300.f;

	MinSpeed = 150.f;
	MaxSpeed = 250.f;

	MinDamage = 5.f;
	MaxDamage = 15.f;

	DotTickInSeconds = 1.f;
	SlowPercentage = 0.5f;
	SlowEffectDuration = 3.f;
	Damage = 15.f;

	MeleeDistanceThreshold = 150.f;
}

// Called when the game starts or when spawned
void AZombieCharacter::BeginPlay()
{
	Super::BeginPlay();

	//ParticleSystemComponent->SetWorldLocation(GetActorLocation());
	ParticleSystemComponent->SetRelativeLocation(FVector(90, 0, 40));
	AudioComponent->SetRelativeLocation(FVector(90, 0, 40));

	//Stats initialization
	StatsInit(MinHealth, MaxHealth, MinDamage, MaxDamage);
	CurrentSpeed = FMath::RandRange(MinSpeed, MaxSpeed);

	bHasDot = false;
	bHasSlow = false;

	InitialMaxWalkSpeed = CurrentSpeed;
	GetCharacterMovement()->MaxWalkSpeed = CurrentSpeed;

	//Animations initialization
	ZAnimInstance = Cast<UZombieAnimInstance>(GetMesh()->GetAnimInstance());
	ZAnimInstance->Speed = CurrentSpeed;

	//Setting up the player reference for future use
	OurCharacter = Cast<AZombieController>(GetController())->GetTarget();

	if(AlphaReductionCurveFloat && DeathMaterialInstance)
	{
		//Binds the functions for the progress and the finished function on the timeline
		FOnTimelineFloat TimelineFunctionProgress;

		TimelineFunctionProgress.BindUFunction(this, FName("HandleTimelineProgress"));

		FOnTimelineEvent CompletedTimelineEvent;
		CompletedTimelineEvent.BindUFunction(this, FName("Destroy"));

		//Binding...
		AlphaReductionTimeline.AddInterpFloat(AlphaReductionCurveFloat, TimelineFunctionProgress);
		AlphaReductionTimeline.SetTimelineFinishedFunc(CompletedTimelineEvent);
	}
}

// Called every frame
void AZombieCharacter::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );
	GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
	ZAnimInstance->Speed = InitialMaxWalkSpeed;

	//If the zombie is within the melee range cancel any movement animation so it's more realistic
	float Distance = FVector::Dist(GetActorLocation(),OurCharacter->GetActorLocation());
	if (Distance<= MeleeDistanceThreshold)
	{
		ZAnimInstance->Speed = 0;
	}

	//Enable the attack raycast sphere in zombies hand if the animation is valid
	if (ZAnimInstance->bEligibleForAttack)
	{
		Attack();
		if (GetCharacterMovement())
		{
			GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
		}
	}

	//Updates the alpha value if the zombie is dead to achieve over-time fade out
	if(!IsAlive())
	{
		//Ticks the alpha reduction timeline
		AlphaReductionTimeline.TickTimeline(DeltaTime);
	}

}

float AZombieCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	//Determines the damage amount and type in order to apply it to the zombie
	const float ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	OurCharacter = Cast<ARoguelikeChar>(DamageCauser);

	HandleDamage(DamageAmount, DamageEvent);

	return ActualDamage;
}

void AZombieCharacter::HandleDamage(float DamageAmount, FDamageEvent DamageEvent)
{
	//Handles the given damage amount and effect
	//Makes sures to apply the right debuff (if any - based on the bullet type the player used) and damage to the zombie
	//Takes into consideration the active debuff in order to deactivate it if necessary (ie dot cancels slow effect and vice versa)

	if(IsAlive())
	{

		//Determing the damage type based on the bullet that the player used
		//ie frost damage if frost ammo is equipped
		UPlayerDamageType* DamageType = Cast<UPlayerDamageType>(DamageEvent.DamageTypeClass.GetDefaultObject());
		switch (DamageType->PlayerDamageType)
		{
			case EPlayerDamageType::Dot:
			{
				DisableSlowEffect();
				ApplyDotEffect(DamageAmount);
				break;
			}
			case EPlayerDamageType::Slow:
			{
				DisableDotEffectIfNecessary(true);
				ApplyDamage(DamageAmount);
				ApplySlowEffect();
				break;
			}
			default:
			{
				ApplyDamage(DamageAmount);
				break;
			}
		}

		if (!IsAlive())
		{
			//Stop logic of the AI
			Die();

		}
	}
}

void AZombieCharacter::ApplyDamage(float DamageAmount)
{
	//Applies damage to the zombie

	//Updating the animation of the zombie
	ZAnimInstance->GetHurt();
	if (bHasDot)
	{
		DisableDotEffectIfNecessary();
	}
	CurrentHealth -= DamageAmount;
	if (!IsAlive())
	{
		Die();
	}
	GLog->Log("Zombie has took damage, current hp:" + FString::SanitizeFloat(CurrentHealth));
}

void AZombieCharacter::DisableDotEffectIfNecessary(bool ForceStop)
{
	//Disables the dot effect is necessary (ie dot timer has been depleted)
	//If ForceStop is active it forces the dot effect to stop even if the dot timer is still active
	UWorld* World = GetWorld();
	//Forced stop of dot effect
	if (ForceStop && World && OurCharacter && World->GetTimerManager().IsTimerActive(DotTimerHandle))
	{
		GLog->Log("Stopping Dot Effect...");
		World->GetTimerManager().ClearTimer(DotTimerHandle);

		ParticleSystemComponent->Deactivate();
		//ZombieDebuffComp->SetFireParticleEnabled(false);

		bHasDot = false;

	}
	else
	{	//Disable effect due to time out
		if (World && OurCharacter &&
			World->GetTimerManager().IsTimerActive(DotTimerHandle) &&
			World->GetTimeSeconds() - DotApplicationTime>=OurCharacter->DotDuration)
		{
			World->GetTimerManager().ClearTimer(DotTimerHandle);

			ParticleSystemComponent->Deactivate();
			//ZombieDebuffComp->SetFireParticleEnabled(false);

			bHasDot = false;
		}
	}

	//ParticleSystemComponent->Deactivate();

}

void AZombieCharacter::ApplyDotEffect(float DamageAmount)
{
	//Applies the damage over time effect on zombie and sets up the corresponding particle template
	//Moreover, initializes the dot timer handle so certain damage is applied over-time.
	if (!bHasDot)
	{
		AZombieController* ZCon = Cast<AZombieController>(GetController());
		if (ZCon)
		{

			//Calculating the tick damage that we're going to apply in the zombie
			//based on the given damage amount.
			float DotTimer = OurCharacter->DotDuration;

			float TickDamage = DamageAmount / DotTimer;

			//The delegate that is binded to the apply damage function
			FTimerDelegate Delegate;
			Delegate.BindUFunction<AZombieCharacter, float>(this, FName("ApplyDamage"), TickDamage);

			//Enabling the dot effect..
			UWorld* World = GetWorld();
			if (World && OurCharacter)
			{
				World->GetTimerManager().SetTimer(DotTimerHandle, Delegate, DotTickInSeconds, true);
				DotApplicationTime = World->GetTimeSeconds();
				GLog->Log("Dot application seconds:" + FString::SanitizeFloat(DotApplicationTime));
				bHasDot = true;

				//Resets the particle system template
				if(ParticleSystemComponent)
				{
					ParticleSystemComponent->SetTemplate(FireEffectParticleTemplate);
					ParticleSystemComponent->Activate(true);
					//Activate the fire particles
					/*ParticleSystemComponent->Deactivate();
					ParticleSystemComponent->Template = FireEffectParticleTemplate;
					ParticleSystemComponent->Activate(true);*/
					//ZombieDebuffComp->SetFireParticleEnabled(true);

				}

			}
		}
	}
}

void AZombieCharacter::ApplySlowEffect()
{
	//Applies the slow effect on zombie and sets up the slow timer
	//as well as the right particle template

	if (!bHasSlow)
	{
		DisableDotEffectIfNecessary(true);
		bHasSlow = true;

		float SlowSpeed = InitialMaxWalkSpeed * (1 - SlowPercentage);

		//Activating slow
		GetCharacterMovement()->MaxWalkSpeed = SlowSpeed;

		//Reseting the slow particle template
		ParticleSystemComponent->SetTemplate(SlowEffectParticleTemplate);
		ParticleSystemComponent->Activate(true);

		GLog->Log("Applied slow effect on zombie.");

		//Initializing the slow timer handle
		UWorld* World = GetWorld();
		if (World && OurCharacter)
		{
			World->GetTimerManager().SetTimer(SlowTimerHandle, this, &AZombieCharacter::DisableSlowEffect, SlowEffectDuration);
		}
	}
}

void AZombieCharacter::DisableSlowEffect()
{
	//Disables the slow effect on the zombie and applies its default movement speed
	bHasSlow = false;

	GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
	GLog->Log("Disabled slow effect on zombie.");

	UWorld* World=GetWorld();
	if (World)
	{
		//Clearing the slow timer so the effect can be applied again
		World->GetTimerManager().ClearTimer(SlowTimerHandle);
	}

	//Turning of the particle system
	ParticleSystemComponent->Deactivate();
}

void AZombieCharacter::StatsInit(float MinHealth, float MaxHealth, float MinDamage, float MaxDamage)
{
	//Initializes the stats of the zombie based on the given range of values
	CurrentHealth = FMath::RandRange(MinHealth, MaxHealth);
	Damage = FMath::RandRange(MinDamage, MaxDamage);
}

void AZombieCharacter::Attack()
{
	//This function is used only when it's permitted by the zombie's animation instance
	//It creates a raycast in a sphere shape and checks for possible hits
	//If the hits contain our player it makes sure it applies damage to him

	//Setting up the start and end location of the raycast
	FVector StartLocation = GetMesh()->GetSocketLocation(FName("MeleeStartSocket"));
	FVector EndLocation = GetMesh()->GetSocketLocation(FName("MeleeEndSocket"));

	//Raycasting in a sphere to detect collisions
	TArray<FHitResult> HitResults;

	//Setting up the shape of the raycast
	FCollisionShape CollisionShape;
	CollisionShape.ShapeType = ECollisionShape::Sphere;
	CollisionShape.SetSphere(AttackRaycastRadius);

	//Object query parameters
	FCollisionObjectQueryParams ObjectQueryParams;
	ObjectQueryParams.AllDynamicObjects;

	//Handling ignored actors
	FCollisionQueryParams QueryParams;
	QueryParams.AddIgnoredActor(this);

	UWorld* World = GetWorld();
	if (World && ZAnimInstance->bEligibleForAttack)
	{
		//Raycasting...
		bool bHit = World->SweepMultiByObjectType(HitResults, StartLocation, EndLocation, FQuat::Identity, ObjectQueryParams, CollisionShape, QueryParams);

		//Raycast visualization
		/*FVector Center = ((EndLocation - StartLocation) / 2) + StartLocation;
		DrawDebugSphere(World, Center, AttackRaycastRadius, 20, FColor::Green, false, 2.f);*/

		//Checking for possible hits
		if (bHit)
		{
			for (auto It = HitResults.CreateIterator(); It; It++)
			{
				ARoguelikeChar* Char = Cast<ARoguelikeChar>(It->GetActor());
				if (Char && ZAnimInstance && GetCharacterMovement())
				{
					//Calling the attack function from character
					Char->TakeDamageFromZombie(Damage);

					//Closing the flag which checks for the attack function
					ZAnimInstance->bEligibleForAttack = false;

					//Updating with new movement speed
					GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
					ZAnimInstance->Speed = InitialMaxWalkSpeed;
					break;
				}
			}
		}
	}

}

void AZombieCharacter::Die()
{
	//Disables the logic of the zombie and gradually destroys this Actor

	AZombieController* ZCon = Cast<AZombieController>(GetController());
	if (ZCon)
	{
		//Increasing the kills and wave count if necessary
		OurCharacter->Kills++;
		URoguelikeGameInstance* GameInstance = Cast<URoguelikeGameInstance>(GetGameInstance());
		GameInstance->IncreaseWaveCountIfNeeded();
		OurCharacter->PlayerController->UpdateUI();

		//Stopping the AI logic
		ZCon->UnPossess();

		AlphaChannelReduction();
		//AlphaReductionTimeline.PlayFromStart();

		//Handling the last animations
		ZAnimInstance->bIsDead = true;
		ZAnimInstance->Speed = 0;

		AudioComponent->Stop();

		//Disabling the collision to avoid false kill count!
		UCapsuleComponent* RootComp = Cast<UCapsuleComponent>(GetRootComponent());
		RootComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	}
}

void AZombieCharacter::HandleTimelineProgress(float AlphaReduction)
{
	//Gradually reducing the Alpha value of the material of the zombie
	//if(DeathMaterialInstance)
	//{
	//	//GLog->Log("Timeline tick");
	//	//GetMesh()->CreateAndSetMaterialInstanceDynamicFromMaterial(0, DeathMaterialInstance);
	//	GetMesh()->CreateDynamicMaterialInstance(0, DeathMaterialInstance);
	//	GLog->Log("Alpha reduction:" + FString::SanitizeFloat(AlphaReduction));

	//	//DeathMaterialInstance->OverrideScalarParameterDefault(FName("AlphaReduction"), AlphaReduction, true, ERHIFeatureLevel::Num);
	//	TArray<FScalarParameterValue> ZombieMatInstScalarParams;
	//	ZombieMatInstScalarParams = DeathMaterialInstance->ScalarParameterValues;
	//
	//	for (auto It = ZombieMatInstScalarParams.CreateIterator(); It;It++)
	//	{
	//		if((*It).ParameterName==FName("AlphaReduction"))
	//		{
	//			(*It).ParameterValue = AlphaReduction;
	//			break;
	//		}
	//	}

	//	FScalarParameterValue ZombieInstance;
	//	ZombieInstance.ParameterName = FName("AlphaReduction");
	//
	//	//int32 AlphaReductionPropertyIndex = ZombieMatInstScalarParams.Find(ZombieInstance.ParameterName);

	//	auto* Parameter=ZombieMatInstScalarParams.FindByPredicate( [&] (int32 i) {return ZombieMatInstScalarParams[i].ParameterName == FName("AlphaReduction"); });
	//	Parameter->ParameterValue = AlphaReduction;
	//

	//	//ZombieMatInstScalarParams[AlphaReductionPropertyIndex].ParameterValue = AlphaReduction;

	//}
}

//void AZombieCharacter::KillZombie()
//{
//	Destroy();
//}
<|endoftext|>


<commit_before>#include "master.hpp"

namespace factor {

void factor_vm::deallocate_inline_cache(cell return_address) {
  // Find the call target.
  void* old_entry_point = get_call_target(return_address);
  code_block* old_block = (code_block*)old_entry_point - 1;

  // Free the old PIC since we know its unreachable
  if (old_block->pic_p())
    code->free(old_block);
}

// Figure out what kind of type check the PIC needs based on the methods
// it contains
static cell determine_inline_cache_type(array* cache_entries) {
  for (cell i = 0; i < array_capacity(cache_entries); i += 2) {
    // Is it a tuple layout?
    if (TAG(array_nth(cache_entries, i)) == ARRAY_TYPE) {
      return PIC_TUPLE;
    }
  }
  return PIC_TAG;
}

void factor_vm::update_pic_count(cell type) {
  if (type == PIC_TAG)
    dispatch_stats.pic_tag_count++;
  else
    dispatch_stats.pic_tuple_count++;
}

struct inline_cache_jit : public jit {
  inline_cache_jit(cell generic_word, factor_vm* vm) : jit(generic_word, vm) {}

  void emit_check_and_jump(cell ic_type, cell i, cell klass, cell method);
  void emit_inline_cache(fixnum index, cell generic_word_, cell methods_,
                         cell cache_entries_, bool tail_call_p);
};

void inline_cache_jit::emit_check_and_jump(cell ic_type, cell i,
                                           cell klass, cell method) {
  // Class equal?
  cell check_type = PIC_CHECK_TAG;
  if (TAG(klass) != FIXNUM_TYPE)
      check_type = PIC_CHECK_TUPLE;

  // The tag check can be skipped if it is the first one and we are
  // checking for the fixnum type which is 0. That is because the
  // AND instruction in the PIC_TAG template already sets the zero
  // flag.
  if (!(i == 0 && ic_type == PIC_TAG && klass == 0)) {
    emit_with_literal(parent->special_objects[check_type], klass);
  }

  // Yes? Jump to method
  emit_with_literal(parent->special_objects[PIC_HIT], method);
}

// index: 0 = top of stack, 1 = item underneath, etc
// cache_entries: array of class/method pairs
// Allocates memory
void inline_cache_jit::emit_inline_cache(fixnum index, cell generic_word_,
                                         cell methods_, cell cache_entries_,
                                         bool tail_call_p) {
  data_root<word> generic_word(generic_word_, parent);
  data_root<array> methods(methods_, parent);
  data_root<array> cache_entries(cache_entries_, parent);

  cell ic_type = determine_inline_cache_type(cache_entries.untagged());
  parent->update_pic_count(ic_type);

  // Generate machine code to determine the object's class.
  emit_with_literal(parent->special_objects[PIC_LOAD],
                    tag_fixnum(-index * sizeof(cell)));

  // Put the tag of the object, or class of the tuple in a register.
  emit(parent->special_objects[ic_type]);

  // Generate machine code to check, in turn, if the class is one of the cached
  // entries.
  for (cell i = 0; i < array_capacity(cache_entries.untagged()); i += 2) {
    cell klass = array_nth(cache_entries.untagged(), i);
    cell method = array_nth(cache_entries.untagged(), i + 1);

    emit_check_and_jump(ic_type, i, klass, method);
  }

  // If none of the above conditionals tested true, then execution "falls
  // through" to here.

  // A stack frame is set up, since the inline-cache-miss sub-primitive
  // makes a subroutine call to the VM.
  emit(parent->special_objects[JIT_PROLOG]);

  // The inline-cache-miss sub-primitive call receives enough information to
  // reconstruct the PIC with the new entry.
  push(generic_word.value());
  push(methods.value());
  push(tag_fixnum(index));
  push(cache_entries.value());

  emit_subprimitive(
      parent->special_objects[tail_call_p ? PIC_MISS_TAIL_WORD : PIC_MISS_WORD],
      true,  // tail_call_p
      true); // stack_frame_p
}

// Allocates memory
cell factor_vm::add_inline_cache_entry(cell cache_entries_, cell klass_,
                                       cell method_) {
  data_root<array> cache_entries(cache_entries_, this);
  data_root<object> klass(klass_, this);
  data_root<word> method(method_, this);

  cell pic_size = array_capacity(cache_entries.untagged());
  data_root<array> new_cache_entries(
      reallot_array(cache_entries.untagged(), pic_size + 2), this);
  set_array_nth(new_cache_entries.untagged(), pic_size, klass.value());
  set_array_nth(new_cache_entries.untagged(), pic_size + 1, method.value());
  return new_cache_entries.value();
}

void factor_vm::update_pic_transitions(cell pic_size) {
  if (pic_size == max_pic_size)
    dispatch_stats.pic_to_mega_transitions++;
  else if (pic_size == 0)
    dispatch_stats.cold_call_to_ic_transitions++;
  else if (pic_size == 1)
    dispatch_stats.ic_to_pic_transitions++;
}

// The cache_entries parameter is empty (on cold call site) or has entries
// (on cache miss). Called from assembly with the actual return address.
// Compilation of the inline cache may trigger a GC, which may trigger a
// compaction;
// also, the block containing the return address may now be dead. Use a
// code_root to take care of the details.
// Allocates memory
cell factor_vm::inline_cache_miss(cell return_address_) {
  code_root return_address(return_address_, this);
  bool tail_call_site = tail_call_site_p(return_address.value);

#ifdef PIC_DEBUG
  FACTOR_PRINT("Inline cache miss at "
               << (tail_call_site ? "tail" : "non-tail")
               << " call site 0x" << std::hex << return_address.value
               << std::dec);
  print_callstack();
#endif

  data_root<array> cache_entries(ctx->pop(), this);
  fixnum index = untag_fixnum(ctx->pop());
  data_root<array> methods(ctx->pop(), this);
  data_root<word> generic_word(ctx->pop(), this);
  data_root<object> object(((cell*)ctx->datastack)[-index], this);

  cell pic_size = array_capacity(cache_entries.untagged()) / 2;

  update_pic_transitions(pic_size);

  cell xt = generic_word->entry_point;
  if (pic_size < max_pic_size) {
    cell klass = object_class(object.value());
    cell method = lookup_method(object.value(), methods.value());

    data_root<array> new_cache_entries(
        add_inline_cache_entry(cache_entries.value(), klass, method), this);

    inline_cache_jit jit(generic_word.value(), this);
    jit.emit_inline_cache(index, generic_word.value(), methods.value(),
                          new_cache_entries.value(), tail_call_site);
    code_block* code = jit.to_code_block(CODE_BLOCK_PIC, JIT_FRAME_SIZE);
    initialize_code_block(code);
    xt = code->entry_point();
  }

  // Install the new stub.
  if (return_address.valid) {
    // Since each PIC is only referenced from a single call site,
    // if the old call target was a PIC, we can deallocate it immediately,
    // instead of leaving dead PICs around until the next GC.
    deallocate_inline_cache(return_address.value);
    set_call_target(return_address.value, xt);

#ifdef PIC_DEBUG
    FACTOR_PRINT("Updated " << (tail_call_site ? "tail" : "non-tail")
                 << " call site 0x" << std::hex << return_address.value << std::dec
                 << " with 0x" << std::hex << (cell)xt << std::dec);
    print_callstack();
#endif
  }

  return xt;
}

// Allocates memory
VM_C_API cell inline_cache_miss(cell return_address, factor_vm* parent) {
  return parent->inline_cache_miss(return_address);
}

}
<commit_msg>VM: fix comments for inline_cache_jit<commit_after>#include "master.hpp"

namespace factor {

void factor_vm::deallocate_inline_cache(cell return_address) {
  // Find the call target.
  void* old_entry_point = get_call_target(return_address);
  code_block* old_block = (code_block*)old_entry_point - 1;

  // Free the old PIC since we know its unreachable
  if (old_block->pic_p())
    code->free(old_block);
}

// Figure out what kind of type check the PIC needs based on the methods
// it contains
static cell determine_inline_cache_type(array* cache_entries) {
  for (cell i = 0; i < array_capacity(cache_entries); i += 2) {
    // Is it a tuple layout?
    if (TAG(array_nth(cache_entries, i)) == ARRAY_TYPE) {
      return PIC_TUPLE;
    }
  }
  return PIC_TAG;
}

void factor_vm::update_pic_count(cell type) {
  if (type == PIC_TAG)
    dispatch_stats.pic_tag_count++;
  else
    dispatch_stats.pic_tuple_count++;
}

struct inline_cache_jit : public jit {
  inline_cache_jit(cell generic_word, factor_vm* vm) : jit(generic_word, vm) {}

  void emit_check_and_jump(cell ic_type, cell i, cell klass, cell method);
  void emit_inline_cache(fixnum index, cell generic_word_, cell methods_,
                         cell cache_entries_, bool tail_call_p);
};

void inline_cache_jit::emit_check_and_jump(cell ic_type, cell i,
                                           cell klass, cell method) {
  // Class equal?
  cell check_type = PIC_CHECK_TAG;
  if (TAG(klass) != FIXNUM_TYPE)
      check_type = PIC_CHECK_TUPLE;

  // The tag check can be skipped if it is the first one and we are
  // checking for the fixnum type which is 0. That is because the
  // AND instruction in the PIC_TAG template already sets the zero
  // flag.
  if (!(i == 0 && ic_type == PIC_TAG && klass == 0)) {
    emit_with_literal(parent->special_objects[check_type], klass);
  }

  // Yes? Jump to method
  emit_with_literal(parent->special_objects[PIC_HIT], method);
}

// index: 0 = top of stack, 1 = item underneath, etc
// cache_entries: array of class/method pairs
// Allocates memory
void inline_cache_jit::emit_inline_cache(fixnum index, cell generic_word_,
                                         cell methods_, cell cache_entries_,
                                         bool tail_call_p) {
  data_root<word> generic_word(generic_word_, parent);
  data_root<array> methods(methods_, parent);
  data_root<array> cache_entries(cache_entries_, parent);

  cell ic_type = determine_inline_cache_type(cache_entries.untagged());
  parent->update_pic_count(ic_type);

  // Put the tag of the object, or class of the tuple in a register.
  emit_with_literal(parent->special_objects[PIC_LOAD],
                    tag_fixnum(-index * sizeof(cell)));

  // Generate machine code to determine the object's class.
  emit(parent->special_objects[ic_type]);

  // Generate machine code to check, in turn, if the class is one of the cached
  // entries.
  for (cell i = 0; i < array_capacity(cache_entries.untagged()); i += 2) {
    cell klass = array_nth(cache_entries.untagged(), i);
    cell method = array_nth(cache_entries.untagged(), i + 1);

    emit_check_and_jump(ic_type, i, klass, method);
  }

  // If none of the above conditionals tested true, then execution "falls
  // through" to here.

  // A stack frame is set up, since the inline-cache-miss sub-primitive
  // makes a subroutine call to the VM.
  emit(parent->special_objects[JIT_PROLOG]);

  // The inline-cache-miss sub-primitive call receives enough information to
  // reconstruct the PIC with the new entry.
  push(generic_word.value());
  push(methods.value());
  push(tag_fixnum(index));
  push(cache_entries.value());

  emit_subprimitive(
      parent->special_objects[tail_call_p ? PIC_MISS_TAIL_WORD : PIC_MISS_WORD],
      true,  // tail_call_p
      true); // stack_frame_p
}

// Allocates memory
cell factor_vm::add_inline_cache_entry(cell cache_entries_, cell klass_,
                                       cell method_) {
  data_root<array> cache_entries(cache_entries_, this);
  data_root<object> klass(klass_, this);
  data_root<word> method(method_, this);

  cell pic_size = array_capacity(cache_entries.untagged());
  data_root<array> new_cache_entries(
      reallot_array(cache_entries.untagged(), pic_size + 2), this);
  set_array_nth(new_cache_entries.untagged(), pic_size, klass.value());
  set_array_nth(new_cache_entries.untagged(), pic_size + 1, method.value());
  return new_cache_entries.value();
}

void factor_vm::update_pic_transitions(cell pic_size) {
  if (pic_size == max_pic_size)
    dispatch_stats.pic_to_mega_transitions++;
  else if (pic_size == 0)
    dispatch_stats.cold_call_to_ic_transitions++;
  else if (pic_size == 1)
    dispatch_stats.ic_to_pic_transitions++;
}

// The cache_entries parameter is empty (on cold call site) or has entries
// (on cache miss). Called from assembly with the actual return address.
// Compilation of the inline cache may trigger a GC, which may trigger a
// compaction;
// also, the block containing the return address may now be dead. Use a
// code_root to take care of the details.
// Allocates memory
cell factor_vm::inline_cache_miss(cell return_address_) {
  code_root return_address(return_address_, this);
  bool tail_call_site = tail_call_site_p(return_address.value);

#ifdef PIC_DEBUG
  FACTOR_PRINT("Inline cache miss at "
               << (tail_call_site ? "tail" : "non-tail")
               << " call site 0x" << std::hex << return_address.value
               << std::dec);
  print_callstack();
#endif

  data_root<array> cache_entries(ctx->pop(), this);
  fixnum index = untag_fixnum(ctx->pop());
  data_root<array> methods(ctx->pop(), this);
  data_root<word> generic_word(ctx->pop(), this);
  data_root<object> object(((cell*)ctx->datastack)[-index], this);

  cell pic_size = array_capacity(cache_entries.untagged()) / 2;

  update_pic_transitions(pic_size);

  cell xt = generic_word->entry_point;
  if (pic_size < max_pic_size) {
    cell klass = object_class(object.value());
    cell method = lookup_method(object.value(), methods.value());

    data_root<array> new_cache_entries(
        add_inline_cache_entry(cache_entries.value(), klass, method), this);

    inline_cache_jit jit(generic_word.value(), this);
    jit.emit_inline_cache(index, generic_word.value(), methods.value(),
                          new_cache_entries.value(), tail_call_site);
    code_block* code = jit.to_code_block(CODE_BLOCK_PIC, JIT_FRAME_SIZE);
    initialize_code_block(code);
    xt = code->entry_point();
  }

  // Install the new stub.
  if (return_address.valid) {
    // Since each PIC is only referenced from a single call site,
    // if the old call target was a PIC, we can deallocate it immediately,
    // instead of leaving dead PICs around until the next GC.
    deallocate_inline_cache(return_address.value);
    set_call_target(return_address.value, xt);

#ifdef PIC_DEBUG
    FACTOR_PRINT("Updated " << (tail_call_site ? "tail" : "non-tail")
                 << " call site 0x" << std::hex << return_address.value << std::dec
                 << " with 0x" << std::hex << (cell)xt << std::dec);
    print_callstack();
#endif
  }

  return xt;
}

// Allocates memory
VM_C_API cell inline_cache_miss(cell return_address, factor_vm* parent) {
  return parent->inline_cache_miss(return_address);
}

}
<|endoftext|>


<commit_before>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
	Copyright (C) 2010-2015  Belledonne Communications SARL, All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "registrardb.hh"
#include "registrardb-internal.hh"
#include "common.hh"

#include <ctime>
#include <cstdio>
#include <vector>
#include <algorithm>

#include <sofia-sip/sip_protos.h>

using namespace std;

RegistrarDbInternal::RegistrarDbInternal(const string &preferredRoute) : RegistrarDb(preferredRoute) {
}

void RegistrarDbInternal::doBind(const url_t *ifrom, sip_contact_t *icontact, const char *iid, uint32_t iseq,
					  const sip_path_t *ipath, list<string> acceptHeaders, bool usedAsRoute, int expire, int alias, int version, const std::shared_ptr<ContactUpdateListener> &listener) {
	string key = Record::defineKeyFromUrl(ifrom);
	time_t now = getCurrentTime();

	map<string, Record *>::iterator it = mRecords.find(key);
	Record *r;
	if (it == mRecords.end()) {
		r = new Record(ifrom);
		mRecords.insert(make_pair(key, r));
		LOGD("Creating AOR %s association", key.c_str());
	} else {
		LOGD("AOR %s found", key.c_str());
		r = (*it).second;
	}

	if (r->isInvalidRegister(iid, iseq)) {
		LOGD("Invalid register");
		if (listener) listener->onInvalid();
		return;
	}

	r->update(icontact, ipath, expire, iid, iseq, now, alias, acceptHeaders, usedAsRoute, listener);

	mLocalRegExpire->update(*r);
	if (listener) listener->onRecordFound(r);
}

void RegistrarDbInternal::doFetch(const url_t *url, const shared_ptr<ContactUpdateListener> &listener) {
	string key(Record::defineKeyFromUrl(url));

	map<string, Record *>::iterator it = mRecords.find(key);
	Record *r = NULL;
	if (it != mRecords.end()) {
		r = (*it).second;
		r->clean(getCurrentTime(), listener);
		if (r->isEmpty()) {
			mRecords.erase(it);
			r = NULL;
		}
	}

	listener->onRecordFound(r);
}

void RegistrarDbInternal::doFetchForGruu(const url_t *url, const string &gruu, const shared_ptr<ContactUpdateListener> &listener) {
	string key(Record::defineKeyFromUrl(url));
	SofiaAutoHome home;

	map<string, Record *>::iterator it = mRecords.find(key);
	Record *r = NULL;

	if (it == mRecords.end()) {
		listener->onRecordFound(r);
		return;
	}

	r = (*it).second;
	r->clean(getCurrentTime(), listener);
	if (r->isEmpty()) {
		mRecords.erase(it);
		r = NULL;
		listener->onRecordFound(r);
		return;
	}

	const std::list<std::shared_ptr<ExtendedContact>> contacts = r->getExtendedContacts();
	for (auto &contact : contacts) {
		if(!url_has_param(contact->mSipContact->m_url, "gr")) {
			r->removeContact(contact);
			continue;
		}
		char *buffer = new char[255];
		isize_t result = url_param(contact->mSipContact->m_url->url_params, "gr", buffer, 255);
		if(result <= 0) {
			r->removeContact(contact);
			continue;
		}
		stringstream stremGruu;
		stremGruu << "\"<" << buffer << ">\"";
		if(stremGruu.str() != gruu)
			r->removeContact(contact);
	}

	listener->onRecordFound(r);
}

void RegistrarDbInternal::doClear(const sip_t *sip, const shared_ptr<ContactUpdateListener> &listener) {
	string key(Record::defineKeyFromUrl(sip->sip_from->a_url));

	if (errorOnTooMuchContactInBind(sip->sip_contact, key, listener)) {
		listener->onError();
		return;
	}

	map<string, Record *>::iterator it = mRecords.find(key);

	if (it == mRecords.end()) {
		listener->onRecordFound(NULL);
		return;
	}

	LOGD("AOR %s found", key.c_str());
	Record *r = (*it).second;

	if (r->isInvalidRegister(sip->sip_call_id->i_id, sip->sip_cseq->cs_seq)) {
		listener->onInvalid();
		return;
	}

	mRecords.erase(it);
	mLocalRegExpire->remove(key);
	listener->onRecordFound(NULL);
}

void RegistrarDbInternal::doMigration() {

}

void RegistrarDbInternal::clearAll() {
	mRecords.clear();
	mLocalRegExpire->clearAll();
}

void RegistrarDbInternal::publish(const std::string &topic, const std::string &uid) {
	LOGD("Publish topic = %s, uid = %s", topic.c_str(), uid.c_str());
	RegistrarDb::notifyContactListener(topic, uid);
}
<commit_msg>fix internal db when fetching for gruu : do not remove extended contacts of the record<commit_after>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
	Copyright (C) 2010-2015  Belledonne Communications SARL, All rights reserved.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "registrardb.hh"
#include "registrardb-internal.hh"
#include "common.hh"

#include <ctime>
#include <cstdio>
#include <vector>
#include <algorithm>

#include <sofia-sip/sip_protos.h>

using namespace std;

RegistrarDbInternal::RegistrarDbInternal(const string &preferredRoute) : RegistrarDb(preferredRoute) {
}

void RegistrarDbInternal::doBind(const url_t *ifrom, sip_contact_t *icontact, const char *iid, uint32_t iseq,
						const sip_path_t *ipath, list<string> acceptHeaders, bool usedAsRoute, int expire, int alias, int version,
						const shared_ptr<ContactUpdateListener> &listener) {
	string key = Record::defineKeyFromUrl(ifrom);
	time_t now = getCurrentTime();

	map<string, Record *>::iterator it = mRecords.find(key);
	Record *r;
	if (it == mRecords.end()) {
		r = new Record(ifrom);
		mRecords.insert(make_pair(key, r));
		LOGD("Creating AOR %s association", key.c_str());
	} else {
		LOGD("AOR %s found", key.c_str());
		r = (*it).second;
	}

	if (r->isInvalidRegister(iid, iseq)) {
		LOGD("Invalid register");
		if (listener) listener->onInvalid();
		return;
	}

	r->update(icontact, ipath, expire, iid, iseq, now, alias, acceptHeaders, usedAsRoute, listener);

	mLocalRegExpire->update(*r);
	if (listener) listener->onRecordFound(r);
}

void RegistrarDbInternal::doFetch(const url_t *url, const shared_ptr<ContactUpdateListener> &listener) {
	string key(Record::defineKeyFromUrl(url));

	map<string, Record *>::iterator it = mRecords.find(key);
	Record *r = NULL;
	if (it != mRecords.end()) {
		r = (*it).second;
		r->clean(getCurrentTime(), listener);
		if (r->isEmpty()) {
			mRecords.erase(it);
			r = NULL;
		}
	}

	listener->onRecordFound(r);
}

void RegistrarDbInternal::doFetchForGruu(const url_t *url, const string &gruu, const shared_ptr<ContactUpdateListener> &listener) {
	string key(Record::defineKeyFromUrl(url));
	SofiaAutoHome home;

	map<string, Record *>::iterator it = mRecords.find(key);
	Record *r = NULL;

	if (it == mRecords.end()) {
		listener->onRecordFound(r);
		return;
	}

	r = (*it).second;
	r->clean(getCurrentTime(), listener);
	if (r->isEmpty()) {
		mRecords.erase(it);
		r = NULL;
		listener->onRecordFound(r);
		return;
	}

	const list<shared_ptr<ExtendedContact>> &contacts = r->getExtendedContacts();
	Record retRecord(url);
	for (auto &contact : contacts) {
		if (!url_has_param(contact->mSipContact->m_url, "gr"))
			continue;

		char buffer[255] = {0};
		isize_t result = url_param(contact->mSipContact->m_url->url_params, "gr", buffer, sizeof(buffer) - 1);
		if (result <= 0)
			continue;

		stringstream streamGruu;
		streamGruu << "\"<" << buffer << ">\"";
		if (streamGruu.str() != gruu)
			continue;

		retRecord.pushContact(contact);
	}

	listener->onRecordFound(&retRecord);
}

void RegistrarDbInternal::doClear(const sip_t *sip, const shared_ptr<ContactUpdateListener> &listener) {
	string key(Record::defineKeyFromUrl(sip->sip_from->a_url));

	if (errorOnTooMuchContactInBind(sip->sip_contact, key, listener)) {
		listener->onError();
		return;
	}

	map<string, Record *>::iterator it = mRecords.find(key);

	if (it == mRecords.end()) {
		listener->onRecordFound(NULL);
		return;
	}

	LOGD("AOR %s found", key.c_str());
	Record *r = (*it).second;

	if (r->isInvalidRegister(sip->sip_call_id->i_id, sip->sip_cseq->cs_seq)) {
		listener->onInvalid();
		return;
	}

	mRecords.erase(it);
	mLocalRegExpire->remove(key);
	listener->onRecordFound(NULL);
}

void RegistrarDbInternal::doMigration() {

}

void RegistrarDbInternal::clearAll() {
	mRecords.clear();
	mLocalRegExpire->clearAll();
}

void RegistrarDbInternal::publish(const string &topic, const string &uid) {
	LOGD("Publish topic = %s, uid = %s", topic.c_str(), uid.c_str());
	RegistrarDb::notifyContactListener(topic, uid);
}
<|endoftext|>