From 6922872115009244febb7d49aafabf4b0898548e Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 18 Sep 2020 17:16:12 -0400 Subject: [PATCH 35/49] FIXME: add contrib/analyzer-compare-enodes.py --- contrib/analyzer-compare-enodes.py | 181 +++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100755 contrib/analyzer-compare-enodes.py diff --git a/contrib/analyzer-compare-enodes.py b/contrib/analyzer-compare-enodes.py new file mode 100755 index 00000000000..2425514c4df --- /dev/null +++ b/contrib/analyzer-compare-enodes.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +# +# Script to analyze the output of -fdump-analyzer-json +# +# Copyright (C) 2020 Free Software Foundation, Inc. +# Contributed by David Malcolm +# +# 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 +# . */ + +import argparse +import gzip +import json +import pprint +import re +import sys + +def parse_enode_indices(s): + """ + Parse the output of print_enode_indices + For example, given "EN: 4-7, EN: 20-23, EN: 42", + return [4, 6, 7, 20, 21, 22, 23, 42] + """ + result = [] + for run in s.split(', '): + m = re.match('EN: ([0-9]+)-([0-9]+)', run) + if m: + result += range(int(m.group(1)), int(m.group(2)) + 1) + continue + m = re.match('EN: ([0-9]+)', run) + if m: + result.append(int(m.group(1))) + continue + raise ValueError('unmatched: %r' % run) + return result + +# FIXME: convert to unit tests +assert(parse_enode_indices("EN: 152, EN: 159, EN: 165, EN: 359," + " EN: 546, EN: 769, EN: 776, EN: 782, EN: 966") + == [152, 159, 165, 359, 546, 769, 776, 782, 966]) +assert(parse_enode_indices("EN: 4-7, EN: 20-23, EN: 42") + == [4, 5, 6, 7, 20, 21, 22, 23, 42]) + +class AnalyzerDump: + @staticmethod + def from_json(js): + return AnalyzerDump(js) + + def __init__(self, js): + self.js = js + + def get_enode(self, idx): + egraph_js = self.js['egraph'] + nodes = egraph_js['nodes'] + return nodes[idx] + #return Exploded_node.from_js(nodes[idx]) + +def summarize(vals, enode_idxs): + """ + Given a list of values VALS, and a list of enode_idxs of the + same length, stringify the values in VALS (to support hashing), + uniqify them, and build a dict mapping from unique + values to the list of enode_idxs which have that value. + """ + uniq_vals = set(str(js_obj) for js_obj in vals) + result = {} + for uniq_val in uniq_vals: + enode_idxs_with_val = [] + for val, enode_idx in zip(vals, enode_idxs): + if str(val) == uniq_val: + enode_idxs_with_val.append(enode_idx) + result[str(uniq_val)] = enode_idxs_with_val + # TODO: if it's all of them, maybe return None + # or maybe make this an option? + if True: + if enode_idxs_with_val == enode_idxs: + return None + return result + +def compare_enodes(js, enode_idxs): + """ + Build a comparison dict, summarizing commonality and variability of + the various states of the given enodes in the dump JS. + """ + comparison = {} + + d = AnalyzerDump.from_json (js) + egraph = js['egraph'] + init_enode = d.get_enode (enode_idxs[0]) + enodes = [d.get_enode (enode_idx) for enode_idx in enode_idxs] + states = [enode['state'] for enode in enodes] + + enodes = [d.get_enode (enode_idx) for enode_idx in enode_idxs] + points = [enode['point'] for enode in enodes] + comparison['point'] = summarize(points, enode_idxs) + + comparison['state'] = {} + + checkers = [state['checkers'] for state in states] + comparison['state']['checkers'] = summarize(checkers, enode_idxs) + + constraints = [state['constraints'] for state in states] + comparison['state']['constraints'] = summarize(constraints, enode_idxs) + + curr_frame = [state['curr_frame'] for state in states] + comparison['state']['curr_frame'] = summarize(curr_frame, enode_idxs) + + stores = [state['store'] for state in states] + + parent_regs = set() + for state in states: + for parent_reg in state['store'].keys(): + parent_regs.add(parent_reg) + #print(parent_regs) + + comparison['state']['store'] = {} + for parent_reg in parent_regs: + if parent_reg == 'called_unknown_fn': + comparison['state']['store'][parent_reg] \ + = summarize([store.get(parent_reg) for store in stores], + enode_idxs) + continue + base_regs = set() + for state in states: + #print(state['store'][parent_reg]) + for base_reg in state['store'][parent_reg].keys(): + base_regs.add(base_reg) + #print(base_regs) + comparison['state']['store'][parent_reg] = {} + for base_reg in base_regs: + comparison['state']['store'][parent_reg][base_reg] \ + = summarize([store.get(parent_reg).get(base_reg) + for store in stores], + enode_idxs) + return comparison + +def main(): + parser = argparse.ArgumentParser(description='Compare commonality and variability in a -fdump-analyzer-json dump.') + parser.add_argument('filename', metavar='FILENAME', type=str, + help='the SRCFILE.analyzer.json.gz file to load') + parser.add_argument('enodes', metavar='ENODES', type=str, + help='FIXME') + args = parser.parse_args() + + with gzip.open(args.filename) as f: + js = json.load(f) + + enode_idxs = parse_enode_indices(args.enodes) + print(repr(enode_idxs)) + + comparison = compare_enodes(js, enode_idxs) + + #pprint.pprint(comparison) + json.dump(comparison, sys.stdout, indent=True) + + if 0: + for enode_idx, enode in zip(enode_idxs, enodes): + state = enode['state'] + #print('state for EN: %i' % enode_idx) + #pprint.pprint(state) + #compare(e'checkers') + # TODO: checkers + # TODO: constraints + # TODO: curr_frame + # TODO: store + +if __name__ == '__main__': + main() -- 2.26.2