<commit_before>#include "inner_readers.h"
#include "../flags.h"
#include <cassert>

using namespace dariadb;
using namespace dariadb::compression;
using namespace dariadb::storage;

InnerReader::InnerReader(dariadb::Flag flag, dariadb::Time from, dariadb::Time to) :
	_chunks{},
	_flag(flag),
	_from(from),
	_to(to),
	_tp_readed(false)
{
	is_time_point_reader = false;
	end = false;
}

void InnerReader::add(Chunk_Ptr c) {
	std::lock_guard<std::mutex> lg(_mutex);
    this->_chunks[c->first.id].push_back(c);
}

void InnerReader::add_tp(Chunk_Ptr c) {
	std::lock_guard<std::mutex> lg(_mutex);
    this->_tp_chunks[c->first.id].push_back(c);
}

bool InnerReader::isEnd() const {
	return this->end && this->_tp_readed;
}

dariadb::IdArray InnerReader::getIds()const {
	dariadb::IdArray result;
	result.resize(_chunks.size());
	size_t pos = 0;
	for (auto &kv : _chunks) {
		result[pos] = kv.first;
		pos++;
	}
	return result;
}

void InnerReader::readNext(storage::ReaderClb*clb) {
	std::lock_guard<std::mutex> lg(_mutex);

	if (!_tp_readed) {
        this->readTimePoint(clb);
	}

    for (auto ch : _chunks) {

        for (Chunk_Ptr cur_ch:ch.second) {
			if (cur_ch->is_dropped) {
				throw MAKE_EXCEPTION("InnerReader::cur_ch->is_dropped");
			}
            cur_ch->lock();
            auto bw = std::make_shared<BinaryBuffer>(cur_ch->bw->get_range());
            bw->reset_pos();
            CopmressedReader crr(bw, cur_ch->first);

            if (check_meas(cur_ch->first)) {
                auto sub = cur_ch->first;
                clb->call(sub);
            }

            for (size_t j = 0; j < cur_ch->count; j++) {
                auto sub = crr.read();
                sub.id = cur_ch->first.id;
                if (check_meas(sub)) {
                    clb->call(sub);
                }
                else {
                    if (sub.time > _to) {
                        cur_ch->unlock();
                        break;
                    }
                }
            }
            cur_ch->unlock();
        }
    }
    _chunks.clear();
	end = true;
}

void InnerReader::readTimePoint(storage::ReaderClb*clb) {
	std::lock_guard<std::mutex> lg(_mutex_tp);
    std::list<Chunk_Ptr> to_read_chunks{};
	for (auto cand_ch : _tp_chunks) {
		auto candidate = cand_ch.second.front();

        for (auto cur_chunk: cand_ch.second) {
            if (candidate->first.time < cur_chunk->first.time) {
                candidate = cur_chunk;
			}
		}
		to_read_chunks.push_back(candidate);
	}

	for (auto ch : to_read_chunks) {

        auto bw = std::make_shared<BinaryBuffer>(ch->bw->get_range());
		bw->reset_pos();
        CopmressedReader crr(bw, ch->first);

		Meas candidate;
        candidate = ch->first;
        ch->lock();
        for (size_t i = 0; i < ch->count; i++) {

			auto sub = crr.read();
            sub.id = ch->first.id;
			if ((sub.time <= _from) && (sub.time >= candidate.time)) {
				candidate = sub;
			}if (sub.time > _from) {
				break;
			}
		}
        ch->unlock();
		if (candidate.time <= _from) {
			//TODO make as options
			candidate.time = _from;

			clb->call(candidate);
			_tp_readed_times.insert(std::make_tuple(candidate.id, candidate.time));
		}
	}
	auto m = dariadb::Meas::empty();
	m.time = _from;
	m.flag = dariadb::Flags::NO_DATA;
	for (auto id : _not_exist) {
		m.id = id;
		clb->call(m);
	}
	_tp_readed = true;
}

bool InnerReader::check_meas(const Meas&m)const {
	auto tmp = std::make_tuple(m.id, m.time);
	if (this->_tp_readed_times.find(tmp) != _tp_readed_times.end()) {
		return false;
	}
	using utils::inInterval;

	if ((in_filter(_flag, m.flag)) && (inInterval(_from, _to, m.time))) {
		return true;
	}
	return false;
}

Reader_ptr InnerReader::clone()const {
	auto res = std::make_shared<InnerReader>(_flag, _from, _to);
	res->_chunks = _chunks;
	res->_tp_chunks = _tp_chunks;
	res->_flag = _flag;
	res->_from = _from;
	res->_to = _to;
	res->_tp_readed = _tp_readed;
	res->end = end;
	res->_not_exist = _not_exist;
	res->_tp_readed_times = _tp_readed_times;
	return res;
}
void InnerReader::reset() {
	end = false;
	_tp_readed = false;
	_tp_readed_times.clear();
}

InnerCurrentValuesReader::InnerCurrentValuesReader() {
	this->end = false;
}
InnerCurrentValuesReader::~InnerCurrentValuesReader() {}

bool InnerCurrentValuesReader::isEnd() const {
	return this->end;
}

void InnerCurrentValuesReader::readCurVals(storage::ReaderClb*clb) {
	for (auto v : _cur_values) {
		clb->call(v);
	}
}

void InnerCurrentValuesReader::readNext(storage::ReaderClb*clb) {
	std::lock_guard<std::mutex> lg(_mutex);
	readCurVals(clb);
	this->end = true;
}

IdArray InnerCurrentValuesReader::getIds()const {
	dariadb::IdArray result;
	result.resize(_cur_values.size());
	size_t pos = 0;
	for (auto v : _cur_values) {
		result[pos] = v.id;
		pos++;
	}
	return result;
}
Reader_ptr InnerCurrentValuesReader::clone()const {
	auto raw_reader = new InnerCurrentValuesReader();
	Reader_ptr result{ raw_reader };
	raw_reader->_cur_values = _cur_values;
	return result;
}
void InnerCurrentValuesReader::reset() {
	end = false;
}
<commit_msg>locks.<commit_after>#include "inner_readers.h"
#include "../flags.h"
#include <cassert>

using namespace dariadb;
using namespace dariadb::compression;
using namespace dariadb::storage;

InnerReader::InnerReader(dariadb::Flag flag, dariadb::Time from, dariadb::Time to) :
	_chunks{},
	_flag(flag),
	_from(from),
	_to(to),
	_tp_readed(false)
{
	is_time_point_reader = false;
	end = false;
}

void InnerReader::add(Chunk_Ptr c) {
	std::lock_guard<std::mutex> lg(_mutex);
    this->_chunks[c->first.id].push_back(c);
}

void InnerReader::add_tp(Chunk_Ptr c) {
	std::lock_guard<std::mutex> lg(_mutex);
    this->_tp_chunks[c->first.id].push_back(c);
}

bool InnerReader::isEnd() const {
	return this->end && this->_tp_readed;
}

dariadb::IdArray InnerReader::getIds()const {
	dariadb::IdArray result;
	result.resize(_chunks.size());
	size_t pos = 0;
	for (auto &kv : _chunks) {
		result[pos] = kv.first;
		pos++;
	}
	return result;
}

void InnerReader::readNext(storage::ReaderClb*clb) {
	std::lock_guard<std::mutex> lg(_mutex);

	if (!_tp_readed) {
        this->readTimePoint(clb);
	}

    for (auto ch : _chunks) {

        for (Chunk_Ptr cur_ch:ch.second) {
			if (cur_ch->is_dropped) {
				throw MAKE_EXCEPTION("InnerReader::cur_ch->is_dropped");
			}
            cur_ch->lock();
            auto bw = std::make_shared<BinaryBuffer>(cur_ch->bw->get_range());
            bw->reset_pos();
            CopmressedReader crr(bw, cur_ch->first);

            if (check_meas(cur_ch->first)) {
                auto sub = cur_ch->first;
                clb->call(sub);
            }

            for (size_t j = 0; j < cur_ch->count; j++) {
                auto sub = crr.read();
                sub.id = cur_ch->first.id;
                if (check_meas(sub)) {
                    clb->call(sub);
                }
                else {
                    if (sub.time > _to) {
                        break;
                    }
                }
            }
            cur_ch->unlock();
        }
    }
    _chunks.clear();
	end = true;
}

void InnerReader::readTimePoint(storage::ReaderClb*clb) {
	std::lock_guard<std::mutex> lg(_mutex_tp);
    std::list<Chunk_Ptr> to_read_chunks{};
	for (auto cand_ch : _tp_chunks) {
		auto candidate = cand_ch.second.front();

        for (auto cur_chunk: cand_ch.second) {
            if (candidate->first.time < cur_chunk->first.time) {
                candidate = cur_chunk;
			}
		}
		to_read_chunks.push_back(candidate);
	}

	for (auto ch : to_read_chunks) {

        auto bw = std::make_shared<BinaryBuffer>(ch->bw->get_range());
		bw->reset_pos();
        CopmressedReader crr(bw, ch->first);

		Meas candidate;
        candidate = ch->first;
        ch->lock();
        for (size_t i = 0; i < ch->count; i++) {

			auto sub = crr.read();
            sub.id = ch->first.id;
			if ((sub.time <= _from) && (sub.time >= candidate.time)) {
				candidate = sub;
			}if (sub.time > _from) {
				break;
			}
		}
        ch->unlock();
		if (candidate.time <= _from) {
			//TODO make as options
			candidate.time = _from;

			clb->call(candidate);
			_tp_readed_times.insert(std::make_tuple(candidate.id, candidate.time));
		}
	}
	auto m = dariadb::Meas::empty();
	m.time = _from;
	m.flag = dariadb::Flags::NO_DATA;
	for (auto id : _not_exist) {
		m.id = id;
		clb->call(m);
	}
	_tp_readed = true;
}

bool InnerReader::check_meas(const Meas&m)const {
	auto tmp = std::make_tuple(m.id, m.time);
	if (this->_tp_readed_times.find(tmp) != _tp_readed_times.end()) {
		return false;
	}
	using utils::inInterval;

	if ((in_filter(_flag, m.flag)) && (inInterval(_from, _to, m.time))) {
		return true;
	}
	return false;
}

Reader_ptr InnerReader::clone()const {
	auto res = std::make_shared<InnerReader>(_flag, _from, _to);
	res->_chunks = _chunks;
	res->_tp_chunks = _tp_chunks;
	res->_flag = _flag;
	res->_from = _from;
	res->_to = _to;
	res->_tp_readed = _tp_readed;
	res->end = end;
	res->_not_exist = _not_exist;
	res->_tp_readed_times = _tp_readed_times;
	return res;
}
void InnerReader::reset() {
	end = false;
	_tp_readed = false;
	_tp_readed_times.clear();
}

InnerCurrentValuesReader::InnerCurrentValuesReader() {
	this->end = false;
}
InnerCurrentValuesReader::~InnerCurrentValuesReader() {}

bool InnerCurrentValuesReader::isEnd() const {
	return this->end;
}

void InnerCurrentValuesReader::readCurVals(storage::ReaderClb*clb) {
	for (auto v : _cur_values) {
		clb->call(v);
	}
}

void InnerCurrentValuesReader::readNext(storage::ReaderClb*clb) {
	std::lock_guard<std::mutex> lg(_mutex);
	readCurVals(clb);
	this->end = true;
}

IdArray InnerCurrentValuesReader::getIds()const {
	dariadb::IdArray result;
	result.resize(_cur_values.size());
	size_t pos = 0;
	for (auto v : _cur_values) {
		result[pos] = v.id;
		pos++;
	}
	return result;
}
Reader_ptr InnerCurrentValuesReader::clone()const {
	auto raw_reader = new InnerCurrentValuesReader();
	Reader_ptr result{ raw_reader };
	raw_reader->_cur_values = _cur_values;
	return result;
}
void InnerCurrentValuesReader::reset() {
	end = false;
}
<|endoftext|>


<commit_before>//===- tools/seec-trace-print/OnlinePythonTutor.cpp -----------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#include "seec/Clang/MappedFunctionState.hpp"
#include "seec/Clang/MappedGlobalVariable.hpp"
#include "seec/Clang/MappedProcessState.hpp"
#include "seec/Clang/MappedProcessTrace.hpp"
#include "seec/Clang/MappedStateMovement.hpp"
#include "seec/Clang/MappedThreadState.hpp"
#include "seec/Trace/FunctionState.hpp"
#include "seec/Trace/TraceReader.hpp"
#include "seec/Util/Printing.hpp"

#include "clang/AST/Decl.h"

#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/raw_ostream.h"

using namespace seec;
using namespace seec::cm;
using namespace seec::util;

llvm::StringRef GetSingularMainFileContents(ProcessTrace const &Trace)
{
  auto const &CompileInfoMap = Trace.getMapping().getCompileInfoMap();
  if (CompileInfoMap.size() != 1)
    return llvm::StringRef();

  auto const FI = CompileInfoMap.cbegin()->second->getMainFileInfo();
  if (!FI)
    return llvm::StringRef();

  return FI->getContents().getBuffer();
}

