v / vlib / v2 / builder / target_os_test.v
1324 lines · 1155 sloc · 39.83 KB · ddb021b9866c3b4523b746fa2f4c16a594f8bd89
Raw
1module builder
2
3import os
4import v2.ast
5import v2.abi
6import v2.gen.x64
7import v2.insel
8import v2.markused
9import v2.mir
10import v2.pref
11import v2.ssa
12import v2.ssa.optimize as ssa_optimize
13import v2.transformer
14import v2.types
15
16fn write_test_file(path string) {
17 os.write_file(path, 'module main\nfn marker() {}\n') or { panic(err) }
18}
19
20fn parse_test_files(mut b Builder, paths []string) []ast.File {
21 b.parse_files(paths)
22 b.flat = b.flat_builder.flat
23 return b.flat.to_files()
24}
25
26fn parse_test_builder_files(mut b Builder, paths []string) {
27 b.files = b.parse_files(paths)
28 b.flat = b.flat_builder.flat
29}
30
31fn transform_test_builder_files(mut b Builder, mut trans transformer.Transformer) {
32 new_flat, files_out := trans.transform_files_to_flat(&b.flat, b.files)
33 b.flat = new_flat
34 b.files = files_out
35}
36
37fn mark_used_windows_x64_test(mut b Builder) map[string]bool {
38 opts := markused.MarkUsedOptions{
39 minimal_runtime_roots: true
40 }
41 return markused.mark_used_flat_with_options(&b.flat, b.env, opts)
42}
43
44fn build_test_ssa(mut b Builder, mut ssa_builder ssa.Builder) {
45 ssa_builder.build_all_from_flat(&b.flat)
46}
47
48fn test_flat_codegen_backends_keep_flat_for_codegen() {
49 mut prefs := pref.Preferences{
50 backend: .c
51 }
52 mut b := Builder{
53 pref: &prefs
54 }
55
56 assert b.should_keep_flat_for_codegen()
57 b.flat = ast.FlatAst{
58 files: [ast.FlatFile{}]
59 }
60 assert b.should_build_ssa_from_flat()
61
62 prefs.backend = .cleanc
63 assert b.should_keep_flat_for_codegen()
64 prefs.backend = .v
65 assert !b.should_keep_flat_for_codegen()
66 prefs.backend = .x64
67 assert b.should_keep_flat_for_codegen()
68}
69
70fn test_gen_ssa_c_consumes_flat_codegen_input() {
71 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_ssa_c_flat_${os.getpid()}')
72 os.rmdir_all(tmp_dir) or {}
73 os.mkdir_all(tmp_dir) or { panic(err) }
74 defer {
75 os.rmdir_all(tmp_dir) or {}
76 }
77
78 output_path := os.join_path(tmp_dir, 'ssa_flat.c')
79 files := [
80 ast.File{
81 name: 'main.v'
82 mod: 'main'
83 stmts: [
84 ast.Stmt(ast.ModuleStmt{
85 name: 'main'
86 }),
87 ]
88 },
89 ]
90 mut prefs := pref.Preferences{
91 backend: .c
92 output_file: output_path
93 }
94 mut b := Builder{
95 pref: &prefs
96 files: files
97 env: types.Environment.new()
98 flat: ast.flatten_files(files)
99 }
100
101 b.gen_ssa_c()
102
103 assert b.flat.files.len == 0
104 assert os.exists(output_path)
105 c_source := os.read_file(output_path) or { panic(err) }
106 assert c_source.len > 0
107}
108
109fn test_get_v_files_from_dir_uses_windows_target_os() {
110 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_filter_windows_${os.getpid()}')
111 os.rmdir_all(tmp_dir) or {}
112 os.mkdir_all(tmp_dir) or { panic(err) }
113 defer {
114 os.rmdir_all(tmp_dir) or {}
115 }
116
117 write_test_file(os.join_path(tmp_dir, 'common.v'))
118 write_test_file(os.join_path(tmp_dir, 'platform_windows.v'))
119 write_test_file(os.join_path(tmp_dir, 'platform_nix.v'))
120 write_test_file(os.join_path(tmp_dir, 'platform_termux.c.v'))
121 write_test_file(os.join_path(tmp_dir, 'platform_linux.v'))
122 write_test_file(os.join_path(tmp_dir, 'platform_darwin.v'))
123
124 names := get_v_files_from_dir(tmp_dir, []string{}, 'windows').map(os.file_name(it))
125 assert 'common.v' in names
126 assert 'platform_windows.v' in names
127 assert 'platform_nix.v' !in names
128 assert 'platform_termux.c.v' !in names
129 assert 'platform_linux.v' !in names
130 assert 'platform_darwin.v' !in names
131}
132
133fn test_get_v_files_from_dir_uses_linux_and_macos_target_os() {
134 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_filter_unix_${os.getpid()}')
135 os.rmdir_all(tmp_dir) or {}
136 os.mkdir_all(tmp_dir) or { panic(err) }
137 defer {
138 os.rmdir_all(tmp_dir) or {}
139 }
140
141 write_test_file(os.join_path(tmp_dir, 'common.v'))
142 write_test_file(os.join_path(tmp_dir, 'platform_windows.v'))
143 write_test_file(os.join_path(tmp_dir, 'platform_nix.v'))
144 write_test_file(os.join_path(tmp_dir, 'platform_termux.c.v'))
145 write_test_file(os.join_path(tmp_dir, 'platform_linux.v'))
146 write_test_file(os.join_path(tmp_dir, 'platform_macos.v'))
147 write_test_file(os.join_path(tmp_dir, 'platform_darwin.v'))
148
149 linux_names := get_v_files_from_dir(tmp_dir, []string{}, 'linux').map(os.file_name(it))
150 assert 'common.v' in linux_names
151 assert 'platform_nix.v' in linux_names
152 assert 'platform_linux.v' in linux_names
153 assert 'platform_windows.v' !in linux_names
154 assert 'platform_termux.c.v' !in linux_names
155 assert 'platform_macos.v' !in linux_names
156 assert 'platform_darwin.v' !in linux_names
157
158 macos_names := get_v_files_from_dir(tmp_dir, []string{}, 'macos').map(os.file_name(it))
159 assert 'common.v' in macos_names
160 assert 'platform_nix.v' in macos_names
161 assert 'platform_macos.v' in macos_names
162 assert 'platform_darwin.v' in macos_names
163 assert 'platform_windows.v' !in macos_names
164 assert 'platform_termux.c.v' !in macos_names
165 assert 'platform_linux.v' !in macos_names
166}
167
168fn test_get_v_files_from_dir_uses_termux_target_os() {
169 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_filter_termux_${os.getpid()}')
170 os.rmdir_all(tmp_dir) or {}
171 os.mkdir_all(tmp_dir) or { panic(err) }
172 defer {
173 os.rmdir_all(tmp_dir) or {}
174 }
175
176 write_test_file(os.join_path(tmp_dir, 'common.v'))
177 write_test_file(os.join_path(tmp_dir, 'platform_nix.v'))
178 write_test_file(os.join_path(tmp_dir, 'platform_termux.c.v'))
179 write_test_file(os.join_path(tmp_dir, 'platform_android_outside_termux.c.v'))
180 write_test_file(os.join_path(tmp_dir, 'platform_android.c.v'))
181 write_test_file(os.join_path(tmp_dir, 'platform_linux.v'))
182 write_test_file(os.join_path(tmp_dir, 'platform_windows.v'))
183 write_test_file(os.join_path(tmp_dir, 'platform_macos.v'))
184 write_test_file(os.join_path(tmp_dir, 'platform_darwin.v'))
185
186 termux_names := get_v_files_from_dir(tmp_dir, []string{}, 'termux').map(os.file_name(it))
187 assert 'common.v' in termux_names
188 assert 'platform_nix.v' in termux_names
189 assert 'platform_termux.c.v' in termux_names
190 assert 'platform_android.c.v' in termux_names
191 assert 'platform_android_outside_termux.c.v' !in termux_names
192 assert 'platform_linux.v' !in termux_names
193 assert 'platform_windows.v' !in termux_names
194 assert 'platform_macos.v' !in termux_names
195 assert 'platform_darwin.v' !in termux_names
196}
197
198fn test_parse_files_uses_host_source_filter_for_cross_target() {
199 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_filter_cross_${os.getpid()}')
200 os.rmdir_all(tmp_dir) or {}
201 os.mkdir_all(tmp_dir) or { panic(err) }
202 defer {
203 os.rmdir_all(tmp_dir) or {}
204 }
205
206 write_test_file(os.join_path(tmp_dir, 'common.v'))
207 write_test_file(os.join_path(tmp_dir, 'platform_nix.v'))
208 write_test_file(os.join_path(tmp_dir, 'platform_linux.v'))
209 write_test_file(os.join_path(tmp_dir, 'platform_macos.v'))
210 write_test_file(os.join_path(tmp_dir, 'platform_darwin.v'))
211 write_test_file(os.join_path(tmp_dir, 'platform_windows.v'))
212 write_test_file(os.join_path(tmp_dir, 'platform_termux.c.v'))
213
214 mut prefs := pref.new_preferences()
215 prefs.skip_builtin = true
216 prefs.skip_imports = true
217 prefs.target_os = 'cross'
218 prefs.output_cross_c = true
219 prefs.user_defines = ['cross']
220 mut b := new_builder(&prefs)
221 files := parse_test_files(mut b, [tmp_dir])
222 cross_names := files.map(os.file_name(it.name))
223 host_os := normalize_target_os_name(os.user_os())
224 assert 'common.v' in cross_names
225 assert ('platform_nix.v' in cross_names) == (host_os != 'windows')
226 assert ('platform_linux.v' in cross_names) == (host_os == 'linux')
227 assert ('platform_macos.v' in cross_names) == (host_os == 'macos')
228 assert ('platform_darwin.v' in cross_names) == (host_os == 'macos')
229 assert ('platform_windows.v' in cross_names) == (host_os == 'windows')
230 assert ('platform_termux.c.v' in cross_names) == (host_os == 'termux')
231}
232
233fn test_parse_files_excludes_os_variants_for_freestanding_none_target() {
234 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_filter_none_${os.getpid()}')
235 os.rmdir_all(tmp_dir) or {}
236 os.mkdir_all(tmp_dir) or { panic(err) }
237 defer {
238 os.rmdir_all(tmp_dir) or {}
239 }
240
241 write_test_file(os.join_path(tmp_dir, 'common.v'))
242 write_test_file(os.join_path(tmp_dir, 'platform_nix.v'))
243 write_test_file(os.join_path(tmp_dir, 'platform_linux.v'))
244 write_test_file(os.join_path(tmp_dir, 'platform_macos.v'))
245 write_test_file(os.join_path(tmp_dir, 'platform_darwin.v'))
246 write_test_file(os.join_path(tmp_dir, 'platform_windows.v'))
247 write_test_file(os.join_path(tmp_dir, 'platform_termux.c.v'))
248
249 mut prefs := pref.new_preferences()
250 prefs.skip_builtin = true
251 prefs.skip_imports = true
252 prefs.freestanding = true
253 prefs.target_os = 'none'
254 prefs.user_defines = ['freestanding']
255 mut b := new_builder(&prefs)
256 files := parse_test_files(mut b, [tmp_dir])
257 names := files.map(os.file_name(it.name))
258 assert 'common.v' in names
259 assert 'platform_nix.v' !in names
260 assert 'platform_termux.c.v' !in names
261 assert 'platform_linux.v' !in names
262 assert 'platform_macos.v' !in names
263 assert 'platform_darwin.v' !in names
264 assert 'platform_windows.v' !in names
265}
266
267fn test_parse_files_uses_target_os_preference_for_windows_files() {
268 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_parse_windows_${os.getpid()}')
269 os.rmdir_all(tmp_dir) or {}
270 os.mkdir_all(tmp_dir) or { panic(err) }
271 defer {
272 os.rmdir_all(tmp_dir) or {}
273 }
274
275 os.write_file(os.join_path(tmp_dir, 'main.v'), 'module main\nfn main() {}\n') or { panic(err) }
276 write_test_file(os.join_path(tmp_dir, 'platform_windows.v'))
277 write_test_file(os.join_path(tmp_dir, 'platform_nix.v'))
278 write_test_file(os.join_path(tmp_dir, 'platform_termux.c.v'))
279 write_test_file(os.join_path(tmp_dir, 'platform_linux.v'))
280 write_test_file(os.join_path(tmp_dir, 'platform_darwin.v'))
281
282 mut prefs := pref.new_preferences()
283 prefs.skip_builtin = true
284 prefs.skip_imports = true
285 prefs.target_os = 'windows'
286 mut b := new_builder(&prefs)
287 files := parse_test_files(mut b, [tmp_dir])
288 names := files.map(os.file_name(it.name))
289
290 assert 'main.v' in names
291 assert 'platform_windows.v' in names
292 assert 'platform_nix.v' !in names
293 assert 'platform_termux.c.v' !in names
294 assert 'platform_linux.v' !in names
295 assert 'platform_darwin.v' !in names
296}
297
298fn test_default_file_key_strips_all_supported_platform_suffixes() {
299 default_key := fname_without_platform_postfix('/tmp/foo_default.c.v')
300 for suffix in ['nix', 'windows', 'linux', 'darwin', 'macos', 'bsd', 'android', 'termux',
301 'android_outside_termux', 'ios', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', 'solaris',
302 'qnx', 'serenity', 'plan9', 'vinix'] {
303 assert fname_without_platform_postfix('/tmp/foo_${suffix}.c.v') == default_key
304 }
305}
306
307fn test_default_files_are_skipped_for_extended_platform_variants() {
308 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_extended_platform_defaults_${os.getpid()}')
309 os.rmdir_all(tmp_dir) or {}
310 os.mkdir_all(tmp_dir) or { panic(err) }
311 defer {
312 os.rmdir_all(tmp_dir) or {}
313 }
314
315 for target in ['ios', 'qnx', 'serenity', 'plan9', 'vinix'] {
316 write_test_file(os.join_path(tmp_dir, 'foo_default.c.v'))
317 write_test_file(os.join_path(tmp_dir, 'foo_${target}.c.v'))
318 names := get_v_files_from_dir(tmp_dir, []string{}, target).map(os.file_name(it))
319 assert 'foo_${target}.c.v' in names
320 assert 'foo_default.c.v' !in names
321 os.rm(os.join_path(tmp_dir, 'foo_default.c.v')) or {}
322 os.rm(os.join_path(tmp_dir, 'foo_${target}.c.v')) or {}
323 }
324}
325
326fn test_active_file_imports_filters_conditional_imports_by_target_os() {
327 file := ast.File{
328 mod: 'main'
329 name: 'main.v'
330 stmts: [
331 ast.Stmt(ast.ExprStmt{
332 expr: ast.ComptimeExpr{
333 expr: ast.IfExpr{
334 cond: ast.Ident{
335 name: 'linux'
336 }
337 stmts: [
338 ast.Stmt(ast.ImportStmt{
339 name: 'linmod'
340 }),
341 ]
342 }
343 }
344 }),
345 ast.Stmt(ast.ExprStmt{
346 expr: ast.ComptimeExpr{
347 expr: ast.IfExpr{
348 cond: ast.Ident{
349 name: 'windows'
350 }
351 stmts: [
352 ast.Stmt(ast.ImportStmt{
353 name: 'winmod'
354 }),
355 ]
356 }
357 }
358 }),
359 ast.Stmt(ast.ExprStmt{
360 expr: ast.ComptimeExpr{
361 expr: ast.IfExpr{
362 cond: ast.Ident{
363 name: 'macos'
364 }
365 stmts: [
366 ast.Stmt(ast.ImportStmt{
367 name: 'macmod'
368 }),
369 ]
370 }
371 }
372 }),
373 ast.Stmt(ast.ExprStmt{
374 expr: ast.ComptimeExpr{
375 expr: ast.IfExpr{
376 cond: ast.Ident{
377 name: 'none'
378 }
379 stmts: [
380 ast.Stmt(ast.ImportStmt{
381 name: 'nonemod'
382 }),
383 ]
384 }
385 }
386 }),
387 ]
388 }
389 windows_names := active_file_imports(file, [], 'windows').map(it.name)
390 linux_names := active_file_imports(file, [], 'linux').map(it.name)
391 macos_names := active_file_imports(file, [], 'macos').map(it.name)
392 none_names := active_file_imports(file, [], 'none').map(it.name)
393 windows_with_linux_define_names := active_file_imports_with_explicit(file, [
394 'linux',
395 ], [
396 'linux',
397 ], 'windows').map(it.name)
398
399 assert 'winmod' in windows_names
400 assert 'linmod' !in windows_names
401 assert 'macmod' !in windows_names
402 assert 'nonemod' !in windows_names
403
404 assert 'linmod' in linux_names
405 assert 'winmod' !in linux_names
406 assert 'macmod' !in linux_names
407 assert 'nonemod' !in linux_names
408
409 assert 'macmod' in macos_names
410 assert 'winmod' !in macos_names
411 assert 'linmod' !in macos_names
412 assert 'nonemod' !in macos_names
413
414 assert 'nonemod' in none_names
415 assert 'linmod' !in none_names
416 assert 'winmod' !in none_names
417 assert 'macmod' !in none_names
418
419 assert 'winmod' in windows_with_linux_define_names
420 assert 'linmod' !in windows_with_linux_define_names
421 assert 'macmod' !in windows_with_linux_define_names
422 assert 'nonemod' !in windows_with_linux_define_names
423}
424
425fn test_header_cache_stamp_uses_target_os_preference() {
426 mut linux_prefs := pref.new_preferences()
427 linux_prefs.skip_builtin = true
428 linux_prefs.target_os = 'linux'
429 linux_builder := new_builder(&linux_prefs)
430 linux_stamp := linux_builder.header_stamp_for_modules([])
431
432 mut windows_prefs := pref.new_preferences()
433 windows_prefs.skip_builtin = true
434 windows_prefs.target_os = 'windows'
435 windows_builder := new_builder(&windows_prefs)
436 windows_stamp := windows_builder.header_stamp_for_modules([])
437
438 assert linux_stamp.contains('target_os=linux')
439 assert windows_stamp.contains('target_os=windows')
440 assert linux_stamp != windows_stamp
441}
442
443fn test_cached_called_fn_names_are_persisted_for_reused_objects() {
444 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cached_called_names_${os.getpid()}')
445 os.mkdir_all(tmp_dir) or { panic(err) }
446 defer {
447 os.rmdir_all(tmp_dir) or {}
448 }
449 prefs := pref.new_preferences()
450 mut b := new_builder(&prefs)
451 b.cached_called_fn_names['local__already'] = true
452 b.cached_called_fn_names['json2__decode_T_Foo'] = true
453 b.write_cached_called_fn_names(tmp_dir, 'virtuals', {
454 'local__already': true
455 })
456
457 mut b2 := new_builder(&prefs)
458 assert b2.load_cached_called_fn_names(tmp_dir, 'virtuals')
459 assert 'json2__decode_T_Foo' in b2.cached_called_fn_names
460 assert 'local__already' !in b2.cached_called_fn_names
461 assert !b2.load_cached_called_fn_names(tmp_dir, 'missing')
462}
463
464fn test_vh_cache_reuse_requires_cached_called_fn_metadata() {
465 tmp_dir := os.join_path(os.vtmp_dir(), 'v2_cached_called_names_required_${os.getpid()}')
466 os.mkdir_all(tmp_dir) or { panic(err) }
467 defer {
468 os.rmdir_all(tmp_dir) or {}
469 }
470 for cache_name in [builtin_cache_name, imports_cache_name, virtuals_cache_name] {
471 os.write_file(cache_path_join(tmp_dir, '${cache_name}.o'), 'stale object') or { panic(err) }
472 os.write_file(cache_path_join(tmp_dir, '${cache_name}.stamp'), 'stale stamp') or {
473 panic(err)
474 }
475 }
476 prefs := pref.new_preferences()
477
478 mut module_builder := new_builder(&prefs)
479 module_builder.used_vh_for_parse = true
480 mut module_reused := true
481 _ := module_builder.ensure_cached_module_object(tmp_dir, builtin_cache_name, [], [], 'cc', '',
482 '', '', false) or {
483 module_reused = false
484 ''
485 }
486 assert !module_reused
487
488 mut parsed_builder := new_builder(&prefs)
489 parsed_builder.used_import_vh_for_parse = true
490 mut parsed_reused := true
491 _ := parsed_builder.ensure_cached_parsed_module_object(tmp_dir, imports_cache_name, [], [],
492 'cc', '', '', '', false) or {
493 parsed_reused = false
494 ''
495 }
496 assert !parsed_reused
497
498 mut virtual_builder := new_builder(&prefs)
499 virtual_builder.used_virtual_vh_for_parse = true
500 mut virtual_reused := true
501 _ := virtual_builder.ensure_cached_virtual_module_object(tmp_dir, [], [], 'cc', '', '', '',
502 false) or {
503 virtual_reused = false
504 ''
505 }
506 assert !virtual_reused
507}
508
509fn test_linux_x64_builder_default_o0_forces_ssa_optimize() {
510 if os.user_os() != 'linux' || os.uname().machine !in ['x86_64', 'amd64'] {
511 return
512 }
513 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_linux_x64_default_o0_${os.getpid()}')
514 os.rmdir_all(tmp_dir) or {}
515 os.mkdir_all(tmp_dir) or { panic(err) }
516 defer {
517 os.rmdir_all(tmp_dir) or {}
518 }
519
520 main_path := os.join_path(tmp_dir, 'main.v')
521 out_path := os.join_path(tmp_dir, 'main')
522 os.write_file(main_path, "module main\nfn main() { println('ok') }\n") or { panic(err) }
523
524 mut prefs := pref.new_preferences_from_args(['-backend', 'x64', '-arch', 'x64', '-o', out_path,
525 '-no-parallel', '-nocache'])
526 assert prefs.no_optimize
527 prefs.target_os = 'linux'
528
529 cwd := os.getwd()
530 os.chdir(tmp_dir) or { panic(err) }
531 defer {
532 os.chdir(cwd) or {}
533 }
534 mut b := new_builder(&prefs)
535 b.build([main_path])
536
537 run := os.execute(os.quoted_path(out_path))
538 assert run.exit_code == 0, run.output
539 assert run.output.trim_space() == 'ok'
540}
541
542struct WindowsX64BuildResult {
543 undefined_symbols []string
544 built_functions []string
545 image []u8
546}
547
548fn windows_x64_map_string_wyhash_source() string {
549 return "module main
550
551fn sink_map(_ map[string]int) {}
552
553fn main() {
554 graph := {
555 'A': 1
556 'B': 2
557 }
558 sink_map(graph)
559}
560"
561}
562
563fn assert_windows_x64_map_string_wyhash_resolved(res WindowsX64BuildResult) {
564 assert 'wyhash' in res.built_functions, res.built_functions.str()
565 assert 'wyhash' !in res.undefined_symbols, res.undefined_symbols.str()
566}
567
568fn test_windows_x64_empty_main_links_without_crt_or_darwin_symbols() {
569 res := build_windows_x64_sample('module main\nfn main() {}\n', false, true)
570
571 assert 'calloc' !in res.undefined_symbols, res.undefined_symbols.str()
572 assert '_errno' !in res.undefined_symbols, res.undefined_symbols.str()
573 assert '__stdoutp' !in res.undefined_symbols, res.undefined_symbols.str()
574 assert '__stderrp' !in res.undefined_symbols, res.undefined_symbols.str()
575 assert '__stdinp' !in res.undefined_symbols, res.undefined_symbols.str()
576 assert '__error' !in res.undefined_symbols, res.undefined_symbols.str()
577
578 linked := build_windows_x64_sample('module main\nfn main() {}\n', true, true)
579 assert linked.image.len > 0
580 assert linked.image[0] == `M`
581 assert linked.image[1] == `Z`
582}
583
584fn test_windows_x64_c_globals_do_not_use_darwin_symbols() {
585 source := 'module main
586fn C.sink_ptr(voidptr)
587fn C.sink_int(int)
588
589fn main() {
590 C.sink_ptr(C.stdout)
591 C.sink_ptr(C.stderr)
592 C.sink_ptr(C.stdin)
593 C.sink_int(C.errno)
594}
595'
596 res := build_windows_x64_sample(source, false, false)
597
598 assert '__stdoutp' !in res.undefined_symbols
599 assert '__stderrp' !in res.undefined_symbols
600 assert '__stdinp' !in res.undefined_symbols
601 assert '__error' !in res.undefined_symbols
602 assert 'stdout' in res.undefined_symbols
603 assert 'stderr' in res.undefined_symbols
604 assert 'stdin' in res.undefined_symbols
605 assert '_errno' in res.undefined_symbols
606 assert 'errno' !in res.undefined_symbols
607}
608
609fn test_windows_x64_c_errno_links_with_minimal_runtime() {
610 linked := build_windows_x64_sample('module main
611
612fn main() {
613 C.errno = 0
614 C.errno += 1
615}
616',
617 true, true)
618
619 assert linked.image.len > 0
620 assert linked.image[0] == `M`
621 assert linked.image[1] == `Z`
622}
623
624fn test_windows_x64_minimal_runtime_builds_markused_core_functions() {
625 res := build_windows_x64_sample("module main\nfn main() { println('x') }\n", false, false)
626
627 assert 'builtin__println' in res.built_functions, res.built_functions.str()
628 assert 'stderr' !in res.undefined_symbols, res.undefined_symbols.str()
629 assert_no_windows_minimal_runtime_retention(res.built_functions)
630}
631
632fn test_windows_x64_minimal_runtime_map_string_generates_referenced_wyhash_stub_sequential() {
633 res := build_windows_x64_sample(windows_x64_map_string_wyhash_source(), false, true)
634 assert_windows_x64_map_string_wyhash_resolved(res)
635}
636
637fn test_windows_x64_minimal_runtime_map_string_generates_referenced_wyhash_stub_flat() {
638 res :=
639 build_windows_x64_sample_configured(windows_x64_map_string_wyhash_source(), false, true, true)
640 assert_windows_x64_map_string_wyhash_resolved(res)
641}
642
643fn test_windows_x64_minimal_runtime_map_string_generates_referenced_wyhash_stub_native_builder_path() {
644 res := build_windows_x64_sample_configured(windows_x64_map_string_wyhash_source(), false, true,
645 false)
646 assert_windows_x64_map_string_wyhash_resolved(res)
647}
648
649fn test_windows_x64_minimal_runtime_os_getwd_markused_does_not_retain_stdio_file_helpers() {
650 used := build_windows_x64_markused_sample('module main
651import os
652
653fn main() {
654 wd := os.getwd()
655 _ = wd.len
656}
657')
658
659 assert used['main|f|main'], used.str()
660 assert used['os|f|getwd'], used.str()
661 assert_no_windows_minimal_targeted_stdio_markused_leaks(used)
662 assert !used['os|f|get_raw_line'], used.str()
663 assert !used['os|f|get_raw_stdin'], used.str()
664 assert !used['os|f|is_atty'], used.str()
665 assert !used['os|f|input_password'], used.str()
666}
667
668fn test_windows_x64_minimal_runtime_os_getwd_build_does_not_retain_stdin_helpers() {
669 res := build_windows_x64_sample('module main
670import os
671
672fn main() {
673 wd := os.getwd()
674 if wd.len == 0 {
675 return
676 }
677}
678',
679 false, false)
680
681 assert 'os__getwd' in res.built_functions, res.built_functions.str()
682 assert 'os__get_raw_line' !in res.built_functions, res.built_functions.str()
683 assert 'os__get_raw_stdin' !in res.built_functions, res.built_functions.str()
684 assert 'os__is_atty' !in res.built_functions, res.built_functions.str()
685 assert 'os__input_password' !in res.built_functions, res.built_functions.str()
686 assert 'STD_INPUT_HANDLE' !in res.undefined_symbols, res.undefined_symbols.str()
687 assert '_get_osfhandle' !in res.undefined_symbols, res.undefined_symbols.str()
688}
689
690fn test_windows_x64_minimal_runtime_get_raw_line_uses_kernel32_stdin_helpers() {
691 res := build_windows_x64_sample('module main
692import os
693
694fn main() {
695 line := os.get_raw_line()
696 _ = line.len
697}
698',
699 false, false)
700
701 assert 'os__get_raw_line' in res.built_functions, res.built_functions.str()
702 assert 'os__is_atty' in res.built_functions, res.built_functions.str()
703 assert 'GetStdHandle' in res.undefined_symbols, res.undefined_symbols.str()
704 assert 'GetConsoleMode' in res.undefined_symbols, res.undefined_symbols.str()
705 assert 'ReadConsole' in res.undefined_symbols, res.undefined_symbols.str()
706 assert 'ReadFile' in res.undefined_symbols, res.undefined_symbols.str()
707 assert 'STD_INPUT_HANDLE' !in res.undefined_symbols, res.undefined_symbols.str()
708 assert 'INVALID_HANDLE_VALUE' !in res.undefined_symbols, res.undefined_symbols.str()
709 assert '_get_osfhandle' !in res.undefined_symbols, res.undefined_symbols.str()
710 assert 'stdin' !in res.undefined_symbols, res.undefined_symbols.str()
711 assert 'stdout' !in res.undefined_symbols, res.undefined_symbols.str()
712 assert 'stderr' !in res.undefined_symbols, res.undefined_symbols.str()
713}
714
715fn test_windows_x64_invalid_handle_value_macro_does_not_become_undefined_symbol() {
716 res := build_windows_x64_sample('module main
717
718fn invalid_handle() voidptr {
719 return C.INVALID_HANDLE_VALUE
720}
721
722fn main() {
723 _ := invalid_handle()
724}
725',
726 false, false)
727
728 assert 'INVALID_HANDLE_VALUE' !in res.undefined_symbols, res.undefined_symbols.str()
729}
730
731fn test_windows_x64_minimal_runtime_prunes_crt_stdio_else_branch_before_markused() {
732 used := build_windows_x64_markused_sample('module main
733import os
734
735fn C.sink_ptr(voidptr)
736fn C.setvbuf(voidptr, voidptr, int, usize) int
737
738fn minimal_marker() {}
739
740fn guarded_stdio_branch() {
741 $if v2_native_windows_pe_minimal ? {
742 minimal_marker()
743 } $else {
744 C.sink_ptr(C.stdout)
745 C.sink_ptr(C.stderr)
746 C.setvbuf(C.stdout, 0, 0, usize(0))
747 unbuffer_stdout()
748 os.stdout()
749 os.stderr()
750 }
751}
752
753fn main() {
754 guarded_stdio_branch()
755}
756')
757
758 assert used['main|f|main'], used.str()
759 assert used['main|f|guarded_stdio_branch'], used.str()
760 assert used['main|f|minimal_marker'], used.str()
761 assert !used['builtin|f|unbuffer_stdout'], used.str()
762 assert !used['os|f|stdout'], used.str()
763 assert !used['os|f|stderr'], used.str()
764 assert !windows_x64_markused_key_contains(used, 'setvbuf'), used.str()
765 assert !windows_x64_markused_key_contains(used, '|f|stdout'), used.str()
766 assert !windows_x64_markused_key_contains(used, '|f|stderr'), used.str()
767}
768
769fn test_windows_x64_minimal_runtime_dynamic_array_markused_does_not_retain_crt_stdio() {
770 used := build_windows_x64_markused_sample("module main
771
772fn main() {
773 mut a := [1, 2, 3]
774 a[1] = 7
775 a << 11
776 mut i := 0
777 mut sum := 0
778 for i < a.len {
779 sum += a[i]
780 i += 1
781 }
782 if sum == 22 {
783 println('ok')
784 }
785}
786")
787
788 assert used['main|f|main'], used.str()
789 assert used['builtin|f|println'], used.str()
790 assert_no_windows_minimal_targeted_stdio_markused_leaks(used)
791}
792
793fn test_windows_x64_minimal_runtime_fixed_array_slice_copy_markused_does_not_retain_crt_stdio() {
794 used := build_windows_x64_markused_sample("module main
795
796fn main() {
797 mut f := [5]int{}
798 f[0] = 2
799 f[1] = 4
800 f[2] = 6
801 f[3] = 8
802 f[4] = 10
803 mut a := f[1..4]
804 a[1] = 9
805 a << 12
806 if a.len == 4 && f[2] == 6 {
807 println('ok')
808 }
809}
810")
811
812 assert used['main|f|main'], used.str()
813 assert used['builtin|f|println'], used.str()
814 assert_no_windows_minimal_targeted_stdio_markused_leaks(used)
815}
816
817fn test_windows_x64_array_slice_call_uses_sret_and_integer_bounds() {
818 mir_mod := build_checked_windows_x64_mir_sample("module main
819
820fn make_array() []int {
821 return [1, 2, 3]
822}
823
824fn main() {
825 mut a := make_array()
826 if a.len == 3 && a.cap >= 3 && a[0] == 1 && a[2] == 3 {
827 print('N')
828 } else {
829 print('n')
830 }
831 a << 4
832 if a.len == 4 && a[1] == 2 && a[3] == 4 {
833 print('G')
834 } else {
835 print('g')
836 }
837 b := a[1..3]
838 if b.len == 2 && b[0] == 2 && b[1] == 3 {
839 print('S')
840 } else {
841 print('s')
842 }
843 unsafe {
844 a.free()
845 }
846 println('F')
847}
848", true)
849 mut found_callee := false
850 for f in mir_mod.funcs {
851 if f.name != 'builtin__array__slice' {
852 continue
853 }
854 found_callee = true
855 assert f.abi_ret_indirect
856 assert f.abi_param_class == [.indirect, .in_reg, .in_reg]
857 }
858 assert found_callee
859
860 mut found_call := false
861 for val in mir_mod.values {
862 if val.kind != .instruction {
863 continue
864 }
865 instr := mir_mod.instrs[val.index]
866 if instr.operands.len == 0 {
867 continue
868 }
869 callee := instr.operands[0]
870 if callee <= 0 || callee >= mir_mod.values.len
871 || mir_mod.values[callee].name != 'builtin__array__slice' {
872 continue
873 }
874 found_call = true
875 assert instr.op == .call_sret
876 assert instr.abi_ret_indirect
877 assert instr.abi_arg_class == [.indirect, .in_reg, .in_reg]
878 assert instr.operands.len >= 4
879 start_arg := instr.operands[2]
880 end_arg := instr.operands[3]
881 assert mir_mod.type_store.types[mir_mod.values[start_arg].typ].kind == .int_t
882 assert mir_mod.type_store.types[mir_mod.values[end_arg].typ].kind == .int_t
883 }
884 assert found_call
885}
886
887fn test_windows_x64_println_links_pe_image() {
888 linked := build_windows_x64_sample("module main\nfn main() { println('x') }\n", true, true)
889
890 assert linked.image.len > 0
891 assert linked.image[0] == `M`
892 assert linked.image[1] == `Z`
893 assert 'WriteConsoleW' in linked.undefined_symbols, linked.undefined_symbols.str()
894 assert 'MultiByteToWideChar' in linked.undefined_symbols, linked.undefined_symbols.str()
895 assert 'GetProcessHeap' in linked.undefined_symbols, linked.undefined_symbols.str()
896 assert 'HeapAlloc' in linked.undefined_symbols, linked.undefined_symbols.str()
897 assert 'HeapFree' in linked.undefined_symbols, linked.undefined_symbols.str()
898 assert 'WriteFile' in linked.undefined_symbols, linked.undefined_symbols.str()
899 assert_no_windows_minimal_crt_symbols(linked.undefined_symbols)
900 assert_no_windows_minimal_runtime_retention(linked.built_functions)
901}
902
903fn test_windows_x64_minimal_runtime_preserves_imported_module_init() {
904 linked := build_windows_x64_sample_with_files({
905 'main.v': 'module main
906import dep
907
908fn main() {}
909'
910 'dep/dep.v': 'module dep
911
912fn init() {
913 touch()
914}
915
916fn touch() {}
917'
918 }, true, true)
919
920 assert 'dep__init' in linked.built_functions, linked.built_functions.str()
921 assert 'dep__touch' in linked.built_functions, linked.built_functions.str()
922 assert linked.image.len > 0
923 assert linked.image[0] == `M`
924 assert linked.image[1] == `Z`
925}
926
927fn test_windows_x64_minimal_runtime_preserves_imported_module_init_without_optimize() {
928 res := build_windows_x64_sample_with_files({
929 'main.v': 'module main
930import dep
931
932fn main() {}
933'
934 'dep/dep.v': 'module dep
935
936fn init() {
937 touch()
938}
939
940fn touch() {}
941'
942 }, false, false)
943
944 assert 'dep__init' in res.built_functions, res.built_functions.str()
945 assert 'dep__touch' in res.built_functions, res.built_functions.str()
946 assert_no_windows_minimal_crt_symbols(res.undefined_symbols)
947 assert_no_windows_minimal_runtime_retention(res.built_functions)
948}
949
950fn test_windows_x64_minimal_runtime_preserves_conditional_imported_module_init() {
951 linked := build_windows_x64_sample_with_files({
952 'main.v': 'module main
953$if windows {
954 import dep
955}
956
957fn main() {}
958'
959 'dep/dep.v': 'module dep
960
961fn init() {
962 touch()
963}
964
965fn touch() {}
966'
967 }, true, true)
968
969 assert 'dep__init' in linked.built_functions, linked.built_functions.str()
970 assert 'dep__touch' in linked.built_functions, linked.built_functions.str()
971 assert linked.image.len > 0
972 assert linked.image[0] == `M`
973 assert linked.image[1] == `Z`
974}
975
976fn test_windows_x64_minimal_runtime_preserves_dotted_import_module_init() {
977 linked := build_windows_x64_sample_with_files({
978 'main.v': 'module main
979import fixture.inner
980
981fn main() {}
982'
983 'fixture/inner/inner.v': 'module inner
984
985const dotted_runtime_value = make_value()
986
987fn init() {
988 touch()
989}
990
991fn touch() {}
992
993fn make_value() int {
994 return 7
995}
996'
997 }, true, true)
998
999 assert 'inner__init' in linked.built_functions, linked.built_functions.str()
1000 assert 'inner__touch' in linked.built_functions, linked.built_functions.str()
1001 assert 'inner____v_init_consts_inner' in linked.built_functions, linked.built_functions.str()
1002 assert 'inner__make_value' in linked.built_functions, linked.built_functions.str()
1003 assert linked.image.len > 0
1004 assert linked.image[0] == `M`
1005 assert linked.image[1] == `Z`
1006}
1007
1008fn test_windows_x64_minimal_runtime_preserves_dotted_import_const_init_matrix() {
1009 for optimize in [false, true] {
1010 res := build_windows_x64_sample_with_files({
1011 'main.v': 'module main
1012import fixture.inner
1013
1014fn main() {}
1015'
1016 'fixture/inner/inner.v': 'module inner
1017
1018const dotted_runtime_value = make_value()
1019
1020fn init() {
1021 touch()
1022}
1023
1024fn touch() {}
1025
1026fn make_value() int {
1027 return 7
1028}
1029'
1030 }, false, optimize)
1031
1032 assert 'inner__init' in res.built_functions, res.built_functions.str()
1033 assert 'inner__touch' in res.built_functions, res.built_functions.str()
1034 assert 'inner____v_init_consts_inner' in res.built_functions, res.built_functions.str()
1035 assert 'inner__make_value' in res.built_functions, res.built_functions.str()
1036 assert_no_windows_minimal_crt_symbols(res.undefined_symbols)
1037 assert_no_windows_large_runtime_module_retention(res.built_functions)
1038 }
1039}
1040
1041fn assert_no_windows_minimal_crt_symbols(undefined_symbols []string) {
1042 assert_no_windows_minimal_crt_stdio_symbols(undefined_symbols)
1043 for name in ['calloc', 'malloc', 'free', 'write', '_get_osfhandle'] {
1044 assert name !in undefined_symbols, undefined_symbols.str()
1045 }
1046}
1047
1048fn assert_no_windows_minimal_crt_stdio_symbols(undefined_symbols []string) {
1049 for name in ['stdin', 'stdout', 'stderr', 'printf', 'fprintf', 'dprintf', 'sprintf', 'snprintf',
1050 'wprintf', 'puts', 'fputs', 'setvbuf', 'fflush', 'fopen', '_wfopen', 'fdopen', 'freopen',
1051 '_wfreopen', 'fclose', 'pclose', '_pclose', 'fread', 'fwrite', 'fgets', 'getc', 'feof',
1052 'ferror', 'fseek', 'ftell', 'rewind', 'fileno', '_fileno', 'fgetpos'] {
1053 assert name !in undefined_symbols, undefined_symbols.str()
1054 }
1055}
1056
1057fn assert_no_windows_minimal_targeted_stdio_markused_leaks(used map[string]bool) {
1058 assert !used['os|f|stdout'], used.str()
1059 assert !used['os|f|stderr'], used.str()
1060 assert !used['builtin|f|is_terminal'], used.str()
1061 assert !used['builtin|f|write_buf_to_console'], used.str()
1062 assert !used['builtin|f|unbuffer_stdout'], used.str()
1063 assert !windows_x64_markused_key_contains(used, 'write_buf_to_fd_windows_non_minimal'), used.str()
1064 assert !windows_x64_markused_key_contains(used, 'setvbuf'), used.str()
1065}
1066
1067fn assert_no_windows_minimal_runtime_retention(built_functions []string) {
1068 for name in built_functions {
1069 assert !name.starts_with('os__'), built_functions.str()
1070 assert !name.starts_with('time__'), built_functions.str()
1071 assert !name.starts_with('io__'), built_functions.str()
1072 assert !name.starts_with('dl__'), built_functions.str()
1073 assert !name.starts_with('sha256__'), built_functions.str()
1074 assert !name.starts_with('binary__'), built_functions.str()
1075 assert name != 'builtin__is_terminal', built_functions.str()
1076 assert name != 'builtin__write_buf_to_console', built_functions.str()
1077 assert !name.contains('write_buf_to_fd_windows_non_minimal'), built_functions.str()
1078 if name.starts_with('__v_init_consts_') {
1079 assert name == '__v_init_consts_main', built_functions.str()
1080 }
1081 assert !name.contains('____v_init_consts_'), built_functions.str()
1082 }
1083}
1084
1085fn assert_no_windows_large_runtime_module_retention(built_functions []string) {
1086 for name in built_functions {
1087 assert !name.starts_with('os__'), built_functions.str()
1088 assert !name.starts_with('time__'), built_functions.str()
1089 assert !name.starts_with('io__'), built_functions.str()
1090 assert !name.starts_with('dl__'), built_functions.str()
1091 assert !name.starts_with('sha256__'), built_functions.str()
1092 assert !name.starts_with('binary__'), built_functions.str()
1093 }
1094}
1095
1096fn build_windows_x64_sample(source string, link bool, optimize bool) WindowsX64BuildResult {
1097 return build_windows_x64_sample_configured(source, link, optimize, true)
1098}
1099
1100fn build_windows_x64_sample_configured(source string, link bool, optimize bool, no_parallel bool) WindowsX64BuildResult {
1101 return build_windows_x64_sample_with_files_configured({
1102 'main.v': source
1103 }, link, optimize, no_parallel)
1104}
1105
1106fn build_windows_x64_markused_sample(source string) map[string]bool {
1107 tmp_dir := os.join_path(os.temp_dir(), 'v2_builder_windows_x64_markused_sample_${os.getpid()}')
1108 os.rmdir_all(tmp_dir) or {}
1109 os.mkdir_all(tmp_dir) or { panic(err) }
1110 defer {
1111 os.rmdir_all(tmp_dir) or {}
1112 }
1113
1114 main_path := os.join_path(tmp_dir, 'main.v')
1115 os.write_file(main_path, source) or { panic(err) }
1116
1117 mut prefs := pref.new_preferences()
1118 prefs.backend = .x64
1119 prefs.arch = .x64
1120 prefs.target_os = 'windows'
1121 prefs.skip_type_check = true
1122 prefs.no_parallel = true
1123 prefs.no_parallel_transform = true
1124 prefs.no_cache = true
1125
1126 mut b := new_builder(&prefs)
1127 b.user_files = [main_path]
1128 parse_test_builder_files(mut b, [main_path])
1129 b.env = types.Environment.new()
1130
1131 mut trans := transformer.Transformer.new_with_pref(b.env, b.pref)
1132 trans.set_file_set(b.file_set)
1133 transform_test_builder_files(mut b, mut trans)
1134 used := mark_used_windows_x64_test(mut b)
1135 assert used.len > 0
1136 return used
1137}
1138
1139fn build_checked_windows_x64_mir_sample(source string, optimize bool) mir.Module {
1140 tmp_dir := os.join_path(os.temp_dir(),
1141 'v2_builder_windows_x64_checked_mir_${os.getpid()}_${optimize}')
1142 os.rmdir_all(tmp_dir) or {}
1143 os.mkdir_all(tmp_dir) or { panic(err) }
1144 defer {
1145 os.rmdir_all(tmp_dir) or {}
1146 }
1147
1148 main_path := os.join_path(tmp_dir, 'main.v')
1149 os.write_file(main_path, source) or { panic(err) }
1150
1151 mut prefs := pref.new_preferences()
1152 prefs.backend = .x64
1153 prefs.arch = .x64
1154 prefs.target_os = 'windows'
1155 prefs.no_parallel = true
1156 prefs.no_parallel_transform = true
1157 prefs.no_cache = true
1158
1159 mut b := new_builder(&prefs)
1160 b.user_files = [main_path]
1161 parse_test_builder_files(mut b, [main_path])
1162 b.env = b.type_check_files()
1163
1164 mut trans := transformer.Transformer.new_with_pref(b.env, b.pref)
1165 trans.set_file_set(b.file_set)
1166 transform_test_builder_files(mut b, mut trans)
1167 b.used_fn_keys = mark_used_windows_x64_test(mut b)
1168 assert b.used_fn_keys.len > 0
1169
1170 mut ssa_mod := ssa.Module.new('main')
1171 mut ssa_builder := ssa.Builder.new_with_env(ssa_mod, b.env)
1172 ssa_builder.guard_invalid_type_payloads = true
1173 ssa_builder.target_os = 'windows'
1174 ssa_builder.minimal_runtime_roots = true
1175 ssa_builder.native_backend_bulk_zero_alloca = true
1176 ssa_builder.used_fn_keys = b.used_fn_keys.clone()
1177 build_test_ssa(mut b, mut ssa_builder)
1178 if optimize {
1179 ssa_optimize.optimize(mut ssa_mod)
1180 }
1181
1182 mut mir_mod := mir.lower_from_ssa(ssa_mod)
1183 abi.lower_with_x64_abi(mut mir_mod, .x64, .windows)
1184 insel.select(mut mir_mod, .x64)
1185 return mir_mod
1186}
1187
1188fn windows_x64_markused_key_contains(used map[string]bool, needle string) bool {
1189 for key, is_used in used {
1190 if is_used && key.contains(needle) {
1191 return true
1192 }
1193 }
1194 return false
1195}
1196
1197fn build_windows_x64_sample_with_files(sources map[string]string, link bool, optimize bool) WindowsX64BuildResult {
1198 return build_windows_x64_sample_with_files_configured(sources, link, optimize, true)
1199}
1200
1201fn build_windows_x64_sample_with_files_configured(sources map[string]string, link bool, optimize bool, no_parallel bool) WindowsX64BuildResult {
1202 tmp_dir := os.join_path(os.temp_dir(),
1203 'v2_builder_windows_x64_sample_${os.getpid()}_${sources.len}_${link}_${optimize}_${no_parallel}')
1204 os.rmdir_all(tmp_dir) or {}
1205 os.mkdir_all(tmp_dir) or { panic(err) }
1206 defer {
1207 os.rmdir_all(tmp_dir) or {}
1208 }
1209
1210 main_path := os.join_path(tmp_dir, 'main.v')
1211 obj_path := os.join_path(tmp_dir, 'main.obj')
1212 exe_path := os.join_path(tmp_dir, 'main.exe')
1213 for rel_path, source in sources {
1214 path := os.join_path(tmp_dir, rel_path)
1215 os.mkdir_all(os.dir(path)) or { panic(err) }
1216 os.write_file(path, source) or { panic(err) }
1217 }
1218
1219 mut prefs := pref.new_preferences()
1220 prefs.backend = .x64
1221 prefs.arch = .x64
1222 prefs.target_os = 'windows'
1223 prefs.skip_type_check = true
1224 prefs.no_parallel = no_parallel
1225 prefs.no_parallel_transform = true
1226 prefs.no_cache = true
1227 prefs.no_optimize = !optimize
1228
1229 mut b := new_builder(&prefs)
1230 b.user_files = [main_path]
1231 parse_test_builder_files(mut b, [main_path])
1232 b.env = types.Environment.new()
1233
1234 mut trans := transformer.Transformer.new_with_pref(b.env, b.pref)
1235 trans.set_file_set(b.file_set)
1236 transform_test_builder_files(mut b, mut trans)
1237 b.used_fn_keys = mark_used_windows_x64_test(mut b)
1238 assert b.used_fn_keys.len > 0
1239
1240 mut ssa_mod := ssa.Module.new('main')
1241 mut ssa_builder := ssa.Builder.new_with_env(ssa_mod, b.env)
1242 ssa_builder.guard_invalid_type_payloads = true
1243 ssa_builder.target_os = 'windows'
1244 ssa_builder.minimal_runtime_roots = true
1245 ssa_builder.native_backend_bulk_zero_alloca = true
1246 ssa_builder.used_fn_keys = b.used_fn_keys.clone()
1247 mut mir_mod := mir.Module{}
1248 mut built_functions := []string{}
1249 if no_parallel {
1250 build_test_ssa(mut b, mut ssa_builder)
1251 if optimize {
1252 ssa_optimize.optimize(mut ssa_mod)
1253 }
1254 built_functions = ssa_mod.funcs.filter(it.blocks.len > 0).map(it.name)
1255 mir_mod = mir.lower_from_ssa(ssa_mod)
1256 abi.lower_with_x64_abi(mut mir_mod, .x64, .windows)
1257 insel.select(mut mir_mod, .x64)
1258 } else {
1259 mir_mod = b.build_native_mir_from_files(b.files, .x64, 'windows', true, b.used_fn_keys,
1260 'Windows x64 sample')
1261 built_functions = mir_mod.funcs.filter(it.blocks.len > 0).map(it.name)
1262 }
1263
1264 mut gen := x64.Gen.new_with_format_and_abi(&mir_mod, .coff, .windows)
1265 gen.gen()
1266 gen.write_file(obj_path)
1267 mut image := []u8{}
1268 if link {
1269 gen.link_executable(exe_path) or { panic(err) }
1270 image = os.read_bytes(exe_path) or { panic(err) }
1271 }
1272 return WindowsX64BuildResult{
1273 undefined_symbols: coff_undefined_symbols(os.read_bytes(obj_path) or { panic(err) })
1274 built_functions: built_functions
1275 image: image
1276 }
1277}
1278
1279fn coff_undefined_symbols(data []u8) []string {
1280 ptr_to_symbols := int(coff_u32(data, 8))
1281 number_of_symbols := int(coff_u32(data, 12))
1282 string_table_off := ptr_to_symbols + number_of_symbols * 18
1283 mut symbols := []string{}
1284 mut i := 0
1285 for i < number_of_symbols {
1286 off := ptr_to_symbols + i * 18
1287 section_number := i16(coff_u16(data, off + 12))
1288 aux_count := int(data[off + 17])
1289 if section_number == 0 {
1290 symbols << coff_symbol_name(data, off, string_table_off)
1291 }
1292 i += 1 + aux_count
1293 }
1294 return symbols
1295}
1296
1297fn coff_symbol_name(data []u8, off int, string_table_off int) string {
1298 if coff_u32(data, off) == 0 {
1299 name_off := int(coff_u32(data, off + 4))
1300 return coff_string(data, string_table_off + name_off)
1301 }
1302 mut end := off
1303 for end < off + 8 && data[end] != 0 {
1304 end++
1305 }
1306 return data[off..end].bytestr()
1307}
1308
1309fn coff_string(data []u8, off int) string {
1310 mut end := off
1311 for end < data.len && data[end] != 0 {
1312 end++
1313 }
1314 return data[off..end].bytestr()
1315}
1316
1317fn coff_u16(data []u8, off int) u16 {
1318 return u16(data[off]) | (u16(data[off + 1]) << 8)
1319}
1320
1321fn coff_u32(data []u8, off int) u32 {
1322 return u32(data[off]) | (u32(data[off + 1]) << 8) | (u32(data[off + 2]) << 16) | (u32(data[
1323 off + 3]) << 24)
1324}
1325