<commit_before>/**
 *
 * Emerald (kbi/elude @2015)
 *
 */
#include "shared.h"
#include "ogl/ogl_context.h"
#include "ogl/ogl_query.h"
#include "ogl/ogl_rendering_handler.h"
#include "system/system_log.h"

/** TODO */
typedef struct _ogl_query_item
{
    GLuint gl_id;
    bool   has_started;

    _ogl_query_item()
    {
        gl_id       = 0;
        has_started = false;
    }

    ~_ogl_query_item()
    {
        ASSERT_DEBUG_SYNC(gl_id == 0,
                          "Sanity check failed.");
    }
} _ogl_query_item;

typedef struct _ogl_query
{
    /* DO NOT retain/release */
    ogl_context      context;
    unsigned int     ring_buffer_size;
    _ogl_query_item* qo_items;
    GLenum           target_gl;

    /* Points at the next QO that can be peeked OR result of whose can be retrieved.
     * A value of -1 indicates no ogl_query_begin() call was made for this query object.
     *
     * index_peek must always be < ring_buffer_size.
     * index_peek must always be != index_next */
    int index_peek;

    /* Points at the QO which has been used for the last ogl_query_begin() call.
     *
     * It is an error if index_next == index_peek, and indicates the ring buffer has run
     * out of space. This can only occur if the query object is used for many queries in a row
     * without checking the queries' results in-between.
     *
     * index_next must always be < ring_buffer_size.
     */
    int index_current;

    /* Cached entry-points */
    PFNGLBEGINQUERYPROC          pGLBeginQuery;
    PFNGLDELETEQUERIESPROC       pGLDeleteQueries;
    PFNGLENDQUERYPROC            pGLEndQuery;
    PFNGLGENQUERIESPROC          pGLGenQueries;
    PFNGLGETQUERYOBJECTUIVPROC   pGLGetQueryObjectuiv;
    PFNGLGETQUERYOBJECTUI64VPROC pGLGetQueryObjectui64v;

     _ogl_query(__in __notnull ogl_context  in_context,
                __in           unsigned int in_ring_buffer_size,
                __in           GLenum       in_target_gl);
    ~_ogl_query();
} _ogl_query;

/* Forward declarations */
PRIVATE void _ogl_query_deinit_renderer_callback(ogl_context context,
                                                 void*       user_arg);
PRIVATE void _ogl_query_init_renderer_callback  (ogl_context context,
                                                 void*       user_arg);

/** TODO */
_ogl_query::_ogl_query(__in __notnull ogl_context  in_context,
                       __in           unsigned int in_ring_buffer_size,
                       __in           GLenum       in_target_gl)
{
    ogl_context_type context_type = OGL_CONTEXT_TYPE_UNDEFINED;

    ASSERT_DEBUG_SYNC(in_ring_buffer_size > 0,
                      "Ring buffer size is invalid");

    context          = in_context;
    index_current    = -1;
    index_peek       = -1;
    qo_items         = new (std::nothrow) _ogl_query_item[in_ring_buffer_size];
    ring_buffer_size = in_ring_buffer_size;
    target_gl        = in_target_gl;

    ASSERT_DEBUG_SYNC(qo_items != NULL,
                      "Out of memory");

    /* Cache ES/GL entry-points */
    ogl_context_get_property(context,
                             OGL_CONTEXT_PROPERTY_TYPE,
                            &context_type);

    if (context_type == OGL_CONTEXT_TYPE_GL)
    {
        const ogl_context_gl_entrypoints* entry_points = NULL;

        ogl_context_get_property(context,
                                 OGL_CONTEXT_PROPERTY_ENTRYPOINTS_GL,
                                &entry_points);

        pGLBeginQuery          = entry_points->pGLBeginQuery;
        pGLDeleteQueries       = entry_points->pGLDeleteQueries;
        pGLEndQuery            = entry_points->pGLEndQuery;
        pGLGenQueries          = entry_points->pGLGenQueries;
        pGLGetQueryObjectuiv   = NULL; /* not needed, we're using the 64-bit version instead */
        pGLGetQueryObjectui64v = entry_points->pGLGetQueryObjectui64v;
    } /* if (context_type == OGL_CONTEXT_TYPE_GL) */
    else
    {
        ASSERT_DEBUG_SYNC(context_type == OGL_CONTEXT_TYPE_ES,
                          "Unrecognized rendering context type");

        const ogl_context_es_entrypoints* entry_points = NULL;

        ogl_context_get_property(context,
                                 OGL_CONTEXT_PROPERTY_ENTRYPOINTS_ES,
                                &entry_points);

        pGLBeginQuery          = entry_points->pGLBeginQuery;
        pGLDeleteQueries       = entry_points->pGLDeleteQueries;
        pGLEndQuery            = entry_points->pGLEndQuery;
        pGLGenQueries          = entry_points->pGLGenQueries;
        pGLGetQueryObjectuiv   = entry_points->pGLGetQueryObjectuiv;
        pGLGetQueryObjectui64v = NULL;
    }

    /* Initialize the object */
    ogl_context_request_callback_from_context_thread(context,
                                                     _ogl_query_init_renderer_callback,
                                                     this);

}

/** TODO */
_ogl_query::~_ogl_query()
{
    ogl_context_request_callback_from_context_thread(context,
                                                     _ogl_query_deinit_renderer_callback,
                                                     this);

    if (qo_items != NULL)
    {
        delete [] qo_items;

        qo_items = NULL;
    } /* if (qo_ids != NULL) */
}

/** TODO */
PRIVATE void _ogl_query_deinit_renderer_callback(ogl_context context,
                                                 void*       user_arg)
{
    _ogl_query* query_ptr = (_ogl_query*) user_arg;

    for (unsigned int n_item = 0;
                      n_item < query_ptr->ring_buffer_size;
                    ++n_item)
    {
        query_ptr->pGLDeleteQueries(1, /* n */
                                   &query_ptr->qo_items[n_item].gl_id);

        query_ptr->qo_items[n_item].gl_id = 0;
    } /* for (all items) */
}

/** TODO */
PRIVATE void _ogl_query_init_renderer_callback(__in __notnull ogl_context context,
                                               __in __notnull void*       user_arg)
{
    _ogl_query* query_ptr = (_ogl_query*) user_arg;

    for (unsigned int n_item = 0;
                      n_item < query_ptr->ring_buffer_size;
                    ++n_item)
    {
        query_ptr->pGLGenQueries(1, /* n */
                                &query_ptr->qo_items[n_item].gl_id);
    } /* for (all items) */
}

/** Please see header for spec */
PUBLIC RENDERING_CONTEXT_CALL void ogl_query_begin(__in __notnull ogl_query query)
{
    _ogl_query* query_ptr = (_ogl_query*) query;

    /* Increment the 'current QO' index */
    query_ptr->index_current = (query_ptr->index_current + 1) % query_ptr->ring_buffer_size;

    ASSERT_DEBUG_SYNC(query_ptr->index_current != query_ptr->index_peek,
                      "Sanity check failed.");

    if (query_ptr->index_current != query_ptr->index_peek)
    {
        /* Kick off the query */
        query_ptr->pGLBeginQuery(query_ptr->target_gl,
                                 query_ptr->qo_items[query_ptr->index_current].gl_id);

        query_ptr->qo_items[query_ptr->index_current].has_started = true;
    }
    else
    {
        LOG_ERROR("ogl_query_begin(): Ignored, peek index == next index! Fetch query object results first.");
    }
}

/** Please see header for spec */
PUBLIC ogl_query ogl_query_create(__in __notnull ogl_context  context,
                                  __in           unsigned int ring_buffer_size,
                                  __in           GLenum       gl_query_target)
{
    _ogl_query* new_instance = new (std::nothrow) _ogl_query(context,
                                                             ring_buffer_size,
                                                             gl_query_target);

    ASSERT_ALWAYS_SYNC(new_instance != NULL,
                       "Out of memory");

    return (ogl_query) new_instance;
}

/** Please see header for spec */
PUBLIC RENDERING_CONTEXT_CALL void ogl_query_end(__in __notnull ogl_query query)
{
    _ogl_query*        query_ptr = (_ogl_query*) query;
    const unsigned int qo_id     = query_ptr->index_current;

    if (query_ptr->qo_items[qo_id].has_started)
    {
        /* Stop the query */
        query_ptr->pGLEndQuery(query_ptr->target_gl);

        query_ptr->qo_items[qo_id].has_started = false;
    }
}

/** Please see header for spec */
PUBLIC RENDERING_CONTEXT_CALL bool ogl_query_peek_result(__in      __notnull ogl_query query,
                                                         __out_opt           GLuint64* out_result)
{
    _ogl_query* query_ptr = (_ogl_query*) query;
    bool        result    = false;

    /* Sanity checks */
    const unsigned int index_peek = (query_ptr->index_peek + 1) % query_ptr->ring_buffer_size;

    ASSERT_DEBUG_SYNC(!query_ptr->qo_items[index_peek].has_started,
                      "The QO that is being peeked has not been ended yet.");

    /* Do we have any spare QO to use for the next begin operation? */
    bool should_force = ( (query_ptr->index_current + 1) % query_ptr->ring_buffer_size == query_ptr->index_peek);

    /* ES offers glGetQueryObjectuiv(), whereas under OpenGL we have glGetQueryObjectui64v() */
    if (query_ptr->pGLGetQueryObjectui64v != NULL)
    {
        /* GL code-path */
        GLuint64 temp = -1;

        query_ptr->pGLGetQueryObjectui64v(query_ptr->qo_items[index_peek].gl_id,
                                          GL_QUERY_RESULT_NO_WAIT,
                                         &temp);

        if (temp == -1 && should_force)
        {
            /* Got to stall the pipeline .. */
            LOG_ERROR("Performance warning: ogl_query_peek_result() about to force an implicit finish.");

            query_ptr->pGLGetQueryObjectui64v(query_ptr->qo_items[index_peek].gl_id,
                                              GL_QUERY_RESULT,
                                             &temp);
        }

        if (temp != -1)
        {
            /* The result has been returned - yay! */
            *out_result = temp;
            result      = true;

            /* Update the index */
            query_ptr->index_peek = index_peek;
        }
    } /* if (query_ptr->pGLGetQueryObjectui64v != NULL) */
    else
    {
        /* ES code-path */
        GLuint is_result_available = GL_FALSE;

        query_ptr->pGLGetQueryObjectuiv(query_ptr->qo_items[index_peek].gl_id,
                                        GL_QUERY_RESULT_AVAILABLE,
                                       &is_result_available);

        if (is_result_available == GL_TRUE ||
            should_force)
        {
            GLuint temp = -1;

            query_ptr->pGLGetQueryObjectuiv(query_ptr->qo_items[index_peek].gl_id,
                                            GL_QUERY_RESULT,
                                           &temp);

            *out_result = (GLuint64) temp;
             result     = true;

            /* Update the index */
            query_ptr->index_peek = index_peek;
        } /* if (is_result_available || should_force) */
    }

    return result;
}

