<commit_before>/**
 * Copyright (c) 2020, Timothy Stack
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * * Neither the name of Timothy Stack nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @file date_time_scanner.cc
 */

#include "date_time_scanner.hh"

#include "config.h"
#include "ptimec.hh"

size_t
date_time_scanner::ftime(char* dst,
                         size_t len,
                         const char* const time_fmt[],
                         const exttm& tm) const
{
    off_t off = 0;

    if (time_fmt == nullptr) {
        PTIMEC_FORMATS[this->dts_fmt_lock].pf_ffunc(dst, off, len, tm);
        if (tm.et_flags & ETF_MILLIS_SET) {
            dst[off++] = '.';
            ftime_L(dst, off, len, tm);
        } else if (tm.et_flags & ETF_MICROS_SET) {
            dst[off++] = '.';
            ftime_f(dst, off, len, tm);
        } else if (tm.et_flags & ETF_NANOS_SET) {
            dst[off++] = '.';
            ftime_N(dst, off, len, tm);
        }
        dst[off] = '\0';
    } else {
        off = ftime_fmt(dst, len, time_fmt[this->dts_fmt_lock], tm);
    }

    return (size_t) off;
}

bool
next_format(const char* const fmt[], int& index, int& locked_index)
{
    bool retval = true;

    if (locked_index == -1) {
        index += 1;
        if (fmt[index] == nullptr) {
            retval = false;
        }
    } else if (index == locked_index) {
        retval = false;
    } else {
        index = locked_index;
    }

    return retval;
}

const char*
date_time_scanner::scan(const char* time_dest,
                        size_t time_len,
                        const char* const time_fmt[],
                        struct exttm* tm_out,
                        struct timeval& tv_out,
                        bool convert_local)
{
    int curr_time_fmt = -1;
    bool found = false;
    const char* retval = nullptr;

    if (!time_fmt) {
        time_fmt = PTIMEC_FORMAT_STR;
    }

    while (next_format(time_fmt, curr_time_fmt, this->dts_fmt_lock)) {
        *tm_out = this->dts_base_tm;
        tm_out->et_flags = 0;
        if (time_len > 1 && time_dest[0] == '+' && isdigit(time_dest[1])) {
            char time_cp[time_len + 1];
            int gmt_int, off;

            retval = nullptr;
            memcpy(time_cp, time_dest, time_len);
            time_cp[time_len] = '\0';
            if (sscanf(time_cp, "+%d%n", &gmt_int, &off) == 1) {
                time_t gmt = gmt_int;

                if (convert_local && this->dts_local_time) {
                    localtime_r(&gmt, &tm_out->et_tm);
#ifdef HAVE_STRUCT_TM_TM_ZONE
                    tm_out->et_tm.tm_zone = nullptr;
#endif
                    tm_out->et_tm.tm_isdst = 0;
                    gmt = tm2sec(&tm_out->et_tm);
                }
                tv_out.tv_sec = gmt;
                tv_out.tv_usec = 0;
                tm_out->et_flags = ETF_DAY_SET | ETF_MONTH_SET | ETF_YEAR_SET
                    | ETF_MACHINE_ORIENTED | ETF_EPOCH_TIME;

                this->dts_fmt_lock = curr_time_fmt;
                this->dts_fmt_len = off;
                retval = time_dest + off;
                found = true;
                break;
            }
        } else if (time_fmt == PTIMEC_FORMAT_STR) {
            ptime_func func = PTIMEC_FORMATS[curr_time_fmt].pf_func;
            off_t off = 0;

#ifdef HAVE_STRUCT_TM_TM_ZONE
            if (!this->dts_keep_base_tz) {
                tm_out->et_tm.tm_zone = nullptr;
            }
#endif
            if (func(tm_out, time_dest, off, time_len)) {
                retval = &time_dest[off];

                if (tm_out->et_tm.tm_year < 70) {
                    tm_out->et_tm.tm_year = 80;
                }
                if (convert_local
                    && (this->dts_local_time
                        || tm_out->et_flags & ETF_EPOCH_TIME))
                {
                    time_t gmt = tm2sec(&tm_out->et_tm);

                    this->to_localtime(gmt, *tm_out);
                }
                tv_out.tv_sec = tm2sec(&tm_out->et_tm);
                tv_out.tv_usec = tm_out->et_nsec / 1000;
                secs2wday(tv_out, &tm_out->et_tm);

                this->dts_fmt_lock = curr_time_fmt;
                this->dts_fmt_len = retval - time_dest;

                found = true;
                break;
            }
        } else {
            off_t off = 0;

#ifdef HAVE_STRUCT_TM_TM_ZONE
            if (!this->dts_keep_base_tz) {
                tm_out->et_tm.tm_zone = nullptr;
            }
#endif
            if (ptime_fmt(
                    time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len)
                && (time_dest[off] == '.' || time_dest[off] == ','
                    || off == (off_t) time_len))
            {
                retval = &time_dest[off];
                if (tm_out->et_tm.tm_year < 70) {
                    tm_out->et_tm.tm_year = 80;
                }
                if (convert_local
                    && (this->dts_local_time
                        || tm_out->et_flags & ETF_EPOCH_TIME))
                {
                    time_t gmt = tm2sec(&tm_out->et_tm);

                    this->to_localtime(gmt, *tm_out);
#ifdef HAVE_STRUCT_TM_TM_ZONE
                    tm_out->et_tm.tm_zone = nullptr;
#endif
                    tm_out->et_tm.tm_isdst = 0;
                }

                tv_out.tv_sec = tm2sec(&tm_out->et_tm);
                tv_out.tv_usec = tm_out->et_nsec / 1000;
                secs2wday(tv_out, &tm_out->et_tm);

                this->dts_fmt_lock = curr_time_fmt;
                this->dts_fmt_len = retval - time_dest;

                found = true;
                break;
            }
        }
    }

    if (!found) {
        retval = nullptr;
    }

    if (retval != nullptr) {
        /* Try to pull out the milli/micro-second value. */
        if (retval[0] == '.' || retval[0] == ',') {
            off_t off = (retval - time_dest) + 1;

            if (ptime_N(tm_out, time_dest, off, time_len)) {
                tv_out.tv_usec
                    = std::chrono::duration_cast<std::chrono::microseconds>(
                          std::chrono::nanoseconds{tm_out->et_nsec})
                          .count();
                this->dts_fmt_len += 10;
                tm_out->et_flags |= ETF_NANOS_SET;
                retval += 10;
            } else if (ptime_f(tm_out, time_dest, off, time_len)) {
                tv_out.tv_usec
                    = std::chrono::duration_cast<std::chrono::microseconds>(
                          std::chrono::nanoseconds{tm_out->et_nsec})
                          .count();
                this->dts_fmt_len += 7;
                tm_out->et_flags |= ETF_MICROS_SET;
                retval += 7;
            } else if (ptime_L(tm_out, time_dest, off, time_len)) {
                tv_out.tv_usec
                    = std::chrono::duration_cast<std::chrono::microseconds>(
                          std::chrono::nanoseconds{tm_out->et_nsec})
                          .count();
                this->dts_fmt_len += 4;
                tm_out->et_flags |= ETF_MILLIS_SET;
                retval += 4;
            }
        }
    }

    return retval;
}

void
date_time_scanner::to_localtime(time_t t, exttm& tm_out)
{
    if (t < (24 * 60 * 60)) {
        // Don't convert and risk going past the epoch.
        return;
    }

    if (t < this->dts_local_offset_valid || t >= this->dts_local_offset_expiry)
    {
        time_t new_gmt;

        localtime_r(&t, &tm_out.et_tm);
#ifdef HAVE_STRUCT_TM_TM_ZONE
        tm_out.et_tm.tm_zone = nullptr;
#endif
        tm_out.et_tm.tm_isdst = 0;

        new_gmt = tm2sec(&tm_out.et_tm);
        this->dts_local_offset_cache = t - new_gmt;
        this->dts_local_offset_valid = t;
        this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1);
        this->dts_local_offset_expiry
            -= this->dts_local_offset_expiry % EXPIRE_TIME;
    } else {
        time_t adjust_gmt = t - this->dts_local_offset_cache;
        gmtime_r(&adjust_gmt, &tm_out.et_tm);
    }
}
<commit_msg>[build] missing include<commit_after>/**
 * Copyright (c) 2020, Timothy Stack
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * * Neither the name of Timothy Stack nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @file date_time_scanner.cc
 */

#include <chrono>

#include "date_time_scanner.hh"

#include "config.h"
#include "ptimec.hh"

size_t
date_time_scanner::ftime(char* dst,
                         size_t len,
                         const char* const time_fmt[],
                         const exttm& tm) const
{
    off_t off = 0;

    if (time_fmt == nullptr) {
        PTIMEC_FORMATS[this->dts_fmt_lock].pf_ffunc(dst, off, len, tm);
        if (tm.et_flags & ETF_MILLIS_SET) {
            dst[off++] = '.';
            ftime_L(dst, off, len, tm);
        } else if (tm.et_flags & ETF_MICROS_SET) {
            dst[off++] = '.';
            ftime_f(dst, off, len, tm);
        } else if (tm.et_flags & ETF_NANOS_SET) {
            dst[off++] = '.';
            ftime_N(dst, off, len, tm);
        }
        dst[off] = '\0';
    } else {
        off = ftime_fmt(dst, len, time_fmt[this->dts_fmt_lock], tm);
    }

    return (size_t) off;
}

