11from __future__ import annotations
22
3+ import json
34import logging
45import shutil
56from abc import ABC , abstractmethod
67from dataclasses import dataclass
7- from pathlib import Path
88from typing import TYPE_CHECKING
99
1010from pyk .kast .inner import KApply , KSort
1515from .kmir import KMIR
1616
1717if TYPE_CHECKING :
18+ from pathlib import Path
1819 from typing import Any , Final
1920
2021 from pyk .kast import KInner
@@ -54,45 +55,74 @@ def create_kmir(self, *, bug_report_file: Path | None = None) -> KMIR:
5455 )
5556
5657
58+ @dataclass
59+ class KompileDigest :
60+ digest : str
61+ symbolic : bool
62+
63+ @staticmethod
64+ def load (target_dir : Path ) -> KompileDigest :
65+ digest_file = KompileDigest ._digest_file (target_dir )
66+
67+ if not digest_file .exists ():
68+ raise ValueError (f'Digest file does not exist: { digest_file } ' )
69+
70+ data = json .loads (digest_file .read_text ())
71+ return KompileDigest (
72+ digest = data ['digest' ],
73+ symbolic = data ['symbolic' ],
74+ )
75+
76+ def write (self , target_dir : Path ) -> None :
77+ self ._digest_file (target_dir ).write_text (
78+ json .dumps (
79+ {
80+ 'digest' : self .digest ,
81+ 'symbolic' : self .symbolic ,
82+ },
83+ ),
84+ )
85+
86+ @staticmethod
87+ def _digest_file (target_dir : Path ) -> Path :
88+ return target_dir / 'smir-digest.json'
89+
90+
5791def kompile_smir (
5892 smir_info : SMIRInfo ,
59- target_dir : str ,
93+ target_dir : Path ,
6094 bug_report : Path | None = None ,
6195 symbolic : bool = True ,
6296) -> KompiledSMIR :
63- kmir = KMIR ( HASKELL_DEF_DIR )
64-
65- def _insert_rules_and_write ( input_file : Path , rules : list [ str ], output_file : Path ) -> None :
66- with open ( input_file , 'r' ) as f :
67- lines = f . readlines ()
97+ kompile_digest : KompileDigest | None = None
98+ try :
99+ kompile_digest = KompileDigest . load ( target_dir )
100+ except Exception :
101+ pass
68102
69- # last line must start with 'endmodule'
70- last_line = lines [- 1 ]
71- assert last_line .startswith ('endmodule' )
72- new_lines = lines [:- 1 ]
103+ target_hs_path = target_dir / 'haskell'
104+ target_llvm_lib_path = target_dir / 'llvm-library'
105+ target_llvm_path = target_dir / 'llvm'
73106
74- # Insert rules before the endmodule line
75- new_lines .append (f'\n // Generated from file { input_file } \n \n ' )
76- new_lines .extend (rules )
77- new_lines .append ('\n ' + last_line )
107+ if kompile_digest is not None and kompile_digest .digest == smir_info .digest and kompile_digest .symbolic == symbolic :
108+ _LOGGER .info (f'Kompiled SMIR up-to-date, no kompilation necessary: { target_dir } ' )
109+ if symbolic :
110+ return KompiledSymbolic (haskell_dir = target_hs_path , llvm_lib_dir = target_llvm_lib_path )
111+ else :
112+ return KompiledConcrete (llvm_dir = target_llvm_path )
78113
79- # Write to output file
80- with open (output_file , 'w' ) as f :
81- f .writelines (new_lines )
114+ _LOGGER .info (f'Kompiling SMIR program: { target_dir } ' )
82115
83- target_path = Path (target_dir )
84- # TODO if target dir exists and contains files, check file dates (definition files and interpreter)
85- # to decide whether or not to recompile. For now we always recompile.
86- target_path .mkdir (parents = True , exist_ok = True )
116+ kompile_digest = KompileDigest (digest = smir_info .digest , symbolic = symbolic )
117+ target_dir .mkdir (parents = True , exist_ok = True )
87118
119+ kmir = KMIR (HASKELL_DEF_DIR )
88120 rules = _make_kore_rules (kmir , smir_info )
89121 _LOGGER .info (f'Generated { len (rules )} function equations to add to `definition.kore' )
90122
91123 if symbolic :
92124 # Create output directories
93- target_llvm_path = target_path / 'llvm-library'
94- target_llvmdt_path = target_llvm_path / 'dt'
95- target_hs_path = target_path / 'haskell'
125+ target_llvmdt_path = target_llvm_lib_path / 'dt'
96126
97127 _LOGGER .info (f'Creating directories { target_llvmdt_path } and { target_hs_path } ' )
98128 target_llvmdt_path .mkdir (parents = True , exist_ok = True )
@@ -101,7 +131,7 @@ def _insert_rules_and_write(input_file: Path, rules: list[str], output_file: Pat
101131 # Process LLVM definition
102132 _LOGGER .info ('Writing LLVM definition file' )
103133 llvm_def_file = LLVM_LIB_DIR / 'definition.kore'
104- llvm_def_output = target_llvm_path / 'definition.kore'
134+ llvm_def_output = target_llvm_lib_path / 'definition.kore'
105135 _insert_rules_and_write (llvm_def_file , rules , llvm_def_output )
106136
107137 # Run llvm-kompile-matching and llvm-kompile for LLVM
@@ -123,7 +153,7 @@ def _insert_rules_and_write(input_file: Path, rules: list[str], output_file: Pat
123153 '-O2' ,
124154 '--' ,
125155 '-o' ,
126- target_llvm_path / 'interpreter' ,
156+ target_llvm_lib_path / 'interpreter' ,
127157 ],
128158 check = True ,
129159 )
@@ -141,13 +171,11 @@ def _insert_rules_and_write(input_file: Path, rules: list[str], output_file: Pat
141171 shutil .copy2 (file_path , target_hs_path / file_path .name )
142172 elif file_path .is_dir ():
143173 shutil .copytree (file_path , target_hs_path / file_path .name , dirs_exist_ok = True )
144- return KompiledSymbolic (
145- haskell_dir = target_hs_path ,
146- llvm_lib_dir = target_llvm_path ,
147- )
148- else :
149174
150- target_llvm_path = target_path / 'llvm'
175+ kompile_digest .write (target_dir )
176+ return KompiledSymbolic (haskell_dir = target_hs_path , llvm_lib_dir = target_llvm_lib_path )
177+
178+ else :
151179 target_llvmdt_path = target_llvm_path / 'dt'
152180 _LOGGER .info (f'Creating directory { target_llvmdt_path } ' )
153181 target_llvmdt_path .mkdir (parents = True , exist_ok = True )
@@ -183,6 +211,8 @@ def _insert_rules_and_write(input_file: Path, rules: list[str], output_file: Pat
183211 for file in to_copy :
184212 _LOGGER .info (f'Copying file { file } ' )
185213 shutil .copy2 (LLVM_DEF_DIR / file , target_llvm_path / file )
214+
215+ kompile_digest .write (target_dir )
186216 return KompiledConcrete (llvm_dir = target_llvm_path )
187217
188218
@@ -294,3 +324,22 @@ def _decode_alloc(smir_info: SMIRInfo, raw_alloc: Any) -> tuple[KInner, KInner]:
294324
295325 alloc_id_term = KApply ('allocId' , intToken (alloc_id ))
296326 return alloc_id_term , value .to_kast ()
327+
328+
329+ def _insert_rules_and_write (input_file : Path , rules : list [str ], output_file : Path ) -> None :
330+ with open (input_file , 'r' ) as f :
331+ lines = f .readlines ()
332+
333+ # last line must start with 'endmodule'
334+ last_line = lines [- 1 ]
335+ assert last_line .startswith ('endmodule' )
336+ new_lines = lines [:- 1 ]
337+
338+ # Insert rules before the endmodule line
339+ new_lines .append (f'\n // Generated from file { input_file } \n \n ' )
340+ new_lines .extend (rules )
341+ new_lines .append ('\n ' + last_line )
342+
343+ # Write to output file
344+ with open (output_file , 'w' ) as f :
345+ f .writelines (new_lines )
0 commit comments