1#
2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3#
4# SPDX-License-Identifier: BSD-2-Clause
5#
6
7import re
8import parser
9import subprocess
10from addr_utils import phyAddrP
11verbose = False
12from elf_file import elfFile, rawVals
13
14class ChronosEmitter:
15    def __init__(self, dir_name, function_name, imm_fun, emit_as_dummy=None):
16        self.function_name = function_name
17        self.imm_fun = imm_fun
18        self.imm_file_name = '%s/%s.imm' % (dir_name, function_name)
19        self.imm_f = open(self.imm_file_name, 'w')
20        self.debug_f = open('%s/d_%s.imm' % (dir_name, function_name),'w')
21        self.emitted_loop_counts_file = open('%s/%s_emittedLoopCounts' % (dir_name, function_name),'w')
22        self.emit_as_dummy = emit_as_dummy
23        self.elf_fun_to_skip = elfFile().funcs['clean_D_PoU']
24        self.skip_fun = False
25
26    def emitTopLevelFunction(self):
27        imm_fun = self.imm_fun
28        imm_f = self.imm_f
29        debug_f = self.debug_f
30
31        self.emitEntry()
32        self.emitSyms()
33        self.emitFunction()
34        self.emitLiterals()
35
36        self.imm_f.close()
37        self.debug_f.close()
38        self.emitted_loop_counts_file.close()
39
40    def emitSyms (self):
41        ef = elfFile()
42        for name in sorted(ef.syms.keys(),key=lambda x: ef.syms[x].addr):
43            flag_str = ''
44            sym = ef.syms[name]
45            #objects(O) in objdump is data
46            if 'O' in sym.flags:
47               flag_str += 'd'
48               #functions are text
49            if 'F' in sym.flags:
50                flag_str += 't'
51                self.imm_f.write('s %s 0x%s %s %s\n' % (name, sym.addr, sym.ali_size, flag_str))
52
53    def emitEntry(self):
54        entry_addr = self.imm_fun.addr
55        s = 'e %#08x\n' % entry_addr
56        self.emitString(s)
57
58    def emitFunction(self):
59        #def emitFunction(imm_fun,imm_f,debug_f=None):
60        imm_fun = self.imm_fun
61        imm_f = self.imm_f
62        debug_f = self.debug_f
63
64        emitted_loop_counts = {}
65
66        i_nodes = imm_fun.imm_nodes
67        imm_loopheads = imm_fun.imm_loopheads
68        #locate the first and last addresses
69        first_addr,last_addr = self.firstAndLastAddr()
70        print 'first - last addrs : %x-%x' % (first_addr,last_addr)
71        size = 4
72        to_emit = {}
73
74        #dict of complex loop "head"s to ( addrs in the loop, its bound)
75        complex_loops = {}
76        #we need to emit instructions in the order of addresses
77        #firstly, put all the lines in a dict
78        for bb_start_addr in imm_fun.bbs:
79            if self.skip_fun and bb_start_addr in self.elf_fun_to_skip.lines:
80                continue
81            for addr in imm_fun.bbs[bb_start_addr]:
82                if addr in imm_loopheads:
83                    p_head, f = imm_loopheads[addr]
84                    bin_head = phyAddrP(p_head,imm_fun.f_problems[f])
85                    import graph_refine.loop_bounds
86                    if imm_fun.loaded_loop_counts and bin_head in imm_fun.bin_loops_by_fs[f]:
87                        #The user specified a manual loop-count override
88                        loop_count,desc,_ = imm_fun.bin_loops_by_fs[f][bin_head]
89                    else:
90
91                        print "imm_fun.loaded_loop_counts: %s, bin_loops_by_fs[f].keys: %s, function: %s"  % (imm_fun.loaded_loop_counts, str(imm_fun.loops_by_fs[f]), f )
92                        assert False
93                        loop_count,desc = graph_refine.loop_bounds.get_bound_super_ctxt(bin_head, [])
94                    if graph_refine.loop_bounds.is_complex_loop(addr):
95                        body_addrs = graph_refine.loop_bounds.get_loop_addrs(addr)
96                        complex_loops[addr] = (body_addrs, loop_count)
97                    emitted_loop_counts[bin_head] = (loop_count, desc)
98                    print '%x: bound %d/0x%x, %s' % (addr, loop_count, loop_count,desc)
99                else:
100                    loop_count = None
101                to_emit[addr] = (addr,addr == bb_start_addr,loop_count)
102
103        for loop_addr in complex_loops.keys():
104            print "complex loop at 0x%x" % (addr)
105            print "body: %s" % str(map(hex, body_addrs))
106            #apply the loopcounts to all the instructions in this complex loop
107            body_addrs, loop_bound = complex_loops[loop_addr]
108            for addr in body_addrs:
109                if addr not in to_emit:
110                    #dodge the halt case
111                    continue
112                addr, is_start_bb, _ = to_emit[addr]
113                to_emit[addr] = (addr,is_start_bb, loop_bound)
114                emitted_loop_counts[addr] = (loop_bound, "complex_body")
115
116
117        #then emit them in order
118        for addr in xrange (first_addr, last_addr + size, size):
119            if addr in to_emit:
120               addr,is_start_bb, loop_count = to_emit[addr]
121               self.emitImm(addr,i_nodes,is_start_bb,loop_count)
122            else:
123               #pad with nop
124               self.emitNop(addr, size)
125
126        for bin_head in emitted_loop_counts:
127            count, desc = emitted_loop_counts[bin_head]
128            self.emitted_loop_counts_file.write("0x%x : count %d, desc: %s\n" % ( bin_head, count, desc))
129
130    def firstAndLastAddr(self):
131        i_addrs = []
132        bbs = self.imm_fun.bbs
133        for bb_n in bbs:
134            i_addrs += bbs[bb_n]
135        #print 'chronos_emit i_addrs %s' % i_addrs
136        return min(i_addrs,key=int), max(i_addrs,key = int)
137
138    def emitLiterals (self):
139        ef = elfFile()
140        for addr in sorted(ef.literals,key=int):
141            (size,value) = ef.literals[addr]
142            self.imm_f.write('v %s %s %d\n'% (hex(addr),value,size))
143
144    def emitLoopcount (self,addr,loop_count):
145        self.imm_f.write('l 0x%x %s\n'% (addr,loop_count))
146        print 'l 0x%x %s\n'% (addr,loop_count)
147        if self.debug_f:
148            self.debug_f.write('l 0x%x %s\n'% (addr,loop_count))
149
150    def emitString(self, s):
151        self.imm_f.write(s)
152        if self.debug_f:
153            self.debug_f.write(s)
154
155    def emitNop(self, addr, size):
156        s = 'i %s %d startbb edges end nop _ _' % (hex(addr), size)
157        if rawVals(addr):
158            _, value = rawVals(addr)
159            s += ' %s' % hexSansX(value)
160        s += '\n'
161        self.emitString(s)
162
163    def emitImm(self,addr,nodes,is_startbb,loop_count):
164        '''
165        Emit a single line of imm instruction
166        '''
167
168        s = ''
169        node = nodes[addr]
170        #if this is a loop head, emit its loop count
171        if loop_count != None:
172            self.emitLoopcount (addr,loop_count)
173        if verbose:
174            print 'emitting %s: %s' % (addr,node.text)
175
176        #is this the start of a basic block ?
177        if is_startbb:
178            bb = 'startbb'
179        else:
180            bb = 'contbb'
181
182        #all insts are of size 4
183        s += ('i %s 4 %s' % (hex(addr),bb))
184
185        #output edges
186        s += ' edges'
187        #types of edges : next, call, callret,tailcall,return
188        #call: function call, callret : where to go when call returns ?
189        #return: this edge returns
190        #tailcall: namesake
191
192
193        for e in sorted(node.edges, key = lambda x: x.targ):
194            if type(e.targ) != int:
195                print 'e.targ %s' % e.targ
196            if e.emit:
197                s += ' next '+ hex(e.targ)
198        dummy_call = False
199        if node.call_edge:
200            assert node.call_ret_edge != None
201            if self.skip_fun and node.call_edge.targ in self.elf_fun_to_skip.lines:
202                assert not node.is_tail_call
203                # skip the function call and go directly to the return site
204                s += ' next ' + hex(node.call_ret_edge.targ)
205                dummy_call = True
206            elif node.is_tail_call:
207                s += ' tailcall ' + hex(node.call_edge.targ)
208            else:
209                s += ' call ' + hex(node.call_edge.targ)
210                #print 'call_ret_edge %s ' % node.call_ret_edge.targ
211                s += ' callret ' + hex(node.call_ret_edge.targ)
212        if node.ret_edge:
213            s += ' return'
214            assert not node.call_edge
215            assert not node.call_ret_edge
216            if verbose and node.edges != []:
217                print '     node.edges : '
218                for x in node.edges:
219                    print '       %s ' % x.targ
220
221        s += (' end')
222        txt = node.text
223        #mnenomic condition setcc, input output etc
224        #print '%s: %s' % (addr,txt)
225        value = node.raw_inst
226        if dummy_call:
227            s += ' nop _ _ %s\n' % hexSansX(value)
228            self.emitString(s)
229            return
230        i = parser.decode_instruction(addr, value, txt)
231        s += ' ' + i.mnemonic + ' '
232
233        if i.condition:
234            s += i.condition + ' '
235        else:
236            s += '_ '
237
238        if i.setcc:
239            s += 's '
240        else:
241            s += '_ '
242
243        for reg in i.input_registers:
244            s += 'input ' + reg + ' '
245        for reg in i.output_registers:
246            s += 'output ' + reg + ' '
247        if hasattr(i, 'shift_val'):
248            s += 'shift #' + i.shift_val + ' ' + i.shift_mode + ' '
249        if hasattr(i,'shift_reg'):
250            s += 'shift ' + i.shift_reg + ' ' + i.shift_mode + ' '
251        #finally the raw inst and the text
252
253        s += '%s ' % hexSansX(value)
254        s += '"%s"' % txt
255        s += '\n'
256
257        self.emitString(s)
258
259
260def hexSansX(n):
261    '''translate the input to a hex without the 0x prefix'''
262    s = hex(n)
263    return s[2:]
264
265
266