1# * Copyright 2016, NICTA
2# *
3# * This software may be distributed and modified according to the terms
4# of
5# * the BSD 2-Clause license. Note that NO WARRANTY is provided.
6# * See "LICENSE_BSD2.txt" for details.
7# *
8# * @TAG(NICTA_BSD)
9import graph_refine.loop_bounds as loop_bounds
10import graph_refine.trace_refute as trace_refute
11import graph_refine.target_objects as target_objects
12from graph_refine.target_objects import functions
13import graph_refine.problem as problem
14import imm_utils
15import sys
16
17#extract all loop heads from loops_by_fs
18def loopHeadsToWorkOn(lbfs, funs_with_phantom_preempt, worker_ids):
19    ret = []
20    n_ignored = 0
21    for f in lbfs:
22        for head in lbfs[f]:
23            if (f not in funs_with_phantom_preempt) and ("ignore" in lbfs[f][head][1]):
24                n_ignored +=1
25            elif (-1 in worker_ids) or (lbfs[f][head][2] in worker_ids):
26                ret += lbfs[f].keys()
27    print 'ignored %d loops' % n_ignored
28    print 'working on %s' % str(map(hex,ret))
29    return ret
30
31def phantomPreemptsAnnoFileName(target_dir_name):
32    return '%s/preempt_refutes.txt' % target_dir_name
33
34def convert_loop_bounds(target_dir_name, worker_ids=None, cached_only=False):
35    if worker_ids == None:
36        worker_ids = set([-1])
37    print 'target_dir_name: %s' % target_dir_name
38    args = target_objects.load_target(target_dir_name)
39    target_dir = target_objects.target_dir
40    context = {}
41    execfile('%s/loop_counts.py' % target_dir,context)
42    assert 'loops_by_fs' in context
43    lbfs = context['loops_by_fs']
44    context = {}
45    execfile('%s/phantom_preempt.py' % target_dir,context)
46    funs_with_phantom_preempt = context['functions']
47    if funs_with_phantom_preempt:
48        preempt_annotations_file = open(phantomPreemptsAnnoFileName(target_dir), 'w')
49    #print 'funs_with_phantom_preempt: %s' % str(funs_with_phantom_preempt)
50    bin_heads = loopHeadsToWorkOn(lbfs, funs_with_phantom_preempt, worker_ids)
51    funs_with_unbounded_loop = set()
52    #all_loop_heads = loop_bounds.get_all_loop_heads()
53    print 'bin_heads: ' + str(bin_heads)
54    for head in bin_heads:
55        f = trace_refute.get_body_addrs_fun(head)
56        if f not in lbfs:
57            lbfs[f] = {}
58        ret= None
59        if f in funs_with_phantom_preempt:
60             ret = (64, 'phantom_preempt')
61             preempt_annotations_file.write("#" + f + "\n")
62             preempt_annotations_file.write("[%s]:phantom_preemp_point\n\n" % hex(head))
63
64             print '%s injected with phantom preemption point' % f
65        else:
66            try:
67                ret = loop_bounds.get_bound_super_ctxt(head,[],
68                    known_bound_only = cached_only)
69            except problem.Abort, e:
70                print 'failed to analyse %s, problem aborted' % f
71            except:
72                print "Unexpected error:", sys.exc_info()
73                raise
74        old_worker = lbfs[f][head][2]
75        if ret == None or ret[1]== 'None':
76            if cached_only:
77                comment = 'did not find cached result'
78            else:
79                comment = 'ignore: failed'
80            lbfs[f][head] = (2**30, comment, old_worker)
81            funs_with_unbounded_loop.add(f)
82        else:
83            lbfs[f][head] = (ret[0],ret[1], old_worker)
84        imm_utils.genLoopheads(lbfs, target_objects.target_dir, incremental_head=(f,head))
85    preempt_annotations_file.close()
86    unex_unbounded_funcs = set([x for x in funs_with_unbounded_loop if x not in funs_with_phantom_preempt])
87    if unex_unbounded_funcs:
88        print 'functions with unbounded loop and not bounded by preemption: %s' % str(unex_unbounded_funcs)
89        return None
90    return lbfs
91
92if __name__== '__main__':
93    import argparse
94    parser = argparse.ArgumentParser()
95    parser.add_argument("target_dir_name")
96    parser.add_argument('--worker_id', type=int, help="what bound marker is this instance responsible for, -1 means everything", default= -1)
97    parser.add_argument('--worker_ids', type=str, help="multiple worker IDs, e.g. 1,2,3", default= None)
98    parser.add_argument('--cached_only', type=bool, default=False, help="Only read what's cached in LoopBounds.txt")
99    args = parser.parse_args()
100    worker_ids = set ([args.worker_id])
101    if args.worker_ids != None:
102      ids = args.worker_ids.split(",")
103      try:
104        worker_ids = set(map(int,ids))
105      except ValueError, e:
106        print "Worker IDs not numeric: %s" % ids
107        sys.exit(-1)
108    target_dir_name = args.target_dir_name
109    print "I am workers %s" % sorted(worker_ids)
110    lbfs = convert_loop_bounds(target_dir_name, worker_ids, cached_only = args.cached_only)
111    if lbfs is None:
112        sys.exit(-1)
113
114