/** Please see header for spec */
PUBLIC void ogl_query_release(__in __notnull __post_invalid ogl_query query)
{
    _ogl_query* query_ptr = (_ogl_query*) query;

    /* Done */
    delete query_ptr;

    query_ptr = NULL;
}

<commit_msg>Emerald: IHV-specific fix to avoid thrashing the logging console with ogl_query ring buffer overflow warnings<commit_after>/**
 *
 * Emerald (kbi/elude @2015)
 *
 */
#include "shared.h"
#include "ogl/ogl_context.h"
#include "ogl/ogl_query.h"
#include "ogl/ogl_rendering_handler.h"
#include "system/system_log.h"

/** TODO */
typedef struct _ogl_query_item
{
    GLuint gl_id;
    bool   has_started;

    _ogl_query_item()
    {
        gl_id       = 0;
        has_started = false;
    }

    ~_ogl_query_item()
    {
        ASSERT_DEBUG_SYNC(gl_id == 0,
                          "Sanity check failed.");
    }
} _ogl_query_item;

typedef struct _ogl_query
{
    /* DO NOT retain/release */
    ogl_context      context;
    unsigned int     ring_buffer_size;
    _ogl_query_item* qo_items;
    GLenum           target_gl;

    /* Points at the next QO that can be peeked OR result of whose can be retrieved.
     * A value of -1 indicates no ogl_query_begin() call was made for this query object.
     *
     * index_peek must always be < ring_buffer_size.
     * index_peek must always be != index_next */
    int index_peek;

    /* Points at the QO which has been used for the last ogl_query_begin() call.
     *
     * It is an error if index_next == index_peek, and indicates the ring buffer has run
     * out of space. This can only occur if the query object is used for many queries in a row
     * without checking the queries' results in-between.
     *
     * index_next must always be < ring_buffer_size.
     */
    int index_current;

    /* Cached entry-points */
    PFNGLBEGINQUERYPROC          pGLBeginQuery;
    PFNGLDELETEQUERIESPROC       pGLDeleteQueries;
    PFNGLENDQUERYPROC            pGLEndQuery;
    PFNGLGENQUERIESPROC          pGLGenQueries;
    PFNGLGETQUERYOBJECTUIVPROC   pGLGetQueryObjectuiv;
    PFNGLGETQUERYOBJECTUI64VPROC pGLGetQueryObjectui64v;

     _ogl_query(__in __notnull ogl_context  in_context,
                __in           unsigned int in_ring_buffer_size,
                __in           GLenum       in_target_gl);
    ~_ogl_query();
} _ogl_query;

/* Forward declarations */
PRIVATE void _ogl_query_deinit_renderer_callback(ogl_context context,
                                                 void*       user_arg);
PRIVATE void _ogl_query_init_renderer_callback  (ogl_context context,
                                                 void*       user_arg);

/** TODO */
_ogl_query::_ogl_query(__in __notnull ogl_context  in_context,
                       __in           unsigned int in_ring_buffer_size,
                       __in           GLenum       in_target_gl)
{
    ogl_context_type context_type = OGL_CONTEXT_TYPE_UNDEFINED;

    ASSERT_DEBUG_SYNC(in_ring_buffer_size > 0,
                      "Ring buffer size is invalid");

    context          = in_context;
    index_current    = -1;
    index_peek       = -1;
    qo_items         = new (std::nothrow) _ogl_query_item[in_ring_buffer_size];
    ring_buffer_size = in_ring_buffer_size;
    target_gl        = in_target_gl;

    ASSERT_DEBUG_SYNC(qo_items != NULL,
                      "Out of memory");

    /* Cache ES/GL entry-points */
    ogl_context_get_property(context,
                             OGL_CONTEXT_PROPERTY_TYPE,
                            &context_type);

    if (context_type == OGL_CONTEXT_TYPE_GL)
    {
        const ogl_context_gl_entrypoints* entry_points = NULL;

        ogl_context_get_property(context,
                                 OGL_CONTEXT_PROPERTY_ENTRYPOINTS_GL,
                                &entry_points);

        pGLBeginQuery          = entry_points->pGLBeginQuery;
        pGLDeleteQueries       = entry_points->pGLDeleteQueries;
        pGLEndQuery            = entry_points->pGLEndQuery;
        pGLGenQueries          = entry_points->pGLGenQueries;
        pGLGetQueryObjectuiv   = NULL; /* not needed, we're using the 64-bit version instead */
        pGLGetQueryObjectui64v = entry_points->pGLGetQueryObjectui64v;
    } /* if (context_type == OGL_CONTEXT_TYPE_GL) */
    else
    {
        ASSERT_DEBUG_SYNC(context_type == OGL_CONTEXT_TYPE_ES,
                          "Unrecognized rendering context type");

        const ogl_context_es_entrypoints* entry_points = NULL;

        ogl_context_get_property(context,
                                 OGL_CONTEXT_PROPERTY_ENTRYPOINTS_ES,
                                &entry_points);

        pGLBeginQuery          = entry_points->pGLBeginQuery;
        pGLDeleteQueries       = entry_points->pGLDeleteQueries;
        pGLEndQuery            = entry_points->pGLEndQuery;
        pGLGenQueries          = entry_points->pGLGenQueries;
        pGLGetQueryObjectuiv   = entry_points->pGLGetQueryObjectuiv;
        pGLGetQueryObjectui64v = NULL;
    }

    /* Initialize the object */
    ogl_context_request_callback_from_context_thread(context,
                                                     _ogl_query_init_renderer_callback,
                                                     this);

}

/** TODO */
_ogl_query::~_ogl_query()
{
    ogl_context_request_callback_from_context_thread(context,
                                                     _ogl_query_deinit_renderer_callback,
                                                     this);

    if (qo_items != NULL)
    {
        delete [] qo_items;

        qo_items = NULL;
    } /* if (qo_ids != NULL) */
}

/** TODO */
PRIVATE void _ogl_query_deinit_renderer_callback(ogl_context context,
                                                 void*       user_arg)
{
    _ogl_query* query_ptr = (_ogl_query*) user_arg;

    for (unsigned int n_item = 0;
                      n_item < query_ptr->ring_buffer_size;
                    ++n_item)
    {
        query_ptr->pGLDeleteQueries(1, /* n */
                                   &query_ptr->qo_items[n_item].gl_id);

        query_ptr->qo_items[n_item].gl_id = 0;
    } /* for (all items) */
}

/** TODO */
PRIVATE void _ogl_query_init_renderer_callback(__in __notnull ogl_context context,
                                               __in __notnull void*       user_arg)
{
    _ogl_query* query_ptr = (_ogl_query*) user_arg;

    for (unsigned int n_item = 0;
                      n_item < query_ptr->ring_buffer_size;
                    ++n_item)
    {
        query_ptr->pGLGenQueries(1, /* n */
                                &query_ptr->qo_items[n_item].gl_id);
    } /* for (all items) */
}

/** Please see header for spec */
PUBLIC RENDERING_CONTEXT_CALL void ogl_query_begin(__in __notnull ogl_query query)
{
    _ogl_query* query_ptr = (_ogl_query*) query;

    /* Increment the 'current QO' index */
    query_ptr->index_current = (query_ptr->index_current + 1) % query_ptr->ring_buffer_size;

    ASSERT_DEBUG_SYNC(query_ptr->index_current != query_ptr->index_peek,
                      "Sanity check failed.");

    if (query_ptr->index_current != query_ptr->index_peek)
    {
        /* Kick off the query */
        query_ptr->pGLBeginQuery(query_ptr->target_gl,
                                 query_ptr->qo_items[query_ptr->index_current].gl_id);

        query_ptr->qo_items[query_ptr->index_current].has_started = true;
    }
    else
    {
        LOG_ERROR("ogl_query_begin(): Ignored, peek index == next index! Fetch query object results first.");
    }
}

/** Please see header for spec */
PUBLIC ogl_query ogl_query_create(__in __notnull ogl_context  context,
                                  __in           unsigned int ring_buffer_size,
                                  __in           GLenum       gl_query_target)
{
    _ogl_query* new_instance = new (std::nothrow) _ogl_query(context,
                                                             ring_buffer_size,
                                                             gl_query_target);

    ASSERT_ALWAYS_SYNC(new_instance != NULL,
                       "Out of memory");

    return (ogl_query) new_instance;
}

/** Please see header for spec */
PUBLIC RENDERING_CONTEXT_CALL void ogl_query_end(__in __notnull ogl_query query)
{
    _ogl_query*        query_ptr = (_ogl_query*) query;
    const unsigned int qo_id     = query_ptr->index_current;

    if (query_ptr->qo_items[qo_id].has_started)
    {
        /* Stop the query */
        query_ptr->pGLEndQuery(query_ptr->target_gl);

        query_ptr->qo_items[qo_id].has_started = false;
    }
}

/** Please see header for spec */
PUBLIC RENDERING_CONTEXT_CALL bool ogl_query_peek_result(__in      __notnull ogl_query query,
                                                         __out_opt           GLuint64* out_result)
{
    _ogl_query* query_ptr = (_ogl_query*) query;
    bool        result    = false;

    /* Sanity checks */
    const unsigned int index_peek = (query_ptr->index_peek + 1) % query_ptr->ring_buffer_size;

    ASSERT_DEBUG_SYNC(!query_ptr->qo_items[index_peek].has_started,
                      "The QO that is being peeked has not been ended yet.");

    /* Do we have any spare QO to use for the next begin operation? */
    bool should_force = ( (query_ptr->index_current + 1) % query_ptr->ring_buffer_size == query_ptr->index_peek);

    /* ES offers glGetQueryObjectuiv(), whereas under OpenGL we have glGetQueryObjectui64v() */
    if (query_ptr->pGLGetQueryObjectui64v != NULL)
    {
        /* GL code-path */
        GLuint64 temp = -1;

        query_ptr->pGLGetQueryObjectui64v(query_ptr->qo_items[index_peek].gl_id,
                                          GL_QUERY_RESULT_NO_WAIT,
                                         &temp);

        if (temp == -1 && should_force)
        {
            /* Got to stall the pipeline .. */
            static system_timeline_time last_warning_time = 0;
            static system_timeline_time one_sec_time      = system_time_get_timeline_time_for_s(1);
            static system_timeline_time time_now          = system_time_now();

            if (time_now - last_warning_time > one_sec_time)
            {
                LOG_ERROR("Performance warning: ogl_query_peek_result() about to force an implicit finish.");

                last_warning_time = time_now;
            }

            query_ptr->pGLGetQueryObjectui64v(query_ptr->qo_items[index_peek].gl_id,
                                              GL_QUERY_RESULT,
                                             &temp);
        }

        if (temp != -1)
        {
            /* The result has been returned - yay! */
            *out_result = temp;
            result      = true;

            /* Update the index */
            query_ptr->index_peek = index_peek;
        }
    } /* if (query_ptr->pGLGetQueryObjectui64v != NULL) */
    else
    {
        /* ES code-path */
        GLuint is_result_available = GL_FALSE;

        query_ptr->pGLGetQueryObjectuiv(query_ptr->qo_items[index_peek].gl_id,
                                        GL_QUERY_RESULT_AVAILABLE,
                                       &is_result_available);

        if (is_result_available == GL_TRUE ||
            should_force)
        {
            GLuint temp = -1;

            query_ptr->pGLGetQueryObjectuiv(query_ptr->qo_items[index_peek].gl_id,
                                            GL_QUERY_RESULT,
                                           &temp);

            *out_result = (GLuint64) temp;
             result     = true;

            /* Update the index */
            query_ptr->index_peek = index_peek;
        } /* if (is_result_available || should_force) */
    }

    return result;
}

