Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions openvaf/hir/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ impl<'a> BodyRef<'a> {
}
hir_def::Stmt::WhileLoop { cond, body } => Some(Stmt::WhileLoop { cond, body }),
hir_def::Stmt::Case { discr, ref case_arms } => Some(Stmt::Case { discr, case_arms }),
hir_def::Stmt::Break => Some(Stmt::Break),
hir_def::Stmt::Continue => Some(Stmt::Continue),
hir_def::Stmt::Return { value } => Some(Stmt::Return { value }),
}
}
}
Expand Down Expand Up @@ -277,6 +280,9 @@ pub enum Stmt<'a> {
ForLoop { init: StmtId, cond: ExprId, incr: StmtId, body: StmtId },
WhileLoop { cond: ExprId, body: StmtId },
Case { discr: ExprId, case_arms: &'a [Case] }, // TODO lint on unreachable
Break,
Continue,
Return { value: Option<ExprId> },
}
impl Stmt<'_> {
#[inline]
Expand Down
5 changes: 5 additions & 0 deletions openvaf/hir_def/src/body/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ impl LowerCtx<'_> {
ast::Stmt::CaseStmt(stmt) => self.collect_case_stmt(stmt),
ast::Stmt::EventStmt(stmt) => return self.collect_event_stmt(stmt),
ast::Stmt::BlockStmt(stmt) => self.collect_block(stmt),
ast::Stmt::BreakStmt(_) => Stmt::Break,
ast::Stmt::ContinueStmt(_) => Stmt::Continue,
ast::Stmt::ReturnStmt(stmt) => {
Stmt::Return { value: stmt.value().map(|e| self.collect_expr(e)) }
}
};
self.alloc_stmt(s, AstPtr::new(&stmt), stmt.attrs())
}
Expand Down
10 changes: 10 additions & 0 deletions openvaf/hir_def/src/body/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ impl Printer<'_> {
});
wln!(self, "endcase");
}
Stmt::Break => wln!(self, "break;"),
Stmt::Continue => wln!(self, "continue;"),
Stmt::Return { value } => {
w!(self, "return");
if let Some(value) = value {
w!(self, " ");
self.pretty_print_expr(value);
}
wln!(self, ";");
}
}
}
pub fn pretty_print_expr(&mut self, e: ExprId) {
Expand Down
63 changes: 54 additions & 9 deletions openvaf/hir_def/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,45 @@ pub enum Stmt {
Missing,
Empty,
Expr(ExprId),
EventControl { event: Event, body: StmtId },
Assignment { dst: ExprId, val: ExprId, assignment_kind: ast::AssignOp },
Block { /*scope: Option<BlockId>,*/ body: Vec<StmtId> },
If { cond: ExprId, then_branch: StmtId, else_branch: StmtId },
ForLoop { init: StmtId, cond: ExprId, incr: StmtId, body: StmtId },
WhileLoop { cond: ExprId, body: StmtId },
Case { discr: ExprId, case_arms: Vec<Case> }, // TODO lint on unreachable
EventControl {
event: Event,
body: StmtId,
},
Assignment {
dst: ExprId,
val: ExprId,
assignment_kind: ast::AssignOp,
},
Block {
/*scope: Option<BlockId>,*/ body: Vec<StmtId>,
},
If {
cond: ExprId,
then_branch: StmtId,
else_branch: StmtId,
},
ForLoop {
init: StmtId,
cond: ExprId,
incr: StmtId,
body: StmtId,
},
WhileLoop {
cond: ExprId,
body: StmtId,
},
Case {
discr: ExprId,
case_arms: Vec<Case>,
}, // TODO lint on unreachable
/// VAMS-2023 §5.11 — exit the innermost loop.
Break,
/// VAMS-2023 §5.11 — skip to the end of the innermost loop (re-check condition).
Continue,
/// VAMS-2023 §5.11 / §4.7.2.2 — early exit from an analog user-defined function.
Return {
value: Option<ExprId>,
},
}

#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
Expand Down Expand Up @@ -177,11 +209,18 @@ impl Stmt {
#[inline]
pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
match *self {
Stmt::Empty | Stmt::Missing | Stmt::Block { .. } | Stmt::EventControl { .. } => (),
Stmt::Empty
| Stmt::Missing
| Stmt::Block { .. }
| Stmt::EventControl { .. }
| Stmt::Break
| Stmt::Continue => (),
Stmt::If { cond: expr, .. }
| Stmt::ForLoop { cond: expr, .. }
| Stmt::WhileLoop { cond: expr, .. }
| Stmt::Expr(expr) => f(expr),
Stmt::Return { value: Some(expr) } => f(expr),
Stmt::Return { value: None } => (),
Stmt::Assignment { dst, val, .. } => {
f(dst);
f(val)
Expand All @@ -202,7 +241,13 @@ impl Stmt {
#[inline]
pub fn walk_child_stmts(&self, mut f: impl FnMut(StmtId)) {
match *self {
Stmt::Expr(_) | Stmt::Assignment { .. } | Stmt::Missing | Stmt::Empty => (),
Stmt::Expr(_)
| Stmt::Assignment { .. }
| Stmt::Missing
| Stmt::Empty
| Stmt::Break
| Stmt::Continue
| Stmt::Return { .. } => (),
Stmt::WhileLoop { body, .. } | Stmt::EventControl { body, .. } => f(body),
Stmt::If { then_branch: true_stmt, else_branch: false_stmt, .. } => {
f(true_stmt);
Expand Down
1 change: 1 addition & 0 deletions openvaf/hir_lower/src/body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl<'c1, 'c2> BodyLoweringCtx<'_, 'c1, 'c2> {
_ => {}
},
Stmt::Assignment { .. } | Stmt::Expr(_) | Stmt::Contribute { .. } => {}
Stmt::Break | Stmt::Continue | Stmt::Return { .. } => {}
Stmt::EventControl { event, body } => {
let inner = in_cross || matches!(event, Event::Cross);
self.collect_cross_assigned(body, inner, dst);
Expand Down
18 changes: 18 additions & 0 deletions openvaf/hir_lower/src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ pub struct LoweringCtx<'a, 'c> {
/// there are their initial value (read from the retained state), not a
/// per-evaluation reset.
pub in_initial_step: bool,
/// Stack of enclosing loops for `break`/`continue` (innermost last).
pub loop_stack: Vec<LoopTargets>,
/// Exit block of the analog function currently being lowered, if any.
pub function_exit: Option<Block>,
/// Function whose return place early `return` statements write into.
pub function_return: Option<hir::Function>,
}

/// CFG targets for the innermost enclosing loop.
#[derive(Clone, Copy)]
pub struct LoopTargets {
/// Where `continue` jumps (condition head for while; incr head for for).
pub continue_to: Block,
/// Where `break` jumps.
pub break_to: Block,
}

/// Synthetic constant base used as the (non-parameter) `lim_state` key for retained
Expand All @@ -59,6 +74,9 @@ impl<'a, 'c> LoweringCtx<'a, 'c> {
num_noise_sources: 0,
retained_states: AHashMap::default(),
in_initial_step: false,
loop_stack: Vec::new(),
function_exit: None,
function_return: None,
}
}

Expand Down
50 changes: 48 additions & 2 deletions openvaf/hir_lower/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use hir::signatures::{
NATURE_ACCESS_NODES, NATURE_ACCESS_NODE_GND, NATURE_ACCESS_PORT_FLOW, REAL_EQ, REAL_OP,
SIMPARAM_DEFAULT, SIMPARAM_NO_DEFAULT, STR_EQ,
};
use hir::{Body, BuiltIn, Expr, ExprId, Literal, /*ParamSysFun,*/ Ref, ResolvedFun, Type};
use hir::{
Body, BodyRef, BuiltIn, Expr, ExprId, Literal, /*ParamSysFun,*/ Ref, ResolvedFun, Stmt,
Type,
};
use mir::builder::InstBuilder;
use mir::{Opcode, Value, FALSE, F_ZERO, GRAVESTONE, INFINITY, TRUE, ZERO};
use stdx::iter::zip;
Expand Down Expand Up @@ -244,7 +247,29 @@ impl BodyLoweringCtx<'_, '_, '_> {
self.ctx.def_place(PlaceKind::FunctionReturn(fun), init);

let body = fun.body(self.ctx.db);
BodyLoweringCtx { body: body.borrow(), path: self.path, ctx: self.ctx }.lower_entry_stmts();
let body_ref = body.borrow();
let needs_exit = body_has_return(&body_ref);
let (prev_exit, prev_fun, exit) = if needs_exit {
let exit = self.ctx.create_block();
let prev_exit = self.ctx.function_exit.replace(exit);
let prev_fun = self.ctx.function_return.replace(fun);
(prev_exit, prev_fun, Some(exit))
} else {
(None, None, None)
};

BodyLoweringCtx { body: body_ref, path: self.path, ctx: self.ctx }.lower_entry_stmts();

if let Some(exit) = exit {
self.ctx.ensured_sealed();
if !self.ctx.func.is_filled() {
self.ctx.ins().jump(exit);
}
self.ctx.seal_block(exit);
self.ctx.switch_to_block(exit);
self.ctx.function_exit = prev_exit;
self.ctx.function_return = prev_fun;
}

// write outputs back to original (including possibly required cast)
for (arg, &expr) in args {
Expand Down Expand Up @@ -1113,3 +1138,24 @@ impl BodyLoweringCtx<'_, '_, '_> {
BodyLoweringCtx { ctx: self.ctx, body: body.borrow(), path: self.path }.lower_expr(expr)
}
}

fn body_has_return(body: &BodyRef<'_>) -> bool {
fn walk(body: &BodyRef<'_>, stmt: hir::StmtId) -> bool {
match body.get_stmt(stmt) {
Some(Stmt::Return { .. }) => true,
Some(Stmt::Block { body: stmts }) => stmts.iter().any(|&s| walk(body, s)),
Some(Stmt::If { then_branch, else_branch, .. }) => {
walk(body, then_branch) || walk(body, else_branch)
}
Some(Stmt::WhileLoop { body: b, .. }) | Some(Stmt::EventControl { body: b, .. }) => {
walk(body, b)
}
Some(Stmt::ForLoop { init, incr, body: b, .. }) => {
walk(body, init) || walk(body, incr) || walk(body, b)
}
Some(Stmt::Case { case_arms, .. }) => case_arms.iter().any(|arm| walk(body, arm.body)),
_ => false,
}
}
body.entry().iter().any(|&s| walk(body, s))
}
Loading
Loading