class OPTPrinter {
  llvm::raw_ostream &Out;

  IndentationGuide Indent;

  ProcessTrace const &Trace;

  ProcessState Process;

  llvm::DenseMap<seec::trace::offset_uint, uint32_t> FrameIDMap;

  unsigned PreviousLine;

  OPTPrinter(llvm::raw_ostream &ToStream, ProcessTrace const &FromTrace)
  : Out(ToStream),
    Indent("  ", 1),
    Trace(FromTrace),
    Process(FromTrace),
    FrameIDMap(),
    PreviousLine(1)
  {}

  uint32_t getFrameID(FunctionState const &Function);

  void printValue(Value const &V);

  void printGlobal(GlobalVariable const &GV, std::string &NameOut);

  void printGlobals();

  void printParameter(ParamState const &Param, std::string &NameOut);

  void printLocal(LocalState const &Local, std::string &NameOut);

  void printFunction(FunctionState const &Function, bool IsActive);

  void printThread(ThreadState const &Thread);

  bool printAndMoveState();

  bool printAllStates();

public:
  static bool print(llvm::raw_ostream &Out, ProcessTrace const &Trace)
  {
    OPTPrinter Printer(Out, Trace);
    return Printer.printAllStates();
  }
};

uint32_t OPTPrinter::getFrameID(FunctionState const &Function)
{
  auto const &Trace = Function.getUnmappedState().getTrace();
  auto const Result = FrameIDMap.insert(std::make_pair(Trace.getEventStart(),
                                                       FrameIDMap.size() + 1));
  return Result.first->second;
}

void OPTPrinter::printValue(Value const &V)
{
  writeJSONStringLiteral(V.getValueAsStringFull(), Out);
}

void OPTPrinter::printGlobal(GlobalVariable const &GV, std::string &NameOut)
{
  NameOut = GV.getClangValueDecl()->getNameAsString();

  Out << Indent.getString();
  writeJSONStringLiteral(NameOut, Out);
  Out << ": ";

  if (auto const V = GV.getValue())
    printValue(*V);
  else
    Out << "\"<no value>\"";
}

void OPTPrinter::printGlobals()
{
  std::vector<std::string> OrderedNames;

  // globals: dict
  Out << Indent.getString() << "\"globals\": {\n";
  Indent.indent();

  bool PreviousPrinted = false;

  for (auto const &GV : Process.getGlobalVariables()) {
    if (!GV || (GV->isInSystemHeader() && !GV->isReferenced()))
      continue;

    if (PreviousPrinted)
      Out << ",\n";
    else
      PreviousPrinted = true;

    OrderedNames.emplace_back();
    printGlobal(*GV, OrderedNames.back());
  }

  Indent.unindent();
  Out << "\n" << Indent.getString() << "},\n";

  // ordered_globals: [string]
  Out << Indent.getString() << "\"ordered_globals\": [";
  for (std::size_t i = 0; i < OrderedNames.size(); ++i) {
    if (i != 0)
      Out << ", ";
    writeJSONStringLiteral(OrderedNames[i], Out);
  }
  Out << "],\n";
}

void OPTPrinter::printParameter(ParamState const &Param, std::string &NameOut)
{
  NameOut = Param.getDecl()->getNameAsString();

  Out << Indent.getString();
  writeJSONStringLiteral(NameOut, Out);
  Out << ": ";

  if (auto const V = Param.getValue())
    printValue(*V);
  else
    Out << "\"<no value>\"";
}

void OPTPrinter::printLocal(LocalState const &Local, std::string &NameOut)
{
  NameOut = Local.getDecl()->getNameAsString();

  Out << Indent.getString();
  writeJSONStringLiteral(NameOut, Out);
  Out << ": ";

  if (auto const V = Local.getValue())
    printValue(*V);
  else
    Out << "\"<no value>\"";
}

void OPTPrinter::printFunction(FunctionState const &Function, bool IsActive)
{
  Out << Indent.getString() << "{\n";
  Indent.indent();

  // func_name
  auto const FnName = Function.getNameAsString();
  Out << Indent.getString() << "\"func_name\": ";
  writeJSONStringLiteral(FnName, Out);
  Out << ",\n";

  // frame_id = unique key for this function call
  // unique_hash = func_name + frame_id
  auto const FrameID = getFrameID(Function);
  Out << Indent.getString() << "\"frame_id\": " << FrameID << ",\n";
  Out << Indent.getString() << "\"unique_hash\": ";
  writeJSONStringLiteral(FnName + std::to_string(FrameID), Out);
  Out << ",\n";

  // encoded_locals = dict
  std::vector<std::string> OrderedVarnames;

  Out << Indent.getString() << "\"encoded_locals\": {\n";
  Indent.indent();

  {
    bool PreviousPrinted = false;

    for (auto const &Param : Function.getParameters()) {
      if (PreviousPrinted)
        Out << ",\n";
      else
        PreviousPrinted = true;

      OrderedVarnames.emplace_back();
      printParameter(Param, OrderedVarnames.back());
    }

    for (auto const &Local : Function.getLocals()) {
      if (PreviousPrinted)
        Out << ",\n";
      else
        PreviousPrinted = true;

      OrderedVarnames.emplace_back();
      printLocal(Local, OrderedVarnames.back());
    }
  }

  Indent.unindent();
  Out << "\n" << Indent.getString() << "},\n";

  // ordered_varnames = [string]
  Out << Indent.getString() << "\"ordered_varnames\": [";
  for (std::size_t i = 0; i < OrderedVarnames.size(); ++i) {
    if (i != 0)
      Out << ", ";
    writeJSONStringLiteral(OrderedVarnames[i], Out);
  }
  Out << "],\n";

  // is_highlighted = ?
  Out << Indent.getString();
  if (IsActive)
    Out << "\"is_highlighted\": true,\n";
  else
    Out << "\"is_highlighted\": false,\n";

  // These are for Closures and Zombie Frames, so we don't need them.
  Out << Indent.getString() << "\"is_parent\": false,\n";
  Out << Indent.getString() << "\"is_zombie\": false,\n";
  Out << Indent.getString() << "\"parent_frame_id_list\": []\n";

  Indent.unindent();
  Out << Indent.getString() << "}";
}

void OPTPrinter::printThread(ThreadState const &Thread)
{
  auto const &Stack = Thread.getCallStack();

  // func_name
  // stack_to_render
  if (!Stack.empty()) {
    // Write the active function's name.
    auto const &Active = Stack.back().get();
    Out << Indent.getString() << "\"func_name\": ";
    writeJSONStringLiteral(Active.getNameAsString(), Out);
    Out << ",\n";

    // Write the stack.
    Out << Indent.getString() << "\"stack_to_render\": [\n";
    Indent.indent();

    bool PreviousPrinted = false;
    for (auto const &Fn : Stack) {
      if (PreviousPrinted)
        Out << ",\n";
      else
        PreviousPrinted = true;

      printFunction(Fn, &Fn.get() == &Active);
    }

    Indent.unindent();
    Out << "\n" << Indent.getString() << "],\n";
  }
  else {
    Out << Indent.getString() << "\"func_name\": \"<none>\",\n";
    Out << Indent.getString() << "\"stack_to_render\": [],\n";
  }

  // event: string
  Out << Indent.getString() << "\"event\": ";
  if (!Thread.isAtEnd())
    Out << "\"step_line\"";
  else
    Out << "\"return\"";
  Out << ",\n";
}

/// \brief Get the start line in the outermost file.
///
static unsigned getLineOutermost(clang::SourceLocation Start,
                                 clang::ASTContext const &AST)
{
  auto const &SourceManager = AST.getSourceManager();

  while (Start.isMacroID())
    Start = SourceManager.getExpansionLoc(Start);

  return SourceManager.getSpellingLineNumber(Start);
}

bool OPTPrinter::printAndMoveState()
{
  Out << Indent.getString() << "{\n";
  Indent.indent();

  printGlobals();
  printThread(Process.getThread(0));

  // stdout
  if (auto const Stream = Process.getStreamStdout()) {
    Out << Indent.getString() << "\"stdout\": ";
    writeJSONStringLiteral(Stream->getWritten(), Out);
    Out << ",\n";
  }
  else {
    Out << Indent.getString() << "\"stdout\": \"\",\n";
  }

  // TODO: heap
  Out << Indent.getString() << "\"heap\": {},\n";

  // Move now so that we can get the "next" line number.
  auto const Moved = moveForward(Process.getThread(0));

  // line: int
  auto const &Thread = Process.getThread(0);
  auto const &Stack = Thread.getCallStack();

  if (!Stack.empty()) {
    auto const &ActiveFn = Stack.back().get();
    auto const &AST = ActiveFn.getMappedAST()->getASTUnit().getASTContext();

    if (auto const ActiveStmt = ActiveFn.getActiveStmt())
      PreviousLine = getLineOutermost(ActiveStmt->getLocStart(), AST);
    else
      PreviousLine = getLineOutermost(ActiveFn.getFunctionDecl()->getLocStart(),
                                      AST);
  }

  Out << Indent.getString() << "\"line\": " << PreviousLine << "\n";

  Indent.unindent();
  Out << Indent.getString() << "}";

  return Moved;
}

bool OPTPrinter::printAllStates()
{
  // OnlinePythonTutor output only supports single-threaded programs.
  if (Process.getThreadCount() != 1)
    return false;

  Out << Indent.getString() << "{\n";
  Indent.indent();

  // Write the source code.
  auto const SourceCode = GetSingularMainFileContents(Trace);
  if (SourceCode.empty())
    return false;

  Out << Indent.getString() << "\"code\": ";
  writeJSONStringLiteral(SourceCode, Out);
  Out << ",\n";

  Out << Indent.getString() << "\"trace\": [\n";
  Indent.indent();

  while (printAndMoveState()) {
    Out << ",\n";
  }
  Out << "\n";

  Indent.unindent();
  Out << Indent.getString() << "]\n";

  Indent.unindent();
  Out << Indent.getString() << "}\n";

  return true;
}

void PrintOnlinePythonTutor(ProcessTrace const &Trace)
{
  OPTPrinter::print(llvm::outs(), Trace);
}
<commit_msg>Render scalar values as primitives when possible.<commit_after>//===- tools/seec-trace-print/OnlinePythonTutor.cpp -----------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#include "seec/Clang/MappedFunctionState.hpp"
#include "seec/Clang/MappedGlobalVariable.hpp"
#include "seec/Clang/MappedProcessState.hpp"
#include "seec/Clang/MappedProcessTrace.hpp"
#include "seec/Clang/MappedStateMovement.hpp"
#include "seec/Clang/MappedThreadState.hpp"
#include "seec/Trace/FunctionState.hpp"
#include "seec/Trace/TraceReader.hpp"
#include "seec/Util/Printing.hpp"

#include "clang/AST/Decl.h"

#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/raw_ostream.h"

using namespace seec;
using namespace seec::cm;
using namespace seec::util;

llvm::StringRef GetSingularMainFileContents(ProcessTrace const &Trace)
{
  auto const &CompileInfoMap = Trace.getMapping().getCompileInfoMap();
  if (CompileInfoMap.size() != 1)
    return llvm::StringRef();

  auto const FI = CompileInfoMap.cbegin()->second->getMainFileInfo();
  if (!FI)
    return llvm::StringRef();

  return FI->getContents().getBuffer();
}

class OPTPrinter {
  llvm::raw_ostream &Out;

  IndentationGuide Indent;

  ProcessTrace const &Trace;

  ProcessState Process;

  llvm::DenseMap<seec::trace::offset_uint, uint32_t> FrameIDMap;

  unsigned PreviousLine;

  OPTPrinter(llvm::raw_ostream &ToStream, ProcessTrace const &FromTrace)
  : Out(ToStream),
    Indent("  ", 1),
    Trace(FromTrace),
    Process(FromTrace),
    FrameIDMap(),
    PreviousLine(1)
  {}

  uint32_t getFrameID(FunctionState const &Function);

  void printValue(Value const &V);

  void printGlobal(GlobalVariable const &GV, std::string &NameOut);

  void printGlobals();

  void printParameter(ParamState const &Param, std::string &NameOut);

  void printLocal(LocalState const &Local, std::string &NameOut);

  void printFunction(FunctionState const &Function, bool IsActive);

  void printThread(ThreadState const &Thread);

  bool printAndMoveState();

  bool printAllStates();

public:
  static bool print(llvm::raw_ostream &Out, ProcessTrace const &Trace)
  {
    OPTPrinter Printer(Out, Trace);
    return Printer.printAllStates();
  }
};

uint32_t OPTPrinter::getFrameID(FunctionState const &Function)
{
  auto const &Trace = Function.getUnmappedState().getTrace();
  auto const Result = FrameIDMap.insert(std::make_pair(Trace.getEventStart(),
                                                       FrameIDMap.size() + 1));
  return Result.first->second;
}

