Skip to content

Commit

Permalink
Extract optimize.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
voltrevo committed Jun 29, 2023
1 parent dc99c52 commit b315f07
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 352 deletions.
2 changes: 2 additions & 0 deletions valuescript_compiler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ mod instruction_mutates_this;
mod link_module;
mod module_compiler;
mod name_allocator;
mod optimize;
mod resolve_path;
mod scope;
mod scope_analysis;
mod static_eval_expr;
mod target_accessor;
mod visit_pointers;

pub use assembler::assemble;
pub use assembly_parser::parse_module;
Expand Down
357 changes: 5 additions & 352 deletions valuescript_compiler/src/link_module.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use std::collections::{HashMap, HashSet};
use std::mem::take;
use std::collections::HashMap;

use crate::asm::{
Array, Definition, DefinitionContent, FnLine, Instruction, InstructionFieldMut, Object, Pointer,
Value,
};
use crate::asm::{Definition, DefinitionContent, FnLine, Instruction, Object, Pointer, Value};
use crate::gather_modules::PathAndModule;
use crate::import_pattern::{ImportKind, ImportPattern};
use crate::name_allocator::NameAllocator;
use crate::resolve_path::{resolve_path, ResolvedPath};
use crate::DiagnosticLevel;
use crate::visit_pointers::{visit_pointers, PointerVisitation};
use crate::{asm::Module, Diagnostic};
use crate::{optimize::optimize, DiagnosticLevel};

pub struct LinkModuleResult {
pub module: Option<Module>,
Expand Down Expand Up @@ -108,9 +105,7 @@ pub fn link_module(
&mut result.diagnostics,
);

collapse_pointers_of_pointers(&mut path_and_module.module);
shake_tree(&mut path_and_module.module);
extract_constants(&mut path_and_module.module, &mut pointer_allocator);
optimize(&mut path_and_module.module, &mut pointer_allocator);

result.module = Some(path_and_module.module);
result
Expand Down Expand Up @@ -140,180 +135,6 @@ fn rewrite_pointers(module: &mut Module, pointer_allocator: &mut NameAllocator)
});
}

fn visit_pointers<Visitor>(module: &mut Module, visitor: Visitor)
where
Visitor: FnMut(PointerVisitation) -> (),
{
let mut pointer_visitor = VisitPointerImpl::new(visitor);
pointer_visitor.module(module);
}

