zinkc/
compiler.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! Zink compiler

use crate::{parser::Parser, Artifact, Config, Error, Result};
use zabi::Abi;
use zingen::{
    wasm::{self, Env},
    Buffer, Dispatcher, Function, JumpTable, BUFFER_LIMIT,
};

/// Zink Compiler
#[derive(Default)]
pub struct Compiler {
    /// ABIs of the compiled contract.
    pub(crate) abi: Vec<Abi>,
    /// EVM bytecode buffer.
    pub(crate) buffer: Buffer,
    /// Compiler configuration.
    pub config: Config,
    /// Global jump table.
    table: JumpTable,
}

impl Compiler {
    /// Create a new compiler from config.
    pub fn new(config: Config) -> Self {
        Self {
            config,
            ..Default::default()
        }
    }

    /// Compile wasm module to evm bytecode.
    ///
    /// Returns runtime bytecode.
    pub fn compile(mut self, wasm: &[u8]) -> Result<Artifact> {
        let mut parser = Parser::try_from(wasm)?;
        let env = parser.env.clone();

        self.compile_dispatcher(&mut parser)?;
        for func in parser.funcs.into_funcs() {
            self.compile_func(env.with_index(func.index()), func)?;
        }

        self.table.code_offset(self.buffer.len() as u16);
        self.table.relocate(&mut self.buffer)?;
        self.artifact()
    }

    /// Generate artifact
    ///
    /// yields runtime bytecode and construct bytecode
    fn artifact(self) -> Result<Artifact> {
        let Compiler {
            abi,
            buffer,
            config,
            ..
        } = self;

        Ok(Artifact {
            abi,
            config,
            runtime_bytecode: buffer.to_vec(),
        })
    }

    /// Compile EVM dispatcher.
    ///
    /// Drain selectors anyway, compile dispatcher if it is enabled.
    fn compile_dispatcher(&mut self, parser: &mut Parser) -> Result<()> {
        let selectors = parser.drain_selectors();
        let env = parser.env.clone();

        if !self.config.dispatcher {
            self.abi.append(&mut env.load_abis(&selectors)?);
            return Ok(());
        }

        let mut dispatcher = Dispatcher::new(env, &parser.funcs)?;
        let buffer = dispatcher.finish(selectors, &mut self.table)?;
        self.buffer.extend_from_slice(&buffer);
        if self.buffer.len() > BUFFER_LIMIT {
            return Err(Error::BufferOverflow(self.buffer.len()));
        }

        self.abi.append(&mut dispatcher.abi);
        Ok(())
    }

    /// Compile WASM function.
    fn compile_func(&mut self, env: Env, mut func: wasm::Function<'_>) -> Result<()> {
        let func_index = func.index();
        let sig = func.sig()?;
        let abi = self.abi(&env, func_index);

        tracing::debug!("compile function {func_index} {:?}, abi: {abi:#?}", sig);
        let is_main = !self.config.dispatcher && env.is_main(func_index);

        let mut codegen = Function::new(env, sig, abi, is_main)?;
        let mut locals_reader = func.body.get_locals_reader()?;
        let mut ops_reader = func.body.get_operators_reader()?;

        codegen.emit_locals(&mut locals_reader, &mut func.validator)?;
        codegen.emit_operators(&mut ops_reader, &mut func.validator)?;

        self.emit_buffer(func_index, codegen)?;
        Ok(())
    }

    /// Emit buffer to the inner buffer.
    fn emit_buffer(&mut self, func_index: u32, codegen: Function) -> Result<()> {
        let buffer = codegen.finish(&mut self.table, self.buffer.len() as u16)?;
        self.table
            .call_offset(func_index, self.buffer.len() as u16)?;
        self.buffer.extend_from_slice(&buffer);

        if self.buffer.len() > BUFFER_LIMIT {
            return Err(Error::BufferOverflow(buffer.len()));
        }

        Ok(())
    }

    /// Get abi from env and function index
    fn abi(&self, env: &Env, index: u32) -> Option<Abi> {
        let name = env.exports.get(&index)?;
        self.abi.iter().find(|a| name == &a.name).cloned()
    }
}