/** Please see header for spec */
PUBLIC void ogl_query_release(__in __notnull __post_invalid ogl_query query)
{
    _ogl_query* query_ptr = (_ogl_query*) query;

    /* Done */
    delete query_ptr;

    query_ptr = NULL;
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * 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 the copyright holders 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 COPYRIGHT HOLDERS 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 COPYRIGHT
 * OWNER 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.
 *
 * Authors: Steve Reinhardt
 */

#include "arch/utility.hh"
#include "arch/faults.hh"
#include "base/cprintf.hh"
#include "base/inifile.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/pollevent.hh"
#include "base/range.hh"
#include "base/stats/events.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "cpu/exetrace.hh"
#include "cpu/profile.hh"
#include "cpu/simple/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/smt.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "mem/packet.hh"
#include "sim/builder.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/host.hh"
#include "sim/sim_events.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

#if FULL_SYSTEM
#include "arch/kernel_stats.hh"
#include "arch/stacktrace.hh"
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#include "base/remote_gdb.hh"
#else // !FULL_SYSTEM
#include "mem/mem_object.hh"
#endif // FULL_SYSTEM

using namespace std;
using namespace TheISA;

BaseSimpleCPU::BaseSimpleCPU(Params *p)
    : BaseCPU(p), thread(NULL)
{
#if FULL_SYSTEM
    thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb);
#else
    thread = new SimpleThread(this, /* thread_num */ 0, p->process,
            /* asid */ 0);
#endif // !FULL_SYSTEM

    thread->setStatus(ThreadContext::Suspended);

    tc = thread->getTC();

    numInst = 0;
    startNumInst = 0;
    numLoad = 0;
    startNumLoad = 0;
    lastIcacheStall = 0;
    lastDcacheStall = 0;

    threadContexts.push_back(tc);
}

BaseSimpleCPU::~BaseSimpleCPU()
{
}

void
BaseSimpleCPU::deallocateContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::haltContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::regStats()
{
    using namespace Stats;

    BaseCPU::regStats();

    numInsts
        .name(name() + ".num_insts")
        .desc("Number of instructions executed")
        ;

    numMemRefs
        .name(name() + ".num_refs")
        .desc("Number of memory references")
        ;

    notIdleFraction
        .name(name() + ".not_idle_fraction")
        .desc("Percentage of non-idle cycles")
        ;

    idleFraction
        .name(name() + ".idle_fraction")
        .desc("Percentage of idle cycles")
        ;

    icacheStallCycles
        .name(name() + ".icache_stall_cycles")
        .desc("ICache total stall cycles")
        .prereq(icacheStallCycles)
        ;

    dcacheStallCycles
        .name(name() + ".dcache_stall_cycles")
        .desc("DCache total stall cycles")
        .prereq(dcacheStallCycles)
        ;

    icacheRetryCycles
        .name(name() + ".icache_retry_cycles")
        .desc("ICache total retry cycles")
        .prereq(icacheRetryCycles)
        ;

    dcacheRetryCycles
        .name(name() + ".dcache_retry_cycles")
        .desc("DCache total retry cycles")
        .prereq(dcacheRetryCycles)
        ;

    idleFraction = constant(1.0) - notIdleFraction;
}

void
BaseSimpleCPU::resetStats()
{
//    startNumInst = numInst;
    // notIdleFraction = (_status != Idle);
}

void
BaseSimpleCPU::serialize(ostream &os)
{
    BaseCPU::serialize(os);
//    SERIALIZE_SCALAR(inst);
    nameOut(os, csprintf("%s.xc.0", name()));
    thread->serialize(os);
}

void
BaseSimpleCPU::unserialize(Checkpoint *cp, const string &section)
{
    BaseCPU::unserialize(cp, section);
//    UNSERIALIZE_SCALAR(inst);
    thread->unserialize(cp, csprintf("%s.xc.0", section));
}

void
change_thread_state(int thread_number, int activate, int priority)
{
}

Fault
BaseSimpleCPU::copySrcTranslate(Addr src)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    int offset = src & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (src & PageMask) != ((src + blk_size) & PageMask) &&
        (src >> 40) != 0xfffffc) {
        warn("Copied block source spans pages %x.", src);
        no_warn = false;
    }

    memReq->reset(src & ~(blk_size - 1), blk_size);

    // translate to physical address
    Fault fault = thread->translateDataReadReq(req);

    if (fault == NoFault) {
        thread->copySrcAddr = src;
        thread->copySrcPhysAddr = memReq->paddr + offset;
    } else {
        assert(!fault->isAlignmentFault());

        thread->copySrcAddr = 0;
        thread->copySrcPhysAddr = 0;
    }
    return fault;
#else
    return NoFault;
#endif
}

Fault
BaseSimpleCPU::copy(Addr dest)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    uint8_t data[blk_size];
    //assert(thread->copySrcAddr);
    int offset = dest & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (dest & PageMask) != ((dest + blk_size) & PageMask) &&
        (dest >> 40) != 0xfffffc) {
        no_warn = false;
        warn("Copied block destination spans pages %x. ", dest);
    }

    memReq->reset(dest & ~(blk_size -1), blk_size);
    // translate to physical address
    Fault fault = thread->translateDataWriteReq(req);

    if (fault == NoFault) {
        Addr dest_addr = memReq->paddr + offset;
        // Need to read straight from memory since we have more than 8 bytes.
        memReq->paddr = thread->copySrcPhysAddr;
        thread->mem->read(memReq, data);
        memReq->paddr = dest_addr;
        thread->mem->write(memReq, data);
        if (dcacheInterface) {
            memReq->cmd = Copy;
            memReq->completionEvent = NULL;
            memReq->paddr = thread->copySrcPhysAddr;
            memReq->dest = dest_addr;
            memReq->size = 64;
            memReq->time = curTick;
            memReq->flags &= ~INST_READ;
            dcacheInterface->access(memReq);
        }
    }
    else
        assert(!fault->isAlignmentFault());

    return fault;
#else
    panic("copy not implemented");
    return NoFault;
#endif
}

#if FULL_SYSTEM
Addr
BaseSimpleCPU::dbg_vtophys(Addr addr)
{
    return vtophys(tc, addr);
}
#endif // FULL_SYSTEM

#if FULL_SYSTEM
void
BaseSimpleCPU::post_interrupt(int int_num, int index)
{
    BaseCPU::post_interrupt(int_num, index);

    if (thread->status() == ThreadContext::Suspended) {
                DPRINTF(IPI,"Suspended Processor awoke\n");
        thread->activate();
    }
}
#endif // FULL_SYSTEM

void
BaseSimpleCPU::checkForInterrupts()
{
#if FULL_SYSTEM
    if (checkInterrupts && check_interrupts(tc)) {
        Fault interrupt = interrupts.getInterrupt(tc);

        if (interrupt != NoFault) {
            interrupts.updateIntrInfo(tc);
            checkInterrupts = false;
            interrupt->invoke(tc);
        }
    }
#endif
}

Fault
BaseSimpleCPU::setupFetchRequest(Request *req)
{
    // set up memory request for instruction fetch
#if ISA_HAS_DELAY_SLOT
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",thread->readPC(),
            thread->readNextPC(),thread->readNextNPC());
#else
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",thread->readPC(),
            thread->readNextPC());
#endif

    req->setVirt(0, thread->readPC() & ~3, sizeof(MachInst),
                 (FULL_SYSTEM && (thread->readPC() & 1)) ? PHYSICAL : 0,
                 thread->readPC());

    Fault fault = thread->translateInstReq(req);

    return fault;
}

void
BaseSimpleCPU::preExecute()
{
    // maintain $r0 semantics
    thread->setIntReg(ZeroReg, 0);
#if THE_ISA == ALPHA_ISA
    thread->setFloatReg(ZeroReg, 0.0);
#endif // ALPHA_ISA

    // keep an instruction count
    numInst++;
    numInsts++;

    thread->funcExeInst++;

    // check for instruction-count-based events
    comInstEventQueue[0]->serviceEvents(numInst);

    // decode the instruction
    inst = gtoh(inst);
    //If we're not in the middle of a macro instruction
    if (!curMacroStaticInst) {
#if THE_ISA == ALPHA_ISA
        StaticInstPtr instPtr = StaticInst::decode(makeExtMI(inst, thread->readPC()));
#elif THE_ISA == SPARC_ISA
        StaticInstPtr instPtr = StaticInst::decode(makeExtMI(inst, thread->getTC()));
#elif THE_ISA == MIPS_ISA
        //Mips doesn't do anything in it's MakeExtMI function right now,
        //so it won't be called.
        StaticInstPtr instPtr = StaticInst::decode(inst);
#endif
        if (instPtr->isMacroOp()) {
            curMacroStaticInst = instPtr;
            curStaticInst = curMacroStaticInst->
                fetchMicroOp(thread->readMicroPC());
        } else {
            curStaticInst = instPtr;
        }
    } else {
        //Read the next micro op from the macro op
        curStaticInst = curMacroStaticInst->
            fetchMicroOp(thread->readMicroPC());
    }

    traceData = Trace::getInstRecord(curTick, tc, curStaticInst,
                                     thread->readPC());

    DPRINTF(Decode,"Decode: Decoded %s instruction (opcode: 0x%x): 0x%x\n",
            curStaticInst->getName(), curStaticInst->getOpcode(),
            curStaticInst->machInst);

#if FULL_SYSTEM
    thread->setInst(inst);
#endif // FULL_SYSTEM
}

void
BaseSimpleCPU::postExecute()
{
#if FULL_SYSTEM
    if (thread->profile) {
        bool usermode = TheISA::inUserMode(tc);
        thread->profilePC = usermode ? 1 : thread->readPC();
        ProfileNode *node = thread->profile->consume(tc, inst);
        if (node)
            thread->profileNode = node;
    }
#endif

    if (curStaticInst->isMemRef()) {
        numMemRefs++;
    }

    if (curStaticInst->isLoad()) {
        ++numLoad;
        comLoadEventQueue[0]->serviceEvents(numLoad);
    }

    traceFunctions(thread->readPC());

    if (traceData) {
        traceData->finalize();
    }
}

