Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Closes #2125) Fix bugs in SymbolTable deep copy and InlineTrans when array bounds reference formal arg #2633

Merged
merged 62 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
6428962
#2125 permit use of force to allow CodeBlocks
arporter Jun 17, 2024
ecf9d35
#2125 update InlineTrans to check for formal args in any array formal…
arporter Jun 17, 2024
fab567d
#2125 fix bug in SymbolTable deep copy and begin testing
arporter Jun 18, 2024
53f67ff
#2125 allow for case where dimensioning symbol is not in table being …
arporter Jun 20, 2024
6764145
#2125 add check for symbols in initial values
arporter Jun 20, 2024
856ad73
#2125 add test showing problem in copy of array shapes [skip ci]
arporter Jun 21, 2024
8b80941
Merge branch 'master' into 2125_inline_fixes
arporter Jun 24, 2024
15c6c8b
#2125 begin adding copy() to ArrayType [skip ci]
arporter Jun 24, 2024
2f8c12b
Merge branch 'master' into 2125_inline_fixes
arporter Jun 25, 2024
a8686d5
#2125 add copy() methods to ArrayType and ArrayType.Extent
arporter Jun 25, 2024
4914e75
#2125 fix coverage of SymbolTable
arporter Jun 26, 2024
041286f
Merge branch 'master' into 2125_tuple_inline_fixes
arporter Jun 27, 2024
eefe088
#2125 tidying and get full coverage of datatypes.py
arporter Jun 28, 2024
c76f384
Merge branch 'master' into 2125_tuple_inline_fixes
arporter Jun 28, 2024
5586630
#2125 improve comments and tidy
arporter Jun 28, 2024
734c04b
#2115 fix error in deep-copy routine
arporter Jun 28, 2024
5cb372f
Merge branch 'master' into 2125_tuple_inline_fixes
arporter Jul 2, 2024
dd79b0e
Merge branch 'master' into 2125_tuple_inline_fixes
arporter Jul 2, 2024
b122dcc
#2125 override UnsupportedFortranType.copy() and test
arporter Jul 2, 2024
1a3b81e
#2125 add precision and intrinsic setters
arporter Jul 3, 2024
fbd9ed6
#2125 WIP extending deep-copy functionality and tests [skip ci]
arporter Jul 3, 2024
a4cf7ab
#2125 add setter for partial_datatype
arporter Jul 3, 2024
2a9b524
#2125 add deep-copy support for symbols of StructureType
arporter Jul 3, 2024
3005118
Merge branch 'master' into 2125_tuple_inline_fixes
arporter Jul 3, 2024
511a700
#2125 improve comments and DataSymbol testing
arporter Jul 3, 2024
23567ff
#2125 add more complex expression to test
arporter Jul 3, 2024
dda78cc
#2125 update copy() and tests for subclasses of TypedSymbol
arporter Jul 3, 2024
510a67a
#2125 mv functionality out of SymbolTable and into relink methods
arporter Jul 3, 2024
6f0290d
#2125 fix linting
arporter Jul 3, 2024
29e008b
#2125 improve testing [skip ci]
arporter Jul 4, 2024
584b4f3
#2125 replace Node.relink() with new Visitor
arporter Jul 4, 2024
8dcbab4
#2125 fix lint error
arporter Jul 4, 2024
3a5b31e
#2125 switch from visitor to Node.update_symbols_from() method
arporter Jul 4, 2024
817ea50
#2125 fix linting (again)
arporter Jul 4, 2024
0f73b67
#2125 get full coverage of datatypes
arporter Jul 5, 2024
5775a16
#2125 add tests for Literal and Reference and fix bug
arporter Jul 5, 2024
ed454f0
#2125 fix coverage of GenericInterfaceSymbol and Symbol
arporter Jul 5, 2024
e7aab1e
#2125 rm unnecessary tests
arporter Jul 5, 2024
bff1a18
#2125 improve comments and tests
arporter Jul 5, 2024
fa15577
#2125 test routinesymbol copy with interface
arporter Jul 5, 2024
24b1181
Merge branch 'master' into 2125_tuple_inline_fixes
arporter Jul 12, 2024
2d379c0
#2125 update dev guide [skip ci]
arporter Jul 12, 2024
018aee4
#2125 consistency improvements
arporter Jul 12, 2024
23c57d7
#2125 add TODOs to LFRicTypes
arporter Jul 12, 2024
2a08535
#2125 make docstrings consistent
arporter Jul 15, 2024
a651400
#2125 rename the method
arporter Jul 15, 2024
3c492ac
#2125 update ScopingNode.refine_copy to use new method and extend Loo…
arporter Jul 15, 2024
5a23841
#2125 fix linting error
arporter Jul 15, 2024
149cc56
#2125 add test for new Loop method
arporter Jul 15, 2024
2bd3b90
#2125 make calls to replace_symbols_using() non-recursive if within _…
arporter Jul 15, 2024
adbbb5b
#2125 add replace_symbols_using to ScopingNode
arporter Jul 16, 2024
9ade9a7
#2125 get full coverage of ScopingNode from associated test file
arporter Jul 16, 2024
0a0351c
Merge branch 'master' into 2125_tuple_inline_fixes
arporter Jul 16, 2024
03308a4
#2125 update dev guide with renamed implementation
arporter Jul 17, 2024
2216835
Merge branch 'master' into 2125_tuple_inline_fixes
arporter Jul 19, 2024
0f0a863
#2125 update docstrings and dev-guide with details on symbol-shadowin…
arporter Jul 19, 2024
b123609
#2125 use automethod to avoid duplicating warning from docstring
arporter Jul 19, 2024
5e83afd
#2125 updates for review
arporter Jul 22, 2024
ceb2f75
#2125 ensure Reference.replace_symbols_using does *not* call Symbol.r…
arporter Jul 22, 2024
8e10095
#2125 fix failing Reference test
arporter Jul 22, 2024
ffcd885
Merge remote-tracking branch 'origin/master' into 2125_tuple_inline_f…
sergisiso Jul 23, 2024
565c2c3
Update changelog
sergisiso Jul 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/psyclone/psyir/symbols/datasymbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,21 @@ def copy(self):

