<commit_before>#include "mem/log/ConsoleFormatter.hpp"

namespace mem { namespace log {

ConsoleFormatter::ConsoleFormatter ()
{
   setId("console");

   this->add_tag("type", format_type_start, format_type_end);
   this->add_tag("symbol", format_type_start, format_type_end);
}

ConsoleFormatter::~ConsoleFormatter ()
{
   this->_tags.clear();
}

void
ConsoleFormatter::add_tag (std::string name, log::TagFunctor fptr_start,
   log::TagFunctor fptr_end)
{
   this->_tags[name] = fptr_start;
   this->_tags["/" + name] = fptr_end;
}

std::string
ConsoleFormatter::format (Message* msg)
{
   std::ostringstream res;

   this->format_message(res, msg);

   bool has_details = msg->SecondaryText() != "" || msg->Position() != NULL;

   /*if (has_details)
   {
      res << "           --------------------------------------------------------------------\n";
   }*/
   if (msg->SecondaryText() != "")
   {
      this->format_description(res, msg);
   }

   if (msg->Position() != NULL)
   {
      this->format_position(res, msg->Position());
   }

   if (has_details)
   {
      res << "\n";//           --------------------------------------------------------------------\n";
   }
   return res.str();
}

void
ConsoleFormatter::format_description (std::ostringstream& str, Message* msg)
{
   std::string sec_text = msg->SecondaryText();

   if (sec_text.length() != 0)
   {
      size_t i;
      size_t last_nl = 0;
      for (i=0; i < sec_text.length(); ++i)
      {
         if (sec_text.data()[i] == '\n')
         {
            str << "     ";
            str << sec_text.substr(last_nl, i-last_nl);
            str << "\n";
            last_nl = i+1;
         }
      }
      if (last_nl != sec_text.size())
      {
         str << "     ";
         str << sec_text.substr(last_nl, sec_text.size()-last_nl);
         str << "\n";
      }
   }
}

void
ConsoleFormatter::format_level_name (std::ostringstream& str, MessageLevel lvl)
{
   switch (lvl)
   {
      case UNKNOWN:     str << " (?)"; break;
      case INFO:        str << " (I)"; break;
      case DEBUG:       str << " \033[1;30m(D)"; break;
      case WARNING:     str << " \033[1;34m(W)"; break;
      case ERROR:       str << " \033[1;31m(E)"; break;
      case FATAL_ERROR: str << " \033[1;31m(F)"; break;
   }

}

void
ConsoleFormatter::format_message (std::ostringstream& str, Message* msg)
{
   this->format_level_name(str, msg->Level());
   str << " "; //\033[1m";
   str << msg->PrimaryText();
   str << "\033[0m\n";
}

void
ConsoleFormatter::format_position (std::ostringstream& str, fs::position::Position* pos)
{
   if (pos->_file != NULL)
   {
      str << "     \n     @ ";
      str << pos->gFile()->gPath();
      str << ":";
      str << pos->gLine();
      str << "\n";

      str << "     > ";
      if (pos->gLine() > 0 && pos->gFile()->isLineInFile(pos->gLine()-1))
      {
         std::string context_line = pos->gFile()->getLineCstr(pos->gLine()-1);
         str << context_line;
         str << "\n       ";

         for (size_t i = 1 ; i <= context_line.length() ; ++i)
         {
            switch (pos->getTypeAt(i))
            {
               case fs::position::NOTHING: str << " "; break;
               case fs::position::RANGE:   str << "-"; break;
               case fs::position::CURSOR:  str << "^"; break;
            }
         }
         str << "\n";
      }
      else
      {
         str << "~~BOGUS LINE (";
         str << pos->_line;
         str << ")~~\n";
      }
   }
   else
   {
      str << "       @  {path:???}\n";
   }
}

const char*
ConsoleFormatter::format_string (const char* message)
{
   std::string sresult;
   char buffer[32] = "";
   size_t buffer_cursor = 0;
   //size_t buffer_len = 0;
   bool buffering = false;
   std::vector<StackElement*> stack;
   StackElement* cur_stack_el = NULL;
   size_t i = 0;
   bool keep_looping = true;
   TagFunctor tag_functor = NULL;

   while (keep_looping)
   {
      char c = message[i];

      switch (c)
      {
         case '\0':
            keep_looping = false;
            break;

         case '{':
            buffering = true;
            cur_stack_el = new StackElement();
            cur_stack_el->start = i;
            stack.push_back(cur_stack_el);
            break;

         case '}':
            buffering = false;
            cur_stack_el = stack.back();
            cur_stack_el->end = i;
            tag_functor = this->get_tag_functor(cur_stack_el->name + "/");
            if (tag_functor != NULL)
            {
               sresult.append(tag_functor());
            }
            delete stack.back();
            stack.pop_back();
            break;

         case ':':
            if (buffering)
            {
               buffer[buffer_cursor] = '\0';
               cur_stack_el->name.assign(buffer);
               tag_functor = this->get_tag_functor(cur_stack_el->name);
               if (tag_functor != NULL)
               {
                  sresult.append(tag_functor());
               }
               buffering = false;
               break;
            }

         default:
            if (buffering)
            {
               buffer[buffer_cursor] = c;
               ++ buffer_cursor;
            }
            else
            {
               sresult += c;
            }
      }
      ++ i;
   }

   for (size_t i = 0 ; stack.size() ; ++i)
   {
      delete stack[i];
   }

   char* result = new char[sresult.size() + 1];
   result = strcpy(result, sresult.c_str());
   return result;
}

const char*
ConsoleFormatter::format_type_end ()
{
   return "\033[0m\0";
}

const char*
ConsoleFormatter::format_type_start ()
{
   return "\033[0;34m\0";
}

log::TagFunctor
ConsoleFormatter::get_tag_functor (std::string name)
{
   if (this->_tags.find(name) != this->_tags.end())
   {
      return this->_tags[name];
   }
   return NULL;
}

} }
<commit_msg>Remove the extra line in log messages that have details (description and/or position)<commit_after>#include "mem/log/ConsoleFormatter.hpp"

namespace mem { namespace log {

ConsoleFormatter::ConsoleFormatter ()
{
   setId("console");

   this->add_tag("type", format_type_start, format_type_end);
   this->add_tag("symbol", format_type_start, format_type_end);
}

ConsoleFormatter::~ConsoleFormatter ()
{
   this->_tags.clear();
}

void
ConsoleFormatter::add_tag (std::string name, log::TagFunctor fptr_start,
   log::TagFunctor fptr_end)
{
   this->_tags[name] = fptr_start;
   this->_tags["/" + name] = fptr_end;
}

std::string
ConsoleFormatter::format (Message* msg)
{
   std::ostringstream res;

   this->format_message(res, msg);

   bool has_details = msg->SecondaryText() != "" || msg->Position() != NULL;

   /*if (has_details)
   {
      res << "           --------------------------------------------------------------------\n";
   }*/
   if (msg->SecondaryText() != "")
   {
      this->format_description(res, msg);
   }

   if (msg->Position() != NULL)
   {
      this->format_position(res, msg->Position());
   }
   return res.str();
}

void
ConsoleFormatter::format_description (std::ostringstream& str, Message* msg)
{
   std::string sec_text = msg->SecondaryText();

   if (sec_text.length() != 0)
   {
      size_t i;
      size_t last_nl = 0;
      for (i=0; i < sec_text.length(); ++i)
      {
         if (sec_text.data()[i] == '\n')
         {
            str << "     ";
            str << sec_text.substr(last_nl, i-last_nl);
            str << "\n";
            last_nl = i+1;
         }
      }
      if (last_nl != sec_text.size())
      {
         str << "     ";
         str << sec_text.substr(last_nl, sec_text.size()-last_nl);
         str << "\n";
      }
   }
}

void
ConsoleFormatter::format_level_name (std::ostringstream& str, MessageLevel lvl)
{
   switch (lvl)
   {
      case UNKNOWN:     str << " (?)"; break;
      case INFO:        str << " (I)"; break;
      case DEBUG:       str << " \033[1;30m(D)"; break;
      case WARNING:     str << " \033[1;34m(W)"; break;
      case ERROR:       str << " \033[1;31m(E)"; break;
      case FATAL_ERROR: str << " \033[1;31m(F)"; break;
   }

}

void
ConsoleFormatter::format_message (std::ostringstream& str, Message* msg)
{
   this->format_level_name(str, msg->Level());
   str << " "; //\033[1m";
   str << msg->PrimaryText();
   str << "\033[0m\n";
}

void
ConsoleFormatter::format_position (std::ostringstream& str, fs::position::Position* pos)
{
   if (pos->_file != NULL)
   {
      str << "     \n     @ ";
      str << pos->gFile()->gPath();
      str << ":";
      str << pos->gLine();
      str << "\n";

      str << "     > ";
      if (pos->gLine() > 0 && pos->gFile()->isLineInFile(pos->gLine()-1))
      {
         std::string context_line = pos->gFile()->getLineCstr(pos->gLine()-1);
         str << context_line;
         str << "\n       ";

         for (size_t i = 1 ; i <= context_line.length() ; ++i)
         {
            switch (pos->getTypeAt(i))
            {
               case fs::position::NOTHING: str << " "; break;
               case fs::position::RANGE:   str << "-"; break;
               case fs::position::CURSOR:  str << "^"; break;
            }
         }
         str << "\n";
      }
      else
      {
         str << "~~BOGUS LINE (";
         str << pos->_line;
         str << ")~~\n";
      }
   }
   else
   {
      str << "       @  {path:???}\n";
   }
}

const char*
ConsoleFormatter::format_string (const char* message)
{
   std::string sresult;
   char buffer[32] = "";
   size_t buffer_cursor = 0;
   //size_t buffer_len = 0;
   bool buffering = false;
   std::vector<StackElement*> stack;
   StackElement* cur_stack_el = NULL;
   size_t i = 0;
   bool keep_looping = true;
   TagFunctor tag_functor = NULL;

   while (keep_looping)
   {
      char c = message[i];

      switch (c)
      {
         case '\0':
            keep_looping = false;
            break;

         case '{':
            buffering = true;
            cur_stack_el = new StackElement();
            cur_stack_el->start = i;
            stack.push_back(cur_stack_el);
            break;

         case '}':
            buffering = false;
            cur_stack_el = stack.back();
            cur_stack_el->end = i;
            tag_functor = this->get_tag_functor(cur_stack_el->name + "/");
            if (tag_functor != NULL)
            {
               sresult.append(tag_functor());
            }
            delete stack.back();
            stack.pop_back();
            break;

         case ':':
            if (buffering)
            {
               buffer[buffer_cursor] = '\0';
               cur_stack_el->name.assign(buffer);
               tag_functor = this->get_tag_functor(cur_stack_el->name);
               if (tag_functor != NULL)
               {
                  sresult.append(tag_functor());
               }
               buffering = false;
               break;
            }

         default:
            if (buffering)
            {
               buffer[buffer_cursor] = c;
               ++ buffer_cursor;
            }
            else
            {
               sresult += c;
            }
      }
      ++ i;
   }

   for (size_t i = 0 ; stack.size() ; ++i)
   {
      delete stack[i];
   }

   char* result = new char[sresult.size() + 1];
   result = strcpy(result, sresult.c_str());
   return result;
}

const char*
ConsoleFormatter::format_type_end ()
{
   return "\033[0m\0";
}

const char*
ConsoleFormatter::format_type_start ()
{
   return "\033[0;34m\0";
}

log::TagFunctor
ConsoleFormatter::get_tag_functor (std::string name)
{
   if (this->_tags.find(name) != this->_tags.end())
   {
      return this->_tags[name];
   }
   return NULL;
}

} }
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Vassil Vassilev <vasil.georgiev.vasilev@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/Interpreter/DynamicLibraryManager.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/InvocationOptions.h"