void
BaseSimpleCPU::advancePC(Fault fault)
{
    if (fault != NoFault) {
        fault->invoke(tc);
    } else {
        //If we're at the last micro op for this instruction
        if (curStaticInst->isLastMicroOp()) {
            //We should be working with a macro op
            assert(curMacroStaticInst);
            //Close out this macro op, and clean up the
            //microcode state
            curMacroStaticInst = StaticInst::nullStaticInstPtr;
            thread->setMicroPC(0);
            thread->setNextMicroPC(1);
        }
        //If we're still in a macro op
        if (curMacroStaticInst) {
            //Advance the micro pc
            thread->setMicroPC(thread->readNextMicroPC());
            //Advance the "next" micro pc. Note that there are no delay
            //slots, and micro ops are "word" addressed.
            thread->setNextMicroPC(thread->readNextMicroPC() + 1);
        } else {
            // go to the next instruction
            thread->setPC(thread->readNextPC());
#if ISA_HAS_DELAY_SLOT
            thread->setNextPC(thread->readNextNPC());
            thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst));
            assert(thread->readNextPC() != thread->readNextNPC());
#else
            thread->setNextPC(thread->readNextPC() + sizeof(MachInst));
#endif
        }
    }

#if FULL_SYSTEM
    Addr oldpc;
    do {
        oldpc = thread->readPC();
        system->pcEventQueue.service(tc);
    } while (oldpc != thread->readPC());
#endif
}

<commit_msg>In the case that we generate a fault (e.g. a tlb miss) on a microcoded instruction set curMacroStaticInst to null This way we'll jump immediately to the handler<commit_after>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * 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 the copyright holders 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 COPYRIGHT HOLDERS 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 COPYRIGHT
 * OWNER 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.
 *
 * Authors: Steve Reinhardt
 */

#include "arch/utility.hh"
#include "arch/faults.hh"
#include "base/cprintf.hh"
#include "base/inifile.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/pollevent.hh"
#include "base/range.hh"
#include "base/stats/events.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "cpu/exetrace.hh"
#include "cpu/profile.hh"
#include "cpu/simple/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/smt.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "mem/packet.hh"
#include "sim/builder.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/host.hh"
#include "sim/sim_events.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

#if FULL_SYSTEM
#include "arch/kernel_stats.hh"
#include "arch/stacktrace.hh"
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#include "base/remote_gdb.hh"
#else // !FULL_SYSTEM
#include "mem/mem_object.hh"
#endif // FULL_SYSTEM

using namespace std;
using namespace TheISA;

BaseSimpleCPU::BaseSimpleCPU(Params *p)
    : BaseCPU(p), thread(NULL)
{
#if FULL_SYSTEM
    thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb);
#else
    thread = new SimpleThread(this, /* thread_num */ 0, p->process,
            /* asid */ 0);
#endif // !FULL_SYSTEM

    thread->setStatus(ThreadContext::Suspended);

    tc = thread->getTC();

    numInst = 0;
    startNumInst = 0;
    numLoad = 0;
    startNumLoad = 0;
    lastIcacheStall = 0;
    lastDcacheStall = 0;

    threadContexts.push_back(tc);
}

BaseSimpleCPU::~BaseSimpleCPU()
{
}

void
BaseSimpleCPU::deallocateContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::haltContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::regStats()
{
    using namespace Stats;

    BaseCPU::regStats();

    numInsts
        .name(name() + ".num_insts")
        .desc("Number of instructions executed")
        ;

    numMemRefs
        .name(name() + ".num_refs")
        .desc("Number of memory references")
        ;

    notIdleFraction
        .name(name() + ".not_idle_fraction")
        .desc("Percentage of non-idle cycles")
        ;

    idleFraction
        .name(name() + ".idle_fraction")
        .desc("Percentage of idle cycles")
        ;

    icacheStallCycles
        .name(name() + ".icache_stall_cycles")
        .desc("ICache total stall cycles")
        .prereq(icacheStallCycles)
        ;

    dcacheStallCycles
        .name(name() + ".dcache_stall_cycles")
        .desc("DCache total stall cycles")
        .prereq(dcacheStallCycles)
        ;

    icacheRetryCycles
        .name(name() + ".icache_retry_cycles")
        .desc("ICache total retry cycles")
        .prereq(icacheRetryCycles)
        ;

    dcacheRetryCycles
        .name(name() + ".dcache_retry_cycles")
        .desc("DCache total retry cycles")
        .prereq(dcacheRetryCycles)
        ;

    idleFraction = constant(1.0) - notIdleFraction;
}

void
BaseSimpleCPU::resetStats()
{
//    startNumInst = numInst;
    // notIdleFraction = (_status != Idle);
}

void
BaseSimpleCPU::serialize(ostream &os)
{
    BaseCPU::serialize(os);
//    SERIALIZE_SCALAR(inst);
    nameOut(os, csprintf("%s.xc.0", name()));
    thread->serialize(os);
}

void
BaseSimpleCPU::unserialize(Checkpoint *cp, const string &section)
{
    BaseCPU::unserialize(cp, section);
//    UNSERIALIZE_SCALAR(inst);
    thread->unserialize(cp, csprintf("%s.xc.0", section));
}

void
change_thread_state(int thread_number, int activate, int priority)
{
}

Fault
BaseSimpleCPU::copySrcTranslate(Addr src)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    int offset = src & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (src & PageMask) != ((src + blk_size) & PageMask) &&
        (src >> 40) != 0xfffffc) {
        warn("Copied block source spans pages %x.", src);
        no_warn = false;
    }

    memReq->reset(src & ~(blk_size - 1), blk_size);

    // translate to physical address
    Fault fault = thread->translateDataReadReq(req);

    if (fault == NoFault) {
        thread->copySrcAddr = src;
        thread->copySrcPhysAddr = memReq->paddr + offset;
    } else {
        assert(!fault->isAlignmentFault());

        thread->copySrcAddr = 0;
        thread->copySrcPhysAddr = 0;
    }
    return fault;
#else
    return NoFault;
#endif
}

Fault
BaseSimpleCPU::copy(Addr dest)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    uint8_t data[blk_size];
    //assert(thread->copySrcAddr);
    int offset = dest & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (dest & PageMask) != ((dest + blk_size) & PageMask) &&
        (dest >> 40) != 0xfffffc) {
        no_warn = false;
        warn("Copied block destination spans pages %x. ", dest);
    }

    memReq->reset(dest & ~(blk_size -1), blk_size);
    // translate to physical address
    Fault fault = thread->translateDataWriteReq(req);

    if (fault == NoFault) {
        Addr dest_addr = memReq->paddr + offset;
        // Need to read straight from memory since we have more than 8 bytes.
        memReq->paddr = thread->copySrcPhysAddr;
        thread->mem->read(memReq, data);
        memReq->paddr = dest_addr;
        thread->mem->write(memReq, data);
        if (dcacheInterface) {
            memReq->cmd = Copy;
            memReq->completionEvent = NULL;
            memReq->paddr = thread->copySrcPhysAddr;
            memReq->dest = dest_addr;
            memReq->size = 64;
            memReq->time = curTick;
            memReq->flags &= ~INST_READ;
            dcacheInterface->access(memReq);
        }
    }
    else
        assert(!fault->isAlignmentFault());

    return fault;
#else
    panic("copy not implemented");
    return NoFault;
#endif
}

#if FULL_SYSTEM
Addr
BaseSimpleCPU::dbg_vtophys(Addr addr)
{
    return vtophys(tc, addr);
}
#endif // FULL_SYSTEM

#if FULL_SYSTEM
void
BaseSimpleCPU::post_interrupt(int int_num, int index)
{
    BaseCPU::post_interrupt(int_num, index);

    if (thread->status() == ThreadContext::Suspended) {
                DPRINTF(IPI,"Suspended Processor awoke\n");
        thread->activate();
    }
}
#endif // FULL_SYSTEM

void
BaseSimpleCPU::checkForInterrupts()
{
#if FULL_SYSTEM
    if (checkInterrupts && check_interrupts(tc)) {
        Fault interrupt = interrupts.getInterrupt(tc);

        if (interrupt != NoFault) {
            interrupts.updateIntrInfo(tc);
            checkInterrupts = false;
            interrupt->invoke(tc);
        }
    }
#endif
}

Fault
BaseSimpleCPU::setupFetchRequest(Request *req)
{
    // set up memory request for instruction fetch
#if ISA_HAS_DELAY_SLOT
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",thread->readPC(),
            thread->readNextPC(),thread->readNextNPC());
#else
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",thread->readPC(),
            thread->readNextPC());
#endif

    req->setVirt(0, thread->readPC() & ~3, sizeof(MachInst),
                 (FULL_SYSTEM && (thread->readPC() & 1)) ? PHYSICAL : 0,
                 thread->readPC());

    Fault fault = thread->translateInstReq(req);

    return fault;
}

void
BaseSimpleCPU::preExecute()
{
    // maintain $r0 semantics
    thread->setIntReg(ZeroReg, 0);
#if THE_ISA == ALPHA_ISA
    thread->setFloatReg(ZeroReg, 0.0);
#endif // ALPHA_ISA

    // keep an instruction count
    numInst++;
    numInsts++;

    thread->funcExeInst++;

    // check for instruction-count-based events
    comInstEventQueue[0]->serviceEvents(numInst);

    // decode the instruction
    inst = gtoh(inst);
    //If we're not in the middle of a macro instruction
    if (!curMacroStaticInst) {
#if THE_ISA == ALPHA_ISA
        StaticInstPtr instPtr = StaticInst::decode(makeExtMI(inst, thread->readPC()));
#elif THE_ISA == SPARC_ISA
        StaticInstPtr instPtr = StaticInst::decode(makeExtMI(inst, thread->getTC()));
#elif THE_ISA == MIPS_ISA
        //Mips doesn't do anything in it's MakeExtMI function right now,
        //so it won't be called.
        StaticInstPtr instPtr = StaticInst::decode(inst);
#endif
        if (instPtr->isMacroOp()) {
            curMacroStaticInst = instPtr;
            curStaticInst = curMacroStaticInst->
                fetchMicroOp(thread->readMicroPC());
        } else {
            curStaticInst = instPtr;
        }
    } else {
        //Read the next micro op from the macro op
        curStaticInst = curMacroStaticInst->
            fetchMicroOp(thread->readMicroPC());
    }

    traceData = Trace::getInstRecord(curTick, tc, curStaticInst,
                                     thread->readPC());

    DPRINTF(Decode,"Decode: Decoded %s instruction (opcode: 0x%x): 0x%x\n",
            curStaticInst->getName(), curStaticInst->getOpcode(),
            curStaticInst->machInst);

#if FULL_SYSTEM
    thread->setInst(inst);
#endif // FULL_SYSTEM
}