'''
if self.initial_value is not None:
# Ensure any References in the initial-value expression are
sergisiso marked this conversation as resolved.
Show resolved Hide resolved
# also copied. At this stage they will still point to the
# same Symbols as the original.
new_init_value = self.initial_value.copy()
else:
new_init_value = None
return DataSymbol(self.name, self.datatype, visibility=self.visibility,
if self.is_array:
# Ensure any References in the shape definition of an ArrayType
# are also copied. At this stage they will still point to the
# same Symbols as the original.
new_datatype = self.datatype.copy()
sergisiso marked this conversation as resolved.
Show resolved Hide resolved
else:
new_datatype = self.datatype
return DataSymbol(self.name, new_datatype,
visibility=self.visibility,
interface=self.interface,
is_constant=self.is_constant,
initial_value=new_init_value)
Expand Down
40 changes: 38 additions & 2 deletions src/psyclone/psyir/symbols/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
''' This module contains the datatype definitions.'''

import abc
import copy
from collections import OrderedDict, namedtuple
from enum import Enum

Expand Down Expand Up @@ -67,6 +68,13 @@ def __eq__(self, other):
'''
return type(other) is type(self)

def copy(self):
'''
:returns: a shallow copy of this datatype.
sergisiso marked this conversation as resolved.
Show resolved Hide resolved
:rtype: :py:class:`psyclone.psyir.symbols.datatypes.DataType`
'''
return copy.copy(self)


class UnresolvedType(DataType):
# pylint: disable=too-few-public-methods
Expand Down Expand Up @@ -116,7 +124,7 @@ def __str__(self):
@property
def declaration(self):
'''
:returns: the original declaration of the symbol. This is obviously \
:returns: the original declaration of the symbol. This is obviously
language specific and hence this class must be subclassed.
sergisiso marked this conversation as resolved.
Show resolved Hide resolved
:rtype: str
'''
sergisiso marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -380,6 +388,13 @@ class Extent(Enum):
DEFERRED = 1
ATTRIBUTE = 2

