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::{AllocatedInstruction, AllocatedOp, 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            AllocatedInstruction::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            AllocatedInstruction::AddrDataId(_, _data_id) => 8,
133            AllocatedInstruction::ConfigurablesOffsetPlaceholder => 8,
134            AllocatedInstruction::DataSectionOffsetPlaceholder => 8,
135            AllocatedInstruction::BLOB(count) => count.value() as u64 * 4,
136            AllocatedInstruction::CFEI(i) | AllocatedInstruction::CFSI(i) if i.value() == 0 => 0,
137            _ => 4,
138        }
139    }
140
141    // Some instructions may be omitted or expanded into multiple instructions, so we compute,
142    // using `op_size_in_bytes`, exactly how many ops will be generated to calculate the offset.
143    let mut offset_to_data_section_in_bytes = ops
144        .iter()
145        .fold(0, |acc, item| acc + op_size_in_bytes(data_section, item));
146
147    // A noop is inserted in ASM generation if required, to word-align the data section.
148    let mut ops_padded = Vec::new();
149    let ops = if offset_to_data_section_in_bytes & 7 == 0 {
150        ops
151    } else {
152        ops_padded.reserve(ops.len() + 1);
153        ops_padded.extend(ops.iter().cloned());
154        ops_padded.push(AllocatedOp {
155            opcode: AllocatedInstruction::NOOP,
156            comment: "word-alignment of data section".into(),
157            owning_span: None,
158        });
159        offset_to_data_section_in_bytes += 4;
160        &ops_padded
161    };
162
163    let mut offset_from_instr_start = 0;
164    for op in ops.iter() {
165        match &op.opcode {
166            AllocatedInstruction::LoadDataId(_reg, data_label)
167                if !data_section
168                    .has_copy_type(data_label)
169                    .expect("data label references non existent data -- internal error") =>
170            {
171                // For non-copy type loads, pre-insert pointers into the data_section so that
172                // from this point on, the data_section remains immutable. This is necessary
173                // so that when we take addresses of configurables, that address doesn't change
174                // later on if a non-configurable is added to the data-section.
175                let offset_bytes = data_section.data_id_to_offset(data_label) as u64;
176                // The -4 is because $pc is added in the *next* instruction.
177                let pointer_offset_from_current_instr =
178                    offset_to_data_section_in_bytes - offset_from_instr_start + offset_bytes - 4;
179                data_section.append_pointer(pointer_offset_from_current_instr);
180            }
181            _ => (),
182        }
183        offset_from_instr_start += op_size_in_bytes(data_section, op);
184    }
185
186    let mut bytecode = Vec::with_capacity(offset_to_data_section_in_bytes as usize);
187
188    if build_config.print_bytecode {
189        println!(";; --- START OF TARGET BYTECODE ---\n");
190    }
191
192    let mut last_span = None;
193    let mut indentation = if build_config.print_bytecode_spans {
194        4
195    } else {
196        0
197    };
198
199    let mut half_word_ix = 0;
200    let mut offset_from_instr_start = 0;
201    for op in ops.iter() {
202        let span = op.owning_span.clone();
203        let fuel_op = op.to_fuel_asm(
204            offset_to_data_section_in_bytes,
205            offset_from_instr_start,
206            data_section,
207        );
208        offset_from_instr_start += op_size_in_bytes(data_section, op);
209
210        match fuel_op {
211            FuelAsmData::DatasectionOffset(data) => {
212                if build_config.print_bytecode {
213                    print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
214                    println!("                                                ;; {data:?}");
215                }
216
217                // Static assert to ensure that we're only dealing with DataSectionOffsetPlaceholder,
218                // a one-word (8 bytes) data within the code. No other uses are known.
219                let _: [u8; 8] = data;
220
221                bytecode.extend(data.iter().cloned());
222                half_word_ix += 2;
223            }
224            FuelAsmData::ConfigurablesOffset(data) => {
225                if build_config.print_bytecode {
226                    print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
227                    println!("                                                ;; {data:?}");
228                }
229
230                // Static assert to ensure that we're only dealing with ConfigurablesOffsetPlaceholder,
231                // a 1-word (8 bytes) data within the code. No other uses are known.
232                let _: [u8; 8] = data;
233
234                bytecode.extend(data.iter().cloned());
235                half_word_ix += 2;
236            }
237            FuelAsmData::Instructions(instructions) => {
238                for instruction in instructions {
239                    // Print original source span only once
240                    if build_config.print_bytecode_spans {
241                        last_span = match (last_span, &span) {
242                            (None, Some(span)) => {
243                                indentation = 4;
244                                let line_col = span.start_line_col_one_index();
245                                println!(
246                                    "{} @ {}:{}:{}",
247                                    span.as_str(),
248                                    span.source_id()
249                                        .map(|source_id| source_engine.get_path(source_id))
250                                        .map(|x| x.display().to_string())
251                                        .unwrap_or("<autogenerated>".to_string()),
252                                    line_col.line,
253                                    line_col.col
254                                );
255                                Some(span.clone())
256                            }
257                            (Some(last), Some(span)) if last != *span => {
258                                indentation = 4;
259                                let line_col = span.start_line_col_one_index();
260                                println!(
261                                    "{} @ {}:{}:{}",
262                                    span.as_str(),
263                                    span.source_id()
264                                        .map(|source_id| source_engine.get_path(source_id))
265                                        .map(|x| x.display().to_string())
266                                        .unwrap_or("<autogenerated>".to_string()),
267                                    line_col.line,
268                                    line_col.col
269                                );
270                                Some(span.clone())
271                            }
272                            (last, _) => last,
273                        };
274                    }
275
276                    if build_config.print_bytecode {
277                        print!("{}{:#010x} ", " ".repeat(indentation), bytecode.len());
278                        print_instruction(&instruction);
279                    }
280
281                    if let Some(span) = &span {
282                        source_map.insert(source_engine, half_word_ix, span);
283                    }
284
285                    let bytes = instruction.to_bytes();
286
287                    if build_config.print_bytecode {
288                        println!(";; {bytes:?}")
289                    }
290
291                    bytecode.extend(bytes.iter());
292                    half_word_ix += 1;
293                }
294            }
295        }
296    }
297
298    if build_config.print_bytecode {
299        println!(".data_section:");
300
301        let offset = bytecode.len();
302
303        fn print_entry(indentation: usize, offset: usize, pair: &Entry) {
304            print!("{}{:#010x} ", " ".repeat(indentation), offset);
305
306            match &pair.value {
307                Datum::Byte(w) => println!(".byte i{w}, as hex {w:02X}"),
308                Datum::Word(w) => {
309                    println!(".word i{w}, as hex be bytes ({:02X?})", w.to_be_bytes())
310                }
311                Datum::ByteArray(bs) => {
312                    print!(".bytes as hex ({bs:02X?}), len i{}, as ascii \"", bs.len());
313
314                    for b in bs {
315                        print!(
316                            "{}",
317                            if *b == b' ' || b.is_ascii_graphic() {
318                                *b as char
319                            } else {
320                                '.'
321                            }
322                        );
323                    }
324                    println!("\"");
325                }
326                Datum::Slice(bs) => {
327                    print!(".slice as hex ({bs:02X?}), len i{}, as ascii \"", bs.len());
328
329                    for b in bs {
330                        print!(
331                            "{}",
332                            if *b == b' ' || b.is_ascii_graphic() {
333                                *b as char
334                            } else {
335                                '.'
336                            }
337                        );
338                    }
339                    println!("\"");
340                }
341                Datum::Collection(els) => {
342                    println!(".collection");
343                    for e in els {
344                        print_entry(indentation + 1, offset, e);
345                    }
346                }
347            };
348        }
349
350        for (i, entry) in data_section.iter_all_entries().enumerate() {
351            let entry_offset = data_section.absolute_idx_to_offset(i);
352            print_entry(indentation, offset + entry_offset, &entry);
353        }
354
355        println!(";; --- END OF TARGET BYTECODE ---\n");
356    }
357
358    assert_eq!(half_word_ix * 4, offset_to_data_section_in_bytes as usize);
359    assert_eq!(bytecode.len(), offset_to_data_section_in_bytes as usize);
360
361    let num_nonconfigurables = data_section.non_configurables.len();
362    let named_data_section_entries_offsets = data_section
363        .configurables
364        .iter()
365        .enumerate()
366        .map(|(id, entry)| {
367            let EntryName::Configurable(name) = &entry.name else {
368                panic!("Non-configurable in configurables part of datasection");
369            };
370            (
371                name.clone(),
372                offset_to_data_section_in_bytes
373                    + data_section.absolute_idx_to_offset(id + num_nonconfigurables) as u64,
374            )
375        })
376        .collect::<BTreeMap<String, u64>>();
377
378    let mut data_section = data_section.serialize_to_bytes();
379    bytecode.append(&mut data_section);
380
381    CompiledBytecode {
382        bytecode,
383        named_data_section_entries_offsets,
384    }
385}
386
387// Code to pretty print bytecode
388fn print_reg(r: RegId) -> String {
389    match r {
390        RegId::BAL => "$bal".to_string(),
391        RegId::CGAS => "$cgas".to_string(),
392        RegId::ERR => "$err".to_string(),
393        RegId::FLAG => "$flag".to_string(),
394        RegId::FP => "$fp".to_string(),
395        RegId::GGAS => "$ggas".to_string(),
396        RegId::HP => "$hp".to_string(),
397        RegId::IS => "$is".to_string(),
398        RegId::OF => "$of".to_string(),
399        RegId::ONE => "$one".to_string(),
400        RegId::PC => "$pc".to_string(),
401        RegId::RET => "$ret".to_string(),
402        RegId::RETL => "$retl".to_string(),
403        RegId::SP => "$sp".to_string(),
404        RegId::SSP => "$ssp".to_string(),
405        RegId::WRITABLE => "$writable".to_string(),
406        RegId::ZERO => "$zero".to_string(),
407        _ => format!("R{:?}", r.to_u8()),
408    }
409}
410
411trait Args {
412    fn print(&self) -> String;
413}
414
415impl Args for RegId {
416    fn print(&self) -> String {
417        print_reg(*self)
418    }
419}
420impl Args for Imm06 {
421    fn print(&self) -> String {
422        format!("{:#x}", self.to_u8())
423    }
424}
425impl Args for Imm12 {
426    fn print(&self) -> String {
427        format!("{:#x}", self.to_u16())
428    }
429}
430impl Args for Imm18 {
431    fn print(&self) -> String {
432        format!("{:#x}", self.to_u32())
433    }
434}
435impl Args for Imm24 {
436    fn print(&self) -> String {
437        format!("{:#x}", self.to_u32())
438    }
439}
440impl Args for () {
441    fn print(&self) -> String {
442        String::new()
443    }
444}
445impl<A: Args> Args for (A,) {
446    fn print(&self) -> String {
447        self.0.print()
448    }
449}
450impl<A: Args, B: Args> Args for (A, B) {
451    fn print(&self) -> String {
452        format!("{} {}", self.0.print(), self.1.print())
453    }
454}
455impl<A: Args, B: Args, C: Args> Args for (A, B, C) {
456    fn print(&self) -> String {
457        format!("{} {} {}", self.0.print(), self.1.print(), self.2.print())
458    }
459}
460impl<A: Args, B: Args, C: Args, D: Args> Args for (A, B, C, D) {
461    fn print(&self) -> String {
462        format!(
463            "{} {} {} {}",
464            self.0.print(),
465            self.1.print(),
466            self.2.print(),
467            self.3.print()
468        )
469    }
470}
471
472fn f(name: &str, args: impl Args) {
473    let mut line = format!("{name} {}", args.print());
474    let s = " ".repeat(48 - line.len());
475    line.push_str(&s);
476    print!("{line}")
477}
478
479fn print_instruction(op: &Instruction) {
480    match op {
481        Instruction::ADD(x) => f("ADD", x.unpack()),
482        Instruction::AND(x) => f("AND", x.unpack()),
483        Instruction::DIV(x) => f("DIV", x.unpack()),
484        Instruction::EQ(x) => f("EQ", x.unpack()),
485        Instruction::EXP(x) => f("EXP", x.unpack()),
486        Instruction::GT(x) => f("GT", x.unpack()),
487        Instruction::LT(x) => f("LT", x.unpack()),
488        Instruction::MLOG(x) => f("MLOG", x.unpack()),
489        Instruction::MROO(x) => f("MROO", x.unpack()),
490        Instruction::MOD(x) => f("MOD", x.unpack()),
491        Instruction::MOVE(x) => f("MOVE", x.unpack()),
492        Instruction::MUL(x) => f("MUL", x.unpack()),
493        Instruction::NOT(x) => f("NOT", x.unpack()),
494        Instruction::OR(x) => f("OR", x.unpack()),
495        Instruction::SLL(x) => f("SLL", x.unpack()),
496        Instruction::SRL(x) => f("SRL", x.unpack()),
497        Instruction::SUB(x) => f("SUB", x.unpack()),
498        Instruction::XOR(x) => f("XOR", x.unpack()),
499        Instruction::MLDV(x) => f("MLDV", x.unpack()),
500        Instruction::RET(x) => f("RET", x.unpack()),
501        Instruction::RETD(x) => f("RETD", x.unpack()),
502        Instruction::ALOC(x) => f("ALOC", x.unpack()),
503        Instruction::MCL(x) => f("MCL", x.unpack()),
504        Instruction::MCP(x) => f("MCP", x.unpack()),
505        Instruction::MEQ(x) => f("MEQ", x.unpack()),
506        Instruction::BHSH(x) => f("BHSH", x.unpack()),
507        Instruction::BHEI(x) => f("BHEI", x.unpack()),
508        Instruction::BURN(x) => f("BURN", x.unpack()),
509        Instruction::CALL(x) => f("CALL", x.unpack()),
510        Instruction::CCP(x) => f("CCP", x.unpack()),
511        Instruction::CROO(x) => f("CROO", x.unpack()),
512        Instruction::CSIZ(x) => f("CSIZ", x.unpack()),
513        Instruction::CB(x) => f("CB", x.unpack()),
514        Instruction::LDC(x) => f("LDC", x.unpack()),
515        Instruction::LOG(x) => f("LOG", x.unpack()),
516        Instruction::LOGD(x) => f("LOGD", x.unpack()),
517        Instruction::MINT(x) => f("MINT", x.unpack()),
518        Instruction::RVRT(x) => f("RVRT", x.unpack()),
519        Instruction::SCWQ(x) => f("SCWQ", x.unpack()),
520        Instruction::SRW(x) => f("SRW", x.unpack()),
521        Instruction::SRWQ(x) => f("SRWQ", x.unpack()),
522        Instruction::SWW(x) => f("SWW", x.unpack()),
523        Instruction::SWWQ(x) => f("SWWQ", x.unpack()),
524        Instruction::TR(x) => f("TR", x.unpack()),
525        Instruction::TRO(x) => f("TRO", x.unpack()),
526        Instruction::ECK1(x) => f("ECK1", x.unpack()),
527        Instruction::ECR1(x) => f("ECR1", x.unpack()),
528        Instruction::ED19(x) => f("ED19", x.unpack()),
529        Instruction::K256(x) => f("K256", x.unpack()),
530        Instruction::S256(x) => f("S256", x.unpack()),
531        Instruction::TIME(x) => f("TIME", x.unpack()),
532        Instruction::NIOP(x) => f("NIOP", x.unpack()),
533        Instruction::NOOP(_) => f("NOOP", ()),
534        Instruction::FLAG(x) => f("FLAG", x.unpack()),
535        Instruction::BAL(x) => f("BAL", x.unpack()),
536        Instruction::JAL(x) => f("JAL", x.unpack()),
537        Instruction::JMP(x) => f("JMP", x.unpack()),
538        Instruction::JNE(x) => f("JNE", x.unpack()),
539        Instruction::SMO(x) => f("SMO", x.unpack()),
540        Instruction::ADDI(x) => f("ADDI", x.unpack()),
541        Instruction::ANDI(x) => f("ANDI", x.unpack()),
542        Instruction::DIVI(x) => f("DIVI", x.unpack()),
543        Instruction::EXPI(x) => f("EXPI", x.unpack()),
544        Instruction::MODI(x) => f("MODI", x.unpack()),
545        Instruction::MULI(x) => f("MULI", x.unpack()),
546        Instruction::ORI(x) => f("ORI", x.unpack()),
547        Instruction::SLLI(x) => f("SLLI", x.unpack()),
548        Instruction::SRLI(x) => f("SRLI", x.unpack()),
549        Instruction::SUBI(x) => f("SUBI", x.unpack()),
550        Instruction::XORI(x) => f("XORI", x.unpack()),
551        Instruction::JNEI(x) => f("JNEI", x.unpack()),
552        Instruction::LB(x) => f("LB", x.unpack()),
553        Instruction::LQW(x) => f("LQW", x.unpack()),
554        Instruction::LHW(x) => f("LHW", x.unpack()),
555        Instruction::LW(x) => f("LW", x.unpack()),
556        Instruction::SB(x) => f("SB", x.unpack()),
557        Instruction::SQW(x) => f("SQW", x.unpack()),
558        Instruction::SHW(x) => f("SHW", x.unpack()),
559        Instruction::SW(x) => f("SW", x.unpack()),
560        Instruction::MCPI(x) => f("MCPI", x.unpack()),
561        Instruction::GTF(x) => f("GTF", x.unpack()),
562        Instruction::MCLI(x) => f("MCLI", x.unpack()),
563        Instruction::GM(x) => f("GM", x.unpack()),
564        Instruction::MOVI(x) => f("MOVI", x.unpack()),
565        Instruction::JNZI(x) => f("JNZI", x.unpack()),
566        Instruction::JMPF(x) => f("JMPF", x.unpack()),
567        Instruction::JMPB(x) => f("JMPB", x.unpack()),
568        Instruction::JNZF(x) => f("JNZF", x.unpack()),
569        Instruction::JNZB(x) => f("JNZB", x.unpack()),
570        Instruction::JNEF(x) => f("JNEF", x.unpack()),
571        Instruction::JNEB(x) => f("JNEB", x.unpack()),
572        Instruction::JI(x) => f("JI", x.unpack()),
573        Instruction::CFEI(x) => f("CFEI", x.unpack()),
574        Instruction::CFSI(x) => f("CFSI", x.unpack()),
575        Instruction::CFE(x) => f("CFE", x.unpack()),
576        Instruction::CFS(x) => f("CFS", x.unpack()),
577        Instruction::PSHL(x) => f("PSHL", x.unpack()),
578        Instruction::PSHH(x) => f("PSHH", x.unpack()),
579        Instruction::POPL(x) => f("POPL", x.unpack()),
580        Instruction::POPH(x) => f("POPH", x.unpack()),
581        Instruction::WDCM(x) => f("WDCM", x.unpack()),
582        Instruction::WQCM(x) => f("WQCM", x.unpack()),
583        Instruction::WDOP(x) => f("WDOP", x.unpack()),
584        Instruction::WQOP(x) => f("WQOP", x.unpack()),
585        Instruction::WDML(x) => f("WDML", x.unpack()),
586        Instruction::WQML(x) => f("WQML", x.unpack()),
587        Instruction::WDDV(x) => f("WDDV", x.unpack()),
588        Instruction::WQDV(x) => f("WQDV", x.unpack()),
589        Instruction::WDMD(x) => f("WDMD", x.unpack()),
590        Instruction::WQMD(x) => f("WQMD", x.unpack()),
591        Instruction::WDAM(x) => f("WDAM", x.unpack()),
592        Instruction::WQAM(x) => f("WQAM", x.unpack()),
593        Instruction::WDMM(x) => f("WDMM", x.unpack()),
594        Instruction::WQMM(x) => f("WQMM", x.unpack()),
595        Instruction::ECAL(x) => f("ECAL", x.unpack()),
596        Instruction::BSIZ(x) => f("BSIZ", x.unpack()),
597        Instruction::BLDD(x) => f("BLDD", x.unpack()),
598        Instruction::ECOP(x) => f("ECOP", x.unpack()),
599        Instruction::EPAR(x) => f("EPAR", x.unpack()),
600    }
601}
602
603/// Checks for disallowed opcodes in non-contract code.
604/// i.e., if this is a script or predicate, we can't use certain contract opcodes.
605/// See https://github.com/FuelLabs/sway/issues/350 for details.
606pub fn check_invalid_opcodes(handler: &Handler, asm: &FinalizedAsm) -> Result<(), ErrorEmitted> {
607    match &asm.program_section {
608        InstructionSet::Fuel { ops } => match asm.program_kind {
609            ProgramKind::Contract | ProgramKind::Library => Ok(()),
610            ProgramKind::Script => checks::check_script_opcodes(handler, &ops[..]),
611            ProgramKind::Predicate => checks::check_predicate_opcodes(handler, &ops[..]),
612        },
613        InstructionSet::Evm { ops: _ } => Ok(()),
614    }
615}