bool
next_format(const char* const fmt[], int& index, int& locked_index)
{
    bool retval = true;

    if (locked_index == -1) {
        index += 1;
        if (fmt[index] == nullptr) {
            retval = false;
        }
    } else if (index == locked_index) {
        retval = false;
    } else {
        index = locked_index;
    }

    return retval;
}

const char*
date_time_scanner::scan(const char* time_dest,
                        size_t time_len,
                        const char* const time_fmt[],
                        struct exttm* tm_out,
                        struct timeval& tv_out,
                        bool convert_local)
{
    int curr_time_fmt = -1;
    bool found = false;
    const char* retval = nullptr;

    if (!time_fmt) {
        time_fmt = PTIMEC_FORMAT_STR;
    }

    while (next_format(time_fmt, curr_time_fmt, this->dts_fmt_lock)) {
        *tm_out = this->dts_base_tm;
        tm_out->et_flags = 0;
        if (time_len > 1 && time_dest[0] == '+' && isdigit(time_dest[1])) {
            char time_cp[time_len + 1];
            int gmt_int, off;

            retval = nullptr;
            memcpy(time_cp, time_dest, time_len);
            time_cp[time_len] = '\0';
            if (sscanf(time_cp, "+%d%n", &gmt_int, &off) == 1) {
                time_t gmt = gmt_int;

                if (convert_local && this->dts_local_time) {
                    localtime_r(&gmt, &tm_out->et_tm);
#ifdef HAVE_STRUCT_TM_TM_ZONE
                    tm_out->et_tm.tm_zone = nullptr;
#endif
                    tm_out->et_tm.tm_isdst = 0;
                    gmt = tm2sec(&tm_out->et_tm);
                }
                tv_out.tv_sec = gmt;
                tv_out.tv_usec = 0;
                tm_out->et_flags = ETF_DAY_SET | ETF_MONTH_SET | ETF_YEAR_SET
                    | ETF_MACHINE_ORIENTED | ETF_EPOCH_TIME;

                this->dts_fmt_lock = curr_time_fmt;
                this->dts_fmt_len = off;
                retval = time_dest + off;
                found = true;
                break;
            }
        } else if (time_fmt == PTIMEC_FORMAT_STR) {
            ptime_func func = PTIMEC_FORMATS[curr_time_fmt].pf_func;
            off_t off = 0;

#ifdef HAVE_STRUCT_TM_TM_ZONE
            if (!this->dts_keep_base_tz) {
                tm_out->et_tm.tm_zone = nullptr;
            }
#endif
            if (func(tm_out, time_dest, off, time_len)) {
                retval = &time_dest[off];

                if (tm_out->et_tm.tm_year < 70) {
                    tm_out->et_tm.tm_year = 80;
                }
                if (convert_local
                    && (this->dts_local_time
                        || tm_out->et_flags & ETF_EPOCH_TIME))
                {
                    time_t gmt = tm2sec(&tm_out->et_tm);

                    this->to_localtime(gmt, *tm_out);
                }
                tv_out.tv_sec = tm2sec(&tm_out->et_tm);
                tv_out.tv_usec = tm_out->et_nsec / 1000;
                secs2wday(tv_out, &tm_out->et_tm);

                this->dts_fmt_lock = curr_time_fmt;
                this->dts_fmt_len = retval - time_dest;

                found = true;
                break;
            }
        } else {
            off_t off = 0;

#ifdef HAVE_STRUCT_TM_TM_ZONE
            if (!this->dts_keep_base_tz) {
                tm_out->et_tm.tm_zone = nullptr;
            }
#endif
            if (ptime_fmt(
                    time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len)
                && (time_dest[off] == '.' || time_dest[off] == ','
                    || off == (off_t) time_len))
            {
                retval = &time_dest[off];
                if (tm_out->et_tm.tm_year < 70) {
                    tm_out->et_tm.tm_year = 80;
                }
                if (convert_local
                    && (this->dts_local_time
                        || tm_out->et_flags & ETF_EPOCH_TIME))
                {
                    time_t gmt = tm2sec(&tm_out->et_tm);

                    this->to_localtime(gmt, *tm_out);
#ifdef HAVE_STRUCT_TM_TM_ZONE
                    tm_out->et_tm.tm_zone = nullptr;
#endif
                    tm_out->et_tm.tm_isdst = 0;
                }

                tv_out.tv_sec = tm2sec(&tm_out->et_tm);
                tv_out.tv_usec = tm_out->et_nsec / 1000;
                secs2wday(tv_out, &tm_out->et_tm);

                this->dts_fmt_lock = curr_time_fmt;
                this->dts_fmt_len = retval - time_dest;

                found = true;
                break;
            }
        }
    }

    if (!found) {
        retval = nullptr;
    }

    if (retval != nullptr) {
        /* Try to pull out the milli/micro-second value. */
        if (retval[0] == '.' || retval[0] == ',') {
            off_t off = (retval - time_dest) + 1;

            if (ptime_N(tm_out, time_dest, off, time_len)) {
                tv_out.tv_usec
                    = std::chrono::duration_cast<std::chrono::microseconds>(
                          std::chrono::nanoseconds{tm_out->et_nsec})
                          .count();
                this->dts_fmt_len += 10;
                tm_out->et_flags |= ETF_NANOS_SET;
                retval += 10;
            } else if (ptime_f(tm_out, time_dest, off, time_len)) {
                tv_out.tv_usec
                    = std::chrono::duration_cast<std::chrono::microseconds>(
                          std::chrono::nanoseconds{tm_out->et_nsec})
                          .count();
                this->dts_fmt_len += 7;
                tm_out->et_flags |= ETF_MICROS_SET;
                retval += 7;
            } else if (ptime_L(tm_out, time_dest, off, time_len)) {
                tv_out.tv_usec
                    = std::chrono::duration_cast<std::chrono::microseconds>(
                          std::chrono::nanoseconds{tm_out->et_nsec})
                          .count();
                this->dts_fmt_len += 4;
                tm_out->et_flags |= ETF_MILLIS_SET;
                retval += 4;
            }
        }
    }

    return retval;
}

void
date_time_scanner::to_localtime(time_t t, exttm& tm_out)
{
    if (t < (24 * 60 * 60)) {
        // Don't convert and risk going past the epoch.
        return;
    }

    if (t < this->dts_local_offset_valid || t >= this->dts_local_offset_expiry)
    {
        time_t new_gmt;

        localtime_r(&t, &tm_out.et_tm);
#ifdef HAVE_STRUCT_TM_TM_ZONE
        tm_out.et_tm.tm_zone = nullptr;
#endif
        tm_out.et_tm.tm_isdst = 0;

        new_gmt = tm2sec(&tm_out.et_tm);
        this->dts_local_offset_cache = t - new_gmt;
        this->dts_local_offset_valid = t;
        this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1);
        this->dts_local_offset_expiry
            -= this->dts_local_offset_expiry % EXPIRE_TIME;
    } else {
        time_t adjust_gmt = t - this->dts_local_offset_cache;
        gmtime_r(&adjust_gmt, &tm_out.et_tm);
    }
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////
//
// DAGON - An Adventure Game Engine
// Copyright (c) 2011-2014 Senscape s.r.l.
// All rights reserved.
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was
// not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
//
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////

#include "Config.h"
#include "Control.h"
#include "Log.h"
#include "System.h"

namespace dagon {

////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////

void System::browse(const char* url) {
  // TODO: Re-implement
  log.warning(kModSystem, "Browsing is currently disabled");
}

#ifdef DAGON_MAC
namespace {
#include <CoreFoundation/CoreFoundation.h>
#include <unistd.h>
}
void System::findPaths() {
  CFURLRef mainRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
  CFURLRef parentRef = CFURLCreateCopyDeletingLastPathComponent(NULL, mainRef);
  CFStringRef macPath = CFURLCopyFileSystemPath(parentRef, kCFURLPOSIXPathStyle);

  const char *pathPtr = CFStringGetCStringPtr(macPath,
                                              CFStringGetSystemEncoding());
  chdir(pathPtr);
}
#else
void System::findPaths() {
  // Nothing to do for now
}
#endif

bool System::init() {
  log.trace(kModSystem, "%s", kString13001);
  SDL_version version;
  SDL_GetVersion(&version);
  log.info(kModSystem, "%s: %d.%d", kString13003,
           version.major, version.minor);

  SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO | SDL_INIT_TIMER);
  Uint32 videoFlags = SDL_WINDOW_OPENGL;

  if (!config.displayWidth || !config.displayHeight) {
    if (config.fullscreen) {
      SDL_DisplayMode displayMode;
      SDL_GetCurrentDisplayMode(0, &displayMode);
      config.displayWidth = displayMode.w;
      config.displayHeight = displayMode.h;
      videoFlags = videoFlags | SDL_WINDOW_FULLSCREEN;
    } else {
      // Use a third of the screen
      SDL_DisplayMode desktopMode;
      SDL_GetDesktopDisplayMode(0, &desktopMode);
      config.displayWidth = static_cast<int>(desktopMode.w / 1.5f);
      config.displayHeight = static_cast<int>(desktopMode.h / 1.5f);
      videoFlags = videoFlags | SDL_WINDOW_RESIZABLE;
    }
  } else {
    if (config.fullscreen) {
      videoFlags = videoFlags | SDL_WINDOW_FULLSCREEN;
    } else {
      videoFlags = videoFlags | SDL_WINDOW_RESIZABLE;
    }
  }

  _window = SDL_CreateWindow("Dagon", SDL_WINDOWPOS_CENTERED,
                             SDL_WINDOWPOS_CENTERED,
                             config.displayWidth, config.displayHeight,
                             videoFlags | SDL_WINDOW_ALLOW_HIGHDPI);
  _context = SDL_GL_CreateContext(_window);