def copy(self):
'''
:returns: a shallow copy of self.
:rtype: :py:class:`psyclone.psyir.symbols.ArrayType.Extent`
'''
return copy.copy(self)

#: namedtuple used to store lower and upper limits of an array dimension
ArrayBounds = namedtuple("ArrayBounds", ["lower", "upper"])

Expand Down Expand Up @@ -456,7 +471,7 @@ def _dangling_parent(var):
# The lower bound is 1 by default.
self._shape.append(
ArrayType.ArrayBounds(
_dangling_parent(one),
_dangling_parent(one.copy()),
_dangling_parent(_node_from_int(dim))))
elif isinstance(dim, tuple):
self._shape.append(
Expand Down Expand Up @@ -709,6 +724,27 @@ def __eq__(self, other):

return True

def copy(self):
'''
Create a shallow copy of this ArrayType. (Any References will be
re-created but the target Symbols will remain unchanged.)
sergisiso marked this conversation as resolved.
Show resolved Hide resolved

:returns: a copy of this ArrayType.
:rtype: :py:class:`psyclone.psyir.datatype.ArrayType`

'''
new_shape = []
for dim in self.shape:
if isinstance(dim, ArrayType.ArrayBounds):
new_bounds = ArrayType.ArrayBounds(dim.lower.copy(),
dim.upper.copy())
new_shape.append(new_bounds)
else:
# This dimension is specified with an ArrayType.Extent
# so no need to copy.
new_shape.append(dim)
return ArrayType(self.datatype, new_shape)


class StructureType(DataType):
sergisiso marked this conversation as resolved.
Show resolved Hide resolved
'''
Expand Down
49 changes: 41 additions & 8 deletions src/psyclone/psyir/symbols/symbol_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from psyclone.psyir.symbols import (
DataSymbol, ContainerSymbol, DataTypeSymbol, GenericInterfaceSymbol,
ImportInterface, RoutineSymbol, Symbol, SymbolError, UnresolvedInterface)
from psyclone.psyir.symbols.datatypes import ArrayType
from psyclone.psyir.symbols.intrinsic_symbol import IntrinsicSymbol
from psyclone.psyir.symbols.typed_symbol import TypedSymbol

Expand Down Expand Up @@ -145,14 +146,12 @@ def parent_symbol_table(self, scope_limit=None):
'''If this symbol table is enclosed in another scope, return the
symbol table of the next outer scope. Otherwise return None.

:param scope_limit: optional Node which limits the symbol \
search space to the symbol tables of the nodes within the \
given scope. If it is None (the default), the whole \
scope (all symbol tables in ancestor nodes) is searched \
otherwise ancestors of the scope_limit node are not \
searched.
:type scope_limit: :py:class:`psyclone.psyir.nodes.Node` or \
`NoneType`
:param scope_limit: optional Node which limits the symbol search
space to the symbol tables of the nodes within the given scope.
If it is None (the default), the whole scope (all symbol tables
in ancestor nodes) is searched otherwise ancestors of the
scope_limit node are not searched.
:type scope_limit: Optional[:py:class:`psyclone.psyir.nodes.Node`]

:returns: the 'parent' SymbolTable of the current SymbolTable (i.e.
the one that encloses this one in the PSyIR hierarchy).
Expand Down Expand Up @@ -303,6 +302,40 @@ def deep_copy(self):
routine.from_container))
symbol.routines = new_routines

# Ensure any Symbols referenced in array shapes are also updated
sergisiso marked this conversation as resolved.
Show resolved Hide resolved
# pylint: disable-next=import-outside-toplevel
from psyclone.psyir.nodes import Node, Reference
for symbol in new_st.symbols:
if not (symbol.is_array and isinstance(symbol.datatype,
ArrayType)):
continue
for dim in symbol.datatype.shape:
if isinstance(dim, ArrayType.ArrayBounds):
exprns = dim
else:
exprns = [dim]
for bnd in exprns:
if isinstance(bnd, Node):
for ref in bnd.walk(Reference):
try:
ref.symbol = new_st.lookup(ref.symbol.name)
except KeyError:
# This dimensioning symbol isn't in the table
# we are copying.
pass

