Files
nixjit/src/compile.rs
2025-05-17 18:31:36 +08:00

409 lines
12 KiB
Rust

use crate::bytecode::*;
use crate::ir;
pub struct Compiler {
opcodes: Vec<OpCode>,
}
pub fn compile(downgraded: ir::Downgraded) -> Program {
Program {
top_level: Compiler::new().compile(downgraded.top_level),
thunks: downgraded
.thunks
.into_iter()
.map(|thunk| Compiler::new().compile(thunk))
.collect(),
funcs: downgraded
.funcs
.into_iter()
.map(|func| Func {
param: func.param.into(),
opcodes: Compiler::new().compile(*func.body),
})
.collect(),
symbols: downgraded.symbols,
symmap: downgraded.symmap,
consts: downgraded.consts,
}
}
impl Compiler {
fn new() -> Self {
Self {
opcodes: Vec::new(),
}
}
fn compile(mut self, ir: ir::Ir) -> OpCodes {
ir.compile(&mut self);
self.opcodes()
}
fn push(&mut self, code: OpCode) {
self.opcodes.push(code);
}
fn idx(&self) -> usize {
self.opcodes.len()
}
fn modify(&mut self, idx: usize, code: OpCode) {
self.opcodes[idx] = code;
}
fn pop(&mut self) -> Option<OpCode> {
self.opcodes.pop()
}
fn opcodes(self) -> OpCodes {
self.opcodes.into()
}
}
pub trait Compile {
fn compile(self, comp: &mut Compiler);
}
pub trait CompileWithLength {
fn compile_with_length(self, comp: &mut Compiler) -> usize;
}
impl<T: Compile> CompileWithLength for T {
fn compile_with_length(self, comp: &mut Compiler) -> usize {
let start = comp.idx();
self.compile(comp);
let end = comp.idx();
end - start
}
}
impl Compile for ir::Const {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::Const { idx: self.idx });
}
}
impl Compile for ir::Var {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::LookUp { sym: self.sym });
}
}
impl Compile for ir::Thunk {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::LoadThunk { idx: self.idx });
}
}
impl Compile for ir::Attrs {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::AttrSet);
for stc in self.stcs {
stc.1.compile(comp);
if !self.rec {
comp.push(OpCode::CaptureEnv);
}
comp.push(OpCode::PushStaticAttr { name: stc.0 });
}
for dynamic in self.dyns {
dynamic.0.compile(comp);
dynamic.1.compile(comp);
if !self.rec {
comp.push(OpCode::CaptureEnv);
}
comp.push(OpCode::PushDynamicAttr)
}
if self.rec {
comp.push(OpCode::FinalizeRec);
}
}
}
impl Compile for ir::List {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::List);
for item in self.items {
item.compile(comp);
comp.push(OpCode::PushElem);
}
}
}
impl Compile for ir::UnOp {
fn compile(self, comp: &mut Compiler) {
use ir::UnOpKind::*;
match self.kind {
Neg => {
self.rhs.compile(comp);
comp.push(OpCode::UnOp { op: UnOp::Neg });
}
Not => {
self.rhs.compile(comp);
comp.push(OpCode::UnOp { op: UnOp::Not });
}
}
}
}
impl Compile for ir::BinOp {
fn compile(self, comp: &mut Compiler) {
use ir::BinOpKind::*;
match self.kind {
Add => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Add });
}
Mul => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Mul });
}
Div => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Div });
}
And => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::And });
}
Or => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Or });
}
Eq => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Eq });
}
Lt => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Lt });
}
Con => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Con });
}
Upd => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Upd });
}
Sub => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Sub });
}
Impl => {
self.lhs.compile(comp);
comp.push(OpCode::UnOp { op: UnOp::Not });
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Or });
}
Neq => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Eq });
comp.push(OpCode::UnOp { op: UnOp::Not });
}
Gt => {
self.rhs.compile(comp);
self.lhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Lt });
}
Leq => {
self.rhs.compile(comp);
self.lhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Lt });
comp.push(OpCode::UnOp { op: UnOp::Not });
}
Geq => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::BinOp { op: BinOp::Lt });
comp.push(OpCode::UnOp { op: UnOp::Not });
}
PipeL => {
self.lhs.compile(comp);
self.rhs.compile(comp);
comp.push(OpCode::Call { arity: 1 });
}
PipeR => {
self.rhs.compile(comp);
self.lhs.compile(comp);
comp.push(OpCode::Call { arity: 1 });
}
}
}
}
impl Compile for ir::HasAttr {
fn compile(self, comp: &mut Compiler) {
self.lhs.compile(comp);
for attr in self.rhs {
match attr {
ir::Attr::Str(sym) => {
comp.push(OpCode::AttrSet);
comp.push(OpCode::SelectOrDefault { sym })
}
ir::Attr::Dynamic(dynamic) => {
dynamic.compile(comp);
comp.push(OpCode::AttrSet);
comp.push(OpCode::SelectDynamicOrDefault);
}
ir::Attr::Strs(string) => {
string.compile(comp);
comp.push(OpCode::AttrSet);
comp.push(OpCode::SelectDynamicOrDefault);
}
}
}
let last = comp.pop().unwrap();
let _ = comp.pop();
match last {
OpCode::SelectOrDefault { sym } => comp.push(OpCode::HasAttr { sym }),
OpCode::SelectDynamicOrDefault => comp.push(OpCode::HasDynamicAttr),
_ => unreachable!(),
}
}
}
impl Compile for ir::Select {
fn compile(self, comp: &mut Compiler) {
self.expr.compile(comp);
for attr in self.attrpath {
match attr {
ir::Attr::Str(sym) => {
comp.push(OpCode::AttrSet);
comp.push(OpCode::SelectOrDefault { sym })
}
ir::Attr::Dynamic(dynamic) => {
dynamic.compile(comp);
comp.push(OpCode::AttrSet);
comp.push(OpCode::SelectDynamicOrDefault);
}
ir::Attr::Strs(string) => {
string.compile(comp);
comp.push(OpCode::AttrSet);
comp.push(OpCode::SelectDynamicOrDefault);
}
}
}
match self.default {
Some(default) => {
let last = comp.pop().unwrap();
let _ = comp.pop();
default.compile(comp);
match last {
OpCode::SelectOrDefault { sym } => comp.push(OpCode::SelectOrDefault { sym }),
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamicOrDefault),
_ => unreachable!(),
}
}
None => {
let last = comp.pop().unwrap();
let _ = comp.pop();
match last {
OpCode::SelectOrDefault { sym } => comp.push(OpCode::Select { sym }),
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamic),
_ => unreachable!(),
}
}
}
}
}
impl Compile for ir::ConcatStrings {
fn compile(self, comp: &mut Compiler) {
let mut iter = self.parts.into_iter();
iter.next().unwrap().compile(comp);
for item in iter {
item.compile(comp);
comp.push(OpCode::ConcatString);
}
}
}
impl Compile for ir::If {
fn compile(self, comp: &mut Compiler) {
self.cond.compile(comp);
let idx_jmp_if_false = comp.idx();
// place holder
comp.push(OpCode::Illegal);
let consq_length = self.consq.compile_with_length(comp);
let idx_jmp = comp.idx();
// place holder
comp.push(OpCode::Illegal);
let alter_length = self.alter.compile_with_length(comp);
comp.modify(
idx_jmp_if_false,
OpCode::JmpIfFalse {
step: consq_length + 1,
},
);
comp.modify(idx_jmp, OpCode::Jmp { step: alter_length });
}
}
impl Compile for ir::Let {
fn compile(self, comp: &mut Compiler) {
self.attrs.compile(comp);
comp.push(OpCode::EnterEnv);
self.expr.compile(comp);
comp.push(OpCode::LeaveEnv);
}
}
impl Compile for ir::With {
fn compile(self, comp: &mut Compiler) {
self.namespace.compile(comp);
comp.push(OpCode::EnterEnv);
self.expr.compile(comp);
comp.push(OpCode::LeaveEnv);
}
}
impl Compile for ir::Assert {
fn compile(self, comp: &mut Compiler) {
self.assertion.compile(comp);
comp.push(OpCode::Assert);
self.expr.compile(comp);
}
}
impl Compile for ir::LoadFunc {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::Func { idx: self.idx });
}
}
impl Compile for ir::Call {
fn compile(self, comp: &mut Compiler) {
let arity = self.args.len();
self.func.compile(comp);
self.args.into_iter().for_each(|arg| {
arg.compile(comp);
});
comp.push(OpCode::Call { arity });
}
}
impl Compile for ir::Path {
fn compile(self, comp: &mut Compiler) {
self.expr.compile(comp);
comp.push(OpCode::Path);
}
}