<commit_before>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2018 <NAME> <<EMAIL>>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero-flow/LiteralType.h>
#include <xzero-flow/TargetCodeGenerator.h>
#include <xzero-flow/ir/BasicBlock.h>
#include <xzero-flow/ir/ConstantArray.h>
#include <xzero-flow/ir/ConstantValue.h>
#include <xzero-flow/ir/IRBuiltinFunction.h>
#include <xzero-flow/ir/IRBuiltinHandler.h>
#include <xzero-flow/ir/IRHandler.h>
#include <xzero-flow/ir/IRProgram.h>
#include <xzero-flow/ir/Instructions.h>
#include <xzero-flow/util/assert.h>
#include <xzero-flow/vm/ConstantPool.h>
#include <xzero-flow/vm/Match.h>
#include <xzero-flow/vm/Program.h>

#include <array>
#include <cstdarg>
#include <limits>
#include <unordered_map>

namespace xzero::flow {

template <typename T, typename S>
std::vector<T> convert(const std::vector<Constant*>& source) {
  std::vector<T> target(source.size());

  for (size_t i = 0, e = source.size(); i != e; ++i)
    target[i] = static_cast<S*>(source[i])->get();

  return target;
}

TargetCodeGenerator::TargetCodeGenerator()
    : errors_(),
      conditionalJumps_(),
      unconditionalJumps_(),
      handlerId_(0),
      code_(),
      stack_(),
      cp_() {
}

TargetCodeGenerator::~TargetCodeGenerator() {
}

std::unique_ptr<Program> TargetCodeGenerator::generate(IRProgram* programIR) {
  for (IRHandler* handler : programIR->handlers())
    generate(handler);

  cp_.setModules(programIR->modules());

  return std::make_unique<Program>(std::move(cp_));
}

void TargetCodeGenerator::generate(IRHandler* handler) {
  // explicitely forward-declare handler, so we can use its ID internally.
  handlerId_ = cp_.makeHandler(handler);

  std::unordered_map<BasicBlock*, size_t> basicBlockEntryPoints;

  // generate code for all basic blocks, sequentially
  for (BasicBlock* bb : handler->basicBlocks()) {
    basicBlockEntryPoints[bb] = getInstructionPointer();
    for (Instr* instr : bb->instructions()) {
      instr->accept(*this);
    }
  }

  // fixiate conditional jump instructions
  for (const auto& target : conditionalJumps_) {
    size_t targetPC = basicBlockEntryPoints[target.first];
    for (const auto& source : target.second) {
      code_[source.pc] = makeInstruction(source.opcode, targetPC);
    }
  }
  conditionalJumps_.clear();

  // fixiate unconditional jump instructions
  for (const auto& target : unconditionalJumps_) {
    size_t targetPC = basicBlockEntryPoints[target.first];
    for (const auto& source : target.second) {
      code_[source.pc] = makeInstruction(source.opcode, targetPC);
    }
  }
  unconditionalJumps_.clear();

  // fixiate match jump table
  for (const auto& hint : matchHints_) {
    size_t matchId = hint.second;
    MatchInstr* matchInstr = hint.first;
    const auto& cases = matchInstr->cases();
    MatchDef& def = cp_.getMatchDef(matchId);

    for (size_t i = 0, e = cases.size(); i != e; ++i) {
      def.cases[i].pc = basicBlockEntryPoints[cases[i].second];
    }

    if (matchInstr->elseBlock()) {
      def.elsePC = basicBlockEntryPoints[matchInstr->elseBlock()];
    }
  }
  matchHints_.clear();

  cp_.getHandler(handlerId_).second = std::move(code_);

  // cleanup remaining handler-local work vars
  //FLOW_TRACE("flow: stack depth after handler code generation: {}", stack_.size());
  stack_.clear();
}

void TargetCodeGenerator::emitInstr(Instruction instr) {
  code_.push_back(instr);
}

void TargetCodeGenerator::emitCondJump(Opcode opcode, BasicBlock* bb) {
  const auto pc = getInstructionPointer();
  emitInstr(opcode);
  changeStack(1, nullptr);
  conditionalJumps_[bb].push_back({pc, opcode});
}

void TargetCodeGenerator::emitJump(BasicBlock* bb) {
  const auto pc = getInstructionPointer();
  emitInstr(Opcode::JMP);
  unconditionalJumps_[bb].push_back({pc, Opcode::JMP});
}

void TargetCodeGenerator::emitBinary(Instr& binaryInstr, Opcode opcode) {
  emitLoad(binaryInstr.operand(0));
  emitLoad(binaryInstr.operand(1));
  emitInstr(opcode);
  changeStack(2, &binaryInstr);
}

void TargetCodeGenerator::emitBinaryAssoc(Instr& binaryInstr, Opcode opcode) {
  // TODO: switch lhs and rhs if lhs is const and rhs is not
  // TODO: revive stack/imm opcodes
  emitLoad(binaryInstr.operand(0));
  emitLoad(binaryInstr.operand(1));
  emitInstr(opcode);
  changeStack(2, &binaryInstr);
}

void TargetCodeGenerator::emitUnary(Instr& unaryInstr, Opcode opcode) {
  emitLoad(unaryInstr.operand(0));
  emitInstr(opcode);
  changeStack(1, &unaryInstr);
}

StackPointer TargetCodeGenerator::getStackPointer(const Value* value) {
  for (size_t i = 0, e = stack_.size(); i != e; ++i)
    if (stack_[i] == value)
      return i;

  // ((Value*) value)->dump();
  return (StackPointer) -1;
}

void TargetCodeGenerator::changeStack(size_t pops, const Value* pushValue) {
  if (pops)
    pop(pops);

  if (pushValue)
    push(pushValue);
}

void TargetCodeGenerator::pop(size_t count) {
  FLOW_ASSERT(count < stack_.size(), "flow: BUG: stack smaller than amount of elements to pop.");

  for (size_t i = 0; i != count; i++)
    stack_.pop_back();
}

void TargetCodeGenerator::push(const Value* alias) {
  stack_.push_back(alias);
}

// {{{ instruction code generation
void TargetCodeGenerator::visit(NopInstr& nopInstr) {
  emitInstr(Opcode::NOP);
}

void TargetCodeGenerator::visit(AllocaInstr& allocaInstr) {
  emitInstr(Opcode::ALLOCA, 1);
  push(&allocaInstr);
}

// variable = expression
void TargetCodeGenerator::visit(StoreInstr& storeInstr) {
  StackPointer di = getStackPointer(storeInstr.variable());
  FLOW_ASSERT(di != size_t(-1), "BUG: StoreInstr.variable not found on stack");

  if (storeInstr.source()->uses().size() == 1 && stack_.back() == storeInstr.source()) {
    emitInstr(Opcode::STORE, di);
		changeStack(1, nullptr);
  } else {
    emitLoad(storeInstr.source());<commit_msg>[flow] fixes target code generation (stack pop assertion)<commit_after>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2018 <NAME> <<EMAIL>>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include <xzero-flow/LiteralType.h>
#include <xzero-flow/TargetCodeGenerator.h>
#include <xzero-flow/ir/BasicBlock.h>
#include <xzero-flow/ir/ConstantArray.h>
#include <xzero-flow/ir/ConstantValue.h>
#include <xzero-flow/ir/IRBuiltinFunction.h>
#include <xzero-flow/ir/IRBuiltinHandler.h>
#include <xzero-flow/ir/IRHandler.h>
#include <xzero-flow/ir/IRProgram.h>
#include <xzero-flow/ir/Instructions.h>
#include <xzero-flow/util/assert.h>
#include <xzero-flow/vm/ConstantPool.h>
#include <xzero-flow/vm/Match.h>
#include <xzero-flow/vm/Program.h>

#include <array>
#include <cstdarg>
#include <limits>
#include <unordered_map>

namespace xzero::flow {

template <typename T, typename S>
std::vector<T> convert(const std::vector<Constant*>& source) {
  std::vector<T> target(source.size());

  for (size_t i = 0, e = source.size(); i != e; ++i)
    target[i] = static_cast<S*>(source[i])->get();

  return target;
}

TargetCodeGenerator::TargetCodeGenerator()
    : errors_(),
      conditionalJumps_(),
      unconditionalJumps_(),
      handlerId_(0),
      code_(),
      stack_(),
      cp_() {
}

TargetCodeGenerator::~TargetCodeGenerator() {
}

std::unique_ptr<Program> TargetCodeGenerator::generate(IRProgram* programIR) {
  for (IRHandler* handler : programIR->handlers())
    generate(handler);

  cp_.setModules(programIR->modules());

  return std::make_unique<Program>(std::move(cp_));
}

void TargetCodeGenerator::generate(IRHandler* handler) {
  // explicitely forward-declare handler, so we can use its ID internally.
  handlerId_ = cp_.makeHandler(handler);

  std::unordered_map<BasicBlock*, size_t> basicBlockEntryPoints;

  // generate code for all basic blocks, sequentially
  for (BasicBlock* bb : handler->basicBlocks()) {
    basicBlockEntryPoints[bb] = getInstructionPointer();
    for (Instr* instr : bb->instructions()) {
      instr->accept(*this);
    }
  }

  // fixiate conditional jump instructions
  for (const auto& target : conditionalJumps_) {
    size_t targetPC = basicBlockEntryPoints[target.first];
    for (const auto& source : target.second) {
      code_[source.pc] = makeInstruction(source.opcode, targetPC);
    }
  }
  conditionalJumps_.clear();

  // fixiate unconditional jump instructions
  for (const auto& target : unconditionalJumps_) {
    size_t targetPC = basicBlockEntryPoints[target.first];
    for (const auto& source : target.second) {
      code_[source.pc] = makeInstruction(source.opcode, targetPC);
    }
  }
  unconditionalJumps_.clear();

  // fixiate match jump table
  for (const auto& hint : matchHints_) {
    size_t matchId = hint.second;
    MatchInstr* matchInstr = hint.first;
    const auto& cases = matchInstr->cases();
    MatchDef& def = cp_.getMatchDef(matchId);

    for (size_t i = 0, e = cases.size(); i != e; ++i) {
      def.cases[i].pc = basicBlockEntryPoints[cases[i].second];
    }

    if (matchInstr->elseBlock()) {
      def.elsePC = basicBlockEntryPoints[matchInstr->elseBlock()];
    }
  }
  matchHints_.clear();

  cp_.getHandler(handlerId_).second = std::move(code_);

  // cleanup remaining handler-local work vars
  //FLOW_TRACE("flow: stack depth after handler code generation: {}", stack_.size());
  stack_.clear();
}

void TargetCodeGenerator::emitInstr(Instruction instr) {
  code_.push_back(instr);
}

void TargetCodeGenerator::emitCondJump(Opcode opcode, BasicBlock* bb) {
  const auto pc = getInstructionPointer();
  emitInstr(opcode);
  changeStack(1, nullptr);
  conditionalJumps_[bb].push_back({pc, opcode});
}

void TargetCodeGenerator::emitJump(BasicBlock* bb) {
  const auto pc = getInstructionPointer();
  emitInstr(Opcode::JMP);
  unconditionalJumps_[bb].push_back({pc, Opcode::JMP});
}

void TargetCodeGenerator::emitBinary(Instr& binaryInstr, Opcode opcode) {
  emitLoad(binaryInstr.operand(0));
  emitLoad(binaryInstr.operand(1));
  emitInstr(opcode);
  changeStack(2, &binaryInstr);
}

void TargetCodeGenerator::emitBinaryAssoc(Instr& binaryInstr, Opcode opcode) {
  // TODO: switch lhs and rhs if lhs is const and rhs is not
  // TODO: revive stack/imm opcodes
  emitLoad(binaryInstr.operand(0));
  emitLoad(binaryInstr.operand(1));
  emitInstr(opcode);
  changeStack(2, &binaryInstr);
}

void TargetCodeGenerator::emitUnary(Instr& unaryInstr, Opcode opcode) {
  emitLoad(unaryInstr.operand(0));
  emitInstr(opcode);
  changeStack(1, &unaryInstr);
}

StackPointer TargetCodeGenerator::getStackPointer(const Value* value) {
  for (size_t i = 0, e = stack_.size(); i != e; ++i)
    if (stack_[i] == value)
      return i;

  // ((Value*) value)->dump();
  return (StackPointer) -1;
}

void TargetCodeGenerator::changeStack(size_t pops, const Value* pushValue) {
  if (pops)
    pop(pops);

  if (pushValue)
    push(pushValue);
}

void TargetCodeGenerator::pop(size_t count) {
  FLOW_ASSERT(count <= stack_.size(), "flow: BUG: stack smaller than amount of elements to pop.");

  for (size_t i = 0; i != count; i++)
    stack_.pop_back();
}

void TargetCodeGenerator::push(const Value* alias) {
  stack_.push_back(alias);
}

// {{{ instruction code generation
void TargetCodeGenerator::visit(NopInstr& nopInstr) {
  emitInstr(Opcode::NOP);
}

void TargetCodeGenerator::visit(AllocaInstr& allocaInstr) {
  emitInstr(Opcode::ALLOCA, 1);
  push(&allocaInstr);
}

// variable = expression
void TargetCodeGenerator::visit(StoreInstr& storeInstr) {
  StackPointer di = getStackPointer(storeInstr.variable());
  FLOW_ASSERT(di != size_t(-1), "BUG: StoreInstr.variable not found on stack");

  if (storeInstr.source()->uses().size() == 1 && stack_.back() == storeInstr.source()) {
    emitInstr(Opcode::STORE, di);
		changeStack(1, nullptr);
  } else {
    emitLoad(storeInstr.source());
{'fraction_non_alphanumeric': 0.1463716668042599, 'fraction_numerical': 0.0047882440353339386, 'mean_word_length': 4.671348314606742, 'pattern_counts': {'":': 0, '<': 63, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5510283', 'n_tokens_mistral': 4096, 'n_tokens_neox': 3941, 'n_words': 874}