fpy2 0.0.2__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fpy2-0.0.3/PKG-INFO +113 -0
- fpy2-0.0.3/README.md +88 -0
- fpy2-0.0.3/fpy2/__init__.py +73 -0
- fpy2-0.0.3/fpy2/analysis/__init__.py +8 -0
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/analysis}/define_use.py +10 -10
- fpy2-0.0.3/fpy2/analysis/live_vars.py +225 -0
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/analysis}/reaching_defs.py +17 -14
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/analysis}/verify.py +20 -13
- fpy2-0.0.3/fpy2/ast/__init__.py +15 -0
- fpy2-0.0.3/fpy2/ast/context_inline.py +115 -0
- fpy2-0.0.3/fpy2/ast/define_use.py +86 -0
- {fpy2-0.0.2/fpy2/frontend → fpy2-0.0.3/fpy2/ast}/formatter.py +53 -19
- fpy2-0.0.3/fpy2/ast/fpyast.py +1070 -0
- fpy2-0.0.3/fpy2/ast/live_vars.py +209 -0
- {fpy2-0.0.2/fpy2/frontend → fpy2-0.0.3/fpy2/ast}/syntax_check.py +143 -45
- fpy2-0.0.3/fpy2/ast/visitor.py +572 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/backend/backend.py +3 -3
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/backend/fpc.py +99 -40
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/backend/fpy.py +105 -97
- fpy2-0.0.3/fpy2/decorator.py +146 -0
- fpy2-0.0.3/fpy2/fpc_context.py +156 -0
- fpy2-0.0.3/fpy2/frontend/__init__.py +9 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/frontend/fpc.py +83 -72
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/frontend/parser.py +105 -35
- fpy2-0.0.3/fpy2/function.py +136 -0
- fpy2-0.0.3/fpy2/interpret/__init__.py +9 -0
- fpy2-0.0.2/fpy2/runtime/titanic/interpreter.py → fpy2-0.0.3/fpy2/interpret/default.py +296 -188
- fpy2-0.0.3/fpy2/interpret/interpreter.py +66 -0
- fpy2-0.0.2/fpy2/runtime/native/interpreter.py → fpy2-0.0.3/fpy2/interpret/native.py +148 -96
- fpy2-0.0.2/fpy2/runtime/real/interpreter.py → fpy2-0.0.3/fpy2/interpret/real.py +140 -76
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/ir/__init__.py +3 -4
- {fpy2-0.0.2/fpy2/frontend → fpy2-0.0.3/fpy2/ir}/codegen.py +81 -35
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/ir/formatter.py +50 -18
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/ir/ir.py +113 -48
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/ir/visitor.py +168 -92
- fpy2-0.0.3/fpy2/math.py +489 -0
- fpy2-0.0.3/fpy2/number/__init__.py +217 -0
- fpy2-0.0.3/fpy2/number/context.py +206 -0
- fpy2-0.0.3/fpy2/number/ext.py +609 -0
- fpy2-0.0.3/fpy2/number/globals.py +45 -0
- fpy2-0.0.3/fpy2/number/gmp.py +529 -0
- fpy2-0.0.3/fpy2/number/ieee754.py +26 -0
- fpy2-0.0.3/fpy2/number/mp.py +150 -0
- fpy2-0.0.3/fpy2/number/mpb.py +371 -0
- fpy2-0.0.3/fpy2/number/mps.py +295 -0
- fpy2-0.0.3/fpy2/number/native.py +41 -0
- fpy2-0.0.3/fpy2/number/number.py +427 -0
- fpy2-0.0.3/fpy2/number/real.py +1016 -0
- fpy2-0.0.3/fpy2/number/round.py +65 -0
- {fpy2-0.0.2/fpy2/runtime → fpy2-0.0.3/fpy2}/profile/expr_profiler.py +6 -6
- {fpy2-0.0.2/fpy2/runtime → fpy2-0.0.3/fpy2}/profile/function_profiler.py +8 -6
- fpy2-0.0.3/fpy2/rewrite/__init__.py +9 -0
- fpy2-0.0.3/fpy2/rewrite/applier.py +234 -0
- fpy2-0.0.3/fpy2/rewrite/matcher.py +436 -0
- fpy2-0.0.3/fpy2/rewrite/pattern.py +99 -0
- fpy2-0.0.3/fpy2/rewrite/rewrite.py +233 -0
- fpy2-0.0.3/fpy2/rewrite/subst.py +61 -0
- fpy2-0.0.3/fpy2/runtime/__init__.py +1 -0
- fpy2-0.0.3/fpy2/runtime/env.py +35 -0
- fpy2-0.0.3/fpy2/runtime/real/__init__.py +2 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/runtime/real/interval.py +1 -1
- {fpy2-0.0.2/fpy2/runtime/sampling → fpy2-0.0.3/fpy2/sample}/sample.py +21 -12
- {fpy2-0.0.2/fpy2/runtime/sampling → fpy2-0.0.3/fpy2/sample}/table.py +11 -11
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/transform}/__init__.py +4 -3
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/transform}/for_bundling.py +18 -18
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/transform}/func_update.py +8 -8
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/transform}/simplify_if.py +41 -36
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/transform}/ssa.py +40 -33
- fpy2-0.0.3/fpy2/transform/unssa.py +196 -0
- {fpy2-0.0.2/fpy2/passes → fpy2-0.0.3/fpy2/transform}/while_bundling.py +18 -18
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/typing/__init__.pyi +3 -9
- fpy2-0.0.3/fpy2/utils/__init__.py +26 -0
- fpy2-0.0.3/fpy2/utils/bits.py +26 -0
- fpy2-0.0.3/fpy2/utils/decorator.py +105 -0
- fpy2-0.0.3/fpy2/utils/float_params.py +30 -0
- fpy2-0.0.3/fpy2/utils/fractions.py +122 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/utils/identifier.py +8 -1
- fpy2-0.0.3/fpy2/utils/iterator.py +15 -0
- fpy2-0.0.3/fpy2/utils/ordering.py +39 -0
- fpy2-0.0.3/fpy2.egg-info/PKG-INFO +113 -0
- fpy2-0.0.3/fpy2.egg-info/SOURCES.txt +98 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2.egg-info/requires.txt +3 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/pyproject.toml +8 -3
- fpy2-0.0.2/PKG-INFO +0 -39
- fpy2-0.0.2/README.md +0 -18
- fpy2-0.0.2/fpy2/__init__.py +0 -19
- fpy2-0.0.2/fpy2/frontend/__init__.py +0 -13
- fpy2-0.0.2/fpy2/frontend/decorator.py +0 -106
- fpy2-0.0.2/fpy2/frontend/definition.py +0 -149
- fpy2-0.0.2/fpy2/frontend/fpyast.py +0 -655
- fpy2-0.0.2/fpy2/frontend/live_vars.py +0 -201
- fpy2-0.0.2/fpy2/frontend/visitor.py +0 -208
- fpy2-0.0.2/fpy2/runtime/__init__.py +0 -15
- fpy2-0.0.2/fpy2/runtime/env.py +0 -23
- fpy2-0.0.2/fpy2/runtime/function.py +0 -110
- fpy2-0.0.2/fpy2/runtime/native/__init__.py +0 -1
- fpy2-0.0.2/fpy2/runtime/real/__init__.py +0 -2
- fpy2-0.0.2/fpy2/runtime/titanic/__init__.py +0 -1
- fpy2-0.0.2/fpy2/utils/__init__.py +0 -10
- fpy2-0.0.2/fpy2/utils/defaults.py +0 -11
- fpy2-0.0.2/fpy2/utils/fraction.py +0 -55
- fpy2-0.0.2/fpy2.egg-info/PKG-INFO +0 -39
- fpy2-0.0.2/fpy2.egg-info/SOURCES.txt +0 -68
- {fpy2-0.0.2 → fpy2-0.0.3}/LICENSE +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/backend/__init__.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/ir/types.py +0 -0
- {fpy2-0.0.2/fpy2/runtime → fpy2-0.0.3/fpy2}/profile/__init__.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/runtime/metric.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/runtime/real/rival_manager.py +0 -0
- /fpy2-0.0.2/fpy2/runtime/common.py → /fpy2-0.0.3/fpy2/runtime/trace.py +0 -0
- {fpy2-0.0.2/fpy2/runtime/sampling → fpy2-0.0.3/fpy2/sample}/__init__.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/typing/__init__.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/utils/compare.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/utils/error.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/utils/gensym.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/utils/location.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2/utils/string.py +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2.egg-info/dependency_links.txt +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/fpy2.egg-info/top_level.txt +0 -0
- {fpy2-0.0.2 → fpy2-0.0.3}/setup.cfg +0 -0
fpy2-0.0.3/PKG-INFO
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fpy2
|
|
3
|
+
Version: 0.0.3
|
|
4
|
+
Summary: An embedded DSL for numerical computing
|
|
5
|
+
Author-email: Brett Saiki <bsaiki@cs.washington.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bksaiki/fpy
|
|
8
|
+
Project-URL: Issues, https://github.com/bksaiki/fpy/issues
|
|
9
|
+
Project-URL: Documentation, https://fpy.readthedocs.io/
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: titanfp==0.1.1
|
|
16
|
+
Requires-Dist: gmpy2==2.2
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: mypy; extra == "dev"
|
|
19
|
+
Requires-Dist: ruff; extra == "dev"
|
|
20
|
+
Requires-Dist: build; extra == "dev"
|
|
21
|
+
Requires-Dist: twine; extra == "dev"
|
|
22
|
+
Requires-Dist: sphinx; extra == "dev"
|
|
23
|
+
Requires-Dist: sphinx-rtd-theme; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# FPy
|
|
27
|
+
|
|
28
|
+
An embedded Python DSL for specifying and simulating numerical algorithms.
|
|
29
|
+
|
|
30
|
+
Important lints:
|
|
31
|
+
- PyPI package: [fpy2](https://pypi.org/project/fpy2/)
|
|
32
|
+
- Documentation: [fpy2.readthedocs.io](https://fpy2.readthedocs.io/)
|
|
33
|
+
- GitHub: [fpy](https://github.com/bksaiki/fpy)
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Requirements:
|
|
38
|
+
- Python 3.11 or later
|
|
39
|
+
|
|
40
|
+
The following instructions assume a `bash`-like shell.
|
|
41
|
+
If you do not have a Python virtual environment,
|
|
42
|
+
create one using
|
|
43
|
+
```bash
|
|
44
|
+
python3 -m venv .env/
|
|
45
|
+
```
|
|
46
|
+
and activate it using using
|
|
47
|
+
```bash
|
|
48
|
+
source .env/bin/activate
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
To install a _frozen_ instance of FPy, run:
|
|
52
|
+
```bash
|
|
53
|
+
pip install .
|
|
54
|
+
```
|
|
55
|
+
or with `make`, run
|
|
56
|
+
```bash
|
|
57
|
+
make install
|
|
58
|
+
```
|
|
59
|
+
Note that this will not install the necessary dependencies for
|
|
60
|
+
development and installs a copy of the `fpy2` package.
|
|
61
|
+
If you checkout a different commit or branch, you will
|
|
62
|
+
need to reinstall FPy to overwrite the previous version.
|
|
63
|
+
|
|
64
|
+
To uninstall FPy, run:
|
|
65
|
+
```bash
|
|
66
|
+
pip uninstall fpy2
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Development
|
|
70
|
+
|
|
71
|
+
Developers of FPy should read this section since
|
|
72
|
+
installing FPy is actually different.
|
|
73
|
+
|
|
74
|
+
Requirements:
|
|
75
|
+
- Python 3.11 or later
|
|
76
|
+
- `make`
|
|
77
|
+
|
|
78
|
+
### Installation
|
|
79
|
+
|
|
80
|
+
If you do not have a Python virtual environment,
|
|
81
|
+
create one using
|
|
82
|
+
```bash
|
|
83
|
+
python3 -m venv .env/
|
|
84
|
+
```
|
|
85
|
+
and activate it using using
|
|
86
|
+
```bash
|
|
87
|
+
source .env/bin/activate
|
|
88
|
+
```
|
|
89
|
+
To install an instance of FPy for development, run:
|
|
90
|
+
```bash
|
|
91
|
+
pip install -e .[dev]
|
|
92
|
+
```
|
|
93
|
+
or with `make`, run
|
|
94
|
+
```bash
|
|
95
|
+
make install-dev
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Testing
|
|
99
|
+
|
|
100
|
+
There are a number of tests that can be run through
|
|
101
|
+
the `Makefile` including
|
|
102
|
+
```bash
|
|
103
|
+
make lint
|
|
104
|
+
```
|
|
105
|
+
to ensure formatting and type safety;
|
|
106
|
+
```bash
|
|
107
|
+
make unittest
|
|
108
|
+
```
|
|
109
|
+
to run the unit tests;
|
|
110
|
+
```bash
|
|
111
|
+
make infratest
|
|
112
|
+
```
|
|
113
|
+
to run the infrastructure tests.
|
fpy2-0.0.3/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# FPy
|
|
2
|
+
|
|
3
|
+
An embedded Python DSL for specifying and simulating numerical algorithms.
|
|
4
|
+
|
|
5
|
+
Important lints:
|
|
6
|
+
- PyPI package: [fpy2](https://pypi.org/project/fpy2/)
|
|
7
|
+
- Documentation: [fpy2.readthedocs.io](https://fpy2.readthedocs.io/)
|
|
8
|
+
- GitHub: [fpy](https://github.com/bksaiki/fpy)
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Requirements:
|
|
13
|
+
- Python 3.11 or later
|
|
14
|
+
|
|
15
|
+
The following instructions assume a `bash`-like shell.
|
|
16
|
+
If you do not have a Python virtual environment,
|
|
17
|
+
create one using
|
|
18
|
+
```bash
|
|
19
|
+
python3 -m venv .env/
|
|
20
|
+
```
|
|
21
|
+
and activate it using using
|
|
22
|
+
```bash
|
|
23
|
+
source .env/bin/activate
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
To install a _frozen_ instance of FPy, run:
|
|
27
|
+
```bash
|
|
28
|
+
pip install .
|
|
29
|
+
```
|
|
30
|
+
or with `make`, run
|
|
31
|
+
```bash
|
|
32
|
+
make install
|
|
33
|
+
```
|
|
34
|
+
Note that this will not install the necessary dependencies for
|
|
35
|
+
development and installs a copy of the `fpy2` package.
|
|
36
|
+
If you checkout a different commit or branch, you will
|
|
37
|
+
need to reinstall FPy to overwrite the previous version.
|
|
38
|
+
|
|
39
|
+
To uninstall FPy, run:
|
|
40
|
+
```bash
|
|
41
|
+
pip uninstall fpy2
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
Developers of FPy should read this section since
|
|
47
|
+
installing FPy is actually different.
|
|
48
|
+
|
|
49
|
+
Requirements:
|
|
50
|
+
- Python 3.11 or later
|
|
51
|
+
- `make`
|
|
52
|
+
|
|
53
|
+
### Installation
|
|
54
|
+
|
|
55
|
+
If you do not have a Python virtual environment,
|
|
56
|
+
create one using
|
|
57
|
+
```bash
|
|
58
|
+
python3 -m venv .env/
|
|
59
|
+
```
|
|
60
|
+
and activate it using using
|
|
61
|
+
```bash
|
|
62
|
+
source .env/bin/activate
|
|
63
|
+
```
|
|
64
|
+
To install an instance of FPy for development, run:
|
|
65
|
+
```bash
|
|
66
|
+
pip install -e .[dev]
|
|
67
|
+
```
|
|
68
|
+
or with `make`, run
|
|
69
|
+
```bash
|
|
70
|
+
make install-dev
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Testing
|
|
74
|
+
|
|
75
|
+
There are a number of tests that can be run through
|
|
76
|
+
the `Makefile` including
|
|
77
|
+
```bash
|
|
78
|
+
make lint
|
|
79
|
+
```
|
|
80
|
+
to ensure formatting and type safety;
|
|
81
|
+
```bash
|
|
82
|
+
make unittest
|
|
83
|
+
```
|
|
84
|
+
to run the unit tests;
|
|
85
|
+
```bash
|
|
86
|
+
make infratest
|
|
87
|
+
```
|
|
88
|
+
to run the infrastructure tests.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FPy is a library for simulating numerical programs
|
|
3
|
+
with many different number systems.
|
|
4
|
+
|
|
5
|
+
It provides an embedded DSL for specifying programs via its `@fpy` decorator.
|
|
6
|
+
The language has a runtime that can simulate programs
|
|
7
|
+
under different number systems and compilers to other languages.
|
|
8
|
+
|
|
9
|
+
The numbers library supports many different number types including:
|
|
10
|
+
|
|
11
|
+
- multiprecision floating point (`MPContext`)
|
|
12
|
+
- multiprecision floatingpoint with subnormalization (`MPSContext`)
|
|
13
|
+
- bounded, multiprecision floating point (`MPBContext`)
|
|
14
|
+
- IEEE 754 floating point (`IEEEContext`)
|
|
15
|
+
|
|
16
|
+
These number systems guarantee correct rounding via MPFR.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from .number import (
|
|
20
|
+
# number types
|
|
21
|
+
Float,
|
|
22
|
+
RealFloat,
|
|
23
|
+
# abstract context types
|
|
24
|
+
Context,
|
|
25
|
+
OrdinalContext,
|
|
26
|
+
SizedContext,
|
|
27
|
+
EncodableContext,
|
|
28
|
+
# concrete context types
|
|
29
|
+
ExtContext,
|
|
30
|
+
MPContext,
|
|
31
|
+
MPSContext,
|
|
32
|
+
MPBContext,
|
|
33
|
+
IEEEContext,
|
|
34
|
+
# rounding utilities
|
|
35
|
+
RoundingMode,
|
|
36
|
+
RoundingDirection, RM,
|
|
37
|
+
# encoding utilities
|
|
38
|
+
ExtNanKind,
|
|
39
|
+
# type aliases
|
|
40
|
+
FP256, FP128, FP64, FP32, FP16,
|
|
41
|
+
TF32, BF16,
|
|
42
|
+
S1E5M2, S1E4M3,
|
|
43
|
+
MX_E5M2, MX_E4M3, MX_E3M2, MX_E2M3, MX_E2M1,
|
|
44
|
+
FP8P1, FP8P2, FP8P3, FP8P4, FP8P5, FP8P6, FP8P7
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
from .fpc_context import FPCoreContext, NoSuchContextError
|
|
48
|
+
|
|
49
|
+
from .decorator import fpy, pattern
|
|
50
|
+
|
|
51
|
+
from .backend import (
|
|
52
|
+
Backend,
|
|
53
|
+
FPCoreCompiler,
|
|
54
|
+
FPYCompiler,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
from .interpret import (
|
|
58
|
+
Interpreter,
|
|
59
|
+
PythonInterpreter,
|
|
60
|
+
RealInterpreter,
|
|
61
|
+
DefaultInterpreter,
|
|
62
|
+
set_default_interpreter,
|
|
63
|
+
get_default_interpreter,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
from .function import Function
|
|
67
|
+
|
|
68
|
+
from .utils import (
|
|
69
|
+
fraction,
|
|
70
|
+
digits_to_fraction as digits,
|
|
71
|
+
decnum_to_fraction as decnum,
|
|
72
|
+
hexnum_to_fraction as hexnum,
|
|
73
|
+
)
|
|
@@ -4,11 +4,11 @@ from ..ir import *
|
|
|
4
4
|
|
|
5
5
|
class _DefineUseInstance(DefaultVisitor):
|
|
6
6
|
"""Per-IR instance of definition-use analysis"""
|
|
7
|
-
func:
|
|
7
|
+
func: FuncDef
|
|
8
8
|
uses: dict[NamedId, set[Var | PhiNode]]
|
|
9
9
|
done: bool
|
|
10
10
|
|
|
11
|
-
def __init__(self, func:
|
|
11
|
+
def __init__(self, func: FuncDef):
|
|
12
12
|
self.func = func
|
|
13
13
|
self.uses = {}
|
|
14
14
|
self.done = False
|
|
@@ -33,17 +33,17 @@ class _DefineUseInstance(DefaultVisitor):
|
|
|
33
33
|
self.uses[var] = set()
|
|
34
34
|
self._visit_expr(e.elt, ctx)
|
|
35
35
|
|
|
36
|
-
def
|
|
36
|
+
def _visit_simple_assign(self, stmt: SimpleAssign, ctx: None):
|
|
37
37
|
self._visit_expr(stmt.expr, ctx)
|
|
38
38
|
if isinstance(stmt.var, NamedId):
|
|
39
39
|
self.uses[stmt.var] = set()
|
|
40
40
|
|
|
41
|
-
def
|
|
41
|
+
def _visit_tuple_unpack(self, stmt: TupleUnpack, ctx: None):
|
|
42
42
|
self._visit_expr(stmt.expr, ctx)
|
|
43
43
|
for var in stmt.binding.names():
|
|
44
44
|
self.uses[var] = set()
|
|
45
45
|
|
|
46
|
-
def
|
|
46
|
+
def _visit_if1(self, stmt: If1Stmt, ctx: None):
|
|
47
47
|
self._visit_expr(stmt.cond, ctx)
|
|
48
48
|
self._visit_block(stmt.body, ctx)
|
|
49
49
|
for phi in stmt.phis:
|
|
@@ -51,7 +51,7 @@ class _DefineUseInstance(DefaultVisitor):
|
|
|
51
51
|
self.uses[phi.lhs].add(phi)
|
|
52
52
|
self.uses[phi.rhs].add(phi)
|
|
53
53
|
|
|
54
|
-
def
|
|
54
|
+
def _visit_if(self, stmt: IfStmt, ctx: None):
|
|
55
55
|
self._visit_expr(stmt.cond, ctx)
|
|
56
56
|
self._visit_block(stmt.ift, ctx)
|
|
57
57
|
self._visit_block(stmt.iff, ctx)
|
|
@@ -60,7 +60,7 @@ class _DefineUseInstance(DefaultVisitor):
|
|
|
60
60
|
self.uses[phi.lhs].add(phi)
|
|
61
61
|
self.uses[phi.rhs].add(phi)
|
|
62
62
|
|
|
63
|
-
def
|
|
63
|
+
def _visit_while(self, stmt: WhileStmt, ctx: None):
|
|
64
64
|
for phi in stmt.phis:
|
|
65
65
|
self.uses[phi.name] = set()
|
|
66
66
|
self.uses[phi.lhs].add(phi)
|
|
@@ -69,7 +69,7 @@ class _DefineUseInstance(DefaultVisitor):
|
|
|
69
69
|
for phi in stmt.phis:
|
|
70
70
|
self.uses[phi.rhs].add(phi)
|
|
71
71
|
|
|
72
|
-
def
|
|
72
|
+
def _visit_for(self, stmt: ForStmt, ctx: None):
|
|
73
73
|
self._visit_expr(stmt.iterable, ctx)
|
|
74
74
|
if isinstance(stmt.var, NamedId):
|
|
75
75
|
self.uses[stmt.var] = set()
|
|
@@ -80,7 +80,7 @@ class _DefineUseInstance(DefaultVisitor):
|
|
|
80
80
|
for phi in stmt.phis:
|
|
81
81
|
self.uses[phi.rhs].add(phi)
|
|
82
82
|
|
|
83
|
-
def _visit_function(self, func:
|
|
83
|
+
def _visit_function(self, func: FuncDef, ctx):
|
|
84
84
|
for arg in func.args:
|
|
85
85
|
if isinstance(arg.name, NamedId):
|
|
86
86
|
self.uses[arg.name] = set()
|
|
@@ -101,5 +101,5 @@ class DefineUse:
|
|
|
101
101
|
analysis_name = 'define_use'
|
|
102
102
|
|
|
103
103
|
@staticmethod
|
|
104
|
-
def analyze(func:
|
|
104
|
+
def analyze(func: FuncDef):
|
|
105
105
|
return _DefineUseInstance(func).analyze()
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Live variable analysis on the FPy IR"""
|
|
2
|
+
|
|
3
|
+
from typing import TypeAlias
|
|
4
|
+
|
|
5
|
+
from ..ir import *
|
|
6
|
+
|
|
7
|
+
_RetType: TypeAlias = set[NamedId]
|
|
8
|
+
|
|
9
|
+
class _LiveVars(ReduceVisitor):
|
|
10
|
+
"""Single-instance of the live variable analysis."""
|
|
11
|
+
e: FuncDef | StmtBlock | Stmt | Expr
|
|
12
|
+
|
|
13
|
+
def __init__(self, e: FuncDef | StmtBlock | Stmt | Expr):
|
|
14
|
+
self.e = e
|
|
15
|
+
|
|
16
|
+
def analyze(self):
|
|
17
|
+
"""Runs live-variable analysis on `self.e`."""
|
|
18
|
+
match self.e:
|
|
19
|
+
case FuncDef():
|
|
20
|
+
return self._visit_function(self.e, None)
|
|
21
|
+
case StmtBlock():
|
|
22
|
+
return self._visit_block(self.e, set())
|
|
23
|
+
case Stmt():
|
|
24
|
+
return self._visit_statement(self.e, set())
|
|
25
|
+
case Expr():
|
|
26
|
+
return self._visit_expr(self.e, None)
|
|
27
|
+
case _:
|
|
28
|
+
raise RuntimeError('unreachable')
|
|
29
|
+
|
|
30
|
+
def _visit_var(self, e: Var, ctx: None) -> _RetType:
|
|
31
|
+
return { e.name }
|
|
32
|
+
|
|
33
|
+
def _visit_bool(self, e: BoolVal, ctx: None) -> _RetType:
|
|
34
|
+
return set()
|
|
35
|
+
|
|
36
|
+
def _visit_foreign(self, e: ForeignVal, ctx: None) -> _RetType:
|
|
37
|
+
return set()
|
|
38
|
+
|
|
39
|
+
def _visit_decnum(self, e: Decnum, ctx: None) -> _RetType:
|
|
40
|
+
return set()
|
|
41
|
+
|
|
42
|
+
def _visit_hexnum(self, e: Hexnum, ctx: None) -> _RetType:
|
|
43
|
+
return set()
|
|
44
|
+
|
|
45
|
+
def _visit_integer(self, e: Integer, ctx: None) -> _RetType:
|
|
46
|
+
return set()
|
|
47
|
+
|
|
48
|
+
def _visit_rational(self, e: Rational, ctx: None) -> _RetType:
|
|
49
|
+
return set()
|
|
50
|
+
|
|
51
|
+
def _visit_constant(self, e: Constant, ctx: None) -> _RetType:
|
|
52
|
+
return set()
|
|
53
|
+
|
|
54
|
+
def _visit_digits(self, e: Digits, ctx: None) -> _RetType:
|
|
55
|
+
return set()
|
|
56
|
+
|
|
57
|
+
def _visit_unknown(self, e: UnknownCall, ctx: None) -> _RetType:
|
|
58
|
+
fvs = set()
|
|
59
|
+
for arg in e.children:
|
|
60
|
+
fvs.update(self._visit_expr(arg, ctx))
|
|
61
|
+
return fvs
|
|
62
|
+
|
|
63
|
+
def _visit_nary_expr(self, e: NaryExpr, ctx: None) -> _RetType:
|
|
64
|
+
fvs = set()
|
|
65
|
+
for arg in e.children:
|
|
66
|
+
fvs.update(self._visit_expr(arg, ctx))
|
|
67
|
+
return fvs
|
|
68
|
+
|
|
69
|
+
def _visit_compare(self, e: Compare, ctx: None) -> _RetType:
|
|
70
|
+
fvs = set()
|
|
71
|
+
for arg in e.children:
|
|
72
|
+
fvs.update(self._visit_expr(arg, ctx))
|
|
73
|
+
return fvs
|
|
74
|
+
|
|
75
|
+
def _visit_tuple_expr(self, e: TupleExpr, ctx: None) -> _RetType:
|
|
76
|
+
fvs = set()
|
|
77
|
+
for arg in e.children:
|
|
78
|
+
fvs.update(self._visit_expr(arg, ctx))
|
|
79
|
+
return fvs
|
|
80
|
+
|
|
81
|
+
def _visit_tuple_ref(self, e: TupleRef, ctx: None) -> _RetType:
|
|
82
|
+
fvs = self._visit_expr(e.value, ctx)
|
|
83
|
+
for arg in e.slices:
|
|
84
|
+
fvs.update(self._visit_expr(arg, ctx))
|
|
85
|
+
return fvs
|
|
86
|
+
|
|
87
|
+
def _visit_tuple_set(self, e: TupleSet, ctx: None) -> _RetType:
|
|
88
|
+
fvs = self._visit_expr(e.array, ctx)
|
|
89
|
+
for arg in e.slices:
|
|
90
|
+
fvs.update(self._visit_expr(arg, ctx))
|
|
91
|
+
fvs.update(self._visit_expr(e.value, ctx))
|
|
92
|
+
return fvs
|
|
93
|
+
|
|
94
|
+
def _visit_comp_expr(self, e: CompExpr, ctx: None) -> _RetType:
|
|
95
|
+
fvs = set()
|
|
96
|
+
for iterable in e.iterables:
|
|
97
|
+
fvs.update(self._visit_expr(iterable, ctx))
|
|
98
|
+
fvs.update(self._visit_expr(e.elt, ctx))
|
|
99
|
+
return fvs
|
|
100
|
+
|
|
101
|
+
def _visit_if_expr(self, e: IfExpr, ctx: None) -> _RetType:
|
|
102
|
+
fvs = self._visit_expr(e.cond, ctx)
|
|
103
|
+
fvs.update(self._visit_expr(e.ift, ctx))
|
|
104
|
+
fvs.update(self._visit_expr(e.iff, ctx))
|
|
105
|
+
return fvs
|
|
106
|
+
|
|
107
|
+
def _visit_simple_assign(self, stmt: SimpleAssign, ctx: _RetType) -> _RetType:
|
|
108
|
+
fvs = ctx | self._visit_expr(stmt.expr, None)
|
|
109
|
+
if isinstance(stmt.var, NamedId):
|
|
110
|
+
fvs.discard(stmt.var)
|
|
111
|
+
return fvs
|
|
112
|
+
|
|
113
|
+
def _visit_tuple_unpack(self, stmt: TupleUnpack, ctx: _RetType) -> _RetType:
|
|
114
|
+
fvs = ctx | self._visit_expr(stmt.expr, None)
|
|
115
|
+
for var in stmt.binding.names():
|
|
116
|
+
if isinstance(var, NamedId):
|
|
117
|
+
fvs.discard(var)
|
|
118
|
+
return fvs
|
|
119
|
+
|
|
120
|
+
def _visit_index_assign(self, stmt: IndexAssign, ctx: _RetType) -> _RetType:
|
|
121
|
+
fvs = ctx | self._visit_expr(stmt.expr, None)
|
|
122
|
+
for slice in stmt.slices:
|
|
123
|
+
fvs.update(self._visit_expr(slice, None))
|
|
124
|
+
fvs.add(stmt.var)
|
|
125
|
+
return fvs
|
|
126
|
+
|
|
127
|
+
def _visit_if1(self, stmt: If1Stmt, ctx: _RetType) -> _RetType:
|
|
128
|
+
ift_fvs = self._visit_block(stmt.body, ctx)
|
|
129
|
+
cond_fvs = self._visit_expr(stmt.cond, None)
|
|
130
|
+
return ctx | ift_fvs | cond_fvs
|
|
131
|
+
|
|
132
|
+
def _visit_if(self, stmt: IfStmt, ctx: _RetType) -> _RetType:
|
|
133
|
+
ift_fvs = self._visit_block(stmt.ift, ctx)
|
|
134
|
+
iff_fvs = self._visit_block(stmt.iff, ctx)
|
|
135
|
+
cond_fvs = self._visit_expr(stmt.cond, None)
|
|
136
|
+
return ift_fvs | iff_fvs | cond_fvs
|
|
137
|
+
|
|
138
|
+
def _visit_while(self, stmt: WhileStmt, ctx: _RetType) -> _RetType:
|
|
139
|
+
ctx = ctx | self._visit_block(stmt.body, ctx)
|
|
140
|
+
return ctx | self._visit_expr(stmt.cond, None)
|
|
141
|
+
|
|
142
|
+
def _visit_for(self, stmt: ForStmt, ctx: _RetType) -> _RetType:
|
|
143
|
+
body_fvs = self._visit_block(stmt.body, ctx)
|
|
144
|
+
if isinstance(stmt.var, NamedId):
|
|
145
|
+
body_fvs -= { stmt.var }
|
|
146
|
+
ctx = ctx | body_fvs
|
|
147
|
+
return ctx | self._visit_expr(stmt.iterable, None)
|
|
148
|
+
|
|
149
|
+
def _visit_foreign_attr(self, e: ForeignAttribute, ctx: None) -> _RetType:
|
|
150
|
+
return { e.name }
|
|
151
|
+
|
|
152
|
+
def _visit_context_expr(self, e: ContextExpr, ctx: None) -> _RetType:
|
|
153
|
+
match e.ctor:
|
|
154
|
+
case Var():
|
|
155
|
+
fvs = self._visit_var(e.ctor, None)
|
|
156
|
+
case ForeignAttribute():
|
|
157
|
+
fvs = self._visit_foreign_attr(e.ctor, None)
|
|
158
|
+
case _:
|
|
159
|
+
raise RuntimeError(f'unreachable {e.ctor}')
|
|
160
|
+
|
|
161
|
+
for arg in e.args:
|
|
162
|
+
match arg:
|
|
163
|
+
case ForeignAttribute():
|
|
164
|
+
fvs |= self._visit_foreign_attr(arg, None)
|
|
165
|
+
case _:
|
|
166
|
+
fvs |= self._visit_expr(arg, None)
|
|
167
|
+
|
|
168
|
+
return fvs
|
|
169
|
+
|
|
170
|
+
def _visit_context(self, stmt: ContextStmt, ctx: _RetType) -> _RetType:
|
|
171
|
+
body_fvs = self._visit_block(stmt.body, ctx)
|
|
172
|
+
if isinstance(stmt.name, NamedId):
|
|
173
|
+
body_fvs -= { stmt.name }
|
|
174
|
+
ctx = ctx | body_fvs
|
|
175
|
+
match stmt.ctx:
|
|
176
|
+
case Var():
|
|
177
|
+
ctx |= self._visit_var(stmt.ctx, None)
|
|
178
|
+
case ContextExpr():
|
|
179
|
+
ctx |= self._visit_context_expr(stmt.ctx, None)
|
|
180
|
+
case _:
|
|
181
|
+
raise RuntimeError(f'unreachable {stmt.ctx}')
|
|
182
|
+
return ctx
|
|
183
|
+
|
|
184
|
+
def _visit_assert(self, stmt: AssertStmt, ctx: _RetType) -> _RetType:
|
|
185
|
+
return ctx | self._visit_expr(stmt.test, None)
|
|
186
|
+
|
|
187
|
+
def _visit_effect(self, stmt: EffectStmt, ctx: _RetType) -> _RetType:
|
|
188
|
+
return ctx | self._visit_expr(stmt.expr, None)
|
|
189
|
+
|
|
190
|
+
def _visit_return(self, stmt: ReturnStmt, ctx: _RetType) -> _RetType:
|
|
191
|
+
return self._visit_expr(stmt.expr, None)
|
|
192
|
+
|
|
193
|
+
def _visit_block(self, block: StmtBlock, ctx: _RetType) -> _RetType:
|
|
194
|
+
fvs = ctx.copy()
|
|
195
|
+
for stmt in reversed(block.stmts):
|
|
196
|
+
if isinstance(stmt, ReturnStmt):
|
|
197
|
+
fvs = set()
|
|
198
|
+
fvs = self._visit_statement(stmt, fvs)
|
|
199
|
+
return fvs
|
|
200
|
+
|
|
201
|
+
def _visit_function(self, func: FuncDef, ctx: None) -> _RetType:
|
|
202
|
+
fvs = self._visit_block(func.body, set())
|
|
203
|
+
for arg in func.args:
|
|
204
|
+
if isinstance(arg, NamedId):
|
|
205
|
+
fvs.remove(arg)
|
|
206
|
+
return fvs
|
|
207
|
+
|
|
208
|
+
# overriden for typing hint
|
|
209
|
+
def _visit_expr(self, e: Expr, ctx: None) -> _RetType:
|
|
210
|
+
return super()._visit_expr(e, ctx)
|
|
211
|
+
|
|
212
|
+
# overriden for typing hint
|
|
213
|
+
def _visit_statement(self, e: Stmt, ctx: _RetType) -> _RetType:
|
|
214
|
+
return super()._visit_statement(e, ctx)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class LiveVars:
|
|
218
|
+
"""Live variable analysis for the FPy AST."""
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def analyze(e: FuncDef | StmtBlock | Stmt | Expr):
|
|
222
|
+
"""Analyze the live variables in a function."""
|
|
223
|
+
if not isinstance(e, (FuncDef, StmtBlock, Stmt, Expr)):
|
|
224
|
+
raise TypeError(f'Expected FunctionDef, Block, Stmt or Expr, got {type(e)}')
|
|
225
|
+
return _LiveVars(e).analyze()
|
|
@@ -18,10 +18,10 @@ _RetType = tuple[set[NamedId], set[NamedId]]
|
|
|
18
18
|
|
|
19
19
|
class _ReachingDefsInstance(DefaultVisitor):
|
|
20
20
|
"""Single-use instance of the reaching definitions analysis."""
|
|
21
|
-
func:
|
|
22
|
-
reaches: dict[
|
|
21
|
+
func: FuncDef
|
|
22
|
+
reaches: dict[StmtBlock, Reach]
|
|
23
23
|
|
|
24
|
-
def __init__(self, func:
|
|
24
|
+
def __init__(self, func: FuncDef):
|
|
25
25
|
self.func = func
|
|
26
26
|
self.reaches = {}
|
|
27
27
|
|
|
@@ -29,23 +29,23 @@ class _ReachingDefsInstance(DefaultVisitor):
|
|
|
29
29
|
self._visit_function(self.func, None)
|
|
30
30
|
return self.reaches
|
|
31
31
|
|
|
32
|
-
def
|
|
32
|
+
def _visit_simple_assign(self, stmt: SimpleAssign, ctx: _StmtCtx) -> _RetType:
|
|
33
33
|
defs_in, kill_in = ctx
|
|
34
34
|
defs_out = { *defs_in, stmt.var } if isinstance(stmt.var, NamedId) else defs_in
|
|
35
35
|
kill_out = kill_in | (defs_in & { stmt.var })
|
|
36
36
|
return defs_out, kill_out
|
|
37
37
|
|
|
38
|
-
def
|
|
38
|
+
def _visit_tuple_unpack(self, stmt: TupleUnpack, ctx: _StmtCtx) -> _RetType:
|
|
39
39
|
defs_in, kill_in = ctx
|
|
40
40
|
gen = set(stmt.binding.names())
|
|
41
41
|
defs_out = defs_in | gen
|
|
42
42
|
kill_out = kill_in | (defs_in & gen)
|
|
43
43
|
return defs_out, kill_out
|
|
44
44
|
|
|
45
|
-
def
|
|
45
|
+
def _visit_index_assign(self, stmt: IndexAssign, ctx: _StmtCtx) -> _RetType:
|
|
46
46
|
return ctx
|
|
47
47
|
|
|
48
|
-
def
|
|
48
|
+
def _visit_if1(self, stmt: If1Stmt, ctx: _StmtCtx) -> _RetType:
|
|
49
49
|
defs_in, kill_in = ctx
|
|
50
50
|
_, block_kill = self._visit_block(stmt.body, defs_in)
|
|
51
51
|
|
|
@@ -53,7 +53,7 @@ class _ReachingDefsInstance(DefaultVisitor):
|
|
|
53
53
|
kill_out = kill_in | (defs_in & block_kill)
|
|
54
54
|
return defs_out, kill_out
|
|
55
55
|
|
|
56
|
-
def
|
|
56
|
+
def _visit_if(self, stmt: IfStmt, ctx: _StmtCtx) -> _RetType:
|
|
57
57
|
defs_in, kill_in = ctx
|
|
58
58
|
ift_defs, ift_kill = self._visit_block(stmt.ift, defs_in)
|
|
59
59
|
iff_defs, iff_kill = self._visit_block(stmt.iff, defs_in)
|
|
@@ -62,7 +62,7 @@ class _ReachingDefsInstance(DefaultVisitor):
|
|
|
62
62
|
kill_out = kill_in | (defs_in & (ift_kill | iff_kill))
|
|
63
63
|
return defs_out, kill_out
|
|
64
64
|
|
|
65
|
-
def
|
|
65
|
+
def _visit_while(self, stmt: WhileStmt, ctx: _StmtCtx) -> _RetType:
|
|
66
66
|
defs_in, kill_in = ctx
|
|
67
67
|
_, block_kill = self._visit_block(stmt.body, defs_in)
|
|
68
68
|
|
|
@@ -70,7 +70,7 @@ class _ReachingDefsInstance(DefaultVisitor):
|
|
|
70
70
|
kill_out = kill_in | (defs_in & block_kill)
|
|
71
71
|
return defs_out, kill_out
|
|
72
72
|
|
|
73
|
-
def
|
|
73
|
+
def _visit_for(self, stmt: ForStmt, ctx: _StmtCtx) -> _RetType:
|
|
74
74
|
defs_in, kill_in = ctx
|
|
75
75
|
defs = { *defs_in, stmt.var } if isinstance(stmt.var, NamedId) else defs_in
|
|
76
76
|
_, block_kill = self._visit_block(stmt.body, defs)
|
|
@@ -91,10 +91,13 @@ class _ReachingDefsInstance(DefaultVisitor):
|
|
|
91
91
|
def _visit_assert(self, stmt: AssertStmt, ctx: _StmtCtx) -> _RetType:
|
|
92
92
|
return ctx
|
|
93
93
|
|
|
94
|
-
def
|
|
94
|
+
def _visit_effect(self, stmt: EffectStmt, ctx: _StmtCtx) -> _RetType:
|
|
95
95
|
return ctx
|
|
96
96
|
|
|
97
|
-
def
|
|
97
|
+
def _visit_return(self, stmt: ReturnStmt, ctx: _StmtCtx) -> _RetType:
|
|
98
|
+
return ctx
|
|
99
|
+
|
|
100
|
+
def _visit_block(self, block: StmtBlock, ctx: _BlockCtx):
|
|
98
101
|
reach_in = ctx.copy()
|
|
99
102
|
|
|
100
103
|
defs: set[NamedId] = reach_in.copy()
|
|
@@ -106,7 +109,7 @@ class _ReachingDefsInstance(DefaultVisitor):
|
|
|
106
109
|
self.reaches[block] = Reach(reach_in, reach_out, kill)
|
|
107
110
|
return defs, kill
|
|
108
111
|
|
|
109
|
-
def _visit_function(self, func:
|
|
112
|
+
def _visit_function(self, func: FuncDef, _: None):
|
|
110
113
|
ctx: set[NamedId] = set()
|
|
111
114
|
for arg in func.args:
|
|
112
115
|
if isinstance(arg.name, NamedId):
|
|
@@ -125,5 +128,5 @@ class ReachingDefs:
|
|
|
125
128
|
analysis_name = 'reaching_defs'
|
|
126
129
|
|
|
127
130
|
@staticmethod
|
|
128
|
-
def analyze(func:
|
|
131
|
+
def analyze(func: FuncDef) -> dict[StmtBlock, Reach]:
|
|
129
132
|
return _ReachingDefsInstance(func).analyze()
|