  if (!_window) {
    log.error(kModSystem, "%s", kString13010);
    return false;
  }

  // Set vertical sync according to our configuration
  SDL_GL_SetSwapInterval(config.verticalSync);

  // Force our own frame limiter on if vertical sync wasn't enabled
  if (!SDL_GL_GetSwapInterval())
    config.frameLimiter = true;

  SDL_WarpMouseInWindow(_window, config.displayWidth >> 1,
                        config.displayHeight >> 1);
  SDL_ShowCursor(false);

  return true;
}

void System::setTitle(const char* title) {
  SDL_SetWindowTitle(_window, title);
}

void System::update() {
  config.setFramesPerSecond(_calculateFrames(kFrameratePrecision));
  SDL_GL_SwapWindow(_window);
  SDL_Event event;
  while(SDL_PollEvent(&event)) {
    switch (event.type) {
      case SDL_KEYDOWN: {
        switch (event.key.keysym.sym) {
          case SDLK_F1:
          case SDLK_F2:
          case SDLK_F3:
          case SDLK_F4:
          case SDLK_F5:
          case SDLK_F6:
          case SDLK_F7:
          case SDLK_F8:
          case SDLK_F9:
          case SDLK_F10:
          case SDLK_F11:
          case SDLK_F12:
            Control::instance().processFunctionKey(event.key.keysym.sym);
            break;
          case SDLK_BACKSPACE:
          case SDLK_ESCAPE:
          case SDLK_TAB:
          case SDLK_RETURN:
            Control::instance().processKey(event.key.keysym.sym,
                                           EventKeyDown);
            break;
          case SDLK_f:
            SDL_Keymod modified = SDL_GetModState();
            if (modified & (KMOD_LALT | KMOD_LCTRL | KMOD_LGUI))
              this->toggleFullscreen();
            break;
        }
        break;
      }
        /*case SDL_KEYUP: {
         switch (event.key.keysym.sym) {
         case SDLK_w:
         case SDLK_a:
         case SDLK_s:
         case SDLK_d:
         Control::instance().processKey(event.key.keysym.sym,
         EventKeyUp);
         break;
         }
         }*/
      case SDL_MOUSEMOTION:
        Control::instance().processMouse(event.motion.x,
                                         event.motion.y,
                                         EventMouseMove);
        break;
      case SDL_MOUSEBUTTONDOWN:
        if (event.button.button == SDL_BUTTON_LEFT) {
          Control::instance().processMouse(event.button.x,
                                           event.button.y,
                                           EventMouseDown);
        } else if (event.button.button == SDL_BUTTON_RIGHT) {
          Control::instance().processMouse(event.button.x,
                                           event.button.y,
                                           EventMouseRightDown);
        }
        break;
      case SDL_MOUSEBUTTONUP:
        if (event.button.button == SDL_BUTTON_LEFT) {
          Control::instance().processMouse(event.button.x,
                                           event.button.y,
                                           EventMouseUp);
        } else if (event.button.button == SDL_BUTTON_RIGHT) {
          Control::instance().processMouse(event.button.x,
                                           event.button.y,
                                           EventMouseRightUp);
        }
        break;
      case SDL_TEXTINPUT:
        Control::instance().processKey(event.text.text[0],
                                       EventKeyDown);
        break;
      case SDL_WINDOWEVENT:
        switch (event.window.event) {
          case SDL_WINDOWEVENT_CLOSE:
            Control::instance().terminate();
            break;
          case SDL_WINDOWEVENT_LEAVE:
            Control::instance().processMouse(config.displayWidth >> 1,
                                             config.displayHeight >> 1,
                                             EventMouseMove);
            break;
          case SDL_WINDOWEVENT_RESIZED:
            Control::instance().reshape(event.window.data1,
                                        event.window.data2);
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }
  if (config.controlMode == kControlFixed) {
    if (Control::instance().isDirectControlActive()) {
      double currentTime = SDL_GetTicks();
      static double lastTime = currentTime;
      if ((currentTime - lastTime) >= 100) {
        SDL_WarpMouseInWindow(_window, config.displayWidth >> 1,
                              config.displayHeight >> 1);
        lastTime = SDL_GetTicks();
      }
    }
  }
}

void System::terminate() {
  SDL_GL_DeleteContext(_context);
  if (config.fullscreen)
    SDL_SetWindowFullscreen(_window, 0);
  SDL_DestroyWindow(_window);
  SDL_Quit();
}

void System::toggleFullscreen() {
  config.fullscreen = !config.fullscreen;
  if (config.fullscreen) {
    SDL_DisplayMode desktopMode;
    SDL_GetDesktopDisplayMode(0, &desktopMode);
    SDL_SetWindowPosition(_window, 0, 0);
    SDL_SetWindowSize(_window, desktopMode.w, desktopMode.h);
    SDL_SetWindowFullscreen(_window, SDL_WINDOW_FULLSCREEN);
    SDL_ShowCursor(false);
  } else {
    SDL_SetWindowFullscreen(_window, 0);
  }
}

////////////////////////////////////////////////////////////
// Implementation - Private methods
////////////////////////////////////////////////////////////

// TODO: For best precision, this should be suspended when
// performing a switch (the most expensive operation)
double System::_calculateFrames(double theInterval = 1.0) {
  static double lastTime = SDL_GetTicks() / 1000;
  static int fpsFrameCount = 0;
  static double fps = 0.0;

  double currentTime = SDL_GetTicks() / 1000;

  if (theInterval < 0.1)
    theInterval = 0.1;

  if (theInterval > 10.0)
    theInterval = 10.0;

  if ((currentTime - lastTime) > theInterval) {
    fps = (double)fpsFrameCount / (currentTime - lastTime);

    fpsFrameCount = 0;
    lastTime = SDL_GetTicks() / 1000;
  }
  else fpsFrameCount++;

  return fps;
}

}
<commit_msg>Rewrite window code to better handle high dpi (#170)<commit_after>////////////////////////////////////////////////////////////
//
// DAGON - An Adventure Game Engine
// Copyright (c) 2011-2014 Senscape s.r.l.
// All rights reserved.
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was
// not distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.
//
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////

#include "Config.h"
#include "Control.h"
#include "Log.h"
#include "System.h"

namespace dagon {

////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////

void System::browse(const char* url) {
  // TODO: Re-implement
  log.warning(kModSystem, "Browsing is currently disabled");
}

#ifdef DAGON_MAC
namespace {
#include <CoreFoundation/CoreFoundation.h>
#include <unistd.h>
}
void System::findPaths() {
  CFURLRef mainRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
  CFURLRef parentRef = CFURLCreateCopyDeletingLastPathComponent(NULL, mainRef);
  CFStringRef macPath = CFURLCopyFileSystemPath(parentRef, kCFURLPOSIXPathStyle);

  const char *pathPtr = CFStringGetCStringPtr(macPath,
                                              CFStringGetSystemEncoding());
  chdir(pathPtr);
}
#else
void System::findPaths() {
  // Nothing to do for now
}
#endif

bool System::init() {
  log.trace(kModSystem, "%s", kString13001);
  SDL_version version;
  SDL_GetVersion(&version);
  log.info(kModSystem, "%s: %d.%d", kString13003,
           version.major, version.minor);

  SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO | SDL_INIT_TIMER);
  Uint32 videoFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
  bool forcedRes = true;

  if (!config.displayWidth || !config.displayHeight) {
    // Use a third of the screen
    SDL_DisplayMode desktopMode;
    SDL_GetDesktopDisplayMode(0, &desktopMode);
    config.displayWidth = static_cast<int>(desktopMode.w / 1.5f);
    config.displayHeight = static_cast<int>(desktopMode.h / 1.5f);
    forcedRes = false;
  }

  _window = SDL_CreateWindow("Dagon", SDL_WINDOWPOS_CENTERED,
                             SDL_WINDOWPOS_CENTERED,
                             config.displayWidth, config.displayHeight,
                             videoFlags);
  _context = SDL_GL_CreateContext(_window);

  if (!_window) {
    log.error(kModSystem, "%s", kString13010);
    return false;
  }

  if (config.fullscreen) {
    int width = config.displayWidth;
    int height = config.displayHeight;

    if (!forcedRes) {
      SDL_DisplayMode desktopMode;
      SDL_GetDesktopDisplayMode(0, &desktopMode);
      width = desktopMode.w;
      height = desktopMode.h;
    }

    SDL_SetWindowPosition(_window, 0, 0);
    SDL_SetWindowSize(_window, width, height);
    SDL_SetWindowFullscreen(_window, SDL_WINDOW_FULLSCREEN);
  }

  // Set vertical sync according to our configuration
  SDL_GL_SetSwapInterval(config.verticalSync);

  // Force our own frame limiter on if vertical sync wasn't enabled
  if (!SDL_GL_GetSwapInterval())
    config.frameLimiter = true;

  SDL_WarpMouseInWindow(_window, config.displayWidth >> 1,
                        config.displayHeight >> 1);
  SDL_ShowCursor(false);

  return true;
}

void System::setTitle(const char* title) {
  SDL_SetWindowTitle(_window, title);
}

void System::update() {
  config.setFramesPerSecond(_calculateFrames(kFrameratePrecision));
  SDL_GL_SwapWindow(_window);
  SDL_Event event;
  while(SDL_PollEvent(&event)) {
    switch (event.type) {
      case SDL_KEYDOWN: {
        switch (event.key.keysym.sym) {
          case SDLK_F1:
          case SDLK_F2:
          case SDLK_F3:
          case SDLK_F4:
          case SDLK_F5:
          case SDLK_F6:
          case SDLK_F7:
          case SDLK_F8:
          case SDLK_F9:
          case SDLK_F10:
          case SDLK_F11:
          case SDLK_F12:
            Control::instance().processFunctionKey(event.key.keysym.sym);
            break;
          case SDLK_BACKSPACE:
          case SDLK_ESCAPE:
          case SDLK_TAB:
          case SDLK_RETURN:
            Control::instance().processKey(event.key.keysym.sym,
                                           EventKeyDown);
            break;
          case SDLK_f:
            SDL_Keymod modified = SDL_GetModState();
            if (modified & (KMOD_LALT | KMOD_LCTRL | KMOD_LGUI))
              this->toggleFullscreen();
            break;
        }
        break;
      }
        /*case SDL_KEYUP: {
         switch (event.key.keysym.sym) {
         case SDLK_w:
         case SDLK_a:
         case SDLK_s:
         case SDLK_d:
         Control::instance().processKey(event.key.keysym.sym,
         EventKeyUp);
         break;
         }
         }*/
      case SDL_MOUSEMOTION:
        Control::instance().processMouse(event.motion.x,
                                         event.motion.y,
                                         EventMouseMove);
        break;
      case SDL_MOUSEBUTTONDOWN:
        if (event.button.button == SDL_BUTTON_LEFT) {
          Control::instance().processMouse(event.button.x,
                                           event.button.y,
                                           EventMouseDown);
        } else if (event.button.button == SDL_BUTTON_RIGHT) {
          Control::instance().processMouse(event.button.x,
                                           event.button.y,
                                           EventMouseRightDown);
        }
        break;
      case SDL_MOUSEBUTTONUP:
        if (event.button.button == SDL_BUTTON_LEFT) {
          Control::instance().processMouse(event.button.x,
                                           event.button.y,
                                           EventMouseUp);
        } else if (event.button.button == SDL_BUTTON_RIGHT) {
          Control::instance().processMouse(event.button.x,
                                           event.button.y,
                                           EventMouseRightUp);
        }
        break;
      case SDL_TEXTINPUT:
        Control::instance().processKey(event.text.text[0],
                                       EventKeyDown);
        break;
      case SDL_WINDOWEVENT:
        switch (event.window.event) {
          case SDL_WINDOWEVENT_CLOSE:
            Control::instance().terminate();
            break;
          case SDL_WINDOWEVENT_LEAVE:
            Control::instance().processMouse(config.displayWidth >> 1,
                                             config.displayHeight >> 1,
                                             EventMouseMove);
            break;
          case SDL_WINDOWEVENT_RESIZED:
            int w, h;
            SDL_GetWindowSize(_window, &w, &h);
            Control::instance().reshape(w, h);
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }
  if (config.controlMode == kControlFixed) {
    if (Control::instance().isDirectControlActive()) {
      double currentTime = SDL_GetTicks();
      static double lastTime = currentTime;
      if ((currentTime - lastTime) >= 100) {
        SDL_WarpMouseInWindow(_window, config.displayWidth >> 1,
                              config.displayHeight >> 1);
        lastTime = SDL_GetTicks();
      }
    }
  }
}

void System::terminate() {
  SDL_GL_DeleteContext(_context);
  if (config.fullscreen)
    SDL_SetWindowFullscreen(_window, 0);
  SDL_DestroyWindow(_window);
  SDL_Quit();
}

void System::toggleFullscreen() {
  config.fullscreen = !config.fullscreen;
  if (config.fullscreen) {
    SDL_SetWindowFullscreen(_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
  } else {
    SDL_SetWindowFullscreen(_window, 0);
    SDL_DisplayMode desktopMode;
    SDL_GetDesktopDisplayMode(0, &desktopMode);
    SDL_SetWindowPosition(_window, 10, 10);
    SDL_SetWindowSize(_window, desktopMode.w, desktopMode.h);
  }
}

////////////////////////////////////////////////////////////
// Implementation - Private methods
////////////////////////////////////////////////////////////

// TODO: For best precision, this should be suspended when
// performing a switch (the most expensive operation)
double System::_calculateFrames(double theInterval = 1.0) {
  static double lastTime = SDL_GetTicks() / 1000;
  static int fpsFrameCount = 0;
  static double fps = 0.0;

  double currentTime = SDL_GetTicks() / 1000;

  if (theInterval < 0.1)
    theInterval = 0.1;

  if (theInterval > 10.0)
    theInterval = 10.0;

  if ((currentTime - lastTime) > theInterval) {
    fps = (double)fpsFrameCount / (currentTime - lastTime);

    fpsFrameCount = 0;
    lastTime = SDL_GetTicks() / 1000;
  }
  else fpsFrameCount++;

  return fps;
}

}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>

#include "Target.h"
#include "Debug.h"
#include "Error.h"
#include "LLVM_Headers.h"
#include "Util.h"

namespace Halide {

using std::string;
using std::vector;

namespace {
#ifndef __arm__

#ifdef _MSC_VER
static void cpuid(int info[4], int infoType, int extra) {
    __cpuidex(info, infoType, extra);
}

#else
// CPU feature detection code taken from ispc
// (https://github.com/ispc/ispc/blob/master/builtins/dispatch.ll)

#ifdef _LP64
static void cpuid(int info[4], int infoType, int extra) {
    __asm__ __volatile__ (
        "cpuid                 \n\t"
        : "=a" (info[0]), "=b" (info[1]), "=c" (info[2]), "=d" (info[3])
        : "0" (infoType), "2" (extra));
}
#else
static void cpuid(int info[4], int infoType, int extra) {
    // We save %ebx in case it's the PIC register
    __asm__ __volatile__ (
        "mov{l}\t{%%}ebx, %1  \n\t"
        "cpuid                 \n\t"
        "xchg{l}\t{%%}ebx, %1  \n\t"
        : "=a" (info[0]), "=r" (info[1]), "=c" (info[2]), "=d" (info[3])
        : "0" (infoType), "2" (extra));
}
#endif
#endif
#endif
}

Target get_host_target() {
    Target::OS os = Target::OSUnknown;
    #ifdef __linux__
    os = Target::Linux;
    #endif
    #ifdef _MSC_VER
    os = Target::Windows;
    #endif
    #ifdef __APPLE__
    os = Target::OSX;
    #endif

    bool use_64_bits = (sizeof(size_t) == 8);
    int bits = use_64_bits ? 64 : 32;

    #if __mips__ || __mips || __MIPS__
    Target::Arch arch = Target::MIPS;
    return Target(os, arch, bits);
    #else
    #ifdef __arm__
    Target::Arch arch = Target::ARM;
    return Target(os, arch, bits);
    #else

    Target::Arch arch = Target::X86;

    int info[4];
    cpuid(info, 1, 0);
    bool have_sse41 = info[2] & (1 << 19);
    bool have_sse2 = info[3] & (1 << 26);
    bool have_avx = info[2] & (1 << 28);
    bool have_f16c = info[2] & (1 << 29);
    bool have_rdrand = info[2] & (1 << 30);
    bool have_fma = info[2] & (1 << 12);

    user_assert(have_sse2)
        << "The x86 backend assumes at least sse2 support. This machine does not appear to have sse2.\n"
        << "cpuid returned: "
        << std::hex << info[0]
        << ", " << info[1]
        << ", " << info[2]
        << ", " << info[3]
        << std::dec << "\n";

    std::vector<Target::Feature> initial_features;
    if (have_sse41) initial_features.push_back(Target::SSE41);
    if (have_avx)   initial_features.push_back(Target::AVX);
    if (have_f16c)  initial_features.push_back(Target::F16C);
    if (have_fma)   initial_features.push_back(Target::FMA);

    if (use_64_bits && have_avx && have_f16c && have_rdrand) {
        // So far, so good.  AVX2?
        // Call cpuid with eax=7, ecx=0
        int info2[4];
        cpuid(info2, 7, 0);
        bool have_avx2 = info[1] & (1 << 5);
        if (have_avx2) {
            initial_features.push_back(Target::AVX2);
        }
    }

    return Target(os, arch, bits, initial_features);
#endif
#endif
}

namespace {
string get_env(const char *name) {
#ifdef _WIN32
    char buf[128];
    size_t read = 0;
    getenv_s(&read, buf, name);
    if (read) {
        return string(buf);
    } else {
        return "";
    }
#else
    char *buf = getenv(name);
    if (buf) {
        return string(buf);
    } else {
        return "";
    }
#endif
}

const std::map<std::string, Target::OS> os_name_map = {
    {"os_unknown", Target::OSUnknown},
    {"linux", Target::Linux},
    {"windows", Target::Windows},
    {"osx", Target::OSX},
    {"android", Target::Android},
    {"ios", Target::IOS},
    {"nacl", Target::NaCl},
};

bool lookup_os(const std::string &tok, Target::OS &result) {
    auto os_iter = os_name_map.find(tok);
    if (os_iter != os_name_map.end()) {
        result = os_iter->second;
        return true;
    }
    return false;
}

const std::map<std::string, Target::Arch> arch_name_map = {
    {"arch_unknown", Target::ArchUnknown},
    {"x86", Target::X86},
    {"arm", Target::ARM},
    {"pnacl", Target::PNaCl},
    {"mips", Target::MIPS},
};

bool lookup_arch(const std::string &tok, Target::Arch &result) {
    auto arch_iter = arch_name_map.find(tok);
    if (arch_iter != arch_name_map.end()) {
        result = arch_iter->second;
        return true;
    }
    return false;
}

const std::map<std::string, Target::Feature> feature_name_map = {
    {"jit", Target::JIT},
    {"debug", Target::Debug},
    {"no_asserts", Target::NoAsserts},
    {"no_bounds_query", Target::NoBoundsQuery},
    {"sse41", Target::SSE41},
    {"avx", Target::AVX},
    {"avx2", Target::AVX2},
    {"fma", Target::FMA},
    {"fma4", Target::FMA4},
    {"f16c", Target::F16C},
    {"armv7s", Target::ARMv7s},
    {"no_neon", Target::NoNEON},
    {"cuda", Target::CUDA},
    {"cuda_capability_30", Target::CUDACapability30},
    {"cuda_capability_32", Target::CUDACapability32},
    {"cuda_capability_35", Target::CUDACapability35},
    {"cuda_capability_50", Target::CUDACapability50},
    {"opencl", Target::OpenCL},
    {"cl_doubles", Target::CLDoubles},
    {"opengl", Target::OpenGL},
    {"openglcompute", Target::OpenGLCompute},
    {"renderscript", Target::Renderscript},
    {"user_context", Target::UserContext},
    {"register_metadata", Target::RegisterMetadata},
    {"matlab", Target::Matlab},
    {"profile", Target::Profile},
    {"no_runtime", Target::NoRuntime},
    {"metal", Target::Metal},
};

bool lookup_feature(const std::string &tok, Target::Feature &result) {
    auto feature_iter = feature_name_map.find(tok);
    if (feature_iter != feature_name_map.end()) {
        result = feature_iter->second;
        return true;
    }
    return false;
}

} // End anonymous namespace

Target get_target_from_environment() {
    string target = get_env("HL_TARGET");
    if (target.empty()) {
        return get_host_target();
    } else {
        return parse_target_string(target);
    }
}

Target get_jit_target_from_environment() {
    Target host = get_host_target();
    host.set_feature(Target::JIT);
    string target = get_env("HL_JIT_TARGET");
    if (target.empty()) {
        return host;
    } else {
        Target t = parse_target_string(target);
        t.set_feature(Target::JIT);
        user_assert(t.os == host.os && t.arch == host.arch && t.bits == host.bits)
            << "HL_JIT_TARGET must match the host OS, architecture, and bit width.\n"
            << "HL_JIT_TARGET was " << target << ". "
            << "Host is " << host.to_string() << ".\n";
        return t;
    }
}

Target parse_target_string(const std::string &target) {
    Target host = get_host_target();

    if (target.empty()) {
        // If nothing is specified, use the host target.
        return host;
    }

    // Default to the host OS and architecture.
    Target t;
    t.os = host.os;
    t.arch = host.arch;
    t.bits = host.bits;

    if (!t.merge_string(target)) {
        const char *separator = "";
        std::string architectures;
        for (auto arch_iter = arch_name_map.begin(); arch_iter != arch_name_map.end(); arch_iter++) {
            architectures += separator + arch_iter->first;
            separator = ", ";
        }
        separator = "";
        std::string oses;
        for (auto os_iter = os_name_map.begin(); os_iter != os_name_map.end(); os_iter++) {
            oses += separator + os_iter->first;
            separator = ", ";
        }
        separator = "";
        // Format the features to go one feature over 70 characters per line,
        // assume the first line starts with "Features are ".
        int line_char_start = -(int)sizeof("Features are");
        std::string features;
        for (auto feature_iter = feature_name_map.begin(); feature_iter != feature_name_map.end(); feature_iter++) {
            features += separator + feature_iter->first;
            if (features.length() - line_char_start > 70) {
                separator = "\n";
                line_char_start = features.length();
            } else {
                separator = ", ";
            }
        }
        user_error << "Did not understand HL_TARGET=" << target << "\n"
                   << "Expected format is arch-os-feature1-feature2-...\n"
                   << "Where arch is " << architectures << " .\n"
                   << "Os is " << oses << " .\n"
                   << "If arch or os are omitted, they default to the host.\n"
                   << "Features are " << features << " .\n"
                   << "HL_TARGET can also begin with \"host\", which sets the "
                   << "host's architecture, os, and feature set, with the "
                   << "exception of the GPU runtimes, which default to off.\n"
                   << "On this platform, the host target is: " << host.to_string() << "\n";
    }

    return t;
}

bool Target::merge_string(const std::string &target) {
    string rest = target;
    vector<string> tokens;
    size_t first_dash;
    while ((first_dash = rest.find('-')) != string::npos) {
        //Internal::debug(0) << first_dash << ", " << rest << "\n";
        tokens.push_back(rest.substr(0, first_dash));
        rest = rest.substr(first_dash + 1);
    }
    tokens.push_back(rest);

    bool os_specified = false, arch_specified = false, bits_specified = false;

    for (size_t i = 0; i < tokens.size(); i++) {
        const string &tok = tokens[i];
        Target::Feature feature;

        if (tok == "host") {
            if (i > 0) {
                // "host" is now only allowed as the first token.
                return false;
            }
            *this = get_host_target();
        } else if (tok == "32" || tok == "64") {
            if (bits_specified) {
                return false;
            }
            bits_specified = true;
            bits = std::stoi(tok);
        } else if (lookup_arch(tok, arch)) {
            if (arch_specified) {
                return false;
            }
            arch_specified = true;
        } else if (lookup_os(tok, os)) {
            if (os_specified) {
                return false;
            }
            os_specified = true;
        } else if (lookup_feature(tok, feature)) {
            set_feature(feature);
        } else {
            return false;
        }
    }

    if (arch_specified && !bits_specified) {
        return false;
    }

    // If arch is PNaCl, require explicit setting of os and bits as well.
    if (arch_specified && arch == Target::PNaCl) {
        if (!os_specified || os != Target::NaCl) {
            return false;
        }
        if (!bits_specified || bits != 32) {
            return false;
        }
    }

    return true;
}

std::string Target::to_string() const {
    string result;
    for (auto const &arch_entry : arch_name_map) {
        if (arch_entry.second == arch) {
            result += arch_entry.first;
            break;
        }
    }
    result += "-" + std::to_string(bits);
    for (auto const &os_entry : os_name_map) {
        if (os_entry.second == os) {
            result += "-" + os_entry.first;
            break;
        }
    }
    for (auto const &feature_entry : feature_name_map) {
        if (has_feature(feature_entry.second)) {
            result += "-" + feature_entry.first;
        }
    }
    return result;
}

namespace Internal{

EXPORT void target_test() {
    Target t;
    for (auto const &feature : feature_name_map) {
        t.set_feature(feature.second);
    }
    for (int i = 0; i < (int)(Target::FeatureEnd); i++) {
        internal_assert(t.has_feature((Target::Feature)i)) << "Feature " << i << " not in feature_names_map.\n";
    }
    std::cout << "Target test passed" << std::endl;
}

}

}
<commit_msg>Switch to uniform use of range based for loops for iterating name maps.<commit_after>#include <iostream>
#include <string>

#include "Target.h"
#include "Debug.h"
#include "Error.h"
#include "LLVM_Headers.h"
#include "Util.h"

namespace Halide {

using std::string;
using std::vector;

namespace {
#ifndef __arm__

#ifdef _MSC_VER
static void cpuid(int info[4], int infoType, int extra) {
    __cpuidex(info, infoType, extra);
}

#else
// CPU feature detection code taken from ispc
// (https://github.com/ispc/ispc/blob/master/builtins/dispatch.ll)

#ifdef _LP64
static void cpuid(int info[4], int infoType, int extra) {
    __asm__ __volatile__ (
        "cpuid                 \n\t"
        : "=a" (info[0]), "=b" (info[1]), "=c" (info[2]), "=d" (info[3])
        : "0" (infoType), "2" (extra));
}
#else
static void cpuid(int info[4], int infoType, int extra) {
    // We save %ebx in case it's the PIC register
    __asm__ __volatile__ (
        "mov{l}\t{%%}ebx, %1  \n\t"
        "cpuid                 \n\t"
        "xchg{l}\t{%%}ebx, %1  \n\t"
        : "=a" (info[0]), "=r" (info[1]), "=c" (info[2]), "=d" (info[3])
        : "0" (infoType), "2" (extra));
}
#endif
#endif
#endif
}

Target get_host_target() {
    Target::OS os = Target::OSUnknown;
    #ifdef __linux__
    os = Target::Linux;
    #endif
    #ifdef _MSC_VER
    os = Target::Windows;
    #endif
    #ifdef __APPLE__
    os = Target::OSX;
    #endif

    bool use_64_bits = (sizeof(size_t) == 8);
    int bits = use_64_bits ? 64 : 32;

    #if __mips__ || __mips || __MIPS__
    Target::Arch arch = Target::MIPS;
    return Target(os, arch, bits);
    #else
    #ifdef __arm__
    Target::Arch arch = Target::ARM;
    return Target(os, arch, bits);
    #else

    Target::Arch arch = Target::X86;

    int info[4];
    cpuid(info, 1, 0);
    bool have_sse41 = info[2] & (1 << 19);
    bool have_sse2 = info[3] & (1 << 26);
    bool have_avx = info[2] & (1 << 28);
    bool have_f16c = info[2] & (1 << 29);
    bool have_rdrand = info[2] & (1 << 30);
    bool have_fma = info[2] & (1 << 12);

    user_assert(have_sse2)
        << "The x86 backend assumes at least sse2 support. This machine does not appear to have sse2.\n"
        << "cpuid returned: "
        << std::hex << info[0]
        << ", " << info[1]
        << ", " << info[2]
        << ", " << info[3]
        << std::dec << "\n";

    std::vector<Target::Feature> initial_features;
    if (have_sse41) initial_features.push_back(Target::SSE41);
    if (have_avx)   initial_features.push_back(Target::AVX);
    if (have_f16c)  initial_features.push_back(Target::F16C);
    if (have_fma)   initial_features.push_back(Target::FMA);

    if (use_64_bits && have_avx && have_f16c && have_rdrand) {
        // So far, so good.  AVX2?
        // Call cpuid with eax=7, ecx=0
        int info2[4];
        cpuid(info2, 7, 0);
        bool have_avx2 = info[1] & (1 << 5);
        if (have_avx2) {
            initial_features.push_back(Target::AVX2);
        }
    }

    return Target(os, arch, bits, initial_features);
#endif
#endif
}

namespace {
string get_env(const char *name) {
#ifdef _WIN32
    char buf[128];
    size_t read = 0;
    getenv_s(&read, buf, name);
    if (read) {
        return string(buf);
    } else {
        return "";
    }
#else
    char *buf = getenv(name);
    if (buf) {
        return string(buf);
    } else {
        return "";
    }
#endif
}

const std::map<std::string, Target::OS> os_name_map = {
    {"os_unknown", Target::OSUnknown},
    {"linux", Target::Linux},
    {"windows", Target::Windows},
    {"osx", Target::OSX},
    {"android", Target::Android},
    {"ios", Target::IOS},
    {"nacl", Target::NaCl},
};

bool lookup_os(const std::string &tok, Target::OS &result) {
    auto os_iter = os_name_map.find(tok);
    if (os_iter != os_name_map.end()) {
        result = os_iter->second;
        return true;
    }
    return false;
}

const std::map<std::string, Target::Arch> arch_name_map = {
    {"arch_unknown", Target::ArchUnknown},
    {"x86", Target::X86},
    {"arm", Target::ARM},
    {"pnacl", Target::PNaCl},
    {"mips", Target::MIPS},
};

bool lookup_arch(const std::string &tok, Target::Arch &result) {
    auto arch_iter = arch_name_map.find(tok);
    if (arch_iter != arch_name_map.end()) {
        result = arch_iter->second;
        return true;
    }
    return false;
}

const std::map<std::string, Target::Feature> feature_name_map = {
    {"jit", Target::JIT},
    {"debug", Target::Debug},
    {"no_asserts", Target::NoAsserts},
    {"no_bounds_query", Target::NoBoundsQuery},
    {"sse41", Target::SSE41},
    {"avx", Target::AVX},
    {"avx2", Target::AVX2},
    {"fma", Target::FMA},
    {"fma4", Target::FMA4},
    {"f16c", Target::F16C},
    {"armv7s", Target::ARMv7s},
    {"no_neon", Target::NoNEON},
    {"cuda", Target::CUDA},
    {"cuda_capability_30", Target::CUDACapability30},
    {"cuda_capability_32", Target::CUDACapability32},
    {"cuda_capability_35", Target::CUDACapability35},
    {"cuda_capability_50", Target::CUDACapability50},
    {"opencl", Target::OpenCL},
    {"cl_doubles", Target::CLDoubles},
    {"opengl", Target::OpenGL},
    {"openglcompute", Target::OpenGLCompute},
    {"renderscript", Target::Renderscript},
    {"user_context", Target::UserContext},
    {"register_metadata", Target::RegisterMetadata},
    {"matlab", Target::Matlab},
    {"profile", Target::Profile},
    {"no_runtime", Target::NoRuntime},
    {"metal", Target::Metal},
};

bool lookup_feature(const std::string &tok, Target::Feature &result) {
    auto feature_iter = feature_name_map.find(tok);
    if (feature_iter != feature_name_map.end()) {
        result = feature_iter->second;
        return true;
    }
    return false;
}

} // End anonymous namespace

Target get_target_from_environment() {
    string target = get_env("HL_TARGET");
    if (target.empty()) {
        return get_host_target();
    } else {
        return parse_target_string(target);
    }
}

Target get_jit_target_from_environment() {
    Target host = get_host_target();
    host.set_feature(Target::JIT);
    string target = get_env("HL_JIT_TARGET");
    if (target.empty()) {
        return host;
    } else {
        Target t = parse_target_string(target);
        t.set_feature(Target::JIT);
        user_assert(t.os == host.os && t.arch == host.arch && t.bits == host.bits)
            << "HL_JIT_TARGET must match the host OS, architecture, and bit width.\n"
            << "HL_JIT_TARGET was " << target << ". "
            << "Host is " << host.to_string() << ".\n";
        return t;
    }
}

Target parse_target_string(const std::string &target) {
    Target host = get_host_target();

    if (target.empty()) {
        // If nothing is specified, use the host target.
        return host;
    }

    // Default to the host OS and architecture.
    Target t;
    t.os = host.os;
    t.arch = host.arch;
    t.bits = host.bits;

    if (!t.merge_string(target)) {
        const char *separator = "";
        std::string architectures;
        for (auto const &arch_entry : arch_name_map) {
            architectures += separator + arch_entry.first;
            separator = ", ";
        }
        separator = "";
        std::string oses;
        for (auto os_entry : os_name_map) {
            oses += separator + os_entry.first;
            separator = ", ";
        }
        separator = "";
        // Format the features to go one feature over 70 characters per line,
        // assume the first line starts with "Features are ".
        int line_char_start = -(int)sizeof("Features are");
        std::string features;
        for (auto feature_entry : feature_name_map) {
            features += separator + feature_entry.first;
            if (features.length() - line_char_start > 70) {
                separator = "\n";
                line_char_start = features.length();
            } else {
                separator = ", ";
            }
        }
        user_error << "Did not understand HL_TARGET=" << target << "\n"
                   << "Expected format is arch-os-feature1-feature2-...\n"
                   << "Where arch is " << architectures << " .\n"
                   << "Os is " << oses << " .\n"
                   << "If arch or os are omitted, they default to the host.\n"
                   << "Features are " << features << " .\n"
                   << "HL_TARGET can also begin with \"host\", which sets the "
                   << "host's architecture, os, and feature set, with the "
                   << "exception of the GPU runtimes, which default to off.\n"
                   << "On this platform, the host target is: " << host.to_string() << "\n";
    }

    return t;
}

bool Target::merge_string(const std::string &target) {
    string rest = target;
    vector<string> tokens;
    size_t first_dash;
    while ((first_dash = rest.find('-')) != string::npos) {
        //Internal::debug(0) << first_dash << ", " << rest << "\n";
        tokens.push_back(rest.substr(0, first_dash));
        rest = rest.substr(first_dash + 1);
    }
    tokens.push_back(rest);

    bool os_specified = false, arch_specified = false, bits_specified = false;

    for (size_t i = 0; i < tokens.size(); i++) {
        const string &tok = tokens[i];
        Target::Feature feature;

        if (tok == "host") {
            if (i > 0) {
                // "host" is now only allowed as the first token.
                return false;
            }
            *this = get_host_target();
        } else if (tok == "32" || tok == "64") {
            if (bits_specified) {
                return false;
            }
            bits_specified = true;
            bits = std::stoi(tok);
        } else if (lookup_arch(tok, arch)) {
            if (arch_specified) {
                return false;
            }
            arch_specified = true;
        } else if (lookup_os(tok, os)) {
            if (os_specified) {
                return false;
            }
            os_specified = true;
        } else if (lookup_feature(tok, feature)) {
            set_feature(feature);
        } else {
            return false;
        }
    }

    if (arch_specified && !bits_specified) {
        return false;
    }

    // If arch is PNaCl, require explicit setting of os and bits as well.
    if (arch_specified && arch == Target::PNaCl) {
        if (!os_specified || os != Target::NaCl) {
            return false;
        }
        if (!bits_specified || bits != 32) {
            return false;
        }
    }

    return true;
}

std::string Target::to_string() const {
    string result;
    for (auto const &arch_entry : arch_name_map) {
        if (arch_entry.second == arch) {
            result += arch_entry.first;
            break;
        }
    }
    result += "-" + std::to_string(bits);
    for (auto const &os_entry : os_name_map) {
        if (os_entry.second == os) {
            result += "-" + os_entry.first;
            break;
        }
    }
    for (auto const &feature_entry : feature_name_map) {
        if (has_feature(feature_entry.second)) {
            result += "-" + feature_entry.first;
        }
    }
    return result;
}

namespace Internal{

EXPORT void target_test() {
    Target t;
    for (auto const &feature : feature_name_map) {
        t.set_feature(feature.second);
    }
    for (int i = 0; i < (int)(Target::FeatureEnd); i++) {
        internal_assert(t.has_feature((Target::Feature)i)) << "Feature " << i << " not in feature_names_map.\n";
    }
    std::cout << "Target test passed" << std::endl;
}

}

}
<|endoftext|>


<commit_before>
#include <libclientserver.h>

void Timers::Start()
{
	m_run = true;
	Thread::Start();
}

void Timers::Stop()
{
	if (m_timers.size() != 0)
		abort(); //We still have active timers
	m_run = false;
	m_mutex.WakeUp(); //Tell thread to wakeup and exit
	Thread::Stop();
}

void Timers::Add(ITimer *timer)
{
	ScopedLock lock(&m_mutex);

	struct timespec ts;
	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
		abort();

	struct timespec timeout;
	timer->GetDelay(&timeout);

	struct timespec expire;
	Time::Add(&ts, &timeout, &expire);

	unsigned long long curtime = Time::NanoSeconds(&expire);
	m_timers.insert( std::pair<unsigned long long, ITimer *>(curtime, timer) );
	m_mutex.WakeUp();
}

void Timers::Remove(ITimer *timer)
{
	ScopedLock lock(&m_mutex);
	std::multimap<unsigned long long, ITimer *>::iterator it = m_timers.begin();
	while(it != m_timers.end())
	{
		if (it->second == timer)
		{
			m_timers.erase(it);
			return;
		}
		it++;
	}
	m_mutex.WakeUp();
}

void Timers::Run()
{
	m_mutex.Lock(); //We really only unlock when we are asleep
	while(m_run == true)
	{
		struct timespec ts;
		if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
			abort();

		unsigned long long curtime = Time::NanoSeconds(&ts);

		std::multimap<unsigned long long, ITimer *>::iterator it = m_timers.begin();
		if (it == m_timers.end())
		{
			m_mutex.Wait(); //Wait forever on empty list until told otherwise
			continue; //Restart
		}

		if (curtime < it->first)
		{
			unsigned long long diff = it->first - curtime;
			struct timespec delay;
			Time::TimeSpecFromNanoSeconds(diff, &delay);
			m_mutex.Wait(&delay);
		}

		it = m_timers.begin();
		if (it == m_timers.end())
			continue; //All timers we removed while we were sleeping

		if (curtime >= it->first)
		{
			ITimer *timer = it->second;
			m_timers.erase(it);
			timer->TimerExpired(this, timer);
			continue;
		}
	}
	m_mutex.Unlock();
}

<commit_msg>Fixed a few bugs. Mutex not being locked during WakeUp and current time needed to be updated after sleeping<commit_after>
#include <libclientserver.h>

void Timers::Start()
{
	m_run = true;
	Thread::Start();
}

void Timers::Stop()
{
	if (m_timers.size() != 0)
		abort(); //We still have active timers
	m_run = false;
	m_mutex.Lock();
	m_mutex.WakeUp(); //Tell thread to wakeup and exit
	m_mutex.Unlock();
	Thread::Stop();
}

void Timers::Add(ITimer *timer)
{
	struct timespec ts;
	Time::MonoTonic(&ts);

	struct timespec timeout;
	timer->GetDelay(&timeout);

	struct timespec expire;
	Time::Add(&ts, &timeout, &expire);

	unsigned long long curtime = Time::NanoSeconds(&expire);
	ScopedLock lock(&m_mutex);
	m_timers.insert( std::pair<unsigned long long, ITimer *>(curtime, timer) );
	m_mutex.WakeUp();
}

void Timers::Remove(ITimer *timer)
{
	ScopedLock lock(&m_mutex);
	std::multimap<unsigned long long, ITimer *>::iterator it = m_timers.begin();
	while(it != m_timers.end())
	{
		if (it->second == timer)
		{
			m_timers.erase(it);
			return;
		}
		it++;
	}
	m_mutex.WakeUp();
}

void Timers::Run()
{
	m_mutex.Lock(); //We really only unlock when we are asleep
	while(m_run == true)
	{
		struct timespec ts;
		Time::MonoTonic(&ts);

		unsigned long long curtime = Time::NanoSeconds(&ts);

		std::multimap<unsigned long long, ITimer *>::iterator it = m_timers.begin();
		if (it == m_timers.end())
		{
			m_mutex.Wait(); //Wait forever on empty list until told otherwise
			continue; //Restart
		}

		if (curtime < it->first)
		{
			unsigned long long diff = it->first - curtime;
			struct timespec delay;
			Time::TimeSpecFromNanoSeconds(diff, &delay);
			m_mutex.Wait(&delay);
		}

		//We were asleep an unlocked. So thing things may have changed.
		it = m_timers.begin();
		if (it == m_timers.end())
			continue; //All timers we removed while we were sleeping

		//Get current time *again*
		Time::MonoTonic(&ts);
		curtime = Time::NanoSeconds(&ts);

		if (curtime >= it->first)
		{
			ITimer *timer = it->second;
			m_timers.erase(it);
			timer->TimerExpired(this, timer);
			continue;
		}
	}
	m_mutex.Unlock();
}

<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.

#include "calling_convention_arm.h"
#include "logging.h"
#include "managed_register_arm.h"

namespace art {
namespace arm {

// Calling convention

ManagedRegister ArmManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
  return ArmManagedRegister::FromCoreRegister(IP);  // R12
}

ManagedRegister ArmJniCallingConvention::InterproceduralScratchRegister() {
  return ArmManagedRegister::FromCoreRegister(IP);  // R12
}

static ManagedRegister ReturnRegisterForMethod(Method* method) {
  if (method->IsReturnAFloat()) {
    return ArmManagedRegister::FromCoreRegister(R0);
  } else if (method->IsReturnADouble()) {
    return ArmManagedRegister::FromRegisterPair(R0_R1);
  } else if (method->IsReturnALong()) {
    return ArmManagedRegister::FromRegisterPair(R0_R1);
  } else if (method->IsReturnVoid()) {
    return ArmManagedRegister::NoRegister();
  } else {
    return ArmManagedRegister::FromCoreRegister(R0);
  }
}

ManagedRegister ArmManagedRuntimeCallingConvention::ReturnRegister() {
  return ReturnRegisterForMethod(GetMethod());
}

ManagedRegister ArmJniCallingConvention::ReturnRegister() {
  return ReturnRegisterForMethod(GetMethod());
}

// Managed runtime calling convention

ManagedRegister ArmManagedRuntimeCallingConvention::MethodRegister() {
  return ArmManagedRegister::FromCoreRegister(R0);
}

bool ArmManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
  return itr_slots_ < 3;
}

bool ArmManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
  if (itr_slots_ < 2) {
    return false;
  } else if (itr_slots_ > 2) {
    return true;
  } else {
    // handle funny case of a long/double straddling registers and the stack
    return GetMethod()->IsParamALongOrDouble(itr_args_);
  }
}

static const Register kManagedArgumentRegisters[] = {
  R1, R2, R3
};
ManagedRegister ArmManagedRuntimeCallingConvention::CurrentParamRegister() {
  CHECK(IsCurrentParamInRegister());
  const Method* method = GetMethod();
  if (method->IsParamALongOrDouble(itr_args_)) {
    if (itr_slots_ == 0) {
      return ArmManagedRegister::FromRegisterPair(R1_R2);
    } else if (itr_slots_ == 1) {
      return ArmManagedRegister::FromRegisterPair(R2_R3);
    } else {
      // This is a long/double split between registers and the stack
      return ArmManagedRegister::FromCoreRegister(
        kManagedArgumentRegisters[itr_slots_]);
    }
  } else {
    return
      ArmManagedRegister::FromCoreRegister(kManagedArgumentRegisters[itr_slots_]);
  }
}

FrameOffset ArmManagedRuntimeCallingConvention::CurrentParamStackOffset() {
  CHECK(IsCurrentParamOnStack());
  FrameOffset result =
      FrameOffset(displacement_.Int32Value() +   // displacement
                  kPointerSize +                 // Method*
                  (itr_slots_ * kPointerSize));  // offset into in args
  if (itr_slots_ == 2) {
    // the odd spanning case, bump the offset to skip the first half of the
    // input which is in a register
    CHECK(IsCurrentParamInRegister());
    result = FrameOffset(result.Int32Value() + 4);
  }
  return result;
}

// JNI calling convention

ArmJniCallingConvention::ArmJniCallingConvention(Method* method) : JniCallingConvention(method) {
  // Compute padding to ensure longs and doubles are not split in AAPCS
  // TODO: in terms of outgoing argument size this may be overly generous
  // due to padding appearing in the registers
  size_t padding = 0;
  size_t check = method->IsStatic() ? 1 : 0;
  for(size_t i = 0; i < method->NumArgs(); i++) {
    if (((i & 1) == check) && method->IsParamALongOrDouble(i)) {
      padding += 4;
    }
  }
  padding_ = padding;
  if (method->IsSynchronized()) {
    // Preserve callee saves that may be clobbered during monitor enter where
    // we copy across R0 to R3
    if (method->NumArgs() > 0) {
      callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R4));
      if (method->NumArgs() > 1) {
        callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R5));
        if (method->NumArgs() > 2) {
          callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R6));
          if (method->NumArgs() > 3) {
            callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R7));
          }
        }
      }
    }
  }
}

