Repository navigation
Fixes for lineinfo emission #7196
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
Merged
sklam
merged 5 commits into
numba:master
from
stuartarchibald:fix/debug_lineinfo_refcounted
Jul 15, 2021
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
3631bdb
Fix up bug in lineinfo wrt decref
stuartarchibald f6bef9d
Handle lineinfo emission correctly across phi node stripping.
stuartarchibald 5c3d7ae
Test for decref, undefined and versioned variables.
stuartarchibald 2eacea6
Refactor unit tests to share common subprocess testing.
stuartarchibald 8f1c198
Respond to feedback
stuartarchibald File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Test for decref, undefined and versioned variables.
As title.
- Loading branch information
commit 5c3d7aefc8fdbb941470b7d3a272a8f279b8f5b1
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -78,6 +78,25 @@ def _get_metadata(self, fn, sig): | |||||
| metadata.append(line) | ||||||
| return metadata | ||||||
|
|
||||||
| def _subprocess_test_runner(self, test_name): | ||||||
| # Runs a named unit test, test_name (str), from this class in a | ||||||
| # subprocess with `NUMBA_OPT` set to 0 so as to get the most unoptimized | ||||||
| # llvm IR available. It then checks the test executed without error. | ||||||
| themod = self.__module__ | ||||||
| thecls = type(self).__name__ | ||||||
| injected_method = f'{themod}.{thecls}.{test_name}' | ||||||
| cmd = [sys.executable, '-m', 'numba.runtests', injected_method] | ||||||
| env_copy = os.environ.copy() | ||||||
| env_copy['SUBPROC_TEST'] = '1' | ||||||
| env_copy['NUMBA_OPT'] = '0' | ||||||
| status = subprocess.run(cmd, stdout=subprocess.PIPE, | ||||||
| stderr=subprocess.PIPE, timeout=60, | ||||||
| env=env_copy, universal_newlines=True) | ||||||
| self.assertEqual(status.returncode, 0) | ||||||
| self.assertIn('OK', status.stderr) | ||||||
| self.assertTrue('FAIL' not in status.stderr) | ||||||
| self.assertTrue('ERROR' not in status.stderr) | ||||||
|
|
||||||
| def test_DW_LANG(self): | ||||||
|
|
||||||
| @njit(debug=True) | ||||||
|
|
@@ -263,24 +282,121 @@ def test_DILocation_entry_blk(self): | |||||
| # as jitting literally anything at any point in the lifetime of the | ||||||
| # process ends up with a codegen at opt 3. This is not amenable to this | ||||||
| # test! | ||||||
| themod = self.__module__ | ||||||
| thecls = type(self).__name__ | ||||||
| injected_method = f'{themod}.{thecls}.test_DILocation_entry_blk_impl' | ||||||
| cmd = [sys.executable, '-m', 'numba.runtests', injected_method] | ||||||
| env_copy = os.environ.copy() | ||||||
| env_copy['SUBPROC_TEST'] = '1' | ||||||
| # This test relies on the CFG not being simplified as it checks the jump | ||||||
| # from the entry block to the first basic block. Force OPT as 0, if set | ||||||
| # via the env var the targetmachine and various pass managers all end up | ||||||
| # at OPT 0 and the IR is minimally transformed prior to lowering to ELF. | ||||||
| env_copy['NUMBA_OPT'] = '0' | ||||||
| status = subprocess.run(cmd, stdout=subprocess.PIPE, | ||||||
| stderr=subprocess.PIPE, timeout=60, | ||||||
| env=env_copy, universal_newlines=True) | ||||||
| self.assertEqual(status.returncode, 0) | ||||||
| self.assertIn('OK', status.stderr) | ||||||
| self.assertTrue('FAIL' not in status.stderr) | ||||||
| self.assertTrue('ERROR' not in status.stderr) | ||||||
| self._subprocess_test_runner('test_DILocation_entry_blk_impl') | ||||||
|
|
||||||
| @needs_subprocess | ||||||
| def test_DILocation_decref_impl(self): | ||||||
| """ This tests that decref's generated from `ir.Del`s as variables go | ||||||
| out of scope do not have debuginfo associated with them (the location of | ||||||
| `ir.Del` is an implementation detail). | ||||||
| """ | ||||||
|
|
||||||
| @njit(debug=True) | ||||||
| def sink(*x): | ||||||
| pass | ||||||
|
|
||||||
| # This function has many decrefs! | ||||||
| @njit(debug=True) | ||||||
| def foo(a): | ||||||
| x = (a, a) | ||||||
| if a[0] == 0: | ||||||
| sink(x) | ||||||
| return 12 | ||||||
| z = x[0][0] | ||||||
| return z | ||||||
|
|
||||||
| sig = (types.float64[::1],) | ||||||
| full_ir = self._get_llvmir(foo, sig=sig) | ||||||
|
|
||||||
| # make sure decref lines end with `meminfo.<number>)`. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| count = 0 | ||||||
| for line in full_ir.splitlines(): | ||||||
| line_stripped = line.strip() | ||||||
| if line_stripped.startswith('call void @NRT_decref'): | ||||||
| self.assertRegex(line, r'.*meminfo\.[0-9]+\)$') | ||||||
| count += 1 | ||||||
| self.assertTrue(count > 0) # make sure there were some decrefs! | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| def test_DILocation_decref(self): | ||||||
| # Test runner for test_DILocation_decref_impl, needs a subprocess | ||||||
| # with opt=0 to preserve decrefs. | ||||||
| self._subprocess_test_runner('test_DILocation_decref_impl') | ||||||
|
|
||||||
| def test_DILocation_undefined(self): | ||||||
| """ Tests that DILocation information for undefined vars is associated | ||||||
| with the line of the function definition (so it ends up in the prologue) | ||||||
| """ | ||||||
| @njit(debug=True) | ||||||
| def foo(n): | ||||||
| if n: | ||||||
| if n > 0: | ||||||
| c = 0 | ||||||
| return c | ||||||
| else: | ||||||
| # variable c is not defined in this branch | ||||||
| c += 1 | ||||||
| return c | ||||||
|
|
||||||
| sig = (types.intp,) | ||||||
| metadata = self._get_metadata(foo, sig=sig) | ||||||
| pysrc, pysrc_line_start = inspect.getsourcelines(foo) | ||||||
| # Looks for versions of variable "c" and captures the line number | ||||||
| expr = r'.*!DILocalVariable\(name: "c\$?[0-9]?",.*line: ([0-9]+),.*' | ||||||
| matcher = re.compile(expr) | ||||||
| associated_lines = set() | ||||||
| for md in metadata: | ||||||
| match = matcher.match(md) | ||||||
| if match: | ||||||
| groups = match.groups() | ||||||
| self.assertEqual(len(groups), 1) | ||||||
| associated_lines.add(int(groups[0])) | ||||||
| self.assertEqual(len(associated_lines), 3) # 3 versions of 'c' | ||||||
| self.assertIn(pysrc_line_start, associated_lines) | ||||||
|
|
||||||
| def test_DILocation_versioned_variables(self): | ||||||
| """ Tests that DILocation information for versions of variables matches | ||||||
| up to their definition site.""" | ||||||
| # Note: there's still something wrong in the DI/SSA naming, the ret c is | ||||||
| # associated with the logically first definition. | ||||||
|
|
||||||
| @njit(debug=True) | ||||||
| def foo(n): | ||||||
| if n: | ||||||
| c = 5 | ||||||
| else: | ||||||
| c = 1 | ||||||
| return c | ||||||
|
|
||||||
| sig = (types.intp,) | ||||||
| metadata = self._get_metadata(foo, sig=sig) | ||||||
| pysrc, pysrc_line_start = inspect.getsourcelines(foo) | ||||||
|
|
||||||
| # Looks for SSA versioned names i.e. <basename>$<version id> of the | ||||||
| # variable 'c' and captures the line | ||||||
| expr = r'.*!DILocalVariable\(name: "c\$[0-9]?",.*line: ([0-9]+),.*' | ||||||
| matcher = re.compile(expr) | ||||||
| associated_lines = set() | ||||||
| for md in metadata: | ||||||
| match = matcher.match(md) | ||||||
| if match: | ||||||
| groups = match.groups() | ||||||
| self.assertEqual(len(groups), 1) | ||||||
| associated_lines.add(int(groups[0])) | ||||||
| self.assertEqual(len(associated_lines), 2) # 2 SSA versioned names 'c' | ||||||
|
|
||||||
| # Now find the `c = ` lines in the python source | ||||||
| py_lines = set() | ||||||
| for ix, pyln in enumerate(pysrc): | ||||||
| if 'c = ' in pyln: | ||||||
| py_lines.add(ix + pysrc_line_start) | ||||||
| self.assertEqual(len(py_lines), 2) # 2 assignments to c | ||||||
|
|
||||||
| # check that the DILocation from the DI for `c` matches the python src | ||||||
| self.assertEqual(associated_lines, py_lines) | ||||||
|
|
||||||
|
|
||||||
| if __name__ == '__main__': | ||||||
|
|
||||||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a duplication of
numba/numba/tests/test_parfors.py
Lines 73 to 86 in 0c499bf
we should dedup here or in a immediate follow up PR