mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[rust] Remove arena allocator
Using an arena allocator can be faster, but it comes with several challenges: * It requires tediously tagging nearly every value with a lifetime. Any function that has to deal with arena-allocated data (which is every meaningful function) ends up with a lifetime parameter. Bleh. We also have to thread the allocator itself wherever we need to allocate, though this is less of a problem since _most_ functions already need the Environment and we can store the allocator there. * There is not yet widespread support in the Rust ecosystem for using custom allocators with custom data types. This means that things like HashMap/Set and IndexMap/Set can't be arena allocated. This means that either we have to add support to these data types (by upstreaming or forking) or just accept that we're only partially using the arena allocator. * Finally, `bumpalo`'s `Box` cannot be moved out of, which turns out to be an annoying limitation that i've already had to work around several times. In the end i'm not sure arena allocators are worth it at this stage of the project. Relay Compiler has been successful without one, and other Rust-based internal compilers get by without them too.
This commit is contained in:
Generated
-7
@@ -450,7 +450,6 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
name = "forget_build_hir"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"forget_diagnostics",
|
||||
"forget_estree",
|
||||
"forget_hir",
|
||||
@@ -472,7 +471,6 @@ dependencies = [
|
||||
name = "forget_estree"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"forget_estree_codegen",
|
||||
"insta",
|
||||
"serde",
|
||||
@@ -505,7 +503,6 @@ dependencies = [
|
||||
name = "forget_fixtures"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"forget_build_hir",
|
||||
"forget_estree",
|
||||
"forget_estree_swc",
|
||||
@@ -520,7 +517,6 @@ dependencies = [
|
||||
name = "forget_hir"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"forget_diagnostics",
|
||||
"forget_estree",
|
||||
"forget_utils",
|
||||
@@ -533,7 +529,6 @@ dependencies = [
|
||||
name = "forget_optimization"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"forget_build_hir",
|
||||
"forget_diagnostics",
|
||||
"forget_estree",
|
||||
@@ -549,7 +544,6 @@ dependencies = [
|
||||
name = "forget_ssa"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"forget_diagnostics",
|
||||
"forget_hir",
|
||||
"forget_utils",
|
||||
@@ -562,7 +556,6 @@ dependencies = [
|
||||
name = "forget_utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"stacker",
|
||||
]
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ forget_swc_demo = { path = "crates/forget_swc_demo" }
|
||||
forget_utils = { path = "crates/forget_utils" }
|
||||
|
||||
# dependencies
|
||||
bumpalo = { version = "3.13.0", features = ["boxed", "collections"] }
|
||||
indexmap = { version = "2.0.0", features = ["serde"] }
|
||||
insta = { version = "1.30.0", features = ["glob"] }
|
||||
miette = { version = "5.9.0" }
|
||||
|
||||
@@ -16,6 +16,5 @@ repository.workspace = true
|
||||
forget_hir = { workspace = true }
|
||||
forget_estree = { workspace = true}
|
||||
indexmap = { workspace = true }
|
||||
bumpalo = { workspace = true }
|
||||
forget_diagnostics = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use bumpalo::collections::{CollectIn, String};
|
||||
use forget_diagnostics::Diagnostic;
|
||||
use forget_estree::{
|
||||
AssignmentTarget, BinaryExpression, BlockStatement, Expression, ExpressionOrSpread,
|
||||
@@ -23,10 +22,7 @@ use crate::error::BuildHIRError;
|
||||
///
|
||||
/// Failures generally include nonsensical input (`delete 1`) or syntax
|
||||
/// that is not yet supported.
|
||||
pub fn build<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
fun: Function,
|
||||
) -> Result<Box<forget_hir::Function<'a>>, Diagnostic> {
|
||||
pub fn build(env: &Environment, fun: Function) -> Result<Box<forget_hir::Function>, Diagnostic> {
|
||||
let mut builder = Builder::new(env);
|
||||
|
||||
match fun.body {
|
||||
@@ -44,7 +40,7 @@ pub fn build<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
let mut params = env.vec_with_capacity(fun.params.len());
|
||||
let mut params = Vec::with_capacity(fun.params.len());
|
||||
for param in fun.params {
|
||||
match param {
|
||||
Pattern::Identifier(param) => {
|
||||
@@ -83,21 +79,19 @@ pub fn build<'a>(
|
||||
|
||||
let body = builder.build()?;
|
||||
Ok(Box::new(forget_hir::Function {
|
||||
id: fun
|
||||
.id
|
||||
.map(|id| String::from_str_in(&id.name, &env.allocator)),
|
||||
id: fun.id.map(|id| id.name),
|
||||
body,
|
||||
params,
|
||||
// TODO: populate context!
|
||||
context: env.vec_new(),
|
||||
context: Default::default(),
|
||||
is_async: fun.is_async,
|
||||
is_generator: fun.is_generator,
|
||||
}))
|
||||
}
|
||||
|
||||
fn lower_block_statement<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_block_statement(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
stmt: BlockStatement,
|
||||
) -> Result<(), Diagnostic> {
|
||||
for stmt in stmt.body {
|
||||
@@ -108,11 +102,11 @@ fn lower_block_statement<'a>(
|
||||
|
||||
/// Convert a statement to HIR. This will often result in multiple instructions and blocks
|
||||
/// being created as statements often describe control flow.
|
||||
fn lower_statement<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_statement(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
stmt: Statement,
|
||||
label: Option<String<'a>>,
|
||||
label: Option<String>,
|
||||
) -> Result<(), Diagnostic> {
|
||||
match stmt {
|
||||
Statement::BlockStatement(stmt) => {
|
||||
@@ -350,9 +344,9 @@ fn lower_statement<'a>(
|
||||
/// InstructionValue is returned, this function is recursive and may cause multiple instructions
|
||||
/// to be emitted, possibly across multiple basic blocks (in the case of expressions with control
|
||||
/// flow semenatics such as logical, conditional, and optional expressions).
|
||||
fn lower_expression<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_expression(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
expr: Expression,
|
||||
) -> Result<InstrIx, Diagnostic> {
|
||||
let value = match expr {
|
||||
@@ -367,16 +361,16 @@ fn lower_expression<'a>(
|
||||
};
|
||||
InstructionValue::LoadLocal(LoadLocal { place })
|
||||
}
|
||||
Binding::Module(..) | Binding::Global => InstructionValue::LoadGlobal(LoadGlobal {
|
||||
name: String::from_str_in(&expr.name, &env.allocator),
|
||||
}),
|
||||
Binding::Module(..) | Binding::Global => {
|
||||
InstructionValue::LoadGlobal(LoadGlobal { name: expr.name })
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::Literal(expr) => InstructionValue::Primitive(forget_hir::Primitive {
|
||||
value: lower_primitive(env, builder, *expr),
|
||||
}),
|
||||
Expression::ArrayExpression(expr) => {
|
||||
let mut elements = env.vec_with_capacity(expr.elements.len());
|
||||
let mut elements = Vec::with_capacity(expr.elements.len());
|
||||
for expr in expr.elements {
|
||||
let element = match expr {
|
||||
Some(forget_estree::ExpressionOrSpread::SpreadElement(expr)) => {
|
||||
@@ -477,12 +471,12 @@ fn lower_expression<'a>(
|
||||
Ok(builder.push(value))
|
||||
}
|
||||
|
||||
fn lower_arguments<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_arguments(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
args: Vec<ExpressionOrSpread>,
|
||||
) -> Result<bumpalo::collections::Vec<'a, PlaceOrSpread>, Diagnostic> {
|
||||
let mut arguments = env.vec_with_capacity(args.len());
|
||||
) -> Result<Vec<PlaceOrSpread>, Diagnostic> {
|
||||
let mut arguments = Vec::with_capacity(args.len());
|
||||
for arg in args {
|
||||
let element = match arg {
|
||||
forget_estree::ExpressionOrSpread::SpreadElement(arg) => {
|
||||
@@ -501,15 +495,15 @@ fn lower_arguments<'a>(
|
||||
Ok(arguments)
|
||||
}
|
||||
|
||||
fn lower_function<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_function(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
function: forget_estree::Function,
|
||||
) -> Result<forget_hir::FunctionExpression<'a>, Diagnostic> {
|
||||
) -> Result<forget_hir::FunctionExpression, Diagnostic> {
|
||||
println!("get_context_identifiers() ...");
|
||||
let context_identifiers = get_context_identifiers(env, &function);
|
||||
println!("ok");
|
||||
let mut context = env.vec_new();
|
||||
let mut context = Vec::new();
|
||||
let mut seen = HashSet::new();
|
||||
for identifier in context_identifiers {
|
||||
match builder.resolve_identifier(identifier)? {
|
||||
@@ -529,17 +523,17 @@ fn lower_function<'a>(
|
||||
fun.context = context;
|
||||
Ok(forget_hir::FunctionExpression {
|
||||
// TODO: collect dependencies!
|
||||
dependencies: env.vec_new(),
|
||||
dependencies: Default::default(),
|
||||
lowered_function: fun,
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_jsx_element<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_jsx_element(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
expr: forget_estree::JSXElement,
|
||||
) -> Result<JSXElement<'a>, Diagnostic> {
|
||||
let props: Result<Vec<JSXAttribute<'_>>, Diagnostic> = expr
|
||||
) -> Result<JSXElement, Diagnostic> {
|
||||
let props: Result<Vec<JSXAttribute>, Diagnostic> = expr
|
||||
.opening_element
|
||||
.attributes
|
||||
.into_iter()
|
||||
@@ -567,25 +561,25 @@ fn lower_jsx_element<'a>(
|
||||
// })
|
||||
}
|
||||
|
||||
fn lower_jsx_attribute<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_jsx_attribute(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
attr: forget_estree::JSXAttributeOrSpread,
|
||||
) -> Result<JSXAttribute<'a>, Diagnostic> {
|
||||
) -> Result<JSXAttribute, Diagnostic> {
|
||||
todo!("lower jsx attribute")
|
||||
}
|
||||
|
||||
fn lower_jsx_child<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_jsx_child(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
child: forget_estree::JSXChildItem,
|
||||
) -> Result<InstrIx, Diagnostic> {
|
||||
todo!("lower jsx child")
|
||||
}
|
||||
|
||||
fn lower_assignment<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_assignment(
|
||||
env: &Environment,
|
||||
builder: &mut Builder,
|
||||
kind: InstructionKind,
|
||||
lvalue: AssignmentTarget,
|
||||
value: InstrIx,
|
||||
@@ -608,12 +602,12 @@ fn lower_assignment<'a>(
|
||||
})
|
||||
}
|
||||
|
||||
fn lower_identifier_for_assignment<'a>(
|
||||
_env: &'a Environment<'a>,
|
||||
builder: &mut Builder<'a>,
|
||||
fn lower_identifier_for_assignment(
|
||||
_env: &Environment,
|
||||
builder: &mut Builder,
|
||||
_kind: InstructionKind,
|
||||
identifier: forget_estree::Identifier,
|
||||
) -> Result<IdentifierOperand<'a>, Diagnostic> {
|
||||
) -> Result<IdentifierOperand, Diagnostic> {
|
||||
let binding = builder.resolve_identifier(&identifier)?;
|
||||
match binding {
|
||||
Binding::Module(..) | Binding::Global => Err(Diagnostic::invalid_react(
|
||||
@@ -632,16 +626,12 @@ fn lower_identifier_for_assignment<'a>(
|
||||
}
|
||||
|
||||
/// Converts an ESTree literal into a HIR primitive
|
||||
fn lower_primitive<'a>(
|
||||
env: &'a Environment<'a>,
|
||||
_builder: &mut Builder<'a>,
|
||||
literal: Literal,
|
||||
) -> PrimitiveValue<'a> {
|
||||
fn lower_primitive(_env: &Environment, _builder: &mut Builder, literal: Literal) -> PrimitiveValue {
|
||||
match literal.value {
|
||||
JsValue::Bool(bool) => PrimitiveValue::Boolean(bool),
|
||||
JsValue::Null => PrimitiveValue::Null,
|
||||
JsValue::Number(value) => PrimitiveValue::Number(f64::from(value).into()),
|
||||
JsValue::String(s) => PrimitiveValue::String(String::from_str_in(&s, &env.allocator)),
|
||||
JsValue::String(s) => PrimitiveValue::String(s),
|
||||
_ => todo!("Lower literal {literal:#?}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use forget_diagnostics::Diagnostic;
|
||||
use forget_hir::{
|
||||
initialize_hir, BasicBlock, BlockId, BlockKind, Blocks, Environment, GotoKind, Identifier,
|
||||
@@ -20,59 +19,59 @@ use crate::BuildHIRError;
|
||||
/// generally involves driving calls to enter/exit blocks, resolve
|
||||
/// labels and variables, and then calling `build()` when the HIR
|
||||
/// is complete.
|
||||
pub(crate) struct Builder<'a> {
|
||||
pub(crate) struct Builder<'e> {
|
||||
#[allow(dead_code)]
|
||||
environment: &'a Environment<'a>,
|
||||
environment: &'e Environment,
|
||||
|
||||
completed: Blocks<'a>,
|
||||
completed: Blocks,
|
||||
|
||||
instructions: Vec<'a, Instruction<'a>>,
|
||||
instructions: Vec<Instruction>,
|
||||
|
||||
entry: BlockId,
|
||||
|
||||
wip: WipBlock<'a>,
|
||||
wip: WipBlock,
|
||||
|
||||
id_gen: InstructionIdGenerator,
|
||||
|
||||
scopes: Vec<'a, ControlFlowScope<'a>>,
|
||||
scopes: Vec<ControlFlowScope>,
|
||||
}
|
||||
|
||||
pub(crate) struct WipBlock<'a> {
|
||||
pub(crate) struct WipBlock {
|
||||
pub id: BlockId,
|
||||
pub kind: BlockKind,
|
||||
pub instructions: Vec<'a, InstrIx>,
|
||||
pub instructions: Vec<InstrIx>,
|
||||
}
|
||||
|
||||
pub(crate) enum Binding<'a> {
|
||||
Local(Identifier<'a>),
|
||||
Module(Identifier<'a>),
|
||||
pub(crate) enum Binding {
|
||||
Local(Identifier),
|
||||
Module(Identifier),
|
||||
Global,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
enum ControlFlowScope<'a> {
|
||||
Loop(LoopScope<'a>),
|
||||
enum ControlFlowScope {
|
||||
Loop(LoopScope),
|
||||
|
||||
// Switch(SwitchScope<'a>),
|
||||
// Switch(SwitchScope),
|
||||
#[allow(dead_code)]
|
||||
Label(LabelScope<'a>),
|
||||
Label(LabelScope),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) struct LoopScope<'a> {
|
||||
pub label: Option<String<'a>>,
|
||||
pub(crate) struct LoopScope {
|
||||
pub label: Option<String>,
|
||||
pub continue_block: BlockId,
|
||||
pub break_block: BlockId,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub(crate) struct LabelScope<'a> {
|
||||
pub label: String<'a>,
|
||||
pub(crate) struct LabelScope {
|
||||
pub label: String,
|
||||
pub block: BlockId,
|
||||
}
|
||||
|
||||
impl<'a> ControlFlowScope<'a> {
|
||||
fn label(&self) -> Option<&String<'a>> {
|
||||
impl ControlFlowScope {
|
||||
fn label(&self) -> Option<&String> {
|
||||
match self {
|
||||
Self::Loop(scope) => scope.label.as_ref(),
|
||||
Self::Label(scope) => Some(&scope.label),
|
||||
@@ -87,22 +86,22 @@ impl<'a> ControlFlowScope<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Builder<'a> {
|
||||
pub(crate) fn new(environment: &'a Environment<'a>) -> Self {
|
||||
impl<'e> Builder<'e> {
|
||||
pub(crate) fn new(environment: &'e Environment) -> Self {
|
||||
let entry = environment.next_block_id();
|
||||
let current = WipBlock {
|
||||
id: entry,
|
||||
kind: BlockKind::Block,
|
||||
instructions: environment.vec_new(),
|
||||
instructions: Default::default(),
|
||||
};
|
||||
Self {
|
||||
environment,
|
||||
completed: Default::default(),
|
||||
instructions: environment.vec_new(),
|
||||
instructions: Default::default(),
|
||||
entry,
|
||||
wip: current,
|
||||
id_gen: InstructionIdGenerator::new(),
|
||||
scopes: environment.vec_new(),
|
||||
scopes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +110,7 @@ impl<'a> Builder<'a> {
|
||||
///
|
||||
/// TODO: refine the type, only invariants should be possible here,
|
||||
/// not other types of errors
|
||||
pub(crate) fn build(self) -> Result<HIR<'a>, Diagnostic> {
|
||||
pub(crate) fn build(self) -> Result<HIR, Diagnostic> {
|
||||
let mut hir = HIR {
|
||||
entry: self.entry,
|
||||
blocks: self.completed,
|
||||
@@ -123,7 +122,7 @@ impl<'a> Builder<'a> {
|
||||
}
|
||||
|
||||
/// Adds a new instruction to the end of the work in progress block
|
||||
pub(crate) fn push(&mut self, value: InstructionValue<'a>) -> InstrIx {
|
||||
pub(crate) fn push(&mut self, value: InstructionValue) -> InstrIx {
|
||||
let instr = Instruction {
|
||||
id: self.id_gen.next(),
|
||||
value,
|
||||
@@ -136,19 +135,19 @@ impl<'a> Builder<'a> {
|
||||
|
||||
/// Terminates the work in progress block with the given terminal, and starts a new
|
||||
/// work in progress block with the given kind
|
||||
pub(crate) fn terminate(&mut self, terminal: TerminalValue<'a>, next_kind: BlockKind) {
|
||||
pub(crate) fn terminate(&mut self, terminal: TerminalValue, next_kind: BlockKind) {
|
||||
let next_wip = WipBlock {
|
||||
id: self.environment.next_block_id(),
|
||||
kind: next_kind,
|
||||
instructions: self.environment.vec_new(),
|
||||
instructions: Default::default(),
|
||||
};
|
||||
self.terminate_with_fallthrough(terminal, next_wip)
|
||||
}
|
||||
|
||||
pub(crate) fn terminate_with_fallthrough(
|
||||
&mut self,
|
||||
terminal: TerminalValue<'a>,
|
||||
fallthrough: WipBlock<'a>,
|
||||
terminal: TerminalValue,
|
||||
fallthrough: WipBlock,
|
||||
) {
|
||||
let prev_wip = std::mem::replace(&mut self.wip, fallthrough);
|
||||
self.completed.insert(Box::new(BasicBlock {
|
||||
@@ -160,21 +159,21 @@ impl<'a> Builder<'a> {
|
||||
value: terminal,
|
||||
},
|
||||
predecessors: Default::default(),
|
||||
phis: self.environment.vec_new(),
|
||||
phis: Default::default(),
|
||||
}));
|
||||
}
|
||||
|
||||
pub(crate) fn reserve(&mut self, kind: BlockKind) -> WipBlock<'a> {
|
||||
pub(crate) fn reserve(&mut self, kind: BlockKind) -> WipBlock {
|
||||
WipBlock {
|
||||
id: self.environment.next_block_id(),
|
||||
kind,
|
||||
instructions: self.environment.vec_new(),
|
||||
instructions: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn enter<F>(&mut self, kind: BlockKind, f: F) -> Result<BlockId, Diagnostic>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> Result<TerminalValue<'a>, Diagnostic>,
|
||||
F: FnOnce(&mut Self) -> Result<TerminalValue, Diagnostic>,
|
||||
{
|
||||
let wip = self.reserve(kind);
|
||||
let id = wip.id;
|
||||
@@ -182,9 +181,9 @@ impl<'a> Builder<'a> {
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn enter_reserved<F>(&mut self, wip: WipBlock<'a>, f: F) -> Result<(), Diagnostic>
|
||||
fn enter_reserved<F>(&mut self, wip: WipBlock, f: F) -> Result<(), Diagnostic>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> Result<TerminalValue<'a>, Diagnostic>,
|
||||
F: FnOnce(&mut Self) -> Result<TerminalValue, Diagnostic>,
|
||||
{
|
||||
let current = std::mem::replace(&mut self.wip, wip);
|
||||
|
||||
@@ -210,18 +209,18 @@ impl<'a> Builder<'a> {
|
||||
value: terminal,
|
||||
},
|
||||
predecessors: Default::default(),
|
||||
phis: self.environment.vec_new(),
|
||||
phis: Default::default(),
|
||||
}));
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn enter_loop<F>(
|
||||
&mut self,
|
||||
scope: LoopScope<'a>,
|
||||
scope: LoopScope,
|
||||
f: F,
|
||||
) -> Result<TerminalValue<'a>, Diagnostic>
|
||||
) -> Result<TerminalValue, Diagnostic>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> Result<TerminalValue<'a>, Diagnostic>,
|
||||
F: FnOnce(&mut Self) -> Result<TerminalValue, Diagnostic>,
|
||||
{
|
||||
self.scopes.push(ControlFlowScope::Loop(scope.clone()));
|
||||
let terminal = f(self);
|
||||
@@ -235,7 +234,7 @@ impl<'a> Builder<'a> {
|
||||
/// we synthesize a temporary identifier to store the possibly-missing value
|
||||
/// into, and emit a later StoreLocal for the original identifier
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn make_temporary(&self) -> forget_hir::Identifier<'a> {
|
||||
pub(crate) fn make_temporary(&self) -> forget_hir::Identifier {
|
||||
forget_hir::Identifier {
|
||||
id: self.environment.next_identifier_id(),
|
||||
name: None,
|
||||
@@ -318,7 +317,7 @@ impl<'a> Builder<'a> {
|
||||
pub(crate) fn resolve_identifier(
|
||||
&mut self,
|
||||
identifier: &forget_estree::Identifier,
|
||||
) -> Result<Binding<'a>, Diagnostic> {
|
||||
) -> Result<Binding, Diagnostic> {
|
||||
match &identifier.binding {
|
||||
Some(binding) => Ok(match binding {
|
||||
forget_estree::Binding::Global => Binding::Global,
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::collections::HashSet;
|
||||
use forget_estree::{Binding, BindingId, Function, Identifier, Visitor};
|
||||
use forget_hir::Environment;
|
||||
|
||||
pub(crate) fn get_context_identifiers<'a, 'ast>(
|
||||
_env: &'a Environment<'a>,
|
||||
pub(crate) fn get_context_identifiers<'ast>(
|
||||
_env: &Environment,
|
||||
function: &'ast Function,
|
||||
) -> Vec<&'ast Identifier> {
|
||||
let mut visitor = ContextVisitor::new();
|
||||
|
||||
@@ -13,7 +13,6 @@ repository.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bumpalo = { workspace = true }
|
||||
insta = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
-15
@@ -395,10 +395,7 @@ Output:
|
||||
"type": "JSXMemberExpression",
|
||||
"object": {
|
||||
"type": "JSXIdentifier",
|
||||
"type": "Identifier",
|
||||
"name": "Foo",
|
||||
"loc": null,
|
||||
"range": null,
|
||||
"loc": {
|
||||
"source": null,
|
||||
"start": {
|
||||
@@ -417,10 +414,7 @@ Output:
|
||||
},
|
||||
"property": {
|
||||
"type": "JSXIdentifier",
|
||||
"type": "Identifier",
|
||||
"name": "Bar",
|
||||
"loc": null,
|
||||
"range": null,
|
||||
"loc": {
|
||||
"source": null,
|
||||
"start": {
|
||||
@@ -458,10 +452,7 @@ Output:
|
||||
"type": "JSXAttribute",
|
||||
"name": {
|
||||
"type": "JSXIdentifier",
|
||||
"type": "Identifier",
|
||||
"name": "a",
|
||||
"loc": null,
|
||||
"range": null,
|
||||
"loc": {
|
||||
"source": null,
|
||||
"start": {
|
||||
@@ -561,10 +552,7 @@ Output:
|
||||
"type": "JSXMemberExpression",
|
||||
"object": {
|
||||
"type": "JSXIdentifier",
|
||||
"type": "Identifier",
|
||||
"name": "Foo",
|
||||
"loc": null,
|
||||
"range": null,
|
||||
"loc": {
|
||||
"source": null,
|
||||
"start": {
|
||||
@@ -583,10 +571,7 @@ Output:
|
||||
},
|
||||
"property": {
|
||||
"type": "JSXIdentifier",
|
||||
"type": "Identifier",
|
||||
"name": "Bar",
|
||||
"loc": null,
|
||||
"range": null,
|
||||
"loc": {
|
||||
"source": null,
|
||||
"start": {
|
||||
|
||||
@@ -20,5 +20,4 @@ forget_hir = { workspace = true }
|
||||
forget_optimization = { workspace = true }
|
||||
forget_ssa = { workspace = true }
|
||||
forget_build_hir = { workspace = true }
|
||||
bumpalo = { workspace = true }
|
||||
miette = { workspace = true, features = ["backtrace", "fancy"] }
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
function Component(props) {
|
||||
return (
|
||||
<Foo bool={true} str="string" number={3.14} var={props.variable}>
|
||||
Hello
|
||||
{props.foo}
|
||||
<div>{props.bar}</div>
|
||||
</Foo>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::env;
|
||||
use std::fmt::Write;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use forget_build_hir::build;
|
||||
use forget_estree::{ModuleItem, Statement};
|
||||
use forget_estree_swc::parse;
|
||||
@@ -24,14 +23,12 @@ fn fixtures() {
|
||||
for (ix, item) in ast.body.into_iter().enumerate() {
|
||||
if let ModuleItem::Statement(stmt) = item {
|
||||
if let Statement::FunctionDeclaration(fun) = stmt {
|
||||
let allocator = Bump::new();
|
||||
let environment = allocator.alloc(Environment::new(
|
||||
&allocator,
|
||||
let environment = Environment::new(
|
||||
Features {
|
||||
validate_frozen_lambdas: true,
|
||||
},
|
||||
Registry,
|
||||
));
|
||||
);
|
||||
if ix != 0 {
|
||||
output.push_str("\n\n");
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ repository.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bumpalo = { workspace = true }
|
||||
forget_estree = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{Identifier, InstrIx, Terminal};
|
||||
///
|
||||
/// [1] Assuming no exceptions are thrown.
|
||||
#[derive(Debug)]
|
||||
pub struct BasicBlock<'a> {
|
||||
pub struct BasicBlock {
|
||||
/// The identifier for the block
|
||||
pub id: BlockId,
|
||||
|
||||
@@ -23,21 +23,21 @@ pub struct BasicBlock<'a> {
|
||||
pub kind: BlockKind,
|
||||
|
||||
/// The ordered instructions in this block
|
||||
pub instructions: bumpalo::collections::Vec<'a, InstrIx>,
|
||||
pub instructions: Vec<InstrIx>,
|
||||
|
||||
/// The terminal instruction for the block
|
||||
pub terminal: Terminal<'a>,
|
||||
pub terminal: Terminal,
|
||||
|
||||
/// The immediate predecessors of this block
|
||||
pub predecessors: IndexSet<BlockId>,
|
||||
|
||||
pub phis: bumpalo::collections::Vec<'a, Phi<'a>>,
|
||||
pub phis: Vec<Phi>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Phi<'a> {
|
||||
pub identifier: Identifier<'a>,
|
||||
pub operands: IndexMap<BlockId, Identifier<'a>>,
|
||||
pub struct Phi {
|
||||
pub identifier: Identifier,
|
||||
pub operands: IndexMap<BlockId, Identifier>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
|
||||
@@ -2,9 +2,6 @@ use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use bumpalo::boxed::Box;
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use forget_estree::BindingId;
|
||||
|
||||
use crate::{
|
||||
@@ -15,7 +12,7 @@ use crate::{
|
||||
/// compiled. Environments may not be reused between React functions, but *are*
|
||||
/// shared between each React function and all its nested function expressions.
|
||||
#[derive(Debug)]
|
||||
pub struct Environment<'a> {
|
||||
pub struct Environment {
|
||||
/// The set of enabled compiler features
|
||||
pub features: Features,
|
||||
|
||||
@@ -24,10 +21,6 @@ pub struct Environment<'a> {
|
||||
#[allow(dead_code)]
|
||||
registry: Registry,
|
||||
|
||||
/// Arena allocator so that data for compilation can be efficiently allocated
|
||||
/// and the memory reclaimed when compilation completes.
|
||||
pub allocator: &'a Bump,
|
||||
|
||||
/// The next available block index
|
||||
next_block_id: Cell<BlockId>,
|
||||
|
||||
@@ -36,13 +29,12 @@ pub struct Environment<'a> {
|
||||
|
||||
next_type_var_id: Cell<TypeVarId>,
|
||||
|
||||
bindings: Rc<RefCell<HashMap<(bumpalo::collections::String<'a>, BindingId), Identifier<'a>>>>,
|
||||
bindings: Rc<RefCell<HashMap<(String, BindingId), Identifier>>>,
|
||||
}
|
||||
|
||||
impl<'a> Environment<'a> {
|
||||
pub fn new(allocator: &'a Bump, features: Features, registry: Registry) -> Self {
|
||||
impl Environment {
|
||||
pub fn new(features: Features, registry: Registry) -> Self {
|
||||
Self {
|
||||
allocator,
|
||||
features,
|
||||
registry,
|
||||
next_block_id: Cell::new(BlockId(0)),
|
||||
@@ -52,23 +44,6 @@ impl<'a> Environment<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocate a value into the environment's memory arena
|
||||
pub fn alloc<T>(&self, value: T) -> &'a mut T {
|
||||
self.allocator.alloc(value)
|
||||
}
|
||||
|
||||
pub fn box_new<T>(&self, value: T) -> Box<'a, T> {
|
||||
Box::new_in(value, &self.allocator)
|
||||
}
|
||||
|
||||
pub fn vec_new<T>(&self) -> Vec<'a, T> {
|
||||
Vec::new_in(&self.allocator)
|
||||
}
|
||||
|
||||
pub fn vec_with_capacity<T>(&self, capacity: usize) -> Vec<'a, T> {
|
||||
Vec::with_capacity_in(capacity, &self.allocator)
|
||||
}
|
||||
|
||||
/// Get the next available block id
|
||||
pub fn next_block_id(&self) -> BlockId {
|
||||
let id = self.next_block_id.get();
|
||||
@@ -90,8 +65,8 @@ impl<'a> Environment<'a> {
|
||||
id
|
||||
}
|
||||
|
||||
pub fn resolve_binding_identifier(&self, name: &str, binding_id: BindingId) -> Identifier<'a> {
|
||||
let key_name = bumpalo::collections::String::from_str_in(name, &self.allocator);
|
||||
pub fn resolve_binding_identifier(&self, name: &str, binding_id: BindingId) -> Identifier {
|
||||
let key_name = name.to_string();
|
||||
let mut bindings = self.bindings.borrow_mut();
|
||||
if let Some(identifier) = bindings.get(&(key_name.clone(), binding_id)) {
|
||||
identifier.clone()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use forget_diagnostics::Diagnostic;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
@@ -6,11 +5,11 @@ use crate::{BasicBlock, BlockId, FunctionExpression, IdentifierOperand, InstrIx,
|
||||
|
||||
/// Represents either a React function or a function expression
|
||||
#[derive(Debug)]
|
||||
pub struct Function<'a> {
|
||||
pub id: Option<String<'a>>,
|
||||
pub body: HIR<'a>,
|
||||
pub params: Vec<'a, IdentifierOperand<'a>>,
|
||||
pub context: Vec<'a, IdentifierOperand<'a>>,
|
||||
pub struct Function {
|
||||
pub id: Option<String>,
|
||||
pub body: HIR,
|
||||
pub params: Vec<IdentifierOperand>,
|
||||
pub context: Vec<IdentifierOperand>,
|
||||
pub is_async: bool,
|
||||
pub is_generator: bool,
|
||||
}
|
||||
@@ -20,20 +19,20 @@ pub struct Function<'a> {
|
||||
/// so that compiler passes can complete forward data flow analysis in a
|
||||
/// single pass over the CFG in the case where there are no loops.
|
||||
#[derive(Debug)]
|
||||
pub struct HIR<'a> {
|
||||
pub struct HIR {
|
||||
/// The id of the first block
|
||||
pub entry: BlockId,
|
||||
|
||||
/// Blocks are stored in a map for easy retrieval by their id,
|
||||
/// but the blocks are in reverse postorder
|
||||
pub blocks: Blocks<'a>,
|
||||
pub blocks: Blocks,
|
||||
|
||||
/// All instructions for the block. This may contain unused items,
|
||||
pub instructions: Vec<'a, Instruction<'a>>,
|
||||
pub instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
impl<'a> HIR<'a> {
|
||||
pub fn inline(&mut self, other: FunctionExpression<'a>) -> () {
|
||||
impl HIR {
|
||||
pub fn inline(&mut self, other: FunctionExpression) -> () {
|
||||
let offset = self.instructions.len();
|
||||
for mut instr in other.lowered_function.body.instructions.into_iter() {
|
||||
instr.each_operand(|operand| {
|
||||
@@ -54,11 +53,11 @@ impl<'a> HIR<'a> {
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Blocks<'a> {
|
||||
data: IndexMap<BlockId, Option<Box<BasicBlock<'a>>>>,
|
||||
pub struct Blocks {
|
||||
data: IndexMap<BlockId, Option<Box<BasicBlock>>>,
|
||||
}
|
||||
|
||||
impl<'a> Blocks<'a> {
|
||||
impl Blocks {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Default::default(),
|
||||
@@ -75,7 +74,7 @@ impl<'a> Blocks<'a> {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, block: Box<BasicBlock<'a>>) -> Option<Option<Box<BasicBlock<'a>>>> {
|
||||
pub fn insert(&mut self, block: Box<BasicBlock>) -> Option<Option<Box<BasicBlock>>> {
|
||||
self.data.insert(block.id, Some(block))
|
||||
}
|
||||
|
||||
@@ -83,7 +82,7 @@ impl<'a> Blocks<'a> {
|
||||
self.data.keys().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: BlockId) -> Box<BasicBlock<'a>> {
|
||||
pub fn remove(&mut self, id: BlockId) -> Box<BasicBlock> {
|
||||
self.data.remove(&id).unwrap().unwrap()
|
||||
}
|
||||
|
||||
@@ -91,38 +90,38 @@ impl<'a> Blocks<'a> {
|
||||
self.data.extend(other.data);
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> BlocksIntoIter<'a> {
|
||||
pub fn into_iter(self) -> BlocksIntoIter {
|
||||
BlocksIntoIter::new(self.data.into_iter())
|
||||
}
|
||||
|
||||
pub fn block(&self, id: BlockId) -> &BasicBlock<'a> {
|
||||
pub fn block(&self, id: BlockId) -> &BasicBlock {
|
||||
self.data.get(&id).unwrap().as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub fn block_mut(&mut self, id: BlockId) -> &mut BasicBlock<'a> {
|
||||
pub fn block_mut(&mut self, id: BlockId) -> &mut BasicBlock {
|
||||
self.data.get_mut(&id).unwrap().as_mut().unwrap()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> BlocksIter<'_, 'a> {
|
||||
pub fn iter(&self) -> BlocksIter<'_> {
|
||||
BlocksIter::new(self.data.iter())
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> BlocksIterMut<'_, 'a> {
|
||||
pub fn iter_mut(&mut self) -> BlocksIterMut<'_> {
|
||||
BlocksIterMut::new(self.data.iter_mut())
|
||||
}
|
||||
}
|
||||
pub struct BlocksIntoIter<'a> {
|
||||
iter: indexmap::map::IntoIter<BlockId, Option<Box<BasicBlock<'a>>>>,
|
||||
pub struct BlocksIntoIter {
|
||||
iter: indexmap::map::IntoIter<BlockId, Option<Box<BasicBlock>>>,
|
||||
}
|
||||
|
||||
impl<'a> BlocksIntoIter<'a> {
|
||||
fn new(iter: indexmap::map::IntoIter<BlockId, Option<Box<BasicBlock<'a>>>>) -> Self {
|
||||
impl BlocksIntoIter {
|
||||
fn new(iter: indexmap::map::IntoIter<BlockId, Option<Box<BasicBlock>>>) -> Self {
|
||||
Self { iter }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for BlocksIntoIter<'a> {
|
||||
type Item = Box<BasicBlock<'a>>;
|
||||
impl Iterator for BlocksIntoIter {
|
||||
type Item = Box<BasicBlock>;
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
@@ -139,18 +138,18 @@ impl<'a> Iterator for BlocksIntoIter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlocksIter<'b, 'a> {
|
||||
iter: indexmap::map::Iter<'b, BlockId, Option<Box<BasicBlock<'a>>>>,
|
||||
pub struct BlocksIter<'b> {
|
||||
iter: indexmap::map::Iter<'b, BlockId, Option<Box<BasicBlock>>>,
|
||||
}
|
||||
|
||||
impl<'b, 'a> BlocksIter<'b, 'a> {
|
||||
fn new(iter: indexmap::map::Iter<'b, BlockId, Option<Box<BasicBlock<'a>>>>) -> Self {
|
||||
impl<'b> BlocksIter<'b> {
|
||||
fn new(iter: indexmap::map::Iter<'b, BlockId, Option<Box<BasicBlock>>>) -> Self {
|
||||
Self { iter }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'a> Iterator for BlocksIter<'b, 'a> {
|
||||
type Item = &'b BasicBlock<'a>;
|
||||
impl<'b> Iterator for BlocksIter<'b> {
|
||||
type Item = &'b BasicBlock;
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
@@ -164,18 +163,18 @@ impl<'b, 'a> Iterator for BlocksIter<'b, 'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlocksIterMut<'b, 'a> {
|
||||
iter: indexmap::map::IterMut<'b, BlockId, Option<Box<BasicBlock<'a>>>>,
|
||||
pub struct BlocksIterMut<'b> {
|
||||
iter: indexmap::map::IterMut<'b, BlockId, Option<Box<BasicBlock>>>,
|
||||
}
|
||||
|
||||
impl<'b, 'a> BlocksIterMut<'b, 'a> {
|
||||
fn new(iter: indexmap::map::IterMut<'b, BlockId, Option<Box<BasicBlock<'a>>>>) -> Self {
|
||||
impl<'b> BlocksIterMut<'b> {
|
||||
fn new(iter: indexmap::map::IterMut<'b, BlockId, Option<Box<BasicBlock>>>) -> Self {
|
||||
Self { iter }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, 'a> Iterator for BlocksIterMut<'b, 'a> {
|
||||
type Item = &'b mut BasicBlock<'a>;
|
||||
impl<'b> Iterator for BlocksIterMut<'b> {
|
||||
type Item = &'b mut BasicBlock;
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
@@ -189,14 +188,14 @@ impl<'b, 'a> Iterator for BlocksIterMut<'b, 'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockRewriter<'blocks, 'a> {
|
||||
blocks: &'blocks mut Blocks<'a>,
|
||||
pub struct BlockRewriter<'blocks> {
|
||||
blocks: &'blocks mut Blocks,
|
||||
current: BlockId,
|
||||
new_blocks: std::vec::Vec<Box<BasicBlock<'a>>>,
|
||||
new_blocks: std::vec::Vec<Box<BasicBlock>>,
|
||||
}
|
||||
|
||||
impl<'blocks, 'a> BlockRewriter<'blocks, 'a> {
|
||||
pub fn new(blocks: &'blocks mut Blocks<'a>, entry: BlockId) -> Self {
|
||||
impl<'blocks> BlockRewriter<'blocks> {
|
||||
pub fn new(blocks: &'blocks mut Blocks, entry: BlockId) -> Self {
|
||||
Self {
|
||||
blocks,
|
||||
current: entry,
|
||||
@@ -206,7 +205,7 @@ impl<'blocks, 'a> BlockRewriter<'blocks, 'a> {
|
||||
|
||||
pub fn each_block<F>(&mut self, mut f: F) -> ()
|
||||
where
|
||||
F: FnMut(Box<BasicBlock<'a>>, &mut Self) -> BlockRewriterAction<'a>,
|
||||
F: FnMut(Box<BasicBlock>, &mut Self) -> BlockRewriterAction,
|
||||
{
|
||||
let mut keys = self.blocks.block_ids();
|
||||
loop {
|
||||
@@ -236,7 +235,7 @@ impl<'blocks, 'a> BlockRewriter<'blocks, 'a> {
|
||||
|
||||
pub fn try_each_block<F>(&mut self, mut f: F) -> Result<(), Diagnostic>
|
||||
where
|
||||
F: FnMut(Box<BasicBlock<'a>>, &mut Self) -> Result<BlockRewriterAction<'a>, Diagnostic>,
|
||||
F: FnMut(Box<BasicBlock>, &mut Self) -> Result<BlockRewriterAction, Diagnostic>,
|
||||
{
|
||||
let mut keys = self.blocks.block_ids();
|
||||
loop {
|
||||
@@ -270,22 +269,22 @@ impl<'blocks, 'a> BlockRewriter<'blocks, 'a> {
|
||||
self.blocks.data.contains_key(&block_id)
|
||||
}
|
||||
|
||||
pub fn block(&self, block_id: BlockId) -> &BasicBlock<'a> {
|
||||
pub fn block(&self, block_id: BlockId) -> &BasicBlock {
|
||||
assert_ne!(block_id, self.current);
|
||||
self.blocks.block(block_id)
|
||||
}
|
||||
|
||||
pub fn block_mut(&mut self, block_id: BlockId) -> &mut BasicBlock<'a> {
|
||||
pub fn block_mut(&mut self, block_id: BlockId) -> &mut BasicBlock {
|
||||
assert_ne!(block_id, self.current);
|
||||
self.blocks.block_mut(block_id)
|
||||
}
|
||||
|
||||
pub fn add_block(&mut self, block: Box<BasicBlock<'a>>) {
|
||||
pub fn add_block(&mut self, block: Box<BasicBlock>) {
|
||||
self.new_blocks.push(block);
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BlockRewriterAction<'a> {
|
||||
Keep(Box<BasicBlock<'a>>),
|
||||
pub enum BlockRewriterAction {
|
||||
Keep(Box<BasicBlock>),
|
||||
Remove,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
/// Runs a variety of passes to put the HIR in canonical form. This should be called
|
||||
/// after initial HIR construction and after any transformations that change the
|
||||
/// shape of the control-flow graph.
|
||||
pub fn initialize_hir<'a>(hir: &mut HIR<'a>) -> Result<(), Diagnostic> {
|
||||
pub fn initialize_hir(hir: &mut HIR) -> Result<(), Diagnostic> {
|
||||
prune_tombstones(hir);
|
||||
reverse_postorder_blocks(hir);
|
||||
remove_unreachable_for_updates(hir);
|
||||
@@ -22,7 +22,7 @@ pub fn initialize_hir<'a>(hir: &mut HIR<'a>) -> Result<(), Diagnostic> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn prune_tombstones<'a>(hir: &mut HIR<'a>) {
|
||||
pub fn prune_tombstones(hir: &mut HIR) {
|
||||
for block in hir.blocks.iter_mut() {
|
||||
block.instructions.retain(|ix| {
|
||||
let instr = &hir.instructions[usize::from(*ix)];
|
||||
@@ -34,12 +34,12 @@ pub fn prune_tombstones<'a>(hir: &mut HIR<'a>) {
|
||||
|
||||
/// Modifies the HIR to put the blocks in reverse postorder, with predecessors before
|
||||
/// successors (except for the case of loops)
|
||||
pub fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) {
|
||||
pub fn reverse_postorder_blocks(hir: &mut HIR) {
|
||||
let mut visited = HashSet::<BlockId>::with_capacity(hir.blocks.len());
|
||||
let mut postorder = std::vec::Vec::<BlockId>::with_capacity(hir.blocks.len());
|
||||
fn visit<'a>(
|
||||
fn visit(
|
||||
block_id: BlockId,
|
||||
hir: &HIR<'a>,
|
||||
hir: &HIR,
|
||||
visited: &mut HashSet<BlockId>,
|
||||
postorder: &mut std::vec::Vec<BlockId>,
|
||||
) {
|
||||
@@ -89,7 +89,7 @@ pub fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) {
|
||||
}
|
||||
|
||||
/// Prunes ForTerminal.update values (sets to None) if they are unreachable
|
||||
pub fn remove_unreachable_for_updates<'a>(hir: &mut HIR<'a>) {
|
||||
pub fn remove_unreachable_for_updates(hir: &mut HIR) {
|
||||
BlockRewriter::new(&mut hir.blocks, hir.entry).each_block(|mut block, rewriter| {
|
||||
if let TerminalValue::For(terminal) = &mut block.terminal.value {
|
||||
if let Some(update) = terminal.update {
|
||||
@@ -104,7 +104,7 @@ pub fn remove_unreachable_for_updates<'a>(hir: &mut HIR<'a>) {
|
||||
|
||||
/// Prunes unreachable fallthrough values, setting them to None if the referenced
|
||||
/// block was not otherwise reachable.
|
||||
pub fn remove_unreachable_fallthroughs<'a>(hir: &mut HIR<'a>) {
|
||||
pub fn remove_unreachable_fallthroughs(hir: &mut HIR) {
|
||||
BlockRewriter::new(&mut hir.blocks, hir.entry).each_block(|mut block, rewriter| {
|
||||
block
|
||||
.terminal
|
||||
@@ -121,7 +121,7 @@ pub fn remove_unreachable_fallthroughs<'a>(hir: &mut HIR<'a>) {
|
||||
}
|
||||
|
||||
/// Rewrites DoWhile statements into Gotos if the test block is not reachable
|
||||
pub fn remove_unreachable_do_while_statements<'a>(hir: &mut HIR<'a>) {
|
||||
pub fn remove_unreachable_do_while_statements(hir: &mut HIR) {
|
||||
BlockRewriter::new(&mut hir.blocks, hir.entry).each_block(|mut block, rewriter| {
|
||||
if let TerminalValue::DoWhile(terminal) = &mut block.terminal.value {
|
||||
if !rewriter.contains(terminal.test) {
|
||||
@@ -137,7 +137,7 @@ pub fn remove_unreachable_do_while_statements<'a>(hir: &mut HIR<'a>) {
|
||||
|
||||
/// Updates the instruction ids for all instructions and blocks
|
||||
/// Relies on the blocks being in reverse postorder to ensure that id ordering is correct
|
||||
pub fn mark_instruction_ids<'a>(hir: &mut HIR<'a>) -> Result<(), Diagnostic> {
|
||||
pub fn mark_instruction_ids(hir: &mut HIR) -> Result<(), Diagnostic> {
|
||||
let mut id_gen = InstructionIdGenerator::new();
|
||||
let mut visited = HashSet::<(usize, usize)>::new();
|
||||
for (ii, block) in hir.blocks.iter_mut().enumerate() {
|
||||
@@ -161,15 +161,15 @@ pub struct BlockVisitedTwice {
|
||||
}
|
||||
|
||||
/// Updates the predecessors of each block
|
||||
pub fn mark_predecessors<'a>(hir: &mut HIR<'a>) {
|
||||
pub fn mark_predecessors(hir: &mut HIR) {
|
||||
for block in hir.blocks.iter_mut() {
|
||||
block.predecessors.clear();
|
||||
}
|
||||
let mut visited = HashSet::<BlockId>::with_capacity(hir.blocks.len());
|
||||
fn visit<'a>(
|
||||
fn visit(
|
||||
block_id: BlockId,
|
||||
prev_id: Option<BlockId>,
|
||||
hir: &mut HIR<'a>,
|
||||
hir: &mut HIR,
|
||||
visited: &mut HashSet<BlockId>,
|
||||
) {
|
||||
let block = hir.blocks.block_mut(block_id);
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
||||
use bumpalo::collections::String;
|
||||
use forget_diagnostics::Diagnostic;
|
||||
|
||||
use crate::{
|
||||
@@ -63,10 +62,7 @@ use crate::{
|
||||
/// [13] StoreLocal 'x', #6
|
||||
/// ```
|
||||
///
|
||||
pub fn inline_use_memo<'a>(
|
||||
env: &Environment<'a>,
|
||||
fun: &mut Function<'a>,
|
||||
) -> Result<(), Diagnostic> {
|
||||
pub fn inline_use_memo(env: &Environment, fun: &mut Function) -> Result<(), Diagnostic> {
|
||||
let mut use_memo_globals: HashSet<InstrIx> = Default::default();
|
||||
let mut functions: HashSet<InstrIx> = Default::default();
|
||||
|
||||
@@ -108,7 +104,7 @@ pub fn inline_use_memo<'a>(
|
||||
let temporary = Identifier {
|
||||
id: temporary_id,
|
||||
// NOTE: for memoization to work correctly this variable has to be named
|
||||
name: Some(String::from_str_in("t", &env.allocator)),
|
||||
name: Some("t".to_string()),
|
||||
data: Rc::new(RefCell::new(IdentifierData {
|
||||
mutable_range: MutableRange::new(),
|
||||
scope: None,
|
||||
@@ -232,7 +228,7 @@ pub fn inline_use_memo<'a>(
|
||||
id: continuation_block_id,
|
||||
instructions: continuation_instructions,
|
||||
kind: block.kind,
|
||||
phis: env.vec_new(),
|
||||
phis: Default::default(),
|
||||
predecessors: Default::default(),
|
||||
terminal,
|
||||
});
|
||||
|
||||
@@ -2,21 +2,20 @@ use std::cell::RefCell;
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use forget_estree::BinaryOperator;
|
||||
|
||||
use crate::{Function, IdentifierId, InstrIx, InstructionId, ScopeId, Type};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Instruction<'a> {
|
||||
pub struct Instruction {
|
||||
pub id: InstructionId,
|
||||
pub value: InstructionValue<'a>,
|
||||
pub value: InstructionValue,
|
||||
}
|
||||
|
||||
impl<'a> Instruction<'a> {
|
||||
impl Instruction {
|
||||
pub fn each_identifier_store<F>(&mut self, mut f: F) -> ()
|
||||
where
|
||||
F: FnMut(&mut LValue<'a>) -> (),
|
||||
F: FnMut(&mut LValue) -> (),
|
||||
{
|
||||
match &mut self.value {
|
||||
InstructionValue::DeclareContext(instr) => {
|
||||
@@ -43,7 +42,7 @@ impl<'a> Instruction<'a> {
|
||||
|
||||
pub fn try_each_identifier_store<F, E>(&mut self, mut f: F) -> Result<(), E>
|
||||
where
|
||||
F: FnMut(&mut LValue<'a>) -> Result<(), E>,
|
||||
F: FnMut(&mut LValue) -> Result<(), E>,
|
||||
{
|
||||
match &mut self.value {
|
||||
InstructionValue::DeclareContext(instr) => {
|
||||
@@ -71,7 +70,7 @@ impl<'a> Instruction<'a> {
|
||||
|
||||
pub fn each_identifier_load<F>(&mut self, mut f: F) -> ()
|
||||
where
|
||||
F: FnMut(&mut IdentifierOperand<'a>) -> (),
|
||||
F: FnMut(&mut IdentifierOperand) -> (),
|
||||
{
|
||||
match &mut self.value {
|
||||
InstructionValue::LoadLocal(instr) => f(&mut instr.place),
|
||||
@@ -151,46 +150,46 @@ impl<'a> Instruction<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InstructionValue<'a> {
|
||||
Array(Array<'a>),
|
||||
// Await(Await<'a>),
|
||||
pub enum InstructionValue {
|
||||
Array(Array),
|
||||
// Await(Await),
|
||||
Binary(Binary),
|
||||
Call(Call<'a>),
|
||||
// ComputedDelete(ComputedDelete<'a>),
|
||||
// ComputedLoad(ComputedLoad<'a>),
|
||||
// ComputedStore(ComputedStore<'a>),
|
||||
// Debugger(Debugger<'a>),
|
||||
DeclareContext(DeclareContext<'a>),
|
||||
DeclareLocal(DeclareLocal<'a>),
|
||||
// Destructure(Destructure<'a>),
|
||||
Function(FunctionExpression<'a>),
|
||||
JSXElement(JSXElement<'a>),
|
||||
// JsxFragment(JsxFragment<'a>),
|
||||
// JsxText(JsxText<'a>),
|
||||
Call(Call),
|
||||
// ComputedDelete(ComputedDelete),
|
||||
// ComputedLoad(ComputedLoad),
|
||||
// ComputedStore(ComputedStore),
|
||||
// Debugger(Debugger),
|
||||
DeclareContext(DeclareContext),
|
||||
DeclareLocal(DeclareLocal),
|
||||
// Destructure(Destructure),
|
||||
Function(FunctionExpression),
|
||||
JSXElement(JSXElement),
|
||||
// JsxFragment(JsxFragment),
|
||||
// JsxText(JsxText),
|
||||
LoadContext(LoadContext),
|
||||
LoadGlobal(LoadGlobal<'a>),
|
||||
LoadLocal(LoadLocal<'a>),
|
||||
// MethodCall(MethodCall<'a>),
|
||||
// New(New<'a>),
|
||||
// NextIterable(NextIterable<'a>),
|
||||
// Object(Object<'a>),
|
||||
Primitive(Primitive<'a>),
|
||||
// PropertyDelete(PropertyDelete<'a>),
|
||||
// PropertyLoad(PropertyLoad<'a>),
|
||||
// PropertyStore(PropertyStore<'a>),
|
||||
// RegExp(RegExp<'a>),
|
||||
// StoreContext(StoreContext<'a>),
|
||||
StoreLocal(StoreLocal<'a>),
|
||||
// TaggedTemplate(TaggedTemplate<'a>),
|
||||
// Template(Template<'a>),
|
||||
// TypeCast(TypeCast<'a>),
|
||||
// Unary(Unary<'a>),
|
||||
LoadGlobal(LoadGlobal),
|
||||
LoadLocal(LoadLocal),
|
||||
// MethodCall(MethodCall),
|
||||
// New(New),
|
||||
// NextIterable(NextIterable),
|
||||
// Object(Object),
|
||||
Primitive(Primitive),
|
||||
// PropertyDelete(PropertyDelete),
|
||||
// PropertyLoad(PropertyLoad),
|
||||
// PropertyStore(PropertyStore),
|
||||
// RegExp(RegExp),
|
||||
// StoreContext(StoreContext),
|
||||
StoreLocal(StoreLocal),
|
||||
// TaggedTemplate(TaggedTemplate),
|
||||
// Template(Template),
|
||||
// TypeCast(TypeCast),
|
||||
// Unary(Unary),
|
||||
Tombstone,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Array<'a> {
|
||||
pub elements: Vec<'a, Option<PlaceOrSpread>>,
|
||||
pub struct Array {
|
||||
pub elements: Vec<Option<PlaceOrSpread>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -207,32 +206,32 @@ pub struct Binary {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Call<'a> {
|
||||
pub struct Call {
|
||||
pub callee: Operand,
|
||||
pub arguments: Vec<'a, PlaceOrSpread>,
|
||||
pub arguments: Vec<PlaceOrSpread>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionExpression<'a> {
|
||||
pub dependencies: Vec<'a, Operand>,
|
||||
pub lowered_function: Box<Function<'a>>,
|
||||
pub struct FunctionExpression {
|
||||
pub dependencies: Vec<Operand>,
|
||||
pub lowered_function: Box<Function>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Primitive<'a> {
|
||||
pub value: PrimitiveValue<'a>,
|
||||
pub struct Primitive {
|
||||
pub value: PrimitiveValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PrimitiveValue<'a> {
|
||||
pub enum PrimitiveValue {
|
||||
Boolean(bool),
|
||||
Null,
|
||||
Number(Number),
|
||||
String(String<'a>),
|
||||
String(String),
|
||||
Undefined,
|
||||
}
|
||||
|
||||
impl<'a> PrimitiveValue<'a> {
|
||||
impl PrimitiveValue {
|
||||
pub fn is_truthy(&self) -> bool {
|
||||
match &self {
|
||||
PrimitiveValue::Boolean(value) => *value,
|
||||
@@ -370,8 +369,8 @@ impl std::ops::Div for Number {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoadLocal<'a> {
|
||||
pub place: IdentifierOperand<'a>,
|
||||
pub struct LoadLocal {
|
||||
pub place: IdentifierOperand,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -380,37 +379,37 @@ pub struct LoadContext {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct LoadGlobal<'a> {
|
||||
pub name: String<'a>,
|
||||
pub struct LoadGlobal {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeclareLocal<'a> {
|
||||
pub lvalue: LValue<'a>,
|
||||
pub struct DeclareLocal {
|
||||
pub lvalue: LValue,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DeclareContext<'a> {
|
||||
pub lvalue: LValue<'a>, // note: kind must be InstructionKind::Let
|
||||
pub struct DeclareContext {
|
||||
pub lvalue: LValue, // note: kind must be InstructionKind::Let
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StoreLocal<'a> {
|
||||
pub lvalue: LValue<'a>,
|
||||
pub struct StoreLocal {
|
||||
pub lvalue: LValue,
|
||||
pub value: Operand,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JSXElement<'a> {
|
||||
pub struct JSXElement {
|
||||
pub tag: Operand,
|
||||
pub props: Vec<'a, JSXAttribute<'a>>,
|
||||
pub children: Option<Vec<'a, Operand>>,
|
||||
pub props: Vec<JSXAttribute>,
|
||||
pub children: Option<Vec<Operand>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum JSXAttribute<'a> {
|
||||
pub enum JSXAttribute {
|
||||
Spread { argument: Operand },
|
||||
Attribute { name: String<'a>, value: Operand },
|
||||
Attribute { name: String, value: Operand },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -420,14 +419,14 @@ pub struct Operand {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IdentifierOperand<'a> {
|
||||
pub identifier: Identifier<'a>,
|
||||
pub struct IdentifierOperand {
|
||||
pub identifier: Identifier,
|
||||
pub effect: Option<Effect>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LValue<'a> {
|
||||
pub identifier: IdentifierOperand<'a>,
|
||||
pub struct LValue {
|
||||
pub identifier: IdentifierOperand,
|
||||
pub kind: InstructionKind,
|
||||
}
|
||||
|
||||
@@ -503,10 +502,10 @@ impl Display for Effect {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Identifier<'a> {
|
||||
pub struct Identifier {
|
||||
/// Uniquely identifiers this identifier
|
||||
pub id: IdentifierId,
|
||||
pub name: Option<String<'a>>,
|
||||
pub name: Option<String>,
|
||||
|
||||
pub data: Rc<RefCell<IdentifierData>>,
|
||||
}
|
||||
|
||||
@@ -17,10 +17,7 @@ use crate::{
|
||||
/// Note that this pass leaves value/loop blocks alone because they cannot
|
||||
/// be merged without breaking the structure of the high-level terminals
|
||||
/// that reference them.
|
||||
pub fn merge_consecutive_blocks<'a>(
|
||||
env: &Environment<'a>,
|
||||
fun: &mut Function<'a>,
|
||||
) -> Result<(), Diagnostic> {
|
||||
pub fn merge_consecutive_blocks(env: &Environment, fun: &mut Function) -> Result<(), Diagnostic> {
|
||||
let mut merged = MergedBlocks::default();
|
||||
let blocks = &mut fun.body.blocks;
|
||||
let instructions = &mut fun.body.instructions;
|
||||
|
||||
@@ -12,11 +12,11 @@ use crate::{
|
||||
/// handle things like indentation and maybe wrapping long lines. The
|
||||
/// `pretty` crate seems to have a lot of usage but the type signatures
|
||||
/// are pretty tedious, we can make something much simpler.
|
||||
pub trait Print<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result;
|
||||
pub trait Print {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result;
|
||||
}
|
||||
|
||||
impl<'a> Function<'a> {
|
||||
impl Function {
|
||||
pub fn debug(&self) {
|
||||
let mut out = String::new();
|
||||
self.print(&self.body, &mut out).unwrap();
|
||||
@@ -24,8 +24,8 @@ impl<'a> Function<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for Function<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for Function {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
ensure_sufficient_stack(|| {
|
||||
writeln!(
|
||||
out,
|
||||
@@ -51,8 +51,8 @@ impl<'a> Print<'a> for Function<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for BasicBlock<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for BasicBlock {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
writeln!(out, "{} ({})", self.id, self.kind)?;
|
||||
if !self.predecessors.is_empty() {
|
||||
write!(out, " predecessors: ")?;
|
||||
@@ -83,8 +83,8 @@ impl<'a> Print<'a> for BasicBlock<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for Phi<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for Phi {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
write!(out, " ")?;
|
||||
self.identifier.print(hir, out)?;
|
||||
write!(out, ": phi(")?;
|
||||
@@ -100,8 +100,8 @@ impl<'a> Print<'a> for Phi<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for Instruction<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for Instruction {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
write!(out, " {} ", self.id)?;
|
||||
self.value.print(hir, out)?;
|
||||
writeln!(out, "")?;
|
||||
@@ -109,8 +109,8 @@ impl<'a> Print<'a> for Instruction<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for InstructionValue<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for InstructionValue {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
match self {
|
||||
InstructionValue::Array(value) => {
|
||||
write!(out, "Array [")?;
|
||||
@@ -211,8 +211,8 @@ impl<'a> Print<'a> for InstructionValue<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for PlaceOrSpread {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for PlaceOrSpread {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
match self {
|
||||
PlaceOrSpread::Place(place) => place.print(hir, out),
|
||||
PlaceOrSpread::Spread(place) => {
|
||||
@@ -224,8 +224,8 @@ impl<'a> Print<'a> for PlaceOrSpread {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for Operand {
|
||||
fn print(&self, _hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for Operand {
|
||||
fn print(&self, _hir: &HIR, out: &mut impl Write) -> Result {
|
||||
write!(
|
||||
out,
|
||||
"{} {}",
|
||||
@@ -238,15 +238,15 @@ impl<'a> Print<'a> for Operand {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for LValue<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for LValue {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
write!(out, "{} ", self.kind)?;
|
||||
self.identifier.print(hir, out)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for IdentifierOperand<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for IdentifierOperand {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
write!(
|
||||
out,
|
||||
"{} ",
|
||||
@@ -259,8 +259,8 @@ impl<'a> Print<'a> for IdentifierOperand<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for Identifier<'a> {
|
||||
fn print(&self, _hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for Identifier {
|
||||
fn print(&self, _hir: &HIR, out: &mut impl Write) -> Result {
|
||||
write!(
|
||||
out,
|
||||
"{}{}",
|
||||
@@ -273,8 +273,8 @@ impl<'a> Print<'a> for Identifier<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for Terminal<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for Terminal {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
write!(out, " {} ", self.id)?;
|
||||
self.value.print(hir, out)?;
|
||||
writeln!(out, "")?;
|
||||
@@ -282,8 +282,8 @@ impl<'a> Print<'a> for Terminal<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Print<'a> for TerminalValue<'a> {
|
||||
fn print(&self, hir: &HIR<'a>, out: &mut impl Write) -> Result {
|
||||
impl Print for TerminalValue {
|
||||
fn print(&self, hir: &HIR, out: &mut impl Write) -> Result {
|
||||
match self {
|
||||
TerminalValue::Return(terminal) => {
|
||||
write!(out, "Return ")?;
|
||||
|
||||
@@ -4,13 +4,13 @@ use crate::{BlockId, InstructionId};
|
||||
/// Terminals represent statements or expressions that affect control flow,
|
||||
/// such as for-of, if-else, return, logical (??), ternaries (?:), etc.
|
||||
#[derive(Debug)]
|
||||
pub struct Terminal<'a> {
|
||||
pub struct Terminal {
|
||||
pub id: InstructionId,
|
||||
pub value: TerminalValue<'a>,
|
||||
pub value: TerminalValue,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TerminalValue<'a> {
|
||||
pub enum TerminalValue {
|
||||
Branch(BranchTerminal),
|
||||
DoWhile(DoWhileTerminal),
|
||||
// ForOf(ForOfTerminal),
|
||||
@@ -25,11 +25,11 @@ pub enum TerminalValue<'a> {
|
||||
// Switch(SwitchTerminal),
|
||||
// Ternary(TernaryTerminal),
|
||||
// Throw(ThrowTerminal),
|
||||
Unsupported(UnsupportedTerminal<'a>),
|
||||
Unsupported(UnsupportedTerminal),
|
||||
// While(WhileTerminal),
|
||||
}
|
||||
|
||||
impl<'a> TerminalValue<'a> {
|
||||
impl TerminalValue {
|
||||
pub fn map_optional_fallthroughs<F>(&mut self, f: F) -> ()
|
||||
where
|
||||
F: Fn(BlockId) -> Option<BlockId>,
|
||||
@@ -103,9 +103,7 @@ impl<'a> TerminalValue<'a> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnsupportedTerminal<'a> {
|
||||
phantom: std::marker::PhantomData<&'a ()>,
|
||||
}
|
||||
pub struct UnsupportedTerminal {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BranchTerminal {
|
||||
|
||||
@@ -19,7 +19,6 @@ forget_ssa = { workspace = true }
|
||||
forget_build_hir = { workspace = true }
|
||||
forget_utils = { workspace = true }
|
||||
forget_diagnostics = { workspace = true }
|
||||
bumpalo = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
miette = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
@@ -9,18 +9,15 @@ use forget_hir::{
|
||||
};
|
||||
use forget_ssa::eliminate_redundant_phis;
|
||||
|
||||
pub fn constant_propagation<'a>(
|
||||
env: &Environment<'a>,
|
||||
fun: &mut Function<'a>,
|
||||
) -> Result<(), Diagnostic> {
|
||||
pub fn constant_propagation(env: &Environment, fun: &mut Function) -> Result<(), Diagnostic> {
|
||||
let mut constants = Constants::new();
|
||||
constant_propagation_impl(env, fun, &mut constants)
|
||||
}
|
||||
|
||||
fn constant_propagation_impl<'a>(
|
||||
env: &Environment<'a>,
|
||||
fun: &mut Function<'a>,
|
||||
constants: &mut Constants<'a>,
|
||||
fn constant_propagation_impl(
|
||||
env: &Environment,
|
||||
fun: &mut Function,
|
||||
constants: &mut Constants,
|
||||
) -> Result<(), Diagnostic> {
|
||||
loop {
|
||||
let have_terminals_changed = apply_constant_propagation(env, fun, constants)?;
|
||||
@@ -55,16 +52,16 @@ fn constant_propagation_impl<'a>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_constant_propagation<'a>(
|
||||
env: &Environment<'a>,
|
||||
fun: &mut Function<'a>,
|
||||
constants: &mut Constants<'a>,
|
||||
fn apply_constant_propagation(
|
||||
env: &Environment,
|
||||
fun: &mut Function,
|
||||
constants: &mut Constants,
|
||||
) -> Result<bool, Diagnostic> {
|
||||
let mut has_changes = false;
|
||||
|
||||
for block in fun.body.blocks.iter_mut() {
|
||||
for phi in block.phis.iter() {
|
||||
let mut value: Option<Constant<'a>> = None;
|
||||
let mut value: Option<Constant> = None;
|
||||
for (_, operand) in &phi.operands {
|
||||
if let Some(operand_value) = constants.get(&operand.id) {
|
||||
match &mut value {
|
||||
@@ -128,10 +125,7 @@ fn apply_constant_propagation<'a>(
|
||||
Ok(has_changes)
|
||||
}
|
||||
|
||||
fn read_primitive_instruction<'a>(
|
||||
instrs: &[Instruction<'a>],
|
||||
operand: &Operand,
|
||||
) -> Option<Primitive<'a>> {
|
||||
fn read_primitive_instruction(instrs: &[Instruction], operand: &Operand) -> Option<Primitive> {
|
||||
let instr = &instrs[usize::from(operand.ix)].value;
|
||||
if let InstructionValue::Primitive(primitive) = instr {
|
||||
Some(primitive.clone())
|
||||
@@ -140,11 +134,11 @@ fn read_primitive_instruction<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_instruction<'a>(
|
||||
env: &Environment<'a>,
|
||||
instrs: &[Instruction<'a>],
|
||||
mut instr: &mut InstructionValue<'a>,
|
||||
constants: &mut Constants<'a>,
|
||||
fn evaluate_instruction(
|
||||
env: &Environment,
|
||||
instrs: &[Instruction],
|
||||
mut instr: &mut InstructionValue,
|
||||
constants: &mut Constants,
|
||||
) -> Result<(), Diagnostic> {
|
||||
let read_constant = |operand: &Operand| {
|
||||
let instr = &instrs[usize::from(operand.ix)].value;
|
||||
@@ -189,7 +183,7 @@ fn evaluate_instruction<'a>(
|
||||
// the function (using its context variables list)
|
||||
// - Track the last such filtered Constants instance we visited the function
|
||||
// with. Only visit again if the Constants have changed.
|
||||
let mut inner_constants: Constants<'a> = value
|
||||
let mut inner_constants: Constants = value
|
||||
.lowered_function
|
||||
.context
|
||||
.iter()
|
||||
@@ -207,12 +201,12 @@ fn evaluate_instruction<'a>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_binary_operator<'a>(
|
||||
_env: &Environment<'a>,
|
||||
left: Primitive<'a>,
|
||||
fn apply_binary_operator(
|
||||
_env: &Environment,
|
||||
left: Primitive,
|
||||
operator: BinaryOperator,
|
||||
right: Primitive<'a>,
|
||||
) -> Option<Primitive<'a>> {
|
||||
right: Primitive,
|
||||
) -> Option<Primitive> {
|
||||
match (left.value, right.value) {
|
||||
(PrimitiveValue::Number(left), PrimitiveValue::Number(right)) => match operator {
|
||||
BinaryOperator::Add => Some(Primitive {
|
||||
@@ -271,16 +265,16 @@ fn apply_binary_operator<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
type Constants<'a> = HashMap<IdentifierId, Constant<'a>>;
|
||||
type Constants = HashMap<IdentifierId, Constant>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum Constant<'a> {
|
||||
Global(LoadGlobal<'a>),
|
||||
Primitive(Primitive<'a>),
|
||||
enum Constant {
|
||||
Global(LoadGlobal),
|
||||
Primitive(Primitive),
|
||||
}
|
||||
|
||||
impl<'a> From<&Constant<'a>> for InstructionValue<'a> {
|
||||
fn from(value: &Constant<'a>) -> Self {
|
||||
impl From<&Constant> for InstructionValue {
|
||||
fn from(value: &Constant) -> Self {
|
||||
match value {
|
||||
Constant::Global(value) => InstructionValue::LoadGlobal(value.clone()),
|
||||
Constant::Primitive(value) => InstructionValue::Primitive(value.clone()),
|
||||
@@ -288,8 +282,8 @@ impl<'a> From<&Constant<'a>> for InstructionValue<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Constant<'a>> for InstructionValue<'a> {
|
||||
fn from(value: Constant<'a>) -> Self {
|
||||
impl From<Constant> for InstructionValue {
|
||||
fn from(value: Constant) -> Self {
|
||||
match value {
|
||||
Constant::Global(value) => InstructionValue::LoadGlobal(value),
|
||||
Constant::Primitive(value) => InstructionValue::Primitive(value),
|
||||
|
||||
@@ -16,7 +16,6 @@ repository.workspace = true
|
||||
forget_hir = { workspace = true }
|
||||
forget_utils = { workspace = true }
|
||||
forget_diagnostics = { workspace = true }
|
||||
bumpalo = { workspace = true }
|
||||
indexmap = { workspace = true }
|
||||
miette = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use forget_hir::{BlockId, Environment, Function, Identifier, IdentifierId, InstructionValue};
|
||||
use forget_utils::RetainMut;
|
||||
|
||||
/// Pass to eliminate redundant phi nodes:
|
||||
/// all operands are the same identifier, ie `x2 = phi(x1, x1, x1)`.
|
||||
@@ -15,8 +14,8 @@ use forget_utils::RetainMut;
|
||||
/// phi is encountered we add a mapping (eg x2 -> x1) to a rewrite table. Subsequent instructions, terminals,
|
||||
/// and phis rewrite all their identifiers based on this table. The algorithm loops over the CFG repeatedly
|
||||
/// until there are no new rewrites: for a CFG without back-edges it completes in a single pass.
|
||||
type Rewrites<'a> = HashMap<IdentifierId, Identifier<'a>>;
|
||||
pub fn eliminate_redundant_phis<'a>(env: &Environment, fun: &mut Function<'a>) {
|
||||
type Rewrites = HashMap<IdentifierId, Identifier>;
|
||||
pub fn eliminate_redundant_phis(env: &Environment, fun: &mut Function) {
|
||||
let hir = &mut fun.body;
|
||||
let mut rewrites = Rewrites::new();
|
||||
|
||||
@@ -97,7 +96,7 @@ pub fn eliminate_redundant_phis<'a>(env: &Environment, fun: &mut Function<'a>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn rewrite<'a>(rewrites: &Rewrites<'a>, identifier: &mut Identifier<'a>) {
|
||||
fn rewrite(rewrites: &Rewrites, identifier: &mut Identifier) {
|
||||
if let Some(rewrite) = rewrites.get(&identifier.id) {
|
||||
*identifier = rewrite.clone()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use bumpalo::collections::Vec;
|
||||
use forget_diagnostics::{invariant, Diagnostic};
|
||||
use forget_hir::{
|
||||
BasicBlock, BlockId, Blocks, Environment, Function, Identifier, IdentifierData, IdentifierId,
|
||||
@@ -9,15 +8,15 @@ use forget_hir::{
|
||||
};
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
|
||||
pub fn enter_ssa<'a>(env: &Environment<'a>, fun: &mut Function<'a>) -> Result<(), Diagnostic> {
|
||||
pub fn enter_ssa(env: &Environment, fun: &mut Function) -> Result<(), Diagnostic> {
|
||||
assert!(fun.context.is_empty());
|
||||
enter_ssa_impl(env, fun, None)
|
||||
}
|
||||
|
||||
pub fn enter_ssa_impl<'a>(
|
||||
env: &Environment<'a>,
|
||||
fun: &mut Function<'a>,
|
||||
context_defs: Option<IndexMap<IdentifierId, Identifier<'a>>>,
|
||||
pub fn enter_ssa_impl(
|
||||
env: &Environment,
|
||||
fun: &mut Function,
|
||||
context_defs: Option<IndexMap<IdentifierId, Identifier>>,
|
||||
) -> Result<(), Diagnostic> {
|
||||
let blocks = &fun.body.blocks;
|
||||
let instructions = &mut fun.body.instructions;
|
||||
@@ -40,10 +39,10 @@ pub fn enter_ssa_impl<'a>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_instructions<'a, 'e, 'f>(
|
||||
env: &Environment<'a>,
|
||||
builder: &mut Builder<'a, 'e, 'f>,
|
||||
instructions: &mut Vec<'a, Instruction<'a>>,
|
||||
fn visit_instructions<'e, 'f>(
|
||||
env: &Environment,
|
||||
builder: &mut Builder<'e, 'f>,
|
||||
instructions: &mut Vec<Instruction>,
|
||||
) -> Result<(), Diagnostic> {
|
||||
builder.each_block(|block, builder| {
|
||||
for instr_ix in &block.instructions {
|
||||
@@ -75,11 +74,11 @@ fn visit_instructions<'a, 'e, 'f>(
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Builder<'a, 'e, 'f> {
|
||||
env: &'e Environment<'a>,
|
||||
blocks: &'f Blocks<'a>,
|
||||
struct Builder<'e, 'f> {
|
||||
env: &'e Environment,
|
||||
blocks: &'f Blocks,
|
||||
|
||||
states: IndexMap<BlockId, BlockState<'a>>,
|
||||
states: IndexMap<BlockId, BlockState>,
|
||||
current: BlockId,
|
||||
unsealed_predecessors: IndexMap<BlockId, usize>,
|
||||
unknown: IndexSet<IdentifierId>,
|
||||
@@ -87,34 +86,34 @@ struct Builder<'a, 'e, 'f> {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BlockState<'a> {
|
||||
defs: IndexMap<IdentifierId, Identifier<'a>>,
|
||||
incomplete_phis: Vec<'a, IncompletePhi<'a>>,
|
||||
phis: Vec<'a, Phi<'a>>,
|
||||
struct BlockState {
|
||||
defs: IndexMap<IdentifierId, Identifier>,
|
||||
incomplete_phis: Vec<IncompletePhi>,
|
||||
phis: Vec<Phi>,
|
||||
}
|
||||
|
||||
impl<'a> BlockState<'a> {
|
||||
fn new(env: &Environment<'a>) -> Self {
|
||||
impl BlockState {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
defs: Default::default(),
|
||||
incomplete_phis: env.vec_new(),
|
||||
phis: env.vec_new(),
|
||||
incomplete_phis: Default::default(),
|
||||
phis: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IncompletePhi<'a> {
|
||||
old_id: Identifier<'a>,
|
||||
new_id: Identifier<'a>,
|
||||
struct IncompletePhi {
|
||||
old_id: Identifier,
|
||||
new_id: Identifier,
|
||||
}
|
||||
|
||||
impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
fn new(env: &'e Environment<'a>, entry: BlockId, blocks: &'f Blocks<'a>) -> Self {
|
||||
impl<'e, 'f> Builder<'e, 'f> {
|
||||
fn new(env: &'e Environment, entry: BlockId, blocks: &'f Blocks) -> Self {
|
||||
let states = blocks
|
||||
.block_ids()
|
||||
.into_iter()
|
||||
.map(|block_id| (block_id, BlockState::new(env)))
|
||||
.map(|block_id| (block_id, BlockState::new()))
|
||||
.collect();
|
||||
Self {
|
||||
env,
|
||||
@@ -127,12 +126,12 @@ impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
}
|
||||
}
|
||||
|
||||
fn initialize_context(&mut self, defs: IndexMap<IdentifierId, Identifier<'a>>) {
|
||||
fn initialize_context(&mut self, defs: IndexMap<IdentifierId, Identifier>) {
|
||||
let state = self.states.get_mut(&self.current).unwrap();
|
||||
state.defs = defs;
|
||||
}
|
||||
|
||||
fn complete(self) -> IndexMap<BlockId, BlockState<'a>> {
|
||||
fn complete(self) -> IndexMap<BlockId, BlockState> {
|
||||
self.states
|
||||
}
|
||||
|
||||
@@ -140,7 +139,7 @@ impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
self.env.next_identifier_id()
|
||||
}
|
||||
|
||||
fn visit_store(&mut self, lvalue: &mut LValue<'a>) -> Result<(), Diagnostic> {
|
||||
fn visit_store(&mut self, lvalue: &mut LValue) -> Result<(), Diagnostic> {
|
||||
let old_identifier = &lvalue.identifier.identifier;
|
||||
// TODO: use Result (?)
|
||||
invariant(!self.unknown.contains(&old_identifier.id), || {
|
||||
@@ -163,7 +162,7 @@ impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_param(&mut self, param: &mut IdentifierOperand<'a>) -> () {
|
||||
fn visit_param(&mut self, param: &mut IdentifierOperand) -> () {
|
||||
let old_identifier = ¶m.identifier;
|
||||
let new_identifier = self.make_identifier(old_identifier);
|
||||
let state = self.states.get_mut(&self.current).unwrap();
|
||||
@@ -171,12 +170,12 @@ impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
param.identifier = new_identifier;
|
||||
}
|
||||
|
||||
fn visit_load(&mut self, local: &mut IdentifierOperand<'a>) -> () {
|
||||
fn visit_load(&mut self, local: &mut IdentifierOperand) -> () {
|
||||
let new_identifier = self.get_id_at(self.current, &local.identifier);
|
||||
local.identifier = new_identifier;
|
||||
}
|
||||
|
||||
fn get_id_at(&mut self, block_id: BlockId, old_identifier: &Identifier<'a>) -> Identifier<'a> {
|
||||
fn get_id_at(&mut self, block_id: BlockId, old_identifier: &Identifier) -> Identifier {
|
||||
// Check if we've already resolved this identifier in this block
|
||||
let state = self.states.get(&block_id).unwrap();
|
||||
if let Some(identifier) = state.defs.get(&old_identifier.id) {
|
||||
@@ -219,9 +218,9 @@ impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
fn add_phi(
|
||||
&mut self,
|
||||
block_id: BlockId,
|
||||
old_identifier: &Identifier<'a>,
|
||||
new_identifier: Identifier<'a>,
|
||||
) -> Identifier<'a> {
|
||||
old_identifier: &Identifier,
|
||||
new_identifier: Identifier,
|
||||
) -> Identifier {
|
||||
let mut phi = Phi {
|
||||
identifier: new_identifier.clone(),
|
||||
operands: Default::default(),
|
||||
@@ -237,7 +236,7 @@ impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
new_identifier
|
||||
}
|
||||
|
||||
fn make_identifier(&self, old_identifier: &Identifier<'a>) -> Identifier<'a> {
|
||||
fn make_identifier(&self, old_identifier: &Identifier) -> Identifier {
|
||||
let old_data = old_identifier.data.borrow();
|
||||
Identifier {
|
||||
id: self.next_ssa_id(),
|
||||
@@ -252,7 +251,7 @@ impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
|
||||
fn fix_incomplete_phis(&mut self, block_id: BlockId) -> () {
|
||||
let state = self.states.get_mut(&block_id).unwrap();
|
||||
let incomplete_phis = std::mem::replace(&mut state.incomplete_phis, self.env.vec_new());
|
||||
let incomplete_phis = std::mem::take(&mut state.incomplete_phis);
|
||||
for phi in incomplete_phis {
|
||||
self.add_phi(block_id, &phi.old_id, phi.new_id);
|
||||
}
|
||||
@@ -260,7 +259,7 @@ impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
|
||||
|
||||
fn each_block<F>(&mut self, mut f: F) -> Result<(), Diagnostic>
|
||||
where
|
||||
F: FnMut(&BasicBlock<'a>, &mut Self) -> Result<(), Diagnostic>,
|
||||
F: FnMut(&BasicBlock, &mut Self) -> Result<(), Diagnostic>,
|
||||
{
|
||||
let mut visited = IndexSet::new();
|
||||
let block_ids = self.blocks.block_ids();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use forget_hir::{Environment, HIR};
|
||||
|
||||
pub fn leave_ssa<'a>(_env: &'a Environment, _hir: &mut HIR<'a>) {
|
||||
pub fn leave_ssa(_env: &Environment, _hir: &mut HIR) {
|
||||
todo!("leave_ssa()");
|
||||
}
|
||||
|
||||
@@ -13,5 +13,4 @@ repository.workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bumpalo = { workspace = true }
|
||||
stacker = { workspace = true }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
mod ensure_sufficient_stack;
|
||||
mod retain_mut;
|
||||
|
||||
pub use ensure_sufficient_stack::*;
|
||||
pub use retain_mut::*;
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
use bumpalo::collections::Vec;
|
||||
|
||||
pub trait RetainMut<T> {
|
||||
fn retain_mut<F>(&mut self, f: F) -> ()
|
||||
where
|
||||
F: FnMut(&mut T) -> bool;
|
||||
}
|
||||
|
||||
impl<'a, T> RetainMut<T> for Vec<'a, T> {
|
||||
fn retain_mut<F>(&mut self, mut f: F) -> ()
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
// NOTE: implementation adapted from retain_mut crate
|
||||
// which is in turn adapted from Rust stdlib
|
||||
// https://docs.rs/retain_mut/latest/src/retain_mut/lib.rs.html#68-69
|
||||
|
||||
let original_len = self.len();
|
||||
// Avoid double drop if the drop guard is not executed,
|
||||
// since we may make some holes during the process.
|
||||
unsafe { self.set_len(0) };
|
||||
|
||||
// Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked]
|
||||
// |<- processed len ->| ^- next to check
|
||||
// |<- deleted cnt ->|
|
||||
// |<- original_len ->|
|
||||
// Kept: Elements which predicate returns true on.
|
||||
// Hole: Moved or dropped element slot.
|
||||
// Unchecked: Unchecked valid elements.
|
||||
//
|
||||
// This drop guard will be invoked when predicate or `drop` of element panicked.
|
||||
// It shifts unchecked elements to cover holes and `set_len` to the correct length.
|
||||
// In cases when predicate and `drop` never panick, it will be optimized out.
|
||||
struct BackshiftOnDrop<'a, 'b, T> {
|
||||
v: &'b mut Vec<'a, T>,
|
||||
processed_len: usize,
|
||||
deleted_cnt: usize,
|
||||
original_len: usize,
|
||||
}
|
||||
|
||||
impl<T> Drop for BackshiftOnDrop<'_, '_, T> {
|
||||
fn drop(&mut self) {
|
||||
if self.deleted_cnt > 0 {
|
||||
// SAFETY: Trailing unchecked items must be valid since we never touch them.
|
||||
unsafe {
|
||||
std::ptr::copy(
|
||||
self.v.as_ptr().add(self.processed_len),
|
||||
self.v
|
||||
.as_mut_ptr()
|
||||
.add(self.processed_len - self.deleted_cnt),
|
||||
self.original_len - self.processed_len,
|
||||
);
|
||||
}
|
||||
}
|
||||
// SAFETY: After filling holes, all items are in contiguous memory.
|
||||
unsafe {
|
||||
self.v.set_len(self.original_len - self.deleted_cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut g = BackshiftOnDrop {
|
||||
v: self,
|
||||
processed_len: 0,
|
||||
deleted_cnt: 0,
|
||||
original_len,
|
||||
};
|
||||
|
||||
fn process_loop<F, T, const DELETED: bool>(
|
||||
original_len: usize,
|
||||
f: &mut F,
|
||||
g: &mut BackshiftOnDrop<'_, '_, T>,
|
||||
) where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
while g.processed_len != original_len {
|
||||
// SAFETY: Unchecked element must be valid.
|
||||
let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) };
|
||||
if !f(cur) {
|
||||
// Advance early to avoid double drop if `drop_in_place` panicked.
|
||||
g.processed_len += 1;
|
||||
g.deleted_cnt += 1;
|
||||
// SAFETY: We never touch this element again after dropped.
|
||||
unsafe { std::ptr::drop_in_place(cur) };
|
||||
// We already advanced the counter.
|
||||
if DELETED {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if DELETED {
|
||||
// SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element.
|
||||
// We use copy for move, and never touch this element again.
|
||||
unsafe {
|
||||
let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt);
|
||||
std::ptr::copy_nonoverlapping(cur, hole_slot, 1);
|
||||
}
|
||||
}
|
||||
g.processed_len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Stage 1: Nothing was deleted.
|
||||
process_loop::<F, T, false>(original_len, &mut f, &mut g);
|
||||
|
||||
// Stage 2: Some elements were deleted.
|
||||
process_loop::<F, T, true>(original_len, &mut f, &mut g);
|
||||
|
||||
// All item are processed. This can be optimized to `set_len` by LLVM.
|
||||
drop(g);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user