v / vlib / v2 / abi / abi_x64_matrix_test.v
999 lines · 888 sloc · 38.77 KB · 81a5657604ec6da99c25e26546870c6888d6fdde
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module abi
6
7import v2.mir
8import v2.ssa
9
10fn register_x64_matrix_struct_bytes(mut ssa_mod ssa.Module, size int) ssa.TypeID {
11 i8_t := ssa_mod.type_store.get_int(8)
12 mut fields := []ssa.TypeID{}
13 for _ in 0 .. size {
14 fields << i8_t
15 }
16 return register_x64_matrix_struct_fields(mut ssa_mod, fields)
17}
18
19fn register_x64_matrix_struct_fields(mut ssa_mod ssa.Module, fields []ssa.TypeID) ssa.TypeID {
20 return ssa_mod.type_store.register(ssa.Type{
21 kind: .struct_t
22 fields: fields
23 })
24}
25
26fn assert_abi_value_class(class mir.AbiValueClass, mode mir.AbiPassMode, classes []mir.AbiEightbyteClass) {
27 assert class.mode == mode
28 assert class.classes == classes
29}
30
31fn assert_abi_layout_locs(layout mir.AbiValueLayout, kinds []mir.AbiLocationKind, indexes []int) {
32 assert layout.locs.len == kinds.len
33 assert indexes.len == kinds.len
34 for i, kind in kinds {
35 assert layout.locs[i].kind == kind
36 assert layout.locs[i].index == indexes[i]
37 }
38}
39
40fn assert_abi_layout(layout mir.AbiValueLayout, kinds []mir.AbiLocationKind, indexes []int, offsets []int, classes []mir.AbiEightbyteClass) {
41 assert layout.locs.len == kinds.len
42 assert indexes.len == kinds.len
43 assert offsets.len == kinds.len
44 assert classes.len == kinds.len
45 for i, kind in kinds {
46 assert layout.locs[i].kind == kind
47 assert layout.locs[i].index == indexes[i]
48 assert layout.locs[i].offset == offsets[i]
49 assert layout.locs[i].class == classes[i]
50 }
51}
52
53fn build_x64_matrix_direct_call_module(x64_abi X64Abi) (mir.Module, ssa.ValueID) {
54 mut ssa_mod := ssa.Module.new('abi_test_x64_direct_call_matrix')
55 i64_t := ssa_mod.type_store.get_int(64)
56 sizes := [1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 32]
57 mut struct_types := []ssa.TypeID{}
58 for size in sizes {
59 struct_types << register_x64_matrix_struct_bytes(mut ssa_mod, size)
60 }
61
62 callee_id := ssa_mod.new_function('callee', i64_t, [])
63 for i, struct_t in struct_types {
64 param := ssa_mod.add_value_node(.argument, struct_t, 'p${sizes[i]}', 0)
65 ssa_mod.funcs[callee_id].params << param
66 }
67 caller_id := ssa_mod.new_function('caller', i64_t, [])
68 mut args := []ssa.ValueID{}
69 for i, struct_t in struct_types {
70 arg := ssa_mod.add_value_node(.argument, struct_t, 'a${sizes[i]}', 0)
71 ssa_mod.funcs[caller_id].params << arg
72 args << arg
73 }
74 caller_entry := ssa_mod.add_block(caller_id, 'entry')
75 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
76 mut operands := [fn_val]
77 operands << args
78 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, operands)
79 zero := ssa_mod.get_or_add_const(i64_t, '0')
80 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
81
82 mut mir_mod := mir.lower_from_ssa(ssa_mod)
83 lower_with_x64_abi(mut mir_mod, .x64, x64_abi)
84 return mir_mod, call_val
85}
86
87fn build_x64_matrix_return_call_module(x64_abi X64Abi, size int) (mir.Module, ssa.ValueID) {
88 mut ssa_mod := ssa.Module.new('abi_test_x64_return_call_matrix')
89 i64_t := ssa_mod.type_store.get_int(64)
90 struct_t := register_x64_matrix_struct_bytes(mut ssa_mod, size)
91
92 callee_id := ssa_mod.new_function('callee', struct_t, [])
93 callee_entry := ssa_mod.add_block(callee_id, 'entry')
94 zero := ssa_mod.get_or_add_const(i64_t, '0')
95 ssa_mod.add_instr(.ret, callee_entry, 0, [zero])
96
97 caller_id := ssa_mod.new_function('caller', i64_t, [])
98 caller_entry := ssa_mod.add_block(caller_id, 'entry')
99 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
100 call_val := ssa_mod.add_instr(.call, caller_entry, struct_t, [fn_val])
101 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
102
103 mut mir_mod := mir.lower_from_ssa(ssa_mod)
104 lower_with_x64_abi(mut mir_mod, .x64, x64_abi)
105 return mir_mod, call_val
106}
107
108fn build_sysv_layout_call_module(prefix_count int, prefix_float bool, aggregate_kind string) (mir.Module, int, ssa.ValueID, int) {
109 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_location_metadata')
110 i64_t := ssa_mod.type_store.get_int(64)
111 f64_t := ssa_mod.type_store.get_float(64)
112 prefix_t := if prefix_float { f64_t } else { i64_t }
113 aggregate_t := match aggregate_kind {
114 'i64_i64' { register_x64_matrix_struct_fields(mut ssa_mod, [i64_t, i64_t]) }
115 'f64_f64' { register_x64_matrix_struct_fields(mut ssa_mod, [f64_t, f64_t]) }
116 'i64_f64' { register_x64_matrix_struct_fields(mut ssa_mod, [i64_t, f64_t]) }
117 else { register_x64_matrix_struct_fields(mut ssa_mod, [i64_t]) }
118 }
119
120 callee_id := ssa_mod.new_function('callee', i64_t, [])
121 mut param_types := []ssa.TypeID{len: prefix_count, init: prefix_t}
122 param_types << aggregate_t
123 for i, typ in param_types {
124 param := ssa_mod.add_value_node(.argument, typ, 'p${i}', 0)
125 ssa_mod.funcs[callee_id].params << param
126 }
127 caller_id := ssa_mod.new_function('caller', i64_t, [])
128 mut args := []ssa.ValueID{}
129 for i, typ in param_types {
130 arg := ssa_mod.add_value_node(.argument, typ, 'a${i}', 0)
131 ssa_mod.funcs[caller_id].params << arg
132 args << arg
133 }
134 caller_entry := ssa_mod.add_block(caller_id, 'entry')
135 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
136 mut operands := [fn_val]
137 operands << args
138 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, operands)
139 zero := ssa_mod.get_or_add_const(i64_t, '0')
140 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
141
142 mut mir_mod := mir.lower_from_ssa(ssa_mod)
143 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
144 return mir_mod, callee_id, call_val, prefix_count
145}
146
147fn build_sysv_scalar_boundary_module(mut ssa_mod ssa.Module, typ ssa.TypeID, count int) (mir.Module, int, ssa.ValueID) {
148 i64_t := ssa_mod.type_store.get_int(64)
149 callee_id := ssa_mod.new_function('callee', i64_t, [])
150 for i := 0; i < count; i++ {
151 param := ssa_mod.add_value_node(.argument, typ, 'p${i}', 0)
152 ssa_mod.funcs[callee_id].params << param
153 }
154 caller_id := ssa_mod.new_function('caller', i64_t, [])
155 mut args := []ssa.ValueID{}
156 for i := 0; i < count; i++ {
157 arg := ssa_mod.add_value_node(.argument, typ, 'a${i}', 0)
158 ssa_mod.funcs[caller_id].params << arg
159 args << arg
160 }
161 caller_entry := ssa_mod.add_block(caller_id, 'entry')
162 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
163 mut operands := [fn_val]
164 operands << args
165 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, operands)
166 zero := ssa_mod.get_or_add_const(i64_t, '0')
167 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
168
169 mut mir_mod := mir.lower_from_ssa(ssa_mod)
170 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
171 return mir_mod, callee_id, call_val
172}
173
174fn test_x64_windows_struct_size_matrix_classification() {
175 mut ssa_mod := ssa.Module.new('abi_test_x64_windows_size_matrix')
176 i64_t := ssa_mod.type_store.get_int(64)
177 sizes := [1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 32]
178 direct_sizes := [1, 2, 4, 8]
179 mut struct_types := []ssa.TypeID{}
180 for size in sizes {
181 struct_types << register_x64_matrix_struct_bytes(mut ssa_mod, size)
182 }
183
184 param_fn_id := ssa_mod.new_function('param_matrix', i64_t, [])
185 for i, struct_t in struct_types {
186 param := ssa_mod.add_value_node(.argument, struct_t, 's${sizes[i]}', 0)
187 ssa_mod.funcs[param_fn_id].params << param
188 }
189 mut ret_fn_ids := []int{}
190 for i, struct_t in struct_types {
191 ret_fn_ids << ssa_mod.new_function('ret${sizes[i]}', struct_t, [])
192 }
193
194 mut mir_mod := mir.lower_from_ssa(ssa_mod)
195 lower_with_x64_abi(mut mir_mod, .x64, .windows)
196
197 for i, size in sizes {
198 should_be_indirect := size !in direct_sizes
199 assert (mir_mod.funcs[param_fn_id].abi_param_class[i] == .indirect) == should_be_indirect
200 assert mir_mod.funcs[ret_fn_ids[i]].abi_ret_indirect == should_be_indirect
201 }
202}
203
204fn test_x64_windows_callsite_return_size_matrix_classification() {
205 sizes := [1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 32]
206 direct_sizes := [1, 2, 4, 8]
207 for size in sizes {
208 mir_mod, call_val := build_x64_matrix_return_call_module(.windows, size)
209 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
210 should_be_indirect := size !in direct_sizes
211 assert call_instr.abi_ret_indirect == should_be_indirect
212 assert (call_instr.op == .call_sret) == should_be_indirect
213 }
214}
215
216fn test_x64_windows_direct_callsite_struct_size_matrix_classification() {
217 sizes := [1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 32]
218 direct_sizes := [1, 2, 4, 8]
219 mir_mod, call_val := build_x64_matrix_direct_call_module(.windows)
220 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
221
222 assert call_instr.abi_arg_class.len == sizes.len
223 for i, size in sizes {
224 should_be_indirect := size !in direct_sizes
225 assert (call_instr.abi_arg_class[i] == .indirect) == should_be_indirect
226 }
227}
228
229fn test_non_sysv_legacy_thresholds_stay_unchanged_with_metadata_fields() {
230 mut ssa_mod := ssa.Module.new('abi_test_non_sysv_legacy_metadata_guard')
231 i64_t := ssa_mod.type_store.get_int(64)
232 struct9_t := register_x64_matrix_struct_bytes(mut ssa_mod, 9)
233 struct16_t := register_x64_matrix_struct_bytes(mut ssa_mod, 16)
234 struct17_t := register_x64_matrix_struct_bytes(mut ssa_mod, 17)
235
236 param_fn_id := ssa_mod.new_function('param_matrix', i64_t, [])
237 for typ in [struct9_t, struct16_t, struct17_t] {
238 param := ssa_mod.add_value_node(.argument, typ, 'p', 0)
239 ssa_mod.funcs[param_fn_id].params << param
240 }
241 ret16_id := ssa_mod.new_function('ret16', struct16_t, [])
242 ret17_id := ssa_mod.new_function('ret17', struct17_t, [])
243
244 mut win_mod := mir.lower_from_ssa(ssa_mod)
245 lower_with_x64_abi(mut win_mod, .x64, .windows)
246 assert win_mod.funcs[param_fn_id].abi_param_class == [.indirect, .indirect, .indirect]
247 assert win_mod.funcs[ret16_id].abi_ret_indirect == true
248 assert win_mod.funcs[ret17_id].abi_ret_indirect == true
249 for layout in win_mod.funcs[param_fn_id].abi_param_layouts {
250 assert layout.locs.len == 0
251 }
252
253 mut arm_mod := mir.lower_from_ssa(ssa_mod)
254 lower_with_x64_abi(mut arm_mod, .arm64, .sysv)
255 assert arm_mod.funcs[param_fn_id].abi_param_class == [.in_reg, .in_reg, .indirect]
256 assert arm_mod.funcs[ret16_id].abi_ret_indirect == false
257 assert arm_mod.funcs[ret17_id].abi_ret_indirect == true
258 for layout in arm_mod.funcs[param_fn_id].abi_param_layouts {
259 assert layout.locs.len == 0
260 }
261}
262
263fn test_x64_sysv_location_metadata_rolls_back_integer_aggregate_when_gprs_are_insufficient() {
264 mir_mod, callee_id, call_val, aggregate_idx := build_sysv_layout_call_module(6, false,
265 'i64_i64')
266 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
267
268 assert_abi_layout_locs(mir_mod.funcs[callee_id].abi_param_layouts[aggregate_idx], [
269 .stack,
270 .stack,
271 ], [0, 1])
272 assert_abi_layout_locs(call_instr.abi_arg_layouts[aggregate_idx], [.stack, .stack], [
273 0,
274 1,
275 ])
276}
277
278fn test_x64_sysv_location_metadata_uses_two_gprs_when_available_for_integer_aggregate() {
279 mir_mod, callee_id, call_val, aggregate_idx := build_sysv_layout_call_module(4, false,
280 'i64_i64')
281 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
282
283 assert_abi_layout_locs(mir_mod.funcs[callee_id].abi_param_layouts[aggregate_idx], [
284 .int_reg,
285 .int_reg,
286 ], [4, 5])
287 assert_abi_layout_locs(call_instr.abi_arg_layouts[aggregate_idx], [.int_reg, .int_reg], [
288 4,
289 5,
290 ])
291}
292
293fn test_x64_sysv_location_metadata_rolls_back_integer_aggregate_after_five_gprs() {
294 mir_mod, callee_id, call_val, aggregate_idx := build_sysv_layout_call_module(5, false,
295 'i64_i64')
296 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
297
298 assert_abi_layout_locs(mir_mod.funcs[callee_id].abi_param_layouts[aggregate_idx], [
299 .stack,
300 .stack,
301 ], [0, 1])
302 assert_abi_layout_locs(call_instr.abi_arg_layouts[aggregate_idx], [.stack, .stack], [
303 0,
304 1,
305 ])
306}
307
308fn test_x64_sysv_location_metadata_rolls_back_sse_aggregate_when_xmms_are_insufficient() {
309 mir_mod, callee_id, call_val, aggregate_idx := build_sysv_layout_call_module(7, true, 'f64_f64')
310 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
311
312 assert_abi_layout_locs(mir_mod.funcs[callee_id].abi_param_layouts[aggregate_idx], [
313 .stack,
314 .stack,
315 ], [0, 1])
316 assert_abi_layout_locs(call_instr.abi_arg_layouts[aggregate_idx], [.stack, .stack], [
317 0,
318 1,
319 ])
320}
321
322fn test_x64_sysv_location_metadata_assigns_mixed_integer_sse_aggregate() {
323 mir_mod, callee_id, call_val, aggregate_idx := build_sysv_layout_call_module(0, false,
324 'i64_f64')
325 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
326
327 assert_abi_layout_locs(mir_mod.funcs[callee_id].abi_param_layouts[aggregate_idx], [
328 .int_reg,
329 .sse_reg,
330 ], [0, 0])
331 assert_abi_layout_locs(call_instr.abi_arg_layouts[aggregate_idx], [.int_reg, .sse_reg], [
332 0,
333 0,
334 ])
335}
336
337fn test_x64_sysv_location_metadata_reserves_rdi_for_hidden_sret_pointer() {
338 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_sret_location_metadata')
339 i64_t := ssa_mod.type_store.get_int(64)
340 f64_t := ssa_mod.type_store.get_float(64)
341 ret_t := register_x64_matrix_struct_fields(mut ssa_mod, [i64_t, i64_t, i64_t])
342 aggregate_t := register_x64_matrix_struct_fields(mut ssa_mod, [i64_t, f64_t])
343
344 callee_id := ssa_mod.new_function('callee', ret_t, [])
345 callee_param := ssa_mod.add_value_node(.argument, aggregate_t, 'p', 0)
346 ssa_mod.funcs[callee_id].params << callee_param
347
348 caller_id := ssa_mod.new_function('caller', i64_t, [])
349 caller_arg := ssa_mod.add_value_node(.argument, aggregate_t, 'a', 0)
350 ssa_mod.funcs[caller_id].params << caller_arg
351 caller_entry := ssa_mod.add_block(caller_id, 'entry')
352 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
353 call_val := ssa_mod.add_instr(.call, caller_entry, ret_t, [fn_val, caller_arg])
354 zero := ssa_mod.get_or_add_const(i64_t, '0')
355 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
356
357 mut mir_mod := mir.lower_from_ssa(ssa_mod)
358 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
359 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
360
361 assert mir_mod.funcs[callee_id].abi_ret_indirect
362 assert call_instr.abi_ret_indirect
363 assert call_instr.op == .call_sret
364 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[0], [
365 .int_reg,
366 .sse_reg,
367 ], [1, 0], [0, 8], [.integer, .sse])
368 assert_abi_layout(call_instr.abi_arg_layouts[0], [.int_reg, .sse_reg], [
369 1,
370 0,
371 ], [0, 8], [.integer, .sse])
372}
373
374fn test_x64_sysv_location_metadata_places_seventh_integer_scalar_on_stack() {
375 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_integer_scalar_boundary')
376 i64_t := ssa_mod.type_store.get_int(64)
377 mir_mod, callee_id, call_val := build_sysv_scalar_boundary_module(mut ssa_mod, i64_t, 7)
378 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
379
380 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[6], [.stack], [0], [
381 0,
382 ], [.integer])
383 assert_abi_layout(call_instr.abi_arg_layouts[6], [.stack], [0], [0], [.integer])
384}
385
386fn test_x64_sysv_location_metadata_places_ninth_sse_scalar_on_stack() {
387 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_sse_scalar_boundary')
388 f64_t := ssa_mod.type_store.get_float(64)
389 mir_mod, callee_id, call_val := build_sysv_scalar_boundary_module(mut ssa_mod, f64_t, 9)
390 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
391
392 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[8], [.stack], [0], [
393 0,
394 ], [.sse])
395 assert_abi_layout(call_instr.abi_arg_layouts[8], [.stack], [0], [0], [.sse])
396}
397
398fn test_x64_sysv_location_metadata_float128_uses_one_sse_register() {
399 // x86-64 psABI 3.2.3 classifies __float128 as SSE plus SSEUP; SSEUP is
400 // carried in the upper half of the previous SSE register, not a new XMM.
401 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_float128_one_xmm')
402 f128_t := ssa_mod.type_store.get_float(128)
403 mir_mod, callee_id, call_val := build_sysv_scalar_boundary_module(mut ssa_mod, f128_t, 1)
404 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
405
406 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[0], [.sse_reg, .sse_reg], [
407 0,
408 0,
409 ], [0, 8], [.sse, .sseup])
410 assert_abi_layout(call_instr.abi_arg_layouts[0], [.sse_reg, .sse_reg], [
411 0,
412 0,
413 ], [0, 8], [.sse, .sseup])
414}
415
416fn test_x64_sysv_location_metadata_float128_rolls_back_when_sse_regs_are_exhausted() {
417 // x86-64 psABI 3.2.3 says an argument that cannot fit entirely in the
418 // available registers is passed on the stack; this guards the SSE/SSEUP
419 // rollback path without claiming codegen consumes the metadata yet.
420 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_float128_sse_exhausted')
421 f64_t := ssa_mod.type_store.get_float(64)
422 f128_t := ssa_mod.type_store.get_float(128)
423 i64_t := ssa_mod.type_store.get_int(64)
424 mut param_types := []ssa.TypeID{len: 8, init: f64_t}
425 param_types << f128_t
426
427 callee_id := ssa_mod.new_function('callee', i64_t, [])
428 for i, typ in param_types {
429 param := ssa_mod.add_value_node(.argument, typ, 'p${i}', 0)
430 ssa_mod.funcs[callee_id].params << param
431 }
432 caller_id := ssa_mod.new_function('caller', i64_t, [])
433 mut args := []ssa.ValueID{}
434 for i, typ in param_types {
435 arg := ssa_mod.add_value_node(.argument, typ, 'a${i}', 0)
436 ssa_mod.funcs[caller_id].params << arg
437 args << arg
438 }
439 caller_entry := ssa_mod.add_block(caller_id, 'entry')
440 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
441 mut operands := [fn_val]
442 operands << args
443 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, operands)
444 zero := ssa_mod.get_or_add_const(i64_t, '0')
445 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
446
447 mut mir_mod := mir.lower_from_ssa(ssa_mod)
448 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
449 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
450
451 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[8], [.stack, .stack], [
452 0,
453 1,
454 ], [0, 8], [.sse, .sseup])
455 assert_abi_layout(call_instr.abi_arg_layouts[8], [.stack, .stack], [0, 1], [
456 0,
457 8,
458 ], [.sse, .sseup])
459 assert call_instr.abi_arg_class[8] == .in_reg
460 assert call_instr.op == .call
461}
462
463fn test_x64_sysv_location_metadata_assigns_two_xmms_when_available_for_sse_aggregate() {
464 mir_mod, callee_id, call_val, aggregate_idx := build_sysv_layout_call_module(0, true, 'f64_f64')
465 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
466
467 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[aggregate_idx], [
468 .sse_reg,
469 .sse_reg,
470 ], [0, 1], [0, 8], [.sse, .sse])
471 assert_abi_layout(call_instr.abi_arg_layouts[aggregate_idx], [.sse_reg, .sse_reg], [
472 0,
473 1,
474 ], [0, 8], [.sse, .sse])
475}
476
477fn test_x64_sysv_location_metadata_passes_memory_class_aggregate_indirectly() {
478 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_memory_class_layout')
479 i64_t := ssa_mod.type_store.get_int(64)
480 f64_t := ssa_mod.type_store.get_float(64)
481 memory_t := register_x64_matrix_struct_fields(mut ssa_mod, [f64_t, f64_t, f64_t])
482 callee_id := ssa_mod.new_function('callee', i64_t, [])
483 param := ssa_mod.add_value_node(.argument, memory_t, 'p', 0)
484 ssa_mod.funcs[callee_id].params << param
485 caller_id := ssa_mod.new_function('caller', i64_t, [])
486 arg := ssa_mod.add_value_node(.argument, memory_t, 'a', 0)
487 ssa_mod.funcs[caller_id].params << arg
488 caller_entry := ssa_mod.add_block(caller_id, 'entry')
489 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
490 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, [fn_val, arg])
491 zero := ssa_mod.get_or_add_const(i64_t, '0')
492 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
493
494 mut mir_mod := mir.lower_from_ssa(ssa_mod)
495 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
496 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
497
498 assert mir_mod.funcs[callee_id].abi_param_class == [.indirect]
499 assert call_instr.abi_arg_class == [.indirect]
500 assert_abi_value_class(mir_mod.funcs[callee_id].abi_param_classes[0], .indirect, [
501 .memory,
502 ])
503 assert_abi_value_class(call_instr.abi_arg_classes[0], .indirect, [.memory])
504 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[0], [.int_reg], [0], [
505 0,
506 ], [.integer])
507 assert_abi_layout(call_instr.abi_arg_layouts[0], [.int_reg], [0], [0], [.integer])
508}
509
510fn test_x64_sysv_location_metadata_rolls_back_mixed_aggregate_when_gprs_are_exhausted() {
511 mir_mod, callee_id, call_val, aggregate_idx := build_sysv_layout_call_module(6, false,
512 'i64_f64')
513 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
514
515 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[aggregate_idx], [
516 .stack,
517 .stack,
518 ], [0, 1], [0, 8], [.integer, .sse])
519 assert_abi_layout(call_instr.abi_arg_layouts[aggregate_idx], [.stack, .stack], [0, 1], [
520 0,
521 8,
522 ], [.integer, .sse])
523 assert call_instr.abi_arg_class[aggregate_idx] == .in_reg
524 assert call_instr.op == .call
525}
526
527fn test_x64_sysv_location_metadata_rolls_back_mixed_aggregate_when_xmms_are_exhausted() {
528 mir_mod, callee_id, call_val, aggregate_idx := build_sysv_layout_call_module(8, true, 'i64_f64')
529 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
530
531 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[aggregate_idx], [
532 .stack,
533 .stack,
534 ], [0, 1], [0, 8], [.integer, .sse])
535 assert_abi_layout(call_instr.abi_arg_layouts[aggregate_idx], [.stack, .stack], [0, 1], [
536 0,
537 8,
538 ], [.integer, .sse])
539 assert call_instr.abi_arg_class[aggregate_idx] == .in_reg
540 assert call_instr.op == .call
541}
542
543fn test_x64_sysv_location_metadata_stack_indexes_continue_after_scalar_spill() {
544 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_stack_continuation_types')
545 i64_t := ssa_mod.type_store.get_int(64)
546 struct_i64_i64_t := register_x64_matrix_struct_fields(mut ssa_mod, [i64_t, i64_t])
547 mut param_types := []ssa.TypeID{len: 6, init: i64_t}
548 param_types << i64_t
549 param_types << struct_i64_i64_t
550
551 callee_id := ssa_mod.new_function('callee', i64_t, [])
552 for i, typ in param_types {
553 param := ssa_mod.add_value_node(.argument, typ, 'p${i}', 0)
554 ssa_mod.funcs[callee_id].params << param
555 }
556 caller_id := ssa_mod.new_function('caller', i64_t, [])
557 mut args := []ssa.ValueID{}
558 for i, typ in param_types {
559 arg := ssa_mod.add_value_node(.argument, typ, 'a${i}', 0)
560 ssa_mod.funcs[caller_id].params << arg
561 args << arg
562 }
563 caller_entry := ssa_mod.add_block(caller_id, 'entry')
564 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
565 mut operands := [fn_val]
566 operands << args
567 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, operands)
568 zero := ssa_mod.get_or_add_const(i64_t, '0')
569 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
570
571 mut mir_mod := mir.lower_from_ssa(ssa_mod)
572 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
573 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
574
575 assert_abi_layout_locs(mir_mod.funcs[callee_id].abi_param_layouts[6], [.stack], [
576 0,
577 ])
578 assert_abi_layout_locs(mir_mod.funcs[callee_id].abi_param_layouts[7], [.stack, .stack], [
579 1,
580 2,
581 ])
582 assert_abi_layout_locs(call_instr.abi_arg_layouts[6], [.stack], [0])
583 assert_abi_layout_locs(call_instr.abi_arg_layouts[7], [.stack, .stack], [1, 2])
584}
585
586fn test_x64_sysv_direct_callsite_struct_size_matrix_current_threshold() {
587 sizes := [1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 32]
588 mir_mod, call_val := build_x64_matrix_direct_call_module(.sysv)
589 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
590
591 assert call_instr.abi_arg_class.len == sizes.len
592 for i, size in sizes {
593 should_be_indirect := size > 16
594 assert (call_instr.abi_arg_class[i] == .indirect) == should_be_indirect
595 }
596}
597
598fn test_x64_sysv_callsite_return_size_matrix_current_threshold() {
599 sizes := [1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 32]
600 for size in sizes {
601 mir_mod, call_val := build_x64_matrix_return_call_module(.sysv, size)
602 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
603 should_be_indirect := size > 16
604 assert call_instr.abi_ret_indirect == should_be_indirect
605 assert (call_instr.op == .call_sret) == should_be_indirect
606 }
607}
608
609fn test_x64_sysv_struct_size_matrix_current_lowering_threshold() {
610 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_size_matrix')
611 i64_t := ssa_mod.type_store.get_int(64)
612 sizes := [1, 2, 3, 4, 5, 6, 7, 8, 9, 16, 17, 32]
613 mut struct_types := []ssa.TypeID{}
614 for size in sizes {
615 struct_types << register_x64_matrix_struct_bytes(mut ssa_mod, size)
616 }
617
618 param_fn_id := ssa_mod.new_function('param_matrix', i64_t, [])
619 for i, struct_t in struct_types {
620 param := ssa_mod.add_value_node(.argument, struct_t, 's${sizes[i]}', 0)
621 ssa_mod.funcs[param_fn_id].params << param
622 }
623 mut ret_fn_ids := []int{}
624 for i, struct_t in struct_types {
625 ret_fn_ids << ssa_mod.new_function('ret${sizes[i]}', struct_t, [])
626 }
627
628 mut mir_mod := mir.lower_from_ssa(ssa_mod)
629 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
630
631 for i, size in sizes {
632 should_be_indirect := size > 16
633 assert (mir_mod.funcs[param_fn_id].abi_param_class[i] == .indirect) == should_be_indirect
634 assert mir_mod.funcs[ret_fn_ids[i]].abi_ret_indirect == should_be_indirect
635 }
636}
637
638fn test_x64_sysv_aggregate_memory_class_for_representable_mir_types() {
639 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_memory_class')
640 i64_t := ssa_mod.type_store.get_int(64)
641 f64_t := ssa_mod.type_store.get_float(64)
642 f80_t := ssa_mod.type_store.get_float(80)
643 f128_t := ssa_mod.type_store.get_float(128)
644 two_eightbytes_t := register_x64_matrix_struct_fields(mut ssa_mod, [f64_t, f64_t])
645 three_eightbytes_t := register_x64_matrix_struct_fields(mut ssa_mod, [f64_t, f64_t, f64_t])
646 x87_aggregate_t := register_x64_matrix_struct_fields(mut ssa_mod, [f80_t])
647 float128_aggregate_t := register_x64_matrix_struct_fields(mut ssa_mod, [f128_t])
648
649 param_fn_id := ssa_mod.new_function('sysv_memory_params', i64_t, [])
650 for typ in [two_eightbytes_t, three_eightbytes_t, x87_aggregate_t, float128_aggregate_t] {
651 param := ssa_mod.add_value_node(.argument, typ, 'p', 0)
652 ssa_mod.funcs[param_fn_id].params << param
653 }
654 ret_two_id := ssa_mod.new_function('ret_two_eightbytes', two_eightbytes_t, [])
655 ret_three_id := ssa_mod.new_function('ret_three_eightbytes', three_eightbytes_t, [])
656 ret_x87_id := ssa_mod.new_function('ret_x87_aggregate', x87_aggregate_t, [])
657 ret_float128_id := ssa_mod.new_function('ret_float128_aggregate', float128_aggregate_t, [])
658
659 mut mir_mod := mir.lower_from_ssa(ssa_mod)
660 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
661
662 assert_abi_value_class(mir_mod.funcs[param_fn_id].abi_param_classes[0], .direct, [
663 .sse,
664 .sse,
665 ])
666 assert_abi_value_class(mir_mod.funcs[param_fn_id].abi_param_classes[1], .indirect, [
667 .memory,
668 ])
669 assert_abi_value_class(mir_mod.funcs[param_fn_id].abi_param_classes[2], .indirect, [
670 .memory,
671 ])
672 assert_abi_value_class(mir_mod.funcs[param_fn_id].abi_param_classes[3], .direct, [
673 .sse,
674 .sseup,
675 ])
676 assert_abi_value_class(mir_mod.funcs[ret_two_id].abi_ret_class, .direct, [.sse, .sse])
677 assert_abi_value_class(mir_mod.funcs[ret_three_id].abi_ret_class, .indirect, [
678 .memory,
679 ])
680 assert_abi_value_class(mir_mod.funcs[ret_x87_id].abi_ret_class, .indirect, [
681 .memory,
682 ])
683 assert_abi_value_class(mir_mod.funcs[ret_float128_id].abi_ret_class, .direct, [
684 .sse,
685 .sseup,
686 ])
687}
688
689fn test_x64_sysv_metadata_eightbyte_classification_for_representable_types() {
690 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_eightbyte_metadata')
691 i64_t := ssa_mod.type_store.get_int(64)
692 f64_t := ssa_mod.type_store.get_float(64)
693 f80_t := ssa_mod.type_store.get_float(80)
694 f128_t := ssa_mod.type_store.get_float(128)
695 struct_i64_t := register_x64_matrix_struct_fields(mut ssa_mod, [i64_t])
696 struct_i64_i64_t := register_x64_matrix_struct_fields(mut ssa_mod, [i64_t, i64_t])
697 struct_f64_t := register_x64_matrix_struct_fields(mut ssa_mod, [f64_t])
698 struct_f64_f64_t := register_x64_matrix_struct_fields(mut ssa_mod, [f64_t, f64_t])
699 struct_i64_f64_t := register_x64_matrix_struct_fields(mut ssa_mod, [i64_t, f64_t])
700 struct_f64_i64_t := register_x64_matrix_struct_fields(mut ssa_mod, [f64_t, i64_t])
701 too_large_t := register_x64_matrix_struct_fields(mut ssa_mod, [f64_t, f64_t, f64_t])
702 x87_aggregate_t := register_x64_matrix_struct_fields(mut ssa_mod, [f80_t])
703 float128_aggregate_t := register_x64_matrix_struct_fields(mut ssa_mod, [f128_t])
704 types := [
705 struct_i64_t,
706 struct_i64_i64_t,
707 struct_f64_t,
708 struct_f64_f64_t,
709 struct_i64_f64_t,
710 struct_f64_i64_t,
711 too_large_t,
712 x87_aggregate_t,
713 float128_aggregate_t,
714 ]
715
716 param_fn_id := ssa_mod.new_function('sysv_metadata_params', i64_t, [])
717 for typ in types {
718 param := ssa_mod.add_value_node(.argument, typ, 'p', 0)
719 ssa_mod.funcs[param_fn_id].params << param
720 }
721 mut ret_fn_ids := []int{}
722 for i, typ in types {
723 ret_fn_ids << ssa_mod.new_function('sysv_metadata_ret_${i}', typ, [])
724 }
725
726 mut mir_mod := mir.lower_from_ssa(ssa_mod)
727 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
728
729 expected_classes := [
730 [mir.AbiEightbyteClass.integer],
731 [mir.AbiEightbyteClass.integer, .integer],
732 [mir.AbiEightbyteClass.sse],
733 [mir.AbiEightbyteClass.sse, .sse],
734 [mir.AbiEightbyteClass.integer, .sse],
735 [mir.AbiEightbyteClass.sse, .integer],
736 [mir.AbiEightbyteClass.memory],
737 [mir.AbiEightbyteClass.memory],
738 [mir.AbiEightbyteClass.sse, .sseup],
739 ]
740 expected_modes := [
741 mir.AbiPassMode.direct,
742 .direct,
743 .direct,
744 .direct,
745 .direct,
746 .direct,
747 .indirect,
748 .indirect,
749 .direct,
750 ]
751 for i in 0 .. types.len {
752 assert_abi_value_class(mir_mod.funcs[param_fn_id].abi_param_classes[i], expected_modes[i],
753 expected_classes[i])
754 assert_abi_value_class(mir_mod.funcs[ret_fn_ids[i]].abi_ret_class, expected_modes[i],
755 expected_classes[i])
756 }
757}
758
759fn test_x64_sysv_memory_class_projects_to_legacy_indirect_flags() {
760 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_callsite_memory_class')
761 i64_t := ssa_mod.type_store.get_int(64)
762 f80_t := ssa_mod.type_store.get_float(80)
763 x87_aggregate_t := register_x64_matrix_struct_fields(mut ssa_mod, [f80_t])
764
765 callee_id := ssa_mod.new_function('callee', x87_aggregate_t, [])
766 callee_param := ssa_mod.add_value_node(.argument, x87_aggregate_t, 'p', 0)
767 ssa_mod.funcs[callee_id].params << callee_param
768 caller_id := ssa_mod.new_function('caller', i64_t, [])
769 arg := ssa_mod.add_value_node(.argument, x87_aggregate_t, 'a', 0)
770 ssa_mod.funcs[caller_id].params << arg
771 caller_entry := ssa_mod.add_block(caller_id, 'entry')
772 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
773 call_val := ssa_mod.add_instr(.call, caller_entry, x87_aggregate_t, [fn_val, arg])
774 zero := ssa_mod.get_or_add_const(i64_t, '0')
775 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
776
777 mut mir_mod := mir.lower_from_ssa(ssa_mod)
778 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
779
780 assert_abi_value_class(mir_mod.funcs[callee_id].abi_param_classes[0], .indirect, [
781 .memory,
782 ])
783 assert_abi_value_class(mir_mod.funcs[callee_id].abi_ret_class, .indirect, [
784 .memory,
785 ])
786 assert mir_mod.funcs[callee_id].abi_param_class == [.indirect]
787 assert mir_mod.funcs[callee_id].abi_ret_indirect == true
788 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[0], [.int_reg], [1], [
789 0,
790 ], [.integer])
791
792 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
793 assert_abi_value_class(call_instr.abi_arg_classes[0], .indirect, [.memory])
794 assert_abi_value_class(call_instr.abi_ret_class, .indirect, [.memory])
795 assert call_instr.abi_arg_class == [.indirect]
796 assert call_instr.abi_ret_indirect == true
797 assert call_instr.op == .call_sret
798 assert_abi_layout(call_instr.abi_arg_layouts[0], [.int_reg], [1], [0], [.integer])
799}
800
801fn test_x64_sysv_direct_aggregate_size_guard_boundary() {
802 assert !sysv_aggregate_must_be_memory_before_classification(16)
803 assert sysv_aggregate_must_be_memory_before_classification(17)
804}
805
806fn test_x64_sysv_huge_fixed_array_uses_early_memory_guard_and_bounded_metadata() {
807 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_huge_fixed_array_memory')
808 i64_t := ssa_mod.type_store.get_int(64)
809 u8_t := ssa_mod.type_store.get_uint(8)
810 huge_array_t := ssa_mod.type_store.register(ssa.Type{
811 kind: .array_t
812 elem_type: u8_t
813 len: 1_000_000
814 })
815
816 callee_id := ssa_mod.new_function('callee', huge_array_t, [])
817 param := ssa_mod.add_value_node(.argument, huge_array_t, 'p', 0)
818 ssa_mod.funcs[callee_id].params << param
819 caller_id := ssa_mod.new_function('caller', i64_t, [])
820 arg := ssa_mod.add_value_node(.argument, huge_array_t, 'a', 0)
821 ssa_mod.funcs[caller_id].params << arg
822 caller_entry := ssa_mod.add_block(caller_id, 'entry')
823 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
824 call_val := ssa_mod.add_instr(.call, caller_entry, huge_array_t, [fn_val, arg])
825 zero := ssa_mod.get_or_add_const(i64_t, '0')
826 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
827
828 mut mir_mod := mir.lower_from_ssa(ssa_mod)
829 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
830 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
831
832 huge_size := mir_mod.type_size(huge_array_t)
833 assert huge_size == 1_000_000
834 assert sysv_aggregate_must_be_memory_before_classification(huge_size)
835 direct_class := sysv_abi_value_class(mir_mod, huge_array_t)
836 assert_abi_value_class(direct_class, .indirect, [.memory])
837 assert direct_class.classes.len == 1
838 assert direct_class.classes.len < (huge_size + 7) / 8
839
840 param_class := mir_mod.funcs[callee_id].abi_param_classes[0]
841 ret_class := mir_mod.funcs[callee_id].abi_ret_class
842 assert_abi_value_class(param_class, .indirect, [.memory])
843 assert_abi_value_class(ret_class, .indirect, [.memory])
844 assert param_class.classes.len == 1
845 assert ret_class.classes.len == 1
846 assert mir_mod.funcs[callee_id].abi_param_class == [.indirect]
847 assert mir_mod.funcs[callee_id].abi_ret_indirect
848 assert mir_mod.funcs[callee_id].abi_param_layouts[0].locs.len == 1
849 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[0], [.int_reg], [1], [
850 0,
851 ], [.integer])
852
853 assert_abi_value_class(call_instr.abi_arg_classes[0], .indirect, [.memory])
854 assert_abi_value_class(call_instr.abi_ret_class, .indirect, [.memory])
855 assert call_instr.abi_arg_classes[0].classes.len == 1
856 assert call_instr.abi_ret_class.classes.len == 1
857 assert call_instr.abi_arg_class == [.indirect]
858 assert call_instr.abi_ret_indirect
859 assert call_instr.op == .call_sret
860 assert call_instr.abi_arg_layouts[0].locs.len == 1
861 assert_abi_layout(call_instr.abi_arg_layouts[0], [.int_reg], [1], [0], [.integer])
862}
863
864fn test_x64_sysv_indirect_argument_consumes_gpr_before_direct_aggregate_layout() {
865 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_indirect_arg_gpr_consumption')
866 i64_t := ssa_mod.type_store.get_int(64)
867 f80_t := ssa_mod.type_store.get_float(80)
868 x87_aggregate_t := register_x64_matrix_struct_fields(mut ssa_mod, [f80_t])
869 integer_pair_t := register_x64_matrix_struct_fields(mut ssa_mod, [i64_t, i64_t])
870
871 callee_id := ssa_mod.new_function('callee', i64_t, [])
872 memory_param := ssa_mod.add_value_node(.argument, x87_aggregate_t, 'memory_arg', 0)
873 pair_param := ssa_mod.add_value_node(.argument, integer_pair_t, 'pair_arg', 0)
874 ssa_mod.funcs[callee_id].params << [memory_param, pair_param]
875 caller_id := ssa_mod.new_function('caller', i64_t, [])
876 memory_arg := ssa_mod.add_value_node(.argument, x87_aggregate_t, 'memory_arg', 0)
877 pair_arg := ssa_mod.add_value_node(.argument, integer_pair_t, 'pair_arg', 0)
878 ssa_mod.funcs[caller_id].params << [memory_arg, pair_arg]
879 caller_entry := ssa_mod.add_block(caller_id, 'entry')
880 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
881 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, [fn_val, memory_arg, pair_arg])
882 zero := ssa_mod.get_or_add_const(i64_t, '0')
883 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
884
885 mut mir_mod := mir.lower_from_ssa(ssa_mod)
886 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
887 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
888
889 assert mir_mod.funcs[callee_id].abi_param_class == [.indirect, .in_reg]
890 assert call_instr.abi_arg_class == [.indirect, .in_reg]
891 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[0], [.int_reg], [0], [
892 0,
893 ], [.integer])
894 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[1], [
895 .int_reg,
896 .int_reg,
897 ], [1, 2], [0, 8], [.integer, .integer])
898 assert_abi_layout(call_instr.abi_arg_layouts[0], [.int_reg], [0], [0], [.integer])
899 assert_abi_layout(call_instr.abi_arg_layouts[1], [.int_reg, .int_reg], [1, 2], [
900 0,
901 8,
902 ], [.integer, .integer])
903}
904
905fn test_x64_sysv_indirect_argument_spills_after_gprs_are_exhausted() {
906 mut ssa_mod := ssa.Module.new('abi_test_x64_sysv_indirect_arg_stack_spill')
907 i64_t := ssa_mod.type_store.get_int(64)
908 f80_t := ssa_mod.type_store.get_float(80)
909 x87_aggregate_t := register_x64_matrix_struct_fields(mut ssa_mod, [f80_t])
910 mut param_types := []ssa.TypeID{len: 6, init: i64_t}
911 param_types << x87_aggregate_t
912 param_types << i64_t
913
914 callee_id := ssa_mod.new_function('callee', i64_t, [])
915 for i, typ in param_types {
916 param := ssa_mod.add_value_node(.argument, typ, 'p${i}', 0)
917 ssa_mod.funcs[callee_id].params << param
918 }
919 caller_id := ssa_mod.new_function('caller', i64_t, [])
920 mut args := []ssa.ValueID{}
921 for i, typ in param_types {
922 arg := ssa_mod.add_value_node(.argument, typ, 'a${i}', 0)
923 ssa_mod.funcs[caller_id].params << arg
924 args << arg
925 }
926 caller_entry := ssa_mod.add_block(caller_id, 'entry')
927 fn_val := ssa_mod.add_value_node(.unknown, 0, 'callee', 0)
928 mut operands := [fn_val]
929 operands << args
930 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, operands)
931 zero := ssa_mod.get_or_add_const(i64_t, '0')
932 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
933
934 mut mir_mod := mir.lower_from_ssa(ssa_mod)
935 lower_with_x64_abi(mut mir_mod, .x64, .sysv)
936 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
937
938 assert mir_mod.funcs[callee_id].abi_param_class[6] == .indirect
939 assert call_instr.abi_arg_class[6] == .indirect
940 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[6], [.stack], [0], [
941 0,
942 ], [.integer])
943 assert_abi_layout(call_instr.abi_arg_layouts[6], [.stack], [0], [0], [.integer])
944 assert_abi_layout(mir_mod.funcs[callee_id].abi_param_layouts[7], [.stack], [1], [
945 0,
946 ], [.integer])
947 assert_abi_layout(call_instr.abi_arg_layouts[7], [.stack], [1], [0], [.integer])
948}
949
950fn test_x64_windows_indirect_call_uses_function_pointer_signature_for_aggregate_args() {
951 mut ssa_mod := ssa.Module.new('abi_test_x64_windows_indirect_call_matrix')
952 i64_t := ssa_mod.type_store.get_int(64)
953 struct8_t := register_x64_matrix_struct_bytes(mut ssa_mod, 8)
954 struct9_t := register_x64_matrix_struct_bytes(mut ssa_mod, 9)
955 fn_sig_t := ssa_mod.type_store.register(ssa.Type{
956 kind: .func_t
957 params: [struct8_t, struct9_t]
958 ret_type: i64_t
959 })
960 fn_ptr_t := ssa_mod.type_store.get_ptr(fn_sig_t)
961
962 caller_id := ssa_mod.new_function('caller', i64_t, [])
963 fn_ptr := ssa_mod.add_value_node(.argument, fn_ptr_t, 'fn_ptr', 0)
964 arg8 := ssa_mod.add_value_node(.argument, struct8_t, 'arg8', 0)
965 arg9 := ssa_mod.add_value_node(.argument, struct9_t, 'arg9', 0)
966 ssa_mod.funcs[caller_id].params << [fn_ptr, arg8, arg9]
967 caller_entry := ssa_mod.add_block(caller_id, 'entry')
968 call_val := ssa_mod.add_instr(.call_indirect, caller_entry, i64_t, [fn_ptr, arg8, arg9])
969 zero := ssa_mod.get_or_add_const(i64_t, '0')
970 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
971
972 mut mir_mod := mir.lower_from_ssa(ssa_mod)
973 lower_with_x64_abi(mut mir_mod, .x64, .windows)
974
975 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
976 assert call_instr.op == .call_indirect
977 assert call_instr.abi_arg_class == [.in_reg, .indirect]
978}
979
980fn test_x64_windows_unresolved_callsite_uses_alloca_logical_aggregate_type() {
981 mut ssa_mod := ssa.Module.new('abi_test_x64_windows_alloca_logical_arg')
982 i64_t := ssa_mod.type_store.get_int(64)
983 struct9_t := register_x64_matrix_struct_bytes(mut ssa_mod, 9)
984 struct9_ptr_t := ssa_mod.type_store.get_ptr(struct9_t)
985
986 caller_id := ssa_mod.new_function('caller', i64_t, [])
987 caller_entry := ssa_mod.add_block(caller_id, 'entry')
988 tmp := ssa_mod.add_instr(.alloca, caller_entry, struct9_ptr_t, []ssa.ValueID{})
989 external_fn := ssa_mod.add_value_node(.unknown, 0, 'external_accept_struct9', 0)
990 call_val := ssa_mod.add_instr(.call, caller_entry, i64_t, [external_fn, tmp])
991 zero := ssa_mod.get_or_add_const(i64_t, '0')
992 ssa_mod.add_instr(.ret, caller_entry, 0, [zero])
993
994 mut mir_mod := mir.lower_from_ssa(ssa_mod)
995 lower_with_x64_abi(mut mir_mod, .x64, .windows)
996
997 call_instr := mir_mod.instrs[mir_mod.values[call_val].index]
998 assert call_instr.abi_arg_class == [.indirect]
999}
1000