compiler.rs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{io::Result, str::Chars};
  19. use darkfi_serial::{serialize, VarInt};
  20. use super::{
  21. ast::{Arg, Constant, Literal, Statement, StatementType, Witness},
  22. constants::{
  23. SECTION_CIRCUIT, SECTION_CONSTANT, SECTION_DEBUG, SECTION_LITERAL, SECTION_WITNESS,
  24. },
  25. error::ErrorEmitter,
  26. types::HeapType,
  27. };
  28. /// Version of the binary
  29. pub const BINARY_VERSION: u8 = 2;
  30. /// Magic bytes prepended to the binary
  31. pub const MAGIC_BYTES: [u8; 4] = [0x0b, 0x01, 0xb1, 0x35];
  32. pub struct Compiler {
  33. namespace: String,
  34. k: u32,
  35. constants: Vec<Constant>,
  36. witnesses: Vec<Witness>,
  37. statements: Vec<Statement>,
  38. literals: Vec<Literal>,
  39. debug_info: bool,
  40. error: ErrorEmitter,
  41. }
  42. impl Compiler {
  43. #[allow(clippy::too_many_arguments)]
  44. pub fn new(
  45. filename: &str,
  46. source: Chars,
  47. namespace: String,
  48. k: u32,
  49. constants: Vec<Constant>,
  50. witnesses: Vec<Witness>,
  51. statements: Vec<Statement>,
  52. literals: Vec<Literal>,
  53. debug_info: bool,
  54. ) -> Self {
  55. // For nice error reporting, we'll load everything into a string
  56. // vector so we have references to lines.
  57. let lines: Vec<String> = source.as_str().lines().map(|x| x.to_string()).collect();
  58. let error = ErrorEmitter::new("Compiler", filename, lines);
  59. Self { namespace, k, constants, witnesses, statements, literals, debug_info, error }
  60. }
  61. pub fn compile(&self) -> Result<Vec<u8>> {
  62. let mut bincode = vec![];
  63. // Write the magic bytes and version
  64. bincode.extend_from_slice(&MAGIC_BYTES);
  65. bincode.push(BINARY_VERSION);
  66. // Write the circuit's k param
  67. bincode.extend_from_slice(&serialize(&self.k));
  68. // Write the circuit's namespace
  69. bincode.extend_from_slice(&serialize(&self.namespace));
  70. // Temporary heap vector for lookups
  71. let mut tmp_heap = vec![];
  72. // In the .constant section of the binary, we write the constant's type,
  73. // and the name so the VM can look it up from `src/crypto/constants/`.
  74. bincode.extend_from_slice(SECTION_CONSTANT);
  75. for i in &self.constants {
  76. tmp_heap.push(i.name.as_str());
  77. bincode.push(i.typ as u8);
  78. bincode.extend_from_slice(&serialize(&i.name));
  79. }
  80. // Currently, our literals are only Uint64 types, in the binary we'll
  81. // add them here in the .literal section. In the VM, they will be on
  82. // their own heap, used for reference by opcodes.
  83. bincode.extend_from_slice(SECTION_LITERAL);
  84. for i in &self.literals {
  85. bincode.push(i.typ as u8);
  86. bincode.extend_from_slice(&serialize(&i.name));
  87. }
  88. // In the .witness section, we write all our witness types, on the heap
  89. // they're in order of appearance.
  90. bincode.extend_from_slice(SECTION_WITNESS);
  91. for i in &self.witnesses {
  92. tmp_heap.push(i.name.as_str());
  93. bincode.push(i.typ as u8);
  94. }
  95. bincode.extend_from_slice(SECTION_CIRCUIT);
  96. for i in &self.statements {
  97. match i.typ {
  98. StatementType::Assign => tmp_heap.push(&i.lhs.as_ref().unwrap().name),
  99. // In case of a simple call, we don't append anything to the heap
  100. StatementType::Call => {}
  101. _ => unreachable!("Invalid statement type in circuit: {:?}", i.typ),
  102. }
  103. bincode.push(i.opcode as u8);
  104. bincode.extend_from_slice(&serialize(&VarInt(i.rhs.len() as u64)));
  105. for arg in &i.rhs {
  106. match arg {
  107. Arg::Var(arg) => {
  108. let heap_idx =
  109. Compiler::lookup_heap(&tmp_heap, &arg.name).ok_or_else(|| {
  110. self.error.abort(
  111. &format!("Failed finding a heap reference for `{}`", arg.name),
  112. arg.line,
  113. arg.column,
  114. )
  115. })?;
  116. bincode.push(HeapType::Var as u8);
  117. bincode.extend_from_slice(&serialize(&VarInt(heap_idx as u64)));
  118. }
  119. Arg::Lit(lit) => {
  120. let lit_idx = Compiler::lookup_literal(&self.literals, &lit.name)
  121. .ok_or_else(|| {
  122. self.error.abort(
  123. &format!("Failed finding literal `{}`", lit.name),
  124. lit.line,
  125. lit.column,
  126. )
  127. })?;
  128. bincode.push(HeapType::Lit as u8);
  129. bincode.extend_from_slice(&serialize(&VarInt(lit_idx as u64)));
  130. }
  131. _ => unreachable!(),
  132. };
  133. }
  134. }
  135. // If we're not doing debug info, we're done here and can return.
  136. if !self.debug_info {
  137. return Ok(bincode)
  138. }
  139. // Otherwise, we proceed appending debug info.
  140. bincode.extend_from_slice(SECTION_DEBUG);
  141. // Write source locations for each opcode.
  142. // This allows mapping runtime errors back to source lines.
  143. bincode.extend_from_slice(&serialize(&VarInt(self.statements.len() as u64)));
  144. for stmt in &self.statements {
  145. bincode.extend_from_slice(&serialize(&VarInt(stmt.line as u64)));
  146. // For column, use the lhs variable's column if available
  147. let column = stmt.lhs.as_ref().map(|v| v.column).unwrap_or(0);
  148. bincode.extend_from_slice(&serialize(&VarInt(column as u64)));
  149. }
  150. // Write heap variable names.
  151. // The heap contains constants, witnesses, assigned variables (in order).
  152. // This allows showing meaningful names instead of heap indices.
  153. let heap_size = self.constants.len() +
  154. self.witnesses.len() +
  155. self.statements.iter().filter(|s| s.typ == StatementType::Assign).count();
  156. bincode.extend_from_slice(&serialize(&VarInt(heap_size as u64)));
  157. for constant in &self.constants {
  158. bincode.extend_from_slice(&serialize(&constant.name));
  159. }
  160. for witness in &self.witnesses {
  161. bincode.extend_from_slice(&serialize(&witness.name));
  162. }
  163. for stmt in &self.statements {
  164. if stmt.typ == StatementType::Assign {
  165. bincode.extend_from_slice(&serialize(&stmt.lhs.as_ref().unwrap().name));
  166. }
  167. }
  168. // Write literal names (the literal values as strings, e.g. "42")
  169. bincode.extend_from_slice(&serialize(&VarInt(self.literals.len() as u64)));
  170. for literal in &self.literals {
  171. bincode.extend_from_slice(&serialize(&literal.name));
  172. }
  173. Ok(bincode)
  174. }
  175. fn lookup_heap(heap: &[&str], name: &str) -> Option<usize> {
  176. heap.iter().position(|&n| n == name)
  177. }
  178. fn lookup_literal(literals: &[Literal], name: &str) -> Option<usize> {
  179. literals.iter().position(|n| n.name == name)
  180. }
  181. }