Skip to content

Commit 48b2e27

Browse files
authored
Merge pull request #357 from boriel/feature/dependency_propagation
Feature/dependency propagation
2 parents 3e7b422 + d4a662b commit 48b2e27

26 files changed

+4260
-71
lines changed

api/identityset.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# vim:ts=4:et:
44

55

6-
class IdentitySet(object):
6+
class IdentitySet:
77
""" This set implementation only adds items
88
if they are not exactly the same (same reference)
99
preserving its order (OrderedDict). Allows deleting by ith-index.

api/optimize.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
import api.errmsg
77
from api.errmsg import warning
88
import api.check as chk
9-
from api.constants import TYPE, SCOPE
9+
from api.constants import TYPE, SCOPE, CLASS
1010
import api.global_ as gl
1111
import symbols
1212
import types
1313
from api.debug import __DEBUG__
1414
from api.errmsg import warning_not_used
1515
import api.utils
16+
import api.symboltable
1617

1718

1819
class ToVisit(object):
@@ -128,6 +129,7 @@ def visit_FUNCCALL(self, node):
128129

129130
def visit_CALL(self, node):
130131
node.args = (yield self.generic_visit(node.args)) # Avoid infinite recursion not visiting node.entry
132+
self._check_if_any_arg_is_an_array_and_needs_lbound_or_ubound(node.entry.params, node.args)
131133
yield node
132134

133135
def visit_FUNCDECL(self, node):
@@ -251,3 +253,31 @@ def generic_visit(node):
251253
for i in range(len(node.children)):
252254
node.children[i] = (yield ToVisit(node.children[i]))
253255
yield node
256+
257+
def _check_if_any_arg_is_an_array_and_needs_lbound_or_ubound(self, params: symbols.PARAMLIST,
258+
args: symbols.ARGLIST):
259+
""" Given a list of params and a list of args, traverse them to check if any arg is a byRef array parameter,
260+
and if so, whether it's use_lbound or use_ubound flag is updated to True and if it's a local var. If so, it's
261+
offset size has changed and must be reevaluated!
262+
"""
263+
for arg, param in zip(args, params):
264+
if not param.byref or param.class_ != CLASS.array:
265+
continue
266+
267+
if arg.value.lbound_used and arg.value.ubound_used:
268+
continue
269+
270+
self._update_bound_status(arg.value, param, params.parent)
271+
272+
def _update_bound_status(self, arg: symbols.VARARRAY, param: symbols.PARAMDECL, func: symbols.FUNCTION):
273+
old_lbound_used = arg.lbound_used
274+
old_ubound_used = arg.ubound_used
275+
arg.lbound_used = arg.lbound_used or param.lbound_used
276+
arg.ubound_used = arg.ubound_used or param.ubound_used
277+
278+
if old_lbound_used != arg.lbound_used or old_ubound_used != arg.ubound_used:
279+
if arg.scope == SCOPE.global_:
280+
return
281+
282+
if arg.scope == SCOPE.local and not arg.byref:
283+
arg.scopeRef.owner.locals_size = api.symboltable.SymbolTable.compute_offsets(arg.scopeRef)

api/symboltable.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def __init__(self, mangle='', parent_mangle=''):
9191
self.caseins = OrderedDict()
9292
self.parent_mangle = parent_mangle
9393
self.mangle = mangle
94+
self.ownwer: Symbol = None # Function, Sub, etc. owning this scope
9495

9596
def __getitem__(self, key):
9697
return self.symbols.get(key, self.caseins.get(key.lower(), None))
@@ -110,7 +111,6 @@ def __delitem__(self, key):
110111
del self.caseins[key.lower()]
111112

112113
def values(self, filter_by_opt=True):
113-
# Return the values ordered
114114
if filter_by_opt and OPTIONS.optimization.value > 1:
115115
return [y for x, y in self.symbols.items() if y.accessed]
116116
return [y for x, y in self.symbols.items()]
@@ -137,7 +137,7 @@ def __init__(self):
137137
self.basic_types[type_] = self.declare_type(symbols.BASICTYPE(type_))
138138

139139
@property
140-
def current_scope(self):
140+
def current_scope(self) -> int:
141141
return len(self.table) - 1
142142

143143
@property
@@ -291,37 +291,25 @@ def enter_scope(self, funcname):
291291
global_.META_LOOPS.append(global_.LOOPS) # saves current LOOPS state
292292
global_.LOOPS = [] # new LOOPS state
293293

294-
def leave_scope(self):
295-
""" Ends a function body and pops current scope out of the symbol table.
296-
"""
297-
def entry_size(entry):
294+
@staticmethod
295+
def compute_offsets(scope: Scope) -> int:
296+
297+
def entry_size(var_entry):
298298
""" For local variables and params, returns the real variable or
299299
local array size in bytes
300300
"""
301-
if entry.scope == SCOPE.global_ or \
302-
entry.is_aliased: # aliases or global variables = 0
301+
if var_entry.scope == SCOPE.global_ or var_entry.is_aliased: # aliases or global variables = 0
303302
return 0
304303

305-
if entry.class_ != CLASS.array:
306-
return entry.size
307-
308-
return entry.memsize
304+
if var_entry.class_ != CLASS.array:
305+
return var_entry.size
309306

310-
for v in self.table[self.current_scope].values(filter_by_opt=False):
311-
if not v.accessed:
312-
if v.scope == SCOPE.parameter:
313-
kind = 'Parameter'
314-
v.accessed = True # HINT: Parameters must always be present even if not used!
315-
if not v.byref: # HINT: byref is always marked as used: it can be used to return a value
316-
warning_not_used(v.lineno, v.name, kind=kind)
307+
return var_entry.memsize
317308

318-
entries = sorted(self.table[self.current_scope].values(filter_by_opt=True), key=entry_size)
309+
entries = sorted(scope.values(filter_by_opt=True), key=entry_size)
319310
offset = 0
320311

321312
for entry in entries: # Symbols of the current level
322-
if entry.class_ is CLASS.unknown:
323-
self.move_to_global_scope(entry.name)
324-
325313
if entry.class_ in (CLASS.function, CLASS.label, CLASS.type_):
326314
continue
327315

@@ -340,6 +328,24 @@ def entry_size(entry):
340328
entry.offset = entry_size(entry) + offset
341329
offset = entry.offset
342330

331+
return offset
332+
333+
def leave_scope(self):
334+
""" Ends a function body and pops current scope out of the symbol table.
335+
"""
336+
for v in self.table[self.current_scope].values(filter_by_opt=False):
337+
if not v.accessed:
338+
if v.scope == SCOPE.parameter:
339+
kind = 'Parameter'
340+
v.accessed = True # HINT: Parameters must always be present even if not used!
341+
if not v.byref: # HINT: byref is always marked as used: it can be used to return a value
342+
warning_not_used(v.lineno, v.name, kind=kind)
343+
344+
for entry in self.table[self.current_scope].values(filter_by_opt=True): # Symbols of the current level
345+
if entry.class_ is CLASS.unknown:
346+
self.move_to_global_scope(entry.name)
347+
348+
offset = self.compute_offsets(self.table[self.current_scope])
343349
self.mangle = self[self.current_scope].parent_mangle
344350
self.table.pop()
345351
global_.LOOPS = global_.META_LOOPS.pop()

ast_/ast.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class Ast(Tree):
2222
pass
2323

2424

25-
class NodeVisitor(object):
25+
class NodeVisitor:
2626
def visit(self, node):
2727
stack = [node]
2828
last_result = None

ast_/tree.py

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#!/usr/bin/python
22
# -*- coding: utf-8 -*-
33

4+
from typing import Optional
45

5-
import copy
66
import collections
77
from api.errors import Error
88

@@ -21,11 +21,13 @@ def __str__(self):
2121
return self.msg
2222

2323

24-
class Tree(object):
24+
class Tree:
2525
""" Simple tree implementation
2626
"""
27-
class childrenList(object):
28-
def __init__(self, node):
27+
parent: Optional['Tree'] = None
28+
29+
class ChildrenList:
30+
def __init__(self, node: 'Tree'):
2931
assert isinstance(node, Tree)
3032
self.node = node # Node having this children
3133
self._children = []
@@ -34,7 +36,7 @@ def __getitem__(self, key):
3436
if isinstance(key, int):
3537
return self._children[key]
3638

37-
result = Tree.childrenList(self.node)
39+
result = Tree.ChildrenList(self.node)
3840
for x in self._children[key]:
3941
result.append(x)
4042
return result
@@ -68,10 +70,10 @@ def __len__(self):
6870
return len(self._children)
6971

7072
def __add__(self, other):
71-
if not isinstance(other, Tree.childrenList):
73+
if not isinstance(other, Tree.ChildrenList):
7274
assert isinstance(other, collections.Container)
7375

74-
result = Tree.childrenList(self.node)
76+
result = Tree.ChildrenList(self.node)
7577
for x in self:
7678
result.append(x)
7779
for x in other:
@@ -82,19 +84,19 @@ def __repr__(self):
8284
return "%s:%s" % (self.node.__repr__(), str([x.__repr__() for x in self._children]))
8385

8486
def __init__(self):
85-
self._children = Tree.childrenList(self)
87+
self._children = Tree.ChildrenList(self)
8688

8789
@property
8890
def children(self):
8991
return self._children
9092

9193
@children.setter
9294
def children(self, value):
93-
assert isinstance(value, collections.Container)
95+
assert isinstance(value, collections.Iterable)
9496
while len(self.children):
9597
self.children.pop()
9698

97-
self._children = Tree.childrenList(self)
99+
self._children = Tree.ChildrenList(self)
98100
for x in value:
99101
self.children.append(x)
100102

@@ -131,24 +133,3 @@ def prependChild(self, node):
131133
""" Inserts the given node at the beginning of the children list
132134
"""
133135
self.children.insert(0, node)
134-
135-
@classmethod
136-
def makenode(cls, symbol, *nexts):
137-
""" Stores the symbol in an AST instance,
138-
and left and right to the given ones
139-
"""
140-
result = cls(symbol)
141-
for i in nexts:
142-
if i is None:
143-
continue
144-
if not isinstance(i, cls):
145-
raise NotAnAstError(i)
146-
result.appendChild(i)
147-
148-
return result
149-
150-
def __deepcopy(self, memo):
151-
result = Tree(self.symbol) # No need to duplicate the symbol memory
152-
result.next = copy.deepcopy(self.children)
153-
154-
return result

symbols/arglist.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616
class SymbolARGLIST(Symbol):
1717
""" Defines a list of arguments in a function call or array access
1818
"""
19-
20-
def __init__(self, *args):
21-
super(SymbolARGLIST, self).__init__(*args)
22-
2319
@property
2420
def args(self):
2521
return self.children

symbols/call.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,19 @@ class SymbolCALL(Symbol):
3333
lineno: source code line where this call was made
3434
"""
3535

