From 2b27072facfde41dfa297bd449b6a815b65488b9 Mon Sep 17 00:00:00 2001 From: Julia K Date: Tue, 26 May 2020 14:12:18 +0200 Subject: [PATCH] compiler: new attributes, prelude customization --- cmd/v/help/build-c.txt | 8 +++++ vlib/v/gen/cgen.v | 22 +++++++++----- vlib/v/gen/cheaders.v | 13 ++++++++- vlib/v/gen/fn.v | 66 ++++++++++++++++++++++++++++++++++++++++-- vlib/v/pref/pref.v | 24 +++++++++++---- 5 files changed, 118 insertions(+), 15 deletions(-) diff --git a/cmd/v/help/build-c.txt b/cmd/v/help/build-c.txt index 2a87203ab3..92b26b5656 100644 --- a/cmd/v/help/build-c.txt +++ b/cmd/v/help/build-c.txt @@ -62,3 +62,11 @@ These build flags are enabled on `build` and `run` as long as the backend is set -shared Tell V to compile a shared object instead of an executable. The resulting file extension will be `.dll` on Windows and `.so` on Unix systems + + -no-prelude + Prevents V from generating a prelude in generated .c files, useful for freestanding targets + where eg. you replace C standard library with your own, or some definitions/headers break something. + + -custom-prelude + Useful for similar use-case as above option, except it replaces V-generated prelude with + your custom one loaded from specified . diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 9ecc132392..b7ae93543b 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -212,13 +212,21 @@ pub fn (g Gen) hashes() string { } pub fn (mut g Gen) init() { - g.cheaders.writeln('// Generated by the V compiler') - g.cheaders.writeln('#include ') // int64_t etc - g.cheaders.writeln(c_builtin_types) - g.cheaders.writeln(c_headers) - g.definitions.writeln('\nvoid _STR_PRINT_ARG(const char*, char**, int*, int*, int, ...);\n') - g.definitions.writeln('\nstring _STR(const char*, int, ...);\n') - g.definitions.writeln('\nstring _STR_TMP(const char*, ...);\n') + if g.pref.custom_prelude != '' { + g.cheaders.writeln(g.pref.custom_prelude) + } else if !g.pref.no_preludes { + g.cheaders.writeln('// Generated by the V compiler') + g.cheaders.writeln('#include ') // int64_t etc + g.cheaders.writeln(c_builtin_types) + if g.pref.is_bare { + g.cheaders.writeln(bare_c_headers) + } else { + g.cheaders.writeln(c_headers) + } + g.definitions.writeln('\nvoid _STR_PRINT_ARG(const char*, char**, int*, int*, int, ...);\n') + g.definitions.writeln('\nstring _STR(const char*, int, ...);\n') + g.definitions.writeln('\nstring _STR_TMP(const char*, ...);\n') + } g.write_builtin_types() g.write_typedef_types() g.write_typeof_functions() diff --git a/vlib/v/gen/cheaders.v b/vlib/v/gen/cheaders.v index 82b26b1a2b..15983d1c6e 100644 --- a/vlib/v/gen/cheaders.v +++ b/vlib/v/gen/cheaders.v @@ -27,6 +27,9 @@ const ( #define EMPTY_ARRAY_OF_ELEMS(x,n) (x[]) #define TCCSKIP(x) x +#define __NOINLINE __attribute__((noinline)) +#define __IRQHANDLER __attribute__((interrupt)) + #ifdef __TINYC__ #undef EMPTY_STRUCT_DECLARATION #undef EMPTY_STRUCT_INITIALIZATION @@ -34,6 +37,11 @@ const ( #define EMPTY_STRUCT_INITIALIZATION 0 #undef EMPTY_ARRAY_OF_ELEMS #define EMPTY_ARRAY_OF_ELEMS(x,n) (x[n]) +#undef __NOINLINE +#undef __IRQHANDLER +// tcc does not support inlining at all +#define __NOINLINE +#define __IRQHANDLER #undef TCCSKIP #define TCCSKIP(x) #include @@ -178,12 +186,15 @@ $c_common_macros #define EMPTY_STRUCT_DECLARATION int ____dummy_variable #define OPTION_CAST(x) +#undef __NOINLINE +#undef __IRQHANDLER +#define __NOINLINE __declspec(noinline) +#define __IRQHANDLER __declspec(naked) #include #pragma comment(lib, "Dbghelp.lib") extern wchar_t **_wenviron; - #endif #else diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index a3c6da53e8..396e15f21c 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -37,9 +37,71 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { } // fn_start_pos := g.out.len - if g.attr == 'inline' { - g.write('inline ') + match g.attr { + 'inline' { + g.write('inline ') + } + // since these are supported by GCC, clang and MSVC, we can consider them officially supported. + 'no_inline' { + g.write('__NOINLINE') + } + 'irq_handler' { + g.write('__IRQHANDLER ') + } + + // GCC/clang attributes + // prefixed by _ to indicate they're for advanced users only and not really supported by V. + // source for descriptions: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes + + // The cold attribute on functions is used to inform the compiler that the function is unlikely + // to be executed. The function is optimized for size rather than speed and on many targets it + // is placed into a special subsection of the text section so all cold functions appear close + // together, improving code locality of non-cold parts of program. + '_cold' { + g.write('__attribute__((cold))') + } + // The constructor attribute causes the function to be called automatically before execution + // enters main (). + '_constructor' { + g.write('__attribute__((constructor))') + } + // The destructor attribute causes the function to be called automatically after main () + // completes or exit () is called. + '_destructor' { + g.write('__attribute__((destructor))') + } + // Generally, inlining into a function is limited. For a function marked with this attribute, + // every call inside this function is inlined, if possible. + '_flatten' { + g.write('__attribute__((flatten))') + } + // The hot attribute on a function is used to inform the compiler that the function is a hot + // spot of the compiled program. + '_hot' { + g.write('__attribute__((hot))') + } + // This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by + // the function cannot alias any other pointer valid when the function returns, and moreover no + // pointers to valid objects occur in any storage addressed by P. + '_malloc' { + g.write('__attribute__((malloc))') + } + + // Calls to functions whose return value is not affected by changes to the observable state + // of the program and that have no observable effects on such state other than to return a + // value may lend themselves to optimizations such as common subexpression elimination. + // Declaring such functions with the const attribute allows GCC to avoid emitting some calls in + // repeated invocations of the function with the same argument values. + '_pure' { + g.write('__attribute__((const)) ') + } + else { + // nothing but keep V happy + } } + + // todo: Clang/GCC only + // is_livefn := g.attr == 'live' is_livemain := g.pref.is_livemain && is_livefn diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 5175fc7abd..973c9139e5 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -90,6 +90,8 @@ pub mut: enable_globals bool // allow __global for low level code is_fmt bool is_bare bool + no_preludes bool // Prevents V from generating preludes in resulting .c files + custom_prelude string // Contents of custom V prelude that will be prepended before code in resulting .c files lookup_path []string output_cross_c bool prealloc bool @@ -143,7 +145,7 @@ pub fn parse_args(args []string) (&Preferences, string) { '-shared' { res.is_shared = true } - '--enable-globals' { + '--enable-globals', '-enable-globals' { res.enable_globals = true } '-autofree' { @@ -155,6 +157,9 @@ pub fn parse_args(args []string) (&Preferences, string) { '-freestanding' { res.is_bare = true } + '-no-preludes' { + res.no_preludes = true + } '-prof', '-profile' { res.profile_file = cmdline.option(current_args, '-profile', '-') res.is_prof = true @@ -176,10 +181,10 @@ pub fn parse_args(args []string) (&Preferences, string) { res.translated = true } '-color' { - res.use_color=.always + res.use_color = .always } '-nocolor' { - res.use_color=.never + res.use_color = .never } '-showcc' { res.show_cc = true @@ -203,7 +208,7 @@ pub fn parse_args(args []string) (&Preferences, string) { res.print_v_files = true } '-error-limit' { - res.error_limit =cmdline.option(current_args, '-error-limit', '0').int() + res.error_limit = cmdline.option(current_args, '-error-limit', '0').int() } '-os' { target_os := cmdline.option(current_args, '-os', '') @@ -213,7 +218,7 @@ pub fn parse_args(args []string) (&Preferences, string) { res.output_cross_c = true continue } - println('unknown operating system target `$target_os`') + eprintln('unknown operating system target `$target_os`') exit(1) } res.os = target_os_kind @@ -253,6 +258,15 @@ pub fn parse_args(args []string) (&Preferences, string) { res.lookup_path = path.split(os.path_delimiter) i++ } + '-custom-prelude' { + path := cmdline.option(current_args, '-custom-prelude', '') + prelude := os.read_file(path) or { + eprintln('cannot open custom prelude file: $err') + exit(1) + } + res.custom_prelude = prelude + i++ + } else { mut should_continue := false for flag_with_param in list_of_flags_with_param {