void OPTPrinter::printValue(Value const &V)
{
  switch (V.getKind()) {
    case Value::Kind::Basic:
      writeJSONStringLiteral(V.getValueAsStringFull(), Out);
      break;

    case Value::Kind::Scalar:
      if (V.isCompletelyInitialized())
        Out << V.getValueAsStringFull();
      else
        writeJSONStringLiteral(V.getValueAsStringFull(), Out);
      break;

    case Value::Kind::Array:
      writeJSONStringLiteral("<cannot render correctly>", Out);
      break;

    case Value::Kind::Record:
      writeJSONStringLiteral("<cannot render correctly>", Out);
      break;

    case Value::Kind::Pointer:
      // TODO: Render as REF when heap rendering is working.
      writeJSONStringLiteral(V.getValueAsStringFull(), Out);
      break;

    case Value::Kind::PointerToFILE:
      writeJSONStringLiteral(V.getValueAsStringFull(), Out);
      break;
  }
}

void OPTPrinter::printGlobal(GlobalVariable const &GV, std::string &NameOut)
{
  NameOut = GV.getClangValueDecl()->getNameAsString();

  Out << Indent.getString();
  writeJSONStringLiteral(NameOut, Out);
  Out << ": ";

  if (auto const V = GV.getValue())
    printValue(*V);
  else
    Out << "\"<no value>\"";
}

void OPTPrinter::printGlobals()
{
  std::vector<std::string> OrderedNames;

  // globals: dict
  Out << Indent.getString() << "\"globals\": {\n";
  Indent.indent();

  bool PreviousPrinted = false;

  for (auto const &GV : Process.getGlobalVariables()) {
    if (!GV || (GV->isInSystemHeader() && !GV->isReferenced()))
      continue;

    if (PreviousPrinted)
      Out << ",\n";
    else
      PreviousPrinted = true;

    OrderedNames.emplace_back();
    printGlobal(*GV, OrderedNames.back());
  }

  Indent.unindent();
  Out << "\n" << Indent.getString() << "},\n";

  // ordered_globals: [string]
  Out << Indent.getString() << "\"ordered_globals\": [";
  for (std::size_t i = 0; i < OrderedNames.size(); ++i) {
    if (i != 0)
      Out << ", ";
    writeJSONStringLiteral(OrderedNames[i], Out);
  }
  Out << "],\n";
}

void OPTPrinter::printParameter(ParamState const &Param, std::string &NameOut)
{
  NameOut = Param.getDecl()->getNameAsString();

  Out << Indent.getString();
  writeJSONStringLiteral(NameOut, Out);
  Out << ": ";

  if (auto const V = Param.getValue())
    printValue(*V);
  else
    Out << "\"<no value>\"";
}

void OPTPrinter::printLocal(LocalState const &Local, std::string &NameOut)
{
  NameOut = Local.getDecl()->getNameAsString();

  Out << Indent.getString();
  writeJSONStringLiteral(NameOut, Out);
  Out << ": ";

  if (auto const V = Local.getValue())
    printValue(*V);
  else
    Out << "\"<no value>\"";
}

void OPTPrinter::printFunction(FunctionState const &Function, bool IsActive)
{
  Out << Indent.getString() << "{\n";
  Indent.indent();

  // func_name
  auto const FnName = Function.getNameAsString();
  Out << Indent.getString() << "\"func_name\": ";
  writeJSONStringLiteral(FnName, Out);
  Out << ",\n";

  // frame_id = unique key for this function call
  // unique_hash = func_name + frame_id
  auto const FrameID = getFrameID(Function);
  Out << Indent.getString() << "\"frame_id\": " << FrameID << ",\n";
  Out << Indent.getString() << "\"unique_hash\": ";
  writeJSONStringLiteral(FnName + std::to_string(FrameID), Out);
  Out << ",\n";

  // encoded_locals = dict
  std::vector<std::string> OrderedVarnames;

  Out << Indent.getString() << "\"encoded_locals\": {\n";
  Indent.indent();

  {
    bool PreviousPrinted = false;

    for (auto const &Param : Function.getParameters()) {
      if (PreviousPrinted)
        Out << ",\n";
      else
        PreviousPrinted = true;

      OrderedVarnames.emplace_back();
      printParameter(Param, OrderedVarnames.back());
    }

    for (auto const &Local : Function.getLocals()) {
      if (PreviousPrinted)
        Out << ",\n";
      else
        PreviousPrinted = true;

      OrderedVarnames.emplace_back();
      printLocal(Local, OrderedVarnames.back());
    }
  }

  Indent.unindent();
  Out << "\n" << Indent.getString() << "},\n";

  // ordered_varnames = [string]
  Out << Indent.getString() << "\"ordered_varnames\": [";
  for (std::size_t i = 0; i < OrderedVarnames.size(); ++i) {
    if (i != 0)
      Out << ", ";
    writeJSONStringLiteral(OrderedVarnames[i], Out);
  }
  Out << "],\n";

  // is_highlighted = ?
  Out << Indent.getString();
  if (IsActive)
    Out << "\"is_highlighted\": true,\n";
  else
    Out << "\"is_highlighted\": false,\n";

  // These are for Closures and Zombie Frames, so we don't need them.
  Out << Indent.getString() << "\"is_parent\": false,\n";
  Out << Indent.getString() << "\"is_zombie\": false,\n";
  Out << Indent.getString() << "\"parent_frame_id_list\": []\n";

  Indent.unindent();
  Out << Indent.getString() << "}";
}

void OPTPrinter::printThread(ThreadState const &Thread)
{
  auto const &Stack = Thread.getCallStack();

  // func_name
  // stack_to_render
  if (!Stack.empty()) {
    // Write the active function's name.
    auto const &Active = Stack.back().get();
    Out << Indent.getString() << "\"func_name\": ";
    writeJSONStringLiteral(Active.getNameAsString(), Out);
    Out << ",\n";

    // Write the stack.
    Out << Indent.getString() << "\"stack_to_render\": [\n";
    Indent.indent();

    bool PreviousPrinted = false;
    for (auto const &Fn : Stack) {
      if (PreviousPrinted)
        Out << ",\n";
      else
        PreviousPrinted = true;

      printFunction(Fn, &Fn.get() == &Active);
    }

    Indent.unindent();
    Out << "\n" << Indent.getString() << "],\n";
  }
  else {
    Out << Indent.getString() << "\"func_name\": \"<none>\",\n";
    Out << Indent.getString() << "\"stack_to_render\": [],\n";
  }

  // event: string
  Out << Indent.getString() << "\"event\": ";
  if (!Thread.isAtEnd())
    Out << "\"step_line\"";
  else
    Out << "\"return\"";
  Out << ",\n";
}

/// \brief Get the start line in the outermost file.
///
static unsigned getLineOutermost(clang::SourceLocation Start,
                                 clang::ASTContext const &AST)
{
  auto const &SourceManager = AST.getSourceManager();

  while (Start.isMacroID())
    Start = SourceManager.getExpansionLoc(Start);

  return SourceManager.getSpellingLineNumber(Start);
}

bool OPTPrinter::printAndMoveState()
{
  Out << Indent.getString() << "{\n";
  Indent.indent();

  printGlobals();
  printThread(Process.getThread(0));

  // stdout
  if (auto const Stream = Process.getStreamStdout()) {
    Out << Indent.getString() << "\"stdout\": ";
    writeJSONStringLiteral(Stream->getWritten(), Out);
    Out << ",\n";
  }
  else {
    Out << Indent.getString() << "\"stdout\": \"\",\n";
  }

  // TODO: heap
  Out << Indent.getString() << "\"heap\": {},\n";

  // Move now so that we can get the "next" line number.
  auto const Moved = moveForward(Process.getThread(0));

  // line: int
  auto const &Thread = Process.getThread(0);
  auto const &Stack = Thread.getCallStack();

  if (!Stack.empty()) {
    auto const &ActiveFn = Stack.back().get();
    auto const &AST = ActiveFn.getMappedAST()->getASTUnit().getASTContext();

    if (auto const ActiveStmt = ActiveFn.getActiveStmt())
      PreviousLine = getLineOutermost(ActiveStmt->getLocStart(), AST);
    else
      PreviousLine = getLineOutermost(ActiveFn.getFunctionDecl()->getLocStart(),
                                      AST);
  }

  Out << Indent.getString() << "\"line\": " << PreviousLine << "\n";

  Indent.unindent();
  Out << Indent.getString() << "}";

  return Moved;
}

bool OPTPrinter::printAllStates()
{
  // OnlinePythonTutor output only supports single-threaded programs.
  if (Process.getThreadCount() != 1)
    return false;

  Out << Indent.getString() << "{\n";
  Indent.indent();

  // Write the source code.
  auto const SourceCode = GetSingularMainFileContents(Trace);
  if (SourceCode.empty())
    return false;

  Out << Indent.getString() << "\"code\": ";
  writeJSONStringLiteral(SourceCode, Out);
  Out << ",\n";

  Out << Indent.getString() << "\"trace\": [\n";
  Indent.indent();

  while (printAndMoveState()) {
    Out << ",\n";
  }
  Out << "\n";

  Indent.unindent();
  Out << Indent.getString() << "]\n";

  Indent.unindent();
  Out << Indent.getString() << "}\n";

  return true;
}

void PrintOnlinePythonTutor(ProcessTrace const &Trace)
{
  OPTPrinter::print(llvm::outs(), Trace);
}
<|endoftext|>


<commit_before>/// \file ROOT/RPageStorageDaos.hxx
/// \ingroup NTuple ROOT7
/// \author Javier Lopez-Gomez <j.lopez@cern.ch>
/// \date 2020-11-03
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT7_RPageStorageDaos
#define ROOT7_RPageStorageDaos

#include <ROOT/RError.hxx>
#include <ROOT/RPageStorage.hxx>
#include <ROOT/RNTupleSerialize.hxx>
#include <ROOT/RNTupleZip.hxx>
#include <ROOT/RStringView.hxx>

#include <array>
#include <atomic>
#include <cstdio>
#include <memory>
#include <string>

namespace ROOT {

namespace Experimental {
namespace Detail {

class RCluster;
class RClusterPool;
class RPageAllocatorHeap;
class RPagePool;
class RDaosPool;
class RDaosContainer;

// clang-format off
/**
\class ROOT::Experimental::Detail::RDaosNTupleAnchor
\ingroup NTuple
\brief Entry point for an RNTuple in a DAOS container. It encodes essential
information to read the ntuple; currently, it contains (un)compressed size of
the header/footer blobs and the object class for user data OIDs.
The length of a serialized anchor cannot be greater than the value returned by the `GetSize` function.
*/
// clang-format on
struct RDaosNTupleAnchor {
   /// Allows for evolving the struct in future versions
   std::uint32_t fVersion = 0;
   /// The size of the compressed ntuple header
   std::uint32_t fNBytesHeader = 0;
   /// The size of the uncompressed ntuple header
   std::uint32_t fLenHeader = 0;
   /// The size of the compressed ntuple footer
   std::uint32_t fNBytesFooter = 0;
   /// The size of the uncompressed ntuple footer
   std::uint32_t fLenFooter = 0;
   /// The object class for user data OIDs, e.g. `SX`
   std::string fObjClass{};

   bool operator ==(const RDaosNTupleAnchor &other) const {
      return fVersion == other.fVersion &&
         fNBytesHeader == other.fNBytesHeader &&
         fLenHeader == other.fLenHeader &&
         fNBytesFooter == other.fNBytesFooter &&
         fLenFooter == other.fLenFooter &&
         fObjClass == other.fObjClass;
   }

   std::uint32_t Serialize(void *buffer) const;
   RResult<std::uint32_t> Deserialize(const void *buffer, std::uint32_t bufSize);

   static std::uint32_t GetSize();
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageSinkDaos
\ingroup NTuple
\brief Storage provider that writes ntuple pages to into a DAOS container

Currently, an object is allocated for each page + 3 additional objects (anchor/header/footer).
*/
// clang-format on
class RPageSinkDaos : public RPageSink {
private:
   std::unique_ptr<RPageAllocatorHeap> fPageAllocator;

   /// \brief Underlying DAOS container. An internal `std::shared_ptr` keep the pool connection alive.
   /// ISO C++ ensures the correct destruction order, i.e., `~RDaosContainer` is invoked first
   /// (which calls `daos_cont_close()`; the destructor for the `std::shared_ptr<RDaosPool>` is invoked
   /// after (which calls `daos_pool_disconect()`).
   std::unique_ptr<RDaosContainer> fDaosContainer;
   /// OID for the next committed page; it is automatically incremented in `CommitSealedPageImpl()`
   std::atomic<std::uint64_t> fOid{0};
   /// \brief A URI to a DAOS pool of the form 'daos://pool-uuid:svc_replicas/container-uuid'
   std::string fURI;
   /// Tracks the number of bytes committed to the current cluster
   std::uint64_t fNBytesCurrentCluster{0};
   /// Used to keeo the column and field IDs issued during header serialization for the footer serialization
   Internal::RNTupleSerializer::RContext fSerializationContext;

