From 3caeadfa0d0b2ff3b85cf48ab827be817e59309d Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 18 Nov 2021 18:09:31 +0200 Subject: [PATCH] cgen,vfmt: support `[weak]` tags for functions and globals --- vlib/v/ast/ast.v | 1 + vlib/v/fmt/fmt.v | 1 + vlib/v/gen/c/cgen.v | 10 +++++--- vlib/v/gen/c/cheaders.v | 9 +++++++ vlib/v/gen/c/coutput_test.v | 12 +++++++-- vlib/v/gen/c/fn.v | 23 +++++++++-------- .../globals_with_weak_tag.c.must_have | 9 +++++++ .../v/gen/c/testdata/globals_with_weak_tag.vv | 25 +++++++++++++++++++ vlib/v/parser/parser.v | 6 +++++ 9 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 vlib/v/gen/c/testdata/globals_with_weak_tag.c.must_have create mode 100644 vlib/v/gen/c/testdata/globals_with_weak_tag.vv diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index d99d75fa6e..f2507e9a1d 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -638,6 +638,7 @@ pub: mod string pos token.Position is_block bool // __global() block + attrs []Attr pub mut: fields []GlobalField end_comments []Comment diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 8940ebe049..27833df5ac 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1002,6 +1002,7 @@ pub fn (mut f Fmt) for_stmt(node ast.ForStmt) { } pub fn (mut f Fmt) global_decl(node ast.GlobalDecl) { + f.attrs(node.attrs) if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line { f.writeln('__global ()') return diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 7e8599467d..7072438c8d 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5853,6 +5853,10 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ fn (mut g Gen) global_decl(node ast.GlobalDecl) { mod := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' } else { '' } + mut attributes := '' + if node.attrs.contains('weak') { + attributes += 'VWEAK ' + } for field in node.fields { if g.pref.skip_unused { if field.name !in g.table.used_globals { @@ -5864,7 +5868,7 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) { } styp := g.typ(field.typ) if field.has_expr { - g.definitions.write_string('$mod$styp $field.name') + g.definitions.write_string('$mod$styp $attributes $field.name') if field.expr.is_literal() { g.definitions.writeln(' = ${g.expr_string(field.expr)}; // global') } else { @@ -5874,9 +5878,9 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) { } else { default_initializer := g.type_default(field.typ) if default_initializer == '{0}' { - g.definitions.writeln('$mod$styp $field.name = {0}; // global') + g.definitions.writeln('$mod$styp $attributes $field.name = {0}; // global') } else { - g.definitions.writeln('$mod$styp $field.name; // global') + g.definitions.writeln('$mod$styp $attributes $field.name; // global') if field.name !in ['as_cast_type_indexes', 'g_memory_block'] { g.global_init.writeln('\t$field.name = *($styp*)&(($styp[]){${g.type_default(field.typ)}}[0]); // global') } diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index f9e568a08b..b723a702a2 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -254,6 +254,15 @@ const c_common_macros = ' #undef __has_include #endif + +#if !defined(VWEAK) + #define VWEAK __attribute__((weak)) + #ifdef _MSC_VER + #undef VWEAK + #define VWEAK + #endif +#endif + #if !defined(VNORETURN) #if defined(__TINYC__) #include diff --git a/vlib/v/gen/c/coutput_test.v b/vlib/v/gen/c/coutput_test.v index a2d558b8ad..59df1aae39 100644 --- a/vlib/v/gen/c/coutput_test.v +++ b/vlib/v/gen/c/coutput_test.v @@ -105,19 +105,22 @@ fn test_c_must_have_files() ? { alloptions := '-o - $file_options.vflags' print(mm('v $alloptions $relpath') + ' matches all line patterns in ${mm(must_have_relpath)} ') - compilation := os.execute('$vexe $alloptions $path') + cmd := '$vexe $alloptions $path' + compilation := os.execute(cmd) ensure_compilation_succeeded(compilation) expected_lines := os.read_lines(must_have_path) or { [] } generated_c_lines := compilation.output.split_into_lines() mut nmatches := 0 + mut failed_patterns := []string{} for idx_expected_line, eline in expected_lines { if does_line_match_one_of_generated_lines(eline, generated_c_lines) { nmatches++ // eprintln('> testing: $must_have_path has line: $eline') } else { + failed_patterns << eline println(term.red('FAIL')) eprintln('$must_have_path:${idx_expected_line + 1}: expected match error:') - eprintln('`$vexe -o - $path` does NOT produce expected line:') + eprintln('`$cmd` did NOT produce expected line:') eprintln(term.colorize(term.red, eline)) total_errors++ continue @@ -128,6 +131,11 @@ fn test_c_must_have_files() ? { } else { eprintln('> ALL lines:') eprintln(compilation.output) + eprintln('--------- failed patterns: -------------------------------------------') + for fpattern in failed_patterns { + eprintln(fpattern) + } + eprintln('----------------------------------------------------------------------') } } assert total_errors == 0 diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 3587683a1f..b6c476dcd1 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -187,7 +187,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { } g.write_v_source_line_info(node.pos) - msvc_attrs := g.write_fn_attrs(node.attrs) + fn_attrs := g.write_fn_attrs(node.attrs) // Live is_livefn := node.attrs.contains('live') is_livemain := g.pref.is_livemain && is_livefn @@ -291,11 +291,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { g.definitions.write_string('VV_LOCAL_SYMBOL ') } } - fn_header := if msvc_attrs.len > 0 { - '$type_name $msvc_attrs ${name}(' - } else { - '$type_name ${name}(' - } + fn_header := '$type_name $fn_attrs${name}(' g.definitions.write_string(fn_header) g.write(fn_header) } @@ -426,8 +422,7 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { for attr in node.attrs { if attr.name == 'export' { g.writeln('// export alias: $attr.arg -> $name') - calling_conv := if msvc_attrs.len > 0 { '$msvc_attrs ' } else { '' } - export_alias := '$type_name $calling_conv${attr.arg}($arg_str)' + export_alias := '$type_name $fn_attrs${attr.arg}($arg_str)' g.definitions.writeln('VV_EXPORTED_SYMBOL $export_alias; // exported fn $node.name') g.writeln('$export_alias {') g.write('\treturn ${name}(') @@ -1565,7 +1560,7 @@ fn (g &Gen) fileis(s string) bool { } fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string { - mut msvc_attrs := '' + mut fn_attrs := '' for attr in attrs { match attr.name { 'inline' { @@ -1575,6 +1570,12 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string { // since these are supported by GCC, clang and MSVC, we can consider them officially supported. g.write('__NOINLINE ') } + 'weak' { + // a `[weak]` tag tells the C compiler, that the next declaration will be weak, i.e. when linking, + // if there is another declaration of a symbol with the same name (a 'strong' one), it should be + // used instead, *without linker errors about duplicate symbols*. + g.write('VWEAK ') + } 'noreturn' { // a `[noreturn]` tag tells the compiler, that a function // *DOES NOT RETURN* to its callsites. @@ -1641,7 +1642,7 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string { 'windows_stdcall' { // windows attributes (msvc/mingw) // prefixed by windows to indicate they're for advanced users only and not really supported by V. - msvc_attrs += '__stdcall ' + fn_attrs += '__stdcall ' } 'console' { g.force_main_console = true @@ -1651,5 +1652,5 @@ fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string { } } } - return msvc_attrs + return fn_attrs } diff --git a/vlib/v/gen/c/testdata/globals_with_weak_tag.c.must_have b/vlib/v/gen/c/testdata/globals_with_weak_tag.c.must_have new file mode 100644 index 0000000000..0fba06806e --- /dev/null +++ b/vlib/v/gen/c/testdata/globals_with_weak_tag.c.must_have @@ -0,0 +1,9 @@ +u64 VWEAK abc = ((u64)(1U)); // global +u64 xyz = ((u64)(2U)); // global +u64 VWEAK weak_1 = ((u64)(4U)); // global +u64 VWEAK weak_2 = ((u64)(5U)); // global +VV_LOCAL_SYMBOL int main__a_weak_function(void); +VV_LOCAL_SYMBOL void main__main(void); + +VWEAK VV_LOCAL_SYMBOL int main__a_weak_function(void) { + diff --git a/vlib/v/gen/c/testdata/globals_with_weak_tag.vv b/vlib/v/gen/c/testdata/globals_with_weak_tag.vv new file mode 100644 index 0000000000..5ff79b2882 --- /dev/null +++ b/vlib/v/gen/c/testdata/globals_with_weak_tag.vv @@ -0,0 +1,25 @@ +// vtest vflags: -enable-globals + +[weak] +__global abc = u64(1) +__global xyz = u64(2) + +[weak] +__global ( + weak_1 = u64(4) + weak_2 = u64(5) +) + +[weak] +fn a_weak_function() int { + return 42 +} + +fn main() { + println('abc: $abc') + println('xyz: $xyz') + println('xyz: $xyz') + println('weak_1: $weak_1') + println('weak_2: $weak_2') + println(a_weak_function()) +} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index dad416ad78..727ac76234 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -3081,6 +3081,11 @@ fn (mut p Parser) return_stmt() ast.Return { // left hand side of `=` or `:=` in `a,b,c := 1,2,3` fn (mut p Parser) global_decl() ast.GlobalDecl { + mut attrs := []ast.Attr{} + if p.attrs.len > 0 { + attrs = p.attrs + p.attrs = [] + } if !p.has_globals && !p.pref.enable_globals && !p.pref.is_fmt && !p.pref.translated && !p.pref.is_livemain && !p.pref.building_v && !p.builtin_mod { p.error('use `v -enable-globals ...` to enable globals') @@ -3173,6 +3178,7 @@ fn (mut p Parser) global_decl() ast.GlobalDecl { fields: fields end_comments: comments is_block: is_block + attrs: attrs } }