| /* Subroutines for log output for Atmel AVR back end. |
| Copyright (C) 2011-2014 Free Software Foundation, Inc. |
| Contributed by Georg-Johann Lay (avr@gjlay.de) |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "rtl.h" |
| #include "tree.h" |
| #include "print-tree.h" |
| #include "output.h" |
| #include "input.h" |
| #include "function.h" |
| #include "tm_p.h" |
| #include "tree-pass.h" /* for current_pass */ |
| |
| /* This file supplies some functions for AVR back-end developers |
| with a printf-like interface. The functions are called through |
| macros avr_edump or avr_fdump from avr-protos.h: |
| |
| avr_edump (const char *fmt, ...); |
| |
| avr_fdump (FILE *stream, const char *fmt, ...); |
| |
| avr_edump (fmt, ...) is a shortcut for avr_fdump (stderr, fmt, ...) |
| |
| == known %-codes == |
| |
| b: bool |
| r: rtx |
| t: tree |
| T: tree (brief) |
| C: enum rtx_code |
| m: enum machine_mode |
| R: enum reg_class |
| L: insn list |
| H: location_t |
| |
| == no arguments == |
| |
| A: call abort() |
| f: current_function_name() |
| F: caller (via __FUNCTION__) |
| P: Pass name and number |
| ?: Print caller, current function and pass info |
| !: Ditto, but only print if in a pass with static pass number, |
| else return. |
| |
| == same as printf == |
| |
| %: % |
| c: char |
| s: string |
| d: int (decimal) |
| x: int (hex) |
| */ |
| |
| /* Set according to -mlog= option. */ |
| avr_log_t avr_log; |
| |
| /* The caller as of __FUNCTION__ */ |
| static const char *avr_log_caller = "?"; |
| |
| /* The worker function implementing the %-codes */ |
| static void avr_log_vadump (FILE*, const char*, va_list); |
| |
| /* As we have no variadic macros, avr_edump maps to a call to |
| avr_log_set_caller_e which saves __FUNCTION__ to avr_log_caller and |
| returns a function pointer to avr_log_fdump_e. avr_log_fdump_e |
| gets the printf-like arguments and calls avr_log_vadump, the |
| worker function. avr_fdump works the same way. */ |
| |
| /* Provide avr_log_fdump_e/f so that avr_log_set_caller_e/_f can return |
| their address. */ |
| |
| static int |
| avr_log_fdump_e (const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start (ap, fmt); |
| avr_log_vadump (stderr, fmt, ap); |
| va_end (ap); |
| |
| return 1; |
| } |
| |
| static int |
| avr_log_fdump_f (FILE *stream, const char *fmt, ...) |
| { |
| va_list ap; |
| |
| va_start (ap, fmt); |
| if (stream) |
| avr_log_vadump (stream, fmt, ap); |
| va_end (ap); |
| |
| return 1; |
| } |
| |
| /* Macros avr_edump/avr_fdump map to calls of the following two functions, |
| respectively. You don't need to call them directly. */ |
| |
| int (* |
| avr_log_set_caller_e (const char *caller) |
| )(const char*, ...) |
| { |
| avr_log_caller = caller; |
| |
| return avr_log_fdump_e; |
| } |
| |
| int (* |
| avr_log_set_caller_f (const char *caller) |
| )(FILE*, const char*, ...) |
| { |
| avr_log_caller = caller; |
| |
| return avr_log_fdump_f; |
| } |
| |
| |
| /* Worker function implementing the %-codes and forwarding to |
| respective print/dump function. */ |
| |
| static void |
| avr_log_vadump (FILE *file, const char *fmt, va_list ap) |
| { |
| char bs[3] = {'\\', '?', '\0'}; |
| |
| while (*fmt) |
| { |
| switch (*fmt++) |
| { |
| default: |
| fputc (*(fmt-1), file); |
| break; |
| |
| case '\\': |
| bs[1] = *fmt++; |
| fputs (bs, file); |
| break; |
| |
| case '%': |
| switch (*fmt++) |
| { |
| case '%': |
| fputc ('%', file); |
| break; |
| |
| case 't': |
| { |
| tree t = va_arg (ap, tree); |
| if (NULL_TREE == t) |
| fprintf (file, "<NULL-TREE>"); |
| else |
| { |
| if (stderr == file) |
| debug_tree (t); |
| else |
| { |
| print_node (file, "", t, 0); |
| putc ('\n', file); |
| } |
| } |
| break; |
| } |
| |
| case 'T': |
| print_node_brief (file, "", va_arg (ap, tree), 3); |
| break; |
| |
| case 'd': |
| fprintf (file, "%d", va_arg (ap, int)); |
| break; |
| |
| case 'x': |
| fprintf (file, "%x", va_arg (ap, int)); |
| break; |
| |
| case 'b': |
| fprintf (file, "%s", va_arg (ap, int) ? "true" : "false"); |
| break; |
| |
| case 'c': |
| fputc (va_arg (ap, int), file); |
| break; |
| |
| case 'r': |
| print_inline_rtx (file, va_arg (ap, rtx), 0); |
| break; |
| |
| case 'L': |
| { |
| rtx insn = va_arg (ap, rtx); |
| |
| while (insn) |
| { |
| print_inline_rtx (file, insn, 0); |
| fprintf (file, "\n"); |
| insn = NEXT_INSN (insn); |
| } |
| break; |
| } |
| |
| case 'f': |
| if (cfun && cfun->decl) |
| fputs (current_function_name(), file); |
| break; |
| |
| case 's': |
| { |
| const char *str = va_arg (ap, char*); |
| fputs (str ? str : "(null)", file); |
| } |
| break; |
| |
| case 'm': |
| fputs (GET_MODE_NAME ((enum machine_mode) va_arg (ap, int)), |
| file); |
| break; |
| |
| case 'C': |
| fputs (rtx_name[va_arg (ap, int)], file); |
| break; |
| |
| case 'R': |
| fputs (reg_class_names[va_arg (ap, int)], file); |
| break; |
| |
| case 'F': |
| fputs (avr_log_caller, file); |
| break; |
| |
| case 'H': |
| { |
| location_t loc = va_arg (ap, location_t); |
| |
| if (BUILTINS_LOCATION == loc) |
| fprintf (file, "<BUILTIN-LOCATION>"); |
| else if (UNKNOWN_LOCATION == loc) |
| fprintf (file, "<UNKNOWN-LOCATION>"); |
| else |
| fprintf (file, "%s:%d", |
| LOCATION_FILE (loc), LOCATION_LINE (loc)); |
| |
| break; |
| } |
| |
| case '!': |
| if (!current_pass) |
| return; |
| /* FALLTHRU */ |
| |
| case '?': |
| avr_log_fdump_f (file, "%F[%f:%P]"); |
| break; |
| |
| case 'P': |
| if (current_pass) |
| fprintf (file, "%s(%d)", |
| current_pass->name, |
| current_pass->static_pass_number); |
| else |
| fprintf (file, "pass=?"); |
| |
| break; |
| |
| case 'A': |
| fflush (file); |
| abort(); |
| |
| default: |
| /* Unknown %-code: Stop printing */ |
| |
| fprintf (file, "??? %%%c ???%s\n", *(fmt-1), fmt); |
| fmt = ""; |
| |
| break; |
| } |
| break; /* % */ |
| } |
| } |
| |
| fflush (file); |
| } |
| |
| |
| /* Called from avr.c:avr_option_override(). |
| Parse argument of -mlog= and set respective fields in avr_log. */ |
| |
| void |
| avr_log_set_avr_log (void) |
| { |
| bool all = TARGET_ALL_DEBUG != 0; |
| |
| if (all || avr_log_details) |
| { |
| /* Adding , at beginning and end of string makes searching easier. */ |
| |
| char *str = (char*) alloca (3 + strlen (avr_log_details)); |
| bool info; |
| |
| str[0] = ','; |
| strcat (stpcpy (str+1, avr_log_details), ","); |
| |
| all |= NULL != strstr (str, ",all,"); |
| info = NULL != strstr (str, ",?,"); |
| |
| if (info) |
| fprintf (stderr, "\n-mlog="); |
| |
| #define SET_DUMP_DETAIL(S) \ |
| do { \ |
| avr_log.S = (all || NULL != strstr (str, "," #S ",")); \ |
| if (info) \ |
| fprintf (stderr, #S ","); \ |
| } while (0) |
| |
| SET_DUMP_DETAIL (address_cost); |
| SET_DUMP_DETAIL (builtin); |
| SET_DUMP_DETAIL (constraints); |
| SET_DUMP_DETAIL (legitimate_address_p); |
| SET_DUMP_DETAIL (legitimize_address); |
| SET_DUMP_DETAIL (legitimize_reload_address); |
| SET_DUMP_DETAIL (progmem); |
| SET_DUMP_DETAIL (rtx_costs); |
| |
| #undef SET_DUMP_DETAIL |
| |
| if (info) |
| fprintf (stderr, "?\n\n"); |
| } |
| } |