   RDaosNTupleAnchor fNTupleAnchor;

protected:
   void CreateImpl(const RNTupleModel &model) final;
   RNTupleLocator CommitPageImpl(ColumnHandle_t columnHandle, const RPage &page) final;
   RNTupleLocator CommitSealedPageImpl(DescriptorId_t columnId, const RPageStorage::RSealedPage &sealedPage) final;
   std::uint64_t CommitClusterImpl(NTupleSize_t nEntries) final;
   void CommitDatasetImpl() final;
   void WriteNTupleHeader(const void *data, size_t nbytes, size_t lenHeader);
   void WriteNTupleFooter(const void *data, size_t nbytes, size_t lenFooter);
   void WriteNTupleAnchor();

public:
   RPageSinkDaos(std::string_view ntupleName, std::string_view uri, const RNTupleWriteOptions &options);
   virtual ~RPageSinkDaos();

   RPage ReservePage(ColumnHandle_t columnHandle, std::size_t nElements) final;
   void ReleasePage(RPage &page) final;
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageAllocatorDaos
\ingroup NTuple
\brief Manages pages read from a DAOS container
*/
// clang-format on
class RPageAllocatorDaos {
public:
   static RPage NewPage(ColumnId_t columnId, void *mem, std::size_t elementSize, std::size_t nElements);
   static void DeletePage(const RPage& page);
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageSourceDaos
\ingroup NTuple
\brief Storage provider that reads ntuple pages from a DAOS container
*/
// clang-format on
class RPageSourceDaos : public RPageSource {
private:
   /// Populated pages might be shared; the memory buffer is managed by the RPageAllocatorDaos
   std::unique_ptr<RPageAllocatorDaos> fPageAllocator;
   // TODO: the page pool should probably be handled by the base class.
   /// The page pool might, at some point, be used by multiple page sources
   std::shared_ptr<RPagePool> fPagePool;
   /// The last cluster from which a page got populated.  Points into fClusterPool->fPool
   RCluster *fCurrentCluster = nullptr;
   /// A container that stores object data (header/footer, pages, etc.)
   std::unique_ptr<RDaosContainer> fDaosContainer;
   /// A URI to a DAOS pool of the form 'daos://pool-uuid:svc_replicas/container-uuid'
   std::string fURI;
   /// The cluster pool asynchronously preloads the next few clusters
   std::unique_ptr<RClusterPool> fClusterPool;

   RPage PopulatePageFromCluster(ColumnHandle_t columnHandle, const RClusterDescriptor &clusterDescriptor,
                                 ClusterSize_t::ValueType idxInCluster);

protected:
   RNTupleDescriptor AttachImpl() final;
   void UnzipClusterImpl(RCluster *cluster) final;

public:
   RPageSourceDaos(std::string_view ntupleName, std::string_view uri, const RNTupleReadOptions &options);
   /// The cloned page source creates a new connection to the pool/container.
   /// The meta-data (header and footer) is reread and parsed by the clone.
   std::unique_ptr<RPageSource> Clone() const final;
   virtual ~RPageSourceDaos();

   RPage PopulatePage(ColumnHandle_t columnHandle, NTupleSize_t globalIndex) final;
   RPage PopulatePage(ColumnHandle_t columnHandle, const RClusterIndex &clusterIndex) final;
   void ReleasePage(RPage &page) final;

   void LoadSealedPage(DescriptorId_t columnId, const RClusterIndex &clusterIndex,
                       RSealedPage &sealedPage) final;

   std::vector<std::unique_ptr<RCluster>> LoadClusters(std::span<RCluster::RKey> clusterKeys) final;

   /// Return the object class used for user data OIDs in this ntuple.
   std::string GetObjectClass() const;
};

} // namespace Detail

} // namespace Experimental
} // namespace ROOT

#endif
<commit_msg>[ntuple] Fix typo in code comment (NFC)<commit_after>/// \file ROOT/RPageStorageDaos.hxx
/// \ingroup NTuple ROOT7
/// \author Javier Lopez-Gomez <j.lopez@cern.ch>
/// \date 2020-11-03
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT7_RPageStorageDaos
#define ROOT7_RPageStorageDaos

#include <ROOT/RError.hxx>
#include <ROOT/RPageStorage.hxx>
#include <ROOT/RNTupleSerialize.hxx>
#include <ROOT/RNTupleZip.hxx>
#include <ROOT/RStringView.hxx>

#include <array>
#include <atomic>
#include <cstdio>
#include <memory>
#include <string>

namespace ROOT {

namespace Experimental {
namespace Detail {

class RCluster;
class RClusterPool;
class RPageAllocatorHeap;
class RPagePool;
class RDaosPool;
class RDaosContainer;

// clang-format off
/**
\class ROOT::Experimental::Detail::RDaosNTupleAnchor
\ingroup NTuple
\brief Entry point for an RNTuple in a DAOS container. It encodes essential
information to read the ntuple; currently, it contains (un)compressed size of
the header/footer blobs and the object class for user data OIDs.
The length of a serialized anchor cannot be greater than the value returned by the `GetSize` function.
*/
// clang-format on
struct RDaosNTupleAnchor {
   /// Allows for evolving the struct in future versions
   std::uint32_t fVersion = 0;
   /// The size of the compressed ntuple header
   std::uint32_t fNBytesHeader = 0;
   /// The size of the uncompressed ntuple header
   std::uint32_t fLenHeader = 0;
   /// The size of the compressed ntuple footer
   std::uint32_t fNBytesFooter = 0;
   /// The size of the uncompressed ntuple footer
   std::uint32_t fLenFooter = 0;
   /// The object class for user data OIDs, e.g. `SX`
   std::string fObjClass{};

   bool operator ==(const RDaosNTupleAnchor &other) const {
      return fVersion == other.fVersion &&
         fNBytesHeader == other.fNBytesHeader &&
         fLenHeader == other.fLenHeader &&
         fNBytesFooter == other.fNBytesFooter &&
         fLenFooter == other.fLenFooter &&
         fObjClass == other.fObjClass;
   }

   std::uint32_t Serialize(void *buffer) const;
   RResult<std::uint32_t> Deserialize(const void *buffer, std::uint32_t bufSize);

   static std::uint32_t GetSize();
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageSinkDaos
\ingroup NTuple
\brief Storage provider that writes ntuple pages to into a DAOS container

Currently, an object is allocated for each page + 3 additional objects (anchor/header/footer).
*/
// clang-format on
class RPageSinkDaos : public RPageSink {
private:
   std::unique_ptr<RPageAllocatorHeap> fPageAllocator;

   /// \brief Underlying DAOS container. An internal `std::shared_ptr` keep the pool connection alive.
   /// ISO C++ ensures the correct destruction order, i.e., `~RDaosContainer` is invoked first
   /// (which calls `daos_cont_close()`; the destructor for the `std::shared_ptr<RDaosPool>` is invoked
   /// after (which calls `daos_pool_disconect()`).
   std::unique_ptr<RDaosContainer> fDaosContainer;
   /// OID for the next committed page; it is automatically incremented in `CommitSealedPageImpl()`
   std::atomic<std::uint64_t> fOid{0};
   /// \brief A URI to a DAOS pool of the form 'daos://pool-uuid:svc_replicas/container-uuid'
   std::string fURI;
   /// Tracks the number of bytes committed to the current cluster
   std::uint64_t fNBytesCurrentCluster{0};
   /// Used to keep the column and field IDs issued during header serialization for the footer serialization
   Internal::RNTupleSerializer::RContext fSerializationContext;

   RDaosNTupleAnchor fNTupleAnchor;

protected:
   void CreateImpl(const RNTupleModel &model) final;
   RNTupleLocator CommitPageImpl(ColumnHandle_t columnHandle, const RPage &page) final;
   RNTupleLocator CommitSealedPageImpl(DescriptorId_t columnId, const RPageStorage::RSealedPage &sealedPage) final;
   std::uint64_t CommitClusterImpl(NTupleSize_t nEntries) final;
   void CommitDatasetImpl() final;
   void WriteNTupleHeader(const void *data, size_t nbytes, size_t lenHeader);
   void WriteNTupleFooter(const void *data, size_t nbytes, size_t lenFooter);
   void WriteNTupleAnchor();

public:
   RPageSinkDaos(std::string_view ntupleName, std::string_view uri, const RNTupleWriteOptions &options);
   virtual ~RPageSinkDaos();

   RPage ReservePage(ColumnHandle_t columnHandle, std::size_t nElements) final;
   void ReleasePage(RPage &page) final;
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageAllocatorDaos
\ingroup NTuple
\brief Manages pages read from a DAOS container
*/
// clang-format on
class RPageAllocatorDaos {
public:
   static RPage NewPage(ColumnId_t columnId, void *mem, std::size_t elementSize, std::size_t nElements);
   static void DeletePage(const RPage& page);
};

// clang-format off
/**
\class ROOT::Experimental::Detail::RPageSourceDaos
\ingroup NTuple
\brief Storage provider that reads ntuple pages from a DAOS container
*/
// clang-format on
class RPageSourceDaos : public RPageSource {
private:
   /// Populated pages might be shared; the memory buffer is managed by the RPageAllocatorDaos
   std::unique_ptr<RPageAllocatorDaos> fPageAllocator;
   // TODO: the page pool should probably be handled by the base class.
   /// The page pool might, at some point, be used by multiple page sources
   std::shared_ptr<RPagePool> fPagePool;
   /// The last cluster from which a page got populated.  Points into fClusterPool->fPool
   RCluster *fCurrentCluster = nullptr;
   /// A container that stores object data (header/footer, pages, etc.)
   std::unique_ptr<RDaosContainer> fDaosContainer;
   /// A URI to a DAOS pool of the form 'daos://pool-uuid:svc_replicas/container-uuid'
   std::string fURI;
   /// The cluster pool asynchronously preloads the next few clusters
   std::unique_ptr<RClusterPool> fClusterPool;

   RPage PopulatePageFromCluster(ColumnHandle_t columnHandle, const RClusterDescriptor &clusterDescriptor,
                                 ClusterSize_t::ValueType idxInCluster);

protected:
   RNTupleDescriptor AttachImpl() final;
   void UnzipClusterImpl(RCluster *cluster) final;

public:
   RPageSourceDaos(std::string_view ntupleName, std::string_view uri, const RNTupleReadOptions &options);
   /// The cloned page source creates a new connection to the pool/container.
   /// The meta-data (header and footer) is reread and parsed by the clone.
   std::unique_ptr<RPageSource> Clone() const final;
   virtual ~RPageSourceDaos();

   RPage PopulatePage(ColumnHandle_t columnHandle, NTupleSize_t globalIndex) final;
   RPage PopulatePage(ColumnHandle_t columnHandle, const RClusterIndex &clusterIndex) final;
   void ReleasePage(RPage &page) final;

   void LoadSealedPage(DescriptorId_t columnId, const RClusterIndex &clusterIndex,
                       RSealedPage &sealedPage) final;

   std::vector<std::unique_ptr<RCluster>> LoadClusters(std::span<RCluster::RKey> clusterKeys) final;

   /// Return the object class used for user data OIDs in this ntuple.
   std::string GetObjectClass() const;
};

} // namespace Detail

} // namespace Experimental
} // namespace ROOT

#endif
<|endoftext|>


<commit_before>/*
 * File:   DebugCommunicator.cpp
 * Author: thomas
 *
 * Created on 8. march 2009, 19:31
 */

#include <stdlib.h>
#include <limits.h>

#include <assert.h>
#include <vector>
#include <string>

#include <iostream>
#include <sstream>

#include <Tools/NaoTime.h>

#include "DebugCommunicator.h"

DebugCommunicator::DebugCommunicator()
  :
    serverSocket(NULL),
    connection(NULL),
    port(-1),
    fatalFail(false),
    time_of_last_message(0),
    time_out_delta(5) // seconds
{
}

DebugCommunicator::~DebugCommunicator()
{
  disconnect();
  closeServerSocket();
}

void DebugCommunicator::init(unsigned short portNum)
{
  port = portNum;

  GError* err = internalInit();
  if (err)
  {
    fatalFail = true;
    std::cerr << "[DebugServer:port " << port << "] " << "No communication available. Error message:\n" << err->message << std::endl;
  } else {
    std::cout << "[DebugServer:port " << port << "] " << "is ready for incoming connections." << std::endl;
  }
}//end init

GError* DebugCommunicator::internalInit()
{
  GError* err = NULL;

  serverSocket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &err);
  if (err) { return err; }

  g_socket_set_blocking(serverSocket, true);

  GInetAddress* inetAddress = g_inet_address_new_any(G_SOCKET_FAMILY_IPV4);
  GSocketAddress* socketAddress = g_inet_socket_address_new(inetAddress, port);

