// File: /workspace/github_c_makefile_star2_O0/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
movq %rsp, %rbp
subq $0x1d0, %rsp            # imm = 0x1D0
leaq -0x60(%rbp), %rdi
leaq 0x114a(%rip), %rsi      # 0x30c0
movl $0x40, %edx
callq 0x10a0
movq $0x1, -0xe0(%rbp)
movq $0x1, -0xd8(%rbp)
leaq -0xc0(%rbp), %rax
movq %rax, -0xd0(%rbp)
leaq -0xe0(%rbp), %rax
movq %rax, -0xe8(%rbp)
movq $0x0, -0x118(%rbp)
movq $0x0, -0x110(%rbp)
movq $0x3, -0x108(%rbp)
leaq -0xe0(%rbp), %rax
movq %rax, -0x100(%rbp)
movq $0x10, -0xf8(%rbp)
leaq -0x60(%rbp), %rax
movq %rax, -0xf0(%rbp)
leaq -0x90(%rbp), %rdi
leaq -0x118(%rbp), %rsi
movl $0x30, %edx
callq 0x10a0
movq $0x2, -0x148(%rbp)
movq $0x1, -0x140(%rbp)
movq $0x0, -0x138(%rbp)
leaq -0xe0(%rbp), %rax
movq %rax, -0x130(%rbp)
movq $0x0, -0x128(%rbp)
leaq -0x60(%rbp), %rax
addq $0x14, %rax
movq %rax, -0x120(%rbp)
leaq -0xc0(%rbp), %rdi
leaq -0x148(%rbp), %rsi
movl $0x30, %edx
callq 0x10a0
movq $0x400, -0x168(%rbp) # imm = 0x400
movq $0x2000, -0x18(%rbp)    # imm = 0x2000
movq -0x18(%rbp), %rdi
callq 0x10b0
movq %rax, -0x160(%rbp)
movq $0x400, -0x158(%rbp) # imm = 0x400
movq $0x6000, -0x20(%rbp)    # imm = 0x6000
movq -0x20(%rbp), %rdi
callq 0x10b0
movq %rax, -0x150(%rbp)
movq -0xe8(%rbp), %rax
movq $0x0, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x178(%rbp)
movq -0x178(%rbp), %rax
movq %rax, -0x170(%rbp)
cmpq $0x1, -0x170(%rbp)
jne 0x20f8
jmp 0x212a
movq 0x2ef1(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xff7(%rip), %rsi       # 0x3100
leaq 0x101c(%rip), %rdx      # 0x312c
movl $0x69, %ecx
leaq 0x101a(%rip), %r8       # 0x3136
leaq 0x101f(%rip), %r9       # 0x3142
movb $0x0, %al
callq 0x1090
movq -0xe8(%rbp), %rax
movq $0x1, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x180(%rbp)
movq -0x180(%rbp), %rax
movq %rax, -0x170(%rbp)
cmpq $0x1, -0x170(%rbp)
jne 0x216c
jmp 0x219e
movq 0x2e7d(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xf83(%rip), %rsi       # 0x3100
leaq 0xfa8(%rip), %rdx       # 0x312c
movl $0x6d, %ecx
leaq 0xfa6(%rip), %r8        # 0x3136
leaq 0xfab(%rip), %r9        # 0x3142
movb $0x0, %al
callq 0x1090
movq -0xe8(%rbp), %rax
movq $0x2, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x188(%rbp)
movq -0x188(%rbp), %rax
movq %rax, -0x170(%rbp)
cmpq $0x2, -0x170(%rbp)
jne 0x21e0
jmp 0x2212
movq 0x2e09(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xf0f(%rip), %rsi       # 0x3100
leaq 0xf34(%rip), %rdx       # 0x312c
movl $0x71, %ecx
leaq 0xf32(%rip), %r8        # 0x3136
leaq 0xf45(%rip), %r9        # 0x3150
movb $0x0, %al
callq 0x1090
movq -0xe8(%rbp), %rax
movq $0x3, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x190(%rbp)
movq -0x190(%rbp), %rax
movq %rax, -0x170(%rbp)
cmpq $0x6, -0x170(%rbp)
jne 0x2254
jmp 0x2286
movq 0x2d95(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xe9b(%rip), %rsi       # 0x3100
leaq 0xec0(%rip), %rdx       # 0x312c
movl $0x75, %ecx
leaq 0xebe(%rip), %r8        # 0x3136
leaq 0xedf(%rip), %r9        # 0x315e
movb $0x0, %al
callq 0x1090
movq -0xe8(%rbp), %rax
movq $0x4, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x198(%rbp)
movq -0x198(%rbp), %rax
movq %rax, -0x170(%rbp)
cmpq $0x18, -0x170(%rbp)
jne 0x22c8
jmp 0x22fa
movq 0x2d21(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xe27(%rip), %rsi       # 0x3100
leaq 0xe4c(%rip), %rdx       # 0x312c
movl $0x79, %ecx
leaq 0xe4a(%rip), %r8        # 0x3136
leaq 0xe79(%rip), %r9        # 0x316c
movb $0x0, %al
callq 0x1090
movq -0xe8(%rbp), %rax
movq $0x5, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x1a0(%rbp)
movq -0x1a0(%rbp), %rax
movq %rax, -0x170(%rbp)
cmpq $0x78, -0x170(%rbp)
jne 0x233c
jmp 0x236e
movq 0x2cad(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xdb3(%rip), %rsi       # 0x3100
leaq 0xdd8(%rip), %rdx       # 0x312c
movl $0x7d, %ecx
leaq 0xdd6(%rip), %r8        # 0x3136
leaq 0xe14(%rip), %r9        # 0x317b
movb $0x0, %al
callq 0x1090
movq -0xe8(%rbp), %rax
movq $0x6, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x1a8(%rbp)
movq -0x1a8(%rbp), %rax
movq %rax, -0x170(%rbp)
cmpq $0x2d0, -0x170(%rbp) # imm = 0x2D0
jne 0x23b3
jmp 0x23e5
movq 0x2c36(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xd3c(%rip), %rsi       # 0x3100
leaq 0xd61(%rip), %rdx       # 0x312c
movl $0x81, %ecx
leaq 0xd5f(%rip), %r8        # 0x3136
leaq 0xdad(%rip), %r9        # 0x318b
movb $0x0, %al
callq 0x1090
movq -0xe8(%rbp), %rax
movq $0xa, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x1b0(%rbp)
movq -0x1b0(%rbp), %rax
movq %rax, -0x170(%rbp)
cmpq $0x375f00, -0x170(%rbp) # imm = 0x375F00
jne 0x242a
jmp 0x245c
movq 0x2bbf(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xcc5(%rip), %rsi       # 0x3100
leaq 0xcea(%rip), %rdx       # 0x312c
movl $0x85, %ecx
leaq 0xce8(%rip), %r8        # 0x3136
leaq 0xd46(%rip), %r9        # 0x319b
movb $0x0, %al
callq 0x1090
callq 0x2770
movq %rax, -0x1b8(%rbp)
movl $0x186a0, -0x1bc(%rbp)  # imm = 0x186A0
movl $0x0, -0x1c0(%rbp)
cmpl $0x186a0, -0x1c0(%rbp)  # imm = 0x186A0
jge 0x251f
movq -0xe8(%rbp), %rax
movq $0xf, (%rax)
leaq -0x168(%rbp), %rdi
leaq -0x90(%rbp), %rsi
callq 0x11e0
movq %rax, -0x1c8(%rbp)
movq -0x1c8(%rbp), %rax
movq %rax, -0x170(%rbp)
movabsq $0x13077775800, %rax    # imm = 0x13077775800
cmpq %rax, -0x170(%rbp)
jne 0x24d7
jmp 0x2509
movq 0x2b12(%rip), %rax      # 0x4ff0
movq (%rax), %rdi
leaq 0xc18(%rip), %rsi       # 0x3100
leaq 0xc3d(%rip), %rdx       # 0x312c
movl $0x8c, %ecx
leaq 0xc3b(%rip), %r8        # 0x3136
leaq 0xcad(%rip), %r9        # 0x31af
movb $0x0, %al
callq 0x1090
jmp 0x250b
movl -0x1c0(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x1c0(%rbp)
jmp 0x247c
movq -0x1b8(%rbp), %rdi
callq 0x2670
movq %rax, %rdi
movl $0x1, %esi
callq 0x25d0
movq %rax, %rdx
leaq 0xc87(%rip), %rdi       # 0x31c9
movl $0x186a0, %esi          # imm = 0x186A0
movb $0x0, %al
callq 0x1070
movq -0x160(%rbp), %rax
movq %rax, -0x8(%rbp)
cmpq $0x0, -0x8(%rbp)
jne 0x257f
leaq 0xced(%rip), %rdi       # 0x3254
leaq 0xce8(%rip), %rsi       # 0x3256
movl $0xf, %edx
leaq 0xcea(%rip), %rcx       # 0x3264
callq 0x1080
movq -0x8(%rbp), %rdi
callq 0x1030
movq -0x150(%rbp), %rax
movq %rax, -0x10(%rbp)
cmpq $0x0, -0x10(%rbp)
jne 0x25b9
leaq 0xcb3(%rip), %rdi       # 0x3254
leaq 0xcae(%rip), %rsi       # 0x3256
movl $0xf, %edx
leaq 0xcb0(%rip), %rcx       # 0x3264
callq 0x1080
movq -0x10(%rbp), %rdi
callq 0x1030
addq $0x1d0, %rsp            # imm = 0x1D0
popq %rbp
retq
nopl (%rax,%rax)