1use super::{AllocatedProgram, FnName, SelectorOpt};
2use crate::{
3 asm_generation::{
4 fuel::{
5 abstract_instruction_set::AbstractInstructionSet,
6 allocated_abstract_instruction_set::AllocatedAbstractInstructionSet,
7 compiler_constants,
8 data_section::{DataSection, Entry, EntryName},
9 globals_section::GlobalsSection,
10 register_sequencer::RegisterSequencer,
11 },
12 ProgramKind,
13 },
14 asm_lang::{
15 allocated_ops::{AllocatedOpcode, AllocatedRegister},
16 AllocatedAbstractOp, ConstantRegister, ControlFlowOp, Label, VirtualImmediate12,
17 VirtualImmediate18, VirtualImmediate24,
18 },
19 decl_engine::DeclRefFunction,
20};
21use either::Either;
22use sway_error::error::CompileError;
23use sway_features::ExperimentalFeatures;
24
25pub(crate) struct AbstractEntry {
27 pub(crate) selector: SelectorOpt,
28 pub(crate) label: Label,
29 pub(crate) ops: AbstractInstructionSet,
30 pub(crate) name: FnName,
31 pub(crate) test_decl_ref: Option<DeclRefFunction>,
32}
33
34pub(crate) struct AbstractProgram {
40 kind: ProgramKind,
41 data_section: DataSection,
42 globals_section: GlobalsSection,
43 before_entries: AbstractInstructionSet,
44 entries: Vec<AbstractEntry>,
45 non_entries: Vec<AbstractInstructionSet>,
46 reg_seqr: RegisterSequencer,
47 experimental: ExperimentalFeatures,
48}
49
50impl AbstractProgram {
51 #[allow(clippy::too_many_arguments)]
52 pub(crate) fn new(
53 kind: ProgramKind,
54 data_section: DataSection,
55 globals_section: GlobalsSection,
56 before_entries: AbstractInstructionSet,
57 entries: Vec<AbstractEntry>,
58 non_entries: Vec<AbstractInstructionSet>,
59 reg_seqr: RegisterSequencer,
60 experimental: ExperimentalFeatures,
61 ) -> Self {
62 AbstractProgram {
63 kind,
64 data_section,
65 globals_section,
66 before_entries,
67 entries,
68 non_entries,
69 reg_seqr,
70 experimental,
71 }
72 }
73
74 pub(crate) fn is_empty(&self) -> bool {
76 self.non_entries.is_empty()
77 && self.entries.is_empty()
78 && self.data_section.iter_all_entries().next().is_none()
79 }
80
81 pub(crate) fn into_allocated_program(
83 mut self,
84 fallback_fn: Option<crate::asm_lang::Label>,
85 ) -> Result<AllocatedProgram, CompileError> {
86 let mut prologue = self.build_prologue();
87 self.append_globals_allocation(&mut prologue);
88 self.append_before_entries(&mut prologue)?;
89
90 match (self.experimental.new_encoding, self.kind) {
91 (true, ProgramKind::Contract) => {
92 self.append_jump_to_entry(&mut prologue);
93 }
94 (false, ProgramKind::Contract) => {
95 self.append_encoding_v0_contract_abi_switch(&mut prologue, fallback_fn);
96 }
97 _ => {}
98 }
99
100 let entries = self
102 .entries
103 .iter()
104 .map(|entry| {
105 (
106 entry.selector,
107 entry.label,
108 entry.name.clone(),
109 entry.test_decl_ref.clone(),
110 )
111 })
112 .collect();
113
114 let all_functions = self
116 .entries
117 .into_iter()
118 .map(|entry| entry.ops)
119 .chain(self.non_entries);
120
121 let abstract_functions = all_functions
123 .map(|instruction_set| instruction_set.optimize(&self.data_section))
124 .map(AbstractInstructionSet::verify)
125 .collect::<Result<Vec<AbstractInstructionSet>, CompileError>>()?;
126
127 let allocated_functions = abstract_functions
129 .into_iter()
130 .map(|abstract_instruction_set| {
131 let allocated = abstract_instruction_set.allocate_registers()?;
132 Ok(allocated.emit_pusha_popa())
133 })
134 .collect::<Result<Vec<AllocatedAbstractInstructionSet>, CompileError>>()?;
135
136 let functions = allocated_functions
139 .into_iter()
140 .map(|instruction_set| instruction_set.optimize())
141 .collect::<Vec<AllocatedAbstractInstructionSet>>();
142
143 Ok(AllocatedProgram {
144 kind: self.kind,
145 data_section: self.data_section,
146 prologue,
147 functions,
148 entries,
149 })
150 }
151
152 fn append_before_entries(
153 &self,
154 prologue: &mut AllocatedAbstractInstructionSet,
155 ) -> Result<(), CompileError> {
156 let before_entries = self.before_entries.clone().optimize(&self.data_section);
157 let before_entries = before_entries.verify()?;
158 let mut before_entries = before_entries.allocate_registers()?;
159 prologue.ops.append(&mut before_entries.ops);
160 Ok(())
161 }
162
163 fn build_prologue(&mut self) -> AllocatedAbstractInstructionSet {
177 const _: () = assert!(
178 crate::PRELUDE_CONFIGURABLES_OFFSET_IN_BYTES == 16,
179 "Inconsistency in the assumption of prelude organisation"
180 );
181 const _: () = assert!(
182 crate::PRELUDE_CONFIGURABLES_SIZE_IN_BYTES == 8,
183 "Inconsistency in the assumption of prelude organisation"
184 );
185 const _: () = assert!(
186 crate::PRELUDE_SIZE_IN_BYTES == 32,
187 "Inconsistency in the assumption of prelude organisation"
188 );
189 let label = self.reg_seqr.get_label();
190 AllocatedAbstractInstructionSet {
191 ops: [
192 AllocatedAbstractOp {
193 opcode: Either::Left(AllocatedOpcode::MOVE(
194 AllocatedRegister::Constant(ConstantRegister::Scratch),
195 AllocatedRegister::Constant(ConstantRegister::ProgramCounter),
196 )),
197 comment: String::new(),
198 owning_span: None,
199 },
200 AllocatedAbstractOp {
202 opcode: Either::Right(ControlFlowOp::Jump(label)),
203 comment: String::new(),
204 owning_span: None,
205 },
206 AllocatedAbstractOp {
208 opcode: Either::Right(ControlFlowOp::DataSectionOffsetPlaceholder),
209 comment: "data section offset".into(),
210 owning_span: None,
211 },
212 AllocatedAbstractOp {
214 opcode: Either::Right(ControlFlowOp::ConfigurablesOffsetPlaceholder),
215 comment: "configurables offset".into(),
216 owning_span: None,
217 },
218 AllocatedAbstractOp {
219 opcode: Either::Right(ControlFlowOp::Label(label)),
220 comment: "end of configurables offset".into(),
221 owning_span: None,
222 },
223 AllocatedAbstractOp {
225 opcode: Either::Left(AllocatedOpcode::LW(
226 AllocatedRegister::Constant(ConstantRegister::DataSectionStart),
227 AllocatedRegister::Constant(ConstantRegister::Scratch),
228 VirtualImmediate12::new_unchecked(1, "1 doesn't fit in 12 bits"),
229 )),
230 comment: "".into(),
231 owning_span: None,
232 },
233 AllocatedAbstractOp {
235 opcode: Either::Left(AllocatedOpcode::ADD(
236 AllocatedRegister::Constant(ConstantRegister::DataSectionStart),
237 AllocatedRegister::Constant(ConstantRegister::DataSectionStart),
238 AllocatedRegister::Constant(ConstantRegister::Scratch),
239 )),
240 comment: "".into(),
241 owning_span: None,
242 },
243 ]
244 .to_vec(),
245 }
246 }
247
248 fn append_jump_to_entry(&mut self, asm: &mut AllocatedAbstractInstructionSet) {
250 let entry = self.entries.iter().find(|x| x.name == "__entry").unwrap();
251 asm.ops.push(AllocatedAbstractOp {
252 opcode: Either::Right(ControlFlowOp::Jump(entry.label)),
253 comment: "jump to ABI function selector".into(),
254 owning_span: None,
255 });
256 }
257
258 fn append_encoding_v0_contract_abi_switch(
263 &mut self,
264 asm: &mut AllocatedAbstractInstructionSet,
265 fallback_fn: Option<crate::asm_lang::Label>,
266 ) {
267 const SELECTOR_WORD_OFFSET: u64 = 73;
268 const INPUT_SELECTOR_REG: AllocatedRegister = AllocatedRegister::Allocated(0);
269 const PROG_SELECTOR_REG: AllocatedRegister = AllocatedRegister::Allocated(1);
270 const CMP_RESULT_REG: AllocatedRegister = AllocatedRegister::Allocated(2);
271
272 asm.ops.push(AllocatedAbstractOp {
274 opcode: Either::Right(ControlFlowOp::Comment),
275 comment: "[function selection]: begin contract function selector switch".into(),
276 owning_span: None,
277 });
278
279 asm.ops.push(AllocatedAbstractOp {
281 opcode: Either::Left(AllocatedOpcode::LW(
282 INPUT_SELECTOR_REG,
283 AllocatedRegister::Constant(ConstantRegister::FramePointer),
284 VirtualImmediate12::new_unchecked(
285 SELECTOR_WORD_OFFSET,
286 "constant infallible value",
287 ),
288 )),
289 comment: "[function selection]: load input function selector".into(),
290 owning_span: None,
291 });
292
293 for entry in &self.entries {
295 let selector = match entry.selector {
296 Some(sel) => sel,
297 None => continue,
299 };
300
301 let data_label = self.data_section.insert_data_value(Entry::new_word(
303 u32::from_be_bytes(selector) as u64,
304 EntryName::NonConfigurable,
305 None,
306 ));
307
308 asm.ops.push(AllocatedAbstractOp {
310 opcode: Either::Left(AllocatedOpcode::LoadDataId(PROG_SELECTOR_REG, data_label)),
311 comment: format!(
312 "[function selection]: load function {} selector for comparison",
313 entry.name
314 ),
315 owning_span: None,
316 });
317
318 asm.ops.push(AllocatedAbstractOp {
320 opcode: Either::Left(AllocatedOpcode::EQ(
321 CMP_RESULT_REG,
322 INPUT_SELECTOR_REG,
323 PROG_SELECTOR_REG,
324 )),
325 comment: format!(
326 "[function selection]: compare function {} selector with input selector",
327 entry.name
328 ),
329 owning_span: None,
330 });
331
332 asm.ops.push(AllocatedAbstractOp {
334 opcode: Either::Right(ControlFlowOp::JumpIfNotZero(CMP_RESULT_REG, entry.label)),
336 comment: "[function selection]: jump to selected contract function".into(),
337 owning_span: None,
338 });
339 }
340
341 if let Some(fallback_fn) = fallback_fn {
342 asm.ops.push(AllocatedAbstractOp {
343 opcode: Either::Right(ControlFlowOp::Call(fallback_fn)),
344 comment: "[function selection]: call contract fallback function".into(),
345 owning_span: None,
346 });
347 }
348
349 asm.ops.push(AllocatedAbstractOp {
350 opcode: Either::Left(AllocatedOpcode::MOVI(
351 AllocatedRegister::Constant(ConstantRegister::Scratch),
352 VirtualImmediate18::new_unchecked(
353 compiler_constants::MISMATCHED_SELECTOR_REVERT_CODE.into(),
354 "constant must fit in 18 bits",
355 ),
356 )),
357 comment: "[function selection]: load revert code for mismatched function selector"
358 .into(),
359 owning_span: None,
360 });
361 asm.ops.push(AllocatedAbstractOp {
362 opcode: Either::Left(AllocatedOpcode::RVRT(AllocatedRegister::Constant(
363 ConstantRegister::Scratch,
364 ))),
365 comment: "[function selection]: revert if no selectors have matched".into(),
366 owning_span: None,
367 });
368 }
369
370 fn append_globals_allocation(&self, asm: &mut AllocatedAbstractInstructionSet) {
371 let len_in_bytes = self.globals_section.len_in_bytes();
372 asm.ops.push(AllocatedAbstractOp {
373 opcode: Either::Left(AllocatedOpcode::CFEI(VirtualImmediate24::new_unchecked(
374 len_in_bytes,
375 "length (bytes) must fit in 24 bits",
376 ))),
377 comment: "allocate stack space for globals".into(),
378 owning_span: None,
379 });
380 }
381}
382
383impl std::fmt::Display for AbstractProgram {
384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
385 writeln!(f, ";; Program kind: {:?}", self.kind)?;
386
387 writeln!(f, ";; --- Before Entries ---")?;
388 writeln!(f, "{}\n", self.before_entries)?;
389
390 writeln!(f, ";; --- Entries ---")?;
391 for entry in &self.entries {
392 writeln!(f, "{}\n", entry.ops)?;
393 }
394 writeln!(f, ";; --- Functions ---")?;
395 for function in &self.non_entries {
396 writeln!(f, "{function}\n")?;
397 }
398 writeln!(f, ";; --- Data ---")?;
399 write!(f, "{}", self.data_section)
400 }
401}