#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

#ifdef WIN32
#include <Windows.h>
#include <shlobj.h>
#else
#include <limits.h> /* PATH_MAX */
#include <dlfcn.h>
#endif

namespace {
#if defined(LLVM_ON_UNIX)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char* env_var = getenv("LD_LIBRARY_PATH");
#if __APPLE__
    if (!env_var)
      env_var = getenv("DYLD_LIBRARY_PATH");
    if (!env_var)
      env_var = getenv("DYLD_FALLBACK_LIBRARY_PATH");
#endif
    if (env_var != 0) {
      static const char PathSeparator = ':';
      const char* at = env_var;
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
          Paths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }

      if (*at != 0)
        if (llvm::sys::fs::is_directory(llvm::StringRef(at)))
          Paths.push_back(at);
    }
#if defined(__APPLE__) || defined(__CYGWIN__)
    Paths.push_back("/usr/local/lib/");
    Paths.push_back("/usr/X11R6/lib/");
    Paths.push_back("/usr/lib/");
    Paths.push_back("/lib/");

    Paths.push_back("/lib/x86_64-linux-gnu/");
    Paths.push_back("/usr/local/lib64/");
    Paths.push_back("/usr/lib64/");
    Paths.push_back("/lib64/");
#else
    static bool initialized = false;
    static std::vector<std::string> SysPaths;
    if (!initialized) {
      // trick to get the system search path
      std::string cmd("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls 2>&1");
      FILE *pf = popen(cmd.c_str (), "r");
      std::string result = "";
      std::string sys_path = "";
      char buffer[128];
      while (!feof(pf)) {
        if (fgets(buffer, 128, pf) != NULL)
          result += buffer;
      }
      pclose(pf);
      std::size_t from
        = result.find("search path=", result.find("(LD_LIBRARY_PATH)"));
      std::size_t to = result.find("(system search path)");
      if (from != std::string::npos && to != std::string::npos) {
        from += 12;
        sys_path = result.substr(from, to-from);
        sys_path.erase(std::remove_if(sys_path.begin(), sys_path.end(), isspace),
                       sys_path.end());
        sys_path += ':';
      }
      static const char PathSeparator = ':';
      const char* at = sys_path.c_str();
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
          SysPaths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }
      initialized = true;
    }

    for (std::vector<std::string>::const_iterator I = SysPaths.begin(),
           E = SysPaths.end(); I != E; ++I)
      Paths.push_back((*I).c_str());
#endif
  }
#elif defined(LLVM_ON_WIN32)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char buff[MAX_PATH];
    // Generic form of C:\Windows\System32
    HRESULT res =  SHGetFolderPathA(NULL,
                                    CSIDL_FLAG_CREATE | CSIDL_SYSTEM,
                                    NULL,
                                    SHGFP_TYPE_CURRENT,
                                    buff);
    if (res != S_OK) {
      assert(0 && "Failed to get system directory");
      return;
    }
    Paths.push_back(buff);

    // Reset buff.
    buff[0] = 0;
    // Generic form of C:\Windows
    res =  SHGetFolderPathA(NULL,
                            CSIDL_FLAG_CREATE | CSIDL_WINDOWS,
                            NULL,
                            SHGFP_TYPE_CURRENT,
                            buff);
    if (res != S_OK) {
      assert(0 && "Failed to get windows directory");
      return;
    }
    Paths.push_back(buff);
  }
#else
# error "Unsupported platform."
#endif

}

namespace cling {
  DynamicLibraryManager::DynamicLibraryManager(const InvocationOptions& Opts)
    : m_Opts(Opts), m_Callbacks(0) {
    GetSystemLibraryPaths(m_SystemSearchPaths);
    m_SystemSearchPaths.push_back(".");
  }

  DynamicLibraryManager::~DynamicLibraryManager() {}

  static bool isSharedLib(llvm::StringRef LibName, bool* exists = 0) {
    using namespace llvm::sys::fs;
    file_magic Magic;
    std::error_code Error = identify_magic(LibName, Magic);
    bool onDisk = (Error != std::errc::no_such_file_or_directory);
    if (exists)
      *exists = onDisk;

    return onDisk &&
#ifdef __APPLE__
      (Magic == file_magic::macho_fixed_virtual_memory_shared_lib
       || Magic == file_magic::macho_dynamically_linked_shared_lib
       || Magic == file_magic::macho_dynamically_linked_shared_lib_stub)
#elif defined(LLVM_ON_UNIX)
#ifdef __CYGWIN__
      (Magic == file_magic::pecoff_executable)
#else
      (Magic == file_magic::elf_shared_object)
#endif
#elif defined(LLVM_ON_WIN32)
      (Magic == file_magic::pecoff_executable)
#else
# error "Unsupported platform."
#endif
      ;
  }

  std::string
  DynamicLibraryManager::lookupLibInPaths(llvm::StringRef libStem) const {
    llvm::SmallVector<std::string, 128>
      Paths(m_Opts.LibSearchPath.begin(), m_Opts.LibSearchPath.end());
    Paths.append(m_SystemSearchPaths.begin(), m_SystemSearchPaths.end());

    for (llvm::SmallVectorImpl<std::string>::const_iterator
           IPath = Paths.begin(), E = Paths.end();IPath != E; ++IPath) {
      llvm::SmallString<512> ThisPath(*IPath); // FIXME: move alloc outside loop
      llvm::sys::path::append(ThisPath, libStem);
      bool exists;
      if (isSharedLib(ThisPath.str(), &exists))
        return ThisPath.str();
      if (exists)
        return "";
    }
    return "";
  }

  std::string
  DynamicLibraryManager::lookupLibMaybeAddExt(llvm::StringRef libStem) const {
    using namespace llvm::sys;

    std::string foundDyLib = lookupLibInPaths(libStem);

    if (foundDyLib.empty()) {
      // Add DyLib extension:
      llvm::SmallString<512> filenameWithExt(libStem);
#if defined(LLVM_ON_UNIX)
#ifdef __APPLE__
      llvm::SmallString<512>::iterator IStemEnd = filenameWithExt.end() - 1;
#endif
      static const char* DyLibExt = ".so";
#elif defined(LLVM_ON_WIN32)
      static const char* DyLibExt = ".dll";
#else
# error "Unsupported platform."
#endif
      filenameWithExt += DyLibExt;
      foundDyLib = lookupLibInPaths(filenameWithExt);
#ifdef __APPLE__
      if (foundDyLib.empty()) {
        filenameWithExt.erase(IStemEnd + 1, filenameWithExt.end());
        filenameWithExt += ".dylib";
        foundDyLib = lookupLibInPaths(filenameWithExt);
      }
#endif
    }

    if (foundDyLib.empty())
      return "";

    // get canonical path name and check if already loaded
#if defined(LLVM_ON_WIN32)
    llvm::SmallString<_MAX_PATH> FullPath("");
    char *res = _fullpath((char *)FullPath.data(), foundDyLib.c_str(), _MAX_PATH);
#else
    llvm::SmallString<PATH_MAX+1> FullPath("");
    char *res = realpath(foundDyLib.c_str(), (char *)FullPath.data());
#endif
    if (res == 0) {
      llvm::errs() << "cling::DynamicLibraryManager::lookupLibMaybeAddExt(): "
        "error getting real (canonical) path of library " << foundDyLib << '\n';
      return foundDyLib;
    }
    FullPath.set_size(strlen(res));
    return FullPath.str();
  }

  std::string DynamicLibraryManager::normalizePath(llvm::StringRef path) {
    // Make the path canonical if the file exists.
    struct stat buffer;
    if (stat(path.data(), &buffer) != 0)
      return "";
#if defined(LLVM_ON_WIN32)
    char buf[_MAX_PATH];
    char *res = _fullpath(buf, path.data(), _MAX_PATH);
#else
    char buf[PATH_MAX+1];
    char *res = realpath(path.data(), buf);
#endif
    if (res == 0) {
      assert(0 && "Cannot normalize!?");
      return "";
    }
    return res;
  }

  std::string
  DynamicLibraryManager::lookupLibrary(llvm::StringRef libStem) const {
    llvm::SmallString<128> Absolute(libStem);
    llvm::sys::fs::make_absolute(Absolute);
    bool isAbsolute = libStem == Absolute;

    // If it is an absolute path, don't try iterate over the paths.
    if (isAbsolute) {
      if (isSharedLib(libStem))
        return normalizePath(libStem);
      else
        return "";
    }

    std::string foundName = lookupLibMaybeAddExt(libStem);
    if (foundName.empty() && !libStem.startswith("lib")) {
      // try with "lib" prefix:
      foundName = lookupLibMaybeAddExt("lib" + libStem.str());
    }

    if (isSharedLib(foundName))
      return normalizePath(foundName);
    return "";
  }

  DynamicLibraryManager::LoadLibResult
  DynamicLibraryManager::loadLibrary(const std::string& libStem,
                                     bool permanent) {
    std::string canonicalLoadedLib = lookupLibrary(libStem);
    if (canonicalLoadedLib.empty())
      return kLoadLibNotFound;

    if (m_LoadedLibraries.find(canonicalLoadedLib) != m_LoadedLibraries.end())
      return kLoadLibAlreadyLoaded;

    std::string errMsg;
    // TODO: !permanent case
#if defined(LLVM_ON_WIN32)
    HMODULE dyLibHandle = LoadLibraryEx(canonicalLoadedLib.c_str(), NULL,
                                        DONT_RESOLVE_DLL_REFERENCES);
    errMsg = "LoadLibraryEx: GetLastError() returned ";
    errMsg += GetLastError();
#else
    const void* dyLibHandle = dlopen(canonicalLoadedLib.c_str(),
                                     RTLD_LAZY|RTLD_GLOBAL);
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (!dyLibHandle) {
      llvm::errs() << "cling::DynamicLibraryManager::loadLibrary(): " << errMsg
                   << '\n';
      return kLoadLibLoadError;
    }
    else if (InterpreterCallbacks* C = getCallbacks())
      C->LibraryLoaded(dyLibHandle, canonicalLoadedLib);

    std::pair<DyLibs::iterator, bool> insRes
      = m_DyLibs.insert(std::pair<DyLibHandle, std::string>(dyLibHandle,
                                                            canonicalLoadedLib));
    if (!insRes.second)
      return kLoadLibAlreadyLoaded;
    m_LoadedLibraries.insert(canonicalLoadedLib);
    return kLoadLibSuccess;
  }