void
BaseSimpleCPU::postExecute()
{
#if FULL_SYSTEM
    if (thread->profile) {
        bool usermode = TheISA::inUserMode(tc);
        thread->profilePC = usermode ? 1 : thread->readPC();
        ProfileNode *node = thread->profile->consume(tc, inst);
        if (node)
            thread->profileNode = node;
    }
#endif

    if (curStaticInst->isMemRef()) {
        numMemRefs++;
    }

    if (curStaticInst->isLoad()) {
        ++numLoad;
        comLoadEventQueue[0]->serviceEvents(numLoad);
    }

    traceFunctions(thread->readPC());

    if (traceData) {
        traceData->finalize();
    }
}

void
BaseSimpleCPU::advancePC(Fault fault)
{
    if (fault != NoFault) {
        curMacroStaticInst = StaticInst::nullStaticInstPtr;
        fault->invoke(tc);
    } else {
        //If we're at the last micro op for this instruction
        if (curStaticInst->isLastMicroOp()) {
            //We should be working with a macro op
            assert(curMacroStaticInst);
            //Close out this macro op, and clean up the
            //microcode state
            curMacroStaticInst = StaticInst::nullStaticInstPtr;
            thread->setMicroPC(0);
            thread->setNextMicroPC(1);
        }
        //If we're still in a macro op
        if (curMacroStaticInst) {
            //Advance the micro pc
            thread->setMicroPC(thread->readNextMicroPC());
            //Advance the "next" micro pc. Note that there are no delay
            //slots, and micro ops are "word" addressed.
            thread->setNextMicroPC(thread->readNextMicroPC() + 1);
        } else {
            // go to the next instruction
            thread->setPC(thread->readNextPC());
#if ISA_HAS_DELAY_SLOT
            thread->setNextPC(thread->readNextNPC());
            thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst));
            assert(thread->readNextPC() != thread->readNextNPC());
#else
            thread->setNextPC(thread->readNextPC() + sizeof(MachInst));
#endif
        }
    }

#if FULL_SYSTEM
    Addr oldpc;
    do {
        oldpc = thread->readPC();
        system->pcEventQueue.service(tc);
    } while (oldpc != thread->readPC());
#endif
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * 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 the copyright holders 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 COPYRIGHT HOLDERS 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 COPYRIGHT
 * OWNER 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.
 *
 * Authors: Steve Reinhardt
 */

#include "arch/utility.hh"
#include "arch/faults.hh"
#include "base/cprintf.hh"
#include "base/inifile.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/pollevent.hh"
#include "base/range.hh"
#include "base/stats/events.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "cpu/exetrace.hh"
#include "cpu/profile.hh"
#include "cpu/simple/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/smt.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "mem/packet.hh"
#include "sim/builder.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/host.hh"
#include "sim/sim_events.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

#if FULL_SYSTEM
#include "arch/kernel_stats.hh"
#include "arch/stacktrace.hh"
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#include "base/remote_gdb.hh"
#else // !FULL_SYSTEM
#include "mem/mem_object.hh"
#endif // FULL_SYSTEM

using namespace std;
using namespace TheISA;

BaseSimpleCPU::BaseSimpleCPU(Params *p)
    : BaseCPU(p), thread(NULL), predecoder(NULL)
{
#if FULL_SYSTEM
    thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb);
#else
    thread = new SimpleThread(this, /* thread_num */ 0, p->process,
            /* asid */ 0);
#endif // !FULL_SYSTEM

    thread->setStatus(ThreadContext::Suspended);

    tc = thread->getTC();

    numInst = 0;
    startNumInst = 0;
    numLoad = 0;
    startNumLoad = 0;
    lastIcacheStall = 0;
    lastDcacheStall = 0;

    threadContexts.push_back(tc);
}

BaseSimpleCPU::~BaseSimpleCPU()
{
}

void
BaseSimpleCPU::deallocateContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::haltContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::regStats()
{
    using namespace Stats;

    BaseCPU::regStats();

    numInsts
        .name(name() + ".num_insts")
        .desc("Number of instructions executed")
        ;

    numMemRefs
        .name(name() + ".num_refs")
        .desc("Number of memory references")
        ;

    notIdleFraction
        .name(name() + ".not_idle_fraction")
        .desc("Percentage of non-idle cycles")
        ;

    idleFraction
        .name(name() + ".idle_fraction")
        .desc("Percentage of idle cycles")
        ;

    icacheStallCycles
        .name(name() + ".icache_stall_cycles")
        .desc("ICache total stall cycles")
        .prereq(icacheStallCycles)
        ;

    dcacheStallCycles
        .name(name() + ".dcache_stall_cycles")
        .desc("DCache total stall cycles")
        .prereq(dcacheStallCycles)
        ;

    icacheRetryCycles
        .name(name() + ".icache_retry_cycles")
        .desc("ICache total retry cycles")
        .prereq(icacheRetryCycles)
        ;

    dcacheRetryCycles
        .name(name() + ".dcache_retry_cycles")
        .desc("DCache total retry cycles")
        .prereq(dcacheRetryCycles)
        ;

    idleFraction = constant(1.0) - notIdleFraction;
}

void
BaseSimpleCPU::resetStats()
{
//    startNumInst = numInst;
    // notIdleFraction = (_status != Idle);
}

void
BaseSimpleCPU::serialize(ostream &os)
{
    BaseCPU::serialize(os);
//    SERIALIZE_SCALAR(inst);
    nameOut(os, csprintf("%s.xc.0", name()));
    thread->serialize(os);
}

void
BaseSimpleCPU::unserialize(Checkpoint *cp, const string &section)
{
    BaseCPU::unserialize(cp, section);
//    UNSERIALIZE_SCALAR(inst);
    thread->unserialize(cp, csprintf("%s.xc.0", section));
}

void
change_thread_state(int thread_number, int activate, int priority)
{
}

Fault
BaseSimpleCPU::copySrcTranslate(Addr src)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    int offset = src & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (src & PageMask) != ((src + blk_size) & PageMask) &&
        (src >> 40) != 0xfffffc) {
        warn("Copied block source spans pages %x.", src);
        no_warn = false;
    }

    memReq->reset(src & ~(blk_size - 1), blk_size);

    // translate to physical address
    Fault fault = thread->translateDataReadReq(req);

    if (fault == NoFault) {
        thread->copySrcAddr = src;
        thread->copySrcPhysAddr = memReq->paddr + offset;
    } else {
        assert(!fault->isAlignmentFault());

        thread->copySrcAddr = 0;
        thread->copySrcPhysAddr = 0;
    }
    return fault;
#else
    return NoFault;
#endif
}

Fault
BaseSimpleCPU::copy(Addr dest)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    uint8_t data[blk_size];
    //assert(thread->copySrcAddr);
    int offset = dest & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (dest & PageMask) != ((dest + blk_size) & PageMask) &&
        (dest >> 40) != 0xfffffc) {
        no_warn = false;
        warn("Copied block destination spans pages %x. ", dest);
    }

    memReq->reset(dest & ~(blk_size -1), blk_size);
    // translate to physical address
    Fault fault = thread->translateDataWriteReq(req);

    if (fault == NoFault) {
        Addr dest_addr = memReq->paddr + offset;
        // Need to read straight from memory since we have more than 8 bytes.
        memReq->paddr = thread->copySrcPhysAddr;
        thread->mem->read(memReq, data);
        memReq->paddr = dest_addr;
        thread->mem->write(memReq, data);
        if (dcacheInterface) {
            memReq->cmd = Copy;
            memReq->completionEvent = NULL;
            memReq->paddr = thread->copySrcPhysAddr;
            memReq->dest = dest_addr;
            memReq->size = 64;
            memReq->time = curTick;
            memReq->flags &= ~INST_READ;
            dcacheInterface->access(memReq);
        }
    }
    else
        assert(!fault->isAlignmentFault());

    return fault;
#else
    panic("copy not implemented");
    return NoFault;
#endif
}

#if FULL_SYSTEM
Addr
BaseSimpleCPU::dbg_vtophys(Addr addr)
{
    return vtophys(tc, addr);
}
#endif // FULL_SYSTEM

#if FULL_SYSTEM
void
BaseSimpleCPU::post_interrupt(int int_num, int index)
{
    BaseCPU::post_interrupt(int_num, index);

    if (thread->status() == ThreadContext::Suspended) {
                DPRINTF(Quiesce,"Suspended Processor awoke\n");
        thread->activate();
    }
}
#endif // FULL_SYSTEM

void
BaseSimpleCPU::checkForInterrupts()
{
#if FULL_SYSTEM
    if (check_interrupts(tc)) {
        Fault interrupt = interrupts.getInterrupt(tc);

        if (interrupt != NoFault) {
            interrupts.updateIntrInfo(tc);
            interrupt->invoke(tc);
        }
    }
#endif
}

Fault
BaseSimpleCPU::setupFetchRequest(Request *req)
{
    // set up memory request for instruction fetch
#if ISA_HAS_DELAY_SLOT
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",thread->readPC(),
            thread->readNextPC(),thread->readNextNPC());
#else
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",thread->readPC(),
            thread->readNextPC());
#endif

    req->setVirt(0, thread->readPC() & ~3, sizeof(MachInst),
                 (FULL_SYSTEM && (thread->readPC() & 1)) ? PHYSICAL : 0,
                 thread->readPC());

    Fault fault = thread->translateInstReq(req);

    return fault;
}

void
BaseSimpleCPU::preExecute()
{
    // maintain $r0 semantics
    thread->setIntReg(ZeroReg, 0);
#if THE_ISA == ALPHA_ISA
    thread->setFloatReg(ZeroReg, 0.0);
#endif // ALPHA_ISA

    // keep an instruction count
    numInst++;
    numInsts++;

    thread->funcExeInst++;

    // check for instruction-count-based events
    comInstEventQueue[0]->serviceEvents(numInst);

    // decode the instruction
    inst = gtoh(inst);
    //If we're not in the middle of a macro instruction
    if (!curMacroStaticInst) {
        StaticInstPtr instPtr = NULL;

        //Predecode, ie bundle up an ExtMachInst
        //This should go away once the constructor can be set up properly
        predecoder.setTC(thread->getTC());
        //If more fetch data is needed, pass it in.
        if(predecoder.needMoreBytes())
            predecoder.moreBytes(thread->readPC(), 0, inst);
        else
            predecoder.process();
        //If an instruction is ready, decode it
        if (predecoder.extMachInstReady())
            instPtr = StaticInst::decode(predecoder.getExtMachInst());

        //If we decoded an instruction and it's microcoded, start pulling
        //out micro ops
        if (instPtr && instPtr->isMacroOp()) {
            curMacroStaticInst = instPtr;
            curStaticInst = curMacroStaticInst->
                fetchMicroOp(thread->readMicroPC());
        } else {
            curStaticInst = instPtr;
        }
    } else {
        //Read the next micro op from the macro op
        curStaticInst = curMacroStaticInst->
            fetchMicroOp(thread->readMicroPC());
    }

    //If we decoded an instruction this "tick", record information about it.
    if(curStaticInst)
    {
        traceData = Trace::getInstRecord(curTick, tc, curStaticInst,
                                         thread->readPC());

        DPRINTF(Decode,"Decode: Decoded %s instruction: 0x%x\n",
                curStaticInst->getName(), curStaticInst->machInst);

#if FULL_SYSTEM
        thread->setInst(inst);
#endif // FULL_SYSTEM
    }
}

