#!/usr/bin/env bash

# This script generates the `load_store_*.clif` test files.
#
# Usage:
#
#     $ ./make-load-store-tests.sh

set -e
cd $(dirname "$0")

function main {
    for spectre in "yes" "no"; do
        for guard in "0" "0xffffffff"; do
            for index_type in "i32" "i64"; do
                for kind in "static" "dynamic"; do
                    for access_type in "i32" "i8"; do
                        for offset in "0" "0x1000" "0xffff0000"; do
                            generate_one_test $kind $index_type $guard $spectre $access_type $offset x86_64 false
                            for target in "x86_64" "aarch64" "s390x" "riscv64"; do
                                generate_one_test $kind $index_type $guard $spectre $access_type $offset $target true
                            done
                        done
                    done
                done
            done
        done
    done
    echo "Done!"
}

function generate_one_test() {
    local kind=$1
    local index_type=$2
    local guard=$3
    local spectre=$4
    local access_type=$5
    local offset=$6
    local target=$7
    local compile=$8

    local filename="load_store_${kind}_kind_${index_type}_index_${guard}_guard_${spectre}_spectre_${access_type}_access_${offset}_offset.wat"
    if [[ $compile == "true" ]]; then
        local target_dir=${target}
        if [[ $target == "x86_64" ]]; then
            target_dir="x64"
        fi
        filename="../../isa/${target_dir}/wasm/${filename}"
    fi
    echo "Generating $filename..."

    local enable_spectre=true
    if [[ $spectre == "no" ]]; then
        enable_spectre=false
    fi
    local settings="['enable_heap_access_spectre_mitigation=$enable_spectre']"

    local store_op=
    local load_op=
    case $access_type in
        "i32")
            store_op="i32.store"
            load_op="i32.load"
            ;;
        "i8")
            store_op="i32.store8"
            load_op="i32.load8_u"
            ;;
    esac

    local bound_global=""
    local style=""
    case $kind in
        "dynamic")
            bound_global=$(cat <<EOF
;;! [globals.heap_bound]
;;! type = "i64"
;;! load = { base = "vmctx", offset = 8, readonly = true }
EOF
                        )
            style='{ kind = "dynamic", bound = "heap_bound" }'
            ;;
        "static")
            bound_global=";;! # (no heap_bound global for static heaps)"
            style='{ kind = "static", bound = 0x10000000 }'
        ;;
    esac

    cat <<EOF > "$filename"
;;! target = "${target}"
;;!
;;! settings = ${settings}
;;!
;;! compile = ${compile}
;;!
;;! [globals.vmctx]
;;! type = "i64"
;;! vmctx = true
;;!
;;! [globals.heap_base]
;;! type = "i64"
;;! load = { base = "vmctx", offset = 0, readonly = true }
;;!
${bound_global}
;;!
;;! [[heaps]]
;;! base = "heap_base"
;;! min_size = 0x10000
;;! offset_guard_size = ${guard}
;;! index_type = "${index_type}"
;;! style = ${style}

;; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;; !!! GENERATED BY 'make-load-store-tests.sh' DO NOT EDIT !!!
;; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

(module
  (memory ${index_type} 1)

  (func (export "do_store") (param ${index_type} i32)
    local.get 0
    local.get 1
    ${store_op} offset=${offset})

  (func (export "do_load") (param ${index_type}) (result i32)
    local.get 0
    ${load_op} offset=${offset}))

;; TODO: run with the 'CRANELIFT_TEST_BLESS=1' env var set to update this test's
;; expected output.
EOF
}

main
