11from __future__ import annotations
22
33import logging
4- import shutil
54import tempfile
65from contextlib import contextmanager
76from functools import cached_property
1312from pyk .kast .inner import KApply , KSequence , KSort , KToken , KVariable , Subst
1413from pyk .kast .manip import abstract_term_safely , free_vars , split_config_from
1514from pyk .kast .prelude .collections import list_empty , list_of
16- from pyk .kast .prelude .kint import intToken
17- from pyk .kast .prelude .utils import token
1815from pyk .kcfg import KCFG
1916from pyk .kcfg .explore import KCFGExplore
2017from pyk .kcfg .semantics import DefaultSemantics
2421from pyk .proof .reachability import APRProof , APRProver
2522from pyk .proof .show import APRProofNodePrinter
2623
27- from .build import HASKELL_DEF_DIR , LLVM_DEF_DIR , LLVM_LIB_DIR
2824from .cargo import cargo_get_smir_json
2925from .kast import mk_call_terminator , symbolic_locals
3026from .kparse import KParse
3329
3430if TYPE_CHECKING :
3531 from collections .abc import Iterator
36- from typing import Any , Final
32+ from typing import Final
3733
3834 from pyk .cterm .show import CTermShow
3935 from pyk .kast .inner import KInner
@@ -62,127 +58,15 @@ def __init__(
6258 def from_kompiled_kore (
6359 smir_info : SMIRInfo , target_dir : str , bug_report : Path | None = None , symbolic : bool = True
6460 ) -> KMIR :
65- kmir = KMIR (HASKELL_DEF_DIR )
66-
67- def _insert_rules_and_write (input_file : Path , rules : list [str ], output_file : Path ) -> None :
68- with open (input_file , 'r' ) as f :
69- lines = f .readlines ()
70-
71- # last line must start with 'endmodule'
72- last_line = lines [- 1 ]
73- assert last_line .startswith ('endmodule' )
74- new_lines = lines [:- 1 ]
75-
76- # Insert rules before the endmodule line
77- new_lines .append (f'\n // Generated from file { input_file } \n \n ' )
78- new_lines .extend (rules )
79- new_lines .append ('\n ' + last_line )
80-
81- # Write to output file
82- with open (output_file , 'w' ) as f :
83- f .writelines (new_lines )
84-
85- target_path = Path (target_dir )
86- # TODO if target dir exists and contains files, check file dates (definition files and interpreter)
87- # to decide whether or not to recompile. For now we always recompile.
88- target_path .mkdir (parents = True , exist_ok = True )
89-
90- rules = kmir .make_kore_rules (smir_info )
91- _LOGGER .info (f'Generated { len (rules )} function equations to add to `definition.kore' )
92-
93- if symbolic :
94- # Create output directories
95- target_llvm_path = target_path / 'llvm-library'
96- target_llvmdt_path = target_llvm_path / 'dt'
97- target_hs_path = target_path / 'haskell'
98-
99- _LOGGER .info (f'Creating directories { target_llvmdt_path } and { target_hs_path } ' )
100- target_llvmdt_path .mkdir (parents = True , exist_ok = True )
101- target_hs_path .mkdir (parents = True , exist_ok = True )
102-
103- # Process LLVM definition
104- _LOGGER .info ('Writing LLVM definition file' )
105- llvm_def_file = LLVM_LIB_DIR / 'definition.kore'
106- llvm_def_output = target_llvm_path / 'definition.kore'
107- _insert_rules_and_write (llvm_def_file , rules , llvm_def_output )
108-
109- # Run llvm-kompile-matching and llvm-kompile for LLVM
110- # TODO use pyk to do this if possible (subprocess wrapper, maybe llvm-kompile itself?)
111- # TODO align compilation options to what we use in plugin.py
112- import subprocess
113-
114- _LOGGER .info ('Running llvm-kompile-matching' )
115- subprocess .run (
116- ['llvm-kompile-matching' , str (llvm_def_output ), 'qbaL' , str (target_llvmdt_path ), '1/2' ], check = True
117- )
118- _LOGGER .info ('Running llvm-kompile' )
119- subprocess .run (
120- [
121- 'llvm-kompile' ,
122- str (llvm_def_output ),
123- str (target_llvmdt_path ),
124- 'c' ,
125- '-O2' ,
126- '--' ,
127- '-o' ,
128- target_llvm_path / 'interpreter' ,
129- ],
130- check = True ,
131- )
132-
133- # Process Haskell definition
134- _LOGGER .info ('Writing Haskell definition file' )
135- hs_def_file = HASKELL_DEF_DIR / 'definition.kore'
136- _insert_rules_and_write (hs_def_file , rules , target_hs_path / 'definition.kore' )
137-
138- # Copy all files except definition.kore and binary from HASKELL_DEF_DIR to out/hs
139- _LOGGER .info ('Copying other artefacts into HS output directory' )
140- for file_path in HASKELL_DEF_DIR .iterdir ():
141- if file_path .name != 'definition.kore' and file_path .name != 'haskellDefinition.bin' :
142- if file_path .is_file ():
143- shutil .copy2 (file_path , target_hs_path / file_path .name )
144- elif file_path .is_dir ():
145- shutil .copytree (file_path , target_hs_path / file_path .name , dirs_exist_ok = True )
146- return KMIR (target_hs_path , target_llvm_path , bug_report = bug_report )
147- else :
148-
149- target_llvm_path = target_path / 'llvm'
150- target_llvmdt_path = target_llvm_path / 'dt'
151- _LOGGER .info (f'Creating directory { target_llvmdt_path } ' )
152- target_llvmdt_path .mkdir (parents = True , exist_ok = True )
153-
154- # Process LLVM definition
155- _LOGGER .info ('Writing LLVM definition file' )
156- llvm_def_file = LLVM_LIB_DIR / 'definition.kore'
157- llvm_def_output = target_llvm_path / 'definition.kore'
158- _insert_rules_and_write (llvm_def_file , rules , llvm_def_output )
159-
160- import subprocess
161-
162- _LOGGER .info ('Running llvm-kompile-matching' )
163- subprocess .run (
164- ['llvm-kompile-matching' , str (llvm_def_output ), 'qbaL' , str (target_llvmdt_path ), '1/2' ], check = True
165- )
166- _LOGGER .info ('Running llvm-kompile' )
167- subprocess .run (
168- [
169- 'llvm-kompile' ,
170- str (llvm_def_output ),
171- str (target_llvmdt_path ),
172- 'main' ,
173- '-O2' ,
174- '--' ,
175- '-o' ,
176- target_llvm_path / 'interpreter' ,
177- ],
178- check = True ,
179- )
180- blacklist = ['definition.kore' , 'interpreter' , 'dt' ]
181- to_copy = [file .name for file in LLVM_DEF_DIR .iterdir () if file .name not in blacklist ]
182- for file in to_copy :
183- _LOGGER .info (f'Copying file { file } ' )
184- shutil .copy2 (LLVM_DEF_DIR / file , target_llvm_path / file )
185- return KMIR (target_llvm_path , None , bug_report = bug_report )
61+ from .kompile import kompile_smir
62+
63+ kompiled_smir = kompile_smir (
64+ smir_info = smir_info ,
65+ target_dir = target_dir ,
66+ bug_report = bug_report ,
67+ symbolic = symbolic ,
68+ )
69+ return kompiled_smir .create_kmir (bug_report_file = bug_report )
18670
18771 class Symbols :
18872 END_PROGRAM : Final = KApply ('#EndProgram_KMIR-CONTROL-FLOW_KItem' )
@@ -203,96 +87,6 @@ def kcfg_explore(self, label: str | None = None) -> Iterator[KCFGExplore]:
20387 ) as cts :
20488 yield KCFGExplore (cts , kcfg_semantics = KMIRSemantics ())
20589
206- def functions (self , smir_info : SMIRInfo ) -> dict [int , KInner ]:
207- functions : dict [int , KInner ] = {}
208-
209- # Parse regular functions
210- for item_name , item in smir_info .items .items ():
211- if not item_name in smir_info .function_symbols_reverse :
212- _LOGGER .warning (f'Item not found in SMIR: { item_name } ' )
213- continue
214- parsed_item = self .parser .parse_mir_json (item , 'MonoItem' )
215- if not parsed_item :
216- raise ValueError (f'Could not parse MonoItemKind: { parsed_item } ' )
217- parsed_item_kinner , _ = parsed_item
218- assert isinstance (parsed_item_kinner , KApply ) and parsed_item_kinner .label .name == 'monoItemWrapper'
219- # each item can have several entries in the function table for linked SMIR JSON
220- for ty in smir_info .function_symbols_reverse [item_name ]:
221- functions [ty ] = parsed_item_kinner .args [1 ]
222-
223- # Add intrinsic functions
224- for ty , sym in smir_info .function_symbols .items ():
225- if 'IntrinsicSym' in sym and ty not in functions :
226- functions [ty ] = KApply (
227- 'IntrinsicFunction' ,
228- [KApply ('symbol(_)_LIB_Symbol_String' , [token (sym ['IntrinsicSym' ])])],
229- )
230-
231- return functions
232-
233- def make_kore_rules (self , smir_info : SMIRInfo ) -> list [str ]: # generates kore syntax directly as string
234- equations = []
235-
236- # kprint tool is too chatty
237- kprint_logger = logging .getLogger ('pyk.ktool.kprint' )
238- kprint_logger .setLevel (logging .WARNING )
239-
240- for fty , kind in self .functions (smir_info ).items ():
241- equations .append (
242- self ._mk_equation ('lookupFunction' , KApply ('ty' , (token (fty ),)), 'Ty' , kind , 'MonoItemKind' )
243- )
244-
245- types : set [KInner ] = set ()
246- for type in smir_info ._smir ['types' ]:
247- parse_result = self .parser .parse_mir_json (type , 'TypeMapping' )
248- assert parse_result is not None
249- type_mapping , _ = parse_result
250- assert isinstance (type_mapping , KApply ) and len (type_mapping .args ) == 2
251- ty , tyinfo = type_mapping .args
252- if ty in types :
253- raise ValueError (f'Key collision in type map: { ty } ' )
254- types .add (ty )
255- equations .append (self ._mk_equation ('lookupTy' , ty , 'Ty' , tyinfo , 'TypeInfo' ))
256-
257- for alloc in smir_info ._smir ['allocs' ]:
258- alloc_id , value = self ._decode_alloc (smir_info = smir_info , raw_alloc = alloc )
259- equations .append (self ._mk_equation ('lookupAlloc' , alloc_id , 'AllocId' , value , 'Evaluation' ))
260-
261- return equations
262-
263- def _mk_equation (self , fun : str , arg : KInner , arg_sort : str , result : KInner , result_sort : str ) -> str :
264- from pyk .kore .rule import FunctionRule
265- from pyk .kore .syntax import App , SortApp
266-
267- arg_kore = self .kast_to_kore (arg , KSort (arg_sort ))
268- fun_app = App ('Lbl' + fun , (), (arg_kore ,))
269- result_kore = self .kast_to_kore (result , KSort (result_sort ))
270-
271- assert isinstance (fun_app , App )
272- rule = FunctionRule (
273- lhs = fun_app ,
274- rhs = result_kore ,
275- req = None ,
276- ens = None ,
277- sort = SortApp ('Sort' + result_sort ),
278- arg_sorts = (SortApp ('Sort' + arg_sort ),),
279- anti_left = None ,
280- priority = 50 ,
281- uid = 'fubar' ,
282- label = 'fubaz' ,
283- )
284- return '\n ' .join (
285- [
286- '' ,
287- '' ,
288- (
289- f'// { fun } ({ self .pretty_print (arg )} )'
290- + (f' => { self .pretty_print (result )} ' if len (self .pretty_print (result )) < 1000 else '' )
291- ),
292- rule .to_axiom ().text ,
293- ]
294- )
295-
29690 def make_call_config (
29791 self ,
29892 smir_info : SMIRInfo ,
@@ -327,22 +121,6 @@ def make_call_config(
327121 config = self .definition .empty_config (KSort (sort ))
328122 return (subst .apply (config ), constraints )
329123
330- def _decode_alloc (self , smir_info : SMIRInfo , raw_alloc : Any ) -> tuple [KInner , KInner ]:
331- from .decoding import UnableToDecodeValue , decode_alloc_or_unable
332-
333- alloc_id = raw_alloc ['alloc_id' ]
334- alloc_info = smir_info .allocs [alloc_id ]
335- value = decode_alloc_or_unable (alloc_info = alloc_info , types = smir_info .types )
336-
337- match value :
338- case UnableToDecodeValue (msg ):
339- _LOGGER .debug (f'Decoding failed: { msg } ' )
340- case _:
341- pass
342-
343- alloc_id_term = KApply ('allocId' , intToken (alloc_id ))
344- return alloc_id_term , value .to_kast ()
345-
346124 def run_smir (self , smir_info : SMIRInfo , start_symbol : str = 'main' , depth : int | None = None ) -> Pattern :
347125 smir_info = smir_info .reduce_to (start_symbol )
348126 init_config , init_constraints = self .make_call_config (smir_info , start_symbol = start_symbol , init = True )
0 commit comments