void
BaseSimpleCPU::postExecute()
{
#if FULL_SYSTEM
    if (thread->profile) {
        bool usermode = TheISA::inUserMode(tc);
        thread->profilePC = usermode ? 1 : thread->readPC();
        ProfileNode *node = thread->profile->consume(tc, inst);
        if (node)
            thread->profileNode = node;
    }
#endif

    if (curStaticInst->isMemRef()) {
        numMemRefs++;
    }

    if (curStaticInst->isLoad()) {
        ++numLoad;
        comLoadEventQueue[0]->serviceEvents(numLoad);
    }

    traceFunctions(thread->readPC());

    if (traceData) {
        traceData->dump();
        delete traceData;
        traceData = NULL;
    }
}

void
BaseSimpleCPU::advancePC(Fault fault)
{
    if (fault != NoFault) {
        curMacroStaticInst = StaticInst::nullStaticInstPtr;
        fault->invoke(tc);
        thread->setMicroPC(0);
        thread->setNextMicroPC(1);
    } else if (predecoder.needMoreBytes()) {
        //If we're at the last micro op for this instruction
        if (curStaticInst && curStaticInst->isLastMicroOp()) {
            //We should be working with a macro op
            assert(curMacroStaticInst);
            //Close out this macro op, and clean up the
            //microcode state
            curMacroStaticInst = StaticInst::nullStaticInstPtr;
            thread->setMicroPC(0);
            thread->setNextMicroPC(1);
        }
        //If we're still in a macro op
        if (curMacroStaticInst) {
            //Advance the micro pc
            thread->setMicroPC(thread->readNextMicroPC());
            //Advance the "next" micro pc. Note that there are no delay
            //slots, and micro ops are "word" addressed.
            thread->setNextMicroPC(thread->readNextMicroPC() + 1);
        } else {
            // go to the next instruction
            thread->setPC(thread->readNextPC());
            thread->setNextPC(thread->readNextNPC());
            thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst));
            assert(thread->readNextPC() != thread->readNextNPC());
        }
    }

#if FULL_SYSTEM
    Addr oldpc;
    do {
        oldpc = thread->readPC();
        system->pcEventQueue.service(tc);
    } while (oldpc != thread->readPC());
#endif
}

<commit_msg>Fix ALPHA_FS compile. The MachInst -> StaticInstPtr constructor is no longer a conversion constructor because it caused ambiguous conversions when setting the pointer to NULL.<commit_after>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * 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 the copyright holders 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 COPYRIGHT HOLDERS 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 COPYRIGHT
 * OWNER 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.
 *
 * Authors: Steve Reinhardt
 */

#include "arch/utility.hh"
#include "arch/faults.hh"
#include "base/cprintf.hh"
#include "base/inifile.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/pollevent.hh"
#include "base/range.hh"
#include "base/stats/events.hh"
#include "base/trace.hh"
#include "cpu/base.hh"
#include "cpu/exetrace.hh"
#include "cpu/profile.hh"
#include "cpu/simple/base.hh"
#include "cpu/simple_thread.hh"
#include "cpu/smt.hh"
#include "cpu/static_inst.hh"
#include "cpu/thread_context.hh"
#include "mem/packet.hh"
#include "sim/builder.hh"
#include "sim/byteswap.hh"
#include "sim/debug.hh"
#include "sim/host.hh"
#include "sim/sim_events.hh"
#include "sim/sim_object.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

#if FULL_SYSTEM
#include "arch/kernel_stats.hh"
#include "arch/stacktrace.hh"
#include "arch/tlb.hh"
#include "arch/vtophys.hh"
#include "base/remote_gdb.hh"
#else // !FULL_SYSTEM
#include "mem/mem_object.hh"
#endif // FULL_SYSTEM

using namespace std;
using namespace TheISA;

BaseSimpleCPU::BaseSimpleCPU(Params *p)
    : BaseCPU(p), thread(NULL), predecoder(NULL)
{
#if FULL_SYSTEM
    thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb);
#else
    thread = new SimpleThread(this, /* thread_num */ 0, p->process,
            /* asid */ 0);
#endif // !FULL_SYSTEM

    thread->setStatus(ThreadContext::Suspended);

    tc = thread->getTC();

    numInst = 0;
    startNumInst = 0;
    numLoad = 0;
    startNumLoad = 0;
    lastIcacheStall = 0;
    lastDcacheStall = 0;

    threadContexts.push_back(tc);
}

BaseSimpleCPU::~BaseSimpleCPU()
{
}

void
BaseSimpleCPU::deallocateContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::haltContext(int thread_num)
{
    // for now, these are equivalent
    suspendContext(thread_num);
}

void
BaseSimpleCPU::regStats()
{
    using namespace Stats;

    BaseCPU::regStats();

    numInsts
        .name(name() + ".num_insts")
        .desc("Number of instructions executed")
        ;

    numMemRefs
        .name(name() + ".num_refs")
        .desc("Number of memory references")
        ;

    notIdleFraction
        .name(name() + ".not_idle_fraction")
        .desc("Percentage of non-idle cycles")
        ;

    idleFraction
        .name(name() + ".idle_fraction")
        .desc("Percentage of idle cycles")
        ;

    icacheStallCycles
        .name(name() + ".icache_stall_cycles")
        .desc("ICache total stall cycles")
        .prereq(icacheStallCycles)
        ;

    dcacheStallCycles
        .name(name() + ".dcache_stall_cycles")
        .desc("DCache total stall cycles")
        .prereq(dcacheStallCycles)
        ;

    icacheRetryCycles
        .name(name() + ".icache_retry_cycles")
        .desc("ICache total retry cycles")
        .prereq(icacheRetryCycles)
        ;

    dcacheRetryCycles
        .name(name() + ".dcache_retry_cycles")
        .desc("DCache total retry cycles")
        .prereq(dcacheRetryCycles)
        ;

    idleFraction = constant(1.0) - notIdleFraction;
}

void
BaseSimpleCPU::resetStats()
{
//    startNumInst = numInst;
    // notIdleFraction = (_status != Idle);
}

void
BaseSimpleCPU::serialize(ostream &os)
{
    BaseCPU::serialize(os);
//    SERIALIZE_SCALAR(inst);
    nameOut(os, csprintf("%s.xc.0", name()));
    thread->serialize(os);
}

void
BaseSimpleCPU::unserialize(Checkpoint *cp, const string &section)
{
    BaseCPU::unserialize(cp, section);
//    UNSERIALIZE_SCALAR(inst);
    thread->unserialize(cp, csprintf("%s.xc.0", section));
}

void
change_thread_state(int thread_number, int activate, int priority)
{
}

Fault
BaseSimpleCPU::copySrcTranslate(Addr src)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    int offset = src & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (src & PageMask) != ((src + blk_size) & PageMask) &&
        (src >> 40) != 0xfffffc) {
        warn("Copied block source spans pages %x.", src);
        no_warn = false;
    }

    memReq->reset(src & ~(blk_size - 1), blk_size);

    // translate to physical address
    Fault fault = thread->translateDataReadReq(req);

    if (fault == NoFault) {
        thread->copySrcAddr = src;
        thread->copySrcPhysAddr = memReq->paddr + offset;
    } else {
        assert(!fault->isAlignmentFault());

        thread->copySrcAddr = 0;
        thread->copySrcPhysAddr = 0;
    }
    return fault;
#else
    return NoFault;
#endif
}

Fault
BaseSimpleCPU::copy(Addr dest)
{
#if 0
    static bool no_warn = true;
    int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64;
    // Only support block sizes of 64 atm.
    assert(blk_size == 64);
    uint8_t data[blk_size];
    //assert(thread->copySrcAddr);
    int offset = dest & (blk_size - 1);

    // Make sure block doesn't span page
    if (no_warn &&
        (dest & PageMask) != ((dest + blk_size) & PageMask) &&
        (dest >> 40) != 0xfffffc) {
        no_warn = false;
        warn("Copied block destination spans pages %x. ", dest);
    }

    memReq->reset(dest & ~(blk_size -1), blk_size);
    // translate to physical address
    Fault fault = thread->translateDataWriteReq(req);

    if (fault == NoFault) {
        Addr dest_addr = memReq->paddr + offset;
        // Need to read straight from memory since we have more than 8 bytes.
        memReq->paddr = thread->copySrcPhysAddr;
        thread->mem->read(memReq, data);
        memReq->paddr = dest_addr;
        thread->mem->write(memReq, data);
        if (dcacheInterface) {
            memReq->cmd = Copy;
            memReq->completionEvent = NULL;
            memReq->paddr = thread->copySrcPhysAddr;
            memReq->dest = dest_addr;
            memReq->size = 64;
            memReq->time = curTick;
            memReq->flags &= ~INST_READ;
            dcacheInterface->access(memReq);
        }
    }
    else
        assert(!fault->isAlignmentFault());

    return fault;
#else
    panic("copy not implemented");
    return NoFault;
#endif
}

#if FULL_SYSTEM
Addr
BaseSimpleCPU::dbg_vtophys(Addr addr)
{
    return vtophys(tc, addr);
}
#endif // FULL_SYSTEM

#if FULL_SYSTEM
void
BaseSimpleCPU::post_interrupt(int int_num, int index)
{
    BaseCPU::post_interrupt(int_num, index);

    if (thread->status() == ThreadContext::Suspended) {
                DPRINTF(Quiesce,"Suspended Processor awoke\n");
        thread->activate();
    }
}
#endif // FULL_SYSTEM

void
BaseSimpleCPU::checkForInterrupts()
{
#if FULL_SYSTEM
    if (check_interrupts(tc)) {
        Fault interrupt = interrupts.getInterrupt(tc);

        if (interrupt != NoFault) {
            interrupts.updateIntrInfo(tc);
            interrupt->invoke(tc);
        }
    }
#endif
}

Fault
BaseSimpleCPU::setupFetchRequest(Request *req)
{
    // set up memory request for instruction fetch
#if ISA_HAS_DELAY_SLOT
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",thread->readPC(),
            thread->readNextPC(),thread->readNextNPC());
#else
    DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",thread->readPC(),
            thread->readNextPC());
