~jojo/effem

204fb3eee8930ccd36dc7b3577b1f745b4144799 — JoJo 1 year, 14 days ago b7b3f2d
tweak base IR to separate divergence from operations more clearly

partly for prepping for flat tree in base
2 files changed, 77 insertions(+), 60 deletions(-)

M src/abase.rs
M src/eval.rs
M src/abase.rs => src/abase.rs +51 -41
@@ 16,39 16,44 @@ pub struct FunDef {
    // Nested definitions (including anonymous functions)
    pub children: HashSet<DefId>,
    pub params: Vec<LocalId>,
    pub body: Block<Return>,
    pub body: Block<Option<Operation>>,
}

#[derive(Clone, Debug)]
pub struct GVarDef {
    pub children: HashSet<DefId>,
    pub body: Block<Operand>,
    pub body: Block<Option<Operation>>,
}

#[derive(Clone, Debug)]
pub struct Block<Term> {
#[derive(Debug, Clone)]
pub struct Block<Leaf> {
    pub stms: Vec<Stm>,
    pub term: Term,
    pub tail: Box<Flow<Leaf>>,
}

#[derive(Clone, Debug)]
#[derive(Debug, Clone)]
pub enum Stm {
    Let { lhs: LocalId, rhs: Expr },
    Let { lhs: LocalId, rhs: Flow<Option<Operation>> },
}

#[derive(Clone, Debug)]
pub enum Return {
    Val(Expr),
    // Void,
#[derive(Debug, Clone)]
pub enum Flow<Leaf> {
    Diverge(Diverge<Leaf>),
    Leaf(Leaf),
}

#[derive(Debug, Clone)]
pub enum Diverge<Leaf> {
    If(Operand, Block<Leaf>, Block<Leaf>),
}

#[derive(Clone, Debug)]
pub enum Expr {
pub enum Operation {
    Binop(Binop, Operand, Operand),
    Call(Operand, Vec<Operand>),
    Operand(Operand),
    If(Operand, Block<Box<Expr>>, Block<Box<Expr>>),
    Wrap(Operand),
}

#[derive(Debug, Clone, Copy)]
pub enum Binop {
    Add,


@@ 161,26 166,29 @@ impl<'c> AbaseDef<'c> {

    fn run(mut self, rhs: &FExpr) -> Result<GlobDef> {
        match self.abase(rhs)? {
            Expr::Operand(rand) => match rand {
            Flow::Leaf(Some(Operation::Wrap(rand))) => match rand {
                Operand::Local(id) => panic!("ice: result of abasing def is a local reference {id:?}"),
                Operand::Global(id) => Ok(GlobDef::Alias(id)),
                _ => Ok(GlobDef::VarDef(GVarDef {
                    children: self.children,
                    body: Block { stms: self.stms, term: rand },
                    body: Block { stms: self.stms, tail: Box::new(Flow::Leaf(Some(Operation::Wrap(rand)))) },
                })),
            },
            term => {
                let term = self.let_anon_soft(term)?;
                Ok(GlobDef::VarDef(GVarDef { children: self.children, body: Block { stms: self.stms, term } }))
            }
            tail => Ok(GlobDef::VarDef(GVarDef {
                children: self.children,
                body: Block { stms: self.stms, tail: Box::new(tail) },
            })),
        }
    }

    fn abase(&mut self, expr: &FExpr) -> Result<Expr> {
    fn abase(&mut self, expr: &FExpr) -> Result<Flow<Option<Operation>>> {
        use crate::fem::ExprKind::*;
        fn wrap(rand: Operand) -> Flow<Option<Operation>> {
            Flow::Leaf(Some(Operation::Wrap(rand)))
        }
        match &expr.kind {
            Bool(x) => Ok(Expr::Operand(Operand::Const(Const::Bool(*x)))),
            &Int(x) => Ok(Expr::Operand(Operand::Const(match expr.typ {
            Bool(x) => Ok(wrap(Operand::Const(Const::Bool(*x)))),
            &Int(x) => Ok(wrap(Operand::Const(match expr.typ {
                Type::Int { width: 8, signed: true } => Const::I8(x as i8),
                Type::Int { width: 16, signed: true } => Const::I16(x as i16),
                Type::Int { width: 32, signed: true } => Const::I32(x as i32),


@@ 194,8 202,8 @@ impl<'c> AbaseDef<'c> {
                Type::NSize => Const::Nat(x as usize),
                ref t => panic!("ice: integer expr has non-integer type {t}"),
            }))),
            F64(x) => Ok(Expr::Operand(Operand::Const(Const::F64(*x)))),
            Fun(ps, b) => Ok(Expr::Operand(self.abase_fun(ps, b)?)),
            F64(x) => Ok(wrap(Operand::Const(Const::F64(*x)))),
            Fun(ps, b) => Ok(wrap(self.abase_fun(ps, b)?)),
            App(f, xs) => match &f.kind {
                Var(ResName { res: Res::Prim(p) }) => match p {
                    Prim::Add => self.abase_binop(xs, Binop::Add),


@@ 220,31 228,32 @@ impl<'c> AbaseDef<'c> {
                            self.let_anon_soft(x)
                        })
                        .collect::<Result<Vec<Operand>>>()?;
                    Ok(Expr::Call(f, xs))
                    Ok(Operation::Call(f, xs))
                }
            },
            Var(ResName { res: Res::Def(id) }) => Ok(Expr::Operand(Operand::Global(*id))),
            }
            .map(|ration| Flow::Leaf(Some(ration))),
            Var(ResName { res: Res::Def(id) }) => Ok(wrap(Operand::Global(*id))),
            &Var(ResName { res: Res::Local(LocalId(id)) }) =>
                Ok(Expr::Operand(Operand::Local(*self.vars.get(id as usize).unwrap_or_else(|| todo!())))),
                Ok(wrap(Operand::Local(*self.vars.get(id as usize).unwrap_or_else(|| todo!())))),
            Var(ResName { res: Res::Prim(_) }) => todo!(), // TODO: generate a closure around the op or smth
            Var(ResName { res: Res::Module(_) }) =>
                panic!("ice: found module id in expr context when abasing. Should've been caught by type checker."),
            If(pred, conseq, alt) => {
                let pred_a = self.abase(pred)?;
                let pred_a = self.let_anon_soft(pred_a)?;
                let conseq_a = self.enter_block(|self_| self_.abase(conseq).map(Box::new))?;
                let alt_a = self.enter_block(|self_| self_.abase(alt).map(Box::new))?;
                Ok(Expr::If(pred_a, conseq_a, alt_a))
                let conseq_a = self.enter_block(|self_| self_.abase(conseq))?;
                let alt_a = self.enter_block(|self_| self_.abase(alt))?;
                Ok(Flow::Diverge(Diverge::If(pred_a, conseq_a, alt_a)))
            }
        }
    }

    fn abase_binop(&mut self, xs: &[FExpr], op: Binop) -> Result<Expr> {
    fn abase_binop(&mut self, xs: &[FExpr], op: Binop) -> Result<Operation> {
        let x = self.abase(&xs[0])?;
        let x = self.let_anon_soft(x)?;
        let y = self.abase(&xs[1])?;
        let y = self.let_anon_soft(y)?;
        Ok(Expr::Binop(op, x, y))
        Ok(Operation::Binop(op, x, y))
    }

    // TODO: closures. capture free vars etc.


@@ 256,7 265,7 @@ impl<'c> AbaseDef<'c> {
        let params = params.iter().map(|_| self.gen_local()).collect::<Vec<_>>();
        self.vars.extend(&params);

        let body = self.enter_block(|self_| self_.abase(body).map(Return::Val))?;
        let body = self.enter_block(|self_| self_.abase(body))?;

        let new_children = std::mem::replace(&mut self.children, old_children);
        self.vars = old_vars;


@@ 268,11 277,11 @@ impl<'c> AbaseDef<'c> {
        Ok(Operand::Global(fid))
    }

    fn enter_block<T>(&mut self, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<Block<T>> {
    fn enter_block<Leaf>(&mut self, f: impl FnOnce(&mut Self) -> Result<Flow<Leaf>>) -> Result<Block<Leaf>> {
        let old_stms = std::mem::take(&mut self.stms);
        let term = f(self)?;
        let tail = Box::new(f(self)?);
        let new_stms = std::mem::replace(&mut self.stms, old_stms);
        Ok(Block { stms: new_stms, term })
        Ok(Block { stms: new_stms, tail })
    }

    fn gen_local(&mut self) -> LocalId {


@@ 281,13 290,14 @@ impl<'c> AbaseDef<'c> {
        LocalId(self.n_locals - 1)
    }

    fn let_anon_soft(&mut self, rhs: Expr) -> Result<Operand> {
    fn let_anon_soft(&mut self, rhs: Flow<Option<Operation>>) -> Result<Operand> {
        match rhs {
            Expr::Operand(rand) => Ok(rand),
            Flow::Leaf(Some(Operation::Wrap(rand))) => Ok(rand),
            _ => self.let_anon_hard(rhs),
        }
    }
    fn let_anon_hard(&mut self, rhs: Expr) -> Result<Operand> {

    fn let_anon_hard(&mut self, rhs: Flow<Option<Operation>>) -> Result<Operand> {
        let lhs = self.gen_local();
        self.stms.push(Stm::Let { lhs, rhs });
        Ok(Operand::Local(lhs))

M src/eval.rs => src/eval.rs +26 -19
@@ 21,29 21,41 @@ impl<'c> EvalDef<'c> {
        Self { cache, regs: HashMap::new() }
    }

    fn run_fun(mut self, params: &[LocalId], args: &[Operand], body: &Block<Return>) -> Result<Operand> {
    fn run_fun(mut self, params: &[LocalId], args: &[Operand], body: &Block<Option<Operation>>) -> Result<Operand> {
        for (id, arg) in params.iter().zip(args) {
            self.regs.insert(*id, arg.clone());
            self.regs.insert(*id, *arg);
        }
        for stm in &body.stms {
            self.eval_stm(stm)?;
        }
        match &body.term {
            Return::Val(tail) => self.eval(tail),
        }
        self.eval_div(&body.tail)
    }

    fn run_var(mut self, body: &Block<Operand>) -> Result<Operand> {
    fn run_var(mut self, body: &Block<Option<Operation>>) -> Result<Operand> {
        self.eval_stms(&body.stms)?;
        self.eval_operand(body.term)
        self.eval_div(&body.tail)
    }

    fn eval_div(&mut self, div: &Flow<Option<Operation>>) -> Result<Operand> {
        use abase::Const::*;
        use Operand::*;
        match div {
            Flow::Leaf(Some(ration)) => self.eval_op(ration),
            Flow::Leaf(None) => todo!(),
            Flow::Diverge(Diverge::If(pred, conseq, alt)) => match self.eval_operand(*pred)? {
                Const(Bool(true)) => self.eval_stms(&conseq.stms).and_then(|()| self.eval_div(&conseq.tail)),
                Const(Bool(false)) => self.eval_stms(&alt.stms).and_then(|()| self.eval_div(&alt.tail)),
                pe => panic!("ice: `if` expects const bool predicate, found {pe:?}"),
            },
        }
    }

    fn eval(&mut self, expr: &Expr) -> Result<Operand> {
    fn eval_op(&mut self, ration: &Operation) -> Result<Operand> {
        use abase::Const::*;
        use Binop::*;
        use Operand::*;
        match expr {
            &Expr::Binop(op, x, y) => match (self.eval_operand(x)?, self.eval_operand(y)?) {
        match ration {
            &Operation::Binop(op, x, y) => match (self.eval_operand(x)?, self.eval_operand(y)?) {
                (Const(x), Const(y)) => Ok(Const(match op {
                    Add => (x + y).unwrap(),
                    Sub => (x - y).unwrap(),


@@ 59,19 71,14 @@ impl<'c> EvalDef<'c> {
                })),
                (x_e, y_e) => panic!("ice: cannot apply binary operation {op:?} to {x_e:?} and {y_e:?}"),
            },
            Expr::Call(f, xs) => match self.eval_operand(*f)? {
            Operation::Call(f, xs) => match self.eval_operand(*f)? {
                Global(fid) => {
                    let xs = xs.iter().map(|x| self.eval_operand(*x)).collect::<Result<Vec<_>>>()?;
                    self.cache.fetch_evaluated_at(fid, &xs)
                }
                fe => panic!("ice: applying non-function {fe:?} ({f:?})"),
            },
            &Expr::Operand(rand) => self.eval_operand(rand),
            Expr::If(pred, conseq, alt) => match self.eval_operand(*pred)? {
                Const(Bool(true)) => self.eval_stms(&conseq.stms).and_then(|()| self.eval(&conseq.term)),
                Const(Bool(false)) => self.eval_stms(&alt.stms).and_then(|()| self.eval(&alt.term)),
                pe => panic!("ice: `if` expects const bool predicate, found {pe:?}"),
            },
            &Operation::Wrap(rand) => self.eval_operand(rand),
        }
    }



@@ 85,7 92,7 @@ impl<'c> EvalDef<'c> {
    fn eval_stm(&mut self, stm: &Stm) -> Result<()> {
        match stm {
            Stm::Let { lhs, rhs } => {
                let val = self.eval(rhs)?;
                let val = self.eval_div(rhs)?;
                self.regs.insert(*lhs, val);
                Ok(())
            }


@@ 99,7 106,7 @@ impl<'c> EvalDef<'c> {
                Operand::Const(_) => return Ok(rand),
                Operand::Local(id) =>
                    if let Some(val) = self.regs.get(&id) {
                        rand = val.clone();
                        rand = *val;
                    } else {
                        panic!("ice: undefined local {rand:?}\nregs: {:?}", self.regs)
                    },