<commit_before>/*
 * Multisim: a microprocessor architecture exploration framework
 * Copyright (C) 2019 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *  ** TODO **
 *
 * Correctness:
 *
 * - Don't commit stores and csr writes to memory until retirement
 * - Force cosim match on IO memory locations (pick one to do IO and
 *   let the other follow)
 *
 * Perf:
 *
 * - Predict branches
 * - Reclaim physical registers a simpler way (push them to the tail
 *   of the freelist as soon as they are reallocated and roll back the
 *   tail on rollbacks)
 *
 * - crack stores and introduce store buffers
 * - LSC
 *
 * Cleanup:
 *
 * - Don't depend on seqno outside of self-checking and visualization
 * - Review what's tracked in data structures
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include "sim.h"
#include "run_simple.h"
#include "loadelf.h"

//////// Configuration and magic numbers

#define FETCH_BUFFER_SIZE       8
#define FETCH_WIDTH             8
#define ROB_SIZE               32
#define PHYSICAL_REGS          64
#define EX_BUFFER_SIZE          8
#define ME_BUFFER_SIZE          8

/*
 * Enable an alternative register scheme that is slightly simpler for
 * the rollback.
 */
#define EARLY_RELEASE           1

// Two special physical registers: the constant zero and the sink for
// for r0 destination (is never read)

#define PR_ZERO                 0
#define PR_SINK (PHYSICAL_REGS-1)

//////// Types

typedef struct fetch_parcel_st {
    unsigned            seqno;
    uint64_t            addr;
    uint32_t            insn;

    // For visualization
    uint64_t            fetch_ts;
    uint64_t            decode_ts;
    uint64_t            issue_ts;
    uint64_t            execute_ts;
    uint64_t            commit_ts;
//  uint64_t            retire_ts; // This is implicit
} fetch_parcel_t;

/* The essence of support for speculation is the register renaming and
 * the reorder buffer.  The former enables us a overlap future and
 * past values of logical registers whereas the reorder buffer is
 * allocated in program order and enables us to restore the sequential
 * view and roll-back mis-speculation.
 *
 * Renaming is just mapping logical registers to physical.  The
 * reorder buffer tracks instructions in flight, what the _previous_
 * register was that was assigned to a logical register, and whether
 * the instruction has committed.
 *
 * Committed instructions from the tail of the ROB can be retired,
 * which means the old register is freed and the entry disappears from
 * the ROB.
 *
 * Rolling back means traversing backwards from the head, undoing the
 * renames and freeing the registers.
 */
typedef struct rob_entry_st {
    int                 r;      /* Logical register written by this instruction */
    int                 pr_old; /* Physical register that r was _previously_ mapped to */
    bool                committed; /* The instruction is done executing and has written the result, if any, to the
                                    * register file */

    bool                restart;
    uint64_t            restart_pc;
    bool                exception;

    // For debugging (XXX well, I do use the seqno, but we could avoid that)
    int                 pr;     /* The physical register with the result */
    fetch_parcel_t      fp;
    isa_decoded_t       dec;
} rob_entry_t;

typedef struct micro_op_st {
    fetch_parcel_t      fetched; // XXX redundant?
    isa_decoded_t       dec;

    unsigned            pr_a;
    unsigned            pr_b;
    unsigned            pr_wb;
    unsigned            rob_index;
} micro_op_t;

//////// State

/* The arrays */
static fetch_parcel_t   fb[FETCH_BUFFER_SIZE];
static rob_entry_t      rob[ROB_SIZE];
static unsigned         rat[32];
static int64_t          prf[PHYSICAL_REGS];
static bool             pr_ready[PHYSICAL_REGS]; // Scoreboard
static unsigned         freelist[PHYSICAL_REGS];
static micro_op_t       ex_buffer[EX_BUFFER_SIZE];
static micro_op_t       me_buffer[EX_BUFFER_SIZE];

static const arch_t    *arch;
static unsigned         fetch_seqno;
static int              fb_head = 0, fb_tail = 0, fb_size = 0;
static unsigned         rob_wp = 0, rob_rp = 0;
static unsigned         freelist_wp = 0, freelist_rp = 0;
static int              ex_size;
static int              me_size;
static uint64_t         exception_seqno = ~0ULL;
static isa_exception_t  exception_info;
static int              cycle;

///////////////////////////////////////////////////////

static bool
is_rob_full(void)
{
    return (rob_wp + 1) % ROB_SIZE == rob_rp;
}

