v / vlib / v2 / gen / x64 / x64_abi_test.v
2752 lines · 2606 sloc · 62.65 KB · ddb021b9866c3b4523b746fa2f4c16a594f8bd89
Raw
1module x64
2
3import v2.mir
4import v2.ssa
5
6fn contains_bytes(haystack []u8, needle []u8) bool {
7 if needle.len == 0 {
8 return true
9 }
10 if needle.len > haystack.len {
11 return false
12 }
13 for i in 0 .. haystack.len - needle.len + 1 {
14 if haystack[i..i + needle.len] == needle {
15 return true
16 }
17 }
18 return false
19}
20
21fn index_bytes(haystack []u8, needle []u8) int {
22 if needle.len == 0 {
23 return 0
24 }
25 if needle.len > haystack.len {
26 return -1
27 }
28 for i in 0 .. haystack.len - needle.len + 1 {
29 if haystack[i..i + needle.len] == needle {
30 return i
31 }
32 }
33 return -1
34}
35
36fn last_index_bytes(haystack []u8, needle []u8) int {
37 if needle.len == 0 {
38 return haystack.len
39 }
40 if needle.len > haystack.len {
41 return -1
42 }
43 for i := haystack.len - needle.len; i >= 0; i-- {
44 if haystack[i..i + needle.len] == needle {
45 return i
46 }
47 }
48 return -1
49}
50
51fn test_x64_abi_int_argument_registers() {
52 assert X64Abi.sysv.int_arg_regs() == [int(rdi), int(rsi), int(rdx), int(rcx), int(r8), int(r9)]
53 assert X64Abi.windows.int_arg_regs() == [int(rcx), int(rdx), int(r8), int(r9)]
54 assert X64Abi.sysv.int_arg_reg_at(0) == int(rdi)
55 assert X64Abi.sysv.int_arg_reg_at(5) == int(r9)
56 assert X64Abi.sysv.int_arg_reg_at(6) == x64_no_arg_reg
57}
58
59fn test_x64_abi_float_argument_registers() {
60 assert X64Abi.sysv.float_arg_regs() == [0, 1, 2, 3, 4, 5, 6, 7]
61 assert X64Abi.windows.float_arg_regs() == [0, 1, 2, 3]
62 assert X64Abi.sysv.float_arg_reg_at(0) == 0
63 assert X64Abi.sysv.float_arg_reg_at(7) == 7
64 assert X64Abi.sysv.float_arg_reg_at(8) == x64_no_arg_reg
65}
66
67fn test_x64_union_type_layout_uses_overlapping_fields() {
68 mut ts := ssa.TypeStore.new()
69 f64_t := ts.get_float(64)
70 u64_t := ts.get_uint(64)
71 union_t := ts.register(ssa.Type{
72 kind: .struct_t
73 fields: [f64_t, u64_t]
74 field_names: ['f', 'u']
75 is_union: true
76 })
77 mut mod := mir.Module{
78 type_store: unsafe { *ts }
79 }
80 gen := Gen.new(&mod)
81
82 assert gen.struct_field_offset_bytes(union_t, 0) == 0
83 assert gen.struct_field_offset_bytes(union_t, 1) == 0
84 assert gen.type_align(union_t) == 8
85 assert gen.type_size(union_t) == 8
86}
87
88fn test_x64_abi_basic_argument_position_matrix() {
89 sysv_int := [int(rdi), int(rsi), int(rdx), int(rcx), int(r8), int(r9), x64_no_arg_reg,
90 x64_no_arg_reg, x64_no_arg_reg]
91 sysv_float := [0, 1, 2, 3, 4, 5, 6, 7, x64_no_arg_reg]
92 windows_int := [int(rcx), int(rdx), int(r8), int(r9), x64_no_arg_reg, x64_no_arg_reg,
93 x64_no_arg_reg, x64_no_arg_reg, x64_no_arg_reg]
94 windows_float := [0, 1, 2, 3, x64_no_arg_reg, x64_no_arg_reg, x64_no_arg_reg, x64_no_arg_reg,
95 x64_no_arg_reg]
96
97 for position in 0 .. 9 {
98 assert X64Abi.sysv.int_arg_reg_at(position) == sysv_int[position]
99 assert X64Abi.sysv.float_arg_reg_at(position) == sysv_float[position]
100 assert X64Abi.windows.int_arg_reg_for_position(position) == windows_int[position]
101 assert X64Abi.windows.float_arg_reg_for_position(position) == windows_float[position]
102 }
103}
104
105fn test_x64_windows_argument_registers_are_position_based() {
106 assert X64Abi.windows.uses_positional_arg_regs()
107 assert X64Abi.windows.int_arg_reg_for_position(0) == int(rcx)
108 assert X64Abi.windows.float_arg_reg_for_position(1) == 1
109 assert X64Abi.windows.int_arg_reg_for_position(2) == int(r8)
110 assert X64Abi.windows.float_arg_reg_for_position(3) == 3
111 assert X64Abi.windows.int_arg_reg_for_position(4) == x64_no_arg_reg
112 assert X64Abi.windows.float_arg_reg_for_position(4) == x64_no_arg_reg
113}
114
115fn test_x64_sysv_argument_helpers_keep_compact_register_order() {
116 assert !X64Abi.sysv.uses_positional_arg_regs()
117 assert X64Abi.sysv.int_arg_reg_at(0) == int(rdi)
118 assert X64Abi.sysv.int_arg_reg_at(1) == int(rsi)
119 assert X64Abi.sysv.float_arg_reg_at(0) == 0
120 assert X64Abi.sysv.float_arg_reg_at(1) == 1
121}
122
123fn test_x64_abi_shadow_space_size() {
124 assert X64Abi.sysv.shadow_space_size() == 0
125 assert X64Abi.windows.shadow_space_size() == 32
126 assert X64Abi.windows.call_stack_arg_offset(4) == 32
127 assert X64Abi.windows.call_stack_arg_offset(5) == 40
128 assert X64Abi.windows.stack_arg_offset(4) == 48
129 assert X64Abi.windows.stack_arg_offset(5) == 56
130 assert X64Abi.windows.call_frame_size(0) == 32
131 assert X64Abi.windows.call_frame_size(1) == 48
132}
133
134fn test_x64_windows_stack_argument_offset_matrix() {
135 expected_call_offsets := [32, 40, 48, 56, 64]
136 expected_callee_offsets := [48, 56, 64, 72, 80]
137 positions := [4, 5, 6, 7, 8]
138 for i, position in positions {
139 assert X64Abi.windows.call_stack_arg_offset(position) == expected_call_offsets[i]
140 assert X64Abi.windows.stack_arg_offset(position) == expected_callee_offsets[i]
141 }
142 assert X64Abi.windows.call_frame_size(0) == 32
143 assert X64Abi.windows.call_frame_size(1) == 48
144 assert X64Abi.windows.call_frame_size(2) == 48
145 assert X64Abi.windows.call_frame_size(3) == 64
146 assert X64Abi.windows.call_frame_size(4) == 64
147 assert X64Abi.windows.call_frame_size(5) == 80
148}
149
150fn test_x64_abi_sret_registers_are_explicit() {
151 assert X64Abi.sysv.sret_reg() == rdi
152 assert X64Abi.sysv.sret_arg_regs() == [int(rsi), int(rdx), int(rcx), int(r8), int(r9)]
153 assert X64Abi.windows.sret_reg() == rcx
154 assert X64Abi.windows.sret_arg_regs() == [int(rdx), int(r8), int(r9)]
155}
156
157fn test_x64_gen_defaults_to_sysv_abi() {
158 mut mod := mir.Module{}
159 gen := Gen.new(&mod)
160 assert gen.abi == .sysv
161
162 coff_gen := Gen.new_with_format(&mod, .coff)
163 assert coff_gen.abi == .sysv
164
165 windows_gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
166 assert windows_gen.abi == .windows
167}
168
169fn test_x64_windows_stack_arg_mask_is_position_based() {
170 mut mod := new_x64_abi_test_module()
171 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
172 instr := mir.Instruction{
173 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5]
174 }
175
176 assert gen.call_stack_arg_mask(instr, gen.abi.int_arg_regs().len, 0) == [false, false, false,
177 false, true]
178 assert gen.call_stack_arg_mask(instr, gen.abi.sret_arg_regs().len, 1) == [false, false, false,
179 true, true]
180}
181
182fn test_x64_sysv_stack_arg_mask_keeps_separate_int_and_float_counters() {
183 mut mod := new_x64_abi_test_module()
184 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
185 instr := mir.Instruction{
186 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5]
187 }
188
189 assert gen.call_stack_arg_mask(instr, gen.abi.int_arg_regs().len, 0) == [false, false, false,
190 false, false]
191}
192
193fn test_x64_sysv_stack_arg_mask_counts_two_eightbyte_aggregate_registers() {
194 mut mod := new_x64_abi_pair_arg_test_module()
195 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
196 // x86-64 psABI 3.2.3 classifies small integer aggregates by eightbyte:
197 // this Pair consumes r8/r9 after four scalar INTEGER arguments, so the
198 // following scalar argument must be passed on the stack.
199 pair_then_scalar := mir.Instruction{
200 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5, 6]
201 }
202 stack_after_pair := gen.call_stack_arg_mask(pair_then_scalar, gen.abi.int_arg_regs().len, 0)
203
204 assert stack_after_pair == [false, false, false, false, false, true]
205 assert gen.call_stack_slots(pair_then_scalar, stack_after_pair) == 1
206}
207
208fn test_x64_sysv_stack_arg_mask_spilled_aggregate_does_not_consume_remaining_register() {
209 mut mod := new_x64_abi_pair_arg_test_module()
210 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
211 // If an aggregate cannot fit entirely in the remaining INTEGER registers,
212 // psABI 3.2.3 requires it to roll back to the stack; the next scalar can
213 // still use the remaining register.
214 spilled_pair_then_scalar := mir.Instruction{
215 operands: [ssa.ValueID(0), 1, 2, 3, 4, 6, 5, 7]
216 }
217 stack_after_spill := gen.call_stack_arg_mask(spilled_pair_then_scalar,
218 gen.abi.int_arg_regs().len, 0)
219
220 assert stack_after_spill == [false, false, false, false, false, true, false]
221 assert gen.call_stack_slots(spilled_pair_then_scalar, stack_after_spill) == 2
222}
223
224fn test_x64_windows_call_frame_reserves_shadow_space() {
225 mut mod := new_x64_abi_test_module()
226 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
227
228 cleanup := gen.emit_windows_call_frame(1)
229 assert cleanup == 48
230 gen.cleanup_windows_call_frame(cleanup)
231 assert gen.coff.text_data == [u8(0x48), 0x83, 0xec, 0x30, 0x48, 0x83, 0xc4, 0x30]
232}
233
234fn test_x64_windows_large_stack_allocation_probes_each_page() {
235 mut mod := new_x64_abi_test_module()
236 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
237 gen.stack_size = x64_windows_stack_probe_page_size * 2 + 8
238
239 gen.emit_stack_allocation()
240
241 page_probe := [u8(0x48), 0x81, 0xec, 0x00, 0x10, 0x00, 0x00, 0xf6, 0x04, 0x24, 0x00]
242 mut expected := []u8{}
243 expected << page_probe
244 expected << page_probe
245 expected << [u8(0x48), 0x83, 0xec, 0x08, 0xf6, 0x04, 0x24, 0x00]
246 assert gen.coff.text_data == expected
247}
248
249fn test_x64_sysv_large_stack_allocation_uses_single_sub() {
250 mut mod := new_x64_abi_test_module()
251 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
252 gen.stack_size = x64_windows_stack_probe_page_size * 2 + 8
253
254 gen.emit_stack_allocation()
255
256 assert gen.elf.text_data == [u8(0x48), 0x81, 0xec, 0x08, 0x20, 0x00, 0x00]
257}
258
259fn test_x64_windows_stack_arg_store_starts_after_shadow_space() {
260 mut mod := new_x64_abi_test_module()
261 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
262 instr := mir.Instruction{
263 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5]
264 }
265
266 gen.store_windows_call_stack_arg(5, 4, 4, instr)
267 assert gen.coff.text_data == [u8(0xb8), 0x05, 0, 0, 0, 0x48, 0x89, 0x44, 0x24, 0x20]
268}
269
270fn test_x64_windows_integer_stack_arg_store_writes_full_slot() {
271 mut mod := new_x64_abi_u32_stack_arg_test_module()
272 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
273 instr := mir.Instruction{
274 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5]
275 }
276
277 gen.store_windows_call_stack_arg(5, 4, 4, instr)
278 assert gen.coff.text_data == [u8(0x31), 0xc0, 0x48, 0x89, 0x44, 0x24, 0x20]
279}
280
281fn test_x64_windows_does_not_emit_sysv_sse_arg_count() {
282 mut mod := new_x64_abi_test_module()
283 mut win_gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
284 win_gen.emit_sse_arg_count(2)
285 assert win_gen.coff.text_data.len == 0
286
287 mut sysv_gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
288 sysv_gen.emit_sse_arg_count(2)
289 assert sysv_gen.elf.text_data == [u8(0xb8), 0x02, 0, 0, 0]
290}
291
292fn test_x64_windows_codegen_mixed_call_uses_positional_registers() {
293 mut mod := new_x64_abi_call_module(false)
294 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
295 gen.gen()
296 code := gen.coff.text_data
297
298 assert contains_bytes(code, [u8(0x48), 0x83, 0xec, 0x30])
299 assert contains_bytes(code, [u8(0xb9), 0x01, 0, 0, 0]) // arg0 int -> RCX
300 assert contains_bytes(code, [u8(0xf2), 0x0f, 0x10, 0x4d]) // arg1 float -> XMM1
301 assert contains_bytes(code, [u8(0x41), 0xb8, 0x03, 0, 0, 0]) // arg2 int -> R8
302 assert contains_bytes(code, [u8(0xf2), 0x0f, 0x10, 0x5d]) // arg3 float -> XMM3
303 assert contains_bytes(code, [u8(0xb8), 0x05, 0, 0, 0, 0x48, 0x89, 0x44, 0x24, 0x20])
304 assert !contains_bytes(code, [u8(0xb8), 0x02, 0, 0, 0]) // no SysV AL SSE count
305}
306
307fn test_x64_windows_codegen_call_sret_shifts_user_arg_registers() {
308 mut mod := new_x64_abi_call_module(true)
309 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
310 gen.gen()
311 code := gen.coff.text_data
312
313 assert contains_bytes(code, [u8(0x48), 0x89, 0xc1]) // hidden sret pointer -> RCX
314 assert contains_bytes(code, [u8(0xba), 0x01, 0, 0, 0]) // first user int -> RDX
315 assert contains_bytes(code, [u8(0xf2), 0x0f, 0x10, 0x55]) // second user float -> XMM2
316}
317
318fn test_x64_windows_codegen_call_sret_with_indirect_receiver_shifts_user_args() {
319 mut mod := new_x64_abi_sret_indirect_receiver_call_module()
320 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
321 gen.gen()
322 code := gen.coff.text_data
323
324 end_arg := index_bytes(code, [u8(0x41), 0xb9, 0x03, 0, 0, 0]) // end -> R9
325 start_arg := index_bytes(code, [u8(0x41), 0xb8, 0x01, 0, 0, 0]) // start -> R8
326 receiver_arg := index_bytes(code, [u8(0x48), 0x89, 0xc2]) // receiver pointer -> RDX
327 hidden_sret := index_bytes(code, [u8(0x48), 0x89, 0xc1]) // hidden sret pointer -> RCX
328
329 assert end_arg >= 0
330 assert start_arg > end_arg
331 assert receiver_arg > start_arg
332 assert hidden_sret > receiver_arg
333}
334
335fn test_x64_windows_codegen_sret_callee_prologue_shifts_indirect_receiver_and_scalars() {
336 mut mod := new_x64_abi_sret_indirect_receiver_callee_module()
337 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
338 gen.gen()
339 code := gen.coff.text_data
340
341 receiver_from_rdx := index_bytes(code, [u8(0x49), 0x89, 0xd2]) // mov r10, rdx
342 receiver_from_rcx := index_bytes(code, [u8(0x49), 0x89, 0xca]) // mov r10, rcx
343 start_from_r8 := index_bytes(code, [u8(0x4c), 0x89, 0x45]) // mov [rbp+disp8], r8
344 end_from_r9 := index_bytes(code, [u8(0x4c), 0x89, 0x4d]) // mov [rbp+disp8], r9
345
346 assert receiver_from_rdx >= 0
347 assert receiver_from_rcx < 0
348 assert start_from_r8 > receiver_from_rdx
349 assert end_from_r9 > start_from_r8
350}
351
352fn test_x64_sysv_codegen_direct_integer_pair_call_result_stores_rax_and_rdx() {
353 mut mod := new_x64_abi_pair_return_call_module()
354 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
355 gen.gen()
356 code := gen.elf.text_data
357
358 rax_store := index_bytes(code, [u8(0x48), 0x89, 0x45]) // mov [rbp+disp8], rax
359 rdx_store := index_bytes(code, [u8(0x48), 0x89, 0x55]) // mov [rbp+disp8], rdx
360
361 assert rax_store >= 0
362 assert rdx_store > rax_store
363}
364
365fn test_x64_sysv_codegen_direct_integer_pair_return_loads_rax_and_rdx() {
366 mut mod := new_x64_abi_pair_return_callee_module()
367 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
368 gen.gen()
369 code := gen.elf.text_data
370
371 rax_load := index_bytes(code, [u8(0x49), 0x8b, 0x02]) // mov rax, [r10]
372 rdx_load := index_bytes(code, [u8(0x49), 0x8b, 0x52, 0x08]) // mov rdx, [r10+8]
373
374 assert rax_load >= 0
375 assert rdx_load > rax_load
376}
377
378fn test_x64_sysv_codegen_mixed_aggregate_param_stores_gpr_and_xmm() {
379 mut mod := new_x64_abi_mixed_aggregate_call_module()
380 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
381 gen.gen()
382 code := gen.elf.text_data
383
384 assert contains_bytes(code, [u8(0x48), 0x89, 0x7d]) // RDI -> integer eightbyte
385 assert contains_bytes(code, [u8(0xf2), 0x0f, 0x11, 0x45]) // XMM0 -> SSE eightbyte
386}
387
388fn test_x64_sysv_codegen_mixed_aggregate_call_arg_loads_gpr_and_xmm() {
389 mut mod := new_x64_abi_mixed_aggregate_call_module()
390 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
391 gen.gen()
392 code := gen.elf.text_data
393
394 rdi_load := index_bytes(code, [u8(0x49), 0x8b, 0x3a]) // mov rdi, [r10]
395 xmm0_load := index_bytes(code, [u8(0xf2), 0x41, 0x0f, 0x10, 0x42, 0x08]) // movsd xmm0, [r10+8]
396
397 assert rdi_load >= 0
398 assert xmm0_load > rdi_load
399}
400
401fn test_x64_sysv_codegen_mixed_aggregate_call_sets_sse_arg_count_to_one() {
402 mut mod := new_x64_abi_mixed_aggregate_call_module()
403 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
404 gen.gen()
405 code := gen.elf.text_data
406
407 xmm0_load := index_bytes(code, [u8(0xf2), 0x41, 0x0f, 0x10, 0x42, 0x08]) // movsd xmm0, [r10+8]
408 al_count := index_bytes(code, [u8(0xb8), 0x01, 0, 0, 0]) // mov eax, 1
409 call := last_index_bytes(code, [u8(0xe8)])
410
411 assert xmm0_load >= 0
412 assert al_count > xmm0_load
413 assert call > al_count
414}
415
416fn test_x64_sysv_codegen_indirect_f64_call_loads_callee_before_sse_arg_count() {
417 code := emit_x64_abi_indirect_f64_call_with_callee_in_rax(false)
418
419 callee_load := index_bytes(code, [u8(0x49), 0x89, 0xc2]) // mov r10, rax
420 al_count := index_bytes(code, [u8(0xb8), 0x01, 0, 0, 0]) // mov eax, 1
421 call := index_bytes(code, [u8(0x41), 0xff, 0xd2]) // call r10
422
423 assert callee_load >= 0
424 assert al_count > callee_load
425 assert call > al_count
426}
427
428fn test_x64_sysv_codegen_indirect_f64_call_sret_loads_callee_before_sse_arg_count() {
429 code := emit_x64_abi_indirect_f64_call_with_callee_in_rax(true)
430
431 callee_load := index_bytes(code, [u8(0x49), 0x89, 0xc2]) // mov r10, rax
432 al_count := index_bytes(code, [u8(0xb8), 0x01, 0, 0, 0]) // mov eax, 1
433 call := index_bytes(code, [u8(0x41), 0xff, 0xd2]) // call r10
434
435 assert callee_load >= 0
436 assert al_count > callee_load
437 assert call > al_count
438}
439
440fn test_x64_sysv_codegen_call_sret_shifts_mixed_user_arg_to_rsi_and_xmm0() {
441 mut mod := new_x64_abi_sret_mixed_aggregate_call_module()
442 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
443 gen.gen()
444 code := gen.elf.text_data
445
446 sret := index_bytes(code, [u8(0x48), 0x89, 0xc7]) // hidden sret pointer -> RDI
447 rsi_load := index_bytes(code, [u8(0x49), 0x8b, 0x32]) // mov rsi, [r10]
448 xmm0_load := index_bytes(code, [u8(0xf2), 0x41, 0x0f, 0x10, 0x42, 0x08]) // movsd xmm0, [r10+8]
449 al_count := index_bytes(code, [u8(0xb8), 0x01, 0, 0, 0]) // mov eax, 1
450 call := last_index_bytes(code, [u8(0xe8)])
451
452 assert sret >= 0
453 assert rsi_load > sret
454 assert xmm0_load > rsi_load
455 assert al_count > xmm0_load
456 assert call > al_count
457}
458
459fn test_x64_sysv_codegen_sse_pair_aggregate_param_stores_xmm0_and_xmm1() {
460 mut mod := new_x64_abi_sse_pair_aggregate_call_module()
461 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
462 gen.gen()
463 code := gen.elf.text_data
464
465 xmm0_store := index_bytes(code, [u8(0xf2), 0x0f, 0x11, 0x45]) // XMM0 -> first f64
466 xmm1_store := index_bytes(code, [u8(0xf2), 0x0f, 0x11, 0x4d]) // XMM1 -> second f64
467
468 assert xmm0_store >= 0
469 assert xmm1_store > xmm0_store
470}
471
472fn test_x64_sysv_codegen_sse_pair_aggregate_call_arg_loads_xmm0_and_xmm1() {
473 mut mod := new_x64_abi_sse_pair_aggregate_call_module()
474 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
475 gen.gen()
476 code := gen.elf.text_data
477
478 xmm0_load := index_bytes(code, [u8(0xf2), 0x41, 0x0f, 0x10, 0x02]) // movsd xmm0, [r10]
479 xmm1_load := index_bytes(code, [u8(0xf2), 0x41, 0x0f, 0x10, 0x4a, 0x08]) // movsd xmm1, [r10+8]
480 al_count := index_bytes(code, [u8(0xb8), 0x02, 0, 0, 0]) // mov eax, 2
481 call := last_index_bytes(code, [u8(0xe8)])
482
483 assert xmm0_load >= 0
484 assert xmm1_load > xmm0_load
485 assert al_count > xmm1_load
486 assert call > al_count
487}
488
489fn test_x64_sysv_codegen_sse_aggregate_call_result_stores_xmms() {
490 mut mod := new_x64_abi_sse_pair_return_call_module()
491 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
492 gen.gen()
493 code := gen.elf.text_data
494
495 xmm0_store := index_bytes(code, [u8(0xf2), 0x0f, 0x11, 0x45]) // movsd [rbp+disp], xmm0
496 xmm1_store := index_bytes(code, [u8(0xf2), 0x0f, 0x11, 0x4d]) // movsd [rbp+disp], xmm1
497
498 assert xmm0_store >= 0
499 assert xmm1_store > xmm0_store
500}
501
502fn test_x64_sysv_codegen_mixed_aggregate_direct_return_loads_gpr_and_xmm() {
503 mut mod := new_x64_abi_mixed_return_callee_module()
504 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
505 gen.gen()
506 code := gen.elf.text_data
507
508 rax_load := index_bytes(code, [u8(0x49), 0x8b, 0x02]) // mov rax, [r10]
509 xmm0_load := index_bytes(code, [u8(0xf2), 0x41, 0x0f, 0x10, 0x42, 0x08]) // movsd xmm0, [r10+8]
510
511 assert rax_load >= 0
512 assert xmm0_load > rax_load
513}
514
515fn test_x64_sysv_codegen_sseup_aggregate_param_and_call_arg_use_one_xmm_register() {
516 mut mod := new_x64_abi_sseup_aggregate_call_module()
517 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
518 gen.gen()
519 code := gen.elf.text_data
520
521 xmm0_store := index_bytes(code, [u8(0xf3), 0x0f, 0x7f, 0x45]) // movdqu [rbp+disp], xmm0
522 xmm0_load := index_bytes(code, [u8(0xf3), 0x41, 0x0f, 0x6f, 0x02]) // movdqu xmm0, [r10]
523
524 assert xmm0_store >= 0
525 assert xmm0_load > xmm0_store
526}
527
528fn test_x64_sysv_codegen_sseup_aggregate_call_result_stores_one_xmm_register() {
529 mut mod := new_x64_abi_sseup_return_call_module()
530 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
531 gen.gen()
532 code := gen.elf.text_data
533
534 assert contains_bytes(code, [u8(0xf3), 0x0f, 0x7f, 0x45]) // movdqu [rbp+disp], xmm0
535}
536
537fn test_x64_sysv_codegen_sseup_aggregate_direct_return_loads_one_xmm_register() {
538 mut mod := new_x64_abi_sseup_return_callee_module()
539 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
540 gen.gen()
541 code := gen.elf.text_data
542
543 assert contains_bytes(code, [u8(0xf3), 0x41, 0x0f, 0x6f, 0x02]) // movdqu xmm0, [r10]
544}
545
546fn test_x64_windows_does_not_take_sysv_integer_pair_return_path() {
547 mut mod := new_x64_abi_pair_return_call_module()
548 gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
549
550 assert !gen.is_sysv_integer_pair_return(x64_abi_integer_pair_return_class())
551}
552
553fn test_x64_windows_codegen_callee_stack_arg_loads_from_rbp_48() {
554 mut mod := new_x64_abi_callee_stack_arg_module()
555 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
556 gen.gen()
557 code := gen.coff.text_data
558
559 assert contains_bytes(code, [u8(0x48), 0x8b, 0x45, 0x30]) // mov rax, [rbp+48]
560}
561
562fn test_x64_windows_codegen_large_aggregate_arg_uses_indirect_pointer() {
563 mut mod := new_x64_abi_indirect_aggregate_call_module(true)
564 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
565 gen.gen()
566 code := gen.coff.text_data
567
568 assert contains_bytes(code, [u8(0x48), 0x83, 0xec, 0x20]) // shadow space
569 assert contains_bytes(code, [u8(0x48), 0x8d, 0x45]) // lea rax, [rbp+disp8]
570 assert contains_bytes(code, [u8(0x48), 0x89, 0xc1]) // indirect arg pointer -> RCX
571}
572
573fn test_x64_windows_codegen_unmarked_large_aggregate_arg_uses_indirect_pointer() {
574 mut mod := new_x64_abi_indirect_aggregate_call_module(false)
575 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
576 gen.gen()
577 code := gen.coff.text_data
578
579 assert contains_bytes(code, [u8(0x48), 0x83, 0xec, 0x20]) // shadow space
580 assert contains_bytes(code, [u8(0x48), 0x8d, 0x45]) // lea rax, [rbp+disp8]
581 assert contains_bytes(code, [u8(0x48), 0x89, 0xc1]) // indirect arg pointer -> RCX
582}
583
584fn test_x64_windows_codegen_struct8_arg_remains_by_value() {
585 mut mod := new_x64_abi_struct8_call_module()
586 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
587 gen.gen()
588 code := gen.coff.text_data
589
590 assert contains_bytes(code, [u8(0x49), 0x8b, 0x0a]) // mov rcx, [r10]
591 assert !contains_bytes(code, [u8(0x48), 0x89, 0xc1]) // no pointer move into RCX
592}
593
594fn test_x64_windows_codegen_stack_large_aggregate_arg_uses_indirect_pointer() {
595 mut mod := new_x64_abi_stack_indirect_aggregate_call_module()
596 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
597 gen.gen()
598 code := gen.coff.text_data
599
600 assert contains_bytes(code, [u8(0x48), 0x83, 0xec, 0x30]) // shadow space + one stack slot
601 assert contains_bytes(code, [u8(0x48), 0x8d, 0x45]) // lea rax, [rbp+disp8]
602 assert contains_bytes(code, [u8(0x48), 0x89, 0x44, 0x24, 0x20]) // pointer -> [rsp+32]
603}
604
605fn test_x64_windows_codegen_fifth_nil_pointer_arg_uses_8_byte_shadow_stack_slot() {
606 mut mod := new_x64_abi_fifth_nil_pointer_call_module()
607 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
608 gen.gen()
609 code := gen.coff.text_data
610
611 assert contains_bytes(code, [u8(0x48), 0x83, 0xec, 0x30]) // 32 shadow bytes + 8 stack arg + align
612 assert contains_bytes(code, [u8(0x31), 0xc0, 0x48, 0x89, 0x44, 0x24, 0x20]) // nil -> [rsp+32]
613}
614
615fn test_x64_windows_codegen_local_u32_pointer_arg_uses_r9_before_fifth_nil() {
616 mut mod := new_x64_abi_writefile_like_pointer_call_module()
617 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
618 gen.gen()
619 code := gen.coff.text_data
620
621 assert contains_bytes(code, [u8(0x48), 0x83, 0xec, 0x30]) // 32 shadow bytes + 8 stack arg + align
622 assert contains_bytes(code, [u8(0x48), 0x8d, 0x45]) // materialize local u32 address
623 assert contains_bytes(code, [u8(0x4c), 0x8d, 0x4d]) // fourth arg pointer -> R9
624 assert contains_bytes(code, [u8(0x31), 0xc0, 0x48, 0x89, 0x44, 0x24, 0x20]) // fifth nil -> [rsp+32]
625}
626
627fn test_x64_windows_codegen_writefile_buffer_pointer_uses_rdx() {
628 mut mod := new_x64_abi_writefile_buffer_pointer_call_module()
629 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
630 gen.gen()
631 code := gen.coff.text_data
632 rdx_buffer := index_bytes(code, [u8(0x48), 0x8d, 0x55])
633 r8_len := index_bytes(code, [u8(0x41), 0xb8, 0x01, 0, 0, 0])
634 r9_written := index_bytes(code, [u8(0x4c), 0x8d, 0x4d])
635 rcx_handle := last_index_bytes(code, [u8(0xb9), 0x01, 0, 0, 0])
636
637 assert contains_bytes(code, [u8(0x48), 0x83, 0xec, 0x30]) // 32 shadow bytes + 8 stack arg + align
638 assert rdx_buffer >= 0 // second arg &u8 buffer pointer -> RDX
639 assert r8_len >= 0 // third arg length -> R8
640 assert r9_written >= 0 // fourth arg &u32 written pointer -> R9
641 assert rcx_handle > rdx_buffer // first arg is loaded last to protect RCX from scratch use
642 assert rcx_handle > r8_len
643 assert rcx_handle > r9_written
644 assert contains_bytes(code, [u8(0x31), 0xc0, 0x48, 0x89, 0x44, 0x24, 0x20]) // fifth nil -> [rsp+32]
645}
646
647fn test_x64_windows_codegen_indirect_string_literal_materializes_before_call() {
648 mut mod := new_x64_abi_indirect_string_literal_call_module()
649 mut gen := Gen.new_with_format_and_abi(&mod, .coff, .windows)
650 gen.gen()
651 code := gen.coff.text_data
652
653 assert gen.coff.rodata == [u8(`h`), `e`, `l`, `l`, `o`, 0]
654 assert contains_bytes(code, [u8(0x48), 0x89, 0x45]) // store string.str in the stack slot
655 assert contains_bytes(code, [u8(0xb8), 0x05, 0, 0, 0]) // materialized len = 5
656 assert contains_bytes(code, [u8(0xb8), 0x01, 0, 0, 0]) // materialized is_lit = 1
657 assert contains_bytes(code, [u8(0x48), 0x89, 0xc1]) // string slot pointer -> RCX
658}
659
660fn new_x64_abi_test_module() mir.Module {
661 mut ts := ssa.TypeStore.new()
662 int_t := ts.get_int(64)
663 float_t := ts.get_float(64)
664 return mir.Module{
665 type_store: unsafe { *ts }
666 values: [
667 mir.Value{
668 id: 0
669 typ: int_t
670 kind: .func_ref
671 name: 'callee'
672 index: 0
673 },
674 mir.Value{
675 id: 1
676 typ: int_t
677 kind: .constant
678 name: '1'
679 index: 1
680 },
681 mir.Value{
682 id: 2
683 typ: float_t
684 kind: .constant
685 name: '2'
686 index: 2
687 },
688 mir.Value{
689 id: 3
690 typ: int_t
691 kind: .constant
692 name: '3'
693 index: 3
694 },
695 mir.Value{
696 id: 4
697 typ: float_t
698 kind: .constant
699 name: '4'
700 index: 4
701 },
702 mir.Value{
703 id: 5
704 typ: int_t
705 kind: .constant
706 name: '5'
707 index: 5
708 },
709 ]
710 }
711}
712
713fn new_x64_abi_u32_stack_arg_test_module() mir.Module {
714 mut ts := ssa.TypeStore.new()
715 ptr_t := ts.get_ptr(ts.get_int(8))
716 u32_t := ts.get_uint(32)
717 return mir.Module{
718 type_store: unsafe { *ts }
719 values: [
720 mir.Value{
721 id: 0
722 typ: ptr_t
723 kind: .func_ref
724 name: 'callee'
725 index: 0
726 },
727 mir.Value{
728 id: 1
729 typ: ptr_t
730 kind: .constant
731 name: '0'
732 index: 1
733 },
734 mir.Value{
735 id: 2
736 typ: ptr_t
737 kind: .constant
738 name: '0'
739 index: 2
740 },
741 mir.Value{
742 id: 3
743 typ: ptr_t
744 kind: .constant
745 name: '0'
746 index: 3
747 },
748 mir.Value{
749 id: 4
750 typ: ptr_t
751 kind: .constant
752 name: '0'
753 index: 4
754 },
755 mir.Value{
756 id: 5
757 typ: u32_t
758 kind: .constant
759 name: '0'
760 index: 5
761 },
762 ]
763 }
764}
765
766fn new_x64_abi_pair_arg_test_module() mir.Module {
767 mut ts := ssa.TypeStore.new()
768 int_t := ts.get_int(64)
769 pair_t := ts.register(ssa.Type{
770 kind: .struct_t
771 fields: [int_t, int_t]
772 })
773 return mir.Module{
774 type_store: unsafe { *ts }
775 values: [
776 mir.Value{
777 id: 0
778 typ: int_t
779 kind: .func_ref
780 name: 'callee'
781 index: 0
782 },
783 mir.Value{
784 id: 1
785 typ: int_t
786 kind: .constant
787 name: '1'
788 index: 1
789 },
790 mir.Value{
791 id: 2
792 typ: int_t
793 kind: .constant
794 name: '2'
795 index: 2
796 },
797 mir.Value{
798 id: 3
799 typ: int_t
800 kind: .constant
801 name: '3'
802 index: 3
803 },
804 mir.Value{
805 id: 4
806 typ: int_t
807 kind: .constant
808 name: '4'
809 index: 4
810 },
811 mir.Value{
812 id: 5
813 typ: pair_t
814 kind: .argument
815 name: 'pair'
816 index: 5
817 },
818 mir.Value{
819 id: 6
820 typ: int_t
821 kind: .constant
822 name: '6'
823 index: 6
824 },
825 mir.Value{
826 id: 7
827 typ: int_t
828 kind: .constant
829 name: '7'
830 index: 7
831 },
832 ]
833 }
834}
835
836fn new_x64_abi_call_module(sret bool) mir.Module {
837 mut ts := ssa.TypeStore.new()
838 int_t := ts.get_int(64)
839 float_t := ts.get_float(64)
840 struct9_t := ts.register(ssa.Type{
841 kind: .struct_t
842 fields: [int_t, ts.get_int(8)]
843 })
844 mut m := mir.Module{
845 type_store: unsafe { *ts }
846 values: []mir.Value{len: 8}
847 instrs: []mir.Instruction{len: 2}
848 blocks: []mir.BasicBlock{len: 1}
849 funcs: []mir.Function{len: 1}
850 }
851 m.values[0] = mir.Value{
852 id: 0
853 typ: int_t
854 kind: .func_ref
855 name: 'callee'
856 index: 0
857 }
858 for i in 1 .. 6 {
859 m.values[i] = mir.Value{
860 id: i
861 typ: if i in [2, 4] { float_t } else { int_t }
862 kind: .constant
863 name: i.str()
864 index: i
865 }
866 }
867 m.values[6] = mir.Value{
868 id: 6
869 typ: struct9_t
870 kind: .instruction
871 name: 'v6'
872 index: 0
873 }
874 m.values[7] = mir.Value{
875 id: 7
876 typ: int_t
877 kind: .instruction
878 name: 'v7'
879 index: 1
880 }
881 m.instrs[0] = mir.Instruction{
882 op: if sret { .call_sret } else { .call }
883 operands: if sret { [ssa.ValueID(0), 1, 2] } else { [ssa.ValueID(0), 1, 2, 3, 4, 5] }
884 typ: if sret { struct9_t } else { int_t }
885 block: 0
886 }
887 m.instrs[1] = mir.Instruction{
888 op: .ret
889 operands: []ssa.ValueID{}
890 typ: 0
891 block: 0
892 }
893 m.blocks[0] = mir.BasicBlock{
894 id: 0
895 val_id: 0
896 name: 'entry'
897 parent: 0
898 instrs: [ssa.ValueID(6), 7]
899 }
900 m.funcs[0] = mir.Function{
901 id: 0
902 name: 'caller'
903 typ: 0
904 blocks: [ssa.BlockID(0)]
905 }
906 return m
907}
908
909fn emit_x64_abi_indirect_f64_call_with_callee_in_rax(sret bool) []u8 {
910 mut mod := new_x64_abi_indirect_f64_call_module(sret)
911 mut gen := Gen.new_with_format_and_abi(&mod, .elf, .sysv)
912 gen.stack_map = {
913 1: -8
914 2: -24
915 }
916 gen.reg_map = {
917 0: int(rax)
918 }
919 gen.gen_instr(2)
920 return gen.elf.text_data
921}
922
923fn new_x64_abi_indirect_f64_call_module(sret bool) mir.Module {
924 mut ts := ssa.TypeStore.new()
925 i64_t := ts.get_int(64)
926 f64_t := ts.get_float(64)
927 ret_t := ts.register(ssa.Type{
928 kind: .struct_t
929 fields: [i64_t, i64_t, i64_t]
930 })
931 fn_t := ts.register(ssa.Type{
932 kind: .func_t
933 params: [f64_t]
934 ret_type: if sret { ret_t } else { i64_t }
935 })
936 mut m := mir.Module{
937 type_store: unsafe { *ts }
938 values: []mir.Value{len: 5}
939 instrs: []mir.Instruction{len: 2}
940 blocks: []mir.BasicBlock{len: 1}
941 funcs: []mir.Function{len: 1}
942 }
943 m.values[0] = mir.Value{
944 id: 0
945 typ: fn_t
946 kind: .argument
947 name: 'callee'
948 index: 0
949 }
950 m.values[1] = mir.Value{
951 id: 1
952 typ: f64_t
953 kind: .argument
954 name: 'arg'
955 index: 1
956 }
957 m.values[2] = mir.Value{
958 id: 2
959 typ: if sret { ret_t } else { i64_t }
960 kind: .instruction
961 name: 'call'
962 index: 0
963 }
964 m.values[3] = mir.Value{
965 id: 3
966 typ: 0
967 kind: .instruction
968 name: 'ret'
969 index: 1
970 }
971 m.values[4] = mir.Value{
972 id: 4
973 typ: 0
974 kind: .basic_block
975 name: 'entry'
976 index: 0
977 }
978 m.instrs[0] = mir.Instruction{
979 op: if sret { .call_sret } else { .call }
980 operands: [ssa.ValueID(0), 1]
981 typ: if sret { ret_t } else { i64_t }
982 block: 0
983 abi_ret_indirect: sret
984 abi_arg_class: [.in_reg]
985 }
986 m.instrs[1] = mir.Instruction{
987 op: .ret
988 operands: []ssa.ValueID{}
989 typ: 0
990 block: 0
991 }
992 m.blocks[0] = mir.BasicBlock{
993 id: 0
994 val_id: 4
995 name: 'entry'
996 parent: 0
997 instrs: [ssa.ValueID(2), 3]
998 }
999 m.funcs[0] = mir.Function{
1000 id: 0
1001 name: 'caller'
1002 typ: 0
1003 blocks: [ssa.BlockID(0)]
1004 params: [ssa.ValueID(0), 1]
1005 }
1006 return m
1007}
1008
1009fn new_x64_abi_sret_indirect_receiver_call_module() mir.Module {
1010 mut ts := ssa.TypeStore.new()
1011 int_t := ts.get_int(64)
1012 array_t := ts.register(ssa.Type{
1013 kind: .struct_t
1014 fields: [int_t, int_t, int_t, int_t]
1015 })
1016 mut m := mir.Module{
1017 type_store: unsafe { *ts }
1018 values: []mir.Value{len: 8}
1019 instrs: []mir.Instruction{len: 2}
1020 blocks: []mir.BasicBlock{len: 1}
1021 funcs: []mir.Function{len: 1}
1022 }
1023 m.values[0] = mir.Value{
1024 id: 0
1025 typ: array_t
1026 kind: .func_ref
1027 name: 'array__slice'
1028 index: 0
1029 }
1030 m.values[1] = mir.Value{
1031 id: 1
1032 typ: array_t
1033 kind: .argument
1034 name: 'receiver'
1035 index: 1
1036 }
1037 m.values[2] = mir.Value{
1038 id: 2
1039 typ: int_t
1040 kind: .constant
1041 name: '1'
1042 index: 2
1043 }
1044 m.values[3] = mir.Value{
1045 id: 3
1046 typ: int_t
1047 kind: .constant
1048 name: '3'
1049 index: 3
1050 }
1051 m.values[4] = mir.Value{
1052 id: 4
1053 typ: array_t
1054 kind: .instruction
1055 name: 'slice'
1056 index: 0
1057 }
1058 m.values[5] = mir.Value{
1059 id: 5
1060 typ: int_t
1061 kind: .instruction
1062 name: 'ret'
1063 index: 1
1064 }
1065 m.values[6] = mir.Value{
1066 id: 6
1067 typ: int_t
1068 kind: .basic_block
1069 name: 'entry'
1070 index: 0
1071 }
1072 m.values[7] = mir.Value{
1073 id: 7
1074 typ: int_t
1075 kind: .constant
1076 name: '0'
1077 index: 7
1078 }
1079 m.instrs[0] = mir.Instruction{
1080 op: .call_sret
1081 operands: [ssa.ValueID(0), 1, 2, 3]
1082 typ: array_t
1083 block: 0
1084 abi_ret_indirect: true
1085 abi_arg_class: [.indirect, .in_reg, .in_reg]
1086 }
1087 m.instrs[1] = mir.Instruction{
1088 op: .ret
1089 operands: [ssa.ValueID(7)]
1090 typ: 0
1091 block: 0
1092 }
1093 m.blocks[0] = mir.BasicBlock{
1094 id: 0
1095 val_id: 6
1096 name: 'entry'
1097 parent: 0
1098 instrs: [ssa.ValueID(4), 5]
1099 }
1100 m.funcs[0] = mir.Function{
1101 id: 0
1102 name: 'caller'
1103 typ: int_t
1104 blocks: [ssa.BlockID(0)]
1105 params: [ssa.ValueID(1)]
1106 abi_param_class: [.indirect]
1107 }
1108 return m
1109}
1110
1111fn new_x64_abi_sret_indirect_receiver_callee_module() mir.Module {
1112 mut ts := ssa.TypeStore.new()
1113 int_t := ts.get_int(64)
1114 array_t := ts.register(ssa.Type{
1115 kind: .struct_t
1116 fields: [int_t, int_t, int_t, int_t]
1117 })
1118 mut m := mir.Module{
1119 type_store: unsafe { *ts }
1120 values: []mir.Value{len: 6}
1121 instrs: []mir.Instruction{len: 1}
1122 blocks: []mir.BasicBlock{len: 1}
1123 funcs: []mir.Function{len: 1}
1124 }
1125 m.values[0] = mir.Value{
1126 id: 0
1127 typ: array_t
1128 kind: .argument
1129 name: 'receiver'
1130 index: 0
1131 }
1132 m.values[1] = mir.Value{
1133 id: 1
1134 typ: int_t
1135 kind: .argument
1136 name: 'start'
1137 index: 1
1138 }
1139 m.values[2] = mir.Value{
1140 id: 2
1141 typ: int_t
1142 kind: .argument
1143 name: 'end'
1144 index: 2
1145 }
1146 m.values[3] = mir.Value{
1147 id: 3
1148 typ: array_t
1149 kind: .instruction
1150 name: 'ret'
1151 index: 0
1152 }
1153 m.values[4] = mir.Value{
1154 id: 4
1155 typ: int_t
1156 kind: .basic_block
1157 name: 'entry'
1158 index: 0
1159 }
1160 m.values[5] = mir.Value{
1161 id: 5
1162 typ: int_t
1163 kind: .constant
1164 name: '0'
1165 index: 5
1166 }
1167 m.instrs[0] = mir.Instruction{
1168 op: .ret
1169 operands: [ssa.ValueID(0)]
1170 typ: 0
1171 block: 0
1172 }
1173 m.blocks[0] = mir.BasicBlock{
1174 id: 0
1175 val_id: 4
1176 name: 'entry'
1177 parent: 0
1178 instrs: [ssa.ValueID(3)]
1179 }
1180 m.funcs[0] = mir.Function{
1181 id: 0
1182 name: 'array__slice'
1183 typ: array_t
1184 blocks: [ssa.BlockID(0)]
1185 params: [ssa.ValueID(0), 1, 2]
1186 abi_ret_indirect: true
1187 abi_param_class: [.indirect, .in_reg, .in_reg]
1188 }
1189 return m
1190}
1191
1192fn new_x64_abi_struct8_call_module() mir.Module {
1193 mut ts := ssa.TypeStore.new()
1194 int_t := ts.get_int(64)
1195 struct8_t := ts.register(ssa.Type{
1196 kind: .struct_t
1197 fields: [int_t]
1198 })
1199 mut m := mir.Module{
1200 type_store: unsafe { *ts }
1201 values: []mir.Value{len: 5}
1202 instrs: []mir.Instruction{len: 2}
1203 blocks: []mir.BasicBlock{len: 1}
1204 funcs: []mir.Function{len: 1}
1205 }
1206 m.values[0] = mir.Value{
1207 id: 0
1208 typ: int_t
1209 kind: .func_ref
1210 name: 'callee'
1211 index: 0
1212 }
1213 m.values[1] = mir.Value{
1214 id: 1
1215 typ: struct8_t
1216 kind: .argument
1217 name: 's'
1218 index: 1
1219 }
1220 m.values[2] = mir.Value{
1221 id: 2
1222 typ: int_t
1223 kind: .instruction
1224 name: 'v2'
1225 index: 0
1226 }
1227 m.values[3] = mir.Value{
1228 id: 3
1229 typ: int_t
1230 kind: .instruction
1231 name: 'v3'
1232 index: 1
1233 }
1234 m.values[4] = mir.Value{
1235 id: 4
1236 typ: int_t
1237 kind: .basic_block
1238 name: 'entry'
1239 index: 0
1240 }
1241 m.instrs[0] = mir.Instruction{
1242 op: .call
1243 operands: [ssa.ValueID(0), 1]
1244 typ: int_t
1245 block: 0
1246 }
1247 m.instrs[1] = mir.Instruction{
1248 op: .ret
1249 operands: []ssa.ValueID{}
1250 typ: 0
1251 block: 0
1252 }
1253 m.blocks[0] = mir.BasicBlock{
1254 id: 0
1255 val_id: 4
1256 name: 'entry'
1257 parent: 0
1258 instrs: [ssa.ValueID(2), 3]
1259 }
1260 m.funcs[0] = mir.Function{
1261 id: 0
1262 name: 'caller'
1263 typ: 0
1264 blocks: [ssa.BlockID(0)]
1265 params: [ssa.ValueID(1)]
1266 }
1267 return m
1268}
1269
1270fn new_x64_abi_stack_indirect_aggregate_call_module() mir.Module {
1271 mut ts := ssa.TypeStore.new()
1272 int_t := ts.get_int(64)
1273 struct16_t := ts.register(ssa.Type{
1274 kind: .struct_t
1275 fields: [int_t, int_t]
1276 })
1277 mut m := mir.Module{
1278 type_store: unsafe { *ts }
1279 values: []mir.Value{len: 10}
1280 instrs: []mir.Instruction{len: 2}
1281 blocks: []mir.BasicBlock{len: 1}
1282 funcs: []mir.Function{len: 1}
1283 }
1284 m.values[0] = mir.Value{
1285 id: 0
1286 typ: int_t
1287 kind: .func_ref
1288 name: 'callee'
1289 index: 0
1290 }
1291 for i in 1 .. 5 {
1292 m.values[i] = mir.Value{
1293 id: i
1294 typ: int_t
1295 kind: .constant
1296 name: i.str()
1297 index: i
1298 }
1299 }
1300 m.values[5] = mir.Value{
1301 id: 5
1302 typ: struct16_t
1303 kind: .argument
1304 name: 's'
1305 index: 5
1306 }
1307 m.values[6] = mir.Value{
1308 id: 6
1309 typ: int_t
1310 kind: .instruction
1311 name: 'v6'
1312 index: 0
1313 }
1314 m.values[7] = mir.Value{
1315 id: 7
1316 typ: int_t
1317 kind: .instruction
1318 name: 'v7'
1319 index: 1
1320 }
1321 m.values[8] = mir.Value{
1322 id: 8
1323 typ: int_t
1324 kind: .basic_block
1325 name: 'entry'
1326 index: 0
1327 }
1328 m.values[9] = mir.Value{
1329 id: 9
1330 typ: int_t
1331 kind: .constant
1332 name: '0'
1333 index: 9
1334 }
1335 m.instrs[0] = mir.Instruction{
1336 op: .call
1337 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5]
1338 typ: int_t
1339 block: 0
1340 abi_arg_class: [.in_reg, .in_reg, .in_reg, .in_reg, .indirect]
1341 }
1342 m.instrs[1] = mir.Instruction{
1343 op: .ret
1344 operands: []ssa.ValueID{}
1345 typ: 0
1346 block: 0
1347 }
1348 m.blocks[0] = mir.BasicBlock{
1349 id: 0
1350 val_id: 8
1351 name: 'entry'
1352 parent: 0
1353 instrs: [ssa.ValueID(6), 7]
1354 }
1355 m.funcs[0] = mir.Function{
1356 id: 0
1357 name: 'caller'
1358 typ: 0
1359 blocks: [ssa.BlockID(0)]
1360 params: [ssa.ValueID(5)]
1361 abi_param_class: [.indirect]
1362 }
1363 return m
1364}
1365
1366fn new_x64_abi_fifth_nil_pointer_call_module() mir.Module {
1367 mut ts := ssa.TypeStore.new()
1368 int_t := ts.get_int(64)
1369 u8_t := ts.get_int(8)
1370 ptr_u8_t := ts.get_ptr(u8_t)
1371 mut m := mir.Module{
1372 type_store: unsafe { *ts }
1373 values: []mir.Value{len: 9}
1374 instrs: []mir.Instruction{len: 2}
1375 blocks: []mir.BasicBlock{len: 1}
1376 funcs: []mir.Function{len: 1}
1377 }
1378 m.values[0] = mir.Value{
1379 id: 0
1380 typ: int_t
1381 kind: .func_ref
1382 name: 'callee'
1383 index: 0
1384 }
1385 for i in 1 .. 5 {
1386 m.values[i] = mir.Value{
1387 id: i
1388 typ: int_t
1389 kind: .constant
1390 name: i.str()
1391 index: i
1392 }
1393 }
1394 m.values[5] = mir.Value{
1395 id: 5
1396 typ: ptr_u8_t
1397 kind: .constant
1398 name: '0'
1399 index: 5
1400 }
1401 m.values[6] = mir.Value{
1402 id: 6
1403 typ: int_t
1404 kind: .instruction
1405 name: 'call'
1406 index: 0
1407 }
1408 m.values[7] = mir.Value{
1409 id: 7
1410 typ: 0
1411 kind: .instruction
1412 name: 'ret'
1413 index: 1
1414 }
1415 m.values[8] = mir.Value{
1416 id: 8
1417 typ: 0
1418 kind: .basic_block
1419 name: 'entry'
1420 index: 0
1421 }
1422 m.instrs[0] = mir.Instruction{
1423 op: .call
1424 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5]
1425 typ: int_t
1426 block: 0
1427 }
1428 m.instrs[1] = mir.Instruction{
1429 op: .ret
1430 operands: []ssa.ValueID{}
1431 typ: 0
1432 block: 0
1433 }
1434 m.blocks[0] = mir.BasicBlock{
1435 id: 0
1436 val_id: 8
1437 name: 'entry'
1438 parent: 0
1439 instrs: [ssa.ValueID(6), 7]
1440 }
1441 m.funcs[0] = mir.Function{
1442 id: 0
1443 name: 'caller'
1444 typ: 0
1445 blocks: [ssa.BlockID(0)]
1446 }
1447 return m
1448}
1449
1450fn new_x64_abi_writefile_like_pointer_call_module() mir.Module {
1451 mut ts := ssa.TypeStore.new()
1452 int_t := ts.get_int(64)
1453 u32_t := ts.get_int(32)
1454 u8_t := ts.get_int(8)
1455 ptr_u32_t := ts.get_ptr(u32_t)
1456 ptr_u8_t := ts.get_ptr(u8_t)
1457 mut m := mir.Module{
1458 type_store: unsafe { *ts }
1459 values: []mir.Value{len: 9}
1460 instrs: []mir.Instruction{len: 3}
1461 blocks: []mir.BasicBlock{len: 1}
1462 funcs: []mir.Function{len: 1}
1463 }
1464 m.values[0] = mir.Value{
1465 id: 0
1466 typ: int_t
1467 kind: .func_ref
1468 name: 'callee'
1469 index: 0
1470 }
1471 for i in 1 .. 4 {
1472 m.values[i] = mir.Value{
1473 id: i
1474 typ: int_t
1475 kind: .constant
1476 name: i.str()
1477 index: i
1478 }
1479 }
1480 m.values[4] = mir.Value{
1481 id: 4
1482 typ: ptr_u32_t
1483 kind: .instruction
1484 name: 'written'
1485 index: 0
1486 }
1487 m.values[5] = mir.Value{
1488 id: 5
1489 typ: ptr_u8_t
1490 kind: .constant
1491 name: '0'
1492 index: 5
1493 }
1494 m.values[6] = mir.Value{
1495 id: 6
1496 typ: int_t
1497 kind: .instruction
1498 name: 'call'
1499 index: 1
1500 }
1501 m.values[7] = mir.Value{
1502 id: 7
1503 typ: 0
1504 kind: .instruction
1505 name: 'ret'
1506 index: 2
1507 }
1508 m.values[8] = mir.Value{
1509 id: 8
1510 typ: 0
1511 kind: .basic_block
1512 name: 'entry'
1513 index: 0
1514 }
1515 m.instrs[0] = mir.Instruction{
1516 op: .alloca
1517 operands: []ssa.ValueID{}
1518 typ: ptr_u32_t
1519 block: 0
1520 }
1521 m.instrs[1] = mir.Instruction{
1522 op: .call
1523 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5]
1524 typ: int_t
1525 block: 0
1526 }
1527 m.instrs[2] = mir.Instruction{
1528 op: .ret
1529 operands: []ssa.ValueID{}
1530 typ: 0
1531 block: 0
1532 }
1533 m.blocks[0] = mir.BasicBlock{
1534 id: 0
1535 val_id: 8
1536 name: 'entry'
1537 parent: 0
1538 instrs: [ssa.ValueID(4), 6, 7]
1539 }
1540 m.funcs[0] = mir.Function{
1541 id: 0
1542 name: 'caller'
1543 typ: 0
1544 blocks: [ssa.BlockID(0)]
1545 }
1546 return m
1547}
1548
1549fn new_x64_abi_writefile_buffer_pointer_call_module() mir.Module {
1550 mut ts := ssa.TypeStore.new()
1551 int_t := ts.get_int(64)
1552 u32_t := ts.get_int(32)
1553 u8_t := ts.get_int(8)
1554 ptr_u32_t := ts.get_ptr(u32_t)
1555 ptr_u8_t := ts.get_ptr(u8_t)
1556 mut m := mir.Module{
1557 type_store: unsafe { *ts }
1558 values: []mir.Value{len: 9}
1559 instrs: []mir.Instruction{len: 4}
1560 blocks: []mir.BasicBlock{len: 1}
1561 funcs: []mir.Function{len: 1}
1562 }
1563 m.values[0] = mir.Value{
1564 id: 0
1565 typ: int_t
1566 kind: .func_ref
1567 name: 'callee'
1568 index: 0
1569 }
1570 m.values[1] = mir.Value{
1571 id: 1
1572 typ: int_t
1573 kind: .constant
1574 name: '1'
1575 index: 1
1576 }
1577 m.values[2] = mir.Value{
1578 id: 2
1579 typ: ptr_u8_t
1580 kind: .instruction
1581 name: 'buf'
1582 index: 0
1583 }
1584 m.values[3] = mir.Value{
1585 id: 3
1586 typ: int_t
1587 kind: .constant
1588 name: '1'
1589 index: 3
1590 }
1591 m.values[4] = mir.Value{
1592 id: 4
1593 typ: ptr_u32_t
1594 kind: .instruction
1595 name: 'written'
1596 index: 1
1597 }
1598 m.values[5] = mir.Value{
1599 id: 5
1600 typ: ptr_u8_t
1601 kind: .constant
1602 name: '0'
1603 index: 5
1604 }
1605 m.values[6] = mir.Value{
1606 id: 6
1607 typ: int_t
1608 kind: .instruction
1609 name: 'call'
1610 index: 2
1611 }
1612 m.values[7] = mir.Value{
1613 id: 7
1614 typ: 0
1615 kind: .instruction
1616 name: 'ret'
1617 index: 3
1618 }
1619 m.values[8] = mir.Value{
1620 id: 8
1621 typ: 0
1622 kind: .basic_block
1623 name: 'entry'
1624 index: 0
1625 }
1626 m.instrs[0] = mir.Instruction{
1627 op: .alloca
1628 operands: []ssa.ValueID{}
1629 typ: ptr_u8_t
1630 block: 0
1631 }
1632 m.instrs[1] = mir.Instruction{
1633 op: .alloca
1634 operands: []ssa.ValueID{}
1635 typ: ptr_u32_t
1636 block: 0
1637 }
1638 m.instrs[2] = mir.Instruction{
1639 op: .call
1640 operands: [ssa.ValueID(0), 1, 2, 3, 4, 5]
1641 typ: int_t
1642 block: 0
1643 }
1644 m.instrs[3] = mir.Instruction{
1645 op: .ret
1646 operands: []ssa.ValueID{}
1647 typ: 0
1648 block: 0
1649 }
1650 m.blocks[0] = mir.BasicBlock{
1651 id: 0
1652 val_id: 8
1653 name: 'entry'
1654 parent: 0
1655 instrs: [ssa.ValueID(2), 4, 6, 7]
1656 }
1657 m.funcs[0] = mir.Function{
1658 id: 0
1659 name: 'caller'
1660 typ: 0
1661 blocks: [ssa.BlockID(0)]
1662 }
1663 return m
1664}
1665
1666fn new_x64_abi_indirect_string_literal_call_module() mir.Module {
1667 mut ts := ssa.TypeStore.new()
1668 i64_t := ts.get_int(64)
1669 i32_t := ts.get_int(32)
1670 i8_t := ts.get_int(8)
1671 ptr_u8_t := ts.get_ptr(i8_t)
1672 string_t := ts.register(ssa.Type{
1673 kind: .struct_t
1674 fields: [ptr_u8_t, i32_t, i32_t]
1675 field_names: ['str', 'len', 'is_lit']
1676 })
1677 mut m := mir.Module{
1678 type_store: unsafe { *ts }
1679 values: []mir.Value{len: 5}
1680 instrs: []mir.Instruction{len: 2}
1681 blocks: []mir.BasicBlock{len: 1}
1682 funcs: []mir.Function{len: 1}
1683 }
1684 m.values[0] = mir.Value{
1685 id: 0
1686 typ: i64_t
1687 kind: .func_ref
1688 name: 'callee'
1689 index: 0
1690 }
1691 m.values[1] = mir.Value{
1692 id: 1
1693 typ: string_t
1694 kind: .string_literal
1695 name: 'hello'
1696 index: 5
1697 }
1698 m.values[2] = mir.Value{
1699 id: 2
1700 typ: i64_t
1701 kind: .instruction
1702 name: 'call'
1703 index: 0
1704 }
1705 m.values[3] = mir.Value{
1706 id: 3
1707 typ: 0
1708 kind: .instruction
1709 name: 'ret'
1710 index: 1
1711 }
1712 m.values[4] = mir.Value{
1713 id: 4
1714 typ: 0
1715 kind: .basic_block
1716 name: 'entry'
1717 index: 0
1718 }
1719 m.instrs[0] = mir.Instruction{
1720 op: .call
1721 operands: [ssa.ValueID(0), 1]
1722 typ: i64_t
1723 block: 0
1724 abi_arg_class: [.indirect]
1725 }
1726 m.instrs[1] = mir.Instruction{
1727 op: .ret
1728 operands: []ssa.ValueID{}
1729 typ: 0
1730 block: 0
1731 }
1732 m.blocks[0] = mir.BasicBlock{
1733 id: 0
1734 val_id: 4
1735 name: 'entry'
1736 parent: 0
1737 instrs: [ssa.ValueID(2), 3]
1738 }
1739 m.funcs[0] = mir.Function{
1740 id: 0
1741 name: 'caller'
1742 typ: 0
1743 blocks: [ssa.BlockID(0)]
1744 }
1745 return m
1746}
1747
1748fn x64_abi_integer_pair_return_class() mir.AbiValueClass {
1749 return mir.AbiValueClass{
1750 mode: .direct
1751 size: 16
1752 classes: [.integer, .integer]
1753 }
1754}
1755
1756fn x64_abi_mixed_aggregate_class() mir.AbiValueClass {
1757 return mir.AbiValueClass{
1758 mode: .direct
1759 size: 16
1760 classes: [.integer, .sse]
1761 }
1762}
1763
1764fn x64_abi_sse_pair_class() mir.AbiValueClass {
1765 return mir.AbiValueClass{
1766 mode: .direct
1767 size: 16
1768 classes: [.sse, .sse]
1769 }
1770}
1771
1772fn x64_abi_sseup_aggregate_class() mir.AbiValueClass {
1773 return mir.AbiValueClass{
1774 mode: .direct
1775 size: 16
1776 classes: [.sse, .sseup]
1777 }
1778}
1779
1780fn x64_abi_mixed_aggregate_layout() mir.AbiValueLayout {
1781 value_class := x64_abi_mixed_aggregate_class()
1782 return mir.AbiValueLayout{
1783 value_class: value_class
1784 locs: [
1785 mir.AbiLocation{
1786 kind: .int_reg
1787 index: 0
1788 offset: 0
1789 class: .integer
1790 },
1791 mir.AbiLocation{
1792 kind: .sse_reg
1793 index: 0
1794 offset: 8
1795 class: .sse
1796 },
1797 ]
1798 }
1799}
1800
1801fn x64_abi_sret_mixed_aggregate_arg_layout() mir.AbiValueLayout {
1802 value_class := x64_abi_mixed_aggregate_class()
1803 return mir.AbiValueLayout{
1804 value_class: value_class
1805 locs: [
1806 mir.AbiLocation{
1807 kind: .int_reg
1808 index: 1
1809 offset: 0
1810 class: .integer
1811 },
1812 mir.AbiLocation{
1813 kind: .sse_reg
1814 index: 0
1815 offset: 8
1816 class: .sse
1817 },
1818 ]
1819 }
1820}
1821
1822fn x64_abi_sse_pair_layout() mir.AbiValueLayout {
1823 value_class := x64_abi_sse_pair_class()
1824 return mir.AbiValueLayout{
1825 value_class: value_class
1826 locs: [
1827 mir.AbiLocation{
1828 kind: .sse_reg
1829 index: 0
1830 offset: 0
1831 class: .sse
1832 },
1833 mir.AbiLocation{
1834 kind: .sse_reg
1835 index: 1
1836 offset: 8
1837 class: .sse
1838 },
1839 ]
1840 }
1841}
1842
1843fn x64_abi_sseup_aggregate_layout() mir.AbiValueLayout {
1844 value_class := x64_abi_sseup_aggregate_class()
1845 return mir.AbiValueLayout{
1846 value_class: value_class
1847 locs: [
1848 mir.AbiLocation{
1849 kind: .sse_reg
1850 index: 0
1851 offset: 0
1852 class: .sse
1853 },
1854 mir.AbiLocation{
1855 kind: .sse_reg
1856 index: 0
1857 offset: 8
1858 class: .sseup
1859 },
1860 ]
1861 }
1862}
1863
1864fn new_x64_abi_pair_return_call_module() mir.Module {
1865 mut ts := ssa.TypeStore.new()
1866 int_t := ts.get_int(64)
1867 pair_t := ts.register(ssa.Type{
1868 kind: .struct_t
1869 fields: [int_t, int_t]
1870 })
1871 mut m := mir.Module{
1872 type_store: unsafe { *ts }
1873 values: []mir.Value{len: 4}
1874 instrs: []mir.Instruction{len: 2}
1875 blocks: []mir.BasicBlock{len: 1}
1876 funcs: []mir.Function{len: 1}
1877 }
1878 m.values[0] = mir.Value{
1879 id: 0
1880 typ: pair_t
1881 kind: .func_ref
1882 name: 'callee'
1883 index: 0
1884 }
1885 m.values[1] = mir.Value{
1886 id: 1
1887 typ: pair_t
1888 kind: .instruction
1889 name: 'pair'
1890 index: 0
1891 }
1892 m.values[2] = mir.Value{
1893 id: 2
1894 typ: 0
1895 kind: .instruction
1896 name: 'ret'
1897 index: 1
1898 }
1899 m.values[3] = mir.Value{
1900 id: 3
1901 typ: 0
1902 kind: .basic_block
1903 name: 'entry'
1904 index: 0
1905 }
1906 m.instrs[0] = mir.Instruction{
1907 op: .call
1908 operands: [ssa.ValueID(0)]
1909 typ: pair_t
1910 block: 0
1911 abi_ret_class: x64_abi_integer_pair_return_class()
1912 }
1913 m.instrs[1] = mir.Instruction{
1914 op: .ret
1915 operands: []ssa.ValueID{}
1916 typ: 0
1917 block: 0
1918 }
1919 m.blocks[0] = mir.BasicBlock{
1920 id: 0
1921 val_id: 3
1922 name: 'entry'
1923 parent: 0
1924 instrs: [ssa.ValueID(1), 2]
1925 }
1926 m.funcs[0] = mir.Function{
1927 id: 0
1928 name: 'caller'
1929 typ: 0
1930 blocks: [ssa.BlockID(0)]
1931 }
1932 return m
1933}
1934
1935fn new_x64_abi_pair_return_callee_module() mir.Module {
1936 mut ts := ssa.TypeStore.new()
1937 int_t := ts.get_int(64)
1938 pair_t := ts.register(ssa.Type{
1939 kind: .struct_t
1940 fields: [int_t, int_t]
1941 })
1942 mut m := mir.Module{
1943 type_store: unsafe { *ts }
1944 values: []mir.Value{len: 3}
1945 instrs: []mir.Instruction{len: 1}
1946 blocks: []mir.BasicBlock{len: 1}
1947 funcs: []mir.Function{len: 1}
1948 }
1949 m.values[0] = mir.Value{
1950 id: 0
1951 typ: pair_t
1952 kind: .argument
1953 name: 'pair'
1954 index: 0
1955 }
1956 m.values[1] = mir.Value{
1957 id: 1
1958 typ: 0
1959 kind: .instruction
1960 name: 'ret'
1961 index: 0
1962 }
1963 m.values[2] = mir.Value{
1964 id: 2
1965 typ: 0
1966 kind: .basic_block
1967 name: 'entry'
1968 index: 0
1969 }
1970 m.instrs[0] = mir.Instruction{
1971 op: .ret
1972 operands: [ssa.ValueID(0)]
1973 typ: 0
1974 block: 0
1975 }
1976 m.blocks[0] = mir.BasicBlock{
1977 id: 0
1978 val_id: 2
1979 name: 'entry'
1980 parent: 0
1981 instrs: [ssa.ValueID(1)]
1982 }
1983 m.funcs[0] = mir.Function{
1984 id: 0
1985 name: 'callee'
1986 typ: pair_t
1987 blocks: [ssa.BlockID(0)]
1988 params: [ssa.ValueID(0)]
1989 abi_ret_class: x64_abi_integer_pair_return_class()
1990 }
1991 return m
1992}
1993
1994fn new_x64_abi_mixed_aggregate_call_module() mir.Module {
1995 mut ts := ssa.TypeStore.new()
1996 i64_t := ts.get_int(64)
1997 f64_t := ts.get_float(64)
1998 mixed_t := ts.register(ssa.Type{
1999 kind: .struct_t
2000 fields: [i64_t, f64_t]
2001 })
2002 layout := x64_abi_mixed_aggregate_layout()
2003 mut m := mir.Module{
2004 type_store: unsafe { *ts }
2005 values: []mir.Value{len: 5}
2006 instrs: []mir.Instruction{len: 2}
2007 blocks: []mir.BasicBlock{len: 1}
2008 funcs: []mir.Function{len: 1}
2009 }
2010 m.values[0] = mir.Value{
2011 id: 0
2012 typ: i64_t
2013 kind: .func_ref
2014 name: 'callee'
2015 index: 0
2016 }
2017 m.values[1] = mir.Value{
2018 id: 1
2019 typ: mixed_t
2020 kind: .argument
2021 name: 'mixed'
2022 index: 0
2023 }
2024 m.values[2] = mir.Value{
2025 id: 2
2026 typ: i64_t
2027 kind: .instruction
2028 name: 'call'
2029 index: 0
2030 }
2031 m.values[3] = mir.Value{
2032 id: 3
2033 typ: 0
2034 kind: .instruction
2035 name: 'ret'
2036 index: 1
2037 }
2038 m.values[4] = mir.Value{
2039 id: 4
2040 typ: 0
2041 kind: .basic_block
2042 name: 'entry'
2043 index: 0
2044 }
2045 m.instrs[0] = mir.Instruction{
2046 op: .call
2047 operands: [ssa.ValueID(0), 1]
2048 typ: i64_t
2049 block: 0
2050 abi_arg_class: [.in_reg]
2051 abi_arg_classes: [layout.value_class]
2052 abi_arg_layouts: [layout]
2053 }
2054 m.instrs[1] = mir.Instruction{
2055 op: .ret
2056 operands: []ssa.ValueID{}
2057 typ: 0
2058 block: 0
2059 }
2060 m.blocks[0] = mir.BasicBlock{
2061 id: 0
2062 val_id: 4
2063 name: 'entry'
2064 parent: 0
2065 instrs: [ssa.ValueID(2), 3]
2066 }
2067 m.funcs[0] = mir.Function{
2068 id: 0
2069 name: 'caller'
2070 typ: 0
2071 blocks: [ssa.BlockID(0)]
2072 params: [ssa.ValueID(1)]
2073 abi_param_classes: [layout.value_class]
2074 abi_param_layouts: [layout]
2075 }
2076 return m
2077}
2078
2079fn new_x64_abi_sret_mixed_aggregate_call_module() mir.Module {
2080 mut ts := ssa.TypeStore.new()
2081 i64_t := ts.get_int(64)
2082 f64_t := ts.get_float(64)
2083 ret_t := ts.register(ssa.Type{
2084 kind: .struct_t
2085 fields: [i64_t, i64_t, i64_t]
2086 })
2087 mixed_t := ts.register(ssa.Type{
2088 kind: .struct_t
2089 fields: [i64_t, f64_t]
2090 })
2091 layout := x64_abi_sret_mixed_aggregate_arg_layout()
2092 mut m := mir.Module{
2093 type_store: unsafe { *ts }
2094 values: []mir.Value{len: 5}
2095 instrs: []mir.Instruction{len: 2}
2096 blocks: []mir.BasicBlock{len: 1}
2097 funcs: []mir.Function{len: 1}
2098 }
2099 m.values[0] = mir.Value{
2100 id: 0
2101 typ: ret_t
2102 kind: .func_ref
2103 name: 'callee'
2104 index: 0
2105 }
2106 m.values[1] = mir.Value{
2107 id: 1
2108 typ: mixed_t
2109 kind: .argument
2110 name: 'mixed'
2111 index: 0
2112 }
2113 m.values[2] = mir.Value{
2114 id: 2
2115 typ: ret_t
2116 kind: .instruction
2117 name: 'call'
2118 index: 0
2119 }
2120 m.values[3] = mir.Value{
2121 id: 3
2122 typ: 0
2123 kind: .instruction
2124 name: 'ret'
2125 index: 1
2126 }
2127 m.values[4] = mir.Value{
2128 id: 4
2129 typ: 0
2130 kind: .basic_block
2131 name: 'entry'
2132 index: 0
2133 }
2134 m.instrs[0] = mir.Instruction{
2135 op: .call_sret
2136 operands: [ssa.ValueID(0), 1]
2137 typ: ret_t
2138 block: 0
2139 abi_ret_indirect: true
2140 abi_arg_class: [.in_reg]
2141 abi_arg_classes: [layout.value_class]
2142 abi_arg_layouts: [layout]
2143 }
2144 m.instrs[1] = mir.Instruction{
2145 op: .ret
2146 operands: []ssa.ValueID{}
2147 typ: 0
2148 block: 0
2149 }
2150 m.blocks[0] = mir.BasicBlock{
2151 id: 0
2152 val_id: 4
2153 name: 'entry'
2154 parent: 0
2155 instrs: [ssa.ValueID(2), 3]
2156 }
2157 m.funcs[0] = mir.Function{
2158 id: 0
2159 name: 'caller'
2160 typ: 0
2161 blocks: [ssa.BlockID(0)]
2162 }
2163 return m
2164}
2165
2166fn new_x64_abi_sse_pair_aggregate_call_module() mir.Module {
2167 mut ts := ssa.TypeStore.new()
2168 i64_t := ts.get_int(64)
2169 f64_t := ts.get_float(64)
2170 pair_t := ts.register(ssa.Type{
2171 kind: .struct_t
2172 fields: [f64_t, f64_t]
2173 })
2174 layout := x64_abi_sse_pair_layout()
2175 mut m := mir.Module{
2176 type_store: unsafe { *ts }
2177 values: []mir.Value{len: 5}
2178 instrs: []mir.Instruction{len: 2}
2179 blocks: []mir.BasicBlock{len: 1}
2180 funcs: []mir.Function{len: 1}
2181 }
2182 m.values[0] = mir.Value{
2183 id: 0
2184 typ: i64_t
2185 kind: .func_ref
2186 name: 'callee'
2187 index: 0
2188 }
2189 m.values[1] = mir.Value{
2190 id: 1
2191 typ: pair_t
2192 kind: .argument
2193 name: 'pair'
2194 index: 0
2195 }
2196 m.values[2] = mir.Value{
2197 id: 2
2198 typ: i64_t
2199 kind: .instruction
2200 name: 'call'
2201 index: 0
2202 }
2203 m.values[3] = mir.Value{
2204 id: 3
2205 typ: 0
2206 kind: .instruction
2207 name: 'ret'
2208 index: 1
2209 }
2210 m.values[4] = mir.Value{
2211 id: 4
2212 typ: 0
2213 kind: .basic_block
2214 name: 'entry'
2215 index: 0
2216 }
2217 m.instrs[0] = mir.Instruction{
2218 op: .call
2219 operands: [ssa.ValueID(0), 1]
2220 typ: i64_t
2221 block: 0
2222 abi_arg_class: [.in_reg]
2223 abi_arg_classes: [layout.value_class]
2224 abi_arg_layouts: [layout]
2225 }
2226 m.instrs[1] = mir.Instruction{
2227 op: .ret
2228 operands: []ssa.ValueID{}
2229 typ: 0
2230 block: 0
2231 }
2232 m.blocks[0] = mir.BasicBlock{
2233 id: 0
2234 val_id: 4
2235 name: 'entry'
2236 parent: 0
2237 instrs: [ssa.ValueID(2), 3]
2238 }
2239 m.funcs[0] = mir.Function{
2240 id: 0
2241 name: 'caller'
2242 typ: 0
2243 blocks: [ssa.BlockID(0)]
2244 params: [ssa.ValueID(1)]
2245 abi_param_classes: [layout.value_class]
2246 abi_param_layouts: [layout]
2247 }
2248 return m
2249}
2250
2251fn new_x64_abi_sseup_aggregate_call_module() mir.Module {
2252 mut ts := ssa.TypeStore.new()
2253 i64_t := ts.get_int(64)
2254 f128_t := ts.get_float(128)
2255 sseup_t := ts.register(ssa.Type{
2256 kind: .struct_t
2257 fields: [f128_t]
2258 })
2259 layout := x64_abi_sseup_aggregate_layout()
2260 mut m := mir.Module{
2261 type_store: unsafe { *ts }
2262 values: []mir.Value{len: 5}
2263 instrs: []mir.Instruction{len: 2}
2264 blocks: []mir.BasicBlock{len: 1}
2265 funcs: []mir.Function{len: 1}
2266 }
2267 m.values[0] = mir.Value{
2268 id: 0
2269 typ: i64_t
2270 kind: .func_ref
2271 name: 'callee'
2272 index: 0
2273 }
2274 m.values[1] = mir.Value{
2275 id: 1
2276 typ: sseup_t
2277 kind: .argument
2278 name: 'vector'
2279 index: 0
2280 }
2281 m.values[2] = mir.Value{
2282 id: 2
2283 typ: i64_t
2284 kind: .instruction
2285 name: 'call'
2286 index: 0
2287 }
2288 m.values[3] = mir.Value{
2289 id: 3
2290 typ: 0
2291 kind: .instruction
2292 name: 'ret'
2293 index: 1
2294 }
2295 m.values[4] = mir.Value{
2296 id: 4
2297 typ: 0
2298 kind: .basic_block
2299 name: 'entry'
2300 index: 0
2301 }
2302 m.instrs[0] = mir.Instruction{
2303 op: .call
2304 operands: [ssa.ValueID(0), 1]
2305 typ: i64_t
2306 block: 0
2307 abi_arg_class: [.in_reg]
2308 abi_arg_classes: [layout.value_class]
2309 abi_arg_layouts: [layout]
2310 }
2311 m.instrs[1] = mir.Instruction{
2312 op: .ret
2313 operands: []ssa.ValueID{}
2314 typ: 0
2315 block: 0
2316 }
2317 m.blocks[0] = mir.BasicBlock{
2318 id: 0
2319 val_id: 4
2320 name: 'entry'
2321 parent: 0
2322 instrs: [ssa.ValueID(2), 3]
2323 }
2324 m.funcs[0] = mir.Function{
2325 id: 0
2326 name: 'caller'
2327 typ: 0
2328 blocks: [ssa.BlockID(0)]
2329 params: [ssa.ValueID(1)]
2330 abi_param_classes: [layout.value_class]
2331 abi_param_layouts: [layout]
2332 }
2333 return m
2334}
2335
2336fn new_x64_abi_sse_pair_return_call_module() mir.Module {
2337 mut ts := ssa.TypeStore.new()
2338 f64_t := ts.get_float(64)
2339 pair_t := ts.register(ssa.Type{
2340 kind: .struct_t
2341 fields: [f64_t, f64_t]
2342 })
2343 mut m := mir.Module{
2344 type_store: unsafe { *ts }
2345 values: []mir.Value{len: 4}
2346 instrs: []mir.Instruction{len: 2}
2347 blocks: []mir.BasicBlock{len: 1}
2348 funcs: []mir.Function{len: 1}
2349 }
2350 m.values[0] = mir.Value{
2351 id: 0
2352 typ: pair_t
2353 kind: .func_ref
2354 name: 'callee'
2355 index: 0
2356 }
2357 m.values[1] = mir.Value{
2358 id: 1
2359 typ: pair_t
2360 kind: .instruction
2361 name: 'pair'
2362 index: 0
2363 }
2364 m.values[2] = mir.Value{
2365 id: 2
2366 typ: 0
2367 kind: .instruction
2368 name: 'ret'
2369 index: 1
2370 }
2371 m.values[3] = mir.Value{
2372 id: 3
2373 typ: 0
2374 kind: .basic_block
2375 name: 'entry'
2376 index: 0
2377 }
2378 m.instrs[0] = mir.Instruction{
2379 op: .call
2380 operands: [ssa.ValueID(0)]
2381 typ: pair_t
2382 block: 0
2383 abi_ret_class: x64_abi_sse_pair_class()
2384 }
2385 m.instrs[1] = mir.Instruction{
2386 op: .ret
2387 operands: []ssa.ValueID{}
2388 typ: 0
2389 block: 0
2390 }
2391 m.blocks[0] = mir.BasicBlock{
2392 id: 0
2393 val_id: 3
2394 name: 'entry'
2395 parent: 0
2396 instrs: [ssa.ValueID(1), 2]
2397 }
2398 m.funcs[0] = mir.Function{
2399 id: 0
2400 name: 'caller'
2401 typ: 0
2402 blocks: [ssa.BlockID(0)]
2403 }
2404 return m
2405}
2406
2407fn new_x64_abi_sseup_return_call_module() mir.Module {
2408 mut ts := ssa.TypeStore.new()
2409 f128_t := ts.get_float(128)
2410 sseup_t := ts.register(ssa.Type{
2411 kind: .struct_t
2412 fields: [f128_t]
2413 })
2414 mut m := mir.Module{
2415 type_store: unsafe { *ts }
2416 values: []mir.Value{len: 4}
2417 instrs: []mir.Instruction{len: 2}
2418 blocks: []mir.BasicBlock{len: 1}
2419 funcs: []mir.Function{len: 1}
2420 }
2421 m.values[0] = mir.Value{
2422 id: 0
2423 typ: sseup_t
2424 kind: .func_ref
2425 name: 'callee'
2426 index: 0
2427 }
2428 m.values[1] = mir.Value{
2429 id: 1
2430 typ: sseup_t
2431 kind: .instruction
2432 name: 'vector'
2433 index: 0
2434 }
2435 m.values[2] = mir.Value{
2436 id: 2
2437 typ: 0
2438 kind: .instruction
2439 name: 'ret'
2440 index: 1
2441 }
2442 m.values[3] = mir.Value{
2443 id: 3
2444 typ: 0
2445 kind: .basic_block
2446 name: 'entry'
2447 index: 0
2448 }
2449 m.instrs[0] = mir.Instruction{
2450 op: .call
2451 operands: [ssa.ValueID(0)]
2452 typ: sseup_t
2453 block: 0
2454 abi_ret_class: x64_abi_sseup_aggregate_class()
2455 }
2456 m.instrs[1] = mir.Instruction{
2457 op: .ret
2458 operands: []ssa.ValueID{}
2459 typ: 0
2460 block: 0
2461 }
2462 m.blocks[0] = mir.BasicBlock{
2463 id: 0
2464 val_id: 3
2465 name: 'entry'
2466 parent: 0
2467 instrs: [ssa.ValueID(1), 2]
2468 }
2469 m.funcs[0] = mir.Function{
2470 id: 0
2471 name: 'caller'
2472 typ: 0
2473 blocks: [ssa.BlockID(0)]
2474 }
2475 return m
2476}
2477
2478fn new_x64_abi_mixed_return_callee_module() mir.Module {
2479 mut ts := ssa.TypeStore.new()
2480 i64_t := ts.get_int(64)
2481 f64_t := ts.get_float(64)
2482 mixed_t := ts.register(ssa.Type{
2483 kind: .struct_t
2484 fields: [i64_t, f64_t]
2485 })
2486 layout := x64_abi_mixed_aggregate_layout()
2487 mut m := mir.Module{
2488 type_store: unsafe { *ts }
2489 values: []mir.Value{len: 4}
2490 instrs: []mir.Instruction{len: 1}
2491 blocks: []mir.BasicBlock{len: 1}
2492 funcs: []mir.Function{len: 1}
2493 }
2494 m.values[0] = mir.Value{
2495 id: 0
2496 typ: i64_t
2497 kind: .constant
2498 name: '0'
2499 index: 0
2500 }
2501 m.values[1] = mir.Value{
2502 id: 1
2503 typ: mixed_t
2504 kind: .argument
2505 name: 'mixed'
2506 index: 0
2507 }
2508 m.values[2] = mir.Value{
2509 id: 2
2510 typ: 0
2511 kind: .instruction
2512 name: 'ret'
2513 index: 0
2514 }
2515 m.values[3] = mir.Value{
2516 id: 3
2517 typ: 0
2518 kind: .basic_block
2519 name: 'entry'
2520 index: 0
2521 }
2522 m.instrs[0] = mir.Instruction{
2523 op: .ret
2524 operands: [ssa.ValueID(1)]
2525 typ: 0
2526 block: 0
2527 }
2528 m.blocks[0] = mir.BasicBlock{
2529 id: 0
2530 val_id: 3
2531 name: 'entry'
2532 parent: 0
2533 instrs: [ssa.ValueID(2)]
2534 }
2535 m.funcs[0] = mir.Function{
2536 id: 0
2537 name: 'callee'
2538 typ: mixed_t
2539 blocks: [ssa.BlockID(0)]
2540 params: [ssa.ValueID(1)]
2541 abi_ret_class: layout.value_class
2542 abi_param_classes: [layout.value_class]
2543 abi_param_layouts: [layout]
2544 }
2545 return m
2546}
2547
2548fn new_x64_abi_sseup_return_callee_module() mir.Module {
2549 mut ts := ssa.TypeStore.new()
2550 i64_t := ts.get_int(64)
2551 f128_t := ts.get_float(128)
2552 sseup_t := ts.register(ssa.Type{
2553 kind: .struct_t
2554 fields: [f128_t]
2555 })
2556 layout := x64_abi_sseup_aggregate_layout()
2557 mut m := mir.Module{
2558 type_store: unsafe { *ts }
2559 values: []mir.Value{len: 4}
2560 instrs: []mir.Instruction{len: 1}
2561 blocks: []mir.BasicBlock{len: 1}
2562 funcs: []mir.Function{len: 1}
2563 }
2564 m.values[0] = mir.Value{
2565 id: 0
2566 typ: i64_t
2567 kind: .constant
2568 name: '0'
2569 index: 0
2570 }
2571 m.values[1] = mir.Value{
2572 id: 1
2573 typ: sseup_t
2574 kind: .argument
2575 name: 'vector'
2576 index: 0
2577 }
2578 m.values[2] = mir.Value{
2579 id: 2
2580 typ: 0
2581 kind: .instruction
2582 name: 'ret'
2583 index: 0
2584 }
2585 m.values[3] = mir.Value{
2586 id: 3
2587 typ: 0
2588 kind: .basic_block
2589 name: 'entry'
2590 index: 0
2591 }
2592 m.instrs[0] = mir.Instruction{
2593 op: .ret
2594 operands: [ssa.ValueID(1)]
2595 typ: 0
2596 block: 0
2597 }
2598 m.blocks[0] = mir.BasicBlock{
2599 id: 0
2600 val_id: 3
2601 name: 'entry'
2602 parent: 0
2603 instrs: [ssa.ValueID(2)]
2604 }
2605 m.funcs[0] = mir.Function{
2606 id: 0
2607 name: 'callee'
2608 typ: sseup_t
2609 blocks: [ssa.BlockID(0)]
2610 params: [ssa.ValueID(1)]
2611 abi_ret_class: layout.value_class
2612 abi_param_classes: [layout.value_class]
2613 abi_param_layouts: [layout]
2614 }
2615 return m
2616}
2617
2618fn new_x64_abi_callee_stack_arg_module() mir.Module {
2619 mut ts := ssa.TypeStore.new()
2620 int_t := ts.get_int(64)
2621 mut m := mir.Module{
2622 type_store: unsafe { *ts }
2623 values: []mir.Value{len: 7}
2624 instrs: []mir.Instruction{len: 1}
2625 blocks: []mir.BasicBlock{len: 1}
2626 funcs: []mir.Function{len: 1}
2627 }
2628 for i in 0 .. 5 {
2629 m.values[i] = mir.Value{
2630 id: i
2631 typ: int_t
2632 kind: .argument
2633 name: 'a${i}'
2634 index: i
2635 }
2636 }
2637 m.values[5] = mir.Value{
2638 id: 5
2639 typ: int_t
2640 kind: .instruction
2641 name: 'v5'
2642 index: 0
2643 }
2644 m.values[6] = mir.Value{
2645 id: 6
2646 typ: int_t
2647 kind: .basic_block
2648 name: 'entry'
2649 index: 0
2650 }
2651 m.instrs[0] = mir.Instruction{
2652 op: .ret
2653 operands: []ssa.ValueID{}
2654 typ: 0
2655 block: 0
2656 }
2657 m.blocks[0] = mir.BasicBlock{
2658 id: 0
2659 val_id: 6
2660 name: 'entry'
2661 parent: 0
2662 instrs: [ssa.ValueID(5)]
2663 }
2664 m.funcs[0] = mir.Function{
2665 id: 0
2666 name: 'callee'
2667 typ: 0
2668 blocks: [ssa.BlockID(0)]
2669 params: [ssa.ValueID(0), 1, 2, 3, 4]
2670 }
2671 return m
2672}
2673
2674fn new_x64_abi_indirect_aggregate_call_module(mark_indirect bool) mir.Module {
2675 mut ts := ssa.TypeStore.new()
2676 int_t := ts.get_int(64)
2677 struct16_t := ts.register(ssa.Type{
2678 kind: .struct_t
2679 fields: [int_t, int_t]
2680 })
2681 mut m := mir.Module{
2682 type_store: unsafe { *ts }
2683 values: []mir.Value{len: 5}
2684 instrs: []mir.Instruction{len: 2}
2685 blocks: []mir.BasicBlock{len: 1}
2686 funcs: []mir.Function{len: 1}
2687 }
2688 m.values[0] = mir.Value{
2689 id: 0
2690 typ: int_t
2691 kind: .func_ref
2692 name: 'callee'
2693 index: 0
2694 }
2695 m.values[1] = mir.Value{
2696 id: 1
2697 typ: struct16_t
2698 kind: .argument
2699 name: 's'
2700 index: 1
2701 }
2702 m.values[2] = mir.Value{
2703 id: 2
2704 typ: int_t
2705 kind: .instruction
2706 name: 'v2'
2707 index: 0
2708 }
2709 m.values[3] = mir.Value{
2710 id: 3
2711 typ: int_t
2712 kind: .instruction
2713 name: 'v3'
2714 index: 1
2715 }
2716 m.values[4] = mir.Value{
2717 id: 4
2718 typ: int_t
2719 kind: .basic_block
2720 name: 'entry'
2721 index: 0
2722 }
2723 m.instrs[0] = mir.Instruction{
2724 op: .call
2725 operands: [ssa.ValueID(0), 1]
2726 typ: int_t
2727 block: 0
2728 abi_arg_class: if mark_indirect { [.indirect] } else { []mir.AbiArgClass{} }
2729 }
2730 m.instrs[1] = mir.Instruction{
2731 op: .ret
2732 operands: []ssa.ValueID{}
2733 typ: 0
2734 block: 0
2735 }
2736 m.blocks[0] = mir.BasicBlock{
2737 id: 0
2738 val_id: 4
2739 name: 'entry'
2740 parent: 0
2741 instrs: [ssa.ValueID(2), 3]
2742 }
2743 m.funcs[0] = mir.Function{
2744 id: 0
2745 name: 'caller'
2746 typ: 0
2747 blocks: [ssa.BlockID(0)]
2748 params: [ssa.ValueID(1)]
2749 abi_param_class: if mark_indirect { [.indirect] } else { []mir.AbiArgClass{} }
2750 }
2751 return m
2752}
2753