uint32_t ArmJniCallingConvention::CoreSpillMask() const {
  // Compute spill mask to agree with callee saves initialized in the constructor
  uint32_t result = 0;
  Method* method = GetMethod();
  if (method->IsSynchronized()) {
    if (method->NumArgs() > 0) {
      result |= 1 << R4;
      if (method->NumArgs() > 1) {
        result |= 1 << R5;
        if (method->NumArgs() > 2) {
          result |= 1 << R6;
          if (method->NumArgs() > 3) {
            result |= 1 << R7;
          }
        }
      }
    }
  }
  return result;
}

ManagedRegister ArmJniCallingConvention::ReturnScratchRegister() const {
  return ArmManagedRegister::FromCoreRegister(R2);
}

size_t ArmJniCallingConvention::FrameSize() {
  // Method*, LR and callee save area size, local reference segment state
  size_t frame_data_size = (3 + CalleeSaveRegisters().size()) * kPointerSize;
  // References plus 2 words for SIRT header
  size_t sirt_size = (ReferenceCount() + 2) * kPointerSize;
  // Plus return value spill area size
  return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(), kStackAlignment);
}

size_t ArmJniCallingConvention::OutArgSize() {
  return RoundUp(NumberOfOutgoingStackArgs() * kPointerSize + padding_,
                 kStackAlignment);
}

