From 4834189452517eda92ba9d88174236ca4d013fa2 Mon Sep 17 00:00:00 2001 From: Jessie Zijia Xu Date: Wed, 3 Jul 2024 16:07:05 -0700 Subject: [PATCH 1/2] parent 9e567c88dcd12a2369bf795ea2810a11377c6f4e author Jessie Zijia Xu 1720048025 -0700 committer Jessie Xu 1724974035 -0700 early return bug & for loop fixes --- crates/forge_analyzer/src/definitions.rs | 139 ++++++- crates/forge_analyzer/src/ir.rs | 97 ++++- test-apps/basic/package.json | 4 +- test-apps/basic/src/index.tsx | 447 +++++++++++++++-------- 4 files changed, 507 insertions(+), 180 deletions(-) diff --git a/crates/forge_analyzer/src/definitions.rs b/crates/forge_analyzer/src/definitions.rs index 9f277f9..d6bda2a 100644 --- a/crates/forge_analyzer/src/definitions.rs +++ b/crates/forge_analyzer/src/definitions.rs @@ -978,6 +978,11 @@ impl<'cx> FunctionAnalyzer<'cx> { self.body.set_terminator(self.block, term); } + #[inline] + fn get_curr_terminator(&mut self) -> Option { + self.body.get_terminator(self.block) + } + fn as_intrinsic(&self, callee: &[PropPath], first_arg: Option<&Expr>) -> Option { fn is_storage_read(prop: &JsWord) -> bool { *prop == *"get" || *prop == *"getSecret" || *prop == *"query" @@ -1120,7 +1125,9 @@ impl<'cx> FunctionAnalyzer<'cx> { /// Sets the current block to `block` and returns the previous block. #[inline] fn goto_block(&mut self, block: BasicBlockId) -> BasicBlockId { - self.set_curr_terminator(Terminator::Goto(block)); + if self.get_curr_terminator().is_none() { + self.set_curr_terminator(Terminator::Goto(block)); + } mem::replace(&mut self.block, block) } @@ -1286,7 +1293,6 @@ impl<'cx> FunctionAnalyzer<'cx> { } fn lower_call(&mut self, callee: CalleeRef<'_>, args: &[ExprOrSpread]) -> Operand { - // debug!("in da lower call"); let props = normalize_callee_expr(callee, self.res, self.module); if let Some(&PropPath::Def(id)) = props.first() { if self.res.is_imported_from(id, "@forge/ui").map_or( @@ -1312,6 +1318,7 @@ impl<'cx> FunctionAnalyzer<'cx> { Expr::Fn(FnExpr { ident: _, function }) => { if let Some(body) = &function.body { self.lower_stmts(&body.stmts); + return Operand::UNDEF; } } @@ -1599,6 +1606,7 @@ impl<'cx> FunctionAnalyzer<'cx> { .push_tmp(self.block, Rvalue::Unary(op.into(), arg), None); Operand::with_var(tmp) } + Expr::Update(UpdateExpr { op, prefix, arg, .. }) => { @@ -1695,10 +1703,12 @@ impl<'cx> FunctionAnalyzer<'cx> { }) => { let cond = self.lower_expr(test, None); let curr = self.block; - let rest = self.body.new_block(); - let cons_block = self.body.new_block(); - let alt_block = self.body.new_block(); + let [temp1, temp2, temp3] = self.body.new_blocks(); + let rest = self.body.new_blockbuilder(); + let cons_block = self.body.new_blockbuilder(); + let alt_block = self.body.new_blockbuilder(); self.set_curr_terminator(Terminator::If { + // TODO: COME BACK TO? cond, cons: cons_block, alt: alt_block, @@ -1706,6 +1716,7 @@ impl<'cx> FunctionAnalyzer<'cx> { self.block = cons_block; let cons = self.lower_expr(cons, None); let cons_phi = self.body.push_tmp(self.block, Rvalue::Read(cons), None); + self.set_curr_terminator(Terminator::Goto(rest)); self.block = alt_block; let alt = self.lower_expr(alt, None); @@ -1719,6 +1730,7 @@ impl<'cx> FunctionAnalyzer<'cx> { ); Operand::with_var(phi) } + Expr::Call(CallExpr { callee, args, .. }) => self.lower_call(callee.into(), args), Expr::New(NewExpr { callee, args, .. }) => { if let Expr::Ident(ident) = &**callee { @@ -1819,12 +1831,28 @@ impl<'cx> FunctionAnalyzer<'cx> { } } + // Lowers all statements from the given `stmts`. + // If a return is encounted, return early to prevent + // unreachable statements that come afterwards from being lowered. fn lower_stmts(&mut self, stmts: &[Stmt]) { for stmt in stmts { self.lower_stmt(stmt); + if let Stmt::Return(_) = stmt { + return; + } } } + // Lowers a single statement by pushing corresponding instruction(s)/expression(s) + // onto self.body.blockbuilders[block]. + // + // Corresponding instruction(s)/expression(s) are initially placed in + // self.body.blockbuilders[block] and transferred to self.body.blocks[block] + // once the block's terminator gets set. + // + // B/c of this, an empty block is pushed to self.body.blocks whenever + // a new block is added to self.body.blockbuilders, to ensure space is allocated + // on self.body.blocks for all blocks when instructions are moved. fn lower_stmt(&mut self, n: &Stmt) { match n { Stmt::Block(BlockStmt { stmts, .. }) => self.lower_stmts(stmts), @@ -1833,6 +1861,7 @@ impl<'cx> FunctionAnalyzer<'cx> { Stmt::With(WithStmt { obj, body, .. }) => { let opnd = self.lower_expr(obj, None); self.body.push_expr(self.block, Rvalue::Read(opnd)); + self.lower_stmt(body); } Stmt::Return(ReturnStmt { arg, .. }) => { @@ -1846,17 +1875,29 @@ impl<'cx> FunctionAnalyzer<'cx> { Stmt::Labeled(LabeledStmt { label, body, .. }) => { self.lower_stmt(body); } + // TODO: Lower Break and Continue Stmt::Break(BreakStmt { label, .. }) => {} Stmt::Continue(ContinueStmt { label, .. }) => {} Stmt::If(IfStmt { test, cons, alt, .. }) => { - let [cons_block, cont] = self.body.new_blocks(); + // Adds two blocks to the body: + // - cons_block: block to store insts that run if the test condition of the Stmt::If is true + // - cont: block to store insts that run after the Stmt::If + let [temp1, temp2] = self.body.new_blocks(); + let [cons_block, cont] = self.body.new_blockbuilders(); + + // If an alt block (`else` case) is present, add another block to the body let alt_block = if let Some(alt) = alt { - let alt_block = self.body.new_block(); + let temp3 = self.body.new_block(); + let alt_block = self.body.new_blockbuilder(); let old_block = mem::replace(&mut self.block, alt_block); self.lower_stmt(alt); - self.set_curr_terminator(Terminator::Goto(cont)); + + if self.get_curr_terminator().is_none() { + self.set_curr_terminator(Terminator::Goto(cont)); + } + self.block = old_block; alt_block } else { @@ -1870,6 +1911,7 @@ impl<'cx> FunctionAnalyzer<'cx> { }); self.block = cons_block; self.lower_stmt(cons); + self.goto_block(cont); } Stmt::Switch(SwitchStmt { @@ -1898,7 +1940,8 @@ impl<'cx> FunctionAnalyzer<'cx> { } } Stmt::While(WhileStmt { test, body, .. }) => { - let [check, cont, body_id] = self.body.new_blocks(); + let [temp1, temp2, temp3] = self.body.new_blocks(); + let [check, cont, body_id] = self.body.new_blockbuilders(); self.set_curr_terminator(Terminator::Goto(check)); self.block = check; let cond = self.lower_expr(test, None); @@ -1913,7 +1956,8 @@ impl<'cx> FunctionAnalyzer<'cx> { self.block = cont; } Stmt::DoWhile(DoWhileStmt { test, body, .. }) => { - let [check, cont, body_id] = self.body.new_blocks(); + let [temp1, temp2, temp3] = self.body.new_blocks(); + let [check, cont, body_id] = self.body.new_blockbuilders(); self.set_curr_terminator(Terminator::Goto(body_id)); self.block = body_id; self.lower_stmt(body); @@ -1943,7 +1987,8 @@ impl<'cx> FunctionAnalyzer<'cx> { } None => {} } - let [check, cont, body_id] = self.body.new_blocks(); + let [temp1, temp2, temp3] = self.body.new_blocks(); + let [check, cont, body_id] = self.body.new_blockbuilders(); self.goto_block(check); if let Some(test) = test { let cond = self.lower_expr(test, None); @@ -2302,7 +2347,18 @@ impl Visit for FunctionCollector<'_> { }; if let Some(BlockStmt { stmts, .. }) = &n.body { analyzer.lower_stmts(stmts); - let body = analyzer.body; + let mut body = analyzer.body; + + let mut blocks_to_update: Vec = Vec::new(); + for (id, block) in body.blocks.iter_enumerated() { + if !block.set_term_called { + blocks_to_update.push(id); + } + } + for id in blocks_to_update { + body.set_terminator(id, Terminator::Ret); + } + *self.res.def_mut(*owner).expect_body() = body; } } @@ -2349,7 +2405,18 @@ impl Visit for FunctionCollector<'_> { }; if let Some(BlockStmt { stmts, .. }) = &n.function.body { analyzer.lower_stmts(stmts); - let body = analyzer.body; + let mut body = analyzer.body; + + let mut blocks_to_update: Vec = Vec::new(); + for (id, block) in body.blocks.iter_enumerated() { + if !block.set_term_called { + blocks_to_update.push(id); + } + } + for id in blocks_to_update { + body.set_terminator(id, Terminator::Ret); + } + *self.res.def_mut(*owner).expect_body() = body; } } @@ -2410,7 +2477,19 @@ impl Visit for FunctionCollector<'_> { .push_inst(analyzer.block, Inst::Assign(RETURN_VAR, Rvalue::Read(opnd))); } } - *self.res.def_mut(owner).expect_body() = analyzer.body; + let mut body = analyzer.body; + + let mut blocks_to_update: Vec = Vec::new(); + for (id, block) in body.blocks.iter_enumerated() { + if !block.set_term_called { + blocks_to_update.push(id); + } + } + for id in blocks_to_update { + body.set_terminator(id, Terminator::Ret); + } + + *self.res.def_mut(owner).expect_body() = body; self.parent = old_parent; } @@ -2518,6 +2597,10 @@ impl Visit for FunctionCollector<'_> { analyzer.block, Inst::Assign(RETURN_VAR, Rvalue::Read(opnd)), ); + + analyzer + .body + .set_terminator(analyzer.block, Terminator::Ret); *self.res.def_mut(owner).expect_body() = analyzer.body; self.parent = old_parent; } @@ -2645,7 +2728,21 @@ impl FunctionCollector<'_> { }; if let Some(BlockStmt { stmts, .. }) = &n.body { analyzer.lower_stmts(stmts); - let body = analyzer.body; + let mut body = analyzer.body; + + let mut blocks_to_update: Vec = Vec::new(); + for (id, block) in body.blocks.iter_enumerated() { + if !block.set_term_called { + blocks_to_update.push(id); + } + } + + // Ensures that instructions of all blocks from body.blockbuilders + // are moved to body.blocks + for id in blocks_to_update { + body.set_terminator(id, Terminator::Ret); + } + *self.res.def_mut(owner).expect_body() = body; } } @@ -3180,7 +3277,17 @@ impl Visit for GlobalCollector<'_> { } } analyzer.lower_stmts(all_module_items.as_slice()); - let body = analyzer.body; + let mut body = analyzer.body; + + let mut blocks_to_update: Vec = Vec::new(); + for (id, block) in body.blocks.iter_enumerated() { + if !block.set_term_called { + blocks_to_update.push(id); + } + } + for id in blocks_to_update { + body.set_terminator(id, Terminator::Ret); + } *self.res.def_mut(owner).expect_body() = body; } diff --git a/crates/forge_analyzer/src/ir.rs b/crates/forge_analyzer/src/ir.rs index 940e630..f156ad9 100644 --- a/crates/forge_analyzer/src/ir.rs +++ b/crates/forge_analyzer/src/ir.rs @@ -55,9 +55,8 @@ pub struct BranchTargets { branch: SmallVec<[BasicBlockId; 2]>, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub enum Terminator { - #[default] Ret, Goto(BasicBlockId), Throw, @@ -107,10 +106,17 @@ pub enum Rvalue { Template(Template), } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct BasicBlock { pub insts: Vec, pub term: Terminator, + pub set_term_called: bool, // represents whether or not we've + // moved over its corresponding BasicBlockBuilder +} + +#[derive(Clone, Debug, Default)] +pub struct BasicBlockBuilder { + pub insts: Vec, } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -145,6 +151,7 @@ pub struct Body { pub class_instantiations: HashMap, predecessors: OnceCell>>, pub dominator_tree: OnceCell, + pub blockbuilders: TiVec, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -320,13 +327,19 @@ impl Body { Self { vars: local_vars, owner: None, - blocks: vec![BasicBlock::default()].into(), + blocks: vec![BasicBlock { + insts: Vec::new(), + term: Terminator::Ret, + set_term_called: false, + }] + .into(), values: FxHashMap::default(), class_instantiations: Default::default(), ident_to_local: Default::default(), def_id_to_vars: Default::default(), predecessors: Default::default(), dominator_tree: Default::default(), + blockbuilders: vec![BasicBlockBuilder { insts: Vec::new() }].into(), } } @@ -374,6 +387,14 @@ impl Body { self.blocks.iter_mut_enumerated() } + #[inline] + pub(crate) fn iter_blockbuilders_enumerated( + &self, + ) -> impl ExactSizeIterator + DoubleEndedIterator + { + self.blockbuilders.iter_enumerated() + } + #[inline] pub(crate) fn owner(&self) -> Option { self.owner @@ -422,18 +443,29 @@ impl Body { #[inline] pub(crate) fn new_block(&mut self) -> BasicBlockId { - self.blocks.push_and_get_key(BasicBlock::default()) + self.new_block_with_terminator(Terminator::Ret) } pub(crate) fn new_blocks(&mut self) -> [BasicBlockId; NUM] { array::from_fn(|_| self.new_block()) } + #[inline] + pub(crate) fn new_blockbuilder(&mut self) -> BasicBlockId { + self.blockbuilders + .push_and_get_key(BasicBlockBuilder::default()) + } + + pub(crate) fn new_blockbuilders(&mut self) -> [BasicBlockId; NUM] { + array::from_fn(|_| self.new_blockbuilder()) + } + #[inline] pub(crate) fn new_block_with_terminator(&mut self, term: Terminator) -> BasicBlockId { self.blocks.push_and_get_key(BasicBlock { + insts: Vec::new(), term, - ..Default::default() + set_term_called: false, }) } @@ -643,6 +675,8 @@ impl Body { }) } + + pub(crate) fn predecessors(&self, block: BasicBlockId) -> &[BasicBlockId] { &self.predecessors.get_or_init(|| { let mut preds: TiVec<_, _> = vec![SmallVec::new(); self.blocks.len()].into(); @@ -660,14 +694,37 @@ impl Body { })[block] } + // Moves all instructions of a given block from body.blockbuilders[bb] + // to body.blocks[bb], and sets its terminator. + // + // TODO: Returning the old terminator may not be necessary #[inline] pub(crate) fn set_terminator(&mut self, bb: BasicBlockId, term: Terminator) -> Terminator { - mem::replace(&mut self.blocks[bb].term, term) + let builder_insts = std::mem::take(&mut self.blockbuilders[bb].insts); + + let block = BasicBlock { + insts: builder_insts, + term, + set_term_called: true, + }; + + let old_block = mem::replace(&mut self.blocks[bb], block); + old_block.term + } + + // Returns the terminator of a given block if it has been set, otherwise returns None. + #[inline] + pub(crate) fn get_terminator(&mut self, bb: BasicBlockId) -> Option { + if self.blocks[bb].set_term_called { + Some(self.blocks[bb].term.clone()) + } else { + None + } } #[inline] pub(crate) fn push_inst(&mut self, bb: BasicBlockId, inst: Inst) { - self.blocks[bb].insts.push(inst); + self.blockbuilders[bb].insts.push(inst); } pub(crate) fn resolve_prop(&mut self, bb: BasicBlockId, opnd: Operand) -> Projection { @@ -757,12 +814,12 @@ impl Body { #[inline] pub(crate) fn push_assign(&mut self, bb: BasicBlockId, var: Variable, val: Rvalue) { - self.blocks[bb].insts.push(Inst::Assign(var, val)); + self.blockbuilders[bb].insts.push(Inst::Assign(var, val)); } #[inline] pub(crate) fn push_expr(&mut self, bb: BasicBlockId, val: Rvalue) { - self.blocks[bb].insts.push(Inst::Expr(val)); + self.blockbuilders[bb].insts.push(Inst::Expr(val)); } #[inline] @@ -893,6 +950,17 @@ impl<'a> IntoIterator for &'a BasicBlock { } } +impl<'a> IntoIterator for &'a BasicBlockBuilder { + type Item = &'a Inst; + + type IntoIter = slice::Iter<'a, Inst>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.insts.iter() + } +} + impl fmt::Display for Literal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { @@ -975,6 +1043,15 @@ impl fmt::Display for BasicBlock { } } +impl fmt::Display for BasicBlockBuilder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for inst in &self.insts { + writeln!(f, " {inst}")?; + } + write!(f, " ") + } +} + impl fmt::Display for UnOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { diff --git a/test-apps/basic/package.json b/test-apps/basic/package.json index f52c320..121b1dc 100644 --- a/test-apps/basic/package.json +++ b/test-apps/basic/package.json @@ -14,6 +14,8 @@ }, "dependencies": { "@forge/api": "3.2.0", - "@forge/ui": "1.11.0" + "@forge/ui": "1.11.0", + "atlassian-jwt": "^2.0.3", + "jsonwebtoken": "^9.0.2" } } \ No newline at end of file diff --git a/test-apps/basic/src/index.tsx b/test-apps/basic/src/index.tsx index 60da0c8..e5c08a9 100644 --- a/test-apps/basic/src/index.tsx +++ b/test-apps/basic/src/index.tsx @@ -1,123 +1,3 @@ -// function newfunc() { -// let a = 3; -// if (a > 2) { -// a = 1; -// let b = a; -// } -// let c = a; -// } - -// function compnewfunc2() { -// let a = Math.random(); -// let b = 0; -// if (a >= 0.5) { -// if (a > 0.5) { -// a = a + 1; -// } else { -// a = a + 2; -// } -// b = a + 3; -// } -// let c = a; -// } - -// function compnewfunc3() { -// let a = Math.random(); // bb0 -// let b = 0; -// if (a >= 0.5) { -// if (a > 0.5) { // bb1 -// a = a + 1; // bb4 -// } else { -// a = a + 2; // bb6 -// } -// b = a + 3; // bb5 -// } else { // bb3 -// b = a + 4; -// } -// let c = a; // bb2 -// } - -// // function forloop() { -// // let a = 0; -// // for (let i = 0; i < 10; i++) { -// // a = a + i; -// // } -// // return a; -// // } - -// function whileloop() { -// let a = 0; -// let i = 0; -// while (i < 10) { -// a = a + i; // bb3 -// i = i + 1; -// } -// return a; -// } - -// function nestedwhileloop() { -// let a = 0; -// let i = 0; -// while (i < 10) { -// let j = 0; -// while (j < 10) { -// a = a + i + j; -// j = j + 1; -// } -// i = i + 1; -// } -// return a; -// } - -// function whileandcond() { -// let a = 0; -// let i = 0; // bb0 -// while (i < 10) { // bb1 -// if (i > 5) { // bb3 -// a = a + i; // bb4 -// } -// i = i + 1; // bb5 -// } -// return a; // bb2 -// } - -// function whileandcond2() { -// let a = 0; -// let i = 0; // bb0 -// while (i < 10) { // bb1 -// if (i > 5) { // bb3 -// a = a + 1; // bb4 -// } else { -// a = a + 2; // bb6 -// } -// i = i + 1; // bb5 -// } -// return a; // bb2 -// } - -// function whileincond() { -// let a = 0; -// let b = Math.random(); // bb0 -// if (b < 0.5) { // bb1 -// while (a < 2) { // bb4 -// a = a + b; // bb6 -// } -// // bb5 -// } else { -// a = 1; // bb3 -// } -// return a; // bb2 -// } - -// function earlyret() { -// let a = 0; -// if (a > 5) { // bb0 -// a = 1; -// return a; // bb1 -// } -// return a; // bb2 -// } - import ForgeUI, { render, Fragment, Macro, Text } from '@forge/ui'; import api, { route, fetch } from '@forge/api'; import { testFn } from './test'; @@ -134,38 +14,38 @@ let test_function = (word) => { } function test_bug() { - let b = 3; - let a = b -} - -function complex(a) { - let b = a - 20; - let c = b + 1; - b = 10; - return b * 10; -} - -function simple() { - let a = 4; - let b = 7; - a = 10; - a = 5; -} - -function reassign() { - let a = 4; - let b = "7"; - b = "7" + a; -} - -function var_reref() { - let a = 4; - let b = 7; - a = 10; - let c = a + b - a = 5; - c = 2 * b; -} + let b = 3; + let a = b + } + + function complex(a) { + let b = a - 20; + let c = b + 1; + b = 10; + return b * 10; + } + + function simple() { + let a = 4; + let b = 7; + a = 10; + a = 5; + } + + function reassign() { + let a = 4; + let b = "7"; + b = "7" + a; + } + + function var_reref() { + let a = 4; + let b = 7; + a = 10; + let c = a + b + a = 5; + c = 2 * b; + } const App = () => { @@ -202,4 +82,265 @@ const App = () => { ); }; -export const run = render(} />); \ No newline at end of file +export const run = render(} />); + +function reassign() { + let a = 3; + let b = 4; + a = 1; + } + + function retimmed() { + return + } + + function reassign2() { + let a = 3; + let b = 4; + a = 1; + return + } + + function updatetest() { + let i = 0; + i++; + } + + function updatetest2() { + let i = 0; + i = i + 1; + } + + // function foo(a) { + // let b = a - 20; + // let c = b + 1; + // b = 10; + // return b * 10; + // } + + // function reassign_expr() { + // let a = 4; + // let b = "7"; + // b = "7" + a; + // let c = a + b; + // } + + // w/ control flow + function newfunc() { + let a = 3; + if (a > 2) { + a = 1; + let b = a; + } + let c = a; + } + + function newfunc2() { + let a = 3; + if (a > 2) { + a = 1; + let b = a; + } else { + a = 2; + let b = a; + } + let c = a; + } + + function compnewfunc2() { + let a = Math.random(); + let b = 0; + if (a >= 0.5) { + if (a > 0.5) { + a = a + 1; + } else { + a = a + 2; + } + b = a + 3; + } + let c = a; + } + + function compnewfunc3() { + let a = Math.random(); // bb0 + let b = 0; + if (a >= 0.5) { + if (a > 0.5) { // bb1 + a = a + 1; // bb4 + } else { + a = a + 2; // bb6 + } + b = a + 3; // bb5 + } else { // bb3 + b = a + 4; + } + let c = a; // bb2 + } + + function forloop() { + let a = 0; + for (let i = 0; i < 10; i = i + 1) { + a = a + i; + } + return a; + } + + function whileloop() { + let a = 0; + let i = 0; + while (i < 10) { + a = a + i; // bb3 + i = i + 1; + } + return a; + } + + function nestedforloop() { + let a = 0; + for (let i = 0; i < 10; i = i + 1) { + for (let j = 0; j < 10; j = j + 1) { + a = a + i + j; + } + } + return a; + } + + function nestedwhileloop() { + let a = 0; + let i = 0; // bb0 + while (i < 10) { // bb1 + let j = 0; // bb3 + while (j < 10) { // bb4 + a = a + i + j; // bb6 + j = j + 1; + } + i = i + 1; // bb5 + } + return a; // bb2 + } + + function whileandcond() { + let a = 0; + let i = 0; // bb0 + while (i < 10) { // bb1 + if (i > 5) { // bb3 + a = a + i; // bb4 + } + i++; // bb5 + } + return a; // bb2 + } + + function whileandcond2() { + let a = 0; + let i = 0; // bb0 + while (i < 10) { // bb1 + if (i > 5) { // bb3 + a = a + 1; // bb4 + } else { + a = a + 2; // bb6 + } + i++; // bb5 + } + return a; // bb2 + } + + function whileincond() { + let a = 0; + let b = Math.random(); // bb0 + if (b < 0.5) { // bb1 + while (a < 2) { // bb4 + a = a + b; // bb6 + } + // bb5 + } else { + a = 1; // bb3 + } + return a; // bb2 + } + function earlyretforloop() { + for (let i = 0; i < 10; i = i + 1) { + let a = Math.random(); + if (a > 0.5) { + return; + } + } + } + + function earlyretwhileloop() { + let i = 0; // bb0 + while (i < 10) { // bb1 + let a = Math.random(); // bb3 + if (a > 0.5) { + return; // bb4 + } + i = i + 1; // bb5 + } + // bb2 + } + + // early return in `if` only + function earlyret() { + let a = 0; + if (a > 5) { // bb0 + a = 1; + return a; // bb1 + } + let b = 4; + return a; // bb2 + } + + // early return in `else` only + function earlyret2() { + let a = 0; + if (a > 5) { + a = 1; + } else { + a = 2; + return a; + let c = 3; + } + let b = 4; + return a; + } + + // early return in 'if' and 'else' + function earlyret3() { + let a = 0; + if (a > 5) { + a = 1; + return a; + } else { + a = 2; + return a; + } + let b = 4; // a bb still gets created, it's just + // never jumped to. check if this is OK? + // assuming that this is how compilers work + // to detect unreachable code + return a; + } + + // function earlyretloop() { + // let a = 0; + // let i = 0; + // while (i < 10) { + // if (i > 5) { + // return 1; + // } + // i = i + 1; + // } + // } + + function loopbreak() { + let a = 0; + let i = 0; // bb0 + while (i < 10) { // bb1 + if (i > 5) { // bb3 + a = 1; // bb4 + break; + } + i = i + 1; // bb5 + } + return a; // bb2 + } \ No newline at end of file From 1dd8abbda4b4d0494d8ad4afea99f888fa33e1d2 Mon Sep 17 00:00:00 2001 From: Jessie Xu Date: Thu, 29 Aug 2024 17:16:19 -0700 Subject: [PATCH 2/2] cargo fmt fix --- crates/forge_analyzer/src/ir.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/forge_analyzer/src/ir.rs b/crates/forge_analyzer/src/ir.rs index f156ad9..2593768 100644 --- a/crates/forge_analyzer/src/ir.rs +++ b/crates/forge_analyzer/src/ir.rs @@ -675,8 +675,6 @@ impl Body { }) } - - pub(crate) fn predecessors(&self, block: BasicBlockId) -> &[BasicBlockId] { &self.predecessors.get_or_init(|| { let mut preds: TiVec<_, _> = vec![SmallVec::new(); self.blocks.len()].into();