static void
free_reg(unsigned pr)
{
    assert((unsigned) pr < PHYSICAL_REGS);
    // DEBUG: Make sure pr isn't already on the freelist
    {
        int p = freelist_rp;
        while (p != freelist_wp) {
            assert(freelist[p] != pr);
            if (++p == sizeof freelist / sizeof *freelist)
                p = 0;
        }
    }

    if (pr != PR_ZERO && pr != PR_SINK) {
        freelist[freelist_wp] = pr;
        if (++freelist_wp == sizeof freelist / sizeof *freelist)
            freelist_wp = 0;
        assert(freelist_rp != freelist_wp);
    }
}

static unsigned
alloc_reg(void)
{
    assert(freelist_rp != freelist_wp);

    unsigned pr = freelist[freelist_rp++];
    if (freelist_rp == sizeof freelist / sizeof *freelist)
        freelist_rp = 0;

    pr_ready[pr] = false;

    return pr;
}

static void
show_rob(const char *msg)
{
    unsigned p = rob_rp;

    fprintf(stderr, "ROB%s:\n", msg);
    while (p != rob_wp) {
        fetch_parcel_t fp = rob[p].fp;
        fprintf(stderr, "  rob[%02d] = %c %d:%08x %08x\n",
                p, "UC"[rob[p].committed], fp.seqno, (uint32_t)fp.addr, fp.insn);
        if (++p == ROB_SIZE)
            p = 0;
    }
}

static void
show_ex(void)
{
    if (ex_size)
        fprintf(stderr, "EX:\n");
    for (int i = 0; i < ex_size; ++i) {
        micro_op_t mop = ex_buffer[i];
        fetch_parcel_t fp = mop.fetched;

        fprintf(stderr, "  rob[%02d] %d:%08x %08x pr%d%c, pr%d%c -> pr%d %s ",
                mop.rob_index,
                fp.seqno, (uint32_t)fp.addr, fp.insn,
                mop.pr_a, "WR"[pr_ready[mop.pr_a]],
                mop.pr_b, "WR"[pr_ready[mop.pr_b]],
                mop.pr_wb, pr_ready[mop.pr_a] & pr_ready[mop.pr_b] ? "READY" : "     ");
        isa_disass(arch, mop.dec, (isa_result_t) { .result = 0xEEEEEEEE });
    }
}

static void
show_me(void)
{
    if (me_size)
        fprintf(stderr, "ME:\n");
    for (int i = 0; i < me_size; ++i) {
        micro_op_t mop = me_buffer[i];
        fetch_parcel_t fp = mop.fetched;

        fprintf(stderr, "  rob[%02d] %d:%08x %08x pr%d%c, pr%d%c -> pr%d %s ",
                mop.rob_index,
                fp.seqno, (uint32_t)fp.addr, fp.insn,
                mop.pr_a, "WR"[pr_ready[mop.pr_a]],
                mop.pr_b, "WR"[pr_ready[mop.pr_b]],
                mop.pr_wb, pr_ready[mop.pr_a] & pr_ready[mop.pr_b] ? "READY" : "     ");
        isa_disass(arch, mop.dec, (isa_result_t) { .result = 0xEEEEEEEE });
    }
}

static unsigned
allocate_rob(int r, int pr, fetch_parcel_t fp)
{
    unsigned rob_index = rob_wp;

    assert(r == ISA_NO_REG || (unsigned) r < 32);  // XXX should be part of the arch
    assert((unsigned) pr < PHYSICAL_REGS);

    rob[rob_index] = (rob_entry_t) {
        .r = r, .pr_old = pr, .committed = false,
        .fp = fp
    };

    if (is_rob_full()) {
        show_rob(" full");
        show_ex();
        show_me();
    }

    if (++rob_wp == ROB_SIZE)
        rob_wp = 0;

    assert(rob_wp != rob_rp);

    return rob_index;
}

static void
visualize_retirement(cpu_state_t *state, rob_entry_t rob)
{
#define WIDTH 64

    static int next_seqno = 0;
    char line[WIDTH+1];
    fetch_parcel_t fp = rob.fp;
    isa_decoded_t dec = rob.dec;
    int            pr = rob.pr;

    memset(line, '.', WIDTH);
    line[WIDTH] = '\0';

    line[fp.fetch_ts   % WIDTH] = 'F';
    line[fp.decode_ts  % WIDTH] = 'D';
    //line[fp.issue_ts   % WIDTH] = 'I';
    line[fp.execute_ts % WIDTH] = 'E';
    line[fp.commit_ts  % WIDTH] = 'C';
    line[cycle         % WIDTH] = 'R';

    fprintf(stderr, "%6d %s ", next_seqno++, line);
    isa_disass(arch, dec, (isa_result_t) { .result = prf[pr] });
}


