ListBench

em.bench.coremark/ListBench.em
import em from '@$$emscript'
export const $U = em.$declare('MODULE')

import * as Crc from '@em.bench.coremark/Crc.em'
import * as Utils from '@em.bench.coremark/Utils.em'

export const memsize = $config<u16>(666)

export class Data extends $struct {
    val: i16
    idx: i16
}
let DataFac = $factory(Data.$make())

class Elem extends $struct {
    next: ref_t<Elem>
    data: ref_t<Data>
}
let ElemFac = $factory(Elem.$make())

type Comparator = (a: ref_t<Data>, b: ref_t<Data>) => i32

const maxElems = $config<u16>(0)

let curHead_c = $config<ref_t<Elem>>()
var curHead: ref_t<Elem>

export namespace em$meta {

    export function em$construct() {
        let itemSize = 16 + $sizeof<Data>()
        maxElems.$$ = Math.round(memsize.$$ / itemSize) - 3
        curHead = ElemFac.$create()
        curHead.$$.data = DataFac.$create()
        let p = curHead
        for (let i = 0; i < maxElems.$$ - 1; i++) {
            let q = p.$$.next = ElemFac.$create()
            q.$$.data = DataFac.$create()
            p = q
        }
        p.$$.data = DataFac.$create()
        p.$$.next = ElemFac.$null()
        curHead_c.$$ = curHead
    }

}

function find(list: ref_t<Elem>, data: ref_t<Data>): ref_t<Elem> {
    let elem = list
    if (data.$$.idx >= 0) {
        while (elem && elem.$$.data.$$.idx != data.$$.idx) {
            elem = elem.$$.next
        }
    }
    else {
        while (elem && <i16>(<u16>elem.$$.data.$$.val & 0xff) != data.$$.val) {
            elem = elem.$$.next
        }
    }
    return elem
}

function idxCompare(a: ref_t<Data>, b: ref_t<Data>): i32 {
    a.$$.val = <i16>((<u16>a.$$.val & 0xff00) | (0x00ff & <u16>(a.$$.val >> 8)))
    b.$$.val = <i16>((<u16>b.$$.val & 0xff00) | (0x00ff & <u16>(b.$$.val >> 8)))
    return a.$$.idx - b.$$.idx
}

export function kind(): Utils.Kind {
    return Utils.Kind.LIST
}

function pr(list: ref_t<Elem>, name: text_t) {
    let sz = 0
    printf`%s\n[`(name)
    for (let e = list; e != null; e = e.$$.next) {
        let pre = (sz++ % 8) == 0 ? t$`\n    ` : t$``
        printf`%s(%04x,%04x)`(pre, e.$$.data.$$.idx, <u16>e.$$.data.$$.val)
    }
    printf`\n], size = %d\n`(sz)
}

export function print() {
    pr(curHead, t$`current`)
}

function remove(item: ref_t<Elem>): ref_t<Elem> {
    let ret = item.$$.next
    let tmp = item.$$.data
    item.$$.data = ret.$$.data
    ret.$$.data = tmp
    item.$$.next = item.$$.next.$$.next
    ret.$$.next = ElemFac.$null()
    return ret
}

function reverse(list: ref_t<Elem>): ref_t<Elem> {
    let next = ElemFac.$null()
    while (list) {
        let tmp = list.$$.next
        list.$$.next = next
        next = list
        list = tmp
    }
    return next
}


export function run(arg: i16): Utils.sum_t {
    let list = curHead
    let finderIdx = <i16>arg
    let findCnt = Utils.getSeed(3)
    let found = <u16>0
    let missed = <u16>0
    let retval = <Crc.sum_t>0
    let data = Data.$make()
    data.idx = finderIdx
    for (let i = 0; i < findCnt; i++) {
        data.val = <i16>(i & 0xff)
        let elem = find(list, $ref(data))
        list = reverse(list)
        if (!elem) {
            missed += 1
            retval += <u16>(list.$$.next.$$.data.$$.val >> 8) & 0x1
        }
        else {
            found += 1
            if (<u16>elem.$$.data.$$.val & 0x1) {
                retval += (<u16>(elem.$$.data.$$.val >> 9)) & 0x1
            }
            if (elem.$$.next) {
                let tmp = elem.$$.next
                elem.$$.next = tmp.$$.next
                tmp.$$.next = list.$$.next
                list.$$.next = tmp
            }
        }
        if (data.idx >= 0) data.idx += 1
    }
    retval += found * 4 - missed
    if (finderIdx > 0) list = sort(list, valCompare)
    let remover = remove(list.$$.next)
    let finder = find(list, $ref(data))
    if (!finder) finder = list.$$.next
    while (finder) {
        retval = Crc.add16(list.$$.data.$$.val, retval)
        finder = finder.$$.next
    }
    unremove(remover, list.$$.next)
    list = sort(list, idxCompare)
    for (let e = list.$$.next; e; e = e.$$.next) {
        retval = Crc.add16(list.$$.data.$$.val, retval)
    }
    return retval
}