  g_socket_bind(serverSocket, socketAddress, true, &err);
  if (err) { return err; }

  g_socket_listen(serverSocket, &err);
  if (err) { return err; }

  return NULL;
}

bool DebugCommunicator::sendMessage(gint32 id, const char* data, size_t size)
{
  if (fatalFail) {
    throw "fatalFail";
  }

  GError* err = internalSendMessage(id, data, size);
  if (err)
  {
    std::cerr << "[DebugServer:port " << port << "] " << "SocketException in sendMessage: " << err->message << std::endl;
    g_error_free(err);
    return false;
  }
  return true;
}//end sendMessage

GError* DebugCommunicator::internalSendMessage(gint32 id, const char* data, size_t size)
{
  GError* err = NULL;
  if (connection != NULL)
  {
    g_socket_set_blocking(connection, true);

    // send the message id
    g_socket_send(connection, (char*) &id, 4, NULL, &err);

    // send message size in 4 bytes
    guint32 sizeFixed = static_cast<unsigned int> (size);
    g_socket_send(connection, (char*) &sizeFixed, 4, NULL, &err);

    gsize pos = 0;
    while(err == NULL && connection != NULL && pos < size)
    {
      gsize length = size-pos;

      gsize sent = g_socket_send(connection, data+pos, length, NULL, &err);
      pos += sent;
    }
  }

  return err;
}//end sendMessage

GString* DebugCommunicator::readMessage(gint32& id)
{
  if (fatalFail) {
    throw "fatalFail";
  }

  GError* err = NULL;
  GString* result = NULL;
  id = internalReadMessage(&result, &err);

  if(err)
  {
    if(err->code != G_IO_ERROR_WOULD_BLOCK)
    {
      std::cerr << "[DebugServer:port " << port << "] " << "ERROR: (SocketException in readMessage) "
                << err->message << std::endl;
      g_error_free(err);

      if(result != NULL) {
        g_string_free(result,true);
      }

      throw "Socket Exception";
    }
    else
    {
      g_error_free(err);
    }
  }
  return result;
}//end readMessage

gint32 DebugCommunicator::internalReadMessage(GString** result, GError** err)
{
  *err = NULL;
  if (connection == NULL) {
    return -1;
  }

  // read the message id
  gint32 messageId = -1;
  gssize initialBytes = g_socket_receive_with_blocking(connection, (char*) &messageId, 4, false, NULL, err);

  // nothing to read
  if ( initialBytes == -1 ) {
    // no data was received for a while, time out
    unsigned long long current_time = naoth::NaoTime::getSystemTimeInMilliSeconds();
    if( time_of_last_message > 0 &&
        time_out_delta > 0 &&
        current_time > time_of_last_message + time_out_delta*1000 )
    {
      throw "connection timed out";
    }

    return -1;
  }

  if(initialBytes == 0) {
    throw "connection closed by peer";
  }

  if(initialBytes != 4) {
    throw "error reading message id";
  }

  // some data was received, so keep the connection alive
  time_of_last_message = naoth::NaoTime::getSystemTimeInMilliSeconds();

  // heartbeat message, ignore
  if(messageId == -1) {
    // std::cout << "heartbeat received" << std::endl;
    return -1;
  }

  gint32 sizeOfMessage = 0;
  if(g_socket_receive_with_blocking(connection, (char*) &sizeOfMessage, 4, true, NULL, err) != 4) {
    throw "error reading message size";
  }

  if(sizeOfMessage > 0)
  {
    GString* buffer = g_string_new("");
    g_string_set_size(buffer, sizeOfMessage);
    g_assert(buffer->len == (guint32)sizeOfMessage);

    // read message completly
    guint32 pos = 0;
    while(*err == NULL && pos < (guint32)sizeOfMessage)
    {
      gssize read_bytes =
          g_socket_receive_with_blocking(connection, buffer->str + pos, sizeOfMessage - pos, true ,NULL, err);

      if(read_bytes == 0)
      {
        g_string_free(buffer, true);
        buffer = NULL;
        // we got 0 bytes, this indicates
        // an error in the connection (-1 is set if no data was available
        // in non-blocking mode)
        throw "Connection Error";
      }
      pos += static_cast<unsigned int> (read_bytes);
    }

    if(*err) {
      g_string_free(buffer, true);
      return -1;
    }

    *result = buffer;
    return messageId;
  }

  return -1;
}

bool DebugCommunicator::connect(int timeout)
{
  GError* err = NULL;

  // TODO: use isConnected()?
  // already connected
  if (connection != NULL) {
    return true;
  }

  // TODO: throw?
  // not initialized
  if (serverSocket == NULL) {
    // re-establish server socket to accept new connections
    init(port);
    if (serverSocket == NULL) {
      return false;
    }
  }

  // prepare the server socket
  if(timeout < 0) {
    g_socket_set_blocking(serverSocket, false);
  } else {
    g_socket_set_blocking(serverSocket, true);
    g_socket_set_timeout(serverSocket, timeout);
  }

  // try to accept an eventually pending connection request
  connection = g_socket_accept(serverSocket, NULL, &err);

  // TODO: throw?
  if (err) {
    return false;
  }

  if (connection != NULL)
  {
    GInetSocketAddress* remoteSocketAddr =
      G_INET_SOCKET_ADDRESS(g_socket_get_remote_address(connection, &err));

    if (err)
    {
      // cleanup
      g_socket_close(connection, NULL);
      g_object_unref(connection);
      connection = NULL;
      return false;
    }

    // print the address
    GInetAddress* addr = g_inet_socket_address_get_address(remoteSocketAddr);
    char* addrString = g_inet_address_to_string(addr);
    std::cout << "[DebugServer:port " << port << "] " << "connected to " << addrString << std::endl;
    g_free(addrString);
    g_object_unref(remoteSocketAddr);

    // init the time stamp for the time out
    time_of_last_message = naoth::NaoTime::getSystemTimeInMilliSeconds();
    g_socket_set_timeout(connection, time_out_delta);

    // close the server socket and reject further connections
    closeServerSocket();

    return true;
  }

  return false;
}

void DebugCommunicator::closeServerSocket()
{
  if (serverSocket != NULL)
  {
    g_socket_close(serverSocket, NULL);
    g_object_unref(serverSocket);
    serverSocket = NULL;
  }
}

void DebugCommunicator::disconnect()
{
  if (connection != NULL)
  {
    g_socket_close(connection, NULL);
    g_object_unref(connection);
    connection = NULL;
    std::cerr << "[DebugServer:port " << port << "] " << "disconnected" << std::endl;
  }
}

bool DebugCommunicator::isConnected()
{
  return connection != NULL && g_socket_is_connected(connection);
}

<commit_msg>Remove warnings in DebugCommunicator<commit_after>/*
 * File:   DebugCommunicator.cpp
 * Author: thomas
 *
 * Created on 8. march 2009, 19:31
 */

#include <stdlib.h>
#include <limits.h>

#include <assert.h>
#include <vector>
#include <string>

#include <iostream>
#include <sstream>

#include <Tools/NaoTime.h>

#include "DebugCommunicator.h"

DebugCommunicator::DebugCommunicator()
  :
    serverSocket(NULL),
    connection(NULL),
    port(0),
    fatalFail(false),
    time_of_last_message(0),
    time_out_delta(5) // seconds
{
}

DebugCommunicator::~DebugCommunicator()
{
  disconnect();
  closeServerSocket();
}

void DebugCommunicator::init(unsigned short portNum)
{
  port = portNum;

  GError* err = internalInit();
  if (err)
  {
    fatalFail = true;
    std::cerr << "[DebugServer:port " << port << "] " << "No communication available. Error message:\n" << err->message << std::endl;
  } else {
    std::cout << "[DebugServer:port " << port << "] " << "is ready for incoming connections." << std::endl;
  }
}//end init

GError* DebugCommunicator::internalInit()
{
  GError* err = NULL;

  serverSocket = g_socket_new(G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_TCP, &err);
  if (err) { return err; }

  g_socket_set_blocking(serverSocket, true);

  GInetAddress* inetAddress = g_inet_address_new_any(G_SOCKET_FAMILY_IPV4);
  GSocketAddress* socketAddress = g_inet_socket_address_new(inetAddress, port);

  g_socket_bind(serverSocket, socketAddress, true, &err);
  if (err) { return err; }

  g_socket_listen(serverSocket, &err);
  if (err) { return err; }

  return NULL;
}

bool DebugCommunicator::sendMessage(gint32 id, const char* data, size_t size)
{
  if (fatalFail) {
    throw "fatalFail";
  }

  GError* err = internalSendMessage(id, data, size);
  if (err)
  {
    std::cerr << "[DebugServer:port " << port << "] " << "SocketException in sendMessage: " << err->message << std::endl;
    g_error_free(err);
    return false;
  }
  return true;
}//end sendMessage

GError* DebugCommunicator::internalSendMessage(gint32 id, const char* data, size_t size)
{
  GError* err = NULL;
  if (connection != NULL)
  {
    g_socket_set_blocking(connection, true);

    // send the message id
    g_socket_send(connection, (char*) &id, 4, NULL, &err);

    // send message size in 4 bytes
    guint32 sizeFixed = static_cast<unsigned int> (size);
    g_socket_send(connection, (char*) &sizeFixed, 4, NULL, &err);

    gsize pos = 0;
    while(err == NULL && connection != NULL && pos < size)
    {
      gsize length = size-pos;

      gssize sent = g_socket_send(connection, data+pos, length, NULL, &err);
      if(sent < 0) {
        break;
      }
      pos += static_cast<gsize>(sent);
    }
  }

  return err;
}//end sendMessage

GString* DebugCommunicator::readMessage(gint32& id)
{
  if (fatalFail) {
    throw "fatalFail";
  }

  GError* err = NULL;
  GString* result = NULL;
  id = internalReadMessage(&result, &err);

  if(err)
  {
    if(err->code != G_IO_ERROR_WOULD_BLOCK)
    {
      std::cerr << "[DebugServer:port " << port << "] " << "ERROR: (SocketException in readMessage) "
                << err->message << std::endl;
      g_error_free(err);

      if(result != NULL) {
        g_string_free(result,true);
      }

      throw "Socket Exception";
    }
    else
    {
      g_error_free(err);
    }
  }
  return result;
}//end readMessage

gint32 DebugCommunicator::internalReadMessage(GString** result, GError** err)
{
  *err = NULL;
  if (connection == NULL) {
    return -1;
  }

  // read the message id
  gint32 messageId = -1;
  gssize initialBytes = g_socket_receive_with_blocking(connection, (char*) &messageId, 4, false, NULL, err);

  // nothing to read
  if ( initialBytes == -1 ) {
    // no data was received for a while, time out
    unsigned long long current_time = naoth::NaoTime::getSystemTimeInMilliSeconds();
    if( time_of_last_message > 0 &&
        time_out_delta > 0 &&
        current_time > time_of_last_message + time_out_delta*1000 )
    {
      throw "connection timed out";
    }

    return -1;
  }

  if(initialBytes == 0) {
    throw "connection closed by peer";
  }

  if(initialBytes != 4) {
    throw "error reading message id";
  }

  // some data was received, so keep the connection alive
  time_of_last_message = naoth::NaoTime::getSystemTimeInMilliSeconds();

  // heartbeat message, ignore
  if(messageId == -1) {
    // std::cout << "heartbeat received" << std::endl;
    return -1;
  }

  gint32 sizeOfMessage = 0;
  if(g_socket_receive_with_blocking(connection, (char*) &sizeOfMessage, 4, true, NULL, err) != 4) {
    throw "error reading message size";
  }

  if(sizeOfMessage > 0)
  {
    GString* buffer = g_string_new("");
    g_string_set_size(buffer, static_cast<gsize>(sizeOfMessage));
    g_assert(buffer->len == (guint32)sizeOfMessage);

    // read message completly
    guint32 pos = 0;
    while(*err == NULL && pos < (guint32)sizeOfMessage)
    {
      gssize read_bytes =
          g_socket_receive_with_blocking(connection, buffer->str + pos, static_cast<gsize>(sizeOfMessage) - pos, true ,NULL, err);

      if(read_bytes == 0)
      {
        g_string_free(buffer, true);
        buffer = NULL;
        // we got 0 bytes, this indicates
        // an error in the connection (-1 is set if no data was available
        // in non-blocking mode)
        throw "Connection Error";
      }
      pos += static_cast<unsigned int> (read_bytes);
    }

    if(*err) {
      g_string_free(buffer, true);
      return -1;
    }

    *result = buffer;
    return messageId;
  }

  return -1;
}

bool DebugCommunicator::connect(int timeout)
{
  GError* err = NULL;

  // TODO: use isConnected()?
  // already connected
  if (connection != NULL) {
    return true;
  }

  // TODO: throw?
  // not initialized
  if (serverSocket == NULL) {
    // re-establish server socket to accept new connections
    init(port);
    if (serverSocket == NULL) {
      return false;
    }
  }

  // prepare the server socket
  if(timeout < 0) {
    g_socket_set_blocking(serverSocket, false);
  } else {
    g_socket_set_blocking(serverSocket, true);
    g_socket_set_timeout(serverSocket, static_cast<gsize>(timeout));
  }

  // try to accept an eventually pending connection request
  connection = g_socket_accept(serverSocket, NULL, &err);

  // TODO: throw?
  if (err) {
    return false;
  }

  if (connection != NULL)
  {
    GInetSocketAddress* remoteSocketAddr =
      G_INET_SOCKET_ADDRESS(g_socket_get_remote_address(connection, &err));

    if (err)
    {
      // cleanup
      g_socket_close(connection, NULL);
      g_object_unref(connection);
      connection = NULL;
      return false;
    }

    // print the address
    GInetAddress* addr = g_inet_socket_address_get_address(remoteSocketAddr);
    char* addrString = g_inet_address_to_string(addr);
    std::cout << "[DebugServer:port " << port << "] " << "connected to " << addrString << std::endl;
    g_free(addrString);
    g_object_unref(remoteSocketAddr);

    // init the time stamp for the time out
    time_of_last_message = naoth::NaoTime::getSystemTimeInMilliSeconds();
    g_socket_set_timeout(connection, time_out_delta);

    // close the server socket and reject further connections
    closeServerSocket();

    return true;
  }

  return false;
}

void DebugCommunicator::closeServerSocket()
{
  if (serverSocket != NULL)
  {
    g_socket_close(serverSocket, NULL);
    g_object_unref(serverSocket);
    serverSocket = NULL;
  }
}

void DebugCommunicator::disconnect()
{
  if (connection != NULL)
  {
    g_socket_close(connection, NULL);
    g_object_unref(connection);
    connection = NULL;
    std::cerr << "[DebugServer:port " << port << "] " << "disconnected" << std::endl;
  }
}

bool DebugCommunicator::isConnected()
{
  return connection != NULL && g_socket_is_connected(connection);
}

<|endoftext|>


<commit_before>//===- DeadStoreElimination.cpp - Dead Store Elimination ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Owen Anderson and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a trivial dead store elimination that only considers
// basic-block local redundant stores.
//
// FIXME: This should eventually be extended to be a post-dominator tree
// traversal.  Doing so would be pretty trivial.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "fdse"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Constants.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/Pass.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/MemoryDependenceAnalysis.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Support/Compiler.h"
using namespace llvm;

STATISTIC(NumFastStores, "Number of stores deleted");
STATISTIC(NumFastOther , "Number of other instrs removed");

namespace {
  struct VISIBILITY_HIDDEN FDSE : public FunctionPass {
    static char ID; // Pass identification, replacement for typeid
    FDSE() : FunctionPass((intptr_t)&ID) {}

