Updates for inlining with common variables#3389
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3389 +/- ##
========================================
Coverage 99.96% 99.96%
========================================
Files 389 389
Lines 54598 54710 +112
========================================
+ Hits 54579 54692 +113
+ Misses 19 18 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
arporter
left a comment
There was a problem hiding this comment.
Thanks @schreiberx, I learnt (or reminded myself) quite a lot about common blocks :-)
Unfortunately, it is currently impossible to do what you want safely as PSyclone does not capture which common a variable with a 'common block' interface is associated with. Since the names of common blocks are global and don't live in the same space as variables, I think extending PSyclone to at least capture those names and the symbols associated with them should be relatively straightforward. The code in question is:
PSyclone/src/psyclone/psyir/frontend/fparser2.py
Lines 2771 to 2798 in 8dd90f5
Given that we've learnt to our cost that safety needs to come first, I suggest we need to extend PSyclone's handling of common a little before we can safely handle common blocks when merging tables.
| if (isinstance(old_sym.datatype, UnsupportedFortranType) and | ||
| isinstance(self_sym.datatype, UnsupportedFortranType)): | ||
| if old_sym.datatype.declaration == self_sym.datatype.declaration: | ||
| # Identical COMMON-block markers – already present in self. |
There was a problem hiding this comment.
This comment isn't right - they are just the same unsupported type, not necessarily a common block.
There was a problem hiding this comment.
In fact, this is quite confusing. I think what we have here is that the two symbols that have a name collision both have a 'common block' interface and also happen to be of UnsupportedFortranType. I think (but could be wrong) that it would also be possible for them to be of a supported type. Unfortunately, we have no way of knowing whether they are both the (same) variable from the same common block.
There was a problem hiding this comment.
I'm afraid I'm still confused here. The check at L1038 means that only one or none of the clashing symbols has a common-block interface. Therefore, old_sym and self_sym can't both be common-block markers and in fact both might not be? Am I misunderstanding something?
| for s in self.symbols if s.is_commonblock) | ||
|
|
||
| # Normalised names of commonblock variables in other_table. Any | ||
| # overlap with self_cb_names means the COMMON block is already |
There was a problem hiding this comment.
Worth extending this comment to say that the same variable cannot appear in more than one common block so if they have the same name and a 'common block' interface then they must originate from the same common. I'm slightly uneasy as the actual names of symbols within a common block don't have to match: just their order and type:
So, two symbols from different scopes but with the same name and a common block interface are not guaranteed to be referring to the same thing.
There was a problem hiding this comment.
Consider:
integer :: var1, var2
common /block1/ var1, var2and (in a different scope):
real :: var1, var2
common /block2/ var1, var2as written, the code would assume that it is safe to ignore this clash because both var1 and var2 have a 'common block' interface. Unfortunately we currently make no attempt to capture the name of a common block - the whole common /.... statement is just reproduced blindly.
|
Since this is public, please also let me add that I informed you in advance that this PR is AI-generated and not proofread. |
Thanks Martin, that's a good point. I would have worded some of my comments differently if I knew you had personally written the code :-) |
|
This would solve #2554 |
|
@arporter Back to you. |
|
I updated the tests. |
|
Thanks Martin, if you could fix the linting so that the test suite runs through then I'll take another look. |
|
linting fixed. |
|
Hi @schreiberx, please could you fix the test failure and then make sure the coverage is OK too? |
… martin_common
|
Hi Andy, I fixed this and added some new code to address another issue with Croco. |
arporter
left a comment
There was a problem hiding this comment.
Nice, thanks Martin. It's impressive to see what can be done now.
There's a bit more code moving to do and I'm still a bit worried that we'll let through overlapping common-block declarations that use different local variable names.
Apart from that, it would be great to add compilation checks to all of the new tests you've added. You need to:
- Add the
tmp_pathtest fixture; output = fortran_writer(psyir)- Add a
Compile(tmp_path).string_compiles(output)
|
|
||
| self_sym = self.lookup(old_sym.name) | ||
| if old_sym.is_unresolved and self_sym.is_unresolved: | ||
| # Update after fixing issue #3392 |
There was a problem hiding this comment.
Since this routine assumes check_for_clashes has been called successfully, this comment doesn't belong here. However, we could do with a comment at 1039 to say that check_for_clashes must have determined that the two variables are from the same common block.
| if (isinstance(old_sym.datatype, UnsupportedFortranType) and | ||
| isinstance(self_sym.datatype, UnsupportedFortranType)): | ||
| if old_sym.datatype.declaration == self_sym.datatype.declaration: | ||
| # Identical COMMON-block markers – already present in self. |
There was a problem hiding this comment.
I'm afraid I'm still confused here. The check at L1038 means that only one or none of the clashing symbols has a common-block interface. Therefore, old_sym and self_sym can't both be common-block markers and in fact both might not be? Am I misunderstanding something?
| # We've dealt with Container symbols in _add_container_symbols. | ||
| continue | ||
|
|
||
| # Avoid duplicate COMMON-block marker symbols when multiple |
There was a problem hiding this comment.
This new logic belongs in _handle_symbol_clash - that's where we determine whether or not to take any action to resolve a clash.
| # (PARAMETER) symbols and redirect their references *before* the | ||
| # routine body is extracted, so that the extracted statements already | ||
| # carry references to the call-site symbols. | ||
| extra_skip: List[DataSymbol] = [] |
There was a problem hiding this comment.
You can use "list" rather than "List".
| extra_skip: List[DataSymbol] = [] | ||
| if not parameter_cloning: | ||
| extra_skip = self._redirect_duplicate_parameters( | ||
| table, routine, routine_table) |
There was a problem hiding this comment.
Please don't pass routine_table - it must always be the one that belongs to routine.
| assert result.count("parameter :: base_val") == 1 | ||
|
|
||
|
|
||
| def test_apply_parameter_cloning_false_unary_op_identical(fortran_reader, |
There was a problem hiding this comment.
I don't think you need this test - it's really just testing the __eq__ of DataNode.
| # negflag depends on flag which differs, so foo's negflag must also | ||
| # appear (renamed), and the inlined if must use foo's (renamed) negflag. | ||
| assert "negflag_1" in result | ||
| assert "if (negflag_1)" in result |
| constval = 7 | ||
| b = constval_1 | ||
|
|
||
| end subroutine bar""" in result |
|
|
||
| b = INT(constval_1) | ||
|
|
||
| end subroutine bar""" in result |
| end subroutine bar | ||
| subroutine foo(a) | ||
| logical, parameter :: flag = .false. | ||
| logical, parameter :: negflag = .not. flag |
There was a problem hiding this comment.
Worth doing a similar test where a kind parameter is duplicated but different between the call site and the callee, e.g.:
integer, parameter :: wp = kind(1.0d0)
real(kind=wp), parameter :: pi = 3.14592and the same in the callee but with:
integer, parameter :: wp = kind(1.0)|
Thanks for your feedback. I'm trying to summarize all changes briefly:
|
|
@arporter Next round :-) |
No description provided.