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