size_t ArmJniCallingConvention::ReturnPcOffset() {
  // Link register is always the first value pushed when the frame is constructed
  return FrameSize() - kPointerSize;
}

// Will reg be crushed by an outgoing argument?
bool ArmJniCallingConvention::IsMethodRegisterClobberedPreCall() {
  return true;  // The method register R0 is always clobbered by the JNIEnv
}

// JniCallingConvention ABI follows AAPCS where longs and doubles must occur
// in even register numbers and stack slots
void ArmJniCallingConvention::Next() {
  JniCallingConvention::Next();
  Method* method = GetMethod();
  size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
  if ((itr_args_ >= 2) &&
      (arg_pos < GetMethod()->NumArgs()) &&
      method->IsParamALongOrDouble(arg_pos)) {
    // itr_slots_ needs to be an even number, according to AAPCS.
    if ((itr_slots_ & 0x1u) != 0) {
      itr_slots_++;
    }
  }
}

bool ArmJniCallingConvention::IsCurrentParamInRegister() {
  return itr_slots_ < 4;
}

bool ArmJniCallingConvention::IsCurrentParamOnStack() {
  return !IsCurrentParamInRegister();
}

static const Register kJniArgumentRegisters[] = {
  R0, R1, R2, R3
};
ManagedRegister ArmJniCallingConvention::CurrentParamRegister() {
  CHECK_LT(itr_slots_, 4u);
  Method* method = GetMethod();
  int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
  if ((itr_args_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
    CHECK_EQ(itr_slots_, 2u);
    return ArmManagedRegister::FromRegisterPair(R2_R3);
  } else {
    return
      ArmManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
  }
}

FrameOffset ArmJniCallingConvention::CurrentParamStackOffset() {
  CHECK_GE(itr_slots_, 4u);
  return FrameOffset(displacement_.Int32Value() - OutArgSize()
                     + ((itr_slots_ - 4) * kPointerSize));
}

size_t ArmJniCallingConvention::NumberOfOutgoingStackArgs() {
  Method* method = GetMethod();
  size_t static_args = method->IsStatic() ? 1 : 0;  // count jclass
  // regular argument parameters and this
  size_t param_args = method->NumArgs() +
                      method->NumLongOrDoubleArgs();
  // count JNIEnv* less arguments in registers
  return static_args + param_args + 1 - 4;
}

}  // namespace arm
}  // namespace art
<commit_msg>Callee-save these ARM registers: R5-R8, R10-R11 and LR.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.

