aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2017-11-13 14:16:10 +0100
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2017-11-13 14:16:10 +0100
commit1f6cd6fe94fc440a9bbd9c2f1070bd016e550be4 (patch)
tree73d5dee14c2d20e92b37b868ae3e12fb8d3b8cc3
parent5d7a65b06e8f8d42f80a046950e8f990d149b48e (diff)
parent555217d63d01f31d6322d475e33b5912a5926169 (diff)
downloadsparse-dev-1f6cd6fe94fc440a9bbd9c2f1070bd016e550be4.tar.gz
Merge branch 'dump-ir' into tip
-rw-r--r--Documentation/options.md29
-rwxr-xr-xcgcc3
-rw-r--r--lib.c246
-rw-r--r--lib.h19
-rw-r--r--linearize.c12
-rw-r--r--sparse.18
-rw-r--r--test-linearize.c2
-rw-r--r--test-unssa.c2
-rw-r--r--validation/linear/bitfield-init-mask.c2
-rw-r--r--validation/option-parsing-00.c5
-rw-r--r--validation/option-parsing-01.c5
11 files changed, 257 insertions, 76 deletions
diff --git a/Documentation/options.md b/Documentation/options.md
new file mode 100644
index 00000000..14698a98
--- /dev/null
+++ b/Documentation/options.md
@@ -0,0 +1,29 @@
+# Options
+
+This file is a complement of man page for sparse but meant
+for options not to be used by sparse itself but by the other
+tools.
+
+## Developer options:
+
+### Select the passes
+
+* '-f\<name-of-the-pass\>[-disable|-enable|=last]'
+
+ If '=last' is used, all passes after the specified one are disabled.
+ By default all passes are enabled.
+
+ The passes currently understood are:
+ * 'mem2reg'
+ * 'optim'
+
+### Internal Representation
+
+* '-fdump-ir[=\<pass\>[,\<pass\>...]]'
+
+ Dump the IR at each of the given passes.
+
+ The passes currently understood are:
+ * 'linearize'
+ * 'mem2reg'
+ * 'final'
diff --git a/cgcc b/cgcc
index 0b191f62..1f7b625c 100755
--- a/cgcc
+++ b/cgcc
@@ -103,7 +103,8 @@ sub check_only_option {
my ($arg) = @_;
return 1 if $arg =~ /^-W(no-?)?(address-space|bitwise|cast-to-as|cast-truncate|context|decl|default-bitfield-sign|designated-init|do-while|enum-mismatch|init-cstring|memcpy-max-count|non-pointer-null|old-initializer|one-bit-signed-bitfield|override-init-all|paren-string|ptr-subtraction-blows|return-void|sizeof-bool|sparse-all|sparse-error|transparent-union|typesign|undef|unknown-attribute)$/;
return 1 if $arg =~ /^-v(no-?)?(entry|dead)$/;
- return 1 if $arg =~ /^-f(dump-linearize|memcpy-max-count)(=\S*)?$/;
+ return 1 if $arg =~ /^-f(dump-ir|memcpy-max-count)(=\S*)?$/;
+ return 1 if $arg =~ /^-f(mem2reg|optim)(-enable|-disable|=last)?$/;
return 0;
}
diff --git a/lib.c b/lib.c
index d6b7c77c..037206f7 100644
--- a/lib.c
+++ b/lib.c
@@ -259,9 +259,10 @@ int dump_macro_defs = 0;
int dbg_entry = 0;
int dbg_dead = 0;
+unsigned long fdump_ir;
int fmem_report = 0;
-int fdump_linearize;
unsigned long long fmemcpy_max_count = 100000;
+unsigned long fpasses = ~0UL;
int preprocess_only;
@@ -477,9 +478,77 @@ static void handle_arch_finalize(void)
handle_arch_msize_long_finalize();
}
+const char *match_option(const char *arg, const char *prefix)
+{
+ unsigned int n = strlen(prefix);
+ if (strncmp(arg, prefix, n) == 0)
+ return arg + n;
+ return NULL;
+}
+
+
+struct mask_map {
+ const char *name;
+ unsigned long mask;
+};
+
+static int apply_mask(unsigned long *val, const char *str, unsigned len, const struct mask_map *map, int neg)
+{
+ const char *name;
+
+ for (;(name = map->name); map++) {
+ if (!strncmp(name, str, len) && !name[len]) {
+ if (neg == 0)
+ *val |= map->mask;
+ else
+ *val &= ~map->mask;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int handle_suboption_mask(const char *arg, const char *opt, const struct mask_map *map, unsigned long *flag)
+{
+ if (*opt == '\0') {
+ apply_mask(flag, "", 0, map, 0);
+ return 1;
+ }
+ if (*opt++ != '=')
+ return 0;
+ while (1) {
+ unsigned int len = strcspn(opt, ",+");
+ int neg = 0;
+ if (len == 0)
+ goto end;
+ if (!strncmp(opt, "no-", 3)) {
+ opt += 3;
+ len -= 3;
+ neg = 1;
+ }
+ if (apply_mask(flag, opt, len, map, neg))
+ die("error: wrong option '%.*s' for \'%s\'", len, opt, arg);
+
+end:
+ opt += len;
+ if (*opt++ == '\0')
+ break;
+ }
+ return 1;
+}
+
+
+#define OPT_INVERSE 1
+struct flag {
+ const char *name;
+ int *flag;
+ int (*fun)(const char *arg, const char *opt, const struct flag *, int options);
+ unsigned long mask;
+};
-static int handle_simple_switch(const char *arg, const char *name, int *flag)
+static int handle_switches(const char *ori, const char *opt, const struct flag *flags)
{
+ const char *arg = opt;
int val = 1;
// Prefixe "no-" mean to turn flag off.
@@ -488,15 +557,58 @@ static int handle_simple_switch(const char *arg, const char *name, int *flag)
val = 0;
}
- if (strcmp(arg, name) == 0) {
- *flag = val;
- return 1;
+ for (; flags->name; flags++) {
+ const char *opt = match_option(arg, flags->name);
+ int rc;
+
+ if (!opt)
+ continue;
+
+ if (flags->fun) {
+ int options = 0;
+ if (!val)
+ options |= OPT_INVERSE;
+ if ((rc = flags->fun(ori, opt, flags, options)))
+ return rc;
+ }
+
+ // boolean flag
+ if (opt[0] == '\0' && flags->flag) {
+ *flags->flag = val;
+ return 1;
+ }
}
// not handled
return 0;
}
+
+#define OPTNUM_ZERO_IS_INF 1
+#define OPTNUM_UNLIMITED 2
+
+#define OPT_NUMERIC(NAME, TYPE, FUNCTION) \
+static int opt_##NAME(const char *arg, const char *opt, TYPE *ptr, int flag) \
+{ \
+ char *end; \
+ TYPE val; \
+ \
+ val = FUNCTION(opt, &end, 0); \
+ if (*end != '\0' || end == opt) { \
+ if ((flag & OPTNUM_UNLIMITED) && !strcmp(opt, "unlimited")) \
+ val = ~val; \
+ else \
+ die("error: wrong argument to \'%s\'", arg); \
+ } \
+ if ((flag & OPTNUM_ZERO_IS_INF) && val == 0) \
+ val = ~val; \
+ *ptr = val; \
+ return 1; \
+}
+
+OPT_NUMERIC(ullong, unsigned long long, strtoull)
+
+
static char **handle_switch_o(char *arg, char **next)
{
if (!strcmp (arg, "o")) { // "-o foo"
@@ -508,10 +620,7 @@ static char **handle_switch_o(char *arg, char **next)
return next;
}
-static const struct warning {
- const char *name;
- int *flag;
-} warnings[] = {
+static const struct flag warnings[] = {
{ "address", &Waddress },
{ "address-space", &Waddress_space },
{ "bitwise", &Wbitwise },
@@ -554,7 +663,7 @@ enum {
};
-static char **handle_onoff_switch(char *arg, char **next, const struct warning warnings[], int n)
+static char **handle_onoff_switch(char *arg, char **next, const struct flag warnings[], int n)
{
int flag = WARNING_ON;
char *p = arg + 1;
@@ -596,7 +705,7 @@ static char **handle_switch_W(char *arg, char **next)
return next;
}
-static struct warning debugs[] = {
+static struct flag debugs[] = {
{ "entry", &dbg_entry},
{ "dead", &dbg_dead},
};
@@ -615,7 +724,7 @@ static char **handle_switch_v(char *arg, char **next)
return next;
}
-static struct warning dumps[] = {
+static struct flag dumps[] = {
{ "D", &dump_macro_defs},
};
@@ -629,7 +738,7 @@ static char **handle_switch_d(char *arg, char **next)
}
-static void handle_onoff_switch_finalize(const struct warning warnings[], int n)
+static void handle_onoff_switch_finalize(const struct flag warnings[], int n)
{
unsigned i;
@@ -690,69 +799,85 @@ static char **handle_switch_O(char *arg, char **next)
return next;
}
-static char **handle_switch_fmemcpy_max_count(char *arg, char **next)
-{
- unsigned long long val;
- char *end;
-
- val = strtoull(arg, &end, 0);
- if (*end != '\0' || end == arg)
- die("error: missing argument to \"-fmemcpy-max-count=\"");
-
- if (val == 0)
- val = ~0ULL;
- fmemcpy_max_count = val;
- return next;
-}
-
-static char **handle_switch_ftabstop(char *arg, char **next)
+static int handle_ftabstop(const char *arg, const char *opt, const struct flag *flag, int options)
{
- char *end;
unsigned long val;
+ char *end;
- if (*arg == '\0')
- die("error: missing argument to \"-ftabstop=\"");
+ if (*opt == '\0')
+ die("error: missing argument to \"%s\"", arg);
/* we silently ignore silly values */
- val = strtoul(arg, &end, 10);
+ val = strtoul(opt, &end, 10);
if (*end == '\0' && 1 <= val && val <= 100)
tabstop = val;
- return next;
+ return 1;
}
-static char **handle_switch_fdump(char *arg, char **next)
+static int handle_fpasses(const char *arg, const char *opt, const struct flag *flag, int options)
{
- if (!strncmp(arg, "linearize", 9)) {
- arg += 9;
- if (*arg == '\0')
- fdump_linearize = 1;
- else if (!strcmp(arg, "=only"))
- fdump_linearize = 2;
+ unsigned long mask;
+
+ mask = flag->mask;
+ if (*opt == '\0') {
+ if (options & OPT_INVERSE)
+ fpasses &= ~mask;
else
- goto err;
+ fpasses |= mask;
+ return 1;
}
+ if (options & OPT_INVERSE)
+ return 0;
+ if (!strcmp(opt, "-enable")) {
+ fpasses |= mask;
+ return 1;
+ }
+ if (!strcmp(opt, "-disable")) {
+ fpasses &= ~mask;
+ return 1;
+ }
+ if (!strcmp(opt, "=last")) {
+ // clear everything above
+ mask |= mask - 1;
+ fpasses &= mask;
+ return 1;
+ }
+ return 0;
+}
- /* ignore others flags */
- return next;
+static int handle_fdump_ir(const char *arg, const char *opt, const struct flag *flag, int options)
+{
+ static const struct mask_map dump_ir_options[] = {
+ { "", PASS_LINEARIZE },
+ { "linearize", PASS_LINEARIZE },
+ { "mem2reg", PASS_MEM2REG },
+ { "final", PASS_FINAL },
+ { },
+ };
-err:
- die("error: unknown flag \"-fdump-%s\"", arg);
+ return handle_suboption_mask(arg, opt, dump_ir_options, &fdump_ir);
}
-static char **handle_switch_f(char *arg, char **next)
+static int handle_fmemcpy_max_count(const char *arg, const char *opt, const struct flag *flag, int options)
{
- arg++;
+ opt_ullong(arg, opt, &fmemcpy_max_count, OPTNUM_ZERO_IS_INF|OPTNUM_UNLIMITED);
+ return 1;
+}
- if (!strncmp(arg, "tabstop=", 8))
- return handle_switch_ftabstop(arg+8, next);
- if (!strncmp(arg, "dump-", 5))
- return handle_switch_fdump(arg+5, next);
- if (!strncmp(arg, "memcpy-max-count=", 17))
- return handle_switch_fmemcpy_max_count(arg+17, next);
+static struct flag fflags[] = {
+ { "dump-ir", NULL, handle_fdump_ir },
+ { "mem-report", &fmem_report },
+ { "memcpy-max-count=", NULL, handle_fmemcpy_max_count },
+ { "tabstop=", NULL, handle_ftabstop },
+ { "mem2reg", NULL, handle_fpasses, PASS_MEM2REG },
+ { "optim", NULL, handle_fpasses, PASS_OPTIM },
+ { },
+};
- /* handle switches w/ arguments above, boolean and only boolean below */
- if (handle_simple_switch(arg, "mem-report", &fmem_report))
+static char **handle_switch_f(char *arg, char **next)
+{
+ if (handle_switches(arg-1, arg+1, fflags))
return next;
return next;
@@ -774,12 +899,9 @@ static char **handle_switch_a(char *arg, char **next)
return next;
}
-static char **handle_switch_s(char *arg, char **next)
+static char **handle_switch_s(const char *arg, char **next)
{
- if (!strncmp (arg, "std=", 4))
- {
- arg += 4;
-
+ if ((arg = match_option(arg, "std="))) {
if (!strcmp (arg, "c89") ||
!strcmp (arg, "iso9899:1990"))
standard = STANDARD_C89;
@@ -1333,6 +1455,8 @@ struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list
handle_switch_v_finalize();
handle_arch_finalize();
+ if (fdump_ir == 0)
+ fdump_ir = PASS_FINAL;
list = NULL;
if (!ptr_list_empty(filelist)) {
diff --git a/lib.h b/lib.h
index a9b70b07..b0f34265 100644
--- a/lib.h
+++ b/lib.h
@@ -107,6 +107,22 @@ extern void expression_error(struct expression *, const char *, ...) FORMAT_ATTR
#define ERROR_PREV_PHASE (1 << 1)
extern int has_error;
+
+enum phase {
+ PASS__PARSE,
+ PASS__LINEARIZE,
+ PASS__MEM2REG,
+ PASS__OPTIM,
+ PASS__FINAL,
+};
+
+#define PASS_PARSE (1UL << PASS__PARSE)
+#define PASS_LINEARIZE (1UL << PASS__LINEARIZE)
+#define PASS_MEM2REG (1UL << PASS__MEM2REG)
+#define PASS_OPTIM (1UL << PASS__OPTIM)
+#define PASS_FINAL (1UL << PASS__FINAL)
+
+
extern void add_pre_buffer(const char *fmt, ...) FORMAT_ATTR(1);
extern int preprocess_only;
@@ -152,8 +168,9 @@ extern int dbg_entry;
extern int dbg_dead;
extern int fmem_report;
-extern int fdump_linearize;
+extern unsigned long fdump_ir;
extern unsigned long long fmemcpy_max_count;
+extern unsigned long fpasses;
extern int arch_m64;
extern int arch_msize_long;
diff --git a/linearize.c b/linearize.c
index 905b473e..9040d5d9 100644
--- a/linearize.c
+++ b/linearize.c
@@ -2186,11 +2186,8 @@ static struct entrypoint *linearize_fn(struct symbol *sym, struct symbol *base_t
add_one_insn(ep, insn);
}
- if (fdump_linearize) {
- if (fdump_linearize == 2)
- return ep;
+ if (fdump_ir & PASS_LINEARIZE)
show_entry(ep);
- }
/*
* Do trivial flow simplification - branches to
@@ -2201,8 +2198,13 @@ static struct entrypoint *linearize_fn(struct symbol *sym, struct symbol *base_t
/*
* Turn symbols into pseudos
*/
- simplify_symbol_usage(ep);
+ if (fpasses & PASS_MEM2REG)
+ simplify_symbol_usage(ep);
+ if (fdump_ir & PASS_MEM2REG)
+ show_entry(ep);
+ if (!(fpasses & PASS_OPTIM))
+ return ep;
repeat:
/*
* Remove trivial instructions, and try to CSE
diff --git a/sparse.1 b/sparse.1
index bdfe1490..5b2bcd9c 100644
--- a/sparse.1
+++ b/sparse.1
@@ -365,12 +365,6 @@ The \fIdir\fR name would normally take the form of the target's
normalized GNU triplet. (e.g. i386-linux-gnu).
.
.SH DEBUG OPTIONS
-.TP
-.B \-fdump-linearize[=only]
-Dump the IR code of a function directly after its linearization,
-before any simplifications is made. If the argument \fB=only\fR is
-also given no further processing is done on the function.
-.
.B \-fmem-report
Report some statistics about memory allocation used by the tool.
.
@@ -378,7 +372,7 @@ Report some statistics about memory allocation used by the tool.
.TP
.B \-fmemcpy-max-count=COUNT
Set the limit for the warnings given by \fB-Wmemcpy-max-count\fR.
-A COUNT of 0, useless in itself, will effectively disable the warning.
+A COUNT of 'unlimited' or '0' will effectively disable the warning.
The default limit is 100000.
.
.TP
diff --git a/test-linearize.c b/test-linearize.c
index fe0673be..e6d1ee3c 100644
--- a/test-linearize.c
+++ b/test-linearize.c
@@ -47,6 +47,8 @@ static void clean_up_symbols(struct symbol_list *list)
expand_symbol(sym);
ep = linearize_symbol(sym);
+ if (!(fdump_ir & PASS_FINAL))
+ continue;
if (ep)
show_entry(ep);
} END_FOR_EACH_PTR(sym);
diff --git a/test-unssa.c b/test-unssa.c
index 240d9960..80752f43 100644
--- a/test-unssa.c
+++ b/test-unssa.c
@@ -62,6 +62,8 @@ static int compile(struct symbol_list *list)
struct entrypoint *ep;
expand_symbol(sym);
ep = linearize_symbol(sym);
+ if (!(fdump_ir & PASS_FINAL))
+ continue;
if (ep)
output_fn(ep);
else
diff --git a/validation/linear/bitfield-init-mask.c b/validation/linear/bitfield-init-mask.c
index 94afa400..aac21e61 100644
--- a/validation/linear/bitfield-init-mask.c
+++ b/validation/linear/bitfield-init-mask.c
@@ -18,7 +18,7 @@ struct bfu bfu_init_20_23(int a)
/*
* check-name: bitfield initializer mask
- * check-command: test-linearize -fdump-linearize=only -Wno-decl $file
+ * check-command: test-linearize -fdump-ir=linearize -Wno-decl $file
* check-output-ignore
*
* check-output-contains: and\\..*fffff800\$
diff --git a/validation/option-parsing-00.c b/validation/option-parsing-00.c
new file mode 100644
index 00000000..9b85943c
--- /dev/null
+++ b/validation/option-parsing-00.c
@@ -0,0 +1,5 @@
+
+/*
+ * check-name: option parsing 00
+ * check-command sparse -foptimize-xyz $file
+ */
diff --git a/validation/option-parsing-01.c b/validation/option-parsing-01.c
new file mode 100644
index 00000000..e33a2ef0
--- /dev/null
+++ b/validation/option-parsing-01.c
@@ -0,0 +1,5 @@
+
+/*
+ * check-name: option parsing 01
+ * check-command sparse -fno-optimize-xyz $file
+ */