export function setup() {
    curHead = curHead_c.$$
    let seed = Utils.getSeed(1)
    let ki = 1
    let kd = maxElems.$$ - 3
    let e = curHead
    e.$$.data.$$.idx = 0
    e.$$.data.$$.val = 0x8080
    for (e = e.$$.next; e.$$.next != null; e = e.$$.next) {
        let pat = <u16>(seed ^ kd) & 0xf
        let dat = (pat << 3) | (kd & 0x7)
        e.$$.data.$$.val = <i16>((dat << 8) | dat)
        kd -= 1
        if (ki < (maxElems.$$ / 5)) {
            e.$$.data.$$.idx = ki++
        }
        else {
            pat = <u16>(seed ^ ki++)
            e.$$.data.$$.idx = <i16>(0x3fff & (((ki & 0x7) << 8) | pat))
        }
    }
    e.$$.data.$$.idx = 0x7fff
    e.$$.data.$$.val = 0xffff
    curHead = sort(curHead, idxCompare)
}

function sort(list: ref_t<Elem>, cmp: Comparator): ref_t<Elem> {
    let insize = <i32>1
    let q: ref_t<Elem>
    let e: ref_t<Elem>
    for (; ;) {
        let p = list
        let tail = list = ElemFac.$null()
        let nmerges = <i32>0  // count number of merges we do in this pass
        while (p) {
            nmerges++  // there exists a merge to be done
            // step `insize` places along from p
            q = p
            let psize = 0
            for (let i = 0; i < insize; i++) {
                psize++
                q = q.$$.next
                if (!q) break
            }
            // if q hasn't fallen off end, we have two lists to merge
            let qsize = insize
            // now we have two lists; merge them
            while (psize > 0 || (qsize > 0 && q)) {
                // decide whether next element of merge comes from p or q
                if (psize == 0) {
                    // p is empty; e must come from q
                    e = q
                    q = q.$$.next
                    qsize--
                }
                else if (qsize == 0 || !q) {
                    // q is empty; e must come from p.
                    e = p
                    p = p.$$.next
                    psize--
                }
                else if (cmp(p.$$.data, q.$$.data) <= 0) {
                    // First element of p is lower (or same); e must come from p.
                    e = p
                    p = p.$$.next
                    psize--
                }
                else {
                    // First element of q is lower; e must come from q.
                    e = q
                    q = q.$$.next
                    qsize--
                }
                // add the next element to the merged list
                if (tail) {
                    tail.$$.next = e
                }
                else {
                    list = e
                }
                tail = e
            }
            // now p has stepped `insize` places along, and q has too
            p = q
        }
        tail.$$.next = ElemFac.$null()
        // If we have done only one merge, we're finished
        if (nmerges <= 1) break  // allow for nmerges==0, the empty list case
        // Otherwise repeat, merging lists twice the size
        insize *= 2
    }
    return list
}

function unremove(removed: ref_t<Elem>, modified: ref_t<Elem>) {
    let tmp = removed.$$.data
    removed.$$.data = modified.$$.data
    modified.$$.data = tmp
    removed.$$.next = modified.$$.next
    modified.$$.next = removed
}

// ---- ValComparator ----

import * as Bench0 from '@em.bench.coremark/StateBench.em'
import * as Bench1 from '@em.bench.coremark/MatrixBench.em'

function valCalc(pval: ref_t<i16>): i16 {
    let val = <u16>pval.$$
    let optype = <u8>(val >> 7) & 1
    if (optype) return <i16>(val & 0x007f)
    let flag = val & 0x7
    let vtype = (val >> 3) & 0xf
    vtype |= vtype << 4
    var ret: u16
    switch (flag) {
        case 0:
            ret = Bench0.run(<i16>vtype)
            Utils.bindCrc(Bench0.kind(), ret)
            break
        case 1:
            ret = Bench1.run(<i16>vtype)
            Utils.bindCrc(Bench1.kind(), ret)
            break
        default:
            ret = val
            break
    }
    let newcrc = Crc.add16(<i16>ret, Utils.getCrc(Utils.Kind.FINAL))
    Utils.setCrc(Utils.Kind.FINAL, Crc.add16(<i16>ret, Utils.getCrc(Utils.Kind.FINAL)))
    ret &= 0x007f
    pval.$$ = <i16>((val & 0xff00) | 0x0080 | ret)   // cache the result
    return <i16>ret
}

function valCompare(a: ref_t<Data>, b: ref_t<Data>): i32 {
    let val1 = valCalc($ref(a.$$.val))
    let val2 = valCalc($ref(b.$$.val))
    return val1 - val2
}