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#[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 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 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 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 let offset_bytes = data_section.data_id_to_offset(data_label) as u64;
180 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 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 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 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
397fn 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
607pub 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}