// File: /workspace/github_c_makefile_star2_O1/yangtau[P]hedgehog/src/test/vm_test.c
// Function: test_vm_fac
void test_vm_fac() {
    /*
     # hg code:
     fn fac(n, acc) {
        if n <= 1 {
            return acc
        } else {
            return fac(n-1, acc*n)
        }
     }
     return fac(5, 1)

     # hg vm ins:
     main:
     loadc R0 consts[2]
     loadc R1 consts[0]
     loadc R2 consts[1]
     call  R0 2 1 ; R0=fac(5, 1)
     return

     fac: ; R1=n, R2=acc
     loadc       R3 consts[1] ; R3=1
     gt          R3 R1 R3     ; R3=R1>R3  if n>1
     jumpfalse   R3 LELSE
     move        R0 R2
     return
     LELSE:
     move        R3 R1        ; n
     loadc       R4 consts[1] ; R4=1
     sub         R1 R3 R4     ; R1=n-1
     mul         R2 R2 R3     ; R2=acc*n
     call        R0 2 1       ; call fac
     return
     --constants table--
     0    -> 5
     1    -> 1
     2    -> fac
     */

#define fac_ptr 5
    struct hg_ins codes[] = {
        // main:
        [0] = {.op = HG_OP_LOADC, .as.load = {0, 2}},
        [1] = {.op = HG_OP_LOADC, .as.load = {1, 0}},
        [2] = {.op = HG_OP_LOADC, .as.load = {2, 1}},
        [3] = {.op = HG_OP_CALL, .as.call = {0, 2, 1}},
        [4] = {.op = HG_OP_RETURN},
        // fac:
        [fac_ptr] = {.op = HG_OP_LOADC, .as.load = {3, 1}},
        [6]       = {.op = HG_OP_GT, .as.ari = {3, 1, 3}},
        [7]       = {.op = HG_OP_JUMPFALSE, .as.jump = {3, 3 /* pc+=3 */}},
        [8]       = {.op = HG_OP_MOVE, .as.move = {0, 2}},
        [9]       = {.op = HG_OP_RETURN},
        //  else:
        [10] = {.op = HG_OP_MOVE, .as.move = {3, 1}},
        [11] = {.op = HG_OP_LOADC, .as.load = {4, 1}},
        [12] = {.op = HG_OP_SUB, .as.ari = {1, 3, 4}},
        [13] = {.op = HG_OP_MUL, .as.ari = {2, 2, 3}},
        [14] = {.op = HG_OP_CALL, .as.call = {0, 2, 1}},
        [15] = {.op = HG_OP_RETURN},
    };

    struct hg_function entry, fac;

    struct hg_value constants[] = {
        [0] = {.as.i = 1},
        [1] = {.as.i = 1},
        [2] = {.as.func = &fac},
    };
    int64_t* input = &constants[0].as.i;

    entry = (struct hg_function){
        .num_args      = 0,
        .num_returns   = 0,
        .constants     = constants,
        .num_constants = sizeof(constants) / sizeof(struct hg_value),
        .code          = codes,
        .size_code     = sizeof(codes) / sizeof(struct hg_ins),
    };
    fac = (struct hg_function){
        .num_args      = 2,
        .num_returns   = 1,
        .constants     = constants,
        .num_constants = 0,
        .code          = &codes[fac_ptr],
        .size_code     = 0,
    };

    struct hg_vm_state vs = {
        .size_stack = 1 << 10,
        .stack      = hg_alloc(sizeof(struct hg_value) * (1 << 10)),
        .size_frame = 1 << 10,
        .frames     = hg_alloc(sizeof(struct hg_call_frame) * (1 << 10)),
    };

    struct hg_value ret;

    *input = 0;
    ret    = hg_vm_execute(&vs, &entry);
    test_assert(ret.as.i == 1);

    *input = 1;
    ret    = hg_vm_execute(&vs, &entry);
    test_assert(ret.as.i == 1);

    *input = 2;
    ret    = hg_vm_execute(&vs, &entry);
    test_assert(ret.as.i == 2);

    *input = 3;
    ret    = hg_vm_execute(&vs, &entry);
    test_assert(ret.as.i == 6);

    *input = 4;
    ret    = hg_vm_execute(&vs, &entry);
    test_assert(ret.as.i == 24);

    *input = 5;
    ret    = hg_vm_execute(&vs, &entry);
    test_assert(ret.as.i == 120);

    *input = 6;
    ret    = hg_vm_execute(&vs, &entry);
    test_assert(ret.as.i == 720);

    *input = 10;
    ret    = hg_vm_execute(&vs, &entry);
    test_assert(ret.as.i == 3628800);

    duration_t start = now();
    const int N      = 100000;
    for (int i = 0; i < N; i++) {
        *input = 15;
        ret    = hg_vm_execute(&vs, &entry);
        test_assert(ret.as.i == 1307674368000);
    }
    test_println("#%d factorial elapsed: %s", N, duration2str(since(start), MACROSEC));

    hg_free(vs.stack);
    hg_free(vs.frames);
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xe8, %rsp
movaps 0x9b0(%rip), %xmm0      # 0x20f0
leaq 0x70(%rsp), %rax
movaps %xmm0, 0x30(%rax)
movaps 0x990(%rip), %xmm0      # 0x20e0
movaps %xmm0, 0x20(%rax)
movaps 0x975(%rip), %xmm0      # 0x20d0
movaps %xmm0, 0x10(%rax)
movaps 0x95a(%rip), %xmm0      # 0x20c0
movaps %xmm0, (%rax)
movl $0x1, %r15d
movq %rsp, %r12
movq %r15, (%r12)
movq %r15, 0x8(%r12)
leaq 0xb8(%rsp), %rcx
movq %rcx, 0x10(%r12)
xorps %xmm0, %xmm0
leaq 0x40(%rsp), %rbx
movaps %xmm0, (%rbx)
movq $0x3, 0x10(%rbx)
movq %r12, 0x18(%rbx)
movq $0x10, 0x20(%rbx)
movq %rax, 0x28(%rbx)
leaq 0x84(%rsp), %rax
movq $0x2, (%rcx)
movq %r15, 0x8(%rcx)
xorl %r13d, %r13d
movq %r13, 0x10(%rcx)
movq %r12, 0x18(%rcx)
movq %r13, 0x20(%rcx)
movq %rax, 0x28(%rcx)
movl $0x400, %ebp            # imm = 0x400
leaq 0x20(%rsp), %r14
movq %rbp, (%r14)
movl $0x2000, %edi           # imm = 0x2000
callq 0x10b0
movq %rax, 0x8(%r14)
movq %rbp, 0x10(%r14)
movl $0x6000, %edi           # imm = 0x6000
callq 0x10b0
movq %rax, 0x18(%r14)
movq %r13, (%r12)
movq %r14, %rdi
movq %rbx, %rsi
callq 0x13cc
cmpq %r15, %rax
jne 0x19c0
movq $0x1, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x1, %rax
jne 0x19ca
movq $0x2, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x2, %rax
jne 0x19d4
movq $0x3, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x6, %rax
jne 0x19de
movq $0x4, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x18, %rax
jne 0x19e8
movq $0x5, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x78, %rax
jne 0x19f2
movq $0x6, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x2d0, %rax            # imm = 0x2D0
jne 0x19fc
movq $0xa, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x375f00, %rax         # imm = 0x375F00
jne 0x1a06
movl $0x186a0, %ebp          # imm = 0x186A0
callq 0x1060
movq %rax, %rbx
leaq 0x20(%rsp), %r14
leaq 0x40(%rsp), %r15
movabsq $0x13077775800, %r12    # imm = 0x13077775800
movq $0xf, (%rsp)
movq %r14, %rdi
movq %r15, %rsi
callq 0x13cc
cmpq %r12, %rax
jne 0x1940
decl %ebp
jne 0x1922
jmp 0x1947
callq 0x12a4
jmp 0x193a
callq 0x1060
subq %rbx, %rax
movq %fs:0x0, %rcx
leaq -0x400(%rcx), %rbx
leaq 0x8da(%rip), %rsi       # 0x2240
leaq 0x8c6(%rip), %rcx       # 0x2233
movq %rbx, %rdi
movq %rax, %rdx
xorl %eax, %eax
callq 0x10c0
leaq 0x848(%rip), %rdi       # 0x21c9
movl $0x186a0, %esi          # imm = 0x186A0
movq %rbx, %rdx
xorl %eax, %eax
callq 0x1080
movq 0x28(%rsp), %rdi
testq %rdi, %rdi
je 0x1a10
callq 0x1030
movq 0x38(%rsp), %rdi
testq %rdi, %rdi
je 0x1a10
callq 0x1030
addq $0xe8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
callq 0x10f0
jmp 0x1816
callq 0x1126
jmp 0x1837
callq 0x115c
jmp 0x1858
callq 0x1192
jmp 0x1879
callq 0x11c8
jmp 0x189a
callq 0x11fe
jmp 0x18bb
callq 0x1234
jmp 0x18de
callq 0x126c
jmp 0x1901
leaq 0x830(%rip), %rdi       # 0x2247
leaq 0x82b(%rip), %rsi       # 0x2249
leaq 0x832(%rip), %rcx       # 0x2257
movl $0xf, %edx
callq 0x1090