Skip to content

Commit f44ca9a

Browse files
committed
v3: tighten fastc direct inference
1 parent c8c34a4 commit f44ca9a

5 files changed

Lines changed: 61 additions & 19 deletions

File tree

vlib/v3/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,15 @@ Integer-range bounds in the direct lane are evaluated once, from left to right.
112112
C-string and embedded-NUL string literals, assertions, `sizeof`, comparison/logical, shift,
113113
division, and modulo expressions, and functions with narrow integer signatures are promoted to
114114
the complete lane. Expressions containing decimal `2147483648`, including composite minimum-`int`
115-
expressions, are promoted as well, as are expressions containing hexadecimal literals above the
116-
signed 32-bit range. Parallel assignments and indexing expressions are promoted, preserving
115+
expressions, are promoted as well, as are hexadecimal and binary literals above the signed 32-bit
116+
range. Parallel assignments and indexing expressions are promoted, preserving
117117
simultaneous assignment, V layouts, inferred types, element types, and bounds checks. Together these
118118
promotions preserve V's formatting, byte-length, diagnostics, boolean typing, integer-wrapping,
119119
safe-shift, and zero-divisor behavior instead of relying on incompatible raw C semantics.
120120

121-
The direct path is limited to host-target, non-production, non-test, non-shared single-file builds.
122-
Compiler/self-host, strict C, and other non-direct modes enter the complete lane before source
123-
scanning.
121+
The direct path is limited to host-target, non-debug, non-production, non-test, non-shared
122+
single-file builds. Compiler/self-host, strict C, and other non-direct modes enter the complete lane
123+
before source scanning.
124124
`-o file.c` emits the standalone fast C translation unit when the direct lane supports the input;
125125
otherwise it emits the complete `v3.gen.fastc` translation unit. Direct C-only output is published
126126
only after both V semantic checking and TinyCC validation succeed.