/*
 * Example roll-back: we determine that the #2:BEQ was mispredicted so
 * we have to flush the pipe and roll ROB back to #2.  That means
 * undoing #4 and #3 in that order.
 *
 * #0: J        <--- oldest uncommitted/rp
 * #1: CSRR
 * #2: BEQ      <--- most recent
 * #3:          <--- wp
 */

static void
rollback_rob(unsigned keep_seqno)
{
    while (rob_rp != rob_wp) {
        unsigned p = rob_wp;
        if (p == 0)
            p = ROB_SIZE - 1;
        else
            --p;

        assert((unsigned)p < ROB_SIZE);
        if (rob[p].fp.seqno == keep_seqno)
            break;
        rob_wp = p;

        if (rob[p].r != ISA_NO_REG) {
            if (0)
                fprintf(stderr, "Rollback %d:%08x now r%d -> pr%d\n",
                        rob[p].fp.seqno, (uint32_t)rob[p].fp.addr, rob[p].r, rob[p].pr_old);

#ifdef EARLY_RELEASE
            // Undo the free'd register and the allocation
	    assert(freelist_wp != freelist_rp);
            if (freelist_wp-- == 0)
                freelist_wp = sizeof freelist / sizeof *freelist - 1;
            if (freelist_rp-- == 0)
                freelist_rp = sizeof freelist / sizeof *freelist - 1;
#else
            free_reg(rat[rob[p].r]);
#endif	    
            rat[rob[p].r] = rob[p].pr_old;
        }
    }
}


static void
flush_and_redirect(cpu_state_t *state, verbosity_t verbosity, unsigned seqno, uint64_t new_pc)
{
    // Flush
    fb_size = 0;
    fb_head = fb_tail;
    ex_size = 0;
    me_size = 0;

    rollback_rob(seqno);

    state->pc = new_pc;
    fetch_seqno = seqno + 1;
    // mispredicted = true;

}


/*
 * The ReOrder Buffer holds instructions in fetch (= program) order.
 * Its purpose is to enable the illusion of sequential instruction
 * semantics.  We do this by traversing the ROB in order and
 * "retiring" committed instructions.  Retired instructions are
 * irrevokably done and will never be restarted, thus we can release
 * any resources that was held in case we needed to revert it, eg. the
 * physical register previously mapped to our target register.
 *
 * There are two special cases to consider: branch mispredicted and
 * exceptions.  Both require flushing the pipeline, undoing all
 * instructions in the ROB, and restarting the pipeline.  In case of
 * an exception, the excepting instruction is undone as well, instead
 * of being retired.
 *
 *
 * Example ROB:
 *
 * #0: XOR      <--- rp
 * #1: AND
 * #2: J        <--- oldest uncommitted
 * #3: CSRR
 * #4: BEQ
 * #5: ADDI
 * #6: LW       <--- most recent
 * #7:          <--- wp
 */
