sway_core/asm_generation/
from_ir.rs

1use super::{
2    asm_builder::AsmBuilder,
3    evm::EvmAsmBuilder,
4    finalized_asm::{check_invalid_opcodes, FinalizedAsm},
5    fuel::{
6        data_section::{DataId, DataSection},
7        fuel_asm_builder::FuelAsmBuilder,
8        register_sequencer::RegisterSequencer,
9    },
10};
11use crate::{asm_generation::ProgramKind, BuildConfig, BuildTarget};
12
13use crate::asm_lang::VirtualImmediate18;
14
15use sway_error::handler::{ErrorEmitted, Handler};
16use sway_ir::{Context, Kind, Module};
17
18pub fn compile_ir_context_to_finalized_asm(
19    handler: &Handler,
20    ir: &Context,
21    build_config: Option<&BuildConfig>,
22) -> Result<FinalizedAsm, ErrorEmitted> {
23    // Eventually when we get this 'correct' with no hacks we'll want to compile all the modules
24    // separately and then use a linker to connect them.  This way we could also keep binary caches
25    // of libraries and link against them, rather than recompile everything each time.  For now we
26    // assume there is one module.
27    assert!(ir.module_iter().count() == 1);
28
29    let module = ir.module_iter().next().unwrap();
30
31    let reg_seqr = RegisterSequencer::new();
32    let kind = match module.get_kind(ir) {
33        Kind::Contract => ProgramKind::Contract,
34        Kind::Library => ProgramKind::Library,
35        Kind::Predicate => ProgramKind::Predicate,
36        Kind::Script => ProgramKind::Script,
37    };
38
39    let build_target = match build_config {
40        Some(cfg) => cfg.build_target,
41        None => BuildTarget::default(),
42    };
43
44    let finalized_asm = match build_target {
45        BuildTarget::Fuel => compile(
46            handler,
47            ir,
48            module,
49            build_config,
50            FuelAsmBuilder::new(kind, DataSection::default(), reg_seqr, ir),
51        ),
52        BuildTarget::EVM => compile(
53            handler,
54            ir,
55            module,
56            build_config,
57            EvmAsmBuilder::new(kind, ir),
58        ),
59    }?;
60
61    check_invalid_opcodes(handler, &finalized_asm)?;
62
63    Ok(finalized_asm)
64}
65
66fn compile(
67    handler: &Handler,
68    context: &Context,
69    module: Module,
70    build_config: Option<&BuildConfig>,
71    mut builder: impl AsmBuilder,
72) -> Result<FinalizedAsm, ErrorEmitted> {
73    let mut fallback_fn = None;
74
75    // Pre-create labels for all functions before we generate other code, so we can call them
76    // before compiling them if needed.
77    for func in module.function_iter(context) {
78        let (start, _) = builder.func_to_labels(&func);
79        if func.is_fallback(context) {
80            fallback_fn = Some(start);
81        }
82    }
83
84    for config in module.iter_configs(context) {
85        builder.compile_configurable(config);
86    }
87
88    for function in module.function_iter(context) {
89        builder.compile_function(handler, function)?;
90    }
91
92    builder.finalize(handler, build_config, fallback_fn)
93}
94
95// -------------------------------------------------------------------------------------------------
96
97// NOTE: For stack storage we need to be aware:
98// - sizes are in bytes; CFEI reserves in bytes.
99// - offsets are in 64-bit words; LW/SW reads/writes to word offsets. XXX Wrap in a WordOffset struct.
100
101#[derive(Clone, Debug)]
102pub(super) enum Storage {
103    Data(DataId),              // Const storage in the data section.
104    Stack(u64), // Storage in the runtime stack starting at an absolute word offset.  Essentially a global.
105    Const(VirtualImmediate18), // An immediate value that can be moved to a register using MOVI.
106}
107
108pub enum StateAccessType {
109    Read,
110    Write,
111}