    virtual bool runOnFunction(Function &F) {
      bool Changed = false;
      for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I)
        Changed |= runOnBasicBlock(*I);
      return Changed;
    }

    bool runOnBasicBlock(BasicBlock &BB);
    bool handleFreeWithNonTrivialDependency(FreeInst* F, Instruction* dependency,
                                            SetVector<Instruction*>& possiblyDead);
    bool handleEndBlock(BasicBlock& BB, SetVector<Instruction*>& possiblyDead);
    bool RemoveUndeadPointers(Value* pointer, unsigned pointerSize,
                              BasicBlock::iterator& BBI,
                              SmallPtrSet<AllocaInst*, 4>& deadPointers,
                              SetVector<Instruction*>& possiblyDead);
    void DeleteDeadInstructionChains(Instruction *I,
                                     SetVector<Instruction*> &DeadInsts);
    void TranslatePointerBitCasts(Value*& v) {
      assert(isa<PointerType>(v->getType()) && "Translating a non-pointer type?");

      // See through pointer-to-pointer bitcasts
      while (isa<BitCastInst>(v) || isa<GetElementPtrInst>(v))
        if (BitCastInst* C = dyn_cast<BitCastInst>(v)) {
          if (isa<PointerType>(C->getSrcTy()))
            v = C->getOperand(0);
          else
            break;
        } else if (GetElementPtrInst* G = dyn_cast<GetElementPtrInst>(v)) {
          if (G->hasAllZeroIndices())
            v = G->getOperand(0);
          else
            break;
        }
    }

    // getAnalysisUsage - We require post dominance frontiers (aka Control
    // Dependence Graph)
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesCFG();
      AU.addRequired<TargetData>();
      AU.addRequired<AliasAnalysis>();
      AU.addRequired<MemoryDependenceAnalysis>();
      AU.addPreserved<AliasAnalysis>();
      AU.addPreserved<MemoryDependenceAnalysis>();
    }
  };
  char FDSE::ID = 0;
  RegisterPass<FDSE> X("fdse", "Fast Dead Store Elimination");
}

FunctionPass *llvm::createFastDeadStoreEliminationPass() { return new FDSE(); }

bool FDSE::runOnBasicBlock(BasicBlock &BB) {
  MemoryDependenceAnalysis& MD = getAnalysis<MemoryDependenceAnalysis>();

  // Record the last-seen store to this pointer
  DenseMap<Value*, StoreInst*> lastStore;
  // Record instructions possibly made dead by deleting a store
  SetVector<Instruction*> possiblyDead;

  bool MadeChange = false;

  // Do a top-down walk on the BB
  for (BasicBlock::iterator BBI = BB.begin(), BBE = BB.end(); BBI != BBE; ++BBI) {
    // If we find a store or a free...
    if (isa<StoreInst>(BBI) || isa<FreeInst>(BBI)) {
      Value* pointer = 0;
      if (StoreInst* S = dyn_cast<StoreInst>(BBI))
        pointer = S->getPointerOperand();
      else if (FreeInst* F = dyn_cast<FreeInst>(BBI))
        pointer = F->getPointerOperand();

      assert(pointer && "Not a free or a store?");

      StoreInst*& last = lastStore[pointer];
      bool deletedStore = false;

      // ... to a pointer that has been stored to before...
      if (last) {

        Instruction* dep = MD.getDependency(BBI);

        // ... and no other memory dependencies are between them....
        while (dep != MemoryDependenceAnalysis::None &&
               dep != MemoryDependenceAnalysis::NonLocal &&
               isa<StoreInst>(dep)) {
          if (dep == last) {

            // Remove it!
            MD.removeInstruction(last);

            // DCE instructions only used to calculate that store
            if (Instruction* D = dyn_cast<Instruction>(last->getOperand(0)))
              possiblyDead.insert(D);
            if (Instruction* D = dyn_cast<Instruction>(last->getOperand(1)))
              possiblyDead.insert(D);

            last->eraseFromParent();
            NumFastStores++;
            deletedStore = true;
            MadeChange = true;

            break;
          } else {
            dep = MD.getDependency(BBI, dep);
          }
        }
      }

      // Handle frees whose dependencies are non-trivial
      if (FreeInst* F = dyn_cast<FreeInst>(BBI))
        if (!deletedStore)
          MadeChange |= handleFreeWithNonTrivialDependency(F, MD.getDependency(F),
                                                           possiblyDead);

      // Update our most-recent-store map
      if (StoreInst* S = dyn_cast<StoreInst>(BBI))
        last = S;
      else
        last = 0;
    }
  }

  // If this block ends in a return, unwind, unreachable, and eventually
  // tailcall, then all allocas are dead at its end.
  if (BB.getTerminator()->getNumSuccessors() == 0)
    MadeChange |= handleEndBlock(BB, possiblyDead);

  // Do a trivial DCE
  while (!possiblyDead.empty()) {
    Instruction *I = possiblyDead.back();
    possiblyDead.pop_back();
    DeleteDeadInstructionChains(I, possiblyDead);
  }

  return MadeChange;
}

/// handleFreeWithNonTrivialDependency - Handle frees of entire structures whose
/// dependency is a store to a field of that structure
bool FDSE::handleFreeWithNonTrivialDependency(FreeInst* F, Instruction* dep,
                                              SetVector<Instruction*>& possiblyDead) {
  TargetData &TD = getAnalysis<TargetData>();
  AliasAnalysis &AA = getAnalysis<AliasAnalysis>();
  MemoryDependenceAnalysis& MD = getAnalysis<MemoryDependenceAnalysis>();

  if (dep == MemoryDependenceAnalysis::None ||
      dep == MemoryDependenceAnalysis::NonLocal)
    return false;

  StoreInst* dependency = dyn_cast<StoreInst>(dep);
  if (!dependency)
    return false;

  Value* depPointer = dependency->getPointerOperand();
  unsigned depPointerSize = TD.getTypeSize(dependency->getOperand(0)->getType());

  // Check for aliasing
  AliasAnalysis::AliasResult A = AA.alias(F->getPointerOperand(), ~0UL,
                                          depPointer, depPointerSize);

  if (A == AliasAnalysis::MustAlias) {
    // Remove it!
    MD.removeInstruction(dependency);

    // DCE instructions only used to calculate that store
    if (Instruction* D = dyn_cast<Instruction>(dependency->getOperand(0)))
      possiblyDead.insert(D);
    if (Instruction* D = dyn_cast<Instruction>(dependency->getOperand(1)))
      possiblyDead.insert(D);

    dependency->eraseFromParent();
    NumFastStores++;
    return true;
  }

  return false;
}

/// handleEndBlock - Remove dead stores to stack-allocated locations in the function
/// end block
bool FDSE::handleEndBlock(BasicBlock& BB, SetVector<Instruction*>& possiblyDead) {
  TargetData &TD = getAnalysis<TargetData>();
  AliasAnalysis &AA = getAnalysis<AliasAnalysis>();
  MemoryDependenceAnalysis& MD = getAnalysis<MemoryDependenceAnalysis>();

  bool MadeChange = false;

  // Pointers alloca'd in this function are dead in the end block
  SmallPtrSet<AllocaInst*, 4> deadPointers;

  // Find all of the alloca'd pointers in the entry block
  BasicBlock *Entry = BB.getParent()->begin();
  for (BasicBlock::iterator I = Entry->begin(), E = Entry->end(); I != E; ++I)
    if (AllocaInst *AI = dyn_cast<AllocaInst>(I))
      deadPointers.insert(AI);

  // Scan the basic block backwards
  for (BasicBlock::iterator BBI = BB.end(); BBI != BB.begin(); ){
    --BBI;

    if (deadPointers.empty())
      break;

    Value* killPointer = 0;
    unsigned killPointerSize = 0;

    // If we find a store whose pointer is dead...
    if (StoreInst* S = dyn_cast<StoreInst>(BBI)) {
      Value* pointerOperand = S->getPointerOperand();
      // See through pointer-to-pointer bitcasts
      TranslatePointerBitCasts(pointerOperand);

      if (deadPointers.count(pointerOperand)){
        // Remove it!
        MD.removeInstruction(S);

        // DCE instructions only used to calculate that store
        if (Instruction* D = dyn_cast<Instruction>(S->getOperand(0)))
          possiblyDead.insert(D);
        if (Instruction* D = dyn_cast<Instruction>(S->getOperand(1)))
          possiblyDead.insert(D);

        BBI++;
        S->eraseFromParent();
        NumFastStores++;
        MadeChange = true;
      }

    // If we encounter a use of the pointer, it is no longer considered dead
    } else if (LoadInst* L = dyn_cast<LoadInst>(BBI)) {
      killPointer = L->getPointerOperand();
      killPointerSize = TD.getTypeSize(L->getType());
    } else if (VAArgInst* V = dyn_cast<VAArgInst>(BBI)) {
      killPointer = V->getOperand(0);
      killPointerSize = TD.getTypeSize(V->getType());
    } else if (FreeInst* F = dyn_cast<FreeInst>(BBI)) {
      killPointer = F->getPointerOperand();
      killPointerSize = ~0UL;
    } else if (AllocaInst* A = dyn_cast<AllocaInst>(BBI)) {
      deadPointers.erase(A);
      continue;
    } else if (CallSite::get(BBI).getInstruction() != 0) {
      // Remove any pointers made undead by the call from the dead set
      std::vector<Instruction*> dead;
      for (SmallPtrSet<AllocaInst*, 4>::iterator I = deadPointers.begin(),
           E = deadPointers.end(); I != E; ++I) {
        // Get size information for the alloca
        unsigned pointerSize = ~0UL;
        if (ConstantInt* C = dyn_cast<ConstantInt>((*I)->getArraySize()))
          pointerSize = C->getZExtValue() * TD.getTypeSize((*I)->getAllocatedType());

        // See if the call site touches it
        AliasAnalysis::ModRefResult A = AA.getModRefInfo(CallSite::get(BBI),
                                                         *I, pointerSize);
        if (A == AliasAnalysis::ModRef || A == AliasAnalysis::Ref)
          dead.push_back(*I);
      }

      for (std::vector<Instruction*>::iterator I = dead.begin(), E = dead.end();
           I != E; ++I)
        deadPointers.erase(*I);

      continue;
    }

    if (!killPointer)
      continue;

    // Deal with undead pointers
    MadeChange |= RemoveUndeadPointers(killPointer, killPointerSize, BBI,
                                       deadPointers, possiblyDead);
  }

  return MadeChange;
}

bool FDSE::RemoveUndeadPointers(Value* killPointer, unsigned killPointerSize,
                                BasicBlock::iterator& BBI,
                                SmallPtrSet<AllocaInst*, 4>& deadPointers,
                                SetVector<Instruction*>& possiblyDead) {
  TargetData &TD = getAnalysis<TargetData>();
  AliasAnalysis &AA = getAnalysis<AliasAnalysis>();
  MemoryDependenceAnalysis& MD = getAnalysis<MemoryDependenceAnalysis>();

  bool MadeChange = false;

  std::vector<Instruction*> undead;

  for (SmallPtrSet<AllocaInst*, 4>::iterator I = deadPointers.begin(),
      E = deadPointers.end(); I != E; ++I) {
    // Get size information for the alloca
    unsigned pointerSize = ~0UL;
    if (ConstantInt* C = dyn_cast<ConstantInt>((*I)->getArraySize()))
      pointerSize = C->getZExtValue() * TD.getTypeSize((*I)->getAllocatedType());

    // See if this pointer could alias it
    AliasAnalysis::AliasResult A = AA.alias(*I, pointerSize, killPointer, killPointerSize);

    // If it must-alias and a store, we can delete it
    if (isa<StoreInst>(BBI) && A == AliasAnalysis::MustAlias) {
      StoreInst* S = cast<StoreInst>(BBI);

      // Remove it!
      MD.removeInstruction(S);

      // DCE instructions only used to calculate that store
      if (Instruction* D = dyn_cast<Instruction>(S->getOperand(0)))
        possiblyDead.insert(D);
      if (Instruction* D = dyn_cast<Instruction>(S->getOperand(1)))
        possiblyDead.insert(D);

      BBI++;
      S->eraseFromParent();
      NumFastStores++;
      MadeChange = true;

      continue;

      // Otherwise, it is undead
      } else if (A != AliasAnalysis::NoAlias)
        undead.push_back(*I);
  }

  for (std::vector<Instruction*>::iterator I = undead.begin(), E = undead.end();
       I != E; ++I)
    deadPointers.erase(*I);

  return MadeChange;
}

void FDSE::DeleteDeadInstructionChains(Instruction *I,
                                      SetVector<Instruction*> &DeadInsts) {
  // Instruction must be dead.
  if (!I->use_empty() || !isInstructionTriviallyDead(I)) return;

  // Let the memory dependence know
  getAnalysis<MemoryDependenceAnalysis>().removeInstruction(I);

  // See if this made any operands dead.  We do it this way in case the
  // instruction uses the same operand twice.  We don't want to delete a
  // value then reference it.
  for (unsigned i = 0, e = I->getNumOperands(); i != e; ++i) {
    if (I->getOperand(i)->hasOneUse())
      if (Instruction* Op = dyn_cast<Instruction>(I->getOperand(i)))
        DeadInsts.insert(Op);      // Attempt to nuke it later.

    I->setOperand(i, 0);         // Drop from the operand list.
  }

  I->eraseFromParent();
  ++NumFastOther;
}
<commit_msg>Take advantage of undefined behavior if the source program tries to GEP beyond the end of an alloca to make FastDSE faster and more aggressive.<commit_after>//===- DeadStoreElimination.cpp - Dead Store Elimination ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Owen Anderson and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements a trivial dead store elimination that only considers
// basic-block local redundant stores.
//
// FIXME: This should eventually be extended to be a post-dominator tree
// traversal.  Doing so would be pretty trivial.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "fdse"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Constants.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/Pass.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/MemoryDependenceAnalysis.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Support/Compiler.h"
using namespace llvm;

STATISTIC(NumFastStores, "Number of stores deleted");
STATISTIC(NumFastOther , "Number of other instrs removed");

namespace {
  struct VISIBILITY_HIDDEN FDSE : public FunctionPass {
    static char ID; // Pass identification, replacement for typeid
    FDSE() : FunctionPass((intptr_t)&ID) {}

