// File: /workspace/github_c_makefile_star2_O2/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 0xba4(%rip), %xmm0      # 0x20f0
leaq 0x70(%rsp), %rax
movaps %xmm0, 0x30(%rax)
movaps 0xb84(%rip), %xmm0      # 0x20e0
movaps %xmm0, 0x20(%rax)
movaps 0xb69(%rip), %xmm0      # 0x20d0
movaps %xmm0, 0x10(%rax)
movaps 0xb4e(%rip), %xmm0      # 0x20c0
movaps %xmm0, (%rax)
pushq $0x1
popq %r15
movq %rsp, %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)
andq $0x0, 0x10(%rcx)
movq %r12, 0x18(%rcx)
andq $0x0, 0x20(%rcx)
movq %rax, 0x28(%rcx)
movl $0x400, %r13d           # imm = 0x400
leaq 0x20(%rsp), %r14
movq %r13, (%r14)
movl $0x2000, %edi           # imm = 0x2000
callq 0x10b0
movq %rax, 0x8(%r14)
movq %r13, 0x10(%r14)
movl $0x6000, %edi           # imm = 0x6000
callq 0x10b0
movq %rax, 0x18(%r14)
andq $0x0, (%r12)
movq %r14, %rdi
movq %rbx, %rsi
callq 0x11dc
cmpq %r15, %rax
jne 0x1800
movq $0x1, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x11dc
cmpq $0x1, %rax
jne 0x1835
movq $0x2, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x11dc
cmpq $0x2, %rax
jne 0x186a
movq $0x3, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x11dc
cmpq $0x6, %rax
jne 0x189f
movq $0x4, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x11dc
cmpq $0x18, %rax
jne 0x18d4
movq $0x5, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x11dc
cmpq $0x78, %rax
jne 0x1909
movq $0x6, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x11dc
cmpq $0x2d0, %rax            # imm = 0x2D0
jne 0x193e
movq $0xa, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x11dc
cmpq $0x375f00, %rax         # imm = 0x375F00
jne 0x1975
movl $0x186a0, %r13d         # imm = 0x186A0
callq 0x1060
movq %rax, %rbx
leaq 0x20(%rsp), %r14
leaq 0x40(%rsp), %r15
movabsq $0x13077775800, %rbp    # imm = 0x13077775800
leaq 0xa7e(%rip), %r12       # 0x21af
subl $0x1, %r13d
jb 0x177f
movq $0xf, (%rsp)
movq %r14, %rdi
movq %r15, %rsi
callq 0x11dc
cmpq %rbp, %rax
je 0x1731
movq 0x289a(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x9a0(%rip), %rsi       # 0x2100
leaq 0x9c5(%rip), %rdx       # 0x212c
movl $0x8c, %ecx
leaq 0x9c3(%rip), %r8        # 0x2136
movq %r12, %r9
xorl %eax, %eax
callq 0x10a0
jmp 0x1731
callq 0x1060
subq %rbx, %rax
movq %fs:0x0, %rcx
leaq -0x400(%rcx), %rbx
leaq 0xaa2(%rip), %rsi       # 0x2240
leaq 0xa8e(%rip), %rcx       # 0x2233
movq %rbx, %rdi
movq %rax, %rdx
xorl %eax, %eax
callq 0x10c0
leaq 0xa10(%rip), %rdi       # 0x21c9
movl $0x186a0, %esi          # imm = 0x186A0
movq %rbx, %rdx
xorl %eax, %eax
callq 0x1080
movq 0x28(%rsp), %rdi
testq %rdi, %rdi
je 0x19ac
callq 0x1030
movq 0x38(%rsp), %rdi
testq %rdi, %rdi
je 0x19ac
callq 0x1030
addq $0xe8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x27e9(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x8ef(%rip), %rsi       # 0x2100
leaq 0x914(%rip), %rdx       # 0x212c
leaq 0x917(%rip), %r8        # 0x2136
leaq 0x91c(%rip), %r9        # 0x2142
pushq $0x69
popq %rcx
xorl %eax, %eax
callq 0x10a0
jmp 0x161d
movq 0x27b4(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x8ba(%rip), %rsi       # 0x2100
leaq 0x8df(%rip), %rdx       # 0x212c
leaq 0x8e2(%rip), %r8        # 0x2136
leaq 0x8e7(%rip), %r9        # 0x2142
pushq $0x6d
popq %rcx
xorl %eax, %eax
callq 0x10a0
jmp 0x163e
movq 0x277f(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x885(%rip), %rsi       # 0x2100
leaq 0x8aa(%rip), %rdx       # 0x212c
leaq 0x8ad(%rip), %r8        # 0x2136
leaq 0x8c0(%rip), %r9        # 0x2150
pushq $0x71
popq %rcx
xorl %eax, %eax
callq 0x10a0
jmp 0x165f
movq 0x274a(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x850(%rip), %rsi       # 0x2100
leaq 0x875(%rip), %rdx       # 0x212c
leaq 0x878(%rip), %r8        # 0x2136
leaq 0x899(%rip), %r9        # 0x215e
pushq $0x75
popq %rcx
xorl %eax, %eax
callq 0x10a0
jmp 0x1680
movq 0x2715(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x81b(%rip), %rsi       # 0x2100
leaq 0x840(%rip), %rdx       # 0x212c
leaq 0x843(%rip), %r8        # 0x2136
leaq 0x872(%rip), %r9        # 0x216c
pushq $0x79
popq %rcx
xorl %eax, %eax
callq 0x10a0
jmp 0x16a1
movq 0x26e0(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x7e6(%rip), %rsi       # 0x2100
leaq 0x80b(%rip), %rdx       # 0x212c
leaq 0x80e(%rip), %r8        # 0x2136
leaq 0x84c(%rip), %r9        # 0x217b
pushq $0x7d
popq %rcx
xorl %eax, %eax
callq 0x10a0
jmp 0x16c2
movq 0x26ab(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x7b1(%rip), %rsi       # 0x2100
leaq 0x7d6(%rip), %rdx       # 0x212c
leaq 0x7d9(%rip), %r8        # 0x2136
leaq 0x827(%rip), %r9        # 0x218b
movl $0x81, %ecx
xorl %eax, %eax
callq 0x10a0
jmp 0x16e5
movq 0x2674(%rip), %rax      # 0x3ff0
movq (%rax), %rdi
leaq 0x77a(%rip), %rsi       # 0x2100
leaq 0x79f(%rip), %rdx       # 0x212c
leaq 0x7a2(%rip), %r8        # 0x2136
leaq 0x800(%rip), %r9        # 0x219b
movl $0x85, %ecx
xorl %eax, %eax
callq 0x10a0
jmp 0x1708
leaq 0x894(%rip), %rdi       # 0x2247
leaq 0x88f(%rip), %rsi       # 0x2249
leaq 0x896(%rip), %rcx       # 0x2257
pushq $0xf
popq %rdx
callq 0x1090