static void
lsc_retire(cpu_state_t *state, cpu_state_t *costate, verbosity_t verbosity)
{
    while (rob_rp != rob_wp && rob[rob_rp].committed) {
        rob_entry_t re = rob[rob_rp];

        if (verbosity & VERBOSE_DISASS) {
            visualize_retirement(state, re);
        }

        if (re.exception) {
            if (state->verbosity & VERBOSE_DISASS)
                fprintf(stderr,
                        "                  EXCEPTION %"PRId64" (%08"PRId64") RAISED\n",
                        exception_info.code, exception_info.info);

            flush_and_redirect(state, verbosity, re.fp.seqno - 1,
                               arch->handle_exception(state, re.dec.insn_addr, exception_info));
            return;
        }

        if (re.restart) {
            flush_and_redirect(state, verbosity, re.fp.seqno, re.restart_pc);
        }

        /* Co-simulate retired instructions.  A complication is that
         * costate->pc might not be the next instuction retired (if
         * the instruction traps, then the next retired instruction<commit_msg>Part 1: towards dropping seqno<commit_after>/*
 * Multisim: a microprocessor architecture exploration framework
 * Copyright (C) 2019 <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 *  ** TODO **
 *
 * Correctness:
 *
 * - Don't commit stores and csr writes to memory until retirement
 * - Force cosim match on IO memory locations (pick one to do IO and
 *   let the other follow)
 *
 * Perf:
 *
 * - Predict branches
 * - Reclaim physical registers a simpler way (push them to the tail
 *   of the freelist as soon as they are reallocated and roll back the
 *   tail on rollbacks)
 *
 * - crack stores and introduce store buffers
 * - LSC
 *
 * Cleanup:
 *
 * - Don't depend on seqno outside of self-checking and visualization
 * - Review what's tracked in data structures
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include "sim.h"
#include "run_simple.h"
#include "loadelf.h"

//////// Configuration and magic numbers

#define FETCH_BUFFER_SIZE       8
#define FETCH_WIDTH             8
#define ROB_SIZE               32
#define PHYSICAL_REGS          64
#define EX_BUFFER_SIZE          8
#define ME_BUFFER_SIZE          8

/*
 * Enable an alternative register scheme that is slightly simpler for
 * the rollback.
 */
#define EARLY_RELEASE           1

// Two special physical registers: the constant zero and the sink for
// for r0 destination (is never read)

#define PR_ZERO                 0
#define PR_SINK (PHYSICAL_REGS-1)

//////// Types

typedef struct fetch_parcel_st {
    unsigned            seqno;
    uint64_t            addr;
    uint32_t            insn;

    // For visualization
    uint64_t            fetch_ts;
    uint64_t            decode_ts;
    uint64_t            issue_ts;
    uint64_t            execute_ts;
    uint64_t            commit_ts;
//  uint64_t            retire_ts; // This is implicit
} fetch_parcel_t;

/* The essence of support for speculation is the register renaming and
 * the reorder buffer.  The former enables us a overlap future and
 * past values of logical registers whereas the reorder buffer is
 * allocated in program order and enables us to restore the sequential
 * view and roll-back mis-speculation.
 *
 * Renaming is just mapping logical registers to physical.  The
 * reorder buffer tracks instructions in flight, what the _previous_
 * register was that was assigned to a logical register, and whether
 * the instruction has committed.
 *
 * Committed instructions from the tail of the ROB can be retired,
 * which means the old register is freed and the entry disappears from
 * the ROB.
 *
 * Rolling back means traversing backwards from the head, undoing the
 * renames and freeing the registers.
 */
typedef struct rob_entry_st {
    int                 r;      /* Logical register written by this instruction */
    int                 pr_old; /* Physical register that r was _previously_ mapped to */
    bool                committed; /* The instruction is done executing and has written the result, if any, to the
                                    * register file */

    bool                restart;
    uint64_t            restart_pc;
    bool                exception;

    // For debugging (XXX well, I do use the seqno, but we could avoid that)
    int                 pr;     /* The physical register with the result */
    fetch_parcel_t      fp;
    isa_decoded_t       dec;
} rob_entry_t;

typedef struct micro_op_st {
    fetch_parcel_t      fetched; // XXX redundant?
    isa_decoded_t       dec;

    unsigned            pr_a;
    unsigned            pr_b;
    unsigned            pr_wb;
    unsigned            rob_index;
} micro_op_t;

//////// State

/* The arrays */
static fetch_parcel_t   fb[FETCH_BUFFER_SIZE];
static rob_entry_t      rob[ROB_SIZE];
static unsigned         rat[32];
static int64_t          prf[PHYSICAL_REGS];
static bool             pr_ready[PHYSICAL_REGS]; // Scoreboard
static unsigned         freelist[PHYSICAL_REGS];
static micro_op_t       ex_buffer[EX_BUFFER_SIZE];
static micro_op_t       me_buffer[EX_BUFFER_SIZE];

static const arch_t    *arch;
static unsigned         fetch_seqno;
static int              fb_head = 0, fb_tail = 0, fb_size = 0;
static unsigned         rob_wp = 0, rob_rp = 0;
static unsigned         freelist_wp = 0, freelist_rp = 0;
static int              ex_size;
static int              me_size;
static uint64_t         exception_seqno = ~0ULL;
static isa_exception_t  exception_info;
static int              cycle;

///////////////////////////////////////////////////////

static bool
is_rob_full(void)
{
    return (rob_wp + 1) % ROB_SIZE == rob_rp;
}

static void
free_reg(unsigned pr)
{
    assert((unsigned) pr < PHYSICAL_REGS);
    // DEBUG: Make sure pr isn't already on the freelist
    {
        int p = freelist_rp;
        while (p != freelist_wp) {
            assert(freelist[p] != pr);
            if (++p == sizeof freelist / sizeof *freelist)
                p = 0;
        }
    }

    if (pr != PR_ZERO && pr != PR_SINK) {
        freelist[freelist_wp] = pr;
        if (++freelist_wp == sizeof freelist / sizeof *freelist)
            freelist_wp = 0;
        assert(freelist_rp != freelist_wp);
    }
}

static unsigned
alloc_reg(void)
{
    assert(freelist_rp != freelist_wp);

    unsigned pr = freelist[freelist_rp++];
    if (freelist_rp == sizeof freelist / sizeof *freelist)
        freelist_rp = 0;

    pr_ready[pr] = false;

    return pr;
}

static void
show_rob(const char *msg)
{
    unsigned p = rob_rp;

    fprintf(stderr, "ROB%s:\n", msg);
    while (p != rob_wp) {
        fetch_parcel_t fp = rob[p].fp;
        fprintf(stderr, "  rob[%02d] = %c %d:%08x %08x\n",
                p, "UC"[rob[p].committed], fp.seqno, (uint32_t)fp.addr, fp.insn);
        if (++p == ROB_SIZE)
            p = 0;
    }
}

static void
show_ex(void)
{
    if (ex_size)
        fprintf(stderr, "EX:\n");
    for (int i = 0; i < ex_size; ++i) {
        micro_op_t mop = ex_buffer[i];
        fetch_parcel_t fp = mop.fetched;

        fprintf(stderr, "  rob[%02d] %d:%08x %08x pr%d%c, pr%d%c -> pr%d %s ",
                mop.rob_index,
                fp.seqno, (uint32_t)fp.addr, fp.insn,
                mop.pr_a, "WR"[pr_ready[mop.pr_a]],
                mop.pr_b, "WR"[pr_ready[mop.pr_b]],
                mop.pr_wb, pr_ready[mop.pr_a] & pr_ready[mop.pr_b] ? "READY" : "     ");
        isa_disass(arch, mop.dec, (isa_result_t) { .result = 0xEEEEEEEE });
    }
}

static void
show_me(void)
{
    if (me_size)
        fprintf(stderr, "ME:\n");
    for (int i = 0; i < me_size; ++i) {
        micro_op_t mop = me_buffer[i];
        fetch_parcel_t fp = mop.fetched;

        fprintf(stderr, "  rob[%02d] %d:%08x %08x pr%d%c, pr%d%c -> pr%d %s ",
                mop.rob_index,
                fp.seqno, (uint32_t)fp.addr, fp.insn,
                mop.pr_a, "WR"[pr_ready[mop.pr_a]],
                mop.pr_b, "WR"[pr_ready[mop.pr_b]],
                mop.pr_wb, pr_ready[mop.pr_a] & pr_ready[mop.pr_b] ? "READY" : "     ");
        isa_disass(arch, mop.dec, (isa_result_t) { .result = 0xEEEEEEEE });
    }
}

static unsigned
allocate_rob(int r, int pr, fetch_parcel_t fp)
{
    unsigned rob_index = rob_wp;

    assert(r == ISA_NO_REG || (unsigned) r < 32);  // XXX should be part of the arch
    assert((unsigned) pr < PHYSICAL_REGS);

    rob[rob_index] = (rob_entry_t) {
        .r = r, .pr_old = pr, .committed = false,
        .fp = fp
    };

    if (is_rob_full()) {
        show_rob(" full");
        show_ex();
        show_me();
    }

    if (++rob_wp == ROB_SIZE)
        rob_wp = 0;

    assert(rob_wp != rob_rp);

    return rob_index;
}

static void
visualize_retirement(cpu_state_t *state, rob_entry_t rob)
{
#define WIDTH 64

    static int next_seqno = 0;
    char line[WIDTH+1];
    fetch_parcel_t fp = rob.fp;
    isa_decoded_t dec = rob.dec;
    int            pr = rob.pr;

    memset(line, '.', WIDTH);
    line[WIDTH] = '\0';

    line[fp.fetch_ts   % WIDTH] = 'F';
    line[fp.decode_ts  % WIDTH] = 'D';
    //line[fp.issue_ts   % WIDTH] = 'I';
    line[fp.execute_ts % WIDTH] = 'E';
    line[fp.commit_ts  % WIDTH] = 'C';
    line[cycle         % WIDTH] = 'R';

    fprintf(stderr, "%6d %s ", next_seqno++, line);
    isa_disass(arch, dec, (isa_result_t) { .result = prf[pr] });
}


/*
 * Example roll-back: we determine that the #2:BEQ was mispredicted so
 * we have to flush the pipe and roll ROB back to #2.  That means
 * undoing #4 and #3 in that order.
 *
 * #0: J        <--- oldest uncommitted/rp
 * #1: CSRR
 * #2: BEQ      <--- most recent
 * #3:          <--- wp
 */

static void
rollback_rob(int keep_rob_index)
{
    do {
        unsigned p = rob_wp;
        if (p-- == 0)
            p = ROB_SIZE - 1;

        assert((unsigned)p < ROB_SIZE);
        if (p == keep_rob_index)
            break;
        rob_wp = p;

        if (rob[p].r == ISA_NO_REG)
	    continue;


#ifdef EARLY_RELEASE
	// Undo the free'd register and the allocation
	assert(freelist_wp != freelist_rp);
	if (freelist_wp-- == 0)
	    freelist_wp = sizeof freelist / sizeof *freelist - 1;
	if (freelist_rp-- == 0)
	    freelist_rp = sizeof freelist / sizeof *freelist - 1;
#else
	free_reg(rat[rob[p].r]);
#endif	    
	rat[rob[p].r] = rob[p].pr_old;
    } while (rob_rp != rob_wp);
}


static void
flush_and_redirect(cpu_state_t *state, verbosity_t verbosity, int rob_index, unsigned seqno, uint64_t new_pc)
{
    // Flush
    fb_size = 0;
    fb_head = fb_tail;
    ex_size = 0;
    me_size = 0;

    rollback_rob(rob_index);

    state->pc = new_pc;
    fetch_seqno = seqno + 1;
    // mispredicted = true;

}


/*
 * The ReOrder Buffer holds instructions in fetch (= program) order.
 * Its purpose is to enable the illusion of sequential instruction
 * semantics.  We do this by traversing the ROB in order and
 * "retiring" committed instructions.  Retired instructions are
 * irrevokably done and will never be restarted, thus we can release
 * any resources that was held in case we needed to revert it, eg. the
 * physical register previously mapped to our target register.
 *
 * There are two special cases to consider: branch mispredicted and
 * exceptions.  Both require flushing the pipeline, undoing all
 * instructions in the ROB, and restarting the pipeline.  In case of
 * an exception, the excepting instruction is undone as well, instead
 * of being retired.
 *
 *
 * Example ROB:
 *
 * #0: XOR      <--- rp
 * #1: AND
 * #2: J        <--- oldest uncommitted
 * #3: CSRR
 * #4: BEQ
 * #5: ADDI
 * #6: LW       <--- most recent
 * #7:          <--- wp
 */
static void
lsc_retire(cpu_state_t *state, cpu_state_t *costate, verbosity_t verbosity)
{
    while (rob_rp != rob_wp && rob[rob_rp].committed) {
        rob_entry_t re = rob[rob_rp];

        if (verbosity & VERBOSE_DISASS) {
            visualize_retirement(state, re);
        }

        if (re.exception) {
            if (state->verbosity & VERBOSE_DISASS)
                fprintf(stderr,
                        "                  EXCEPTION %"PRId64" (%08"PRId64") RAISED\n",
                        exception_info.code, exception_info.info);

	    int prev_rob_pr = rob_rp == 0 ? ROB_SIZE - 1 : rob_rp - 1;
            flush_and_redirect(state, verbosity, prev_rob_pr, re.fp.seqno - 1,
                               arch->handle_exception(state, re.dec.insn_addr, exception_info));
            return;
        }

        if (re.restart) {
            flush_and_redirect(state, verbosity, rob_rp, re.fp.seqno, re.restart_pc);
        }

        /* Co-simulate retired instructions.  A complication is that
         * costate->pc might not be the next instuction retired (if
         * the instruction traps, then the next retired instruction
{'fraction_non_alphanumeric': 0.1080683039445926, 'fraction_numerical': 0.011583011583011582, 'mean_word_length': 2.2103245591617684, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7091690', 'n_tokens_mistral': 8600, 'n_tokens_neox': 8097, 'n_words': 2505}