    virtual bool runOnFunction(Function &F) {
      bool Changed = false;
      for (Function::iterator I = F.begin(), E = F.end(); I != E; ++I)
        Changed |= runOnBasicBlock(*I);
      return Changed;
    }

    bool runOnBasicBlock(BasicBlock &BB);
    bool handleFreeWithNonTrivialDependency(FreeInst* F, Instruction* dependency,
                                            SetVector<Instruction*>& possiblyDead);
    bool handleEndBlock(BasicBlock& BB, SetVector<Instruction*>& possiblyDead);
    bool RemoveUndeadPointers(Value* pointer, unsigned pointerSize,
                              BasicBlock::iterator& BBI,
                              SmallPtrSet<AllocaInst*, 4>& deadPointers,
                              SetVector<Instruction*>& possiblyDead);
    void DeleteDeadInstructionChains(Instruction *I,
                                     SetVector<Instruction*> &DeadInsts);
    void TranslatePointerBitCasts(Value*& v) {
      assert(isa<PointerType>(v->getType()) && "Translating a non-pointer type?");

      // See through pointer-to-pointer bitcasts
      while (isa<BitCastInst>(v) || isa<GetElementPtrInst>(v))
        if (BitCastInst* C = dyn_cast<BitCastInst>(v))
          v = C->getOperand(0);
        else if (GetElementPtrInst* G = dyn_cast<GetElementPtrInst>(v))
          v = G->getOperand(0);
    }

    // getAnalysisUsage - We require post dominance frontiers (aka Control
    // Dependence Graph)
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesCFG();
      AU.addRequired<TargetData>();
      AU.addRequired<AliasAnalysis>();
      AU.addRequired<MemoryDependenceAnalysis>();
      AU.addPreserved<AliasAnalysis>();
      AU.addPreserved<MemoryDependenceAnalysis>();
    }
  };
  char FDSE::ID = 0;
  RegisterPass<FDSE> X("fdse", "Fast Dead Store Elimination");
}

FunctionPass *llvm::createFastDeadStoreEliminationPass() { return new FDSE(); }

bool FDSE::runOnBasicBlock(BasicBlock &BB) {
  MemoryDependenceAnalysis& MD = getAnalysis<MemoryDependenceAnalysis>();

  // Record the last-seen store to this pointer
  DenseMap<Value*, StoreInst*> lastStore;
  // Record instructions possibly made dead by deleting a store
  SetVector<Instruction*> possiblyDead;

  bool MadeChange = false;

  // Do a top-down walk on the BB
  for (BasicBlock::iterator BBI = BB.begin(), BBE = BB.end(); BBI != BBE; ++BBI) {
    // If we find a store or a free...
    if (isa<StoreInst>(BBI) || isa<FreeInst>(BBI)) {
      Value* pointer = 0;
      if (StoreInst* S = dyn_cast<StoreInst>(BBI))
        pointer = S->getPointerOperand();
      else if (FreeInst* F = dyn_cast<FreeInst>(BBI))
        pointer = F->getPointerOperand();

      assert(pointer && "Not a free or a store?");

      StoreInst*& last = lastStore[pointer];
      bool deletedStore = false;

      // ... to a pointer that has been stored to before...
      if (last) {

        Instruction* dep = MD.getDependency(BBI);

        // ... and no other memory dependencies are between them....
        while (dep != MemoryDependenceAnalysis::None &&
               dep != MemoryDependenceAnalysis::NonLocal &&
               isa<StoreInst>(dep)) {
          if (dep == last) {

            // Remove it!
            MD.removeInstruction(last);

            // DCE instructions only used to calculate that store
            if (Instruction* D = dyn_cast<Instruction>(last->getOperand(0)))
              possiblyDead.insert(D);
            if (Instruction* D = dyn_cast<Instruction>(last->getOperand(1)))
              possiblyDead.insert(D);

            last->eraseFromParent();
            NumFastStores++;
            deletedStore = true;
            MadeChange = true;

            break;
          } else {
            dep = MD.getDependency(BBI, dep);
          }
        }
      }

      // Handle frees whose dependencies are non-trivial
      if (FreeInst* F = dyn_cast<FreeInst>(BBI))
        if (!deletedStore)
          MadeChange |= handleFreeWithNonTrivialDependency(F, MD.getDependency(F),
                                                           possiblyDead);

      // Update our most-recent-store map
      if (StoreInst* S = dyn_cast<StoreInst>(BBI))
        last = S;
      else
        last = 0;
    }
  }

  // If this block ends in a return, unwind, unreachable, and eventually
  // tailcall, then all allocas are dead at its end.
  if (BB.getTerminator()->getNumSuccessors() == 0)
    MadeChange |= handleEndBlock(BB, possiblyDead);

  // Do a trivial DCE
  while (!possiblyDead.empty()) {
    Instruction *I = possiblyDead.back();
    possiblyDead.pop_back();
    DeleteDeadInstructionChains(I, possiblyDead);
  }

  return MadeChange;
}

/// handleFreeWithNonTrivialDependency - Handle frees of entire structures whose
/// dependency is a store to a field of that structure
bool FDSE::handleFreeWithNonTrivialDependency(FreeInst* F, Instruction* dep,
                                              SetVector<Instruction*>& possiblyDead) {
  TargetData &TD = getAnalysis<TargetData>();
  AliasAnalysis &AA = getAnalysis<AliasAnalysis>();
  MemoryDependenceAnalysis& MD = getAnalysis<MemoryDependenceAnalysis>();

  if (dep == MemoryDependenceAnalysis::None ||
      dep == MemoryDependenceAnalysis::NonLocal)
    return false;

  StoreInst* dependency = dyn_cast<StoreInst>(dep);
  if (!dependency)
    return false;

  Value* depPointer = dependency->getPointerOperand();
  unsigned depPointerSize = TD.getTypeSize(dependency->getOperand(0)->getType());

  // Check for aliasing
  AliasAnalysis::AliasResult A = AA.alias(F->getPointerOperand(), ~0UL,
                                          depPointer, depPointerSize);

  if (A == AliasAnalysis::MustAlias) {
    // Remove it!
    MD.removeInstruction(dependency);

    // DCE instructions only used to calculate that store
    if (Instruction* D = dyn_cast<Instruction>(dependency->getOperand(0)))
      possiblyDead.insert(D);
    if (Instruction* D = dyn_cast<Instruction>(dependency->getOperand(1)))
      possiblyDead.insert(D);

    dependency->eraseFromParent();
    NumFastStores++;
    return true;
  }

  return false;
}

/// handleEndBlock - Remove dead stores to stack-allocated locations in the function
/// end block
bool FDSE::handleEndBlock(BasicBlock& BB, SetVector<Instruction*>& possiblyDead) {
  TargetData &TD = getAnalysis<TargetData>();
  AliasAnalysis &AA = getAnalysis<AliasAnalysis>();
  MemoryDependenceAnalysis& MD = getAnalysis<MemoryDependenceAnalysis>();

  bool MadeChange = false;

  // Pointers alloca'd in this function are dead in the end block
  SmallPtrSet<AllocaInst*, 4> deadPointers;

  // Find all of the alloca'd pointers in the entry block
  BasicBlock *Entry = BB.getParent()->begin();
  for (BasicBlock::iterator I = Entry->begin(), E = Entry->end(); I != E; ++I)
    if (AllocaInst *AI = dyn_cast<AllocaInst>(I))
      deadPointers.insert(AI);

  // Scan the basic block backwards
  for (BasicBlock::iterator BBI = BB.end(); BBI != BB.begin(); ){
    --BBI;

    if (deadPointers.empty())
      break;

    Value* killPointer = 0;
    unsigned killPointerSize = 0;

    // If we find a store whose pointer is dead...
    if (StoreInst* S = dyn_cast<StoreInst>(BBI)) {
      Value* pointerOperand = S->getPointerOperand();
      // See through pointer-to-pointer bitcasts
      TranslatePointerBitCasts(pointerOperand);

      if (deadPointers.count(pointerOperand)){
        // Remove it!
        MD.removeInstruction(S);

        // DCE instructions only used to calculate that store
        if (Instruction* D = dyn_cast<Instruction>(S->getOperand(0)))
          possiblyDead.insert(D);
        if (Instruction* D = dyn_cast<Instruction>(S->getOperand(1)))
          possiblyDead.insert(D);

        BBI++;
        S->eraseFromParent();
        NumFastStores++;
        MadeChange = true;
      }

    // If we encounter a use of the pointer, it is no longer considered dead
    } else if (LoadInst* L = dyn_cast<LoadInst>(BBI)) {
      killPointer = L->getPointerOperand();
      killPointerSize = TD.getTypeSize(L->getType());
    } else if (VAArgInst* V = dyn_cast<VAArgInst>(BBI)) {
      killPointer = V->getOperand(0);
      killPointerSize = TD.getTypeSize(V->getType());
    } else if (FreeInst* F = dyn_cast<FreeInst>(BBI)) {
      killPointer = F->getPointerOperand();
      killPointerSize = ~0UL;
    } else if (AllocaInst* A = dyn_cast<AllocaInst>(BBI)) {
      deadPointers.erase(A);
      continue;
    } else if (CallSite::get(BBI).getInstruction() != 0) {
      // Remove any pointers made undead by the call from the dead set
      std::vector<Instruction*> dead;
      for (SmallPtrSet<AllocaInst*, 4>::iterator I = deadPointers.begin(),
           E = deadPointers.end(); I != E; ++I) {
        // Get size information for the alloca
        unsigned pointerSize = ~0UL;
        if (ConstantInt* C = dyn_cast<ConstantInt>((*I)->getArraySize()))
          pointerSize = C->getZExtValue() * TD.getTypeSize((*I)->getAllocatedType());

        // See if the call site touches it
        AliasAnalysis::ModRefResult A = AA.getModRefInfo(CallSite::get(BBI),
                                                         *I, pointerSize);
        if (A == AliasAnalysis::ModRef || A == AliasAnalysis::Ref)
          dead.push_back(*I);
      }

      for (std::vector<Instruction*>::iterator I = dead.begin(), E = dead.end();
           I != E; ++I)
        deadPointers.erase(*I);

      continue;
    }

    if (!killPointer)
      continue;

    // Deal with undead pointers
    MadeChange |= RemoveUndeadPointers(killPointer, killPointerSize, BBI,
                                       deadPointers, possiblyDead);
  }

  return MadeChange;
}

bool FDSE::RemoveUndeadPointers(Value* killPointer, unsigned killPointerSize,
                                BasicBlock::iterator& BBI,
                                SmallPtrSet<AllocaInst*, 4>& deadPointers,
                                SetVector<Instruction*>& possiblyDead) {
  TargetData &TD = getAnalysis<TargetData>();
  AliasAnalysis &AA = getAnalysis<AliasAnalysis>();
  MemoryDependenceAnalysis& MD = getAnalysis<MemoryDependenceAnalysis>();

  bool MadeChange = false;

  std::vector<Instruction*> undead;

  for (SmallPtrSet<AllocaInst*, 4>::iterator I = deadPointers.begin(),
      E = deadPointers.end(); I != E; ++I) {
    // Get size information for the alloca
    unsigned pointerSize = ~0UL;
    if (ConstantInt* C = dyn_cast<ConstantInt>((*I)->getArraySize()))
      pointerSize = C->getZExtValue() * TD.getTypeSize((*I)->getAllocatedType());

    // See if this pointer could alias it
    AliasAnalysis::AliasResult A = AA.alias(*I, pointerSize, killPointer, killPointerSize);

    // If it must-alias and a store, we can delete it
    if (isa<StoreInst>(BBI) && A == AliasAnalysis::MustAlias) {
      StoreInst* S = cast<StoreInst>(BBI);

      // Remove it!
      MD.removeInstruction(S);

      // DCE instructions only used to calculate that store
      if (Instruction* D = dyn_cast<Instruction>(S->getOperand(0)))
        possiblyDead.insert(D);
      if (Instruction* D = dyn_cast<Instruction>(S->getOperand(1)))
        possiblyDead.insert(D);

      BBI++;
      S->eraseFromParent();
      NumFastStores++;
      MadeChange = true;

      continue;

      // Otherwise, it is undead
      } else if (A != AliasAnalysis::NoAlias)
        undead.push_back(*I);
  }

  for (std::vector<Instruction*>::iterator I = undead.begin(), E = undead.end();
       I != E; ++I)
    deadPointers.erase(*I);

  return MadeChange;
}

void FDSE::DeleteDeadInstructionChains(Instruction *I,
                                      SetVector<Instruction*> &DeadInsts) {
  // Instruction must be dead.
  if (!I->use_empty() || !isInstructionTriviallyDead(I)) return;

  // Let the memory dependence know
  getAnalysis<MemoryDependenceAnalysis>().removeInstruction(I);

  // See if this made any operands dead.  We do it this way in case the
  // instruction uses the same operand twice.  We don't want to delete a
  // value then reference it.
  for (unsigned i = 0, e = I->getNumOperands(); i != e; ++i) {
    if (I->getOperand(i)->hasOneUse())
      if (Instruction* Op = dyn_cast<Instruction>(I->getOperand(i)))
        DeadInsts.insert(Op);      // Attempt to nuke it later.

    I->setOperand(i, 0);         // Drop from the operand list.
  }

  I->eraseFromParent();
  ++NumFastOther;
}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Lukasz Janyst <ljanyst@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/Utils/SourceNormalization.h"

#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"

#include <utility>

using namespace clang;

namespace {
///\brief A Lexer that exposes preprocessor directives.
class MinimalPPLexer: public Lexer {
public:
  ///\brief Construct a Lexer from LangOpts and source.
  MinimalPPLexer(const LangOptions &LangOpts, llvm::StringRef source):
    Lexer(SourceLocation(), LangOpts,
          source.begin(), source.begin(), source.end()) {}

