cgen,vfmt: support `[weak]` tags for functions and globals

pull/12511/head
Delyan Angelov 2021-11-18 18:09:31 +02:00
parent 7fba3e65e9
commit 3caeadfa0d
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
9 changed files with 80 additions and 16 deletions

View File

@ -638,6 +638,7 @@ pub:
mod string
pos token.Position
is_block bool // __global() block
attrs []Attr
pub mut:
fields []GlobalField
end_comments []Comment

View File

@ -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

View File

@ -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')
}

View File

@ -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 <stdnoreturn.h>

View File

@ -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

View File

@ -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
}

View File

@ -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) {

View File

@ -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())
}

View File

@ -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
}
}