#endif

    req->setVirt(0, thread->readPC() & ~3, sizeof(MachInst),
                 (FULL_SYSTEM && (thread->readPC() & 1)) ? PHYSICAL : 0,
                 thread->readPC());

    Fault fault = thread->translateInstReq(req);

    return fault;
}

void
BaseSimpleCPU::preExecute()
{
    // maintain $r0 semantics
    thread->setIntReg(ZeroReg, 0);
#if THE_ISA == ALPHA_ISA
    thread->setFloatReg(ZeroReg, 0.0);
#endif // ALPHA_ISA

    // keep an instruction count
    numInst++;
    numInsts++;

    thread->funcExeInst++;

    // check for instruction-count-based events
    comInstEventQueue[0]->serviceEvents(numInst);

    // decode the instruction
    inst = gtoh(inst);
    //If we're not in the middle of a macro instruction
    if (!curMacroStaticInst) {
        StaticInstPtr instPtr = NULL;

        //Predecode, ie bundle up an ExtMachInst
        //This should go away once the constructor can be set up properly
        predecoder.setTC(thread->getTC());
        //If more fetch data is needed, pass it in.
        if(predecoder.needMoreBytes())
            predecoder.moreBytes(thread->readPC(), 0, inst);
        else
            predecoder.process();
        //If an instruction is ready, decode it
        if (predecoder.extMachInstReady())
            instPtr = StaticInst::decode(predecoder.getExtMachInst());

        //If we decoded an instruction and it's microcoded, start pulling
        //out micro ops
        if (instPtr && instPtr->isMacroOp()) {
            curMacroStaticInst = instPtr;
            curStaticInst = curMacroStaticInst->
                fetchMicroOp(thread->readMicroPC());
        } else {
            curStaticInst = instPtr;
        }
    } else {
        //Read the next micro op from the macro op
        curStaticInst = curMacroStaticInst->
            fetchMicroOp(thread->readMicroPC());
    }

    //If we decoded an instruction this "tick", record information about it.
    if(curStaticInst)
    {
        traceData = Trace::getInstRecord(curTick, tc, curStaticInst,
                                         thread->readPC());

        DPRINTF(Decode,"Decode: Decoded %s instruction: 0x%x\n",
                curStaticInst->getName(), curStaticInst->machInst);

#if FULL_SYSTEM
        thread->setInst(inst);
#endif // FULL_SYSTEM
    }
}

void
BaseSimpleCPU::postExecute()
{
#if FULL_SYSTEM
    if (thread->profile) {
        bool usermode = TheISA::inUserMode(tc);
        thread->profilePC = usermode ? 1 : thread->readPC();
        StaticInstPtr si(inst);
        ProfileNode *node = thread->profile->consume(tc, si);
        if (node)
            thread->profileNode = node;
    }
#endif

    if (curStaticInst->isMemRef()) {
        numMemRefs++;
    }

    if (curStaticInst->isLoad()) {
        ++numLoad;
        comLoadEventQueue[0]->serviceEvents(numLoad);
    }

    traceFunctions(thread->readPC());

    if (traceData) {
        traceData->dump();
        delete traceData;
        traceData = NULL;
    }
}

void
BaseSimpleCPU::advancePC(Fault fault)
{
    if (fault != NoFault) {
        curMacroStaticInst = StaticInst::nullStaticInstPtr;
        fault->invoke(tc);
        thread->setMicroPC(0);
        thread->setNextMicroPC(1);
    } else if (predecoder.needMoreBytes()) {
        //If we're at the last micro op for this instruction
        if (curStaticInst && curStaticInst->isLastMicroOp()) {
            //We should be working with a macro op
            assert(curMacroStaticInst);
            //Close out this macro op, and clean up the
            //microcode state
            curMacroStaticInst = StaticInst::nullStaticInstPtr;
            thread->setMicroPC(0);
            thread->setNextMicroPC(1);
        }
        //If we're still in a macro op
        if (curMacroStaticInst) {
            //Advance the micro pc
            thread->setMicroPC(thread->readNextMicroPC());
            //Advance the "next" micro pc. Note that there are no delay
            //slots, and micro ops are "word" addressed.
            thread->setNextMicroPC(thread->readNextMicroPC() + 1);
        } else {
            // go to the next instruction
            thread->setPC(thread->readNextPC());
            thread->setNextPC(thread->readNextNPC());
            thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst));
            assert(thread->readNextPC() != thread->readNextNPC());
        }
    }

#if FULL_SYSTEM
    Addr oldpc;
    do {
        oldpc = thread->readPC();
        system->pcEventQueue.service(tc);
    } while (oldpc != thread->readPC());
#endif
}

<|endoftext|>


<commit_before>/*
 *  This file is part of Poedit (http://poedit.net)
 *
 *  Copyright (C) 2015 Vaclav Slavik
 *
 *  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 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.
 *
 */

#include "customcontrols.h"

#include "concurrency.h"
#include "errors.h"
#include "hidpi.h"

#include <wx/app.h>
#include <wx/clipbrd.h>
#include <wx/menu.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/weakref.h>
#include <wx/wupdlock.h>

#if wxCHECK_VERSION(3,1,0)
    #include <wx/activityindicator.h>
#else
    #include "wx_backports/activityindicator.h"
#endif

#include <unicode/brkiter.h>
#ifdef __WXGTK__
#include <gtk/gtk.h>
#endif

#include "str_helpers.h"

#include <memory>

namespace
{

wxString WrapTextAtWidth(const wxString& text_, int width, wxWindow *wnd)
{
    if (text_.empty())
        return text_;
    auto text = str::to_icu(text_);

    static std::unique_ptr<icu::BreakIterator> iter;
    if (!iter)
    {
        UErrorCode err = U_ZERO_ERROR;
        iter.reset(icu::BreakIterator::createLineInstance(icu::Locale(), err));
    }

    iter->setText(text);

    wxString out;
    out.reserve(text_.length() + 10);

    int32_t lineStart = 0;
    wxString previousSubstr;

    for (int32_t pos = iter->next(); pos != icu::BreakIterator::DONE; pos = iter->next())
    {
        auto substr = str::to_wx(text.tempSubStringBetween(lineStart, pos));

        if (wnd->GetTextExtent(substr).x > width)
        {
            auto previousPos = iter->previous();
            if (previousPos == lineStart || previousPos == icu::BreakIterator::DONE)
            {
                // line is too large but we can't break it, so have no choice but not to wrap
                out += substr;
                lineStart = pos;
            }
            else
            {
                // need to wrap at previous linebreak position
                out += previousSubstr;
                lineStart = previousPos;
            }
            out += '\n';
            previousSubstr.clear();
        }
        else if (pos > 0 && text[pos-1] == '\n') // forced line feed
        {
            out += substr;
            lineStart = pos;
            previousSubstr.clear();
        }
        else
        {
            previousSubstr = substr;
        }
    }

    if (!previousSubstr.empty())
    {
        out += previousSubstr;
    }

    if (out.Last() == '\n')
        out.RemoveLast();

    return out;
}

} // anonymous namespace

AutoWrappingText::AutoWrappingText(wxWindow *parent, const wxString& label)
    : wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE),
      m_text(label),
      m_wrapWidth(-1)
{
    m_text.Replace("\n", " ");

    SetInitialSize(wxSize(10,10));
    Bind(wxEVT_SIZE, &ExplanationLabel::OnSize, this);
}

void AutoWrappingText::SetAlignment(int align)
{
    if (GetWindowStyleFlag() & align)
        return;
    SetWindowStyleFlag(wxST_NO_AUTORESIZE | align);
}

void AutoWrappingText::SetAndWrapLabel(const wxString& label)
{
    wxWindowUpdateLocker lock(this);
    m_text = label;
    m_wrapWidth = GetSize().x;
    SetLabelText(WrapTextAtWidth(label, m_wrapWidth, this));

    InvalidateBestSize();
    SetMinSize(wxDefaultSize);
    SetMinSize(GetBestSize());
}

void AutoWrappingText::OnSize(wxSizeEvent& e)
{
    e.Skip();
    int w = wxMax(0, e.GetSize().x - PX(4));
    if (w == m_wrapWidth)
        return;

    wxWindowUpdateLocker lock(this);

    m_wrapWidth = w;
    SetLabel(WrapTextAtWidth(m_text, w, this));

    InvalidateBestSize();
    SetMinSize(wxDefaultSize);
    SetMinSize(GetBestSize());
}

SelectableAutoWrappingText::SelectableAutoWrappingText(wxWindow *parent, const wxString& label)
    : AutoWrappingText(parent, label)
{
#if defined(__WXOSX__)
    NSTextField *view = (NSTextField*)GetHandle();
    [view setSelectable:YES];
#elif defined(__WXGTK__)
    GtkLabel *view = GTK_LABEL(GetHandle());
    gtk_label_set_selectable(view, TRUE);
#else
    // at least allow copying
    static wxWindowID idCopy = wxNewId();
    Bind(wxEVT_CONTEXT_MENU, [=](wxContextMenuEvent&){
        wxMenu *menu = new wxMenu();
        menu->Append(idCopy, _("&Copy"));
        PopupMenu(menu);
    });
    Bind(wxEVT_MENU, [=](wxCommandEvent&){
        wxClipboardLocker lock;
        wxClipboard::Get()->SetData(new wxTextDataObject(m_text));
    }, idCopy);
#endif
}

ExplanationLabel::ExplanationLabel(wxWindow *parent, const wxString& label)
    : AutoWrappingText(parent, label)
{
#if defined(__WXOSX__) || defined(__WXGTK__)
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#endif
#ifndef __WXGTK__
    SetForegroundColour(GetTextColor());
#endif
}

wxColour ExplanationLabel::GetTextColor()
{
#if defined(__WXOSX__)
    return wxColour("#777777");
#elif defined(__WXGTK__)
    return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
#else
    return wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
#endif
}

SecondaryLabel::SecondaryLabel(wxWindow *parent, const wxString& label)
    : wxStaticText(parent, wxID_ANY, label)
{
#if defined(__WXOSX__) || defined(__WXGTK__)
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#endif
#ifndef __WXGTK__
    SetForegroundColour(GetTextColor());
#endif
}

LearnMoreLink::LearnMoreLink(wxWindow *parent, const wxString& url, wxString label, wxWindowID winid)
{
    if (label.empty())
    {
#ifdef __WXMSW__
        label = _("Learn more");
#else
        label = _("Learn More");
#endif
    }

    wxHyperlinkCtrl::Create(parent, winid, label, url);
    SetNormalColour("#2F79BE");
    SetVisitedColour("#2F79BE");
    SetHoverColour("#3D8DD5");

#ifdef __WXOSX__
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
    SetFont(GetFont().Underlined());
#endif
}

wxObject *LearnMoreLinkXmlHandler::DoCreateResource()
{
    auto w = new LearnMoreLink(m_parentAsWindow, GetText("url"), GetText("label"), GetID());
    w->SetName(GetName());
    SetupWindow(w);
    return w;
}

