cranelift_codegen/
incremental_cache.rs1use core::fmt;
24use core::hash::{Hash, Hasher};
25
26use crate::alloc::vec::Vec;
27use crate::ir::Function;
28use crate::ir::function::{FunctionStencil, VersionMarker};
29use crate::machinst::{CompiledCode, CompiledCodeStencil};
30use crate::result::CompileResult;
31use crate::{CompileError, Context, trace};
32use crate::{isa::TargetIsa, timing};
33use alloc::borrow::Cow;
34use cranelift_control::ControlPlane;
35
36impl Context {
37 pub fn compile_with_cache(
40 &mut self,
41 isa: &dyn TargetIsa,
42 cache_store: &mut dyn CacheKvStore,
43 ctrl_plane: &mut ControlPlane,
44 ) -> CompileResult<'_, (&CompiledCode, bool)> {
45 let cache_key_hash = {
46 let _tt = timing::try_incremental_cache();
47
48 let cache_key_hash = compute_cache_key(isa, &self.func);
49
50 if let Some(blob) = cache_store.get(&cache_key_hash.0) {
51 match try_finish_recompile(&self.func, &blob) {
52 Ok(compiled_code) => {
53 let info = compiled_code.code_info();
54
55 if isa.flags().enable_incremental_compilation_cache_checks() {
56 let actual_result = self.compile(isa, ctrl_plane)?;
57 assert_eq!(*actual_result, compiled_code);
58 assert_eq!(actual_result.code_info(), info);
59 return Ok((actual_result, true));
61 }
62
63 let compiled_code = self.compiled_code.insert(compiled_code);
64 return Ok((compiled_code, true));
65 }
66 Err(err) => {
67 trace!("error when finishing recompilation: {err}");
68 }
69 }
70 }
71
72 cache_key_hash
73 };
74
75 let stencil = self
76 .compile_stencil(isa, ctrl_plane)
77 .map_err(|err| CompileError {
78 inner: err,
79 func: &self.func,
80 })?;
81
82 let stencil = {
83 let _tt = timing::store_incremental_cache();
84 let (stencil, res) = serialize_compiled(stencil);
85 if let Ok(blob) = res {
86 cache_store.insert(&cache_key_hash.0, blob);
87 }
88 stencil
89 };
90
91 let compiled_code = self
92 .compiled_code
93 .insert(stencil.apply_params(&self.func.params));
94
95 Ok((compiled_code, false))
96 }
97}
98
99pub trait CacheKvStore {
101 fn get(&self, key: &[u8]) -> Option<Cow<'_, [u8]>>;
103
104 fn insert(&mut self, key: &[u8], val: Vec<u8>);
107}
108
109#[derive(Clone, Hash, PartialEq, Eq)]
112pub struct CacheKeyHash([u8; 32]);
113
114impl std::fmt::Display for CacheKeyHash {
115 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
116 write!(f, "CacheKeyHash:{:?}", self.0)
117 }
118}
119
120#[derive(serde_derive::Serialize, serde_derive::Deserialize)]
121struct CachedFunc {
122 version_marker: VersionMarker,
125 stencil: CompiledCodeStencil,
126}
127
128struct CacheKey<'a> {
136 stencil: &'a FunctionStencil,
137 isa: &'a dyn TargetIsa,
138}
139
140impl<'a> Hash for CacheKey<'a> {
141 fn hash<H: Hasher>(&self, state: &mut H) {
142 self.stencil.hash(state);
143 self.isa.name().hash(state);
144 self.isa.triple().hash(state);
145 self.isa.flags().hash(state);
146 self.isa.isa_flags_hash_key().hash(state);
147 }
148}
149
150impl<'a> CacheKey<'a> {
151 fn new(isa: &'a dyn TargetIsa, f: &'a Function) -> Self {
155 CacheKey {
156 stencil: &f.stencil,
157 isa,
158 }
159 }
160}
161
162pub fn compute_cache_key(isa: &dyn TargetIsa, func: &Function) -> CacheKeyHash {
166 use core::hash::{Hash as _, Hasher};
167 use sha2::Digest as _;
168
169 struct Sha256Hasher(sha2::Sha256);
170
171 impl Hasher for Sha256Hasher {
172 fn finish(&self) -> u64 {
173 panic!("Sha256Hasher doesn't support finish!");
174 }
175 fn write(&mut self, bytes: &[u8]) {
176 self.0.update(bytes);
177 }
178 }
179
180 let cache_key = CacheKey::new(isa, func);
181
182 let mut hasher = Sha256Hasher(sha2::Sha256::new());
183 cache_key.hash(&mut hasher);
184 let hash: [u8; 32] = hasher.0.finalize().into();
185
186 CacheKeyHash(hash)
187}
188
189pub fn serialize_compiled(
195 result: CompiledCodeStencil,
196) -> (CompiledCodeStencil, Result<Vec<u8>, postcard::Error>) {
197 let cached = CachedFunc {
198 version_marker: VersionMarker,
199 stencil: result,
200 };
201 let result = postcard::to_allocvec(&cached);
202 (cached.stencil, result)
203}
204
205#[derive(Debug)]
207pub enum RecompileError {
208 VersionMismatch,
210 Deserialize(postcard::Error),
212}
213
214impl fmt::Display for RecompileError {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 match self {
217 RecompileError::VersionMismatch => write!(f, "cranelift version mismatch",),
218 RecompileError::Deserialize(err) => {
219 write!(f, "postcard failed during deserialization: {err}")
220 }
221 }
222 }
223}
224
225pub fn try_finish_recompile(func: &Function, bytes: &[u8]) -> Result<CompiledCode, RecompileError> {
231 match postcard::from_bytes::<CachedFunc>(bytes) {
232 Ok(result) => {
233 if result.version_marker != func.stencil.version_marker {
234 Err(RecompileError::VersionMismatch)
235 } else {
236 Ok(result.stencil.apply_params(&func.params))
237 }
238 }
239 Err(err) => Err(RecompileError::Deserialize(err)),
240 }
241}