nx_x86_64_branch_test.nx source
↩ module page · 110 lines · 3660 B
1// nx_x86_64_branch_test.nx -- session 3 smoke for cmp/setcc/branch.
2//
3// Builds a program that exercises every cmp/branch primitive:
4//
5// rax := 5
6// rcx := 5
7// cmpq %rcx, %rax ; rax - rcx == 0
8// sete %al ; al = 1
9// movzbq %al, %rax ; rax = 1
10// testq %rax, %rax ; flags from rax (non-zero)
11// jne .Leq ; branch taken
12// write 'N' and exit (should NOT reach)
13// .Leq:
14// write 'Y' (means: equality compare worked)
15//
16// rax := 3
17// rcx := 7
18// cmpq %rcx, %rax ; rax - rcx == -4 < 0
19// jl .Llt ; signed less-than (cmpq operand order:
20// ; AT&T `cmpq src1, src2` is src2 - src1,
21// ; so jl jumps when src2 < src1 -- here
22// ; rax (3) < rcx (7) yes)
23// write 'B' and exit (should NOT reach)
24// .Llt:
25// write 'L' (means: signed-less-than branch worked)
26//
27// jmp .Ldone
28// .Ldone:
29// exit 0
30//
31// Expected stdout: "YL" (2 bytes hex 59 4C).
32
33import "syscalls.nx"
34import "nx_outbuf.nx"
35import "nx_x86_64.nx"
36
37func emit_write_byte(o: *OutBuf, ch: i64, scratch_disp: i64) -> i64 {
38 // store byte ch at scratch_disp(%rbp); write(1, &scratch, 1)
39 x86_emit_movabsq(o, "rax" as *u8, ch)
40 x86_emit_store_byte(o, "rax" as *u8, "rbp" as *u8, scratch_disp)
41 x86_emit_movabsq(o, "rdi" as *u8, 1)
42 x86_emit_lea_disp(o, "rbp" as *u8, scratch_disp, "rsi" as *u8)
43 x86_emit_movabsq(o, "rdx" as *u8, 1)
44 x86_emit_movabsq(o, "rax" as *u8, NX_X64_SYS_WRITE)
45 x86_emit_syscall(o)
46 return 0
47}
48
49func main() -> i64 {
50 let o: *OutBuf = out_new(8192)
51
52 let main_name: *u8 = "main" as *u8
53 let _start_name: *u8 = "_start" as *u8
54 let l_eq: *u8 = ".Leq" as *u8
55 let l_lt: *u8 = ".Llt" as *u8
56 let l_done: *u8 = ".Ldone" as *u8
57 let l_ne_path: *u8 = ".Lne_path" as *u8
58 let l_ge_path: *u8 = ".Lge_path" as *u8
59
60 out_str(o, "# Emitted by nx_x86_64.nx session 3 (cmp/setcc/branch)\n")
61 out_str(o, " .att_syntax prefix\n")
62
63 // _start trampoline
64 x86_emit_function_start(o, _start_name)
65 out_str(o, " call main\n")
66 x86_emit_movabsq(o, "rax" as *u8, NX_X64_SYS_EXIT)
67 x86_emit_movabsq(o, "rdi" as *u8, 0)
68 x86_emit_syscall(o)
69 x86_emit_function_end(o, _start_name)
70
71 x86_emit_function_start(o, main_name)
72 x86_emit_prologue(o, 16)
73
74 // ---- Block 1: equality compare ----
75 x86_emit_movabsq(o, "rax" as *u8, 5)
76 x86_emit_movabsq(o, "rcx" as *u8, 5)
77 x86_emit_cmpq_rr(o, "rcx" as *u8, "rax" as *u8)
78 x86_emit_setcc(o, NX_X64_CC_EQ, "al" as *u8)
79 x86_emit_movzbq_rr(o, "al" as *u8, "rax" as *u8)
80 // rax should be 1; testq then jne -> .Leq
81 x86_emit_testq_rr(o, "rax" as *u8, "rax" as *u8)
82 x86_emit_jcc_label(o, NX_X64_CC_NE, l_eq)
83 // unreachable fallthrough: write 'N' + exit
84 emit_write_byte(o, 0x4E, 0 - 1) // 'N'
85 x86_emit_jmp_label(o, l_done)
86
87 x86_emit_label(o, l_eq)
88 emit_write_byte(o, 0x59, 0 - 1) // 'Y'
89
90 // ---- Block 2: signed less-than ----
91 x86_emit_movabsq(o, "rax" as *u8, 3)
92 x86_emit_movabsq(o, "rcx" as *u8, 7)
93 x86_emit_cmpq_rr(o, "rcx" as *u8, "rax" as *u8)
94 x86_emit_jcc_label(o, NX_X64_CC_LT_S, l_lt)
95 emit_write_byte(o, 0x42, 0 - 1) // 'B' (bad)
96 x86_emit_jmp_label(o, l_done)
97
98 x86_emit_label(o, l_lt)
99 emit_write_byte(o, 0x4C, 0 - 1) // 'L'
100
101 x86_emit_label(o, l_done)
102 x86_emit_movabsq(o, "rax" as *u8, 0)
103 x86_emit_epilogue(o)
104 x86_emit_function_end(o, main_name)
105
106 x86_emit_gnu_stack_note(o)
107
108 sys_write(1, o.buf, o.pos)
109 return 0
110}