runmat_vm/bytecode/
region.rs1use runmat_types::{ProgramPointId, RegionContract, RegionId};
2use serde::{Deserialize, Serialize};
3
4use super::program::Bytecode;
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(deny_unknown_fields)]
8pub struct BytecodeRegionBoundary {
9 pub point: ProgramPointId,
10 pub pc: usize,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14#[serde(deny_unknown_fields)]
15pub struct BytecodeRegion {
16 pub id: RegionId,
17 pub entry: BytecodeRegionBoundary,
18 pub exits: Vec<BytecodeRegionBoundary>,
19}
20
21impl Bytecode {
22 pub fn install_regions(&mut self, contracts: &[RegionContract]) -> Result<(), String> {
27 let mut contracts = contracts.iter().collect::<Vec<_>>();
28 contracts.sort_by_key(|contract| contract.id);
29 if contracts.windows(2).any(|pair| pair[0].id == pair[1].id) {
30 return Err("region inventory contains duplicate identities".to_string());
31 }
32 let mut mapped = Vec::with_capacity(contracts.len());
33 for contract in contracts {
34 contract
35 .validate()
36 .map_err(|error| format!("{}: {}", error.path, error.message))?;
37 let function = runmat_hir::FunctionId(
38 usize::try_from(contract.id.function.0)
39 .map_err(|_| "region function identity exceeds this target".to_string())?,
40 );
41 let layout_points = self
46 .layout
47 .as_ref()
48 .and_then(|layout| layout.functions.get(&function))
49 .map(|layout| &layout.resume_points);
50 let registry_points = self
51 .function_registry
52 .functions
53 .get(&function)
54 .map(|function| &function.resume_points);
55 let Some(points) = layout_points
56 .filter(|points| contains_region_boundaries(contract, points))
57 .or_else(|| {
58 registry_points.filter(|points| contains_region_boundaries(contract, points))
59 })
60 else {
61 continue;
62 };
63 let region = map_bytecode_region(contract, points)?;
64 mapped.push((function, region));
65 }
66
67 self.regions = mapped.iter().map(|(_, region)| region.clone()).collect();
68 for function in self.function_registry.functions.values_mut() {
69 function.regions.clear();
70 }
71 for function in self.bound_functions.values_mut() {
72 function.regions.clear();
73 }
74 for (function, region) in mapped {
75 if let Some(bytecode) = self.function_registry.functions.get_mut(&function) {
76 bytecode.regions.push(region.clone());
77 }
78 if let Some(bytecode) = self.bound_functions.get_mut(&function) {
79 bytecode.regions.push(region);
80 }
81 }
82 Ok(())
83 }
84}
85
86fn contains_region_boundaries(
87 contract: &RegionContract,
88 points: &std::collections::BTreeMap<ProgramPointId, usize>,
89) -> bool {
90 points.contains_key(&contract.entry)
91 && contract.exits.iter().all(|exit| points.contains_key(exit))
92}
93
94fn map_bytecode_region(
95 contract: &RegionContract,
96 points: &std::collections::BTreeMap<ProgramPointId, usize>,
97) -> Result<BytecodeRegion, String> {
98 let entry = BytecodeRegionBoundary {
99 point: contract.entry,
100 pc: points.get(&contract.entry).copied().ok_or_else(|| {
101 format!(
102 "region {:?} entry {:?} has no bytecode boundary",
103 contract.id, contract.entry
104 )
105 })?,
106 };
107 let exits = contract
108 .exits
109 .iter()
110 .map(|point| {
111 Ok(BytecodeRegionBoundary {
112 point: *point,
113 pc: points.get(point).copied().ok_or_else(|| {
114 format!(
115 "region {:?} exit {:?} has no bytecode boundary",
116 contract.id, point
117 )
118 })?,
119 })
120 })
121 .collect::<Result<Vec<_>, String>>()?;
122 Ok(BytecodeRegion {
123 id: contract.id,
124 entry,
125 exits,
126 })
127}