  void DynamicLibraryManager::unloadLibrary(llvm::StringRef libStem) {
    std::string canonicalLoadedLib = lookupLibrary(libStem);
    if (!isLibraryLoaded(canonicalLoadedLib))
      return;

    DyLibHandle dyLibHandle = 0;
    for (DyLibs::const_iterator I = m_DyLibs.begin(), E = m_DyLibs.end();
         I != E; ++I) {
      if (I->second == canonicalLoadedLib)
        dyLibHandle = I->first;
    }

    std::string errMsg;
    // TODO: !permanent case
#if defined(LLVM_ON_WIN32)
    FreeLibrary((HMODULE)dyLibHandle);
    errMsg = "UnoadLibraryEx: GetLastError() returned ";
    errMsg += GetLastError();
#else
    dlclose(const_cast<void*>(dyLibHandle));
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (InterpreterCallbacks* C = getCallbacks())
      C->LibraryUnloaded(dyLibHandle, canonicalLoadedLib);

    m_DyLibs.erase(dyLibHandle);
    m_LoadedLibraries.erase(canonicalLoadedLib);
  }

  bool DynamicLibraryManager::isLibraryLoaded(llvm::StringRef fullPath) const {
    std::string canonPath = normalizePath(fullPath);
    if (m_LoadedLibraries.find(canonPath) != m_LoadedLibraries.end())
      return true;
    return false;
  }

  void DynamicLibraryManager::ExposeHiddenSharedLibrarySymbols(void* handle) {
    llvm::sys::DynamicLibrary::addPermanentLibrary(const_cast<void*>(handle));
  }
} // end namespace cling
<commit_msg>Implement loading of MachO universal bins (e.g. Frameworks).<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Vassil Vassilev <vasil.georgiev.vasilev@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/Interpreter/DynamicLibraryManager.h"
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/InvocationOptions.h"

#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

#ifdef WIN32
#include <Windows.h>
#include <shlobj.h>
#else
#include <limits.h> /* PATH_MAX */
#include <dlfcn.h>
#endif

namespace {
#if defined(LLVM_ON_UNIX)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char* env_var = getenv("LD_LIBRARY_PATH");
#if __APPLE__
    if (!env_var)
      env_var = getenv("DYLD_LIBRARY_PATH");
    if (!env_var)
      env_var = getenv("DYLD_FALLBACK_LIBRARY_PATH");
#endif
    if (env_var != 0) {
      static const char PathSeparator = ':';
      const char* at = env_var;
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
          Paths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }

      if (*at != 0)
        if (llvm::sys::fs::is_directory(llvm::StringRef(at)))
          Paths.push_back(at);
    }
#if defined(__APPLE__) || defined(__CYGWIN__)
    Paths.push_back("/usr/local/lib/");
    Paths.push_back("/usr/X11R6/lib/");
    Paths.push_back("/usr/lib/");
    Paths.push_back("/lib/");

    Paths.push_back("/lib/x86_64-linux-gnu/");
    Paths.push_back("/usr/local/lib64/");
    Paths.push_back("/usr/lib64/");
    Paths.push_back("/lib64/");
#else
    static bool initialized = false;
    static std::vector<std::string> SysPaths;
    if (!initialized) {
      // trick to get the system search path
      std::string cmd("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls 2>&1");
      FILE *pf = popen(cmd.c_str (), "r");
      std::string result = "";
      std::string sys_path = "";
      char buffer[128];
      while (!feof(pf)) {
        if (fgets(buffer, 128, pf) != NULL)
          result += buffer;
      }
      pclose(pf);
      std::size_t from
        = result.find("search path=", result.find("(LD_LIBRARY_PATH)"));
      std::size_t to = result.find("(system search path)");
      if (from != std::string::npos && to != std::string::npos) {
        from += 12;
        sys_path = result.substr(from, to-from);
        sys_path.erase(std::remove_if(sys_path.begin(), sys_path.end(), isspace),
                       sys_path.end());
        sys_path += ':';
      }
      static const char PathSeparator = ':';
      const char* at = sys_path.c_str();
      const char* delim = strchr(at, PathSeparator);
      while (delim != 0) {
        std::string tmp(at, size_t(delim-at));
        if (llvm::sys::fs::is_directory(tmp.c_str()))
          SysPaths.push_back(tmp);
        at = delim + 1;
        delim = strchr(at, PathSeparator);
      }
      initialized = true;
    }

    for (std::vector<std::string>::const_iterator I = SysPaths.begin(),
           E = SysPaths.end(); I != E; ++I)
      Paths.push_back((*I).c_str());
#endif
  }
#elif defined(LLVM_ON_WIN32)
  static void GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
    char buff[MAX_PATH];
    // Generic form of C:\Windows\System32
    HRESULT res =  SHGetFolderPathA(NULL,
                                    CSIDL_FLAG_CREATE | CSIDL_SYSTEM,
                                    NULL,
                                    SHGFP_TYPE_CURRENT,
                                    buff);
    if (res != S_OK) {
      assert(0 && "Failed to get system directory");
      return;
    }
    Paths.push_back(buff);

    // Reset buff.
    buff[0] = 0;
    // Generic form of C:\Windows
    res =  SHGetFolderPathA(NULL,
                            CSIDL_FLAG_CREATE | CSIDL_WINDOWS,
                            NULL,
                            SHGFP_TYPE_CURRENT,
                            buff);
    if (res != S_OK) {
      assert(0 && "Failed to get windows directory");
      return;
    }
    Paths.push_back(buff);
  }
#else
# error "Unsupported platform."
#endif

}

namespace cling {
  DynamicLibraryManager::DynamicLibraryManager(const InvocationOptions& Opts)
    : m_Opts(Opts), m_Callbacks(0) {
    GetSystemLibraryPaths(m_SystemSearchPaths);
    m_SystemSearchPaths.push_back(".");
  }

  DynamicLibraryManager::~DynamicLibraryManager() {}

  static bool isSharedLib(llvm::StringRef LibName, bool* exists = 0) {
    using namespace llvm::sys::fs;
    file_magic Magic;
    std::error_code Error = identify_magic(LibName, Magic);
    bool onDisk = (Error != std::errc::no_such_file_or_directory);
    if (exists)
      *exists = onDisk;

    return onDisk &&
#ifdef __APPLE__
      (Magic == file_magic::macho_fixed_virtual_memory_shared_lib
       || Magic == file_magic::macho_dynamically_linked_shared_lib
       || Magic == file_magic::macho_dynamically_linked_shared_lib_stub
       || Magic == file_magic::macho_universal_binary)
#elif defined(LLVM_ON_UNIX)
#ifdef __CYGWIN__
      (Magic == file_magic::pecoff_executable)
#else
      (Magic == file_magic::elf_shared_object)
#endif
#elif defined(LLVM_ON_WIN32)
      (Magic == file_magic::pecoff_executable)
#else
# error "Unsupported platform."
#endif
      ;
  }

  std::string
  DynamicLibraryManager::lookupLibInPaths(llvm::StringRef libStem) const {
    llvm::SmallVector<std::string, 128>
      Paths(m_Opts.LibSearchPath.begin(), m_Opts.LibSearchPath.end());
    Paths.append(m_SystemSearchPaths.begin(), m_SystemSearchPaths.end());

    for (llvm::SmallVectorImpl<std::string>::const_iterator
           IPath = Paths.begin(), E = Paths.end();IPath != E; ++IPath) {
      llvm::SmallString<512> ThisPath(*IPath); // FIXME: move alloc outside loop
      llvm::sys::path::append(ThisPath, libStem);
      bool exists;
      if (isSharedLib(ThisPath.str(), &exists))
        return ThisPath.str();
      if (exists)
        return "";
    }
    return "";
  }

  std::string
  DynamicLibraryManager::lookupLibMaybeAddExt(llvm::StringRef libStem) const {
    using namespace llvm::sys;

    std::string foundDyLib = lookupLibInPaths(libStem);

    if (foundDyLib.empty()) {
      // Add DyLib extension:
      llvm::SmallString<512> filenameWithExt(libStem);
#if defined(LLVM_ON_UNIX)
#ifdef __APPLE__
      llvm::SmallString<512>::iterator IStemEnd = filenameWithExt.end() - 1;
#endif
      static const char* DyLibExt = ".so";
#elif defined(LLVM_ON_WIN32)
      static const char* DyLibExt = ".dll";
#else
# error "Unsupported platform."
#endif
      filenameWithExt += DyLibExt;
      foundDyLib = lookupLibInPaths(filenameWithExt);
#ifdef __APPLE__
      if (foundDyLib.empty()) {
        filenameWithExt.erase(IStemEnd + 1, filenameWithExt.end());
        filenameWithExt += ".dylib";
        foundDyLib = lookupLibInPaths(filenameWithExt);
      }
#endif
    }

    if (foundDyLib.empty())
      return "";

    // get canonical path name and check if already loaded
#if defined(LLVM_ON_WIN32)
    llvm::SmallString<_MAX_PATH> FullPath("");
    char *res = _fullpath((char *)FullPath.data(), foundDyLib.c_str(), _MAX_PATH);
#else
    llvm::SmallString<PATH_MAX+1> FullPath("");
    char *res = realpath(foundDyLib.c_str(), (char *)FullPath.data());
#endif
    if (res == 0) {
      llvm::errs() << "cling::DynamicLibraryManager::lookupLibMaybeAddExt(): "
        "error getting real (canonical) path of library " << foundDyLib << '\n';
      return foundDyLib;
    }
    FullPath.set_size(strlen(res));
    return FullPath.str();
  }

  std::string DynamicLibraryManager::normalizePath(llvm::StringRef path) {
    // Make the path canonical if the file exists.
    struct stat buffer;
    if (stat(path.data(), &buffer) != 0)
      return "";
#if defined(LLVM_ON_WIN32)
    char buf[_MAX_PATH];
    char *res = _fullpath(buf, path.data(), _MAX_PATH);
#else
    char buf[PATH_MAX+1];
    char *res = realpath(path.data(), buf);
#endif
    if (res == 0) {
      assert(0 && "Cannot normalize!?");
      return "";
    }
    return res;
  }

  std::string
  DynamicLibraryManager::lookupLibrary(llvm::StringRef libStem) const {
    llvm::SmallString<128> Absolute(libStem);
    llvm::sys::fs::make_absolute(Absolute);
    bool isAbsolute = libStem == Absolute;

    // If it is an absolute path, don't try iterate over the paths.
    if (isAbsolute) {
      if (isSharedLib(libStem))
        return normalizePath(libStem);
      else
        return "";
    }

    std::string foundName = lookupLibMaybeAddExt(libStem);
    if (foundName.empty() && !libStem.startswith("lib")) {
      // try with "lib" prefix:
      foundName = lookupLibMaybeAddExt("lib" + libStem.str());
    }

    if (isSharedLib(foundName))
      return normalizePath(foundName);
    return "";
  }