#[derive(PartialEq, Debug)]
enum PointerVisitation<'a> {
Export(&'a mut Pointer),
Definition(&'a mut Pointer),
Reference(&'a Pointer, &'a mut Pointer),
}

struct VisitPointerImpl<Visitor>
where
Visitor: FnMut(PointerVisitation) -> (),
{
visitor: Visitor,
}

impl<Visitor> VisitPointerImpl<Visitor>
where
Visitor: FnMut(PointerVisitation) -> (),
{
fn new(visitor: Visitor) -> Self {
Self { visitor }
}

pub fn module(&mut self, module: &mut Module) {
self.value(None, &mut module.export_default);
self.object(None, &mut module.export_star);

for definition in &mut module.definitions {
self.definition(definition);
}
}

fn definition(&mut self, definition: &mut Definition) {
(self.visitor)(PointerVisitation::Definition(&mut definition.pointer));

match &mut definition.content {
DefinitionContent::Function(function) => {
self.body(&definition.pointer, &mut function.body);
}
DefinitionContent::Class(class) => {
self.value(Some(&definition.pointer), &mut class.constructor);
self.value(Some(&definition.pointer), &mut class.prototype);
self.value(Some(&definition.pointer), &mut class.static_);
}
DefinitionContent::Value(value) => {
self.value(Some(&definition.pointer), value);
}
DefinitionContent::Lazy(lazy) => {
self.body(&definition.pointer, &mut lazy.body);
}
}
}

fn array(&mut self, owner: Option<&Pointer>, array: &mut Array) {
for value in &mut array.values {
self.value(owner, value);
}
}

fn object(&mut self, owner: Option<&Pointer>, object: &mut Object) {
for (key, value) in object.properties.iter_mut() {
self.value(owner, key);
self.value(owner, value);
}
}

fn value(&mut self, owner: Option<&Pointer>, value: &mut Value) {
use Value::*;

match value {
Void | Undefined | Null | Bool(_) | Number(_) | BigInt(_) | String(_) | Register(_)
| Builtin(_) => {}
Array(array) => {
self.array(owner, array);
}
Object(object) => {
self.object(owner, object);
}
Pointer(pointer) => {
(self.visitor)(match owner {
Some(owner) => PointerVisitation::Reference(owner, pointer),
None => PointerVisitation::Export(pointer),
});
}
}
}

fn instruction(&mut self, owner: &Pointer, instruction: &mut Instruction) {
use Instruction::*;

match instruction {
End | UnsetCatch | RequireMutableThis | OpInc(..) | OpDec(..) | Jmp(..) | SetCatch(..)
| Next(..) | UnpackIterRes(..) => {}
Mov(arg, _)
| OpNot(arg, _)
| OpBitNot(arg, _)
| TypeOf(arg, _)
| UnaryPlus(arg, _)
| UnaryMinus(arg, _)
| Import(arg, _)
| ImportStar(arg, _)
| Throw(arg)
| Cat(arg, _)
| Yield(arg, _)
| YieldStar(arg, _) => {
self.value(Some(owner), arg);
}
OpPlus(arg1, arg2, _)
| OpMinus(arg1, arg2, _)
| OpMul(arg1, arg2, _)
| OpDiv(arg1, arg2, _)
| OpMod(arg1, arg2, _)
| OpExp(arg1, arg2, _)
| OpEq(arg1, arg2, _)
| OpNe(arg1, arg2, _)
| OpTripleEq(arg1, arg2, _)
| OpTripleNe(arg1, arg2, _)
| OpAnd(arg1, arg2, _)
| OpOr(arg1, arg2, _)
| OpLess(arg1, arg2, _)
| OpLessEq(arg1, arg2, _)
| OpGreater(arg1, arg2, _)
| OpGreaterEq(arg1, arg2, _)
| OpNullishCoalesce(arg1, arg2, _)
| OpOptionalChain(arg1, arg2, _)
| OpBitAnd(arg1, arg2, _)
| OpBitOr(arg1, arg2, _)
| OpBitXor(arg1, arg2, _)
| OpLeftShift(arg1, arg2, _)
| OpRightShift(arg1, arg2, _)
| OpRightShiftUnsigned(arg1, arg2, _)
| InstanceOf(arg1, arg2, _)
| In(arg1, arg2, _)
| Call(arg1, arg2, _)
| Bind(arg1, arg2, _)
| Sub(arg1, arg2, _)
| SubMov(arg1, arg2, _)
| New(arg1, arg2, _) => {
self.value(Some(owner), arg1);
self.value(Some(owner), arg2);
}
Apply(arg1, arg2, arg3, _)
| SubCall(arg1, arg2, arg3, _)
| ConstSubCall(arg1, arg2, arg3, _)
| ThisSubCall(arg1, arg2, arg3, _) => {
self.value(Some(owner), arg1);
self.value(Some(owner), arg2);
self.value(Some(owner), arg3);
}
JmpIf(arg, _) => {
self.value(Some(owner), arg);
}
};
}

fn body(&mut self, owner: &Pointer, body: &mut Vec<FnLine>) {
for fn_line in body {
match fn_line {
FnLine::Instruction(instruction) => {
self.instruction(owner, instruction);
}
FnLine::Label(..) | FnLine::Empty | FnLine::Comment(..) => {}
}
}
}
}

fn resolve_and_rewrite_import_patterns(path_and_module: &mut PathAndModule) -> Vec<ResolvedPath> {
let mut resolved_paths = Vec::<ResolvedPath>::new();

Expand Down Expand Up @@ -394,171 +215,3 @@ fn link_import_patterns(
*definition = new_definition;
}
}

fn collapse_pointers_of_pointers(module: &mut Module) {
let mut double_pointer_map = HashMap::<Pointer, Pointer>::new();

for definition in &mut module.definitions {
let pointer = match &definition.content {
DefinitionContent::Value(Value::Pointer(pointer)) => pointer,
_ => continue,
};

double_pointer_map.insert(definition.pointer.clone(), pointer.clone());
}

visit_pointers(module, |visitation| match visitation {
PointerVisitation::Definition(_) => {}
PointerVisitation::Export(pointer) | PointerVisitation::Reference(_, pointer) => {
let mut mapped_pointer: &Pointer = pointer;

loop {
if let Some(new_pointer) = double_pointer_map.get(mapped_pointer) {
mapped_pointer = new_pointer;
continue;
}

break;
}

*pointer = mapped_pointer.clone();
}
});
}

fn shake_tree(module: &mut Module) {
let mut dependency_graph = HashMap::<Pointer, HashSet<Pointer>>::new();
let mut pointers_to_include = Vec::<Pointer>::new();

visit_pointers(module, |visitation| match visitation {
PointerVisitation::Export(exported_pointer) => {
pointers_to_include.push(exported_pointer.clone());
}
PointerVisitation::Definition(_) => {}
PointerVisitation::Reference(owner, pointer) => {
dependency_graph
.entry(owner.clone())
.or_default()
.insert(pointer.clone());
}
});

let mut pointers_included = HashSet::<Pointer>::new();
let mut pointers_to_include_i = 0;

while pointers_to_include_i < pointers_to_include.len() {
let pointer = &pointers_to_include[pointers_to_include_i];
pointers_to_include_i += 1;

pointers_included.insert(pointer.clone());

if let Some(dependencies) = dependency_graph.get(pointer) {
// TODO: Avoid randomness caused by HashSet iteration
for dependency in dependencies {
if !pointers_included.contains(dependency) {
pointers_to_include.push(dependency.clone());
}
}
}
}

let previous_definitions = std::mem::take(&mut module.definitions);
let mut new_definitions_map = HashMap::<Pointer, Definition>::new();

for definition in previous_definitions {
if pointers_included.contains(&definition.pointer) {
new_definitions_map.insert(definition.pointer.clone(), definition);
}
}

let mut new_definitions = Vec::<Definition>::new();

for pointer in &pointers_to_include {
let defn = new_definitions_map.get_mut(pointer).unwrap();

if let DefinitionContent::Value(_) = defn.content {
// Exclude values on the first pass. TODO: Proper depth-first search (+reverse) to ensure
// correct ordering.
continue;
}

if defn.pointer.name != "" {
new_definitions.push(take(defn));
}
}

for pointer in pointers_to_include {
let defn = new_definitions_map.get_mut(&pointer).unwrap();

if defn.pointer.name != "" {
new_definitions.push(take(defn));
}
}

module.definitions = new_definitions;
}

fn extract_constants(module: &mut Module, pointer_allocator: &mut NameAllocator) {
let mut constants = HashMap::<Value, Pointer>::new();

for defn in &mut module.definitions {
if let DefinitionContent::Function(f) = &mut defn.content {
for line in &mut f.body {
if let FnLine::Instruction(instr) = line {
instr.visit_fields_mut(&mut |field| match field {
InstructionFieldMut::Value(value) => {
value.visit_values_mut(&mut |sub_value| {
if let Some(p) = constants.get(&sub_value) {
*sub_value = Value::Pointer(p.clone());
return;
}

if let Some(name) = should_extract_value_as_constant(&sub_value) {
let p = Pointer {
name: pointer_allocator.allocate(&name),
};

let existing_p = constants.insert(take(sub_value), p.clone());
assert!(existing_p.is_none());
*sub_value = Value::Pointer(p);
}
});
}
InstructionFieldMut::Register(_) | InstructionFieldMut::LabelRef(_) => {}
});
}
}
}
}

for (value, pointer) in constants {
module.definitions.push(Definition {
pointer,
content: DefinitionContent::Value(value),
});
}
}

fn should_extract_value_as_constant(value: &Value) -> Option<String> {
if let Value::String(s) = value {
if s.len() >= 4 {
return Some(mangle_string(s));
}
}

None
}

fn mangle_string(s: &String) -> String {
let mut res = "s_".to_string();

for c in s.chars() {
if c.is_ascii_alphanumeric() {
res.push(c);
} else {
res.push('_');
}
}

res
}
Loading

0 comments on commit b315f07

Please sign in to comment.