# Ensure any Symbols referenced in initial values are updated.
for symbol in new_st.symbols:
if not isinstance(symbol, DataSymbol):
continue
if symbol.initial_value:
for ref in symbol.initial_value.walk(Reference):
try:
ref.symbol = new_st.lookup(ref.symbol.name)
except KeyError:
# This symbol isn't in the table we are copying.
pass

# Set the default visibility
new_st._default_visibility = self.default_visibility

Expand Down
49 changes: 33 additions & 16 deletions src/psyclone/psyir/transformations/inline_trans.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ def apply(self, node, options=None):
:type node: :py:class:`psyclone.psyir.nodes.Routine`
:param options: a dictionary with options for transformations.
:type options: Optional[Dict[str, Any]]

:param bool options["force"]: whether or not to permit the inlining
of Routines containing CodeBlocks. Default is False.
'''
self.validate(node, options)
# The table associated with the scoping region holding the Call.
Expand Down Expand Up @@ -448,12 +449,18 @@ def _update_actual_indices(self, actual_arg, local_ref,
# The formal argument declaration has a shape.
local_shape = local_decln_shape[local_idx_posn]
local_decln_start = local_shape.lower
if isinstance(local_decln_start, Node):
# Ensure any references to formal arguments within
# the declared array lower bound are updated.
local_decln_start = self._replace_formal_arg(
local_decln_start, call_node, formal_args)
elif (local_decln_shape[local_idx_posn] ==
ArrayType.Extent.DEFERRED):
# The formal argument is declared to be allocatable and
# therefore has the same bounds as the actual argument.
local_shape = None
local_decln_start = actual_start

if not local_decln_start:
local_shape = None
local_decln_start = _ONE
Expand Down Expand Up @@ -609,35 +616,41 @@ def validate(self, node, options=None):
:type node: subclass of :py:class:`psyclone.psyir.nodes.Call`
:param options: a dictionary with options for transformations.
:type options: Optional[Dict[str, Any]]
:param bool options["force"]: whether or not to ignore any CodeBlocks
in the candidate routine. Default is False.

:raises TransformationError: if the supplied node is not a Call or is \
:raises TransformationError: if the supplied node is not a Call or is
an IntrinsicCall.
:raises TransformationError: if the routine has a return value.
:raises TransformationError: if the routine body contains a Return \
:raises TransformationError: if the routine body contains a Return
that is not the first or last statement.
:raises TransformationError: if the routine body contains a CodeBlock.
:raises TransformationError: if the called routine has a named \
:raises TransformationError: if the routine body contains a CodeBlock
and the 'force' option is not True.
:raises TransformationError: if the called routine has a named
argument.
:raises TransformationError: if any of the variables declared within \
:raises TransformationError: if any of the variables declared within
the called routine are of UnknownInterface.
:raises TransformationError: if any of the variables declared within \
:raises TransformationError: if any of the variables declared within
the called routine have a StaticInterface.
:raises TransformationError: if any of the subroutine arguments is of \
:raises TransformationError: if any of the subroutine arguments is of
UnsupportedType.
:raises TransformationError: if a symbol of a given name is imported \
:raises TransformationError: if a symbol of a given name is imported
from different containers at the call site and within the routine.
:raises TransformationError: if the routine accesses an un-resolved \
:raises TransformationError: if the routine accesses an un-resolved
symbol.
:raises TransformationError: if the number of arguments in the call \
:raises TransformationError: if the number of arguments in the call
does not match the number of formal arguments of the routine.
:raises TransformationError: if a symbol declared in the parent \
:raises TransformationError: if a symbol declared in the parent
container is accessed in the target routine.
:raises TransformationError: if the shape of an array formal argument \
:raises TransformationError: if the shape of an array formal argument
does not match that of the corresponding actual argument.

'''
super().validate(node, options=options)

options = {} if options is None else options
forced = options.get("force", False)

# The node should be a Call.
if not isinstance(node, Call):
raise TransformationError(
Expand Down Expand Up @@ -665,10 +678,14 @@ def validate(self, node, options=None):
f"Routine '{name}' contains one or more "
f"Return statements and therefore cannot be inlined.")