  DynamicLibraryManager::LoadLibResult
  DynamicLibraryManager::loadLibrary(const std::string& libStem,
                                     bool permanent) {
    std::string canonicalLoadedLib = lookupLibrary(libStem);
    if (canonicalLoadedLib.empty())
      return kLoadLibNotFound;

    if (m_LoadedLibraries.find(canonicalLoadedLib) != m_LoadedLibraries.end())
      return kLoadLibAlreadyLoaded;

    std::string errMsg;
    // TODO: !permanent case
#if defined(LLVM_ON_WIN32)
    HMODULE dyLibHandle = LoadLibraryEx(canonicalLoadedLib.c_str(), NULL,
                                        DONT_RESOLVE_DLL_REFERENCES);
    errMsg = "LoadLibraryEx: GetLastError() returned ";
    errMsg += GetLastError();
#else
    const void* dyLibHandle = dlopen(canonicalLoadedLib.c_str(),
                                     RTLD_LAZY|RTLD_GLOBAL);
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (!dyLibHandle) {
      llvm::errs() << "cling::DynamicLibraryManager::loadLibrary(): " << errMsg
                   << '\n';
      return kLoadLibLoadError;
    }
    else if (InterpreterCallbacks* C = getCallbacks())
      C->LibraryLoaded(dyLibHandle, canonicalLoadedLib);

    std::pair<DyLibs::iterator, bool> insRes
      = m_DyLibs.insert(std::pair<DyLibHandle, std::string>(dyLibHandle,
                                                            canonicalLoadedLib));
    if (!insRes.second)
      return kLoadLibAlreadyLoaded;
    m_LoadedLibraries.insert(canonicalLoadedLib);
    return kLoadLibSuccess;
  }

  void DynamicLibraryManager::unloadLibrary(llvm::StringRef libStem) {
    std::string canonicalLoadedLib = lookupLibrary(libStem);
    if (!isLibraryLoaded(canonicalLoadedLib))
      return;

    DyLibHandle dyLibHandle = 0;
    for (DyLibs::const_iterator I = m_DyLibs.begin(), E = m_DyLibs.end();
         I != E; ++I) {
      if (I->second == canonicalLoadedLib)
        dyLibHandle = I->first;
    }

    std::string errMsg;
    // TODO: !permanent case
#if defined(LLVM_ON_WIN32)
    FreeLibrary((HMODULE)dyLibHandle);
    errMsg = "UnoadLibraryEx: GetLastError() returned ";
    errMsg += GetLastError();
#else
    dlclose(const_cast<void*>(dyLibHandle));
    if (const char* DyLibError = dlerror()) {
      errMsg = DyLibError;
    }
#endif
    if (InterpreterCallbacks* C = getCallbacks())
      C->LibraryUnloaded(dyLibHandle, canonicalLoadedLib);

    m_DyLibs.erase(dyLibHandle);
    m_LoadedLibraries.erase(canonicalLoadedLib);
  }

  bool DynamicLibraryManager::isLibraryLoaded(llvm::StringRef fullPath) const {
    std::string canonPath = normalizePath(fullPath);
    if (m_LoadedLibraries.find(canonPath) != m_LoadedLibraries.end())
      return true;
    return false;
  }

  void DynamicLibraryManager::ExposeHiddenSharedLibrarySymbols(void* handle) {
    llvm::sys::DynamicLibrary::addPermanentLibrary(const_cast<void*>(handle));
  }
} // end namespace cling
<|endoftext|>


<commit_before>// =============================================================================
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2014 projectchrono.org
// All right reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file at the top level of the distribution and at
// http://projectchrono.org/license-chrono.txt.
//
// =============================================================================
// Authors: Radu Serban, Justin Madsen
// =============================================================================
//
// Irrlicht-based GUI driver for the a vehicle. This class implements the
// functionality required by its base ChDriver class using keyboard inputs.
// As an Irrlicht event receiver, its OnEvent() callback is used to keep track
// and update the current driver inputs. As such it does not need to override
// the default no-op Update() virtual method.
//
// In addition, this class provides additional Irrlicht support for rendering:
//  - implements a custom camera (which follows the vehicle)
//  - provides support for rendering links, force elements, displaying stats,
//    etc.  In order to render these elements, call the its DrawAll() method
//    instead of ChIrrAppInterface::DrawAll().
//
// =============================================================================

#include <algorithm>

#include "subsys/driver/ChIrrGuiDriver.h"
#include "subsys/powertrain/ChShaftsPowertrain.h"

using namespace irr;

