v / vlib / v2 / gen / x64 / x64_backend_diagnostics_test.v
276 lines · 248 sloc · 9.75 KB · e78b7311ad580c800e5a3a0bd0afe3876e609685
Raw
1module x64
2
3import os
4import v2.mir
5
6struct X64BackendCompileFailure {
7 stdout string
8 stderr string
9 exit_code int
10}
11
12fn run_x64_backend_compile_failure(name string, source string) X64BackendCompileFailure {
13 tmp_dir := os.join_path(os.vtmp_dir(), 'v_x64_backend_diagnostics_${name}_${os.getpid()}')
14 os.mkdir_all(tmp_dir) or { panic(err) }
15 defer {
16 os.rmdir_all(tmp_dir) or {}
17 }
18 source_path := os.join_path(tmp_dir, '${name}.v')
19 bin_path := os.join_path(tmp_dir, name)
20 os.write_file(source_path, source) or { panic(err) }
21 vexe := x64_backend_diagnostic_vexe_command_path()
22 mut build := os.new_process(vexe)
23 defer {
24 build.close()
25 }
26 build.set_args(['-v2', '-b', 'x64', source_path, '-o', bin_path])
27 build.set_redirect_stdio()
28 build.run()
29 build.wait()
30 stdout := x64_backend_diagnostic_stdout(build.stdout_slurp())
31 stderr := build.stderr_slurp()
32 return X64BackendCompileFailure{
33 stdout: stdout
34 stderr: stderr
35 exit_code: build.code
36 }
37}
38
39fn x64_backend_diagnostic_vexe_command_path() string {
40 vexe := os.getenv_opt('VEXE') or { @VEXE }
41 if os.is_abs_path(vexe) || !os.exists(vexe) {
42 return vexe
43 }
44 return os.abs_path(vexe)
45}
46
47fn x64_backend_diagnostic_stdout(stdout string) string {
48 mut lines := []string{}
49 for line in stdout.split_into_lines() {
50 if line.starts_with(' * ') {
51 continue
52 }
53 lines << line
54 }
55 return lines.join('\n')
56}
57
58fn assert_x64_user_visible_compile_failure(failure X64BackendCompileFailure, expected_message string) {
59 assert failure.exit_code != 0, failure.stdout + failure.stderr
60 assert failure.stdout == '', failure.stdout
61 assert failure.stderr.contains(expected_message), failure.stderr
62 assert failure.stderr.contains(x64_backend_limitation_hint), failure.stderr
63 assert !failure.stderr.contains('Link failed:'), failure.stderr
64 assert !failure.stderr.contains('V panic:'), failure.stderr
65 assert !failure.stderr.contains('Backtrace'), failure.stderr
66 assert_x64_message_avoids_old_wording(failure.stderr)
67}
68
69fn assert_x64_clean_user_diagnostic_message(msg string) {
70 assert msg.starts_with('x64: unsupported backend feature: '), msg
71 assert !msg.contains('Link failed:'), msg
72 assert !msg.contains('V panic:'), msg
73 assert !msg.contains('Backtrace'), msg
74 assert_x64_message_avoids_old_wording(msg)
75}
76
77fn assert_x64_message_avoids_old_wording(msg string) {
78 assert !msg.contains('reachable helper'), msg
79 assert !msg.contains('stdout/stderr path'), msg
80 assert !msg.contains('must be compiled'), msg
81 assert !msg.contains('lowered before linking'), msg
82 assert !msg.contains('before external linking'), msg
83 assert !msg.contains('cannot resolve C stdio symbol'), msg
84 assert !msg.contains('C FILE streams'), msg
85}
86
87fn assert_x64_message_mentions_only_target_linker(format ObjectFormat, msg string) {
88 match format {
89 .elf {
90 assert msg.contains('ELF linker'), msg
91 assert !msg.contains('Mach-O'), msg
92 assert !msg.contains('PE linker'), msg
93 assert !msg.contains('Windows'), msg
94 assert !msg.contains('Kernel32'), msg
95 assert !msg.contains('macOS'), msg
96 }
97 .macho {
98 assert msg.contains('Mach-O linker'), msg
99 assert !msg.contains('ELF linker'), msg
100 assert !msg.contains('PE linker'), msg
101 assert !msg.contains('Windows'), msg
102 assert !msg.contains('Kernel32'), msg
103 assert !msg.contains('Linux'), msg
104 }
105 .coff {
106 assert msg.contains('PE linker'), msg
107 assert !msg.contains('ELF linker'), msg
108 assert !msg.contains('Mach-O'), msg
109 assert !msg.contains('Linux'), msg
110 assert !msg.contains('macOS'), msg
111 }
112 }
113}
114
115fn test_x64_user_visible_stderr_reports_clean_codegen_abi_unsupported_without_link_noise() {
116 $if windows {
117 println('skipping ${@FN}: SysV codegen diagnostic is not available on Windows')
118 } $else $if x64 {
119 failure := run_x64_backend_compile_failure('clean_codegen_abi_unsupported', 'module main
120
121fn many(a0 f64, a1 f64, a2 f64, a3 f64, a4 f64, a5 f64, a6 f64, a7 f64, a8 f64) f64 {
122 return a8
123}
124
125fn main() {
126 _ = many(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)
127}
128')
129 assert_x64_user_visible_compile_failure(failure,
130 'x64: unsupported backend feature: stack-passed float parameter')
131 assert !failure.stderr.contains('Windows'), failure.stderr
132 assert !failure.stderr.contains('Kernel32'), failure.stderr
133 assert !failure.stderr.contains('Mach-O'), failure.stderr
134 assert !failure.stderr.contains('PE linker'), failure.stderr
135 }
136}
137
138fn test_x64_user_visible_stderr_reports_captured_fn_literal_unsupported() {
139 $if x64 {
140 failure := run_x64_backend_compile_failure('captured_fn_literal_unsupported', 'module main
141
142fn make_delta(delta int) fn (int) int {
143 return fn [delta] (value int) int {
144 return value + delta
145 }
146}
147
148fn main() {
149 f := make_delta(10)
150 println(f(1))
151}
152')
153 assert_x64_user_visible_compile_failure(failure, 'x64: unsupported backend feature: ')
154 assert failure.stderr.contains('native x64 cannot lower captured function literal'), failure.stderr
155
156 assert failure.stderr.contains('closure environments are not implemented yet'), failure.stderr
157 }
158}
159
160fn test_x64_unsupported_backend_feature_message_is_normalized() {
161 assert x64_unsupported_backend_feature_message('stack-passed float parameter') == 'x64: unsupported backend feature: stack-passed float parameter'
162 assert x64_unsupported_backend_feature_message('backend feature: Windows argument lowering') == 'x64: unsupported backend feature: Windows argument lowering'
163 assert x64_unsupported_backend_feature_message('backend feature: SysV direct aggregate call result with MEMORY eightbyte classes is not implemented yet') == 'x64: unsupported backend feature: SysV direct aggregate call result with MEMORY eightbyte classes is not implemented yet'
164}
165
166fn test_x64_user_visible_linker_diagnostic_message_for_generic_external_by_format() {
167 for format in [ObjectFormat.elf, .macho, .coff] {
168 msg := x64_unresolved_external_symbol_message(format, 'v_missing_runtime_symbol',
169 'referenced from test relocation')
170
171 assert_x64_clean_user_diagnostic_message(msg)
172 assert_x64_message_mentions_only_target_linker(format, msg)
173 assert msg.contains('cannot resolve external symbol `v_missing_runtime_symbol` yet'), msg
174 assert msg.contains('referenced from test relocation'), msg
175 assert !msg.contains('C stdio/file-descriptor symbol'), msg
176 assert !msg.contains('Windows'), msg
177 assert !msg.contains('Kernel32'), msg
178 assert !msg.contains('C FILE/stdio'), msg
179 }
180}
181
182fn test_x64_user_visible_crt_stdio_and_fd_diagnostic_is_windows_only() {
183 for symbol_name in ['stderr', 'fread', '_get_osfhandle', '_open_osfhandle'] {
184 for format in [ObjectFormat.elf, .macho] {
185 msg := x64_unresolved_external_symbol_message(format, symbol_name,
186 'referenced from test relocation')
187
188 assert_x64_clean_user_diagnostic_message(msg)
189 assert_x64_message_mentions_only_target_linker(format, msg)
190 assert msg.contains('cannot resolve external symbol `${symbol_name}` yet'), msg
191 assert !msg.contains('C stdio/file-descriptor symbol'), msg
192 assert !msg.contains('Kernel32'), msg
193 assert !msg.contains('C FILE/stdio'), msg
194 }
195
196 pe_msg := x64_unresolved_external_symbol_message(.coff, symbol_name,
197 'referenced from test relocation')
198 assert_x64_clean_user_diagnostic_message(pe_msg)
199 assert_x64_message_mentions_only_target_linker(.coff, pe_msg)
200 assert pe_msg.contains('cannot resolve C stdio/file-descriptor symbol `${symbol_name}`'), pe_msg
201 assert pe_msg.contains('Windows x64 native backend uses Kernel32 handles'), pe_msg
202 assert pe_msg.contains('Kernel32 handles'), pe_msg
203 assert pe_msg.contains('C FILE/stdio calls'), pe_msg
204 assert !pe_msg.contains('Linux'), pe_msg
205 assert !pe_msg.contains('macOS'), pe_msg
206 }
207}
208
209fn test_x64_user_visible_linker_diagnostic_message_for_missing_runtime_helper_by_format() {
210 for format in [ObjectFormat.elf, .macho, .coff] {
211 msg := x64_unresolved_external_symbol_message(format, 'builtin__Map_string_int__keys',
212 'referenced from test relocation')
213
214 assert_x64_clean_user_diagnostic_message(msg)
215 assert_x64_message_mentions_only_target_linker(format, msg)
216 assert msg.contains('cannot resolve V runtime helper `builtin__Map_string_int__keys`'), msg
217 assert msg.contains('native x64 backend does not implement this feature for this target yet'), msg
218 assert msg.contains('referenced from test relocation'), msg
219 assert !msg.contains('C stdio/file-descriptor symbol'), msg
220 assert !msg.contains('Kernel32'), msg
221 assert !msg.contains('C FILE/stdio'), msg
222 assert !msg.contains('not imported'), msg
223 }
224}
225
226fn test_x64_gen_detects_missing_runtime_helper_while_preparing_output() {
227 for format in [ObjectFormat.elf, .macho, .coff] {
228 mut mod := mir.Module{}
229 mut gen := Gen.new_with_format(&mod, format)
230 match format {
231 .elf {
232 gen.elf.add_undefined('builtin__Map_string_int__keys')
233 }
234 .macho {
235 gen.macho.add_undefined('_builtin__Map_string_int__keys')
236 }
237 .coff {
238 gen.coff.add_undefined('builtin__Map_string_int__keys')
239 }
240 }
241
242 msg := gen.unsupported_external_symbol_message() or {
243 assert false
244 continue
245 }
246 assert_x64_clean_user_diagnostic_message(msg)
247 assert_x64_message_mentions_only_target_linker(format, msg)
248 assert msg.contains(x64_linker_name(format)), msg
249 assert msg.contains('builtin__Map_string_int__keys'), msg
250 assert msg.contains('cannot resolve V runtime helper `builtin__Map_string_int__keys`'), msg
251 assert msg.contains('native x64 backend does not implement this feature for this target yet'), msg
252 assert msg.contains('needed while preparing native x64 output'), msg
253 }
254}
255
256fn test_x64_gen_keeps_linker_resolved_external_symbols_for_external_linkers() {
257 for format in [ObjectFormat.elf, .macho, .coff] {
258 mut mod := mir.Module{}
259 mut gen := Gen.new_with_format(&mod, format)
260 match format {
261 .elf {
262 gen.elf.add_undefined('calloc')
263 }
264 .macho {
265 gen.macho.add_undefined('_calloc')
266 }
267 .coff {
268 gen.coff.add_undefined('calloc')
269 }
270 }
271
272 if msg := gen.unsupported_external_symbol_message() {
273 assert false, msg
274 }
275 }
276}
277