Skip to content

Commit f8f55a5

Browse files
authored
Stratify lookup functions to speed up llvm-kompile-matching (#875)
Instead of equations for a single function, `K` helper functions are generated and the equations split (more or less?) evenly among them (by modulo of the Int-valued IDs). ``` syntax TypeInfo ::= lookupType0(Int) [function, total] syntax TypeInfo ::= lookupType1(Int) [function, total] ... syntax Typeinfo ::= lookupType<K-1>(Int) [function, total] rule lookupType(ty(N)) => lookupType0(N) requires N %Int K ==Int 0 rule lookupType(ty(N)) => lookupType1(N) requires N %Int K ==Int 1 ... rule lookupType(ty(N)) => lookupType<K-1>(N) requires N %Int K ==Int <K - 1> ``` Each of the helpers gets its own default, all are total. ``` rule lookupType0(_) => TypeInfoVoidType [owise] // (i.e., not found) rule lookupType1(_) => TypeInfoVoidType [owise] // (i.e., not found) ... ``` For a given `TypeMapping(ty('X), 'TYPEINFO)`, the `X` is divided by `K` and the equation is generated in the respective helper function `m = 'X %Int K` ``` rule lookupType'<m>('X) => 'TYPEINFO ``` This stratification is applied for the `lookupAlloc` and `lookupTy` functions.
1 parent a9876cb commit f8f55a5

File tree

2 files changed

+138
-42
lines changed

2 files changed

+138
-42
lines changed

kmir/src/kmir/kdist/mir-semantics/rt/value.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,26 +136,22 @@ The basic operations of reading and writing those values can use K's "heating" a
136136

137137
These functions are global static data accessed from many places, and will be extended for the particular program.
138138

139-
**TODO find a better home for these definitions.**
140139

141140
```k
142141
// // function store, Ty -> MonoItemFn
143142
syntax MonoItemKind ::= lookupFunction ( Ty ) [function, total, symbol(lookupFunction)]
144-
// ------------------------------------------------------------
145-
rule lookupFunction(ty(TY)) => monoItemFn(symbol("** UNKNOWN FUNCTION **"), defId(TY), noBody) [owise] // HACK
146-
// cannot be total without a default "error" element. `Ty` is key for both functions and data.
147143
148144
// // static allocations: AllocId -> AllocData (Value or error)
149145
syntax Evaluation ::= lookupAlloc ( AllocId ) [function, total, symbol(lookupAlloc)]
150-
// -----------------------------------------------------------
151-
rule lookupAlloc(ID) => InvalidAlloc(ID) [owise]
152-
153-
syntax Evaluation ::= InvalidAlloc ( AllocId )
146+
| InvalidAlloc ( AllocId ) // error marker
154147
155148
// // static information about the base type interning in the MIR: Ty -> TypeInfo
156149
syntax TypeInfo ::= lookupTy ( Ty ) [function, total, symbol(lookupTy)]
157-
// -----------------------------------------------------
158-
rule lookupTy(_TY) => typeInfoVoidType [owise] // HACK
150+
151+
// default rules (unused, only required for compilation of the base semantics)
152+
rule lookupFunction(ty(TY)) => monoItemFn(symbol("** UNKNOWN FUNCTION **"), defId(TY), noBody ) [owise]
153+
rule lookupAlloc(ID) => InvalidAlloc(ID) [owise]
154+
rule lookupTy(_) => typeInfoFunType(" ** INVALID LOOKUP CALL **" ) [owise]
159155
```
160156

161157
```k

kmir/src/kmir/kompile.py

Lines changed: 132 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@
77
from dataclasses import dataclass
88
from typing import TYPE_CHECKING
99

10-
from pyk.kast.inner import KApply, KSort
10+
from pyk.kast.inner import KApply, KSort, KToken, KVariable
1111
from pyk.kast.prelude.kint import intToken
1212
from pyk.kast.prelude.string import stringToken
13+
from pyk.kore.syntax import App, EVar, SortApp, String, Symbol, SymbolDecl
1314

1415
from .build import HASKELL_DEF_DIR, LLVM_DEF_DIR, LLVM_LIB_DIR
1516
from .kmir import KMIR
1617

1718
if TYPE_CHECKING:
19+
from collections.abc import Sequence
1820
from pathlib import Path
1921
from typing import Any, Final
2022

2123
from pyk.kast import KInner
24+
from pyk.kore.syntax import Axiom, Sentence
2225

2326
from .smir import SMIRInfo
2427

@@ -117,7 +120,7 @@ def kompile_smir(
117120
target_dir.mkdir(parents=True, exist_ok=True)
118121

119122
kmir = KMIR(HASKELL_DEF_DIR)
120-
rules = _make_kore_rules(kmir, smir_info)
123+
rules = make_kore_rules(kmir, smir_info)
121124
_LOGGER.info(f'Generated {len(rules)} function equations to add to `definition.kore')
122125

123126
if symbolic:
@@ -216,35 +219,142 @@ def kompile_smir(
216219
return KompiledConcrete(llvm_dir=target_llvm_path)
217220

218221

219-
def _make_kore_rules(kmir: KMIR, smir_info: SMIRInfo) -> list[str]: # generates kore syntax directly as string
222+
def _make_stratified_rules(
223+
kmir: KMIR,
224+
fun: str,
225+
arg_sort: str,
226+
result_sort: str,
227+
id_cons: str,
228+
assocs: list[tuple[int, KInner]],
229+
not_found: KInner,
230+
strata: int = 10,
231+
) -> Sequence[Sentence]:
232+
from pyk.kore.prelude import int_dv
233+
from pyk.kore.rule import FunctionRule
234+
235+
int_eqls = "Lbl'UndsEqlsEqls'Int'Unds'"
236+
int_tmod = "Lbl'UndsPerc'Int'Unds'"
237+
238+
arg_sort_kore = SortApp('Sort' + arg_sort)
239+
int_sort_kore = SortApp('SortInt')
240+
result_sort_kore = SortApp('Sort' + result_sort)
241+
id_cons_kore = 'Lbl' + id_cons
242+
243+
declarations = [
244+
# declare stratified functions
245+
SymbolDecl(
246+
symbol=Symbol('Lbl' + fun + str(i)),
247+
param_sorts=(int_sort_kore,),
248+
sort=result_sort_kore,
249+
attrs=(
250+
App('function'),
251+
App('total'),
252+
),
253+
)
254+
for i in range(strata)
255+
]
256+
dispatch = [
257+
# define dispatch equations to stratified functions
258+
# f'rule {fun}({id_cons}(N) => {fun}{i}(N)) requires N /Int {strata} ==Int {i}'
259+
FunctionRule(
260+
App('Lbl' + fun, (), (App(id_cons_kore, (), (EVar('VarN', int_sort_kore),)),)),
261+
App('Lbl' + fun + str(i), (), (EVar('VarN', int_sort_kore),)),
262+
App(int_eqls, (), [App(int_tmod, (), [EVar('VarN', int_sort_kore), int_dv(strata)]), int_dv(i)]),
263+
None,
264+
result_sort_kore,
265+
(arg_sort_kore,),
266+
None,
267+
0,
268+
f'{fun}{i}-dispatch',
269+
f'{fun}{i}-dispatch',
270+
)
271+
.to_axiom()
272+
.let_attrs((App("UNIQUE'Unds'ID", (), (String(f'{fun}{i}-dispatch'),)),))
273+
for i in range(strata)
274+
]
275+
defaults = [
276+
# define dispatch equations to stratified functions
277+
# f'rule {fun}{i}(N) => {default} [owise]'
278+
FunctionRule(
279+
App('Lbl' + fun + str(i), (), (EVar('VarN', SortApp('SortInt')),)),
280+
kmir.kast_to_kore(not_found, KSort(result_sort)),
281+
None,
282+
None,
283+
result_sort_kore,
284+
(int_sort_kore,),
285+
None,
286+
200,
287+
f'{fun}{i}-default',
288+
f'{fun}{i}-default',
289+
)
290+
.to_axiom()
291+
.let_attrs((App('owise'), App("UNIQUE'Unds'ID", (), (String(f'{fun}{i}-default'),))))
292+
for i in range(strata)
293+
]
220294
equations = []
295+
for i, result in assocs:
296+
m = i % strata
297+
equations.append(
298+
_mk_equation(kmir, fun + str(m), intToken(i), 'Int', result, result_sort).let_attrs(
299+
(App("UNIQUE'Unds'ID", (), (String(f'{fun}{m}-{i}-generated'),)),)
300+
)
301+
)
302+
return [*declarations, *dispatch, *defaults, *equations]
303+
221304

305+
def make_kore_rules(kmir: KMIR, smir_info: SMIRInfo) -> Sequence[Sentence]:
222306
# kprint tool is too chatty
223307
kprint_logger = logging.getLogger('pyk.ktool.kprint')
224308
kprint_logger.setLevel(logging.WARNING)
225309

310+
unknown_function = KApply(
311+
'MonoItemKind::MonoItemFn',
312+
(
313+
KApply('symbol(_)_LIB_Symbol_String', (KToken('"** UNKNOWN FUNCTION **"', KSort('String')),)),
314+
KApply('defId(_)_BODY_DefId_Int', (KVariable('TY', KSort('Int')),)),
315+
KApply('noBody_BODY_MaybeBody', ()),
316+
),
317+
)
318+
default_function = _mk_equation(
319+
kmir, 'lookupFunction', KApply('ty', (KVariable('TY'),)), 'Ty', unknown_function, 'MonoItemKind'
320+
).let_attrs(((App('owise')),))
321+
322+
equations: list[Axiom] = [default_function]
323+
226324
for fty, kind in _functions(kmir, smir_info).items():
227325
equations.append(
228326
_mk_equation(kmir, 'lookupFunction', KApply('ty', (intToken(fty),)), 'Ty', kind, 'MonoItemKind')
229327
)
230328

231-
types: set[KInner] = set()
232-
for type in smir_info._smir['types']:
233-
parse_result = kmir.parser.parse_mir_json(type, 'TypeMapping')
234-
assert parse_result is not None
235-
type_mapping, _ = parse_result
236-
assert isinstance(type_mapping, KApply) and len(type_mapping.args) == 2
237-
ty, tyinfo = type_mapping.args
238-
if ty in types:
239-
raise ValueError(f'Key collision in type map: {ty}')
240-
types.add(ty)
241-
equations.append(_mk_equation(kmir, 'lookupTy', ty, 'Ty', tyinfo, 'TypeInfo'))
329+
# stratify type and alloc lookups
330+
def get_int_arg(app: KInner) -> int:
331+
match app:
332+
case KApply(_, args=(KToken(token=int_arg, sort=KSort('Int')),)):
333+
return int(int_arg)
334+
case _:
335+
raise Exception(f'Cannot extract int arg from {app}')
336+
337+
invalid_type = KApply('TypeInfo::VoidType', ())
338+
parsed_types = [kmir.parser.parse_mir_json(type, 'TypeMapping') for type in smir_info._smir['types']]
339+
type_mappings = [type_mapping for type_mapping, _ in (result for result in parsed_types if result is not None)]
340+
341+
type_assocs = [
342+
(get_int_arg(ty), info)
343+
for ty, info in (type_mapping.args for type_mapping in type_mappings if isinstance(type_mapping, KApply))
344+
]
242345

243-
for alloc in smir_info._smir['allocs']:
244-
alloc_id, value = _decode_alloc(smir_info=smir_info, raw_alloc=alloc)
245-
equations.append(_mk_equation(kmir, 'lookupAlloc', alloc_id, 'AllocId', value, 'Evaluation'))
346+
type_equations = _make_stratified_rules(kmir, 'lookupTy', 'Ty', 'TypeInfo', 'ty', type_assocs, invalid_type)
246347

247-
return equations
348+
invalid_alloc_n = KApply(
349+
'InvalidAlloc(_)_RT-VALUE-SYNTAX_Evaluation_AllocId', (KApply('allocId', (KVariable('N'),)),)
350+
)
351+
decoded_allocs = [_decode_alloc(smir_info=smir_info, raw_alloc=alloc) for alloc in smir_info._smir['allocs']]
352+
allocs = [(get_int_arg(alloc_id), value) for (alloc_id, value) in decoded_allocs]
353+
alloc_equations = _make_stratified_rules(
354+
kmir, 'lookupAlloc', 'AllocId', 'Evaluation', 'allocId', allocs, invalid_alloc_n
355+
)
356+
357+
return [*equations, *type_equations, *alloc_equations]
248358

249359

250360
def _functions(kmir: KMIR, smir_info: SMIRInfo) -> dict[int, KInner]:
@@ -275,7 +385,7 @@ def _functions(kmir: KMIR, smir_info: SMIRInfo) -> dict[int, KInner]:
275385
return functions
276386

277387

278-
def _mk_equation(kmir: KMIR, fun: str, arg: KInner, arg_sort: str, result: KInner, result_sort: str) -> str:
388+
def _mk_equation(kmir: KMIR, fun: str, arg: KInner, arg_sort: str, result: KInner, result_sort: str) -> Axiom:
279389
from pyk.kore.rule import FunctionRule
280390
from pyk.kore.syntax import App, SortApp
281391

@@ -296,17 +406,7 @@ def _mk_equation(kmir: KMIR, fun: str, arg: KInner, arg_sort: str, result: KInne
296406
uid='fubar',
297407
label='fubaz',
298408
)
299-
return '\n'.join(
300-
[
301-
'',
302-
'',
303-
(
304-
f'// {fun}({kmir.pretty_print(arg)})'
305-
+ (f' => {kmir.pretty_print(result)}' if len(kmir.pretty_print(result)) < 1000 else '')
306-
),
307-
rule.to_axiom().text,
308-
]
309-
)
409+
return rule.to_axiom()
310410

311411

312412
def _decode_alloc(smir_info: SMIRInfo, raw_alloc: Any) -> tuple[KInner, KInner]:
@@ -326,7 +426,7 @@ def _decode_alloc(smir_info: SMIRInfo, raw_alloc: Any) -> tuple[KInner, KInner]:
326426
return alloc_id_term, value.to_kast()
327427

328428

329-
def _insert_rules_and_write(input_file: Path, rules: list[str], output_file: Path) -> None:
429+
def _insert_rules_and_write(input_file: Path, rules: Sequence[Sentence], output_file: Path) -> None:
330430
with open(input_file, 'r') as f:
331431
lines = f.readlines()
332432

@@ -337,7 +437,7 @@ def _insert_rules_and_write(input_file: Path, rules: list[str], output_file: Pat
337437

338438
# Insert rules before the endmodule line
339439
new_lines.append(f'\n// Generated from file {input_file}\n\n')
340-
new_lines.extend(rules)
440+
new_lines.extend([ax.text + '\n' for ax in rules])
341441
new_lines.append('\n' + last_line)
342442

343443
# Write to output file

0 commit comments

Comments
 (0)