fpy2 0.0.1__tar.gz → 0.0.2__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.2/PKG-INFO +39 -0
- fpy2-0.0.2/README.md +18 -0
- fpy2-0.0.2/fpy2/__init__.py +19 -0
- fpy2-0.0.2/fpy2/backend/__init__.py +5 -0
- fpy2-0.0.2/fpy2/backend/backend.py +18 -0
- fpy2-0.0.2/fpy2/backend/fpc.py +480 -0
- fpy2-0.0.2/fpy2/backend/fpy.py +298 -0
- fpy2-0.0.2/fpy2/frontend/__init__.py +13 -0
- fpy2-0.0.2/fpy2/frontend/codegen.py +278 -0
- fpy2-0.0.2/fpy2/frontend/decorator.py +106 -0
- fpy2-0.0.2/fpy2/frontend/definition.py +149 -0
- fpy2-0.0.2/fpy2/frontend/formatter.py +250 -0
- fpy2-0.0.2/fpy2/frontend/fpc.py +713 -0
- fpy2-0.0.2/fpy2/frontend/fpyast.py +655 -0
- fpy2-0.0.2/fpy2/frontend/live_vars.py +201 -0
- fpy2-0.0.2/fpy2/frontend/parser.py +756 -0
- fpy2-0.0.2/fpy2/frontend/syntax_check.py +294 -0
- fpy2-0.0.2/fpy2/frontend/visitor.py +208 -0
- fpy2-0.0.2/fpy2/ir/__init__.py +13 -0
- fpy2-0.0.2/fpy2/ir/formatter.py +269 -0
- fpy2-0.0.2/fpy2/ir/ir.py +739 -0
- fpy2-0.0.2/fpy2/ir/types.py +29 -0
- fpy2-0.0.2/fpy2/ir/visitor.py +578 -0
- fpy2-0.0.2/fpy2/passes/__init__.py +9 -0
- fpy2-0.0.2/fpy2/passes/define_use.py +105 -0
- fpy2-0.0.2/fpy2/passes/for_bundling.py +109 -0
- fpy2-0.0.2/fpy2/passes/func_update.py +45 -0
- fpy2-0.0.2/fpy2/passes/reaching_defs.py +129 -0
- fpy2-0.0.2/fpy2/passes/simplify_if.py +109 -0
- fpy2-0.0.2/fpy2/passes/ssa.py +330 -0
- fpy2-0.0.2/fpy2/passes/verify.py +221 -0
- fpy2-0.0.2/fpy2/passes/while_bundling.py +125 -0
- fpy2-0.0.2/fpy2/runtime/__init__.py +15 -0
- fpy2-0.0.2/fpy2/runtime/common.py +12 -0
- fpy2-0.0.2/fpy2/runtime/env.py +23 -0
- fpy2-0.0.2/fpy2/runtime/function.py +110 -0
- fpy2-0.0.2/fpy2/runtime/metric.py +32 -0
- fpy2-0.0.2/fpy2/runtime/native/__init__.py +1 -0
- fpy2-0.0.2/fpy2/runtime/native/interpreter.py +512 -0
- fpy2-0.0.2/fpy2/runtime/profile/__init__.py +2 -0
- fpy2-0.0.2/fpy2/runtime/profile/expr_profiler.py +240 -0
- fpy2-0.0.2/fpy2/runtime/profile/function_profiler.py +138 -0
- fpy2-0.0.2/fpy2/runtime/real/__init__.py +2 -0
- fpy2-0.0.2/fpy2/runtime/real/interpreter.py +583 -0
- fpy2-0.0.2/fpy2/runtime/real/interval.py +61 -0
- fpy2-0.0.2/fpy2/runtime/real/rival_manager.py +142 -0
- fpy2-0.0.2/fpy2/runtime/sampling/__init__.py +5 -0
- fpy2-0.0.2/fpy2/runtime/sampling/sample.py +171 -0
- fpy2-0.0.2/fpy2/runtime/sampling/table.py +341 -0
- fpy2-0.0.2/fpy2/runtime/titanic/__init__.py +1 -0
- fpy2-0.0.2/fpy2/runtime/titanic/interpreter.py +616 -0
- fpy2-0.0.2/fpy2/typing/__init__.py +20 -0
- fpy2-0.0.2/fpy2/typing/__init__.pyi +103 -0
- fpy2-0.0.2/fpy2/utils/__init__.py +10 -0
- fpy2-0.0.2/fpy2/utils/compare.py +44 -0
- fpy2-0.0.2/fpy2/utils/defaults.py +11 -0
- fpy2-0.0.2/fpy2/utils/error.py +12 -0
- fpy2-0.0.2/fpy2/utils/fraction.py +55 -0
- fpy2-0.0.2/fpy2/utils/gensym.py +79 -0
- fpy2-0.0.2/fpy2/utils/identifier.py +86 -0
- fpy2-0.0.2/fpy2/utils/location.py +31 -0
- fpy2-0.0.2/fpy2/utils/string.py +21 -0
- fpy2-0.0.2/fpy2.egg-info/PKG-INFO +39 -0
- fpy2-0.0.2/fpy2.egg-info/SOURCES.txt +68 -0
- fpy2-0.0.2/fpy2.egg-info/requires.txt +7 -0
- {fpy2-0.0.1 → fpy2-0.0.2}/pyproject.toml +20 -6
- fpy2-0.0.1/PKG-INFO +0 -17
- fpy2-0.0.1/README.md +0 -3
- fpy2-0.0.1/src/fpy2/__init__.py +0 -0
- fpy2-0.0.1/src/fpy2.egg-info/PKG-INFO +0 -17
- fpy2-0.0.1/src/fpy2.egg-info/SOURCES.txt +0 -8
- {fpy2-0.0.1 → fpy2-0.0.2}/LICENSE +0 -0
- {fpy2-0.0.1/src → fpy2-0.0.2}/fpy2.egg-info/dependency_links.txt +0 -0
- {fpy2-0.0.1/src → fpy2-0.0.2}/fpy2.egg-info/top_level.txt +0 -0
- {fpy2-0.0.1 → fpy2-0.0.2}/setup.cfg +0 -0
fpy2-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fpy2
|
|
3
|
+
Version: 0.0.2
|
|
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
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: titanfp==0.1.1
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: mypy; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff; extra == "dev"
|
|
18
|
+
Requires-Dist: build; extra == "dev"
|
|
19
|
+
Requires-Dist: twine; extra == "dev"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# FPy
|
|
23
|
+
|
|
24
|
+
An embedded Python DSL for numerical computing.
|
|
25
|
+
|
|
26
|
+
## Building
|
|
27
|
+
|
|
28
|
+
Create a virtual environment:
|
|
29
|
+
```bash
|
|
30
|
+
python3 -m venv .env/
|
|
31
|
+
```
|
|
32
|
+
and activate it using using
|
|
33
|
+
```bash
|
|
34
|
+
source .env/bin/activate
|
|
35
|
+
```
|
|
36
|
+
Then, install the dependencies:
|
|
37
|
+
```bash
|
|
38
|
+
pip install -e .[dev]
|
|
39
|
+
```
|
fpy2-0.0.2/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# FPy
|
|
2
|
+
|
|
3
|
+
An embedded Python DSL for numerical computing.
|
|
4
|
+
|
|
5
|
+
## Building
|
|
6
|
+
|
|
7
|
+
Create a virtual environment:
|
|
8
|
+
```bash
|
|
9
|
+
python3 -m venv .env/
|
|
10
|
+
```
|
|
11
|
+
and activate it using using
|
|
12
|
+
```bash
|
|
13
|
+
source .env/bin/activate
|
|
14
|
+
```
|
|
15
|
+
Then, install the dependencies:
|
|
16
|
+
```bash
|
|
17
|
+
pip install -e .[dev]
|
|
18
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .frontend import fpy
|
|
2
|
+
from .backend import (
|
|
3
|
+
Backend,
|
|
4
|
+
FPCoreCompiler,
|
|
5
|
+
FPYCompiler
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from .runtime import (
|
|
9
|
+
Function,
|
|
10
|
+
Interpreter,
|
|
11
|
+
PythonInterpreter,
|
|
12
|
+
RealInterpreter,
|
|
13
|
+
TitanicInterpreter,
|
|
14
|
+
set_default_interpreter,
|
|
15
|
+
get_default_interpreter,
|
|
16
|
+
FunctionProfiler,
|
|
17
|
+
ExprProfiler
|
|
18
|
+
)
|
|
19
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FPy backend abstraction.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
from ..runtime import Function
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Backend(ABC):
|
|
11
|
+
"""
|
|
12
|
+
Abstract base class for FPy backends.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def compile(self, func: Function):
|
|
17
|
+
"""Compiles `func` to the backend's target language."""
|
|
18
|
+
raise NotImplementedError('virtual method')
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"""Compilation from FPy IR to FPCore"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import titanfp.fpbench.fpcast as fpc
|
|
6
|
+
|
|
7
|
+
from ..runtime import Function
|
|
8
|
+
from ..passes import *
|
|
9
|
+
from ..ir import *
|
|
10
|
+
from ..utils import Gensym
|
|
11
|
+
|
|
12
|
+
from .backend import Backend
|
|
13
|
+
|
|
14
|
+
_op_table = {
|
|
15
|
+
'+': fpc.Add,
|
|
16
|
+
'-': fpc.Sub,
|
|
17
|
+
'*': fpc.Mul,
|
|
18
|
+
'/': fpc.Div,
|
|
19
|
+
'fabs': fpc.Fabs,
|
|
20
|
+
'sqrt': fpc.Sqrt,
|
|
21
|
+
'fma': fpc.Fma,
|
|
22
|
+
'neg': fpc.Neg,
|
|
23
|
+
'copysign': fpc.Copysign,
|
|
24
|
+
'fdim': fpc.Fdim,
|
|
25
|
+
'fmax': fpc.Fmax,
|
|
26
|
+
'fmin': fpc.Fmin,
|
|
27
|
+
'fmod': fpc.Fmod,
|
|
28
|
+
'remainder': fpc.Remainder,
|
|
29
|
+
'hypot': fpc.Hypot,
|
|
30
|
+
'cbrt': fpc.Cbrt,
|
|
31
|
+
'ceil': fpc.Ceil,
|
|
32
|
+
'floor': fpc.Floor,
|
|
33
|
+
'nearbyint': fpc.Nearbyint,
|
|
34
|
+
'round': fpc.Round,
|
|
35
|
+
'trunc': fpc.Trunc,
|
|
36
|
+
'acos': fpc.Acos,
|
|
37
|
+
'asin': fpc.Asin,
|
|
38
|
+
'atan': fpc.Atan,
|
|
39
|
+
'atan2': fpc.Atan2,
|
|
40
|
+
'cos': fpc.Cos,
|
|
41
|
+
'sin': fpc.Sin,
|
|
42
|
+
'tan': fpc.Tan,
|
|
43
|
+
'acosh': fpc.Acosh,
|
|
44
|
+
'asinh': fpc.Asinh,
|
|
45
|
+
'atanh': fpc.Atanh,
|
|
46
|
+
'cosh': fpc.Cosh,
|
|
47
|
+
'sinh': fpc.Sinh,
|
|
48
|
+
'tanh': fpc.Tanh,
|
|
49
|
+
'exp': fpc.Exp,
|
|
50
|
+
'exp2': fpc.Exp2,
|
|
51
|
+
'expm1': fpc.Expm1,
|
|
52
|
+
'log': fpc.Log,
|
|
53
|
+
'log10': fpc.Log10,
|
|
54
|
+
'log1p': fpc.Log1p,
|
|
55
|
+
'log2': fpc.Log2,
|
|
56
|
+
'pow': fpc.Pow,
|
|
57
|
+
'erf': fpc.Erf,
|
|
58
|
+
'erfc': fpc.Erfc,
|
|
59
|
+
'lgamma': fpc.Lgamma,
|
|
60
|
+
'tgamma': fpc.Tgamma,
|
|
61
|
+
'isfinite': fpc.Isfinite,
|
|
62
|
+
'isinf': fpc.Isinf,
|
|
63
|
+
'isnan': fpc.Isnan,
|
|
64
|
+
'isnormal': fpc.Isnormal,
|
|
65
|
+
'signbit': fpc.Signbit,
|
|
66
|
+
'not': fpc.Not,
|
|
67
|
+
'or': fpc.Or,
|
|
68
|
+
'and': fpc.And,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
class FPCoreCompileError(Exception):
|
|
72
|
+
"""Any FPCore compilation error"""
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
def _nary_mul(args: list[fpc.Expr]):
|
|
76
|
+
assert args != [], 'must be at least 1 argument'
|
|
77
|
+
if len(args) == 1:
|
|
78
|
+
return args[0]
|
|
79
|
+
else:
|
|
80
|
+
e = fpc.Mul(args[0], args[1])
|
|
81
|
+
for arg in args[2:]:
|
|
82
|
+
e = fpc.Mul(e, arg)
|
|
83
|
+
return e
|
|
84
|
+
|
|
85
|
+
def _size0_expr(x: str):
|
|
86
|
+
return fpc.Size(fpc.Var(x), fpc.Integer(0))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class FPCoreCompileInstance(ReduceVisitor):
|
|
90
|
+
"""Compilation instance from FPy to FPCore"""
|
|
91
|
+
func: FunctionDef
|
|
92
|
+
gensym: Gensym
|
|
93
|
+
|
|
94
|
+
def __init__(self, func: FunctionDef):
|
|
95
|
+
uses = DefineUse().analyze(func)
|
|
96
|
+
self.func = func
|
|
97
|
+
self.gensym = Gensym(reserved=uses.keys())
|
|
98
|
+
|
|
99
|
+
def compile(self) -> fpc.FPCore:
|
|
100
|
+
f = self._visit_function(self.func, None)
|
|
101
|
+
assert isinstance(f, fpc.FPCore), 'unexpected result type'
|
|
102
|
+
return f
|
|
103
|
+
|
|
104
|
+
def _compile_arg(self, arg: Argument):
|
|
105
|
+
match arg.ty:
|
|
106
|
+
case RealType():
|
|
107
|
+
return arg.name, None, None
|
|
108
|
+
case AnyType():
|
|
109
|
+
return arg.name, None, None
|
|
110
|
+
case _:
|
|
111
|
+
raise FPCoreCompileError('unsupported argument type', arg)
|
|
112
|
+
|
|
113
|
+
def _compile_tuple_binding(self, tuple_id: str, binding: TupleBinding, pos: list[int]):
|
|
114
|
+
tuple_binds: list[tuple[str, fpc.Expr]] = []
|
|
115
|
+
for i, elt in enumerate(binding):
|
|
116
|
+
match elt:
|
|
117
|
+
case Id():
|
|
118
|
+
idxs = [fpc.Integer(idx) for idx in [i, *pos]]
|
|
119
|
+
tuple_binds.append((str(elt), fpc.Ref(fpc.Var(tuple_id), *idxs)))
|
|
120
|
+
case TupleBinding():
|
|
121
|
+
tuple_binds += self._compile_tuple_binding(tuple_id, elt, [i, *pos])
|
|
122
|
+
case _:
|
|
123
|
+
raise FPCoreCompileError('unexpected tensor element', elt)
|
|
124
|
+
return tuple_binds
|
|
125
|
+
|
|
126
|
+
def _compile_compareop(self, op: CompareOp):
|
|
127
|
+
match op:
|
|
128
|
+
case CompareOp.LT:
|
|
129
|
+
return fpc.LT
|
|
130
|
+
case CompareOp.LE:
|
|
131
|
+
return fpc.LEQ
|
|
132
|
+
case CompareOp.GE:
|
|
133
|
+
return fpc.GEQ
|
|
134
|
+
case CompareOp.GT:
|
|
135
|
+
return fpc.GT
|
|
136
|
+
case CompareOp.EQ:
|
|
137
|
+
return fpc.EQ
|
|
138
|
+
case CompareOp.NE:
|
|
139
|
+
return fpc.NEQ
|
|
140
|
+
case _:
|
|
141
|
+
raise NotImplementedError('unreachable', op)
|
|
142
|
+
|
|
143
|
+
def _visit_var(self, e, ctx) -> fpc.Expr:
|
|
144
|
+
return fpc.Var(str(e.name))
|
|
145
|
+
|
|
146
|
+
def _visit_bool(self, e: Bool, ctx: None):
|
|
147
|
+
return fpc.Constant('TRUE' if e.val else 'FALSE')
|
|
148
|
+
|
|
149
|
+
def _visit_decnum(self, e, ctx) -> fpc.Expr:
|
|
150
|
+
return fpc.Decnum(e.val)
|
|
151
|
+
|
|
152
|
+
def _visit_hexnum(self, e, ctx):
|
|
153
|
+
raise fpc.Hexnum(e.val)
|
|
154
|
+
|
|
155
|
+
def _visit_integer(self, e, ctx) -> fpc.Expr:
|
|
156
|
+
return fpc.Integer(e.val)
|
|
157
|
+
|
|
158
|
+
def _visit_rational(self, e, ctx):
|
|
159
|
+
raise fpc.Rational(e.p, e.q)
|
|
160
|
+
|
|
161
|
+
def _visit_constant(self, e, ctx):
|
|
162
|
+
raise fpc.Constant(e.val)
|
|
163
|
+
|
|
164
|
+
def _visit_digits(self, e, ctx) -> fpc.Expr:
|
|
165
|
+
return fpc.Digits(e.m, e.e, e.b)
|
|
166
|
+
|
|
167
|
+
def _visit_unknown(self, e, ctx) -> fpc.Expr:
|
|
168
|
+
args = [self._visit_expr(c, ctx) for c in e.children]
|
|
169
|
+
return fpc.UnknownOperator(e.name, *args)
|
|
170
|
+
|
|
171
|
+
def _visit_nary_expr(self, e, ctx) -> fpc.Expr:
|
|
172
|
+
if e.name == Range.name:
|
|
173
|
+
# expand range expression
|
|
174
|
+
tuple_id = 'i' # only identifier in scope => no need for uniqueness
|
|
175
|
+
size = self._visit_expr(e.children[0], ctx)
|
|
176
|
+
return fpc.Tensor([(tuple_id, size)], fpc.Var(tuple_id))
|
|
177
|
+
else:
|
|
178
|
+
cls = _op_table.get(e.name)
|
|
179
|
+
if cls is None:
|
|
180
|
+
raise NotImplementedError('no FPCore operator for', e.name)
|
|
181
|
+
return cls(*[self._visit_expr(c, ctx) for c in e.children])
|
|
182
|
+
|
|
183
|
+
def _visit_compare(self, e: Compare, ctx: None) -> fpc.Expr:
|
|
184
|
+
assert e.ops != [], 'should not be empty'
|
|
185
|
+
match e.ops:
|
|
186
|
+
case [op]:
|
|
187
|
+
# 2-argument case: just compile
|
|
188
|
+
cls = self._compile_compareop(op)
|
|
189
|
+
arg0 = self._visit_expr(e.children[0], ctx)
|
|
190
|
+
arg1 = self._visit_expr(e.children[1], ctx)
|
|
191
|
+
return cls(arg0, arg1)
|
|
192
|
+
case [op, *ops]:
|
|
193
|
+
# N-argument case:
|
|
194
|
+
# TODO: want to evaluate each argument only once;
|
|
195
|
+
# may need to let-bind in case any argument is
|
|
196
|
+
# used multiple times
|
|
197
|
+
args = [self._visit_expr(arg, ctx) for arg in e.children]
|
|
198
|
+
curr_group = (op, [args[0], args[1]])
|
|
199
|
+
groups: list[tuple[CompareOp, list[fpc.Expr]]] = [curr_group]
|
|
200
|
+
for op, lhs, rhs in zip(ops, args[1:], args[2:]):
|
|
201
|
+
if op == curr_group[0] or isinstance(lhs, fpc.ValueExpr):
|
|
202
|
+
# same op => append
|
|
203
|
+
# different op (terminal) => append
|
|
204
|
+
curr_group[1].append(lhs)
|
|
205
|
+
else:
|
|
206
|
+
# different op (non-terminal) => new group
|
|
207
|
+
new_group = (op, [lhs, rhs])
|
|
208
|
+
groups.append(new_group)
|
|
209
|
+
curr_group = new_group
|
|
210
|
+
|
|
211
|
+
if len(groups) == 1:
|
|
212
|
+
op, args = groups[0]
|
|
213
|
+
cls = self._compile_compareop(op)
|
|
214
|
+
return cls(*args)
|
|
215
|
+
else:
|
|
216
|
+
args = [self._compile_compareop(op)(*args) for op, args in groups]
|
|
217
|
+
return fpc.And(*args)
|
|
218
|
+
case _:
|
|
219
|
+
raise NotImplementedError('unreachable', e.ops)
|
|
220
|
+
|
|
221
|
+
def _visit_tuple_expr(self, e, ctx) -> fpc.Expr:
|
|
222
|
+
return fpc.Array(*[self._visit_expr(c, ctx) for c in e.children])
|
|
223
|
+
|
|
224
|
+
def _visit_tuple_ref(self, e, ctx) -> fpc.Expr:
|
|
225
|
+
value = self._visit_expr(e.value, ctx)
|
|
226
|
+
slices = [self._visit_expr(s, ctx) for s in e.slices]
|
|
227
|
+
return fpc.Ref(value, *slices)
|
|
228
|
+
|
|
229
|
+
def _generate_tuple_set(self, tuple_id: str, iter_id: str, idx_ids: list[str], val_id: str):
|
|
230
|
+
# dimension bindings
|
|
231
|
+
idx_id = idx_ids[0]
|
|
232
|
+
tensor_dims = [(iter_id, _size0_expr(tuple_id))]
|
|
233
|
+
# generate if expression
|
|
234
|
+
cond_expr = fpc.EQ(fpc.Var(iter_id), fpc.Var(idx_id))
|
|
235
|
+
iff_expr = fpc.Ref(fpc.Var(tuple_id), fpc.Var(idx_id))
|
|
236
|
+
if len(idx_ids) == 1:
|
|
237
|
+
ift_expr = fpc.Var(val_id)
|
|
238
|
+
else:
|
|
239
|
+
let_bindings = [(tuple_id, fpc.Ref(fpc.Var(tuple_id), fpc.Var(idx_id)))]
|
|
240
|
+
rec_expr = self._generate_tuple_set(tuple_id, iter_id, idx_ids[1:], val_id)
|
|
241
|
+
ift_expr = fpc.Let(let_bindings, rec_expr)
|
|
242
|
+
if_expr = fpc.If(cond_expr, ift_expr, iff_expr)
|
|
243
|
+
return fpc.Tensor(tensor_dims, if_expr)
|
|
244
|
+
|
|
245
|
+
def _visit_tuple_set(self, e, ctx) -> fpc.Expr:
|
|
246
|
+
# general case:
|
|
247
|
+
#
|
|
248
|
+
# (let ([t <tuple>] [i0 <index>] ... [v <value>]))
|
|
249
|
+
# (tensor ([k (size t 0)])
|
|
250
|
+
# (if (= k i)
|
|
251
|
+
# (let ([t (ref t i0)])
|
|
252
|
+
# <recurse with i1, ...>)
|
|
253
|
+
# (ref t i0)
|
|
254
|
+
#
|
|
255
|
+
# where <recurse with i1, ...> is
|
|
256
|
+
#
|
|
257
|
+
# (tensor ([k (size t 0)])
|
|
258
|
+
# (if (= k i1)
|
|
259
|
+
# (let ([t (ref t i1)])
|
|
260
|
+
# <recurse with i2, ...>)
|
|
261
|
+
# (ref t i1)
|
|
262
|
+
#
|
|
263
|
+
# and <recurse with iN> is
|
|
264
|
+
#
|
|
265
|
+
# (tensor ([k (size t 0)])
|
|
266
|
+
# (if (= k iN) v (ref t iN))
|
|
267
|
+
#
|
|
268
|
+
|
|
269
|
+
# generate temporary variables
|
|
270
|
+
tuple_id = str(self.gensym.fresh('t'))
|
|
271
|
+
idx_ids = [str(self.gensym.fresh('i')) for _ in e.slices]
|
|
272
|
+
iter_id = str(self.gensym.fresh('k'))
|
|
273
|
+
val_id = str(self.gensym.fresh('v'))
|
|
274
|
+
|
|
275
|
+
# compile each component
|
|
276
|
+
tuple_expr = self._visit_expr(e.array, ctx)
|
|
277
|
+
idx_exprs = [self._visit_expr(idx, ctx) for idx in e.slices]
|
|
278
|
+
val_expr = self._visit_expr(e.value, ctx)
|
|
279
|
+
|
|
280
|
+
# create initial let binding
|
|
281
|
+
let_bindings = [(tuple_id, tuple_expr)]
|
|
282
|
+
for idx_id, idx_expr in zip(idx_ids, idx_exprs):
|
|
283
|
+
let_bindings.append((idx_id, idx_expr))
|
|
284
|
+
let_bindings.append((val_id, val_expr))
|
|
285
|
+
|
|
286
|
+
# recursively generate tensor expressions
|
|
287
|
+
tensor_expr = self._generate_tuple_set(tuple_id, iter_id, idx_ids, val_id)
|
|
288
|
+
return fpc.Let(let_bindings, tensor_expr)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _visit_comp_expr(self, e, ctx) -> fpc.Expr:
|
|
292
|
+
if len(e.vars) == 1:
|
|
293
|
+
# simple case:
|
|
294
|
+
# (let ([t <iterable>]) (tensor ([i (size t)]) (let ([<var> (ref t i)]) <elt>))
|
|
295
|
+
iterable = e.iterables[0]
|
|
296
|
+
var = str(e.vars[0])
|
|
297
|
+
|
|
298
|
+
tuple_id = str(self.gensym.fresh('t'))
|
|
299
|
+
iter_id = str(self.gensym.fresh('i'))
|
|
300
|
+
iterable = self._visit_expr(iterable, ctx)
|
|
301
|
+
elt = self._visit_expr(e.elt, ctx)
|
|
302
|
+
|
|
303
|
+
let_bindings = [(tuple_id, iterable)]
|
|
304
|
+
tensor_dims: list[tuple[str, fpc.Expr]] = [(iter_id, _size0_expr(tuple_id))]
|
|
305
|
+
ref_bindings: list[tuple[str, fpc.Expr]] = [(var, fpc.Ref(fpc.Var(tuple_id), fpc.Var(iter_id)))]
|
|
306
|
+
return fpc.Let(let_bindings, fpc.Tensor(tensor_dims, fpc.Let(ref_bindings, elt)))
|
|
307
|
+
else:
|
|
308
|
+
# hard case:
|
|
309
|
+
# (let ([t0 <iterable>] ...)
|
|
310
|
+
# (let ([n0 (size t0)] ...)
|
|
311
|
+
# (tensor ([k (! :precision integer (* n0 ...))])
|
|
312
|
+
# (let ([i0 (! :precision integer :round toZero (/ k (* n1 ...)))]
|
|
313
|
+
# [i1 (! :precision integer :round toZero (fmod (/ k (* n2 ...)) n1))]
|
|
314
|
+
# ...
|
|
315
|
+
# [iN (! :precision integer :round toZero (fmod k nN))])
|
|
316
|
+
# (let ([v0 (ref t0 i0)] ...)
|
|
317
|
+
# <elt>))))
|
|
318
|
+
|
|
319
|
+
# bind the tuples to temporaries
|
|
320
|
+
tuple_ids = [str(self.gensym.fresh('t')) for _ in e.vars]
|
|
321
|
+
tuple_binds: list[tuple[str, fpc.Expr]] = [
|
|
322
|
+
(tid, self._visit_expr(iterable, ctx))
|
|
323
|
+
for tid, iterable in zip(tuple_ids, e.iterables)
|
|
324
|
+
]
|
|
325
|
+
# bind the sizes to temporaries
|
|
326
|
+
size_ids = [str(self.gensym.fresh('n')) for _ in e.vars]
|
|
327
|
+
size_binds: list[tuple[str, fpc.Expr]] = [
|
|
328
|
+
(sid, _size0_expr(tid))
|
|
329
|
+
for sid, tid in zip(size_ids, tuple_ids)
|
|
330
|
+
]
|
|
331
|
+
# bind the indices to temporaries
|
|
332
|
+
idx_ctx = { 'precision': 'integer', 'round': 'toZero' }
|
|
333
|
+
idx_ids = [str(self.gensym.fresh('i')) for _ in e.vars]
|
|
334
|
+
idx_binds: list[tuple[str, fpc.Expr]] = []
|
|
335
|
+
for i, iid in enumerate(idx_ids):
|
|
336
|
+
if i == 0:
|
|
337
|
+
mul_expr = _nary_mul([fpc.Var(id) for id in size_ids[1:]])
|
|
338
|
+
idx_expr = fpc.Ctx(idx_ctx, fpc.Div(fpc.Var('k'), mul_expr))
|
|
339
|
+
elif i == len(size_ids) - 1:
|
|
340
|
+
idx_expr = fpc.Ctx(idx_ctx, fpc.Fmod(fpc.Var('k'), fpc.Var(size_ids[i])))
|
|
341
|
+
else:
|
|
342
|
+
mul_expr = _nary_mul([fpc.Var(id) for id in size_ids[1:]])
|
|
343
|
+
idx_expr = fpc.Ctx(idx_ctx, fpc.Fmod(fpc.Div(fpc.Var('k'), mul_expr), fpc.Var(size_ids[i])))
|
|
344
|
+
idx_binds.append((iid, idx_expr))
|
|
345
|
+
# iteration variable
|
|
346
|
+
iter_ctx = { 'precision': 'integer'}
|
|
347
|
+
iter_id = str(self.gensym.fresh('k'))
|
|
348
|
+
iter_expr = fpc.Ctx(iter_ctx, _nary_mul([fpc.Var(sid) for sid in size_ids]))
|
|
349
|
+
# reference variables
|
|
350
|
+
ref_ids = [str(self.gensym.refresh(var)) for var in e.vars]
|
|
351
|
+
ref_binds: list[tuple[str, fpc.Expr]] = [
|
|
352
|
+
(rid, fpc.Ref(fpc.Var(tid), fpc.Var(iid)))
|
|
353
|
+
for rid, tid, iid in zip(ref_ids, tuple_ids, idx_ids)
|
|
354
|
+
]
|
|
355
|
+
# element expression
|
|
356
|
+
elt = self._visit_expr(e.elt, ctx)
|
|
357
|
+
# compose the expression
|
|
358
|
+
tensor_expr = fpc.Tensor([(iter_id, iter_expr)], fpc.Let(idx_binds, fpc.Let(ref_binds, elt)))
|
|
359
|
+
return fpc.Let(tuple_binds, fpc.Let(size_binds, tensor_expr))
|
|
360
|
+
|
|
361
|
+
def _visit_if_expr(self, e, ctx) -> fpc.Expr:
|
|
362
|
+
cond = self._visit_expr(e.cond, ctx)
|
|
363
|
+
ift = self._visit_expr(e.ift, ctx)
|
|
364
|
+
iff = self._visit_expr(e.iff, ctx)
|
|
365
|
+
return fpc.If(cond, ift, iff)
|
|
366
|
+
|
|
367
|
+
def _visit_var_assign(self, stmt: VarAssign, ctx: fpc.Expr):
|
|
368
|
+
bindings = [(str(stmt.var), self._visit_expr(stmt.expr, None))]
|
|
369
|
+
return fpc.Let(bindings, ctx)
|
|
370
|
+
|
|
371
|
+
def _visit_tuple_assign(self, stmt: TupleAssign, ctx: fpc.Expr):
|
|
372
|
+
tuple_id = str(self.gensym.fresh('t'))
|
|
373
|
+
tuple_bind = (tuple_id, self._visit_expr(stmt.expr, None))
|
|
374
|
+
destruct_bindings = self._compile_tuple_binding(tuple_id, stmt.binding, [])
|
|
375
|
+
return fpc.Let([tuple_bind] + destruct_bindings, ctx)
|
|
376
|
+
|
|
377
|
+
def _visit_ref_assign(self, stmt: RefAssign, ctx: fpc.Expr):
|
|
378
|
+
raise FPCoreCompileError(f'cannot compile to FPCore: {type(stmt).__name__}')
|
|
379
|
+
|
|
380
|
+
def _visit_if1_stmt(self, stmt, ctx):
|
|
381
|
+
raise FPCoreCompileError(f'cannot compile to FPCore: {type(stmt).__name__}')
|
|
382
|
+
|
|
383
|
+
def _visit_if_stmt(self, stmt, ctx):
|
|
384
|
+
raise FPCoreCompileError(f'cannot compile to FPCore: {type(stmt).__name__}')
|
|
385
|
+
|
|
386
|
+
def _visit_while_stmt(self, stmt, ctx: fpc.Expr):
|
|
387
|
+
if len(stmt.phis) != 1:
|
|
388
|
+
raise FPCoreCompileError('while loops must have exactly one phi node')
|
|
389
|
+
phi = stmt.phis[0]
|
|
390
|
+
name, init, update = str(phi.name), str(phi.lhs), str(phi.rhs)
|
|
391
|
+
cond = self._visit_expr(stmt.cond, None)
|
|
392
|
+
body = self._visit_block(stmt.body, fpc.Var(update))
|
|
393
|
+
return fpc.While(cond, [(name, fpc.Var(init), body)], ctx)
|
|
394
|
+
|
|
395
|
+
def _visit_for_stmt(self, stmt, ctx):
|
|
396
|
+
if len(stmt.phis) != 1:
|
|
397
|
+
raise FPCoreCompileError('for loops must have exactly one phi node')
|
|
398
|
+
# phi nodes
|
|
399
|
+
phi = stmt.phis[0]
|
|
400
|
+
name, init, update = str(phi.name), str(phi.lhs), str(phi.rhs)
|
|
401
|
+
# fresh variable for the iterable value
|
|
402
|
+
tuple_id = str(self.gensym.fresh('t'))
|
|
403
|
+
iterable = self._visit_expr(stmt.iterable, None)
|
|
404
|
+
body = self._visit_block(stmt.body, fpc.Var(update))
|
|
405
|
+
# index variables and state merging
|
|
406
|
+
dim_binding = (str(stmt.var), _size0_expr(tuple_id))
|
|
407
|
+
while_binding = (name, fpc.Var(init), body)
|
|
408
|
+
return fpc.Let([(tuple_id, iterable)], fpc.For([dim_binding], [while_binding], ctx))
|
|
409
|
+
|
|
410
|
+
def _visit_context(self, stmt, ctx):
|
|
411
|
+
body = self._visit_block(stmt.body, ctx)
|
|
412
|
+
return fpc.Ctx(stmt.props, body)
|
|
413
|
+
|
|
414
|
+
def _visit_assert(self, stmt, ctx):
|
|
415
|
+
# strip the assertion
|
|
416
|
+
return ctx
|
|
417
|
+
|
|
418
|
+
def _visit_return(self, stmt, ctx) -> fpc.Expr:
|
|
419
|
+
return self._visit_expr(stmt.expr, ctx)
|
|
420
|
+
|
|
421
|
+
def _visit_phis(self, phis, lctx, rctx):
|
|
422
|
+
raise NotImplementedError('do not call directly')
|
|
423
|
+
|
|
424
|
+
def _visit_loop_phis(self, phis, lctx, rctx):
|
|
425
|
+
raise NotImplementedError('do not call directly')
|
|
426
|
+
|
|
427
|
+
def _visit_block(self, block, ctx: Optional[fpc.Expr]):
|
|
428
|
+
if ctx is None:
|
|
429
|
+
e = self._visit_statement(block.stmts[-1], None)
|
|
430
|
+
stmts = block.stmts[:-1]
|
|
431
|
+
else:
|
|
432
|
+
e = ctx
|
|
433
|
+
stmts = block.stmts
|
|
434
|
+
|
|
435
|
+
for stmt in reversed(stmts):
|
|
436
|
+
if isinstance(stmt, Return):
|
|
437
|
+
raise FPCoreCompileError('return statements must be at the end of blocks')
|
|
438
|
+
e = self._visit_statement(stmt, e)
|
|
439
|
+
|
|
440
|
+
return e
|
|
441
|
+
|
|
442
|
+
def _visit_function(self, func, ctx: Optional[fpc.Expr]):
|
|
443
|
+
args = [self._compile_arg(arg) for arg in func.args]
|
|
444
|
+
body = self._visit_block(func.body, ctx)
|
|
445
|
+
# TODO: parse data
|
|
446
|
+
ident = func.name
|
|
447
|
+
name = func.ctx.get('name', None)
|
|
448
|
+
pre = func.ctx.get('pre', None)
|
|
449
|
+
spec = func.ctx.get('spec', None)
|
|
450
|
+
return fpc.FPCore(
|
|
451
|
+
inputs=args,
|
|
452
|
+
e=body,
|
|
453
|
+
props=func.ctx.copy(),
|
|
454
|
+
ident=ident,
|
|
455
|
+
name=name,
|
|
456
|
+
pre=pre,
|
|
457
|
+
spec=spec
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
# override to get typing hint
|
|
461
|
+
def _visit_expr(self, e: Expr, ctx: None) -> fpc.Expr:
|
|
462
|
+
return super()._visit_expr(e, ctx)
|
|
463
|
+
|
|
464
|
+
# override to get typing hint
|
|
465
|
+
def _visit_statement(self, stmt: Stmt, ctx: fpc.Expr) -> fpc.Expr:
|
|
466
|
+
return super()._visit_statement(stmt, ctx)
|
|
467
|
+
|
|
468
|
+
class FPCoreCompiler(Backend):
|
|
469
|
+
"""Compiler from FPy IR to FPCore"""
|
|
470
|
+
|
|
471
|
+
def compile(self, func: Function) -> fpc.FPCore:
|
|
472
|
+
ir = func.ir
|
|
473
|
+
|
|
474
|
+
# normalization passes
|
|
475
|
+
ir = FuncUpdate.apply(ir)
|
|
476
|
+
ir = ForBundling.apply(ir)
|
|
477
|
+
ir = WhileBundling.apply(ir)
|
|
478
|
+
ir = SimplifyIf.apply(ir)
|
|
479
|
+
|
|
480
|
+
return FPCoreCompileInstance(ir).compile()
|