1use crate::{Pointer, Result};
2use ruint::aliases::U256;
3use std::{
4 fmt,
5 path::{Path, PathBuf},
6};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct BackendConfig {
15 pub opt_level: OptimizationLevel,
17 pub is_dumping: bool,
19 pub debug_assertions: bool,
21 pub debug_support: bool,
25 pub profiling_support: bool,
29 pub simple_perf: bool,
41 pub debug_file: Option<PathBuf>,
43}
44
45impl Default for BackendConfig {
46 fn default() -> Self {
47 Self {
48 opt_level: OptimizationLevel::Default,
49 is_dumping: false,
50 debug_assertions: cfg!(debug_assertions),
51 debug_support: true,
52 profiling_support: false,
53 simple_perf: true,
54 debug_file: None,
55 }
56 }
57}
58
59#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
61pub enum OptimizationLevel {
62 None,
64 Less,
66 #[default]
68 Default,
69 Aggressive,
74}
75
76impl std::str::FromStr for OptimizationLevel {
77 type Err = String;
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 Ok(match s {
81 "0" | "none" => Self::None,
82 "1" | "less" => Self::Less,
83 "2" | "default" => Self::Default,
84 "3" | "aggressive" => Self::Aggressive,
85 _ => return Err(format!("unknown optimization level: {s}")),
86 })
87 }
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
92pub enum IntCC {
93 Equal,
95 NotEqual,
97 SignedLessThan,
99 SignedGreaterThanOrEqual,
101 SignedGreaterThan,
103 SignedLessThanOrEqual,
105 UnsignedLessThan,
107 UnsignedGreaterThanOrEqual,
109 UnsignedGreaterThan,
111 UnsignedLessThanOrEqual,
113}
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq)]
119#[non_exhaustive]
120pub enum Attribute {
121 WillReturn,
123 NoReturn,
124 NoFree,
125 NoRecurse,
126 NoSync,
127 NoUnwind,
128 NonLazyBind,
129 UWTable,
130 AllFramePointers,
131 NativeTargetCpu,
132 Cold,
133 Hot,
134 HintInline,
135 AlwaysInline,
136 NoInline,
137 Speculatable,
138
139 NoAlias,
141 NoCapture,
142 NoUndef,
143 Align(u64),
144 NonNull,
145 Dereferenceable(u64),
146 SRet(u64),
148 ReadNone,
149 ReadOnly,
150 WriteOnly,
151 Writable,
152 ArgMemOnly,
154 Initializes(u64),
156 DeadOnReturn,
159 }
161
162#[derive(Clone, Copy, Debug, PartialEq, Eq)]
164pub enum Linkage {
165 Import,
167 Public,
169 Private,
171}
172
173#[derive(Clone, Copy, Debug, PartialEq, Eq)]
175pub enum FunctionAttributeLocation {
176 Return,
178 Param(u32),
180 Function,
182}
183
184#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
186pub enum CallConv {
187 #[default]
188 Default,
189 Cold,
191}
192
193#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
195pub enum TailCallKind {
196 #[default]
197 None,
198 Tail,
199 MustTail,
200 NoTail,
201}
202
203pub trait BackendTypes: Sized {
204 type Type: Copy + Eq + fmt::Debug;
205 type Value: Copy + Eq + fmt::Debug;
206 type StackSlot: Copy + Eq + fmt::Debug;
207 type BasicBlock: Copy + Eq + fmt::Debug;
208 type Function: Copy + Eq + fmt::Debug;
209}
210
211#[allow(clippy::missing_safety_doc)]
212pub trait Backend: BackendTypes + TypeMethods {
213 type Builder<'a>: Builder<
214 Type = Self::Type,
215 Value = Self::Value,
216 StackSlot = Self::StackSlot,
217 BasicBlock = Self::BasicBlock,
218 Function = Self::Function,
219 >
220 where
221 Self: 'a;
222 type FuncId: Copy + Eq + std::hash::Hash + fmt::Debug;
223
224 fn ir_extension(&self) -> &'static str;
225
226 fn set_module_name(&mut self, name: &str);
227
228 fn config(&self) -> &BackendConfig;
230
231 fn apply_config(&mut self, config: BackendConfig);
235
236 fn finalize_debug_info(&mut self) -> Result<()> {
240 Ok(())
241 }
242 fn dump_ir(&mut self, path: &Path) -> Result<()>;
243 fn dump_disasm(&mut self, path: &Path) -> Result<()>;
244
245 fn is_aot(&self) -> bool;
246
247 fn function_name_is_unique(&self, name: &str) -> bool;
248
249 fn build_function(
250 &mut self,
251 name: &str,
252 ret: Option<Self::Type>,
253 params: &[Self::Type],
254 param_names: &[&str],
255 linkage: Linkage,
256 ) -> Result<(Self::Builder<'_>, Self::FuncId)>;
257 fn verify_module(&mut self) -> Result<()>;
258 fn optimize_module(&mut self) -> Result<()>;
259 fn write_object<W: std::io::Write>(&mut self, w: W) -> Result<()>;
260 fn jit_function(&mut self, id: Self::FuncId) -> Result<usize>;
261
262 fn function_name(&self, id: Self::FuncId) -> Option<&str>;
264
265 fn function_sizes(&self) -> Vec<(String, usize)> {
267 Vec::new()
268 }
269
270 fn clear_ir(&mut self) -> Result<()>;
275
276 unsafe fn free_function(&mut self, id: Self::FuncId) -> Result<()>;
277 unsafe fn free_all_functions(&mut self) -> Result<()>;
278}
279
280pub trait TypeMethods: BackendTypes {
281 fn type_ptr(&self) -> Self::Type;
282 fn type_ptr_sized_int(&self) -> Self::Type;
283 fn type_int(&self, bits: u32) -> Self::Type;
284 fn type_array(&self, ty: Self::Type, size: u32) -> Self::Type;
285 fn type_bit_width(&self, ty: Self::Type) -> u32;
286}
287
288pub trait Builder: BackendTypes + TypeMethods {
289 fn create_block(&mut self, name: &str) -> Self::BasicBlock;
290 fn create_block_after(&mut self, after: Self::BasicBlock, name: &str) -> Self::BasicBlock;
291 fn switch_to_block(&mut self, block: Self::BasicBlock);
292 fn seal_block(&mut self, block: Self::BasicBlock);
293 fn seal_all_blocks(&mut self);
294 fn set_current_block_cold(&mut self);
295 fn current_block(&mut self) -> Option<Self::BasicBlock>;
296 fn block_addr(&mut self, block: Self::BasicBlock) -> Option<Self::Value>;
297
298 fn add_comment_to_current_inst(&mut self, comment: &str);
299
300 fn set_debug_location(&mut self, _line: u32, _col: u32) {}
302
303 fn clear_debug_location(&mut self) {}
305
306 fn fn_param(&mut self, index: usize) -> Self::Value;
307 fn num_fn_params(&self) -> usize;
308
309 fn bool_const(&mut self, value: bool) -> Self::Value;
310 fn iconst(&mut self, ty: Self::Type, value: i64) -> Self::Value;
312 fn uconst(&mut self, ty: Self::Type, value: u64) -> Self::Value;
313 fn iconst_256(&mut self, value: impl TryInto<U256>) -> Self::Value;
314 fn cstr_const(&mut self, value: &std::ffi::CStr) -> Self::Value {
315 self.str_const(value.to_str().unwrap())
316 }
317 fn str_const(&mut self, value: &str) -> Self::Value;
318 fn nullptr(&mut self) -> Self::Value;
319
320 fn new_stack_slot(&mut self, ty: Self::Type, name: &str) -> Pointer<Self> {
321 Pointer::new_stack_slot(self, ty, name)
322 }
323 fn new_stack_slot_raw(&mut self, ty: Self::Type, name: &str) -> Self::StackSlot;
324 fn stack_load(&mut self, ty: Self::Type, slot: Self::StackSlot, name: &str) -> Self::Value;
325 fn stack_store(&mut self, value: Self::Value, slot: Self::StackSlot);
326 fn stack_addr(&mut self, ty: Self::Type, slot: Self::StackSlot) -> Self::Value;
327
328 fn load(&mut self, ty: Self::Type, ptr: Self::Value, name: &str) -> Self::Value;
330 fn load_aligned(
332 &mut self,
333 ty: Self::Type,
334 ptr: Self::Value,
335 align: usize,
336 name: &str,
337 ) -> Self::Value;
338 fn store(&mut self, value: Self::Value, ptr: Self::Value);
340 fn store_aligned(&mut self, value: Self::Value, ptr: Self::Value, align: usize);
342
343 fn nop(&mut self);
344 fn ret(&mut self, values: &[Self::Value]);
345 fn assume(&mut self, cond: Self::Value) {
346 let _ = cond;
347 }
348
349 fn icmp(&mut self, cond: IntCC, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
350 fn icmp_imm(&mut self, cond: IntCC, lhs: Self::Value, rhs: i64) -> Self::Value;
351 fn is_null(&mut self, ptr: Self::Value) -> Self::Value;
352 fn is_not_null(&mut self, ptr: Self::Value) -> Self::Value;
353
354 fn br(&mut self, dest: Self::BasicBlock);
355 fn brif(
356 &mut self,
357 cond: Self::Value,
358 then_block: Self::BasicBlock,
359 else_block: Self::BasicBlock,
360 );
361 fn brif_cold(
362 &mut self,
363 cond: Self::Value,
364 then_block: Self::BasicBlock,
365 else_block: Self::BasicBlock,
366 then_is_cold: bool,
367 ) {
368 let _ = then_is_cold;
369 self.brif(cond, then_block, else_block)
370 }
371 fn switch(
372 &mut self,
373 index: Self::Value,
374 default: Self::BasicBlock,
375 targets: &[(u64, Self::BasicBlock)],
376 default_is_cold: bool,
377 );
378 fn br_indirect(&mut self, address: Self::Value, destinations: &[Self::BasicBlock]);
379 fn phi(&mut self, ty: Self::Type, incoming: &[(Self::Value, Self::BasicBlock)]) -> Self::Value;
380 fn select(
381 &mut self,
382 cond: Self::Value,
383 then_value: Self::Value,
384 else_value: Self::Value,
385 ) -> Self::Value;
386
387 fn iadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
388 fn isub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
389 fn imul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
390 fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
391 fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
392 fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
393 fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
394
395 fn iadd_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
396 fn isub_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
397 fn imul_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
398
399 fn uadd_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value);
401 fn usub_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value);
402
403 fn uadd_sat(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
404
405 fn umax(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
406 fn umin(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
407 fn bswap(&mut self, value: Self::Value) -> Self::Value;
408
409 fn bitor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
410 fn bitand(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
411 fn bitxor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
412 fn bitnot(&mut self, value: Self::Value) -> Self::Value;
413 fn clz(&mut self, value: Self::Value) -> Self::Value;
414
415 fn bitor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
416 fn bitand_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
417 fn bitxor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value;
418
419 fn ishl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
420 fn ushr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
421 fn sshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
422
423 fn zext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value;
424 fn sext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value;
425 #[doc(alias = "trunc")]
426 fn ireduce(&mut self, to: Self::Type, value: Self::Value) -> Self::Value;
427
428 fn inttoptr(&mut self, value: Self::Value, ty: Self::Type) -> Self::Value;
430
431 fn gep(
432 &mut self,
433 ty: Self::Type,
434 ptr: Self::Value,
435 indexes: &[Self::Value],
436 name: &str,
437 ) -> Self::Value;
438
439 #[must_use]
440 fn call(&mut self, function: Self::Function, args: &[Self::Value]) -> Option<Self::Value> {
441 self.tail_call(function, args, TailCallKind::None)
442 }
443 #[must_use]
444 fn tail_call(
445 &mut self,
446 function: Self::Function,
447 args: &[Self::Value],
448 tail_call: TailCallKind,
449 ) -> Option<Self::Value>;
450
451 fn is_compile_time_known(&mut self, value: Self::Value) -> Option<Self::Value>;
453
454 fn memcpy(&mut self, dst: Self::Value, src: Self::Value, len: Self::Value);
455 fn memcpy_inline(&mut self, dst: Self::Value, src: Self::Value, len: i64) {
456 let len = self.iconst(self.type_int(64), len);
457 self.memcpy(dst, src, len);
458 }
459
460 fn unreachable(&mut self);
461
462 fn get_or_build_function(
463 &mut self,
464 name: &str,
465 params: &[Self::Type],
466 ret: Option<Self::Type>,
467 linkage: Linkage,
468 build: impl FnOnce(&mut Self),
469 ) -> Self::Function;
470
471 fn get_function(&mut self, name: &str) -> Option<Self::Function>;
472
473 fn get_printf_function(&mut self) -> Self::Function;
474
475 fn add_function(
479 &mut self,
480 name: &str,
481 params: &[Self::Type],
482 ret: Option<Self::Type>,
483 address: Option<usize>,
484 linkage: Linkage,
485 call_conv: CallConv,
486 ) -> Self::Function;
487
488 fn add_function_stub(
490 &mut self,
491 function: Self::Function,
492 call_conv: CallConv,
493 ) -> Self::Function {
494 let _ = call_conv;
495 function
496 }
497
498 fn add_function_attribute(
502 &mut self,
503 function: Option<Self::Function>,
504 attribute: Attribute,
505 loc: FunctionAttributeLocation,
506 );
507}
508
509#[cfg(test)]
510mod tests {
511 use super::{BackendConfig, OptimizationLevel};
512 use std::{path::PathBuf, str::FromStr};
513
514 #[test]
515 fn backend_config_defaults_match_runtime_settings() {
516 let config = BackendConfig::default();
517
518 assert_eq!(config.opt_level, OptimizationLevel::Default);
519 assert!(!config.is_dumping);
520 assert_eq!(config.debug_assertions, cfg!(debug_assertions));
521 assert!(config.debug_support);
522 assert!(!config.profiling_support);
523 assert!(config.simple_perf);
524 assert_eq!(config.debug_file, None);
525 }
526
527 #[test]
528 fn backend_config_is_cloneable_and_comparable() {
529 let config = BackendConfig {
530 opt_level: OptimizationLevel::Aggressive,
531 is_dumping: true,
532 debug_assertions: true,
533 debug_support: false,
534 profiling_support: true,
535 simple_perf: false,
536 debug_file: Some(PathBuf::from("debug.sol")),
537 };
538
539 assert_eq!(config.clone(), config);
540 }
541
542 #[test]
543 fn optimization_level_parses_numeric_and_named_values() {
544 for (input, expected) in [
545 ("0", OptimizationLevel::None),
546 ("none", OptimizationLevel::None),
547 ("1", OptimizationLevel::Less),
548 ("less", OptimizationLevel::Less),
549 ("2", OptimizationLevel::Default),
550 ("default", OptimizationLevel::Default),
551 ("3", OptimizationLevel::Aggressive),
552 ("aggressive", OptimizationLevel::Aggressive),
553 ] {
554 assert_eq!(OptimizationLevel::from_str(input), Ok(expected));
555 }
556 }
557
558 #[test]
559 fn optimization_level_reports_unknown_values() {
560 assert_eq!(
561 OptimizationLevel::from_str("fast").unwrap_err(),
562 "unknown optimization level: fast"
563 );
564 }
565}