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!(" ;; {data:?}");
215 }
216
217 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 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 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
387fn 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
603pub 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}