#include "calling_convention_arm.h"
#include "logging.h"
#include "managed_register_arm.h"

namespace art {
namespace arm {

// Calling convention

ManagedRegister ArmManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
  return ArmManagedRegister::FromCoreRegister(IP);  // R12
}

ManagedRegister ArmJniCallingConvention::InterproceduralScratchRegister() {
  return ArmManagedRegister::FromCoreRegister(IP);  // R12
}

static ManagedRegister ReturnRegisterForMethod(Method* method) {
  if (method->IsReturnAFloat()) {
    return ArmManagedRegister::FromCoreRegister(R0);
  } else if (method->IsReturnADouble()) {
    return ArmManagedRegister::FromRegisterPair(R0_R1);
  } else if (method->IsReturnALong()) {
    return ArmManagedRegister::FromRegisterPair(R0_R1);
  } else if (method->IsReturnVoid()) {
    return ArmManagedRegister::NoRegister();
  } else {
    return ArmManagedRegister::FromCoreRegister(R0);
  }
}

ManagedRegister ArmManagedRuntimeCallingConvention::ReturnRegister() {
  return ReturnRegisterForMethod(GetMethod());
}

ManagedRegister ArmJniCallingConvention::ReturnRegister() {
  return ReturnRegisterForMethod(GetMethod());
}

// Managed runtime calling convention

ManagedRegister ArmManagedRuntimeCallingConvention::MethodRegister() {
  return ArmManagedRegister::FromCoreRegister(R0);
}

bool ArmManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
  return itr_slots_ < 3;
}