vlib/v3/driver/driver.v

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7349,11 +7349,12 @@ pub fn run(args []string) {
73497349
fastc_eligible := input_file.ends_with('.v') && os.is_file(input_file) && file_list.len == 0
73507350
&& target.os == fastc_host.os && target.arch == fastc_host.arch && !is_test_command
73517351
&& !building_v && !is_selfhost && !is_checker_fixture && !is_prod && !is_shared
7352-
&& !is_livemain && !is_liveshared && !is_o && !is_prof && coverage_dir.len == 0
7353-
&& !ownership_mode && !only_check_syntax && !check_only && print_fn_names.len == 0
7354-
&& !print_v_files && !print_watched_files && dump_c_flags.len == 0
7355-
&& generate_c_project.len == 0 && !c99_explicit && !c_compiler_explicit && !no_builtin
7356-
&& !no_preludes && !check_overflow && !translated_mode && !is_repl && !is_strict
7352+
&& !is_livemain && !is_liveshared && !is_o && !is_prof && !is_debug
7353+
&& coverage_dir.len == 0 && !ownership_mode && !only_check_syntax && !check_only
7354+
&& print_fn_names.len == 0 && !print_v_files && !print_watched_files
7355+
&& dump_c_flags.len == 0 && generate_c_project.len == 0 && !c99_explicit
7356+
&& !c_compiler_explicit && !no_builtin && !no_preludes && !check_overflow
7357+
&& !translated_mode && !is_repl && !is_strict
73577358
mut generated_fastc := false
73587359
mut fastc_source := ''
73597360
if fastc_eligible {

vlib/v3/gen/fastc/fastc.v

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -589,18 +589,24 @@ fn (g &DirectGen) expression_token() !string {
589589
}
590590
}
591591

592-
fn fastc_hex_literal_requires_checked_type(literal string) bool {
592+
fn fastc_nondecimal_literal_requires_checked_type(literal string) bool {
593593
clean := literal.replace('_', '')
594-
if clean.len <= 2 || clean[0] != `0` || clean[1] !in [`x`, `X`] {
594+
if clean.len <= 2 || clean[0] != `0` {
595595
return false
596596
}
597597
digits := clean[2..].trim_left('0')
598-
if digits.len > 8 {
599-
return true
598+
if clean[1] in [`x`, `X`] {
599+
if digits.len > 8 {
600+
return true
601+
}
602+
return digits.len == 8 && ((digits[0] >= `8` && digits[0] <= `9`)
603+
|| (digits[0] >= `a` && digits[0] <= `f`)
604+
|| (digits[0] >= `A` && digits[0] <= `F`))
600605
}
601-
return digits.len == 8 && ((digits[0] >= `8` && digits[0] <= `9`)
602-
|| (digits[0] >= `a` && digits[0] <= `f`)
603-
|| (digits[0] >= `A` && digits[0] <= `F`))
606+
if clean[1] in [`b`, `B`] {
607+
return digits.len >= 32
608+
}
609+
return false
604610
}
605611

606612
fn fastc_c_number(literal string) !string {
@@ -611,8 +617,8 @@ fn fastc_c_number(literal string) !string {
611617
// int, so only the checked lane can preserve its inferred type and wrapping.
612618
return error('minimum int literal expressions require checked fastc')
613619
}
614-
if fastc_hex_literal_requires_checked_type(literal) {
615-
return error('high-bit hexadecimal literals require checked fastc')
620+
if fastc_nondecimal_literal_requires_checked_type(literal) {
621+
return error('high-bit nondecimal literals require checked fastc')
616622
}
617623
if clean.len < 2 || clean[0] != `0` || !clean[1].is_digit() || clean.contains_any('.eE') {
618624
return clean

vlib/v3/gen/fastc/fastc_test.v

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ fn test_type_sensitive_expressions_request_checked_lane() {
174174
'module main\nfn main() { for i := -2_147_483_648; true; i-- { println(i); break } }\n',
175175
'module main\nfn main() { mut x := -2_147_483_648 - 1; println(x) }\n',
176176
'module main\nfn main() { x := 0xffff_ffff | 0; println(x) }\n',
177+
'module main\nfn main() { x := 0b11111111111111111111111111111111 | 0; println(x) }\n',
177178
'module main\nfn main() { mut a := 1; mut b := 2; a, b = b, a; println(a); println(b) }\n',
178179
] {
179180
mut failed := false
@@ -191,6 +192,9 @@ fn test_type_sensitive_expressions_request_checked_lane() {
191192
low_hex_c := generate('module main\nfn main() { x := 0x7fff_ffff | 0; println(x) }\n',
192193
'low_hex_literal.v', prefs) or { panic(err) }
193194
assert low_hex_c.contains('__typeof__((0x7fffffff|0)) x = (0x7fffffff|0);')
195+
low_binary_c := generate('module main\nfn main() { x := 0b01111111111111111111111111111111 | 0; println(x) }\n',
196+
'low_binary_literal.v', prefs) or { panic(err) }
197+
assert low_binary_c.contains('__typeof__((0b01111111111111111111111111111111|0))')
194198
max_int_c := generate('module main\nfn main() { x := 2_147_483_647 - 1; println(x) }\n',
195199
'max_int_expression.v', prefs) or { panic(err) }
196200
assert max_int_c.contains('__typeof__((2147483647-1)) x = (2147483647-1);')

vlib/v3/tests/fastc_backend_test.v

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ fn main() {
5555
assert strict_run.exit_code == 0, strict_run.output
5656
assert strict_run.output.trim_space() == '42'
5757

58+
debug_binary := os.join_path(root, 'debug')
59+
debug_compile := cmdexec.run(v3_bin, ['-silent', '-g', '-b', 'fastc', '-o', debug_binary,
60+
valid_source])
61+
assert debug_compile.exit_code == 0, debug_compile.output
62+
debug_c := os.read_file(debug_binary + '.c') or { panic(err) }
63+
assert !debug_c.contains('__typeof__((twice(21))) value = (twice(21));')
64+
assert !debug_c.contains('V_FASTC_PRINT_SELECT')
65+
debug_run := cmdexec.run(debug_binary, [])
66+
assert debug_run.exit_code == 0, debug_run.output
67+
assert debug_run.output.trim_space() == '42'
68+
5869
invalid_source := os.join_path(root, 'invalid.v')
5970
os.write_file(invalid_source, 'module main
6071
@@ -298,6 +309,26 @@ fn main() {
298309
assert high_hex_run.exit_code == 0, high_hex_run.output
299310
assert high_hex_run.output.trim_space() == '-1'
300311

312+
high_binary_source := os.join_path(root, 'inferred_high_binary.v')
313+
os.write_file(high_binary_source, 'module main
314+
315+
fn main() {
316+
x := 0b11111111111111111111111111111111 | 0
317+
println(x)
318+
}
319+
') or {
320+
panic(err)
321+
}
322+
high_binary_binary := os.join_path(root, 'inferred_high_binary')
323+
high_binary_compile := cmdexec.run(v3_bin, ['-silent', '-b', 'fastc', '-o', high_binary_binary,
324+
high_binary_source])
325+
assert high_binary_compile.exit_code == 0, high_binary_compile.output
326+
high_binary_c := os.read_file(high_binary_binary + '.c') or { panic(err) }
327+
assert !high_binary_c.contains('V_FASTC_PRINT_SELECT')
328+
high_binary_run := cmdexec.run(high_binary_binary, [])
329+
assert high_binary_run.exit_code == 0, high_binary_run.output
330+
assert high_binary_run.output.trim_space() == '-1'
331+
301332
parallel_assign_source := os.join_path(root, 'parallel_assign.v')
302333
os.write_file(parallel_assign_source, 'module main
303334

0 commit comments

Comments
 (0)