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#[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 pub size: u64,
33 pub used: u64,
35 pub value_pairs: Vec<Entry>,
37}
38
39#[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 pub fn_name: String,
54 pub imm: u64,
56 pub selector: Option<[u8; 4]>,
58 pub test_decl_ref: Option<DeclRefFunction>,
61}
62
63pub 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 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 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 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 let offset_bytes = data_section.data_id_to_offset(data_label) as u64;
176 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 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 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 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
393fn 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
609pub 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}