sway_core/asm_generation/
finalized_asm.rs

1use super::instruction_set::InstructionSet;
2use super::{
3    fuel::{checks, data_section::DataSection},
4    ProgramABI, ProgramKind,
5};
6use crate::asm_generation::fuel::data_section::{Datum, Entry, EntryName};
7use crate::asm_lang::allocated_ops::{AllocatedOp, AllocatedOpcode, FuelAsmData};
8use crate::decl_engine::DeclRefFunction;
9use crate::source_map::SourceMap;
10use crate::BuildConfig;
11
12use etk_asm::asm::Assembler;
13use fuel_vm::fuel_asm::{Imm06, Imm12, Imm18, Imm24, Instruction, RegId};
14use sway_error::error::CompileError;
15use sway_error::handler::{ErrorEmitted, Handler};
16use sway_types::span::Span;
17use sway_types::SourceEngine;
18
19use std::{collections::BTreeMap, fmt};
20
21/// Represents an ASM set which has had register allocation, jump elimination, and optimization
22/// applied to it
23#[derive(Clone, serde::Serialize)]
24pub struct AsmInformation {
25    pub bytecode_size: u64,
26    pub data_section: DataSectionInformation,
27}
28
29#[derive(Default, Clone, Debug, serde::Serialize)]
30pub struct DataSectionInformation {
31    /// The total size of the data section in bytes
32    pub size: u64,
33    /// The used size of the data section in bytes
34    pub used: u64,
35    /// The data to be put in the data section of the asm
36    pub value_pairs: Vec<Entry>,
37}
38
39/// Represents an ASM set which has had register allocation, jump elimination, and optimization
40/// applied to it
41#[derive(Clone)]
42pub struct FinalizedAsm {
43    pub data_section: DataSection,
44    pub program_section: InstructionSet,
45    pub program_kind: ProgramKind,
46    pub entries: Vec<FinalizedEntry>,
47    pub abi: Option<ProgramABI>,
48}
49
50#[derive(Clone, Debug)]
51pub struct FinalizedEntry {
52    /// The original entry point function name.
53    pub fn_name: String,
54    /// The immediate instruction offset at which the entry function begins.
55    pub imm: u64,
56    /// The function selector (only `Some` for contract ABI methods).
57    pub selector: Option<[u8; 4]>,
58    /// If this entry is constructed from a test function contains the declaration id for that
59    /// function, otherwise contains `None`.
60    pub test_decl_ref: Option<DeclRefFunction>,
61}
62
63/// The bytecode for a sway program as well as the byte offsets of configuration-time constants in
64/// the bytecode.
65pub struct CompiledBytecode {
66    pub bytecode: Vec<u8>,
67    pub named_data_section_entries_offsets: BTreeMap<String, u64>,
68}
69
70impl FinalizedAsm {
71    pub(crate) fn to_bytecode_mut(
72        &mut self,
73        handler: &Handler,
74        source_map: &mut SourceMap,
75        source_engine: &SourceEngine,
76        build_config: &BuildConfig,
77    ) -> Result<CompiledBytecode, ErrorEmitted> {
78        match &self.program_section {
79            InstructionSet::Fuel { ops } => Ok(to_bytecode_mut(
80                ops,
81                &mut self.data_section,
82                source_map,
83                source_engine,
84                build_config,
85            )),
86            InstructionSet::Evm { ops } => {
87                let mut assembler = Assembler::new();
88                if let Err(e) = assembler.push_all(ops.clone()) {
89                    Err(handler.emit_err(CompileError::InternalOwned(e.to_string(), Span::dummy())))
90                } else {
91                    Ok(CompiledBytecode {
92                        bytecode: assembler.take(),
93                        named_data_section_entries_offsets: BTreeMap::new(),
94                    })
95                }
96            }
97        }
98    }
99}
100
101impl FinalizedEntry {
102    /// We assume the entry point is for a test function in the case it is neither an ABI method
103    /// (no selector) or it is not "main".
104    pub fn is_test(&self) -> bool {
105        self.selector.is_none()
106            && self.fn_name != sway_types::constants::DEFAULT_ENTRY_POINT_FN_NAME
107    }
108}
109
110impl fmt::Display for FinalizedAsm {
111    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112        write!(f, "{}\n{}", self.program_section, self.data_section)
113    }
114}
115
116fn to_bytecode_mut(
117    ops: &[AllocatedOp],
118    data_section: &mut DataSection,
119    source_map: &mut SourceMap,
120    source_engine: &SourceEngine,
121    build_config: &BuildConfig,
122) -> CompiledBytecode {
123    fn op_size_in_bytes(data_section: &DataSection, item: &AllocatedOp) -> u64 {
124        match &item.opcode {
125            AllocatedOpcode::LoadDataId(_reg, data_label)
126                if !data_section
127                    .has_copy_type(data_label)
128                    .expect("data label references non existent data -- internal error") =>
129            {
130                8
131            }
132            AllocatedOpcode::AddrDataId(_, id)
133                if data_section.data_id_to_offset(id) > usize::from(Imm12::MAX.to_u16()) =>
134            {
135                8
136            }
137            AllocatedOpcode::ConfigurablesOffsetPlaceholder => 8,
138            AllocatedOpcode::DataSectionOffsetPlaceholder => 8,
139            AllocatedOpcode::BLOB(count) => count.value() as u64 * 4,
140            AllocatedOpcode::CFEI(i) | AllocatedOpcode::CFSI(i) if i.value() == 0 => 0,
141            _ => 4,
142        }
143    }
144
145    // Some instructions may be omitted or expanded into multiple instructions, so we compute,
146    // using `op_size_in_bytes`, exactly how many ops will be generated to calculate the offset.
147    let mut offset_to_data_section_in_bytes = ops
148        .iter()
149        .fold(0, |acc, item| acc + op_size_in_bytes(data_section, item));
150
151    // A noop is inserted in ASM generation if required, to word-align the data section.
152    let mut ops_padded = Vec::new();
153    let ops = if offset_to_data_section_in_bytes & 7 == 0 {
154        ops
155    } else {
156        ops_padded.reserve(ops.len() + 1);
157        ops_padded.extend(ops.iter().cloned());
158        ops_padded.push(AllocatedOp {
159            opcode: AllocatedOpcode::NOOP,
160            comment: "word-alignment of data section".into(),
161            owning_span: None,
162        });
163        offset_to_data_section_in_bytes += 4;
164        &ops_padded
165    };
166
167    let mut offset_from_instr_start = 0;
168    for op in ops.iter() {
169        match &op.opcode {
170            AllocatedOpcode::LoadDataId(_reg, data_label)
171                if !data_section
172                    .has_copy_type(data_label)
173                    .expect("data label references non existent data -- internal error") =>
174            {
175                // For non-copy type loads, pre-insert pointers into the data_section so that
176                // from this point on, the data_section remains immutable. This is necessary
177                // so that when we take addresses of configurables, that address doesn't change
178                // later on if a non-configurable is added to the data-section.
179                let offset_bytes = data_section.data_id_to_offset(data_label) as u64;
180                // The -4 is because $pc is added in the *next* instruction.
181                let pointer_offset_from_current_instr =
182                    offset_to_data_section_in_bytes - offset_from_instr_start + offset_bytes - 4;
183                data_section.append_pointer(pointer_offset_from_current_instr);
184            }
185            _ => (),
186        }
187        offset_from_instr_start += op_size_in_bytes(data_section, op);
188    }
189
190    let mut bytecode = Vec::with_capacity(offset_to_data_section_in_bytes as usize);
191
192    if build_config.print_bytecode {
193        println!(";; --- START OF TARGET BYTECODE ---\n");
194    }
195
196    let mut last_span = None;
197    let mut indentation = if build_config.print_bytecode_spans {
198        4
199    } else {
200        0
201    };
202
203    let mut half_word_ix = 0;
204    let mut offset_from_instr_start = 0;
205    for op in ops.iter() {
206        let span = op.owning_span.clone();
207        let fuel_op = op.to_fuel_asm(
208            offset_to_data_section_in_bytes,
209            offset_from_instr_start,
210            data_section,
211        );
212        offset_from_instr_start += op_size_in_bytes(data_section, op);
213
214        match fuel_op {
215            FuelAsmData::DatasectionOffset(data) => {
216                if build_config.print_bytecode {
217                    print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
218                    println!(
219                        "                                                ;; {:?}",
220                        data
221                    );
222                }
223
224                // Static assert to ensure that we're only dealing with DataSectionOffsetPlaceholder,
225                // a one-word (8 bytes) data within the code. No other uses are known.
226                let _: [u8; 8] = data;
227
228                bytecode.extend(data.iter().cloned());
229                half_word_ix += 2;
230            }
231            FuelAsmData::ConfigurablesOffset(data) => {
232                if build_config.print_bytecode {
233                    print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
234                    println!(
235                        "                                                ;; {:?}",
236                        data
237                    );
238                }
239
240                // Static assert to ensure that we're only dealing with ConfigurablesOffsetPlaceholder,
241                // a 1-word (8 bytes) data within the code. No other uses are known.
242                let _: [u8; 8] = data;
243
244                bytecode.extend(data.iter().cloned());
245                half_word_ix += 2;
246            }
247            FuelAsmData::Instructions(instructions) => {
248                for instruction in instructions {
249                    // Print original source span only once
250                    if build_config.print_bytecode_spans {
251                        last_span = match (last_span, &span) {
252                            (None, Some(span)) => {
253                                indentation = 4;
254                                let line_col = span.start_pos().line_col();
255                                println!(
256                                    "{} @ {}:{}:{}",
257                                    span.as_str(),
258                                    span.source_id()
259                                        .map(|source_id| source_engine.get_path(source_id))
260                                        .map(|x| x.display().to_string())
261                                        .unwrap_or("<autogenerated>".to_string()),
262                                    line_col.line,
263                                    line_col.col
264                                );
265                                Some(span.clone())
266                            }
267                            (Some(last), Some(span)) if last != *span => {
268                                indentation = 4;
269                                let line_col = span.start_pos().line_col();
270                                println!(
271                                    "{} @ {}:{}:{}",
272                                    span.as_str(),
273                                    span.source_id()
274                                        .map(|source_id| source_engine.get_path(source_id))
275                                        .map(|x| x.display().to_string())
276                                        .unwrap_or("<autogenerated>".to_string()),
277                                    line_col.line,
278                                    line_col.col
279                                );
280                                Some(span.clone())
281                            }
282                            (last, _) => last,
283                        };
284                    }
285
286                    if build_config.print_bytecode {
287                        print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
288                        print_instruction(&instruction);
289                    }
290
291                    if let Some(span) = &span {
292                        source_map.insert(source_engine, half_word_ix, span);
293                    }
294
295                    let bytes = instruction.to_bytes();
296
297                    if build_config.print_bytecode {
298                        println!(";; {bytes:?}")
299                    }
300
301                    bytecode.extend(bytes.iter());
302                    half_word_ix += 1;
303                }
304            }
305        }
306    }
307
308    if build_config.print_bytecode {
309        println!(".data_section:");
310
311        let offset = bytecode.len();
312
313        fn print_entry(indentation: usize, offset: usize, pair: &Entry) {
314            print!("{}{:#010x} ", " ".repeat(indentation), offset);
315
316            match &pair.value {
317                Datum::Byte(w) => println!(".byte i{w}, as hex {w:02X}"),
318                Datum::Word(w) => {
319                    println!(".word i{w}, as hex be bytes ({:02X?})", w.to_be_bytes())
320                }
321                Datum::ByteArray(bs) => {
322                    print!(".bytes as hex ({bs:02X?}), len i{}, as ascii \"", bs.len());
323
324                    for b in bs {
325                        print!(
326                            "{}",
327                            if *b == b' ' || b.is_ascii_graphic() {
328                                *b as char
329                            } else {
330                                '.'
331                            }
332                        );
333                    }
334                    println!("\"");
335                }
336                Datum::Slice(bs) => {
337                    print!(".slice as hex ({bs:02X?}), len i{}, as ascii \"", bs.len());
338
339                    for b in bs {
340                        print!(
341                            "{}",
342                            if *b == b' ' || b.is_ascii_graphic() {
343                                *b as char
344                            } else {
345                                '.'
346                            }
347                        );
348                    }
349                    println!("\"");
350                }
351                Datum::Collection(els) => {
352                    println!(".collection");
353                    for e in els {
354                        print_entry(indentation + 1, offset, e);
355                    }
356                }
357            };
358        }
359
360        for (i, entry) in data_section.iter_all_entries().enumerate() {
361            let entry_offset = data_section.absolute_idx_to_offset(i);
362            print_entry(indentation, offset + entry_offset, &entry);
363        }
364
365        println!(";; --- END OF TARGET BYTECODE ---\n");
366    }
367
368    assert_eq!(half_word_ix * 4, offset_to_data_section_in_bytes as usize);
369    assert_eq!(bytecode.len(), offset_to_data_section_in_bytes as usize);
370
371    let num_nonconfigurables = data_section.non_configurables.len();
372    let named_data_section_entries_offsets = data_section
373        .configurables
374        .iter()
375        .enumerate()
376        .map(|(id, entry)| {
377            let EntryName::Configurable(name) = &entry.name else {
378                panic!("Non-configurable in configurables part of datasection");
379            };
380            (
381                name.clone(),
382                offset_to_data_section_in_bytes
383                    + data_section.absolute_idx_to_offset(id + num_nonconfigurables) as u64,
384            )
385        })
386        .collect::<BTreeMap<String, u64>>();
387
388    let mut data_section = data_section.serialize_to_bytes();
389    bytecode.append(&mut data_section);
390
391    CompiledBytecode {
392        bytecode,
393        named_data_section_entries_offsets,
394    }
395}
396
397// Code to pretty print bytecode
398fn print_reg(r: RegId) -> String {
399    match r {
400        RegId::BAL => "$bal".to_string(),
401        RegId::CGAS => "$cgas".to_string(),
402        RegId::ERR => "$err".to_string(),
403        RegId::FLAG => "$flag".to_string(),
404        RegId::FP => "$fp".to_string(),
405        RegId::GGAS => "$ggas".to_string(),
406        RegId::HP => "$hp".to_string(),
407        RegId::IS => "$is".to_string(),
408        RegId::OF => "$of".to_string(),
409        RegId::ONE => "$one".to_string(),
410        RegId::PC => "$pc".to_string(),
411        RegId::RET => "$ret".to_string(),
412        RegId::RETL => "$retl".to_string(),
413        RegId::SP => "$sp".to_string(),
414        RegId::SSP => "$ssp".to_string(),
415        RegId::WRITABLE => "$writable".to_string(),
416        RegId::ZERO => "$zero".to_string(),
417        _ => format!("R{:?}", r.to_u8()),
418    }
419}
420
421trait Args {
422    fn print(&self) -> String;
423}
424
425impl Args for RegId {
426    fn print(&self) -> String {
427        print_reg(*self)
428    }
429}
430impl Args for Imm06 {
431    fn print(&self) -> String {
432        format!("{:#x}", self.to_u8())
433    }
434}
435impl Args for Imm12 {
436    fn print(&self) -> String {
437        format!("{:#x}", self.to_u16())
438    }
439}
440impl Args for Imm18 {
441    fn print(&self) -> String {
442        format!("{:#x}", self.to_u32())
443    }
444}
445impl Args for Imm24 {
446    fn print(&self) -> String {
447        format!("{:#x}", self.to_u32())
448    }
449}
450impl Args for () {
451    fn print(&self) -> String {
452        String::new()
453    }
454}
455impl<A: Args> Args for (A,) {
456    fn print(&self) -> String {
457        self.0.print()
458    }
459}
460impl<A: Args, B: Args> Args for (A, B) {
461    fn print(&self) -> String {
462        format!("{} {}", self.0.print(), self.1.print())
463    }
464}
465impl<A: Args, B: Args, C: Args> Args for (A, B, C) {
466    fn print(&self) -> String {
467        format!("{} {} {}", self.0.print(), self.1.print(), self.2.print())
468    }
469}
470impl<A: Args, B: Args, C: Args, D: Args> Args for (A, B, C, D) {
471    fn print(&self) -> String {
472        format!(
473            "{} {} {} {}",
474            self.0.print(),
475            self.1.print(),
476            self.2.print(),
477            self.3.print()
478        )
479    }
480}
481
482fn f(name: &str, args: impl Args) {
483    let mut line = format!("{name} {}", args.print());
484    let s = " ".repeat(48 - line.len());
485    line.push_str(&s);
486    print!("{line}")
487}
488
489fn print_instruction(op: &Instruction) {
490    match op {
491        Instruction::ADD(x) => f("ADD", x.unpack()),
492        Instruction::AND(x) => f("AND", x.unpack()),
493        Instruction::DIV(x) => f("DIV", x.unpack()),
494        Instruction::EQ(x) => f("EQ", x.unpack()),
495        Instruction::EXP(x) => f("EXP", x.unpack()),
496        Instruction::GT(x) => f("GT", x.unpack()),
497        Instruction::LT(x) => f("LT", x.unpack()),
498        Instruction::MLOG(x) => f("MLOG", x.unpack()),
499        Instruction::MROO(x) => f("MROO", x.unpack()),
500        Instruction::MOD(x) => f("MOD", x.unpack()),
501        Instruction::MOVE(x) => f("MOVE", x.unpack()),
502        Instruction::MUL(x) => f("MUL", x.unpack()),
503        Instruction::NOT(x) => f("NOT", x.unpack()),
504        Instruction::OR(x) => f("OR", x.unpack()),
505        Instruction::SLL(x) => f("SLL", x.unpack()),
506        Instruction::SRL(x) => f("SRL", x.unpack()),
507        Instruction::SUB(x) => f("SUB", x.unpack()),
508        Instruction::XOR(x) => f("XOR", x.unpack()),
509        Instruction::MLDV(x) => f("MLDV", x.unpack()),
510        Instruction::RET(x) => f("RET", x.unpack()),
511        Instruction::RETD(x) => f("RETD", x.unpack()),
512        Instruction::ALOC(x) => f("ALOC", x.unpack()),
513        Instruction::MCL(x) => f("MCL", x.unpack()),
514        Instruction::MCP(x) => f("MCP", x.unpack()),
515        Instruction::MEQ(x) => f("MEQ", x.unpack()),
516        Instruction::BHSH(x) => f("BHSH", x.unpack()),
517        Instruction::BHEI(x) => f("BHEI", x.unpack()),
518        Instruction::BURN(x) => f("BURN", x.unpack()),
519        Instruction::CALL(x) => f("CALL", x.unpack()),
520        Instruction::CCP(x) => f("CCP", x.unpack()),
521        Instruction::CROO(x) => f("CROO", x.unpack()),
522        Instruction::CSIZ(x) => f("CSIZ", x.unpack()),
523        Instruction::CB(x) => f("CB", x.unpack()),
524        Instruction::LDC(x) => f("LDC", x.unpack()),
525        Instruction::LOG(x) => f("LOG", x.unpack()),
526        Instruction::LOGD(x) => f("LOGD", x.unpack()),
527        Instruction::MINT(x) => f("MINT", x.unpack()),
528        Instruction::RVRT(x) => f("RVRT", x.unpack()),
529        Instruction::SCWQ(x) => f("SCWQ", x.unpack()),
530        Instruction::SRW(x) => f("SRW", x.unpack()),
531        Instruction::SRWQ(x) => f("SRWQ", x.unpack()),
532        Instruction::SWW(x) => f("SWW", x.unpack()),
533        Instruction::SWWQ(x) => f("SWWQ", x.unpack()),
534        Instruction::TR(x) => f("TR", x.unpack()),
535        Instruction::TRO(x) => f("TRO", x.unpack()),
536        Instruction::ECK1(x) => f("ECK1", x.unpack()),
537        Instruction::ECR1(x) => f("ECR1", x.unpack()),
538        Instruction::ED19(x) => f("ED19", x.unpack()),
539        Instruction::K256(x) => f("K256", x.unpack()),
540        Instruction::S256(x) => f("S256", x.unpack()),
541        Instruction::TIME(x) => f("TIME", x.unpack()),
542        Instruction::NOOP(_) => f("NOOP", ()),
543        Instruction::FLAG(x) => f("FLAG", x.unpack()),
544        Instruction::BAL(x) => f("BAL", x.unpack()),
545        Instruction::JMP(x) => f("JMP", x.unpack()),
546        Instruction::JNE(x) => f("JNE", x.unpack()),
547        Instruction::SMO(x) => f("SMO", x.unpack()),
548        Instruction::ADDI(x) => f("ADDI", x.unpack()),
549        Instruction::ANDI(x) => f("ANDI", x.unpack()),
550        Instruction::DIVI(x) => f("DIVI", x.unpack()),
551        Instruction::EXPI(x) => f("EXPI", x.unpack()),
552        Instruction::MODI(x) => f("MODI", x.unpack()),
553        Instruction::MULI(x) => f("MULI", x.unpack()),
554        Instruction::ORI(x) => f("ORI", x.unpack()),
555        Instruction::SLLI(x) => f("SLLI", x.unpack()),
556        Instruction::SRLI(x) => f("SRLI", x.unpack()),
557        Instruction::SUBI(x) => f("SUBI", x.unpack()),
558        Instruction::XORI(x) => f("XORI", x.unpack()),
559        Instruction::JNEI(x) => f("JNEI", x.unpack()),
560        Instruction::LB(x) => f("LB", x.unpack()),
561        Instruction::LW(x) => f("LW", x.unpack()),
562        Instruction::SB(x) => f("SB", x.unpack()),
563        Instruction::SW(x) => f("SW", x.unpack()),
564        Instruction::MCPI(x) => f("MCPI", x.unpack()),
565        Instruction::GTF(x) => f("GTF", x.unpack()),
566        Instruction::MCLI(x) => f("MCLI", x.unpack()),
567        Instruction::GM(x) => f("GM", x.unpack()),
568        Instruction::MOVI(x) => f("MOVI", x.unpack()),
569        Instruction::JNZI(x) => f("JNZI", x.unpack()),
570        Instruction::JMPF(x) => f("JMPF", x.unpack()),
571        Instruction::JMPB(x) => f("JMPB", x.unpack()),
572        Instruction::JNZF(x) => f("JNZF", x.unpack()),
573        Instruction::JNZB(x) => f("JNZB", x.unpack()),
574        Instruction::JNEF(x) => f("JNEF", x.unpack()),
575        Instruction::JNEB(x) => f("JNEB", x.unpack()),
576        Instruction::JI(x) => f("JI", x.unpack()),
577        Instruction::CFEI(x) => f("CFEI", x.unpack()),
578        Instruction::CFSI(x) => f("CFSI", x.unpack()),
579        Instruction::CFE(x) => f("CFE", x.unpack()),
580        Instruction::CFS(x) => f("CFS", x.unpack()),
581        Instruction::PSHL(x) => f("PSHL", x.unpack()),
582        Instruction::PSHH(x) => f("PSHH", x.unpack()),
583        Instruction::POPL(x) => f("POPL", x.unpack()),
584        Instruction::POPH(x) => f("POPH", x.unpack()),
585        Instruction::WDCM(x) => f("WDCM", x.unpack()),
586        Instruction::WQCM(x) => f("WQCM", x.unpack()),
587        Instruction::WDOP(x) => f("WDOP", x.unpack()),
588        Instruction::WQOP(x) => f("WQOP", x.unpack()),
589        Instruction::WDML(x) => f("WDML", x.unpack()),
590        Instruction::WQML(x) => f("WQML", x.unpack()),
591        Instruction::WDDV(x) => f("WDDV", x.unpack()),
592        Instruction::WQDV(x) => f("WQDV", x.unpack()),
593        Instruction::WDMD(x) => f("WDMD", x.unpack()),
594        Instruction::WQMD(x) => f("WQMD", x.unpack()),
595        Instruction::WDAM(x) => f("WDAM", x.unpack()),
596        Instruction::WQAM(x) => f("WQAM", x.unpack()),
597        Instruction::WDMM(x) => f("WDMM", x.unpack()),
598        Instruction::WQMM(x) => f("WQMM", x.unpack()),
599        Instruction::ECAL(x) => f("ECAL", x.unpack()),
600        Instruction::BSIZ(x) => f("BSIZ", x.unpack()),
601        Instruction::BLDD(x) => f("BLDD", x.unpack()),
602        Instruction::ECOP(x) => f("ECOP", x.unpack()),
603        Instruction::EPAR(x) => f("EPAR", x.unpack()),
604    }
605}
606
607/// Checks for disallowed opcodes in non-contract code.
608/// i.e., if this is a script or predicate, we can't use certain contract opcodes.
609/// See https://github.com/FuelLabs/sway/issues/350 for details.
610pub fn check_invalid_opcodes(handler: &Handler, asm: &FinalizedAsm) -> Result<(), ErrorEmitted> {
611    match &asm.program_section {
612        InstructionSet::Fuel { ops } => match asm.program_kind {
613            ProgramKind::Contract | ProgramKind::Library => Ok(()),
614            ProgramKind::Script => checks::check_script_opcodes(handler, &ops[..]),
615            ProgramKind::Predicate => checks::check_predicate_opcodes(handler, &ops[..]),
616        },
617        InstructionSet::Evm { ops: _ } => Ok(()),
618    }
619}