5 redef class AnalysisManager
6 fun build_cfg
( model
: Model ) : CFG
8 var cfg
= new CFG( model
)
13 redef class ABranchInstruction
17 return op
isa AAnyOperand and (once
["x","d"]).has
(op
.addressing_mode
)
22 var start
: BasicBlock
23 var finish
: BasicBlock
24 var addr_to_blocks
= new HashMap[Int,BasicBlock]
25 var blocks
= new Array[BasicBlock]
27 var has_function_calls
= false
31 assert not model
.lines
.is_empty
34 var ends
= [model
.lines
.last
.address
]
36 # check for indirect branches
37 #var indirect_jump_sites = new Array[Int]
38 var has_indirect_jump_sites
= false
40 # detect basic block limits
41 for line
in model
.lines
do
42 if line
isa AInstructionLine then
43 var instr
= line
.n_instruction
44 if instr
isa ABranchInstruction then
45 if instr
.is_indirect
then
46 #indirect_jump_sites.add(line.address)
47 has_indirect_jump_sites
= true
48 manager
.notes
.add
(new Warn(instr
.location
, "use of indirect jumps, the CFG may be wrong"))
50 var op
= instr
.n_operand
51 var dest
= op
.n_value
.to_i
54 ends
.add
(line
.address
)
55 if not instr
isa ABrInstruction then
56 # next line is possible start
57 starts
.add
(line
.address
+4)
59 else if instr
isa AStopInstruction or
60 instr
isa ARetInstruction then
61 ends
.add
(line
.address
)
66 var addresses_in_memory
= new Array[Int]
67 #if not indirect_jumps.is_empty then
68 if has_indirect_jump_sites
then
69 # find possible jump destinations
71 for line
in model
.lines
do
72 if line
isa ADirectiveLine then
73 var dir
= line
.n_directive
74 if dir
isa AAddrssDirective then
75 var dest
= dir
.n_value
.to_i
77 addresses_in_memory
.add
(dest
)
82 # link indirect jumps to possible destinations
83 #for src in addresses_in_memory do
87 # sort breakpoints in order
88 starts
= starts
.iterator
.uniq
.sort
.to_a
89 ends
= ends
.iterator
.uniq
.sort
.to_a
92 var current_block
: nullable BasicBlock = null
95 for line
in model
.lines
do
96 var addr
= line
.address
97 while next_start_i
< starts
.length
and
98 starts
[next_start_i
] < addr
do next_start_i
+= 1
99 if next_start_i
< starts
.length
and
100 starts
[next_start_i
] == addr
then
101 # is a dest, and not already started
102 current_block
= new BasicBlock
103 blocks
.add
(current_block
)
106 if current_block
== null then
107 current_block
= new BasicBlock
108 blocks
.add
(current_block
)
111 current_block
.lines
.add
(line
)
113 while next_end_i
< ends
.length
and
114 ends
[next_end_i
] < addr
do next_end_i
+= 1
115 if next_end_i
< ends
.length
and
116 ends
[next_end_i
] == addr
then
117 # stops here, unless already at the end
122 # adds created blocks to instance attribute
124 # save them in the class attribute
125 addr_to_blocks
[b
.lines
.first
.address
] = b
129 start
= new BasicBlock.start
131 if blocks
.length
> 0 and blocks
.first
!= start
then
132 start
.successors
.add
(blocks
.first
)
133 blocks
.first
.predecessors
.add
(start
)
135 addr_to_blocks
[-2] = start
138 finish
= new BasicBlock.finish
140 addr_to_blocks
[-1] = finish
142 # set successors and predecessors
143 for b
in blocks
do if not b
.lines
.is_empty
then
144 var line
= b
.lines
.last
146 if line
isa AInstructionLine then
147 var instr
= line
.n_instruction
148 if instr
isa ABranchInstruction then
149 var op
= instr
.n_operand
150 if instr
.is_indirect
then
151 for dest
in addresses_in_memory
do
152 var db
= addr_to_blocks
[dest
]
154 db
.predecessors
.add
(b
)
157 var dest
= op
.n_value
.to_i
158 var db
= addr_to_blocks
[dest
]
160 db
.predecessors
.add
(b
)
164 if not instr
isa ABrInstruction and
165 not instr
isa AStopInstruction and
166 not instr
isa ACallInstruction and
167 not instr
isa ARetInstruction then
168 # next line is possible start
169 var dest
= line
.address
+4
170 if addr_to_blocks
.has_key
(dest
) then
171 var db
= addr_to_blocks
[dest
]
173 db
.predecessors
.add
(b
)
175 manager
.notes
.add
(new Error(line
.location
, "invalid line following instruction"))
179 if instr
isa ACallInstruction then
180 has_function_calls
= true
181 var next_addr
= line
.address
+4
182 if not addr_to_blocks
.has_key
(next_addr
) then
183 print
"error, no instruction following call {b.name}"
185 b
.after_call
= addr_to_blocks
[next_addr
]
189 if b
.successors
.is_empty
and
190 not instr
isa ARetInstruction then
191 b
.successors
.add
(finish
)
192 finish
.predecessors
.add
(b
)
197 # Verify use of function calls
198 # To be by the book, each path from the start to the end should have
199 # the same number of calls and rets. There should never be more rets
202 # TODO check for instructions not in a path from start to end
204 # TODO check if branching on variable
206 # TODO check there is there is a consistant use of the stack between
210 # duplicate function calls
211 # at each call site, the tree representing the
214 inline_functions_recursive
(start
, new List[BasicBlock]) #0)
216 # retain only blocks reachble from start
217 var reachables
= new HashSet[BasicBlock]
218 var todo
= new Array[BasicBlock]
220 while not todo
.is_empty
do
222 if not reachables
.has
(n
) then
224 for s
in n
.successors
do if not reachables
.has
(s
) then
229 self.blocks
= reachables
.to_a
232 private fun inline_functions_recursive
(b
: BasicBlock, seq
: List[BasicBlock]) #depth: Int)
234 # Protection against cycles
235 #if depth > 1000 then return
238 if sl
> loop_length
then
239 for i
in [0..sl-loop_length
[ do
241 for j
in [0..loop_length
[ do if seq
[i
+j
]!=seq
[sl-3
+j
] then
247 print
"recursive since {seq[i].name}"
252 #if seq.has(b) then return
254 if not b
.lines
.is_empty
then
255 var line
= b
.lines
.last
256 if line
isa AInstructionLine then
257 var instr
= line
.n_instruction
258 if instr
isa ACallInstruction then
259 # replace called by a dup
260 assert b
.successors
.length
== 1
261 var called
= b
.successors
.first
262 var rets
= new HashSet[BasicBlock]
263 var n
= called
.duplicate_tree
(
264 new HashMap[BasicBlock,BasicBlock], rets
, 0, blocks
)
266 n
.predecessors
.add
(b
)
268 if b
.after_call
== null then
269 print
"Already inlined"
272 # TODO bring back assert
273 if n
.predecessors
.length
> 1 then
276 print
"preds {n.predecessors.join(" ")}"
278 assert n
.predecessors
.length
== 1 else print n
.predecessors
.length
279 assert b
.successors
.length
== 1
280 # TODO add information about duplicated block that are not dead
283 var next
= b
.after_call
.as(not null)
284 # Another protection against cycles
286 ret
.successors
.add
(next
)
287 next
.predecessors
.add
(ret
)
289 #print "linking {b.name} to {next.name} ret len {rets.length}"
298 while si
< b
.successors
.length
do
299 var s
= b
.successors
[si
]
301 inline_functions_recursive
(s
, seq
)
307 var watchdog
writable = 0
308 fun link_ret_to_calls
(b
: BasicBlock, to_link_ori
: List[BasicBlock], seq
: List[BasicBlock], depth
: Int): Bool
311 if watchdog
== 10000 then
312 print
"Error: Umanagable cycle detected"
318 if sl
> loop_length
then
319 for i
in [0..sl-loop_length
[ do
321 for j
in [0..loop_length
[ do if seq
[i
+j
]!=seq
[sl-loop_length
+j
] then
327 #print "recursive since {seq[i].name}"
334 var to_link
= new List[BasicBlock]
335 to_link
.add_all
(to_link_ori
)
337 if not b
.lines
.is_empty
then
338 var line
= b
.lines
.last
339 if line
isa AInstructionLine then
340 var instr
= line
.n_instruction
341 if instr
isa ACallInstruction then
344 else if instr
isa ARetInstruction then
345 if to_link
.is_empty
then
346 manager
.notes
.add
( new Error(instr
.location
,"no CALL can be linked to return") )
349 var caller
= to_link
.pop
351 var next
= caller
.after_call
.as(not null)
353 # Another protection against cycles
354 if b
.successors
.has
(next
) then return true
356 b
.successors
.add
(next
)
357 next
.predecessors
.add
(b
)
364 while si
< b
.successors
.length
do
365 var s
= b
.successors
[si
]
367 if not link_ret_to_calls
(s
, to_link
,seq
,depth
+1) then return false
378 var lines
= new Array[ANonEmptyLine]
379 var successors
= new Array[BasicBlock]
380 var predecessors
= new Array[BasicBlock]
381 var after_call
: nullable BasicBlock = null
385 var count
= (once
new Counter).next
388 init named
(name
: String) do self.name
= name
389 init start
do name
= "start"
395 fun duplicate_tree
(dups
: HashMap[BasicBlock,BasicBlock],
396 rets
: Set[BasicBlock], calls
: Int,
397 blocks
: Array[BasicBlock]) : BasicBlock
399 if dups
.has_key
(self) then return dups
[self]
401 var n
= new BasicBlock.from
(self)
404 n
.successors
= new Array[BasicBlock]
405 n
.predecessors
= new Array[BasicBlock]
407 if after_call
!= null then
408 var nac
= after_call
.duplicate_tree
(dups
, rets
, calls
, blocks
)
410 calls
+= 1 # for within that call
413 for s
in successors
do
414 var ns
= s
.duplicate_tree
(dups
, rets
, calls
, blocks
)
416 ns
.predecessors
.add
(n
)
419 if calls
== 0 and successors
.is_empty
then rets
.add
(n
)
424 init from
(o
: BasicBlock)
426 var count
= (once
new Counter).next
427 name
= "c{count}_from_{o.name}"
429 successors
= o
.successors
430 predecessors
= o
.predecessors
431 after_call
= o
.after_call
435 private class Counter