36-
def __init__(self, entry, arglist, lineno):
36+
def __init__(self, entry: SymbolFUNCTION, arglist, lineno):
3737
super(SymbolCALL, self).__init__()
3838
assert isinstance(lineno, int)
3939
assert all(isinstance(x, SymbolARGUMENT) for x in arglist)
4040
self.entry = entry
4141
self.args = arglist # Func. call / array access
4242
self.lineno = lineno
4343

44+
if entry.token == 'FUNCTION':
45+
for arg, param in zip(arglist, entry.params): # Sets dependency graph for each argument -> parameter
46+
if arg.value is not None:
47+
arg.value.add_required_symbol(param)
48+
4449
@property
4550
def entry(self):
4651
return self.children[0]

symbols/function.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# This program is Free Software and is released under the terms of
99
# the GNU General License
1010
# ----------------------------------------------------------------------
11+
from typing import Optional
1112

1213
from api.constants import CLASS
1314
from api.constants import KIND
@@ -52,13 +53,13 @@ def set_kind(self, value, lineno):
5253
self.__kind = value
5354

5455
@property
55-
def params(self):
56+
def params(self) -> SymbolPARAMLIST:
5657
if not self.children:
5758
return SymbolPARAMLIST()
5859
return self.children[0]
5960

6061
@params.setter
61-
def params(self, value):
62+
def params(self, value: SymbolPARAMLIST):
6263
assert isinstance(value, SymbolPARAMLIST)
6364
if self.children is None:
6465
self.children = []
@@ -69,14 +70,14 @@ def params(self, value):
6970
self.children = [value]
7071

7172
@property
72-
def body(self):
73+
def body(self) -> SymbolBLOCK:
7374
if not self.children or len(self.children) < 2:
7475
self.body = SymbolBLOCK()
7576

7677
return self.children[1]
7778

7879
@body.setter
79-
def body(self, value):
80+
def body(self, value: Optional[SymbolBLOCK]):
8081
if value is None:
8182
value = SymbolBLOCK()
8283
assert isinstance(value, SymbolBLOCK)

0 commit comments

Comments
 (0)