bool ArmManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
  if (itr_slots_ < 2) {
    return false;
  } else if (itr_slots_ > 2) {
    return true;
  } else {
    // handle funny case of a long/double straddling registers and the stack
    return GetMethod()->IsParamALongOrDouble(itr_args_);
  }
}

static const Register kManagedArgumentRegisters[] = {
  R1, R2, R3
};
ManagedRegister ArmManagedRuntimeCallingConvention::CurrentParamRegister() {
  CHECK(IsCurrentParamInRegister());
  const Method* method = GetMethod();
  if (method->IsParamALongOrDouble(itr_args_)) {
    if (itr_slots_ == 0) {
      return ArmManagedRegister::FromRegisterPair(R1_R2);
    } else if (itr_slots_ == 1) {
      return ArmManagedRegister::FromRegisterPair(R2_R3);
    } else {
      // This is a long/double split between registers and the stack
      return ArmManagedRegister::FromCoreRegister(
        kManagedArgumentRegisters[itr_slots_]);
    }
  } else {
    return
      ArmManagedRegister::FromCoreRegister(kManagedArgumentRegisters[itr_slots_]);
  }
}

FrameOffset ArmManagedRuntimeCallingConvention::CurrentParamStackOffset() {
  CHECK(IsCurrentParamOnStack());
  FrameOffset result =
      FrameOffset(displacement_.Int32Value() +   // displacement
                  kPointerSize +                 // Method*
                  (itr_slots_ * kPointerSize));  // offset into in args
  if (itr_slots_ == 2) {
    // the odd spanning case, bump the offset to skip the first half of the
    // input which is in a register
    CHECK(IsCurrentParamInRegister());
    result = FrameOffset(result.Int32Value() + 4);
  }
  return result;
}

