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
//! Shared lowering state

use std::cell::RefCell;
use std::rc::Rc;

use crate::code::CodeObject;
use crate::gen::ModuleMeta;
use crate::var::Variable;

use super::*;

/// Information for the process of lowering HIR to LIR
pub struct HirLoweringRuntime<'lir> {
    code: Vec<LirElement<'lir>>,
    counter: LabelCounterRef,
    meta: ModuleMeta,
    optimizer: Box<dyn Optimizer>,

    branch_stack: Vec<HirLoweringBranch>,
    locals: Vec<&'lir Variable>,
    loop_stack: Vec<HirLoweringRepeat>,
}

impl<'lir> HirLoweringRuntime<'lir> {
    pub fn new(meta: ModuleMeta, options: CompileOptions) -> Self {
        let optimizer = if options.optimize {
            Box::new(StandardOptimizer::new()) as Box<dyn Optimizer>
        } else {
            Box::new(NoOptimizer) as Box<dyn Optimizer>
        };

        Self {
            code: vec![],
            counter: Rc::new(RefCell::new(LabelCounter::default())),
            meta,
            optimizer,

            branch_stack: vec![],
            locals: vec![],
            loop_stack: vec![],
        }
    }

    pub fn create_new_label(&mut self) -> Label {
        self.counter.borrow_mut().create_new_label()
    }

    pub fn add_hir(&mut self, hir: &'lir Hir) -> Lovm2CompileResult<()> {
        // before lowering a code object function, reset locals
        self.locals.clear();

        // read in code object parameters from value stack
        // read this in reverse, because last parameter is top of stack
        for arg in hir.args.iter().rev() {
            self.emit(LirElement::store(Scope::Local, arg));
        }

        hir.block.lower(self);

        // automatically add a `return nil` if not present already
        match self.code.last_mut() {
            Some(LirElement::Ret) => {}
            _ => {
                self.emit(LirElement::push_constant_owned(Value::Nil));
                self.emit(LirElement::Ret);
            }
        }

        Ok(())
    }

    pub fn complete(mut self) -> Lovm2CompileResult<CodeObject> {
        let lir_runtime = LirLoweringRuntime::from(self.meta);
        self.optimizer.postprocess(&mut self.code);
        lir_runtime.lower(self.code)
    }

    pub fn emit(&mut self, elem: LirElement<'lir>) {
        if let LirElement::StoreDynamic {
            ident,
            scope: Scope::Local,
        } = &elem
        {
            if !self.has_local(ident) {
                self.locals.push(ident);
            }
        }

        self.code.push(elem);

        self.optimizer.transform(&mut self.code);
    }

    pub fn has_local(&self, var: &Variable) -> bool {
        self.locals.contains(&var)
    }

    pub fn loop_mut(&mut self) -> Option<&mut HirLoweringRepeat> {
        self.loop_stack.last_mut()
    }

    pub fn push_loop(&mut self) -> &HirLoweringRepeat {
        self.loop_stack
            .push(HirLoweringRepeat::new(self.counter.clone()));
        self.loop_stack.last_mut().unwrap()
    }

    pub fn pop_loop(&mut self) -> Option<HirLoweringRepeat> {
        self.loop_stack.pop()
    }

    pub fn branch_mut(&mut self) -> Option<&mut HirLoweringBranch> {
        self.branch_stack.last_mut()
    }

    pub fn push_branch(&mut self) -> &HirLoweringBranch {
        self.branch_stack
            .push(HirLoweringBranch::new(self.counter.clone()));
        self.branch_stack.last_mut().unwrap()
    }

    pub fn pop_branch(&mut self) -> Option<HirLoweringBranch> {
        self.branch_stack.pop()
    }
}