  bool inPPDirective() const { return ParsingPreprocessorDirective; }

  ///\brief Lex, forwarding to Lexer::LexFromRawLexer, and keeping track of
  /// preprocessor directives to provide a tok::eod corresponding to a
  /// tok::hash.
  bool Lex(Token& Tok) {
    bool ret = LexFromRawLexer(Tok);
    if (inPPDirective()) {
      // Saw a PP directive; probe for eod to end PP parsing mode.
      if (Tok.is(tok::eod))
        ParsingPreprocessorDirective = false;
    } else {
      if (Tok.is(tok::hash)) {
        // Found a PP directive, request tok::eod to be generated.
        ParsingPreprocessorDirective = true;
      }
    }
    return ret;
  }

  ///\brief Advance to token with given token kind.
  ///
  /// \param Tok - Token to advance.
  /// \param kind - Token kind where to stop lexing.
  /// \return - Result of most recent call to Lex().
  bool AdvanceTo(Token& Tok, tok::TokenKind kind) {
    while (!Lex(Tok)) {
      if (Tok.is(kind))
        return false;
    }
    return true;
  }
};

size_t getFileOffset(const Token& Tok) {
  return Tok.getLocation().getRawEncoding();
}

}

size_t
cling::utils::isUnnamedMacro(llvm::StringRef source,
                             clang::LangOptions& LangOpts) {
  // Find the first token that is not a non-cpp directive nor a comment.
  // If that token is a '{' we have an unnamed macro.

  MinimalPPLexer Lex(LangOpts, source);
  Token Tok;
  while (!Lex.Lex(Tok)) {
    if (Lex.inPPDirective())
      continue; // Skip PP directives.

    const tok::TokenKind kind = Tok.getKind();
    if (kind == tok::comment) continue; // ignore comments
    if (kind == tok::l_brace)
      return getFileOffset(Tok);

    return std::string::npos;
  }

  // Empty file?

  return std::string::npos;
}

size_t cling::utils::getWrapPoint(std::string& source,
                                  const clang::LangOptions& LangOpts) {
  // TODO: For future reference.
  // Parser* P = const_cast<clang::Parser*>(m_IncrParser->getParser());
  // Parser::TentativeParsingAction TA(P);
  // TPResult result = P->isCXXDeclarationSpecifier();
  // TA.Revert();
  // return result == TPResult::True();

  MinimalPPLexer Lex(LangOpts, source);
  Token Tok;

  size_t wrapPoint;

  while (!Lex.Lex(Tok)) {
    if (Lex.inPPDirective()) {
      wrapPoint = getFileOffset(Tok);
      continue; // Skip PP directives; they just move the wrap point.
    }

    const tok::TokenKind kind = Tok.getKind();

    if (kind == tok::raw_identifier && !Tok.needsCleaning()) {
      StringRef keyword(Tok.getRawIdentifier());
      if (keyword.equals("using")) {
        // FIXME: Using definitions and declarations should be decl extracted.
        // Until we have that, don't wrap them if they are the only input.
        if (Lex.AdvanceTo(Tok, tok::semi)) {
          // EOF while looking for semi. Don't wrap.
          return std::string::npos;
        }
        // There is "more" - let's assume this input consists of a using
        // declaration or definition plus some code that should be wrapped.
        return getFileOffset(Tok);
      }
      if (keyword.equals("extern"))
        return std::string::npos;
      if (keyword.equals("namespace"))
        return std::string::npos;
      if (keyword.equals("template"))
        return std::string::npos;
      // Else there is something else here that needs to be wrapped.
      return wrapPoint;
    }
  }

  // We have only had PP directives; no need to wrap.
  return std::string::npos;
}
<commit_msg>Handle last token, too.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Lukasz Janyst <ljanyst@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "cling/Utils/SourceNormalization.h"

#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"

#include <utility>

using namespace clang;

namespace {
///\brief A Lexer that exposes preprocessor directives.
class MinimalPPLexer: public Lexer {
public:
  ///\brief Construct a Lexer from LangOpts and source.
  MinimalPPLexer(const LangOptions &LangOpts, llvm::StringRef source):
    Lexer(SourceLocation(), LangOpts,
          source.begin(), source.begin(), source.end()) {}

  bool inPPDirective() const { return ParsingPreprocessorDirective; }

  ///\brief Lex, forwarding to Lexer::LexFromRawLexer, and keeping track of
  /// preprocessor directives to provide a tok::eod corresponding to a
  /// tok::hash.
  bool Lex(Token& Tok) {
    bool ret = LexFromRawLexer(Tok);
    if (inPPDirective()) {
      // Saw a PP directive; probe for eod to end PP parsing mode.
      if (Tok.is(tok::eod))
        ParsingPreprocessorDirective = false;
    } else {
      if (Tok.is(tok::hash)) {
        // Found a PP directive, request tok::eod to be generated.
        ParsingPreprocessorDirective = true;
      }
    }
    return ret;
  }

  ///\brief Advance to token with given token kind.
  ///
  /// \param Tok - Token to advance.
  /// \param kind - Token kind where to stop lexing.
  /// \return - Result of most recent call to Lex().
  bool AdvanceTo(Token& Tok, tok::TokenKind kind) {
    while (!Lex(Tok)) {
      if (Tok.is(kind))
        return false;
    }
    return true;
  }
};

size_t getFileOffset(const Token& Tok) {
  return Tok.getLocation().getRawEncoding();
}

}

size_t
cling::utils::isUnnamedMacro(llvm::StringRef source,
                             clang::LangOptions& LangOpts) {
  // Find the first token that is not a non-cpp directive nor a comment.
  // If that token is a '{' we have an unnamed macro.

  MinimalPPLexer Lex(LangOpts, source);
  Token Tok;
  while (true) {
    bool atEOF = Lex.Lex(Tok);
    const tok::TokenKind kind = Tok.getKind();

    if (kind == tok::l_brace)
      return getFileOffset(Tok);

    if (atEOF)
      return std::string::npos;

    if (Lex.inPPDirective())
      continue; // Skip PP directives.

    if (kind == tok::comment)
      continue; // ignore comments

    return std::string::npos;
  }

  // Empty file?

  return std::string::npos;
}

size_t cling::utils::getWrapPoint(std::string& source,
                                  const clang::LangOptions& LangOpts) {
  // TODO: For future reference.
  // Parser* P = const_cast<clang::Parser*>(m_IncrParser->getParser());
  // Parser::TentativeParsingAction TA(P);
  // TPResult result = P->isCXXDeclarationSpecifier();
  // TA.Revert();
  // return result == TPResult::True();

  MinimalPPLexer Lex(LangOpts, source);
  Token Tok;

  size_t wrapPoint;

  while (true) {
    bool atEOF = Lex.Lex(Tok);
    if (Lex.inPPDirective()) {
      wrapPoint = getFileOffset(Tok);
      continue; // Skip PP directives; they just move the wrap point.
    }

    const tok::TokenKind kind = Tok.getKind();

    if (kind == tok::raw_identifier && !Tok.needsCleaning()) {
      StringRef keyword(Tok.getRawIdentifier());
      if (keyword.equals("using")) {
        // FIXME: Using definitions and declarations should be decl extracted.
        // Until we have that, don't wrap them if they are the only input.
        if (Lex.AdvanceTo(Tok, tok::semi)) {
          // EOF while looking for semi. Don't wrap.
          return std::string::npos;
        }
        // There is "more" - let's assume this input consists of a using
        // declaration or definition plus some code that should be wrapped.
        return getFileOffset(Tok);
      }
      if (keyword.equals("extern"))
        return std::string::npos;
      if (keyword.equals("namespace"))
        return std::string::npos;
      if (keyword.equals("template"))
        return std::string::npos;
      // Else there is something else here that needs to be wrapped.
      return wrapPoint;
    }
    if (atEOF)
      break;
  }

  // We have only had PP directives; no need to wrap.
  return std::string::npos;
}
<|endoftext|>