From 779d1b65fbe61e27df49ccc3a0dc22cf98bacb56 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 29 Apr 2019 23:20:34 +0200 Subject: [PATCH] perf(compiler): Shrink the core::Expr type to 40 bytes (from 72) --- Cargo.lock | 1 + vm/Cargo.toml | 15 ++--- vm/src/compiler.rs | 14 ++--- vm/src/core/grammar.lalrpop | 8 +-- vm/src/core/interpreter.rs | 8 +-- vm/src/core/mod.rs | 110 +++++++++++++++++++++++++----------- vm/src/core/optimize.rs | 19 ++++--- 7 files changed, 114 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30e8d5658e..b2f4350448 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1027,6 +1027,7 @@ dependencies = [ "lalrpop-util 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mopa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "pretty 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 77eb48dd8a..41dbeec71e 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -14,18 +14,19 @@ documentation = "https://docs.rs/gluon" build = "build.rs" [dependencies] +bitflags = "1.0.0" +codespan = "0.2" +collect-mac = "0.1.0" frunk_core = "0.2" +futures = "0.1.0" +itertools = "0.8" log = "0.4" -quick-error = "1.1.0" mopa = "0.2.2" -collect-mac = "0.1.0" +ordered-float = "1" pretty = "0.5" -bitflags = "1.0.0" -itertools = "0.8" -futures = "0.1.0" -typed-arena = "1.2.0" +quick-error = "1.1.0" smallvec = "0.6" -codespan = "0.2" +typed-arena = "1.2.0" serde = { version = "1.0.0", optional = true } serde_json = { version = "1.0.0", optional = true } diff --git a/vm/src/compiler.rs b/vm/src/compiler.rs index c6709d2c33..ece4f7df96 100644 --- a/vm/src/compiler.rs +++ b/vm/src/compiler.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; use crate::base::{ - ast::{self, DisplayEnv, Literal, Typed, TypedIdent}, + ast::{DisplayEnv, Typed, TypedIdent}, kind::{ArcKind, KindEnv}, pos::Line, resolve, @@ -12,7 +12,7 @@ use crate::base::{ }; use crate::{ - core::{self, CExpr, Expr, Pattern}, + core::{self, CExpr, Expr, Literal, Pattern}, interner::InternedStr, source_map::{LocalMap, SourceMap}, types::*, @@ -815,27 +815,27 @@ impl<'a> Compiler<'a> { Pattern::Literal(ref l) => { let lhs_i = function.stack_size() - 1; match *l { - ast::Literal::Byte(b) => { + Literal::Byte(b) => { function.emit(Push(lhs_i)); function.emit(PushByte(b)); function.emit(ByteEQ); } - ast::Literal::Int(i) => { + Literal::Int(i) => { function.emit(Push(lhs_i)); function.emit(PushInt(i)); function.emit(IntEQ); } - ast::Literal::Char(ch) => { + Literal::Char(ch) => { function.emit(Push(lhs_i)); function.emit(PushInt(u32::from(ch).into())); function.emit(IntEQ); } - ast::Literal::Float(f) => { + Literal::Float(f) => { function.emit(Push(lhs_i)); function.emit(PushFloat(f.into_inner())); function.emit(FloatEQ); } - ast::Literal::String(ref s) => { + Literal::String(ref s) => { self.load_identifier(&Symbol::from("@string_eq"), function)?; let lhs_i = function.stack_size() - 2; function.emit(Push(lhs_i)); diff --git a/vm/src/core/grammar.lalrpop b/vm/src/core/grammar.lalrpop index c9ebe91fad..a58beda0c2 100644 --- a/vm/src/core/grammar.lalrpop +++ b/vm/src/core/grammar.lalrpop @@ -1,10 +1,10 @@ use crate::base::{ - ast::{Literal, TypedIdent}, + ast::{TypedIdent}, pos::{BytePos, Span}, symbol::{Symbol, Symbols}, types::{Field, Type}}; -use crate::core::{Allocator, Alternative, Closure, Expr, LetBinding, Named, Pattern}; +use crate::core::{Allocator, Alternative, Closure, Expr, LetBinding, Literal, Named, Pattern}; grammar<'env, 'a>(symbols: &'env mut Symbols, allocator: &'a Allocator<'a>); @@ -27,7 +27,7 @@ Field: (Symbol, Option) = { Literal: Literal = { => Literal::Int(<>.parse().unwrap()), - => Literal::String(<>[1..<>.len() - 1].to_string()), + => Literal::String(Box::from(&<>[1..<>.len() - 1])), }; Pattern: Pattern = { @@ -122,7 +122,7 @@ pub Expr: Expr<'a> = { } }, "in" => { - Expr::Let(bind, expr) + Expr::Let(allocator.let_binding_arena.alloc(bind), expr) }, "match" "with" "end" => Expr::Match(expr, allocator.alternative_arena.alloc_extend(alts.into_iter())), diff --git a/vm/src/core/interpreter.rs b/vm/src/core/interpreter.rs index cf82814864..f24766aa09 100644 --- a/vm/src/core/interpreter.rs +++ b/vm/src/core/interpreter.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; use crate::base::{ - ast::{Literal, TypedIdent}, + ast::TypedIdent, fnv::FnvSet, kind::{ArcKind, KindEnv}, merge::merge_iter, @@ -14,7 +14,7 @@ use crate::{ core::{ self, optimize::{walk_expr_alloc, DifferentLifetime, ExprProducer, SameLifetime, Visitor}, - Allocator, CExpr, Closure, Expr, LetBinding, Named, Pattern, + Allocator, CExpr, Closure, Expr, LetBinding, Literal, Named, Pattern, }, types::*, Error, Result, @@ -443,7 +443,7 @@ impl<'a, 'e> Compiler<'a, 'e> { }; if variable_in_value { value = Some(Reduced::Local(&*self.allocator.arena.alloc(Expr::Let( - bind, + self.allocator.let_binding_arena.alloc(bind), value.map_or(expr, |value| value.into_local(allocator)), )))); } @@ -524,7 +524,7 @@ impl<'a, 'e> Compiler<'a, 'e> { if let Some(expr) = new_named { self.bindings.push(LetBinding { expr, - ..let_binding.clone() + ..(*let_binding).clone() }); } return Ok(TailCall::Tail(body)); diff --git a/vm/src/core/mod.rs b/vm/src/core/mod.rs index bc58e1a637..2a4ea5cf06 100644 --- a/vm/src/core/mod.rs +++ b/vm/src/core/mod.rs @@ -40,7 +40,7 @@ mod pretty; use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt, iter::once, mem}; -use {itertools::Itertools, smallvec::SmallVec}; +use {itertools::Itertools, ordered_float::NotNan, smallvec::SmallVec}; use self::{ optimize::{walk_expr_alloc, SameLifetime, Visitor}, @@ -48,7 +48,7 @@ use self::{ }; use crate::base::{ - ast::{self, Literal, SpannedExpr, SpannedPattern, Typed, TypedIdent}, + ast::{self, SpannedExpr, SpannedPattern, Typed, TypedIdent}, fnv::{FnvMap, FnvSet}, pos::{spanned, BytePos, Span, Spanned}, resolve::remove_aliases_cow, @@ -83,12 +83,21 @@ pub struct LetBinding<'a> { pub span_start: BytePos, } +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum Literal { + Byte(u8), + Int(i64), + Float(NotNan), + String(Box), + Char(char), +} + #[derive(Clone, Debug, PartialEq)] pub enum Pattern { Constructor(TypedIdent, Vec>), Record(Vec<(TypedIdent, Option)>), Ident(TypedIdent), - Literal(ast::Literal), + Literal(Literal), } #[derive(Clone, Debug, PartialEq)] @@ -103,10 +112,10 @@ pub type CExpr<'a> = &'a Expr<'a>; pub enum Expr<'a> { Const(Literal, Span), Ident(TypedIdent, Span), - Call(&'a Expr<'a>, &'a [Expr<'a>]), + Call(CExpr<'a>, &'a [Expr<'a>]), Data(TypedIdent, &'a [Expr<'a>], BytePos), - Let(LetBinding<'a>, &'a Expr<'a>), - Match(&'a Expr<'a>, &'a [Alternative<'a>]), + Let(&'a LetBinding<'a>, CExpr<'a>), + Match(CExpr<'a>, &'a [Alternative<'a>]), } #[cfg(feature = "test")] @@ -145,6 +154,32 @@ impl<'a> fmt::Display for Expr<'a> { } } +impl Literal { + fn from_ast(literal: &ast::Literal) -> Self { + match literal { + ast::Literal::Byte(x) => Literal::Byte(*x), + ast::Literal::Int(x) => Literal::Int(*x), + ast::Literal::Float(x) => Literal::Float(*x), + ast::Literal::String(x) => Literal::String(Box::from(&x[..])), + ast::Literal::Char(x) => Literal::Char(*x), + } + } +} + +impl Typed for Literal { + type Ident = Symbol; + + fn try_type_of(&self, _: &TypeEnv) -> Result { + Ok(match *self { + Literal::Int(_) => Type::int(), + Literal::Float(_) => Type::float(), + Literal::Byte(_) => Type::byte(), + Literal::String(_) => Type::string(), + Literal::Char(_) => Type::char(), + }) + } +} + #[derive(Default)] #[must_use] struct Binder<'a> { @@ -170,18 +205,21 @@ impl<'a> Binder<'a> { ident_expr } - fn into_expr(self, arena: &'a Arena>, expr: Expr<'a>) -> Expr<'a> { - self.bindings - .into_iter() - .rev() - .fold(expr, |expr, bind| Expr::Let(bind, arena.alloc(expr))) + fn into_expr(self, allocator: &'a Allocator<'a>, expr: Expr<'a>) -> Expr<'a> { + self.bindings.into_iter().rev().fold(expr, |expr, bind| { + Expr::Let( + allocator.let_binding_arena.alloc(bind), + allocator.arena.alloc(expr), + ) + }) } - fn into_expr_ref(self, arena: &'a Arena>, expr: &'a Expr<'a>) -> &'a Expr<'a> { - self.bindings - .into_iter() - .rev() - .fold(expr, |expr, bind| arena.alloc(Expr::Let(bind, expr))) + fn into_expr_ref(self, allocator: &'a Allocator<'a>, expr: &'a Expr<'a>) -> &'a Expr<'a> { + self.bindings.into_iter().rev().fold(expr, |expr, bind| { + allocator + .arena + .alloc(Expr::Let(allocator.let_binding_arena.alloc(bind), expr)) + }) } } @@ -247,6 +285,7 @@ pub use self::internal::CoreExpr; pub struct Allocator<'a> { pub arena: Arena>, pub alternative_arena: Arena>, + pub let_binding_arena: Arena>, } impl<'a> Allocator<'a> { @@ -254,6 +293,7 @@ impl<'a> Allocator<'a> { Allocator { arena: Arena::new(), alternative_arena: Arena::new(), + let_binding_arena: Arena::new(), } } } @@ -423,11 +463,11 @@ impl<'a, 'e> Translator<'a, 'e> { let result = self.translate(last); prefix.iter().rev().fold(result, |result, expr| { Expr::Let( - LetBinding { + self.allocator.let_binding_arena.alloc(LetBinding { name: self.dummy_symbol.clone(), expr: Named::Expr(self.translate_alloc(expr)), span_start: expr.span.start(), - }, + }), arena.alloc(result), ) }) @@ -500,7 +540,7 @@ impl<'a, 'e> Translator<'a, 'e> { self.translate_let(binds, self.translate(tail), expr.span.start()) } - ast::Expr::Literal(ref literal) => Expr::Const(literal.clone(), expr.span), + ast::Expr::Literal(ref literal) => Expr::Const(Literal::from_ast(literal), expr.span), ast::Expr::Match(ref expr, ref alts) => { let expr = self.translate_alloc(expr); @@ -613,7 +653,7 @@ impl<'a, 'e> Translator<'a, 'e> { arena.alloc_extend(args), expr.span.start(), ); - binder.into_expr(arena, record_constructor) + binder.into_expr(&self.allocator, record_constructor) } ast::Expr::Tuple { ref elems, .. } => { @@ -689,7 +729,7 @@ impl<'a, 'e> Translator<'a, 'e> { let f = self.translate_alloc(flat_map_id); binder.into_expr( - arena, + &self.allocator, Expr::Call( f, arena.alloc_extend(Some(lambda).into_iter().chain(Some(bound_ident))), @@ -759,12 +799,12 @@ impl<'a, 'e> Translator<'a, 'e> { }) .collect(); Expr::Let( - LetBinding { + self.allocator.let_binding_arena.alloc(LetBinding { // TODO name: self.dummy_symbol.clone(), expr: Named::Recursive(closures), span_start: span_start, - }, + }), arena.alloc(tail), ) } else { @@ -794,11 +834,11 @@ impl<'a, 'e> Translator<'a, 'e> { }]) }; Expr::Let( - LetBinding { + self.allocator.let_binding_arena.alloc(LetBinding { name: name, expr: named, span_start: bind.expr.span.start(), - }, + }), arena.alloc(tail), ) }) @@ -900,7 +940,7 @@ impl<'a, 'e> Translator<'a, 'e> { ) -> Expr<'a> { let arena = &self.allocator.arena; Expr::Let( - LetBinding { + self.allocator.let_binding_arena.alloc(LetBinding { name: name.clone(), expr: Named::Recursive(vec![Closure { pos, @@ -909,7 +949,7 @@ impl<'a, 'e> Translator<'a, 'e> { expr: body, }]), span_start: span.start(), - }, + }), arena.alloc(Expr::Ident(name, span)), ) } @@ -1302,7 +1342,7 @@ impl<'a, 'e> PatternTranslator<'a, 'e> { .into_iter() .map(|key| { let equations = &groups[key]; - let pattern = Pattern::Literal(key.clone()); + let pattern = Pattern::Literal(Literal::from_ast(key)); let new_equations = equations .iter() @@ -1374,11 +1414,11 @@ impl<'a, 'e> PatternTranslator<'a, 'e> { .arena .alloc(Expr::Ident(name.clone(), expr.span())); Expr::Let( - LetBinding { + self.0.allocator.let_binding_arena.alloc(LetBinding { name: name, expr: Named::Expr(expr), span_start: expr.span().start(), - }, + }), self.translate(default, &[id_expr], equations), ) } @@ -1480,8 +1520,8 @@ impl<'a, 'e> PatternTranslator<'a, 'e> { equations.iter().format(",\n"), expr ); - let arena = &self.0.allocator.arena; - binder.into_expr_ref(arena, expr) + let allocator = &self.0.allocator; + binder.into_expr_ref(allocator, expr) } fn extract_ident(&self, index: usize, pattern: &ast::Pattern) -> TypedIdent { @@ -2208,4 +2248,10 @@ mod tests { "#; check_translation(expr_str, expected_str); } + + #[test] + fn expr_size() { + let s = std::mem::size_of::(); + assert!(s <= 40, "{} is to large for expressions", s); + } } diff --git a/vm/src/core/optimize.rs b/vm/src/core/optimize.rs index 07be7f4639..96b4eb5bd6 100644 --- a/vm/src/core/optimize.rs +++ b/vm/src/core/optimize.rs @@ -110,14 +110,14 @@ impl<'a> Visitor<'a, 'a> for RecognizeUnnecessaryAllocation<'a> { }) .unwrap_or_else(|| Symbol::from("dummy")); let new_expr = Expr::Let( - LetBinding { + self_.allocator.let_binding_arena.alloc(LetBinding { name: TypedIdent { name: pattern_field.clone(), typ: field.typ.clone(), }, expr: Named::Expr(expr), span_start: pos::BytePos::default(), - }, + }), next_expr, ); &*self_.allocator().arena.alloc(new_expr) @@ -279,7 +279,7 @@ where } } -fn walk_bind<'a, 'b, V>(visitor: &mut V, bind: &LetBinding<'b>) -> Option> +fn walk_bind<'a, 'b, V>(visitor: &mut V, bind: &LetBinding<'b>) -> Option<&'a LetBinding<'a>> where V: ?Sized + Visitor<'a, 'b>, { @@ -305,10 +305,15 @@ where .map(Named::Recursive), Named::Expr(bind_expr) => visitor.visit_expr(bind_expr).map(Named::Expr), }; - new_named.map(|named| LetBinding { - name: bind.name.clone(), - expr: named, - span_start: bind.span_start, + new_named.map(|named| { + &*allocator + .expect("Allocator") + .let_binding_arena + .alloc(LetBinding { + name: bind.name.clone(), + expr: named, + span_start: bind.span_start, + }) }) }