bool LearnMoreLinkXmlHandler::CanHandle(wxXmlNode *node)
{
    return IsOfClass(node, "LearnMoreLink");
}

ActivityIndicator::ActivityIndicator(wxWindow *parent)
    : wxWindow(parent, wxID_ANY), m_running(false)
{
    auto sizer = new wxBoxSizer(wxHORIZONTAL);
    SetSizer(sizer);

    m_spinner = new wxActivityIndicator(this, wxID_ANY);
    m_spinner->SetWindowVariant(wxWINDOW_VARIANT_SMALL);
    m_label = new wxStaticText(this, wxID_ANY, "");
#ifdef __WXOSX__
    m_label->SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#endif

    sizer->Add(m_spinner, wxSizerFlags().Center().Border(wxRIGHT, PX(4)));
    sizer->Add(m_label, wxSizerFlags(1).Center());

    HandleError = on_main_thread_for_window<std::exception_ptr>(this, [=](std::exception_ptr e){
        StopWithError(DescribeException(e));
    });
}

void ActivityIndicator::Start(const wxString& msg)
{
    m_running = true;

    m_label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
    m_label->SetLabel(msg);

    auto sizer = GetSizer();
    sizer->Show(m_spinner);
    sizer->Show(m_label, !msg.empty());
    Layout();

    m_spinner->Start();
}

void ActivityIndicator::Stop()
{
    m_running = false;

    m_spinner->Stop();
    m_label->SetLabel("");

    auto sizer = GetSizer();
    sizer->Hide(m_spinner);
    sizer->Hide(m_label);
    Layout();
}

void ActivityIndicator::StopWithError(const wxString& msg)
{
    m_running = false;

    m_spinner->Stop();
    m_label->SetForegroundColour(*wxRED);
    m_label->SetLabel(msg);

    auto sizer = GetSizer();
    sizer->Hide(m_spinner);
    sizer->Show(m_label);
    Layout();
}
<commit_msg>Use correct explanation text size even with wxGTK 3.0<commit_after>/*
 *  This file is part of Poedit (http://poedit.net)
 *
 *  Copyright (C) 2015 Vaclav Slavik
 *
 *  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 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.
 *
 */

#include "customcontrols.h"

#include "concurrency.h"
#include "errors.h"
#include "hidpi.h"

#include <wx/app.h>
#include <wx/clipbrd.h>
#include <wx/menu.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/weakref.h>
#include <wx/wupdlock.h>

#if wxCHECK_VERSION(3,1,0)
    #include <wx/activityindicator.h>
#else
    #include "wx_backports/activityindicator.h"
#endif

#include <unicode/brkiter.h>
#ifdef __WXGTK__
#include <gtk/gtk.h>
#endif

#include "str_helpers.h"

#include <memory>

namespace
{

wxString WrapTextAtWidth(const wxString& text_, int width, wxWindow *wnd)
{
    if (text_.empty())
        return text_;
    auto text = str::to_icu(text_);

    static std::unique_ptr<icu::BreakIterator> iter;
    if (!iter)
    {
        UErrorCode err = U_ZERO_ERROR;
        iter.reset(icu::BreakIterator::createLineInstance(icu::Locale(), err));
    }

    iter->setText(text);

    wxString out;
    out.reserve(text_.length() + 10);

    int32_t lineStart = 0;
    wxString previousSubstr;

    for (int32_t pos = iter->next(); pos != icu::BreakIterator::DONE; pos = iter->next())
    {
        auto substr = str::to_wx(text.tempSubStringBetween(lineStart, pos));

        if (wnd->GetTextExtent(substr).x > width)
        {
            auto previousPos = iter->previous();
            if (previousPos == lineStart || previousPos == icu::BreakIterator::DONE)
            {
                // line is too large but we can't break it, so have no choice but not to wrap
                out += substr;
                lineStart = pos;
            }
            else
            {
                // need to wrap at previous linebreak position
                out += previousSubstr;
                lineStart = previousPos;
            }
            out += '\n';
            previousSubstr.clear();
        }
        else if (pos > 0 && text[pos-1] == '\n') // forced line feed
        {
            out += substr;
            lineStart = pos;
            previousSubstr.clear();
        }
        else
        {
            previousSubstr = substr;
        }
    }

    if (!previousSubstr.empty())
    {
        out += previousSubstr;
    }

    if (out.Last() == '\n')
        out.RemoveLast();

    return out;
}

} // anonymous namespace

AutoWrappingText::AutoWrappingText(wxWindow *parent, const wxString& label)
    : wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE),
      m_text(label),
      m_wrapWidth(-1)
{
    m_text.Replace("\n", " ");

    SetInitialSize(wxSize(10,10));
    Bind(wxEVT_SIZE, &ExplanationLabel::OnSize, this);
}

void AutoWrappingText::SetAlignment(int align)
{
    if (GetWindowStyleFlag() & align)
        return;
    SetWindowStyleFlag(wxST_NO_AUTORESIZE | align);
}

void AutoWrappingText::SetAndWrapLabel(const wxString& label)
{
    wxWindowUpdateLocker lock(this);
    m_text = label;
    m_wrapWidth = GetSize().x;
    SetLabelText(WrapTextAtWidth(label, m_wrapWidth, this));

    InvalidateBestSize();
    SetMinSize(wxDefaultSize);
    SetMinSize(GetBestSize());
}

void AutoWrappingText::OnSize(wxSizeEvent& e)
{
    e.Skip();
    int w = wxMax(0, e.GetSize().x - PX(4));
    if (w == m_wrapWidth)
        return;

    wxWindowUpdateLocker lock(this);

    m_wrapWidth = w;
    SetLabel(WrapTextAtWidth(m_text, w, this));

    InvalidateBestSize();
    SetMinSize(wxDefaultSize);
    SetMinSize(GetBestSize());
}

SelectableAutoWrappingText::SelectableAutoWrappingText(wxWindow *parent, const wxString& label)
    : AutoWrappingText(parent, label)
{
#if defined(__WXOSX__)
    NSTextField *view = (NSTextField*)GetHandle();
    [view setSelectable:YES];
#elif defined(__WXGTK__)
    GtkLabel *view = GTK_LABEL(GetHandle());
    gtk_label_set_selectable(view, TRUE);
#else
    // at least allow copying
    static wxWindowID idCopy = wxNewId();
    Bind(wxEVT_CONTEXT_MENU, [=](wxContextMenuEvent&){
        wxMenu *menu = new wxMenu();
        menu->Append(idCopy, _("&Copy"));
        PopupMenu(menu);
    });
    Bind(wxEVT_MENU, [=](wxCommandEvent&){
        wxClipboardLocker lock;
        wxClipboard::Get()->SetData(new wxTextDataObject(m_text));
    }, idCopy);
#endif
}

ExplanationLabel::ExplanationLabel(wxWindow *parent, const wxString& label)
    : AutoWrappingText(parent, label)
{
#if defined(__WXOSX__)
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#elif defined(__WXGTK__)
    #if wxCHECK_VERSION(3,1,0)
        SetWindowVariant(wxWINDOW_VARIANT_SMALL);
    #else
        SetFont(GetFont().Smaller());
    #endif
#endif
#ifndef __WXGTK__
    SetForegroundColour(GetTextColor());
#endif
}

wxColour ExplanationLabel::GetTextColor()
{
#if defined(__WXOSX__)
    return wxColour("#777777");
#elif defined(__WXGTK__)
    return wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
#else
    return wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
#endif
}

SecondaryLabel::SecondaryLabel(wxWindow *parent, const wxString& label)
    : wxStaticText(parent, wxID_ANY, label)
{
#if defined(__WXOSX__)
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#elif defined(__WXGTK__)
    #if wxCHECK_VERSION(3,1,0)
        SetWindowVariant(wxWINDOW_VARIANT_SMALL);
    #else
        SetFont(GetFont().Smaller());
    #endif
#endif
#ifndef __WXGTK__
    SetForegroundColour(GetTextColor());
#endif
}

LearnMoreLink::LearnMoreLink(wxWindow *parent, const wxString& url, wxString label, wxWindowID winid)
{
    if (label.empty())
    {
#ifdef __WXMSW__
        label = _("Learn more");
#else
        label = _("Learn More");
#endif
    }

    wxHyperlinkCtrl::Create(parent, winid, label, url);
    SetNormalColour("#2F79BE");
    SetVisitedColour("#2F79BE");
    SetHoverColour("#3D8DD5");

#ifdef __WXOSX__
    SetWindowVariant(wxWINDOW_VARIANT_SMALL);
    SetFont(GetFont().Underlined());
#endif
}

wxObject *LearnMoreLinkXmlHandler::DoCreateResource()
{
    auto w = new LearnMoreLink(m_parentAsWindow, GetText("url"), GetText("label"), GetID());
    w->SetName(GetName());
    SetupWindow(w);
    return w;
}

bool LearnMoreLinkXmlHandler::CanHandle(wxXmlNode *node)
{
    return IsOfClass(node, "LearnMoreLink");
}

ActivityIndicator::ActivityIndicator(wxWindow *parent)
    : wxWindow(parent, wxID_ANY), m_running(false)
{
    auto sizer = new wxBoxSizer(wxHORIZONTAL);
    SetSizer(sizer);

    m_spinner = new wxActivityIndicator(this, wxID_ANY);
    m_spinner->SetWindowVariant(wxWINDOW_VARIANT_SMALL);
    m_label = new wxStaticText(this, wxID_ANY, "");
#ifdef __WXOSX__
    m_label->SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#endif

    sizer->Add(m_spinner, wxSizerFlags().Center().Border(wxRIGHT, PX(4)));
    sizer->Add(m_label, wxSizerFlags(1).Center());

    HandleError = on_main_thread_for_window<std::exception_ptr>(this, [=](std::exception_ptr e){
        StopWithError(DescribeException(e));
    });
}

void ActivityIndicator::Start(const wxString& msg)
{
    m_running = true;

    m_label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
    m_label->SetLabel(msg);

    auto sizer = GetSizer();
    sizer->Show(m_spinner);
    sizer->Show(m_label, !msg.empty());
    Layout();

    m_spinner->Start();
}

void ActivityIndicator::Stop()
{
    m_running = false;

    m_spinner->Stop();
    m_label->SetLabel("");

    auto sizer = GetSizer();
    sizer->Hide(m_spinner);
    sizer->Hide(m_label);
    Layout();
}

void ActivityIndicator::StopWithError(const wxString& msg)
{
    m_running = false;

    m_spinner->Stop();
    m_label->SetForegroundColour(*wxRED);
    m_label->SetLabel(msg);

    auto sizer = GetSizer();
    sizer->Hide(m_spinner);
    sizer->Show(m_label);
    Layout();
}
<|endoftext|>