Skip to content

Commit 9537be7

Browse files
committed
Forward implementation for the definition use chains handling unsupported types correctly
1 parent 075f2ec commit 9537be7

3 files changed

Lines changed: 315 additions & 0 deletions

File tree

src/psyclone/psyir/tools/definition_use_chains.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
WhileLoop,
6464
PSyDataNode,
6565
)
66+
from psyclone.psyir.symbols import UnsupportedType, UnresolvedType
6667

6768

6869
class DefinitionUseChain:
@@ -590,6 +591,30 @@ def _compute_forward_uses(self, basic_block_list: list[Node]):
590591
self._defsout[sig].append(defs_out[sig])
591592
return
592593
for i, ref in enumerate(self._references[:]):
594+
# If the DUC's searched Reference is to an
595+
# UnsupportedType then
596+
# it needs to assume the worst case
597+
# for the CodeBlock if it contains an
598+
# UnsupportedType or UnresolvedType
599+
if isinstance(ref.datatype, UnsupportedType):
600+
for sym in reference.get_symbol_names():
601+
symbol = reference.scope.symbol_table.lookup(
602+
sym
603+
)
604+
if isinstance(symbol.datatype,
605+
(UnsupportedType,
606+
UnresolvedType)):
607+
# Assume the worst for a CodeBlock and we
608+
# count them as killed and defsout and
609+
# uses.
610+
sig = self._reference_signatures[i]
611+
if defs_out[sig] is not None:
612+
self._killed[sig].append(
613+
defs_out[sig]
614+
)
615+
defs_out[sig] = reference
616+
continue
617+
# Otherwise we check if ref appears in the CodeBlock.
593618
if (
594619
ref.symbol.name
595620
in reference.get_symbol_names()
@@ -671,6 +696,67 @@ def _compute_forward_uses(self, basic_block_list: list[Node]):
671696
# condition for example.
672697
if defs_out[sig] is None:
673698
self._uses[sig].append(reference)
699+
# If we have a reference whose datatype is an
700+
# UnsupportedType then we must assume it is aliased and
701+
# therefore can access any other UnsupportedType Reference.
702+
elif isinstance(reference.datatype, UnsupportedType):
703+
# If its accessed on the lhs of an assignment or as an
704+
# argument of a call then its
705+
# a write to every signature, otherwise its a read to
706+
# every signature.
707+
assign = reference.ancestor(Assignment)
708+
if assign is not None:
709+
if assign.lhs is reference:
710+
for i, sig in enumerate(
711+
self._reference_signatures
712+
):
713+
if not isinstance(
714+
self._references[i].datatype,
715+
UnsupportedType
716+
):
717+
continue
718+
if defs_out[sig] is not None:
719+
self._killed[sig].append(defs_out[sig])
720+
defs_out[sig] = reference
721+
# If the reference is on the rhs of an assignment
722+
# where the lhs is a write to any of the signatures
723+
# then this should be added to the uses for that
724+
# signature if its also an UnsupportedType.
725+
elif (
726+
any((defs_out[sig] is assign.lhs and
727+
len(self._killed[sig]) == 0) for sig in
728+
self._reference_signatures)
729+
):
730+
for i, sig in enumerate(
731+
self._reference_signatures
732+
):
733+
if not isinstance(
734+
self._references[i].datatype,
735+
UnsupportedType
736+
):
737+
continue
738+
if (defs_out[sig] is assign.lhs and
739+
len(self._killed[sig]) == 0):
740+
self._uses[sig].append(reference)
741+
elif reference.ancestor(Call):
742+
# If its an argument to a Call then it is a read to
743+
# all UnsupportedType references.
744+
for i, sig in enumerate(self._reference_signatures):
745+
if not isinstance(self._references[i].datatype,
746+
UnsupportedType):
747+
continue
748+
if defs_out[sig] is not None:
749+
self._killed[sig].append(defs_out[sig])
750+
defs_out[sig] = reference
751+
else:
752+
# Otherwise all other UnsupportedType References
753+
# count as a read to all UnsupportedType inputs.
754+
for i, sig in enumerate(self._reference_signatures):
755+
if not isinstance(self._references[i].datatype,
756+
UnsupportedType):
757+
continue
758+
if defs_out[sig] is None:
759+
self._uses[sig].append(reference)
674760
for sig in self._reference_signatures:
675761
if defs_out[sig] is not None:
676762
self._defsout[sig].append(defs_out[sig])
@@ -994,6 +1080,41 @@ def _compute_backward_uses(self, basic_block_list: list[Node]):
9941080
# condition for example.
9951081
if defs_out[sig] is None:
9961082
self._uses[sig].append(reference)
1083+
# FIXME We need this block still.
1084+
# elif isinstance(reference.datatype, UnsupportedType):
1085+
# # If its accessed on the lhs of an assignment or as an
1086+
# # argument of a call then its
1087+
# # a write to every signature, otherwise its a read to
1088+
# # every signature.
1089+
# assign = reference.ancestor(Assignment)
1090+
# if assign is not None:
1091+
# if assign.lhs is reference:
1092+
# for sig in self._reference_signatures:
1093+
# if defs_out[sig] is not None:
1094+
# self._killed[sig].append(defs_out[sig])
1095+
# defs_out[sig] = reference
1096+
# # If the reference is on the rhs of an assignment
1097+
# # where the lhs is a write to any of the signatures
1098+
# # then this should be added to the uses for that
1099+
# # signature.
1100+
# elif (
1101+
# any((defs_out[sig] is assign.lhs and
1102+
# len(self._killed[sig]) == 0) for sig in
1103+
# self._reference_signatures)
1104+
# ):
1105+
# for sig in self._reference_signatures:
1106+
# if (defs_out[sig] is assign.lhs and
1107+
# len(self._killed[sig]) == 0):
1108+
# self._uses[sig].append(reference)
1109+
# elif reference.ancestor(Call):
1110+
# for sig in self._reference_signatures:
1111+
# if defs_out[sig] is not None:
1112+
# self._killed[sig].append(defs_out[sig])
1113+
# defs_out[sig] = reference
1114+
# else:
1115+
# for sig in self._reference_signatures:
1116+
# if defs_out[sig] is None:
1117+
# self._uses[sig].append(reference)
9971118
for sig in self._reference_signatures:
9981119
if defs_out[sig] is not None:
9991120
self._defsout[sig].append(defs_out[sig])

src/psyclone/tests/psyir/tools/definition_use_chains_backward_dependence_test.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,3 +905,91 @@ def test_backward_accesses_nested_loop(fortran_reader):
905905
chains = DefinitionUseChain([lhs])
906906
reaches = chains.find_backward_accesses()[sig]
907907
assert len(reaches) == 1
908+
909+
910+
def test_backward_accesses_unsupported_type(fortran_reader):
911+
"""Test that if we have an unsupported type we get the expected
912+
worst-case behaviour from the DUCs."""
913+
# Test the result for assignments
914+
code = """
915+
subroutine x
916+
integer :: a
917+
integer, pointer :: c
918+
integer, target :: b
919+
920+
c = 1
921+
b = c
922+
a = 1
923+
end subroutine x"""
924+
925+
psyir = fortran_reader.psyir_from_source(code)
926+
routine = psyir.walk(Routine)[0]
927+
assign = routine.walk(Assignment)[-1]
928+
sig = assign.lhs.get_signature_and_indices()[0]
929+
chains = DefinitionUseChain(assign.lhs)
930+
reaches = chains.find_backward_accesses()[sig]
931+
# The result should be the access to b in the b = c
932+
# assignment.
933+
assert len(reaches) == 1
934+
assert reaches[0] is routine.walk(Assignment)[1].lhs
935+
936+
code = """
937+
subroutine x
938+
integer :: a
939+
integer, pointer :: c
940+
integer :: b
941+
942+
c = 1
943+
b = c
944+
a = 1
945+
end subroutine x"""
946+
psyir = fortran_reader.psyir_from_source(code)
947+
routine = psyir.walk(Routine)[0]
948+
assign = routine.walk(Assignment)[-1]
949+
sig = assign.lhs.get_signature_and_indices()[0]
950+
chains = DefinitionUseChain(assign.lhs)
951+
reaches = chains.find_backward_accesses()[sig]
952+
953+
# The result should be the two accesses to c in b = C
954+
# and C = 1.
955+
assert len(reaches) == 2
956+
assert reaches[0] is routine.walk(Assignment)[1].rhs
957+
assert reaches[1] is routine.walk(Assignment)[0].lhs
958+
959+
# Test that unsupported type arguments to pure subroutines
960+
# is always counted as an access.
961+
code = """
962+
pure subroutine test(a)
963+
integer :: a
964+
a = a * 2
965+
end subroutine test
966+
subroutine test2
967+
integer :: a
968+
integer, pointer :: b
969+
a = 1
970+
call test(b)
971+
a = 2
972+
end subroutine test2"""
973+
psyir = fortran_reader.psyir_from_source(code)
974+
routine = psyir.walk(Routine)[0]
975+
assign = routine.walk(Assignment)[-1]
976+
sig = assign.lhs.get_signature_and_indices()[0]
977+
chains = DefinitionUseChain(assign.lhs)
978+
reaches = chains.find_backward_accesses()[sig]
979+
980+
# The result should be the b argument to call test(b)
981+
assert len(reaches) == 1
982+
assert reaches[0] is routine.walk(Call)[0].arguments[0]
983+
984+
# Test that unsupported type references in non assignment
985+
# non-Call locations are counted as a read.
986+
code = """subroutine test
987+
integer :: a
988+
logical, pointer :: b
989+
990+
a = 1
991+
if (b) then
992+
end if
993+
a = 2
994+
end subroutine test"""
995+
assert False

src/psyclone/tests/psyir/tools/definition_use_chains_forward_dependence_test.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,112 @@ def test_definition_use_chains_return_statement(
10741074
assert reaches[3] is routine.walk(Assignment)[4].rhs.children[0]
10751075

10761076

1077+
def test_definition_use_chains_forward_accesses_unsupported_type(
1078+
fortran_reader,
1079+
):
1080+
'''Test the forward_accesses function always hits unsupported types.'''
1081+
# Test the code works as expected for unsupported types in an assignment.
1082+
code = """subroutine test
1083+
integer, pointer :: a
1084+
integer, pointer :: b
1085+
integer, target :: c
1086+
integer :: d
1087+
a = 1
1088+
d = 1
1089+
b => c
1090+
end subroutine test"""
1091+
psyir = fortran_reader.psyir_from_source(code)
1092+
routine = psyir.walk(Routine)[0]
1093+
# Start the chain from a = 1.
1094+
ref = routine.walk(Assignment)[0].lhs
1095+
sig = ref.get_signature_and_indices()[0]
1096+
chains = DefinitionUseChain(ref)
1097+
reaches = chains.find_forward_accesses()[sig]
1098+
assert len(reaches) == 2
1099+
# First reached is the rhs of the b => c assignment.
1100+
assert reaches[0] is routine.walk(Assignment)[2].rhs
1101+
# Second reached is the lhs of the b => c assignment.
1102+
assert reaches[1] is routine.walk(Assignment)[2].lhs
1103+
1104+
# Test the behaviour for a pure Call with an Unsupported type as
1105+
# an argument.
1106+
code = """
1107+
pure subroutine y(in)
1108+
integer :: in
1109+
in = in + 1
1110+
end subroutine y
1111+
subroutine x(a, b)
1112+
integer, target:: a
1113+
integer :: b
1114+
integer, pointer :: c
1115+
a = 2
1116+
b = 1
1117+
call y(c)
1118+
a = a + 2
1119+
c = 2
1120+
end subroutine"""
1121+
psyir = fortran_reader.psyir_from_source(code)
1122+
routine = psyir.walk(Routine)[1]
1123+
# Start the chain from a = 2.
1124+
ref = routine.walk(Assignment)[0].lhs
1125+
sig = ref.get_signature_and_indices()[0]
1126+
chains = DefinitionUseChain(ref)
1127+
reaches = chains.find_forward_accesses()[sig]
1128+
print(reaches[0].parent.debug_string())
1129+
assert len(reaches) == 1
1130+
# The result is the c argument of the pure call.
1131+
assert reaches[0] is routine.walk(Call)[0].arguments[0]
1132+
1133+
# Test we get the correct unsupported type access when
1134+
# we have a reference not as a child of an Assignment or Call.
1135+
code = """subroutine x
1136+
integer, target :: a
1137+
integer :: b
1138+
logical, pointer :: c
1139+
1140+
a = 1
1141+
if(c) then
1142+
b = 1
1143+
a = 2
1144+
end if
1145+
end subroutine x
1146+
"""
1147+
psyir = fortran_reader.psyir_from_source(code)
1148+
routine = psyir.walk(Routine)[0]
1149+
# Start the chain from a = 1.
1150+
ref = routine.walk(Assignment)[0].lhs
1151+
sig = ref.get_signature_and_indices()[0]
1152+
chains = DefinitionUseChain(ref)
1153+
reaches = chains.find_forward_accesses()[sig]
1154+
assert len(reaches) == 2
1155+
# The first reached is the condition of the if statement.
1156+
assert reaches[0] is routine.walk(IfBlock)[0].condition
1157+
# The second reached is the a = 2 assignment lhs
1158+
assert reaches[1] is routine.walk(Assignment)[2].lhs
1159+
1160+
# Test that if we have an unsupported type in a Codeblock
1161+
# then we count as a read/write for all the unsupported
1162+
# type input references.
1163+
code = """subroutine x
1164+
integer, target :: a
1165+
integer :: b
1166+
logical, pointer :: c
1167+
1168+
a = 1
1169+
print *, b, c
1170+
a = 2
1171+
end subroutine x"""
1172+
psyir = fortran_reader.psyir_from_source(code)
1173+
routine = psyir.walk(Routine)[0]
1174+
# Start the chain from a = 1.
1175+
ref = routine.walk(Assignment)[0].lhs
1176+
sig = ref.get_signature_and_indices()[0]
1177+
chains = DefinitionUseChain(ref)
1178+
reaches = chains.find_forward_accesses()[sig]
1179+
assert len(reaches) == 1
1180+
assert reaches[0] is routine.walk(CodeBlock)[0]
1181+
1182+
10771183
def test_definition_use_chains_forward_accesses_multiple_routines(
10781184
fortran_reader,
10791185
):

0 commit comments

Comments
 (0)