cgen: add support for -g again

pull/6112/head
Delyan Angelov 2020-08-12 00:08:19 +03:00
parent 6a8a589adb
commit ca7a64a451
6 changed files with 77 additions and 15 deletions

View File

@ -36,6 +36,7 @@ pub fn (mut b Builder) gen_c(v_files []string) string {
pub fn (mut b Builder) build_c(v_files []string, out_file string) { pub fn (mut b Builder) build_c(v_files []string, out_file string) {
b.out_name_c = out_file b.out_name_c = out_file
b.pref.out_name_c = os.real_path(out_file)
b.info('build_c($out_file)') b.info('build_c($out_file)')
output2 := b.gen_c(v_files) output2 := b.gen_c(v_files)
mut f := os.create(out_file) or { mut f := os.create(out_file) or {

View File

@ -59,6 +59,8 @@ mut:
is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
is_shared bool // for initialization of hidden mutex in `[rw]shared` literals is_shared bool // for initialization of hidden mutex in `[rw]shared` literals
is_vlines_enabled bool // is it safe to generate #line directives when -g is passed
vlines_path string // set to the proper path for generating #line directives
optionals []string // to avoid duplicates TODO perf, use map optionals []string // to avoid duplicates TODO perf, use map
shareds []int // types with hidden mutex for which decl has been emitted shareds []int // types with hidden mutex for which decl has been emitted
inside_ternary int // ?: comma separated statements on a single line inside_ternary int // ?: comma separated statements on a single line
@ -147,6 +149,9 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
mut autofree_used := false mut autofree_used := false
for file in files { for file in files {
g.file = file g.file = file
if g.pref.is_vlines {
g.vlines_path = util.vlines_escape_path(file.path, g.pref.ccompiler)
}
// println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len') // println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len')
// building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v')) // building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v'))
is_test := g.file.path.ends_with('.vv') || g.file.path.ends_with('_test.v') is_test := g.file.path.ends_with('.vv') || g.file.path.ends_with('_test.v')
@ -174,6 +179,8 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
} }
g.write_variadic_types() g.write_variadic_types()
// g.write_str_definitions() // g.write_str_definitions()
// v files are finished, what remains is pure C code
g.gen_vlines_reset()
if g.pref.build_mode != .build_module { if g.pref.build_mode != .build_module {
// no init in builtin.o // no init in builtin.o
g.write_init_function() g.write_init_function()
@ -235,7 +242,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
return b.str() return b.str()
} }
pub fn (g Gen) hashes() string { pub fn (g &Gen) hashes() string {
mut res := c_commit_hash_default.replace('@@@', util.vhash()) mut res := c_commit_hash_default.replace('@@@', util.vhash())
res += c_current_commit_hash_default.replace('@@@', util.githash(g.pref.building_v)) res += c_current_commit_hash_default.replace('@@@', util.githash(g.pref.building_v))
return res return res
@ -563,9 +570,6 @@ pub fn (mut g Gen) write_variadic_types() {
} }
} }
pub fn (g Gen) save() {
}
pub fn (mut g Gen) write(s string) { pub fn (mut g Gen) write(s string) {
$if trace_gen ? { $if trace_gen ? {
eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | write: $s') eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | write: $s')
@ -648,6 +652,15 @@ fn (mut g Gen) stmts(stmts []ast.Stmt) {
} }
} }
[inline]
fn (mut g Gen) write_v_source_line_info(pos token.Position) {
if g.inside_ternary == 0 && g.pref.is_vlines && g.is_vlines_enabled {
nline := pos.line_nr + 1
lineinfo := '\n#line $nline "$g.vlines_path"'
g.writeln(lineinfo)
}
}
fn (mut g Gen) stmt(node ast.Stmt) { fn (mut g Gen) stmt(node ast.Stmt) {
g.stmt_path_pos << g.out.len g.stmt_path_pos << g.out.len
defer { defer {
@ -664,9 +677,11 @@ fn (mut g Gen) stmt(node ast.Stmt) {
// g.writeln('//// stmt start') // g.writeln('//// stmt start')
match node { match node {
ast.AssertStmt { ast.AssertStmt {
g.write_v_source_line_info(node.pos)
g.gen_assert_stmt(node) g.gen_assert_stmt(node)
} }
ast.AssignStmt { ast.AssignStmt {
g.write_v_source_line_info(node.pos)
g.gen_assign_stmt(node) g.gen_assign_stmt(node)
} }
ast.Block { ast.Block {
@ -679,11 +694,13 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.writeln('}') g.writeln('}')
} }
ast.BranchStmt { ast.BranchStmt {
g.write_v_source_line_info(node.tok.position())
// continue or break // continue or break
g.write(node.tok.kind.str()) g.write(node.tok.kind.str())
g.writeln(';') g.writeln(';')
} }
ast.ConstDecl { ast.ConstDecl {
g.write_v_source_line_info(node.pos)
// if g.pref.build_mode != .build_module { // if g.pref.build_mode != .build_module {
g.const_decl(node) g.const_decl(node)
// } // }
@ -692,6 +709,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.comp_for(node) g.comp_for(node)
} }
ast.CompIf { ast.CompIf {
g.write_v_source_line_info(node.pos)
g.comp_if(node) g.comp_if(node)
} }
ast.DeferStmt { ast.DeferStmt {
@ -723,6 +741,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.enum_typedefs.writeln('} $enum_name;\n') g.enum_typedefs.writeln('} $enum_name;\n')
} }
ast.ExprStmt { ast.ExprStmt {
g.write_v_source_line_info(node.pos)
g.expr(node.expr) g.expr(node.expr)
if g.inside_ternary == 0 && !node.is_expr && !(node.expr is ast.IfExpr) { if g.inside_ternary == 0 && !node.is_expr && !(node.expr is ast.IfExpr) {
g.writeln(';') g.writeln(';')
@ -779,6 +798,8 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} }
} }
ast.ForCStmt { ast.ForCStmt {
g.write_v_source_line_info(node.pos)
g.is_vlines_enabled = false
g.write('for (') g.write('for (')
if !node.has_init { if !node.has_init {
g.write('; ') g.write('; ')
@ -799,13 +820,17 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.stmt(node.inc) g.stmt(node.inc)
} }
g.writeln(') {') g.writeln(') {')
g.is_vlines_enabled = true
g.stmts(node.stmts) g.stmts(node.stmts)
g.writeln('}') g.writeln('}')
} }
ast.ForInStmt { ast.ForInStmt {
g.write_v_source_line_info(node.pos)
g.for_in(node) g.for_in(node)
} }
ast.ForStmt { ast.ForStmt {
g.write_v_source_line_info(node.pos)
g.is_vlines_enabled = false
g.write('while (') g.write('while (')
if node.is_inf { if node.is_inf {
g.write('1') g.write('1')
@ -813,6 +838,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.expr(node.cond) g.expr(node.cond)
} }
g.writeln(') {') g.writeln(') {')
g.is_vlines_enabled = true
g.stmts(node.stmts) g.stmts(node.stmts)
g.writeln('}') g.writeln('}')
} }
@ -2784,7 +2810,7 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
} }
[inline] [inline]
fn (g Gen) expr_is_multi_return_call(expr ast.Expr) bool { fn (g &Gen) expr_is_multi_return_call(expr ast.Expr) bool {
match expr { match expr {
ast.CallExpr { return g.table.get_type_symbol(expr.return_type).kind == .multi_return } ast.CallExpr { return g.table.get_type_symbol(expr.return_type).kind == .multi_return }
else { return false } else { return false }
@ -2792,6 +2818,7 @@ fn (g Gen) expr_is_multi_return_call(expr ast.Expr) bool {
} }
fn (mut g Gen) return_statement(node ast.Return) { fn (mut g Gen) return_statement(node ast.Return) {
g.write_v_source_line_info(node.pos)
if node.exprs.len > 0 { if node.exprs.len > 0 {
// skip `retun $vweb.html()` // skip `retun $vweb.html()`
if node.exprs[0] is ast.ComptimeCall { if node.exprs[0] is ast.ComptimeCall {
@ -3525,7 +3552,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) {
} }
// sort structs by dependant fields // sort structs by dependant fields
fn (g Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol { fn (g &Gen) sort_structs(typesa []table.TypeSymbol) []table.TypeSymbol {
mut dep_graph := depgraph.new_dep_graph() mut dep_graph := depgraph.new_dep_graph()
// types name list // types name list
mut type_names := []string{} mut type_names := []string{}
@ -4168,7 +4195,7 @@ fn (mut g Gen) type_default(typ table.Type) string {
*/ */
} }
fn (g Gen) get_all_test_function_names() []string { fn (g &Gen) get_all_test_function_names() []string {
mut tfuncs := []string{} mut tfuncs := []string{}
mut tsuite_begin := '' mut tsuite_begin := ''
mut tsuite_end := '' mut tsuite_end := ''
@ -4215,7 +4242,7 @@ fn (g Gen) get_all_test_function_names() []string {
return all_tfuncs_c return all_tfuncs_c
} }
fn (g Gen) is_importing_os() bool { fn (g &Gen) is_importing_os() bool {
return 'os' in g.table.imports return 'os' in g.table.imports
} }
@ -4385,7 +4412,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) {
g.auto_str_funcs.writeln('}') g.auto_str_funcs.writeln('}')
} }
fn (g Gen) type_to_fmt(typ table.Type) string { fn (g &Gen) type_to_fmt(typ table.Type) string {
sym := g.table.get_type_symbol(typ) sym := g.table.get_type_symbol(typ)
if sym.kind in [.struct_, .array, .array_fixed, .map] { if sym.kind in [.struct_, .array, .array_fixed, .map] {
return '%.*s\\000' return '%.*s\\000'

View File

@ -1,5 +1,7 @@
module gen module gen
import v.util
pub fn (mut g Gen) gen_c_main() { pub fn (mut g Gen) gen_c_main() {
if !g.has_main { if !g.has_main {
return return
@ -7,16 +9,33 @@ pub fn (mut g Gen) gen_c_main() {
if g.pref.is_liveshared { if g.pref.is_liveshared {
return return
} }
g.out.writeln('') g.out.writeln('')
main_fn_start_pos := g.out.len main_fn_start_pos := g.out.len
g.gen_c_main_header() g.gen_c_main_header()
g.writeln('\tmain__main();') g.writeln('\tmain__main();')
g.gen_c_main_footer() g.gen_c_main_footer()
if g.pref.printfn_list.len > 0 && 'main' in g.pref.printfn_list { if g.pref.printfn_list.len > 0 && 'main' in g.pref.printfn_list {
println(g.out.after(main_fn_start_pos)) println(g.out.after(main_fn_start_pos))
} }
} }
fn (mut g Gen) gen_vlines_reset() {
if g.pref.is_vlines {
// At this point, the v files are transpiled.
// The rest is auto generated code, which will not have
// different .v source file/line numbers.
//
// TODO: calculate the proper line here, based on
// the actual C lines in all the buffers
lines_so_far := 1000000
g.vlines_path = util.vlines_escape_path(g.pref.out_name_c, g.pref.ccompiler)
g.writeln('')
g.writeln('\n// Reset the file/line numbers')
g.writeln('\n#line $lines_so_far "${g.vlines_path}"')
g.writeln('')
}
}
fn (mut g Gen) gen_c_main_header() { fn (mut g Gen) gen_c_main_header() {
if g.pref.os == .windows { if g.pref.os == .windows {
if g.is_gui_app() { if g.is_gui_app() {
@ -37,13 +56,11 @@ fn (mut g Gen) gen_c_main_header() {
g.writeln('\twchar_t** ___argv = CommandLineToArgvW(cmd_line, &___argc);') g.writeln('\twchar_t** ___argv = CommandLineToArgvW(cmd_line, &___argc);')
} }
g.writeln('\t_vinit();') g.writeln('\t_vinit();')
if g.pref.is_prof { if g.pref.is_prof {
g.writeln('') g.writeln('')
g.writeln('\tatexit(vprint_profile_stats);') g.writeln('\tatexit(vprint_profile_stats);')
g.writeln('') g.writeln('')
} }
if g.is_importing_os() { if g.is_importing_os() {
if g.autofree { if g.autofree {
g.writeln('free(_const_os__args.data); // empty, inited in _vinit()') g.writeln('free(_const_os__args.data); // empty, inited in _vinit()')
@ -63,7 +80,6 @@ pub fn (mut g Gen) gen_c_main_footer() {
if g.autofree { if g.autofree {
g.writeln('\t_vcleanup();') g.writeln('\t_vcleanup();')
} }
g.writeln('\treturn 0;') g.writeln('\treturn 0;')
g.writeln('}') g.writeln('}')
} }

View File

@ -36,6 +36,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) {
} }
// g.cur_fn = it // g.cur_fn = it
fn_start_pos := g.out.len fn_start_pos := g.out.len
g.write_v_source_line_info(it.pos)
msvc_attrs := g.write_fn_attrs(it.attrs) msvc_attrs := g.write_fn_attrs(it.attrs)
// Live // Live
is_livefn := it.attrs.contains('live') is_livefn := it.attrs.contains('live')

View File

@ -70,7 +70,7 @@ pub mut:
is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler. is_debug bool // false by default, turned on by -g or -cg, it tells v to pass -g to the C backend compiler.
is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly). is_vlines bool // turned on by -g, false by default (it slows down .tmp.c generation slightly).
show_cc bool // -showcc, print cc command show_cc bool // -showcc, print cc command
// NB: passing -cg instead of -g will set is_vlines to false and is_g to true, thus making v generate cleaner C files, // NB: passing -cg instead of -g will set is_vlines to false and is_debug to true, thus making v generate cleaner C files,
// which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks). // which are sometimes easier to debug / inspect manually than the .tmp.c files by plain -g (when/if v line number generation breaks).
use_cache bool // turns on v usage of the module cache to speed up compilation. use_cache bool // turns on v usage of the module cache to speed up compilation.
is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run is_stats bool // `v -stats file_test.v` will produce more detailed statistics for the tests that were run
@ -101,6 +101,7 @@ pub mut:
output_cross_c bool output_cross_c bool
prealloc bool prealloc bool
vroot string vroot string
out_name_c string // full os.real_path to the generated .tmp.c file; set by builder.
out_name string out_name string
display_name string display_name string
bundle_id string bundle_id string
@ -144,8 +145,13 @@ pub fn parse_args(args []string) (&Preferences, string) {
'-silent' { '-silent' {
res.output_mode = .silent res.output_mode = .silent
} }
'-g' {
res.is_debug = true
res.is_vlines = true
}
'-cg' { '-cg' {
res.is_debug = true res.is_debug = true
res.is_vlines = false
} }
'-repl' { '-repl' {
res.is_repl = true res.is_repl = true

View File

@ -141,3 +141,14 @@ pub fn verror(kind, s string) {
eprintln('$final_kind: $s') eprintln('$final_kind: $s')
exit(1) exit(1)
} }
pub fn vlines_escape_path(path string, ccompiler string) string {
is_cc_tcc := ccompiler.contains('tcc')
if is_cc_tcc {
// tcc currently has a bug, causing all #line files,
// to be prefixed with the *same folder as the .tmp.c file*
// this ../../ escaping, is a temporary workaround for that
return '../../../../../..' + cescaped_path(os.real_path(path))
}
return cescaped_path(os.real_path(path))
}