namespace chrono {

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
ChIrrGuiDriver::ChIrrGuiDriver(ChIrrApp&         app,
                               const ChVehicle&  car,
                               int               HUD_x,
                               int               HUD_y)
: m_app(app),
  m_car(car),
  m_HUD_x(HUD_x),
  m_HUD_y(HUD_y),
  m_terrainHeight(0),
  m_throttleDelta(1.0/50),
  m_steeringDelta(1.0/50),
  m_camera(car.GetChassis())
{
  app.SetUserEventReceiver(this);
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
bool ChIrrGuiDriver::OnEvent(const SEvent& event)
{
  // Only interpret keyboard inputs.
  if (event.EventType != EET_KEY_INPUT_EVENT)
    return false;

  if (event.KeyInput.PressedDown) {

    switch (event.KeyInput.Key) {
    case KEY_KEY_A:
      setSteering(m_steering - m_steeringDelta);
      return true;
    case KEY_KEY_D:
      setSteering(m_steering + m_steeringDelta);
      return true;
    case KEY_KEY_W:
      setThrottle(m_throttle + m_throttleDelta);
      return true;
    case KEY_KEY_S:
      setThrottle(m_throttle - m_throttleDelta);
      return true;

    case KEY_DOWN:
      m_camera.Zoom(1);
      return true;
    case KEY_UP:
      m_camera.Zoom(-1);
      return true;
    case KEY_LEFT:
      m_camera.Turn(1);
      return true;
    case KEY_RIGHT:
      m_camera.Turn(-1);
      return true;
    }

  } else {

    switch (event.KeyInput.Key) {
    case KEY_KEY_1:
      m_camera.SetState(utils::ChChaseCamera::Chase);
      return true;
    case KEY_KEY_2:
      m_camera.SetState(utils::ChChaseCamera::Follow);
      return true;
    case KEY_KEY_3:
      m_camera.SetState(utils::ChChaseCamera::Track);
      return true;
    }

  }

  return false;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChIrrGuiDriver::CreateCamera(const ChVector<>& ptOnChassis,
                                  double            chaseDist,
                                  double            chaseHeight)
{
  // Initialize the ChChaseCamera
  m_camera.Initialize(ptOnChassis, chaseDist, chaseHeight);
  ChVector<> cam_pos = m_camera.GetCameraPos();
  ChVector<> cam_target = m_camera.GetTargetPos();

  // Create and initialize the Irrlicht camera
  scene::ICameraSceneNode *camera = m_app.GetSceneManager()->addCameraSceneNode(
    m_app.GetSceneManager()->getRootSceneNode(),
    core::vector3df(0, 0, 0), core::vector3df(0, 0, 0));

  camera->setUpVector(core::vector3df(0, 0, 1));
  camera->setPosition(core::vector3df((f32)cam_pos.x, (f32)cam_pos.y, (f32)cam_pos.z));
  camera->setTarget(core::vector3df((f32)cam_target.x, (f32)cam_target.y, (f32)cam_target.z));
}

void ChIrrGuiDriver::UpdateCamera(double step_size)
{
  // Update the ChChaseCamera
  m_camera.Update(step_size);
  ChVector<> cam_pos = m_camera.GetCameraPos();
  ChVector<> cam_target = m_camera.GetTargetPos();

  // Update the Irrlicht camera
  scene::ICameraSceneNode *camera = m_app.GetSceneManager()->getActiveCamera();

  camera->setPosition(core::vector3df((f32)cam_pos.x, (f32)cam_pos.y, (f32)cam_pos.z));
  camera->setTarget(core::vector3df((f32)cam_target.x, (f32)cam_target.y, (f32)cam_target.z));
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChIrrGuiDriver::DrawAll()
{
  renderGrid();

  m_app.DrawAll();

  renderSprings();
  renderLinks();
  renderStats();
}

void ChIrrGuiDriver::renderSprings()
{
  std::list<chrono::ChLink*>::iterator ilink = m_app.GetSystem()->Get_linklist()->begin();
  for (; ilink != m_app.GetSystem()->Get_linklist()->end(); ++ilink) {
    if (ChLinkSpring* link = dynamic_cast<ChLinkSpring*>(*ilink)) {
      ChIrrTools::drawSpring(m_app.GetVideoDriver(), 0.05,
        link->GetEndPoint1Abs(),
        link->GetEndPoint2Abs(),
        video::SColor(255, 150, 20, 20), 80, 15, true);
    }
  }
}

void ChIrrGuiDriver::renderLinks()
{
  std::list<chrono::ChLink*>::iterator ilink = m_app.GetSystem()->Get_linklist()->begin();
  for (; ilink != m_app.GetSystem()->Get_linklist()->end(); ++ilink) {
    if (ChLinkDistance* link = dynamic_cast<ChLinkDistance*>(*ilink)) {
      ChIrrTools::drawSegment(m_app.GetVideoDriver(),
                              link->GetEndPoint1Abs(),
                              link->GetEndPoint2Abs(),
                              video::SColor(255, 0, 20, 0), true);
    }
  }
}

void ChIrrGuiDriver::renderGrid()
{
  ChCoordsys<> gridCsys(ChVector<>(0, 0, m_terrainHeight + 0.02),
                        chrono::Q_from_AngAxis(-CH_C_PI_2, VECT_Z));

  ChIrrTools::drawGrid(m_app.GetVideoDriver(),
                       0.5, 0.5, 100, 100,
                       gridCsys,
                       video::SColor(255, 80, 130, 255),
                       true);
}

void ChIrrGuiDriver::renderLinGauge(const std::string& msg,
                                    double factor, bool sym,
                                    int xpos, int ypos,
                                    int length, int height)
{
  irr::core::rect<s32> mclip(xpos,ypos, xpos+length, ypos+height);
  m_app.GetVideoDriver()->draw2DRectangle(
            irr::video::SColor(90,60,60,60),
            irr::core::rect<s32>(xpos,ypos, xpos+length, ypos+height),
            &mclip);

  int left  = sym ? (int)((length/2 - 2) * std::min<>(factor,0.0) + length/2) : 2;
  int right = sym ? (int)((length/2 - 2) * std::max<>(factor,0.0) + length/2) : (int)((length - 4)*factor + 2);

  m_app.GetVideoDriver()->draw2DRectangle(
            irr::video::SColor(255,250,200,00),
            irr::core::rect<s32>(xpos+left, ypos+2, xpos+right, ypos+height-2),
            &mclip);

  irr::gui::IGUIFont* font = m_app.GetIGUIEnvironment()->getBuiltInFont();
  font->draw(msg.c_str(),
             irr::core::rect<s32>(xpos+3,ypos+3, xpos+length, ypos+height),
             irr::video::SColor(255,20,20,20));
}

void ChIrrGuiDriver::renderTextBox(const std::string& msg,
                                   int xpos, int ypos,
                                   int length, int height)
{
  irr::core::rect<s32> mclip(xpos, ypos, xpos + length, ypos + height);
  m_app.GetVideoDriver()->draw2DRectangle(
    irr::video::SColor(90, 60, 60, 60),
    irr::core::rect<s32>(xpos, ypos, xpos + length, ypos + height),
    &mclip);

  irr::gui::IGUIFont* font = m_app.GetIGUIEnvironment()->getBuiltInFont();
  font->draw(msg.c_str(),
    irr::core::rect<s32>(xpos + 3, ypos + 3, xpos + length, ypos + height),
    irr::video::SColor(255, 20, 20, 20));
}

void ChIrrGuiDriver::renderStats()
{
  char msg[100];

  sprintf(msg, "Camera mode: %s", m_camera.GetStateName().c_str());
  renderTextBox(std::string(msg), m_HUD_x, m_HUD_y + 10, 120, 15);

  sprintf(msg, "Steering: %+.2f", m_steering);
  renderLinGauge(std::string(msg), m_steering, true, m_HUD_x, m_HUD_y + 40, 120, 15);

  sprintf(msg, "Throttle: %+.2f", m_throttle*100.);
  renderLinGauge(std::string(msg), m_throttle, false, m_HUD_x, m_HUD_y + 60, 120, 15);

  double speed = m_car.GetVehicleSpeed();
  sprintf(msg, "Speed: %+.2f", speed);
  renderLinGauge(std::string(msg), speed/30, false, m_HUD_x, m_HUD_y + 80, 120, 15);

  ChPowertrain* ptrain = m_car.m_powertrain;

  double engine_rpm = ptrain->GetMotorSpeed() * 60 / chrono::CH_C_2PI;
  sprintf(msg, "Eng. RPM: %+.2f", engine_rpm);
  renderLinGauge(std::string(msg), engine_rpm / 7000, false, m_HUD_x, m_HUD_y + 120, 120, 15);

  double engine_torque = ptrain->GetMotorTorque();
  sprintf(msg, "Eng. Nm: %+.2f", engine_torque);
  renderLinGauge(std::string(msg), engine_torque / 600, false, m_HUD_x, m_HUD_y + 140, 120, 15);

  if (ChShaftsPowertrain* powertrain = dynamic_cast<ChShaftsPowertrain*>(ptrain))
  {
    double tc_slip = powertrain->m_torqueconverter->GetSlippage();
    sprintf(msg, "T.conv. slip: %+.2f", tc_slip);
    renderLinGauge(std::string(msg), tc_slip / 1, false, m_HUD_x, m_HUD_y + 160, 120, 15);

    double tc_torquein = -powertrain->m_torqueconverter->GetTorqueReactionOnInput();
    sprintf(msg, "T.conv. in  Nm: %+.2f", tc_torquein);
    renderLinGauge(std::string(msg), tc_torquein / 600, false, m_HUD_x, m_HUD_y + 180, 120, 15);

    double tc_torqueout = powertrain->m_torqueconverter->GetTorqueReactionOnOutput();
    sprintf(msg, "T.conv. out Nm: %+.2f", tc_torqueout);
    renderLinGauge(std::string(msg), tc_torqueout / 600, false, m_HUD_x, m_HUD_y + 200, 120, 15);

    double torque_wheelL = -powertrain->m_rear_differential->GetTorqueReactionOn2();
    sprintf(msg, "Torque wheel L: %+.2f", torque_wheelL);
    renderLinGauge(std::string(msg), torque_wheelL / 5000, false, m_HUD_x, m_HUD_y + 220, 120, 15);

    double torque_wheelR = -powertrain->m_rear_differential->GetTorqueReactionOn3();
    sprintf(msg, "Torque wheel R: %+.2f", torque_wheelR);
    renderLinGauge(std::string(msg), torque_wheelR / 5000, false, m_HUD_x, m_HUD_y + 240, 120, 15);
  }

}

} // end namespace chrono
<commit_msg>use keys for interactive change of transmission mode in Irrlicht 3d view: - key Z = forward mode - key X = neutral mode - key C = reverse mode<commit_after>// =============================================================================
// PROJECT CHRONO - http://projectchrono.org
//
// Copyright (c) 2014 projectchrono.org
// All right reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file at the top level of the distribution and at
// http://projectchrono.org/license-chrono.txt.
//
// =============================================================================
// Authors: Radu Serban, Justin Madsen
// =============================================================================
//
// Irrlicht-based GUI driver for the a vehicle. This class implements the
// functionality required by its base ChDriver class using keyboard inputs.
// As an Irrlicht event receiver, its OnEvent() callback is used to keep track
// and update the current driver inputs. As such it does not need to override
// the default no-op Update() virtual method.
//
// In addition, this class provides additional Irrlicht support for rendering:
//  - implements a custom camera (which follows the vehicle)
//  - provides support for rendering links, force elements, displaying stats,
//    etc.  In order to render these elements, call the its DrawAll() method
//    instead of ChIrrAppInterface::DrawAll().
//
// =============================================================================

#include <algorithm>

#include "subsys/driver/ChIrrGuiDriver.h"
#include "subsys/powertrain/ChShaftsPowertrain.h"

using namespace irr;

namespace chrono {

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
ChIrrGuiDriver::ChIrrGuiDriver(ChIrrApp&         app,
                               const ChVehicle&  car,
                               int               HUD_x,
                               int               HUD_y)
: m_app(app),
  m_car(car),
  m_HUD_x(HUD_x),
  m_HUD_y(HUD_y),
  m_terrainHeight(0),
  m_throttleDelta(1.0/50),
  m_steeringDelta(1.0/50),
  m_camera(car.GetChassis())
{
  app.SetUserEventReceiver(this);
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
bool ChIrrGuiDriver::OnEvent(const SEvent& event)
{
  // Only interpret keyboard inputs.
  if (event.EventType != EET_KEY_INPUT_EVENT)
    return false;

  if (event.KeyInput.PressedDown) {

    switch (event.KeyInput.Key) {
    case KEY_KEY_A:
      setSteering(m_steering - m_steeringDelta);
      return true;
    case KEY_KEY_D:
      setSteering(m_steering + m_steeringDelta);
      return true;
    case KEY_KEY_W:
      setThrottle(m_throttle + m_throttleDelta);
      return true;
    case KEY_KEY_S:
      setThrottle(m_throttle - m_throttleDelta);
      return true;

    case KEY_DOWN:
      m_camera.Zoom(1);
      return true;
    case KEY_UP:
      m_camera.Zoom(-1);
      return true;
    case KEY_LEFT:
      m_camera.Turn(1);
      return true;
    case KEY_RIGHT:
      m_camera.Turn(-1);
      return true;
    }

  } else {

    switch (event.KeyInput.Key) {
    case KEY_KEY_1:
      m_camera.SetState(utils::ChChaseCamera::Chase);
      return true;
    case KEY_KEY_2:
      m_camera.SetState(utils::ChChaseCamera::Follow);
      return true;
    case KEY_KEY_3:
      m_camera.SetState(utils::ChChaseCamera::Track);
      return true;

	case KEY_KEY_Z:
      if (ChShaftsPowertrain* powertrain = dynamic_cast<ChShaftsPowertrain*>(m_car.m_powertrain))
		  powertrain->SetDriveMode(ChShaftsPowertrain::eDriveMode::FORWARD);
      return true;
	case KEY_KEY_X:
      if (ChShaftsPowertrain* powertrain = dynamic_cast<ChShaftsPowertrain*>(m_car.m_powertrain))
		  powertrain->SetDriveMode(ChShaftsPowertrain::eDriveMode::NEUTRAL);
      return true;
	case KEY_KEY_C:
      if (ChShaftsPowertrain* powertrain = dynamic_cast<ChShaftsPowertrain*>(m_car.m_powertrain))
		  powertrain->SetDriveMode(ChShaftsPowertrain::eDriveMode::REVERSE);
      return true;
    }

  }

  return false;
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChIrrGuiDriver::CreateCamera(const ChVector<>& ptOnChassis,
                                  double            chaseDist,
                                  double            chaseHeight)
{
  // Initialize the ChChaseCamera
  m_camera.Initialize(ptOnChassis, chaseDist, chaseHeight);
  ChVector<> cam_pos = m_camera.GetCameraPos();
  ChVector<> cam_target = m_camera.GetTargetPos();

  // Create and initialize the Irrlicht camera
  scene::ICameraSceneNode *camera = m_app.GetSceneManager()->addCameraSceneNode(
    m_app.GetSceneManager()->getRootSceneNode(),
    core::vector3df(0, 0, 0), core::vector3df(0, 0, 0));

  camera->setUpVector(core::vector3df(0, 0, 1));
  camera->setPosition(core::vector3df((f32)cam_pos.x, (f32)cam_pos.y, (f32)cam_pos.z));
  camera->setTarget(core::vector3df((f32)cam_target.x, (f32)cam_target.y, (f32)cam_target.z));
}

void ChIrrGuiDriver::UpdateCamera(double step_size)
{
  // Update the ChChaseCamera
  m_camera.Update(step_size);
  ChVector<> cam_pos = m_camera.GetCameraPos();
  ChVector<> cam_target = m_camera.GetTargetPos();

  // Update the Irrlicht camera
  scene::ICameraSceneNode *camera = m_app.GetSceneManager()->getActiveCamera();

  camera->setPosition(core::vector3df((f32)cam_pos.x, (f32)cam_pos.y, (f32)cam_pos.z));
  camera->setTarget(core::vector3df((f32)cam_target.x, (f32)cam_target.y, (f32)cam_target.z));
}

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
void ChIrrGuiDriver::DrawAll()
{
  renderGrid();

  m_app.DrawAll();

  renderSprings();
  renderLinks();
  renderStats();
}

void ChIrrGuiDriver::renderSprings()
{
  std::list<chrono::ChLink*>::iterator ilink = m_app.GetSystem()->Get_linklist()->begin();
  for (; ilink != m_app.GetSystem()->Get_linklist()->end(); ++ilink) {
    if (ChLinkSpring* link = dynamic_cast<ChLinkSpring*>(*ilink)) {
      ChIrrTools::drawSpring(m_app.GetVideoDriver(), 0.05,
        link->GetEndPoint1Abs(),
        link->GetEndPoint2Abs(),
        video::SColor(255, 150, 20, 20), 80, 15, true);
    }
  }
}

void ChIrrGuiDriver::renderLinks()
{
  std::list<chrono::ChLink*>::iterator ilink = m_app.GetSystem()->Get_linklist()->begin();
  for (; ilink != m_app.GetSystem()->Get_linklist()->end(); ++ilink) {
    if (ChLinkDistance* link = dynamic_cast<ChLinkDistance*>(*ilink)) {
      ChIrrTools::drawSegment(m_app.GetVideoDriver(),
                              link->GetEndPoint1Abs(),
                              link->GetEndPoint2Abs(),
                              video::SColor(255, 0, 20, 0), true);
    }
  }
}

void ChIrrGuiDriver::renderGrid()
{
  ChCoordsys<> gridCsys(ChVector<>(0, 0, m_terrainHeight + 0.02),
                        chrono::Q_from_AngAxis(-CH_C_PI_2, VECT_Z));

  ChIrrTools::drawGrid(m_app.GetVideoDriver(),
                       0.5, 0.5, 100, 100,
                       gridCsys,
                       video::SColor(255, 80, 130, 255),
                       true);
}

void ChIrrGuiDriver::renderLinGauge(const std::string& msg,
                                    double factor, bool sym,
                                    int xpos, int ypos,
                                    int length, int height)
{
  irr::core::rect<s32> mclip(xpos,ypos, xpos+length, ypos+height);
  m_app.GetVideoDriver()->draw2DRectangle(
            irr::video::SColor(90,60,60,60),
            irr::core::rect<s32>(xpos,ypos, xpos+length, ypos+height),
            &mclip);

  int left  = sym ? (int)((length/2 - 2) * std::min<>(factor,0.0) + length/2) : 2;
  int right = sym ? (int)((length/2 - 2) * std::max<>(factor,0.0) + length/2) : (int)((length - 4)*factor + 2);

  m_app.GetVideoDriver()->draw2DRectangle(
            irr::video::SColor(255,250,200,00),
            irr::core::rect<s32>(xpos+left, ypos+2, xpos+right, ypos+height-2),
            &mclip);

  irr::gui::IGUIFont* font = m_app.GetIGUIEnvironment()->getBuiltInFont();
  font->draw(msg.c_str(),
             irr::core::rect<s32>(xpos+3,ypos+3, xpos+length, ypos+height),
             irr::video::SColor(255,20,20,20));
}

void ChIrrGuiDriver::renderTextBox(const std::string& msg,
                                   int xpos, int ypos,
                                   int length, int height)
{
  irr::core::rect<s32> mclip(xpos, ypos, xpos + length, ypos + height);
  m_app.GetVideoDriver()->draw2DRectangle(
    irr::video::SColor(90, 60, 60, 60),
    irr::core::rect<s32>(xpos, ypos, xpos + length, ypos + height),
    &mclip);

  irr::gui::IGUIFont* font = m_app.GetIGUIEnvironment()->getBuiltInFont();
  font->draw(msg.c_str(),
    irr::core::rect<s32>(xpos + 3, ypos + 3, xpos + length, ypos + height),
    irr::video::SColor(255, 20, 20, 20));
}

void ChIrrGuiDriver::renderStats()
{
  char msg[100];

  sprintf(msg, "Camera mode: %s", m_camera.GetStateName().c_str());
  renderTextBox(std::string(msg), m_HUD_x, m_HUD_y + 10, 120, 15);

  sprintf(msg, "Steering: %+.2f", m_steering);
  renderLinGauge(std::string(msg), m_steering, true, m_HUD_x, m_HUD_y + 40, 120, 15);

  sprintf(msg, "Throttle: %+.2f", m_throttle*100.);
  renderLinGauge(std::string(msg), m_throttle, false, m_HUD_x, m_HUD_y + 60, 120, 15);

  double speed = m_car.GetVehicleSpeed();
  sprintf(msg, "Speed: %+.2f", speed);
  renderLinGauge(std::string(msg), speed/30, false, m_HUD_x, m_HUD_y + 80, 120, 15);

  ChPowertrain* ptrain = m_car.m_powertrain;

  double engine_rpm = ptrain->GetMotorSpeed() * 60 / chrono::CH_C_2PI;
  sprintf(msg, "Eng. RPM: %+.2f", engine_rpm);
  renderLinGauge(std::string(msg), engine_rpm / 7000, false, m_HUD_x, m_HUD_y + 120, 120, 15);

  double engine_torque = ptrain->GetMotorTorque();
  sprintf(msg, "Eng. Nm: %+.2f", engine_torque);
  renderLinGauge(std::string(msg), engine_torque / 600, false, m_HUD_x, m_HUD_y + 140, 120, 15);

  if (ChShaftsPowertrain* powertrain = dynamic_cast<ChShaftsPowertrain*>(ptrain))
  {
    double tc_slip = powertrain->m_torqueconverter->GetSlippage();
    sprintf(msg, "T.conv. slip: %+.2f", tc_slip);
    renderLinGauge(std::string(msg), tc_slip / 1, false, m_HUD_x, m_HUD_y + 160, 120, 15);

    double tc_torquein = -powertrain->m_torqueconverter->GetTorqueReactionOnInput();
    sprintf(msg, "T.conv. in  Nm: %+.2f", tc_torquein);
    renderLinGauge(std::string(msg), tc_torquein / 600, false, m_HUD_x, m_HUD_y + 180, 120, 15);

    double tc_torqueout = powertrain->m_torqueconverter->GetTorqueReactionOnOutput();
    sprintf(msg, "T.conv. out Nm: %+.2f", tc_torqueout);
    renderLinGauge(std::string(msg), tc_torqueout / 600, false, m_HUD_x, m_HUD_y + 200, 120, 15);

    double torque_wheelL = -powertrain->m_rear_differential->GetTorqueReactionOn2();
    sprintf(msg, "Torque wheel L: %+.2f", torque_wheelL);
    renderLinGauge(std::string(msg), torque_wheelL / 5000, false, m_HUD_x, m_HUD_y + 220, 120, 15);

    double torque_wheelR = -powertrain->m_rear_differential->GetTorqueReactionOn3();
    sprintf(msg, "Torque wheel R: %+.2f", torque_wheelR);
    renderLinGauge(std::string(msg), torque_wheelR / 5000, false, m_HUD_x, m_HUD_y + 240, 120, 15);

	int ngear = powertrain->GetSelectedGear();
	ChShaftsPowertrain::eDriveMode drivemode = powertrain->GetDriveMode();
	switch (drivemode)
	{
		case ChShaftsPowertrain::FORWARD:
			sprintf(msg, "Gear: forward, n.gear: %d", ngear);
			break;
		case ChShaftsPowertrain::NEUTRAL:
			sprintf(msg, "Gear: neutral %d", ngear);
			break;
		case ChShaftsPowertrain::REVERSE:
			sprintf(msg, "Gear: reverse %d", ngear);
			break;
		default:
			sprintf(msg, "");
			break;
	}
    renderLinGauge(std::string(msg), (double)ngear / 4.0, false, m_HUD_x, m_HUD_y + 260, 120, 15);
  }

}

} // end namespace chrono
<|endoftext|>


<commit_before>/*
 * Copyright © 2012 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/** @file brw_fs_register_coalesce.cpp
 *
 * Implements register coalescing: Checks if the two registers involved in a
 * raw move don't interfere, in which case they can both be stored in the same
 * place and the MOV removed.
 *
 * To do this, all uses of the source of the MOV in the shader are replaced
 * with the destination of the MOV. For example:
 *
 * add vgrf3:F, vgrf1:F, vgrf2:F
 * mov vgrf4:F, vgrf3:F
 * mul vgrf5:F, vgrf5:F, vgrf4:F
 *
 * becomes
 *
 * add vgrf4:F, vgrf1:F, vgrf2:F
 * mul vgrf5:F, vgrf5:F, vgrf4:F
 */

#include "brw_fs.h"
#include "brw_fs_live_variables.h"

static bool
is_nop_mov(const fs_inst *inst)
{
   if (inst->opcode == BRW_OPCODE_MOV) {
      return inst->dst.equals(inst->src[0]);
   }

   return false;
}

static bool
is_coalesce_candidate(const fs_inst *inst, const int *virtual_grf_sizes)
{
   if (inst->opcode != BRW_OPCODE_MOV ||
       inst->is_partial_write() ||
       inst->saturate ||
       inst->src[0].file != GRF ||
       inst->src[0].negate ||
       inst->src[0].abs ||
       !inst->src[0].is_contiguous() ||
       inst->dst.file != GRF ||
       inst->dst.type != inst->src[0].type) {
      return false;
   }

   if (virtual_grf_sizes[inst->src[0].reg] >
       virtual_grf_sizes[inst->dst.reg])
      return false;

   return true;
}

static bool
can_coalesce_vars(brw::fs_live_variables *live_intervals,
                  const exec_list *instructions, const fs_inst *inst, int ip,
                  int var_to, int var_from)
{
   if (!live_intervals->vars_interfere(var_from, var_to))
      return true;

   /* We know that the live ranges of A (var_from) and B (var_to)
    * interfere because of the ->vars_interfere() call above. If the end
    * of B's live range is after the end of A's range, then we know two
    * things:
    *  - the start of B's live range must be in A's live range (since we
    *    already know the two ranges interfere, this is the only remaining
    *    possibility)
    *  - the interference isn't of the form we're looking for (where B is
    *    entirely inside A)
    */
   if (live_intervals->end[var_to] > live_intervals->end[var_from])
      return false;

   assert(ip >= live_intervals->start[var_to]);

   fs_inst *scan_inst;
   for (scan_inst = (fs_inst *)inst->next;
        !scan_inst->is_tail_sentinel() && ip <= live_intervals->end[var_to];
        scan_inst = (fs_inst *)scan_inst->next, ip++) {
      if (scan_inst->is_control_flow())
         return false;

      if (scan_inst->dst.equals(inst->dst) ||
          scan_inst->dst.equals(inst->src[0]))
         return false;
   }

   return true;
}

bool
fs_visitor::register_coalesce()
{
   bool progress = false;

   calculate_live_intervals();

   int src_size = 0;
   int channels_remaining = 0;
   int reg_from = -1, reg_to = -1;
   int reg_to_offset[MAX_SAMPLER_MESSAGE_SIZE];
   fs_inst *mov[MAX_SAMPLER_MESSAGE_SIZE];
   int var_to[MAX_SAMPLER_MESSAGE_SIZE];
   int var_from[MAX_SAMPLER_MESSAGE_SIZE];
   int ip = -1;

   foreach_list(node, &this->instructions) {
      fs_inst *inst = (fs_inst *)node;
      ip++;

      if (!is_coalesce_candidate(inst, virtual_grf_sizes))
         continue;

      if (is_nop_mov(inst)) {
         inst->opcode = BRW_OPCODE_NOP;
         progress = true;
         continue;
      }

      if (reg_from != inst->src[0].reg) {
         reg_from = inst->src[0].reg;

         src_size = virtual_grf_sizes[inst->src[0].reg];
         assert(src_size <= MAX_SAMPLER_MESSAGE_SIZE);

         channels_remaining = src_size;
         memset(mov, 0, sizeof(mov));

         reg_to = inst->dst.reg;
      }

      if (reg_to != inst->dst.reg)
         continue;

      const int offset = inst->src[0].reg_offset;
      reg_to_offset[offset] = inst->dst.reg_offset;
      mov[offset] = inst;
      channels_remaining--;

      if (channels_remaining)
         continue;

      bool can_coalesce = true;
      for (int i = 0; i < src_size; i++) {
         var_to[i] = live_intervals->var_from_vgrf[reg_to] + reg_to_offset[i];
         var_from[i] = live_intervals->var_from_vgrf[reg_from] + i;

         if (!can_coalesce_vars(live_intervals, &instructions, inst, ip,
                                var_to[i], var_from[i])) {
            can_coalesce = false;
            reg_from = -1;
            break;
         }
      }

      if (!can_coalesce)
         continue;

      progress = true;

      for (int i = 0; i < src_size; i++) {
         if (mov[i]) {
            mov[i]->opcode = BRW_OPCODE_NOP;
            mov[i]->conditional_mod = BRW_CONDITIONAL_NONE;
            mov[i]->dst = reg_undef;
            mov[i]->src[0] = reg_undef;
            mov[i]->src[1] = reg_undef;
            mov[i]->src[2] = reg_undef;
         }
      }

      foreach_list(node, &this->instructions) {
         fs_inst *scan_inst = (fs_inst *)node;

         for (int i = 0; i < src_size; i++) {
            if (mov[i]) {
               if (scan_inst->dst.file == GRF &&
                   scan_inst->dst.reg == reg_from &&
                   scan_inst->dst.reg_offset == i) {
                  scan_inst->dst.reg = reg_to;
                  scan_inst->dst.reg_offset = reg_to_offset[i];
               }
               for (int j = 0; j < 3; j++) {
                  if (scan_inst->src[j].file == GRF &&
                      scan_inst->src[j].reg == reg_from &&
                      scan_inst->src[j].reg_offset == i) {
                     scan_inst->src[j].reg = reg_to;
                     scan_inst->src[j].reg_offset = reg_to_offset[i];
                  }
               }
            }
         }
      }

      for (int i = 0; i < src_size; i++) {
         live_intervals->start[var_to[i]] =
            MIN2(live_intervals->start[var_to[i]],
                 live_intervals->start[var_from[i]]);
         live_intervals->end[var_to[i]] =
            MAX2(live_intervals->end[var_to[i]],
                 live_intervals->end[var_from[i]]);
      }
      reg_from = -1;
   }

   if (progress) {
      foreach_list_safe(node, &this->instructions) {
         fs_inst *inst = (fs_inst *)node;

         if (inst->opcode == BRW_OPCODE_NOP) {
            inst->remove();
         }
      }

      invalidate_live_intervals();
   }

   return progress;
}
<commit_msg>Revert "i965/fs: Simplify interference scan in register coalescing."<commit_after>/*
 * Copyright © 2012 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/** @file brw_fs_register_coalesce.cpp
 *
 * Implements register coalescing: Checks if the two registers involved in a
 * raw move don't interfere, in which case they can both be stored in the same
 * place and the MOV removed.
 *
 * To do this, all uses of the source of the MOV in the shader are replaced
 * with the destination of the MOV. For example:
 *
 * add vgrf3:F, vgrf1:F, vgrf2:F
 * mov vgrf4:F, vgrf3:F
 * mul vgrf5:F, vgrf5:F, vgrf4:F
 *
 * becomes
 *
 * add vgrf4:F, vgrf1:F, vgrf2:F
 * mul vgrf5:F, vgrf5:F, vgrf4:F
 */

#include "brw_fs.h"
#include "brw_fs_live_variables.h"

static bool
is_nop_mov(const fs_inst *inst)
{
   if (inst->opcode == BRW_OPCODE_MOV) {
      return inst->dst.equals(inst->src[0]);
   }

   return false;
}

static bool
is_coalesce_candidate(const fs_inst *inst, const int *virtual_grf_sizes)
{
   if (inst->opcode != BRW_OPCODE_MOV ||
       inst->is_partial_write() ||
       inst->saturate ||
       inst->src[0].file != GRF ||
       inst->src[0].negate ||
       inst->src[0].abs ||
       !inst->src[0].is_contiguous() ||
       inst->dst.file != GRF ||
       inst->dst.type != inst->src[0].type) {
      return false;
   }

   if (virtual_grf_sizes[inst->src[0].reg] >
       virtual_grf_sizes[inst->dst.reg])
      return false;

   return true;
}

static bool
can_coalesce_vars(brw::fs_live_variables *live_intervals,
                  const exec_list *instructions, const fs_inst *inst,
                  int var_to, int var_from)
{
   if (!live_intervals->vars_interfere(var_from, var_to))
      return true;

   /* We know that the live ranges of A (var_from) and B (var_to)
    * interfere because of the ->vars_interfere() call above. If the end
    * of B's live range is after the end of A's range, then we know two
    * things:
    *  - the start of B's live range must be in A's live range (since we
    *    already know the two ranges interfere, this is the only remaining
    *    possibility)
    *  - the interference isn't of the form we're looking for (where B is
    *    entirely inside A)
    */
   if (live_intervals->end[var_to] > live_intervals->end[var_from])
      return false;

   int scan_ip = -1;

   foreach_list(n, instructions) {
      fs_inst *scan_inst = (fs_inst *)n;
      scan_ip++;

      if (scan_inst->is_control_flow())
         return false;

      if (scan_ip <= live_intervals->start[var_to])
         continue;

      if (scan_ip > live_intervals->end[var_to])
         break;

      if (scan_inst->dst.equals(inst->dst) ||
          scan_inst->dst.equals(inst->src[0]))
         return false;
   }

   return true;
}

bool
fs_visitor::register_coalesce()
{
   bool progress = false;

   calculate_live_intervals();

   int src_size = 0;
   int channels_remaining = 0;
   int reg_from = -1, reg_to = -1;
   int reg_to_offset[MAX_SAMPLER_MESSAGE_SIZE];
   fs_inst *mov[MAX_SAMPLER_MESSAGE_SIZE];
   int var_to[MAX_SAMPLER_MESSAGE_SIZE];
   int var_from[MAX_SAMPLER_MESSAGE_SIZE];

   foreach_list(node, &this->instructions) {
      fs_inst *inst = (fs_inst *)node;

      if (!is_coalesce_candidate(inst, virtual_grf_sizes))
         continue;

      if (is_nop_mov(inst)) {
         inst->opcode = BRW_OPCODE_NOP;
         progress = true;
         continue;
      }

      if (reg_from != inst->src[0].reg) {
         reg_from = inst->src[0].reg;

         src_size = virtual_grf_sizes[inst->src[0].reg];
         assert(src_size <= MAX_SAMPLER_MESSAGE_SIZE);

         channels_remaining = src_size;
         memset(mov, 0, sizeof(mov));

         reg_to = inst->dst.reg;
      }

      if (reg_to != inst->dst.reg)
         continue;

      const int offset = inst->src[0].reg_offset;
      reg_to_offset[offset] = inst->dst.reg_offset;
      mov[offset] = inst;
      channels_remaining--;

      if (channels_remaining)
         continue;

      bool can_coalesce = true;
      for (int i = 0; i < src_size; i++) {
         var_to[i] = live_intervals->var_from_vgrf[reg_to] + reg_to_offset[i];
         var_from[i] = live_intervals->var_from_vgrf[reg_from] + i;

         if (!can_coalesce_vars(live_intervals, &instructions, inst,
                                var_to[i], var_from[i])) {
            can_coalesce = false;
            reg_from = -1;
            break;
         }
      }

      if (!can_coalesce)
         continue;

      progress = true;

      for (int i = 0; i < src_size; i++) {
         if (mov[i]) {
            mov[i]->opcode = BRW_OPCODE_NOP;
            mov[i]->conditional_mod = BRW_CONDITIONAL_NONE;
            mov[i]->dst = reg_undef;
            mov[i]->src[0] = reg_undef;
            mov[i]->src[1] = reg_undef;
            mov[i]->src[2] = reg_undef;
         }
      }

      foreach_list(node, &this->instructions) {
         fs_inst *scan_inst = (fs_inst *)node;

         for (int i = 0; i < src_size; i++) {
            if (mov[i]) {
               if (scan_inst->dst.file == GRF &&
                   scan_inst->dst.reg == reg_from &&
                   scan_inst->dst.reg_offset == i) {
                  scan_inst->dst.reg = reg_to;
                  scan_inst->dst.reg_offset = reg_to_offset[i];
               }
               for (int j = 0; j < 3; j++) {
                  if (scan_inst->src[j].file == GRF &&
                      scan_inst->src[j].reg == reg_from &&
                      scan_inst->src[j].reg_offset == i) {
                     scan_inst->src[j].reg = reg_to;
                     scan_inst->src[j].reg_offset = reg_to_offset[i];
                  }
               }
            }
         }
      }

      for (int i = 0; i < src_size; i++) {
         live_intervals->start[var_to[i]] =
            MIN2(live_intervals->start[var_to[i]],
                 live_intervals->start[var_from[i]]);
         live_intervals->end[var_to[i]] =
            MAX2(live_intervals->end[var_to[i]],
                 live_intervals->end[var_from[i]]);
      }
      reg_from = -1;
   }

   if (progress) {
      foreach_list_safe(node, &this->instructions) {
         fs_inst *inst = (fs_inst *)node;

         if (inst->opcode == BRW_OPCODE_NOP) {
            inst->remove();
         }
      }

      invalidate_live_intervals();
   }

   return progress;
}
<|endoftext|>


<commit_before>#include <tbb/task.h>

#include <iostream>
#include <sstream>
#include <cassert>
#include <atomic>

// node in a linked list of tasks that depend on a cont
struct cont_node
{
    tbb::task* task;
    cont_node* next;
};

// base class for working with conts (encapsulates tricky atomic code)
class cont_base
{
    // head of the linked list of successors queued on this cont
    std::atomic<cont_node*> _head = NULL;

public:
    cont_base() = default;

    cont_base(const cont_base&) = delete;
    cont_base& operator=(const cont_base&) = delete;
    cont_base(cont_base&&) = delete;
    cont_base& operator=(cont_base&&) = delete;

    // return true if this cont has been set_ready()
    bool is_ready() const
    {
        // TODO: pick better than memory_order_seq_cst?
        return ((intptr_t)_head.load(std::memory_order_seq_cst) & 1) != 0;
    }

    // sends this cont to all successors in the linked list.
    void set_ready()
    {
        assert(!is_ready());

        cont_node* old_head;

        // mark the cont as ready atomically. readiness is indicated by the least significant bit of the head pointer.
        for (;;)
        {
            // TODO: pick better than memory_order_seq_cst
            old_head = _head.load(std::memory_order_seq_cst);
            cont_node* ready_head = (cont_node*)((intptr_t)old_head | 1);

            // TODO: pick better than memory_order_seq_cst and consider if I should use _explicit
            if (_head.compare_exchange_weak(old_head, ready_head, std::memory_order_seq_cst))
            {
                // If the CAS failed, that means a successor just added themselves to the list,
                // since that successor thought this cont was not ready yet. (or possibly it was a spurious wakeup.)
                // If it succeeded, then the cont can notify all successors that have been queued so far.
                break;
            }
        }

        // Notify all successors that have been queued
        for (cont_node* node = old_head; node != NULL; node = node->next)
        {
            if (node->task->decrement_ref_count() == 0)
            {
                // this was the last missing input, so the task can now be spawned.
                tbb::task::spawn(*node->task);
            }
        }
    }

    // Tries adding the given task to the cont's successor linked list using the given linked list node.
    // This fails (and returns false) if the successor queue has already been closed because the cont has already been set.
    // If it succeeds (and returns true), then the passed-in task was successfully added to the linked list.
    bool try_register_successor(tbb::task* t, cont_node* c)
    {
        cont_node* new_head = c;
        new_head->task = t;

        // TODO: pick better than memory_order_seq_cst
        cont_node* old_head = _head.load(std::memory_order_seq_cst);
        new_head->next = old_head;

        // TODO: pick better than memory_order_seq_cst and consider if I should use _explicit
        return _head.compare_exchange_strong(old_head, new_head, std::memory_order_seq_cst);
    }
};

// Associates data to a cont, std::optional-style.
// TODO: Just replace all of this with std::optional?
template<class T>
class cont : public cont_base
{
    bool _has_value = false;
    std::aligned_storage_t<sizeof(T), alignof(T)> _storage;

public:
    cont() = default;

    cont(const cont&) = delete;
    cont& operator=(const cont&) = delete;
    cont(cont&&) = delete;
    cont& operator=(cont&&) = delete;

    ~cont()
    {
        if (_has_value)
        {
            (**this).~T();
        }
    }

    T* operator->()
    {
        return reinterpret_cast<T*>(&_storage);
    }

    const T* operator->() const
    {
        return *reinterpret_cast<const T*>(&_storage);
    }

    T& operator*()
    {
        return *reinterpret_cast<T*>(&_storage);
    }

    const T& operator*() const
    {
        return *reinterpret_cast<const T*>(&_storage);
    }

    template<class... Args>
    void emplace(Args&&... args)
    {
        assert(!is_ready());

        if (_has_value)
        {
            (**this).~T();
        }

        new (&_storage) T(std::forward<Args>(args)...);

        _has_value = true;
    }
};

// spawns the given task when all the "conts" are ready. There must be a linked list node supplied for each cont.
void spawn_when_ready(tbb::task& t, cont_base** conts, cont_node* nodes, int num_conts)
{
    // +1 reference count for each missing argument
    // the task is only spawned when the reference count is zero,
    // so that means it gets decremented once for each input that gets filled in.
    t.add_ref_count(num_conts);

    int num_inputs_already_ok = 0;

    for (size_t cont_i = 0; cont_i < num_conts; cont_i++)
    {
        cont_base* c = conts[cont_i];

        // try registering the task as a successor of each cont, so the task will get notified (and its refcount decremented) when the cont becomes available
        if (!c->try_register_successor(&t, &nodes[cont_i]))
        {
            // if we can't subscribe a successor to the cont, that means the cont is already set.
            // in other words, that input is already ready to go, and we don't need to wait for a notification about it.
            num_inputs_already_ok++;
        }
    }

    // incorporate the inputs that already okay into the reference count.
    // if the reference count hits zero, that means all inputs are satisfied and the task can be spawned.
    if (t.add_ref_count(-num_inputs_already_ok) == 0)
    {
        tbb::task::spawn(t);
    }
}

class TaskA : public tbb::task
{
    class A_Subtask1 : public tbb::task
    {
        cont<int>* c;

    public:
        A_Subtask1(cont<int>* c)
        {
            this->c = c;
        }

        tbb::task* execute() override
        {
            std::cout << "A_Subtask1\n";

            // set the value of c
            c->emplace(1337);
            // broadcast that c is ready to all the successors enqueued on the cont.
            c->set_ready();

            return NULL;
        }
    };

    class A_Subtask2 : public tbb::task
    {
    public:
        tbb::task* execute() override
        {
            std::cout << "A_Subtask2\n";

            return NULL;
        }
    };

    cont<int>* c;

public:
    TaskA(cont<int>* c)
    {
        this->c = c;
    }

    tbb::task* execute() override
    {
        std::cout << "A\n";

        // 2 children + wait
        set_ref_count(3);

        spawn(*new(allocate_child()) A_Subtask1(c));
        spawn_and_wait_for_all(*new(allocate_child()) A_Subtask2());

        return NULL;
    }
};

class TaskB : public tbb::task
{
public:
    tbb::task* execute() override
    {
        std::cout << "B\n";

        return NULL;
    }
};

class TaskC : public tbb::task
{
    cont<int>* c;

public:
    cont_node cnodes[1];

    TaskC(cont<int>* c)
    {
        this->c = c;
    }

    tbb::task* execute() override
    {
        std::cout << "C\n";

        std::stringstream ss;
        ss << "C received " << **c << "\n";
        std::cout << ss.rdbuf();

        return NULL;
    }
};

class MainTask : public tbb::task
{
public:
    tbb::task* execute() override
    {
        cont<int> c;

        // 3 children + wait
        set_ref_count(4);

        spawn(*new(allocate_child()) TaskA(&c));
        spawn(*new(allocate_child()) TaskB());

        // run TaskC when all the conts are satisfied (cont "c" gets set inside TaskA)
        TaskC& taskC = *new(allocate_child()) TaskC(&c);
        cont_base* conts[] = { &c };
        spawn_when_ready(taskC, conts, taskC.cnodes, _countof(conts));

        wait_for_all();

        return NULL;
    }
};

int main()
{
    tbb::task::spawn_root_and_wait(*new(tbb::task::allocate_root()) MainTask());
}<commit_msg>minor docs<commit_after>#include <tbb/task.h>

#include <iostream>
#include <sstream>
#include <cassert>
#include <atomic>

// node in a linked list of tasks that depend on a cont
struct cont_node
{
    tbb::task* task;
    cont_node* next;
};

// base class for working with conts (encapsulates tricky atomic code)
class cont_base
{
    // head of the linked list of successors queued on this cont
    std::atomic<cont_node*> _head = NULL;

public:
    cont_base() = default;

    cont_base(const cont_base&) = delete;
    cont_base& operator=(const cont_base&) = delete;
    cont_base(cont_base&&) = delete;
    cont_base& operator=(cont_base&&) = delete;

    // return true if this cont has been set_ready()
    bool is_ready() const
    {
        // TODO: pick better than memory_order_seq_cst?
        return ((intptr_t)_head.load(std::memory_order_seq_cst) & 1) != 0;
    }

    // sends this cont to all successors in the linked list.
    void set_ready()
    {
        assert(!is_ready());

        cont_node* old_head;

        // mark the cont as ready atomically. readiness is indicated by the least significant bit of the head pointer.
        for (;;)
        {
            // TODO: pick better than memory_order_seq_cst
            old_head = _head.load(std::memory_order_seq_cst);
            cont_node* ready_head = (cont_node*)((intptr_t)old_head | 1);

            // TODO: pick better than memory_order_seq_cst and consider if I should use _explicit
            if (_head.compare_exchange_weak(old_head, ready_head, std::memory_order_seq_cst))
            {
                // If the CAS failed, that means a successor just added themselves to the list,
                // since that successor thought this cont was not ready yet. (or possibly it was a spurious wakeup.)
                // If it succeeded, then the cont can notify all successors that have been queued so far.
                break;
            }
        }

        // Notify all successors that have been queued
        for (cont_node* node = old_head; node != NULL; node = node->next)
        {
            if (node->task->decrement_ref_count() == 0)
            {
                // this was the last missing input, so the task can now be spawned.
                tbb::task::spawn(*node->task);
            }
        }
    }

    // Tries adding the given task to the cont's successor linked list using the given linked list node.
    // This fails (and returns false) if the successor queue has already been closed because the cont has already been set.
    // If it succeeds (and returns true), then the passed-in task was successfully added to the linked list.
    bool try_register_successor(tbb::task* t, cont_node* c)
    {
        cont_node* new_head = c;
        new_head->task = t;

        // TODO: pick better than memory_order_seq_cst
        cont_node* old_head = _head.load(std::memory_order_seq_cst);
        new_head->next = old_head;

        // TODO: pick better than memory_order_seq_cst and consider if I should use _explicit
        return _head.compare_exchange_strong(old_head, new_head, std::memory_order_seq_cst);
    }
};

// Associates data to a cont, std::optional-style.
// TODO: Just replace all of this with std::optional?
template<class T>
class cont : public cont_base
{
    bool _has_value = false;
    std::aligned_storage_t<sizeof(T), alignof(T)> _storage;

public:
    cont() = default;

    cont(const cont&) = delete;
    cont& operator=(const cont&) = delete;
    cont(cont&&) = delete;
    cont& operator=(cont&&) = delete;

    ~cont()
    {
        if (_has_value)
        {
            (**this).~T();
        }
    }

    T* operator->()
    {
        return reinterpret_cast<T*>(&_storage);
    }

    const T* operator->() const
    {
        return *reinterpret_cast<const T*>(&_storage);
    }

    T& operator*()
    {
        return *reinterpret_cast<T*>(&_storage);
    }

    const T& operator*() const
    {
        return *reinterpret_cast<const T*>(&_storage);
    }

    template<class... Args>
    void emplace(Args&&... args)
    {
        assert(!is_ready());

        if (_has_value)
        {
            (**this).~T();
        }

        new (&_storage) T(std::forward<Args>(args)...);

        _has_value = true;
    }
};

// spawns the given task when all the "conts" are ready. There must be a linked list node supplied for each cont.
void spawn_when_ready(tbb::task& t, cont_base** conts, cont_node* nodes, int num_conts)
{
    // +1 reference count for each missing argument
    // the task is only spawned when the reference count is zero,
    // so that means it gets decremented once for each input that gets filled in.
    t.add_ref_count(num_conts);

    int num_inputs_already_ok = 0;

    for (size_t cont_i = 0; cont_i < num_conts; cont_i++)
    {
        cont_base* c = conts[cont_i];

        // try registering the task as a successor of each cont, so the task will get notified (and its refcount decremented) when the cont becomes available
        if (!c->try_register_successor(&t, &nodes[cont_i]))
        {
            // if we can't subscribe a successor to the cont, that means the cont is already set.
            // in other words, that input is already ready to go, and we don't need to wait for a notification about it.
            num_inputs_already_ok++;
        }
    }

    // incorporate the inputs that already okay into the reference count.
    // if the reference count hits zero, that means all inputs are satisfied and the task can be spawned.
    if (t.add_ref_count(-num_inputs_already_ok) == 0)
    {
        tbb::task::spawn(t);
    }
}

class TaskA : public tbb::task
{
    class A_Subtask1 : public tbb::task
    {
        cont<int>* c;

    public:
        A_Subtask1(cont<int>* c)
        {
            this->c = c;
        }

        tbb::task* execute() override
        {
            std::cout << "A_Subtask1\n";

            // set the value of c
            c->emplace(1337);

            // broadcast that c is ready to all the successors enqueued on the cont.
            c->set_ready();

            return NULL;
        }
    };

    class A_Subtask2 : public tbb::task
    {
    public:
        tbb::task* execute() override
        {
            std::cout << "A_Subtask2\n";

            return NULL;
        }
    };

    cont<int>* c;

public:
    TaskA(cont<int>* c)
    {
        this->c = c;
    }

    tbb::task* execute() override
    {
        std::cout << "A\n";

        // 2 children + wait
        set_ref_count(3);

        spawn(*new(allocate_child()) A_Subtask1(c));
        spawn_and_wait_for_all(*new(allocate_child()) A_Subtask2());

        return NULL;
    }
};

class TaskB : public tbb::task
{
public:
    tbb::task* execute() override
    {
        std::cout << "B\n";

        return NULL;
    }
};

class TaskC : public tbb::task
{
    cont<int>* c;

public:
    cont_node cnodes[1];

    TaskC(cont<int>* c)
    {
        this->c = c;
    }

    tbb::task* execute() override
    {
        std::cout << "C\n";

        std::stringstream ss;
        ss << "C received " << **c << "\n";
        std::cout << ss.rdbuf();

        return NULL;
    }
};

class MainTask : public tbb::task
{
public:
    tbb::task* execute() override
    {
        // this variable will be produced by a subtask of task A, and consumed by task C.
        cont<int> c;

        // 3 children + wait
        set_ref_count(4);

        spawn(*new(allocate_child()) TaskA(&c));
        spawn(*new(allocate_child()) TaskB());

        // run TaskC when all the conts are satisfied (cont "c" gets set inside TaskA)
        TaskC& taskC = *new(allocate_child()) TaskC(&c);
        cont_base* conts[] = { &c };
        spawn_when_ready(taskC, conts, taskC.cnodes, _countof(conts));

        wait_for_all();

        return NULL;
    }
};

int main()
{
    tbb::task::spawn_root_and_wait(*new(tbb::task::allocate_root()) MainTask());
}
<|endoftext|>