// JNI calling convention

ArmJniCallingConvention::ArmJniCallingConvention(Method* method) : JniCallingConvention(method) {
  // Compute padding to ensure longs and doubles are not split in AAPCS
  // TODO: in terms of outgoing argument size this may be overly generous
  // due to padding appearing in the registers
  size_t padding = 0;
  size_t check = method->IsStatic() ? 1 : 0;
  for(size_t i = 0; i < method->NumArgs(); i++) {
    if (((i & 1) == check) && method->IsParamALongOrDouble(i)) {
      padding += 4;
    }
  }
  padding_ = padding;

  callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R5));
  callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R6));
  callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R7));
  callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R8));
  callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R10));
  callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R11));
}

uint32_t ArmJniCallingConvention::CoreSpillMask() const {
  // Compute spill mask to agree with callee saves initialized in the constructor
  uint32_t result = 0;
  result =  1 << R5 | 1 << R6 | 1 << R7 | 1 << R8 | 1 << R10 | 1 << R11 | 1 << LR;
  return result;
}

ManagedRegister ArmJniCallingConvention::ReturnScratchRegister() const {
  return ArmManagedRegister::FromCoreRegister(R2);
}

size_t ArmJniCallingConvention::FrameSize() {
  // Method*, LR and callee save area size, local reference segment state
  size_t frame_data_size = (3 + CalleeSaveRegisters().size()) * kPointerSize;
  // References plus 2 words for SIRT header
  size_t sirt_size = (ReferenceCount() + 2) * kPointerSize;
  // Plus return value spill area size
  return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(), kStackAlignment);
}

size_t ArmJniCallingConvention::OutArgSize() {
  return RoundUp(NumberOfOutgoingStackArgs() * kPointerSize + padding_,
                 kStackAlignment);
}

size_t ArmJniCallingConvention::ReturnPcOffset() {
  // Link register is always the first value pushed when the frame is constructed
  return FrameSize() - kPointerSize;
}

// Will reg be crushed by an outgoing argument?
bool ArmJniCallingConvention::IsMethodRegisterClobberedPreCall() {
  return true;  // The method register R0 is always clobbered by the JNIEnv
}

// JniCallingConvention ABI follows AAPCS where longs and doubles must occur
// in even register numbers and stack slots
void ArmJniCallingConvention::Next() {
  JniCallingConvention::Next();
  Method* method = GetMethod();
  size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
  if ((itr_args_ >= 2) &&
      (arg_pos < GetMethod()->NumArgs()) &&
      method->IsParamALongOrDouble(arg_pos)) {
    // itr_slots_ needs to be an even number, according to AAPCS.
    if ((itr_slots_ & 0x1u) != 0) {
      itr_slots_++;
    }
  }
}

bool ArmJniCallingConvention::IsCurrentParamInRegister() {
  return itr_slots_ < 4;
}

bool ArmJniCallingConvention::IsCurrentParamOnStack() {
  return !IsCurrentParamInRegister();
}

static const Register kJniArgumentRegisters[] = {
  R0, R1, R2, R3
};
ManagedRegister ArmJniCallingConvention::CurrentParamRegister() {
  CHECK_LT(itr_slots_, 4u);
  Method* method = GetMethod();
  int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
  if ((itr_args_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
    CHECK_EQ(itr_slots_, 2u);
    return ArmManagedRegister::FromRegisterPair(R2_R3);
  } else {
    return
      ArmManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
  }
}

FrameOffset ArmJniCallingConvention::CurrentParamStackOffset() {
  CHECK_GE(itr_slots_, 4u);
  return FrameOffset(displacement_.Int32Value() - OutArgSize()
                     + ((itr_slots_ - 4) * kPointerSize));
}

size_t ArmJniCallingConvention::NumberOfOutgoingStackArgs() {
  Method* method = GetMethod();
  size_t static_args = method->IsStatic() ? 1 : 0;  // count jclass
  // regular argument parameters and this
  size_t param_args = method->NumArgs() +
                      method->NumLongOrDoubleArgs();
  // count JNIEnv* less arguments in registers
  return static_args + param_args + 1 - 4;
}

}  // namespace arm
}  // namespace art
<|endoftext|>