if routine.walk(CodeBlock):
if routine.walk(CodeBlock) and not forced:
# N.B. we permit the user to specify the "force" option to allow
# CodeBlocks to be included.
raise TransformationError(
f"Routine '{name}' contains one or more "
f"CodeBlocks and therefore cannot be inlined.")
f"Routine '{name}' contains one or more CodeBlocks and "
"therefore cannot be inlined. (If you are confident that "
"the code may safely be inlined despite this then use "
"`options={'force': True}` to override.)")

# Support for routines with named arguments is not yet implemented.
# TODO #924.
Expand Down
2 changes: 1 addition & 1 deletion src/psyclone/tests/psyir/backend/c_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ def test_cw_loop(fortran_reader):
code = '''
module test
contains
subroutine tmp()
subroutine tmp(b)
integer :: i, a
integer, dimension(:) :: b
do i = 1, 20, 2
Expand Down
44 changes: 42 additions & 2 deletions src/psyclone/tests/psyir/symbols/datatype_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ def test_unresolvedtype_eq():
assert data_type1 != NoType()


def test_unresolvedtype_copy():
'''Test the copy() method of UnresolvedType (which is just inherited
from DataType).'''
dtype1 = UnresolvedType()
dtype2 = dtype1.copy()
assert isinstance(dtype2, UnresolvedType)
assert dtype2 is not dtype1


# NoType class

def test_notype():
Expand Down Expand Up @@ -268,6 +277,14 @@ def test_scalartype_immutable():

# ArrayType class

def test_arraytype_extent():
'''Test the ArrayType.Extent class. This is just an enum with a
copy() method. '''
xtent = ArrayType.Extent.ATTRIBUTE
ytent = xtent.copy()
assert isinstance(ytent, ArrayType.Extent)


def test_arraytype():
'''Test that the ArrayType class __init__ works as expected. Test the
different dimension datatypes that are supported.'''
Expand Down Expand Up @@ -319,8 +336,9 @@ def test_arraytype():
assert array_type.shape[1] == ArrayType.Extent.DEFERRED
# Provided as an attribute extent
array_type = ArrayType(
scalar_type, [ArrayType.Extent.ATTRIBUTE, ArrayType.Extent.ATTRIBUTE])
assert array_type.shape[1] == ArrayType.Extent.ATTRIBUTE
scalar_type, [ArrayType.Extent.ATTRIBUTE,
(2, ArrayType.Extent.ATTRIBUTE)])
assert array_type.shape[1].upper == ArrayType.Extent.ATTRIBUTE


def test_arraytype_invalid_datatype():
Expand Down Expand Up @@ -366,6 +384,7 @@ def test_arraytype_unsupportedtype():
assert isinstance(atype, ArrayType)
assert atype.datatype is utype
assert atype.precision is None
assert utype.declaration == "integer, pointer :: var"


def test_arraytype_invalid_shape():
Expand Down Expand Up @@ -572,6 +591,27 @@ def test_arraytype_eq():
assert data_type1 != ArrayType(iscalar_type, [10, 10])


def test_arraytype_copy():
'''Test the copy() method of ArrayType.'''
sym1 = DataSymbol("alimit", INTEGER_TYPE)
atype = ArrayType(INTEGER_TYPE, [Reference(sym1),
(Reference(sym1), Reference(sym1))])
acopy = atype.copy()
assert acopy == atype
assert acopy is not atype
# The Reference defining the upper bound should have been copied.
assert acopy.shape[0].upper is not atype.shape[0].upper
assert acopy.shape[1].lower is not atype.shape[1].lower
# But the new Reference should still refer to the same Symbol.
assert acopy.shape[0].upper.symbol is atype.shape[0].upper.symbol
assert acopy.shape[1].lower.symbol is atype.shape[1].lower.symbol
# When shape doesn't have set bounds.
sergisiso marked this conversation as resolved.
Show resolved Hide resolved
btype = ArrayType(INTEGER_TYPE, [ArrayType.Extent.ATTRIBUTE])
bcopy = btype.copy()
assert bcopy == btype
assert bcopy is not btype


# UnsupportedFortranType tests

def test_unsupported_fortran_type():
Expand Down
Loading
Loading