// File: /workspace/github_c_makefile_star2_O3/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, 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 0x19bc
movq $0x1, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x1, %rax
jne 0x19c6
movq $0x2, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x2, %rax
jne 0x19d0
movq $0x3, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x6, %rax
jne 0x19da
movq $0x4, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x18, %rax
jne 0x19e4
movq $0x5, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x78, %rax
jne 0x19ee
movq $0x6, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x2d0, %rax            # imm = 0x2D0
jne 0x19f8
movq $0xa, (%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x13cc
cmpq $0x375f00, %rax         # imm = 0x375F00
jne 0x1a02
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 0x193c
decl %ebp
jne 0x191e
jmp 0x1943
callq 0x12a4
jmp 0x1936
callq 0x1060
subq %rbx, %rax
movq %fs:0x0, %rcx
leaq -0x400(%rcx), %rbx
leaq 0x8de(%rip), %rsi       # 0x2240
leaq 0x8ca(%rip), %rcx       # 0x2233
movq %rbx, %rdi
movq %rax, %rdx
xorl %eax, %eax
callq 0x10c0
leaq 0x84c(%rip), %rdi       # 0x21c9
movl $0x186a0, %esi          # imm = 0x186A0
movq %rbx, %rdx
xorl %eax, %eax
callq 0x1080
movq 0x28(%rsp), %rdi
testq %rdi, %rdi
je 0x1a0c
callq 0x1030
movq 0x38(%rsp), %rdi
testq %rdi, %rdi
je 0x1a0c
callq 0x1030
addq $0xe8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
callq 0x10f0
jmp 0x1812
callq 0x1126
jmp 0x1833
callq 0x115c
jmp 0x1854
callq 0x1192
jmp 0x1875
callq 0x11c8
jmp 0x1896
callq 0x11fe
jmp 0x18b7
callq 0x1234
jmp 0x18da
callq 0x126c
jmp 0x18fd
leaq 0x834(%rip), %rdi       # 0x2247
leaq 0x82f(%rip), %rsi       # 0x2249
leaq 0x836(%rip), %rcx       # 0x2257
movl $0xf, %edx
callq 0x1090