Add code improvement to IR and implement simple numeric constant folding.
This commit is contained in:
parent
8942c04c6e
commit
596ee84f44
3 changed files with 285 additions and 16 deletions
|
@ -9,7 +9,7 @@ use huia_parser::ast::{Location, NodeType, Term, Value};
|
||||||
|
|
||||||
/// The Builder is a simple stack machine for converting AST into IR.
|
/// The Builder is a simple stack machine for converting AST into IR.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Builder {
|
pub struct Builder {
|
||||||
blocks: Vec<Block>,
|
blocks: Vec<Block>,
|
||||||
types: Vec<TyIdx>,
|
types: Vec<TyIdx>,
|
||||||
}
|
}
|
||||||
|
@ -553,7 +553,7 @@ impl Builder {
|
||||||
self.peek_block_mut().unwrap().push(ir);
|
self.peek_block_mut().unwrap().push(ir);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_ir(&mut self) -> Option<IR> {
|
pub fn pop_ir(&mut self) -> Option<IR> {
|
||||||
self.peek_block_mut().unwrap().pop()
|
self.peek_block_mut().unwrap().pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
100
huia-compiler/src/ir/constant_folding.rs
Normal file
100
huia-compiler/src/ir/constant_folding.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use crate::context::Context;
|
||||||
|
use crate::ir::{IRKind, Val, IR};
|
||||||
|
use huia_parser::ast::binary::Operator as BinOp;
|
||||||
|
use std::f64;
|
||||||
|
|
||||||
|
/// Perform constant folding on the provided node, or not.
|
||||||
|
pub fn pass(ir: &mut IR, context: &mut Context) {
|
||||||
|
if ir.is_infix() {
|
||||||
|
let (op, lhs, rhs) = ir.get_infix().unwrap();
|
||||||
|
if !(lhs.is_constant() && rhs.is_constant()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lhs = lhs.get_value().unwrap();
|
||||||
|
let rhs = rhs.get_value().unwrap();
|
||||||
|
|
||||||
|
if lhs.is_integer() && rhs.is_integer() {
|
||||||
|
let lhs = lhs.integer().unwrap();
|
||||||
|
let rhs = rhs.integer().unwrap();
|
||||||
|
let value = perform_i64(&op, lhs, rhs);
|
||||||
|
if let Some(value) = value {
|
||||||
|
ir.set_kind(IRKind::Constant(Val::Integer(value)));
|
||||||
|
let idx = context.constant_string("Huia.Native.Integer");
|
||||||
|
ir.set_result_type(context.reference_type(&idx, ir.location.clone()));
|
||||||
|
}
|
||||||
|
} else if lhs.is_float() && rhs.is_float() {
|
||||||
|
let lhs = lhs.float().unwrap();
|
||||||
|
let rhs = rhs.float().unwrap();
|
||||||
|
let value = perform_f64(&op, lhs, rhs);
|
||||||
|
if let Some(value) = value {
|
||||||
|
ir.set_kind(IRKind::Constant(Val::Float(value)));
|
||||||
|
let idx = context.constant_string("Huia.Native.Float");
|
||||||
|
ir.set_result_type(context.reference_type(&idx, ir.location.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform_i64(op: &BinOp, lhs: i64, rhs: i64) -> Option<i64> {
|
||||||
|
match op {
|
||||||
|
BinOp::BitwiseAnd => Some(lhs & rhs),
|
||||||
|
BinOp::BitwiseOr => Some(lhs | rhs),
|
||||||
|
BinOp::BitwiseXor => Some(lhs ^ rhs),
|
||||||
|
BinOp::Divide => Some(lhs / rhs),
|
||||||
|
BinOp::Exponent => Some(lhs.pow(rhs as u32)), // FIXME: Potentially lossy conversion.
|
||||||
|
BinOp::Minus => Some(lhs - rhs),
|
||||||
|
BinOp::Modulus => Some(lhs % rhs),
|
||||||
|
BinOp::Multiply => Some(lhs * rhs),
|
||||||
|
BinOp::Plus => Some(lhs + rhs),
|
||||||
|
BinOp::ShiftLeft => Some(lhs << rhs),
|
||||||
|
BinOp::ShiftRight => Some(lhs >> rhs),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn perform_f64(op: &BinOp, lhs: f64, rhs: f64) -> Option<f64> {
|
||||||
|
match op {
|
||||||
|
BinOp::Divide => Some(lhs / rhs),
|
||||||
|
BinOp::Minus => Some(lhs - rhs),
|
||||||
|
BinOp::Modulus => Some(lhs % rhs),
|
||||||
|
BinOp::Multiply => Some(lhs * rhs),
|
||||||
|
BinOp::Plus => Some(lhs + rhs),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::ir::builder::Builder;
|
||||||
|
use huia_parser::ast::Term;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn integer_folding() {
|
||||||
|
let term = Term::input("3 * 2 + 13 / 2").unwrap()[0].clone();
|
||||||
|
let mut context = Context::test();
|
||||||
|
let mut builder = Builder::default();
|
||||||
|
|
||||||
|
builder.build(term, &mut context);
|
||||||
|
let mut ir = builder.pop_ir().unwrap();
|
||||||
|
ir.improve(&mut context, pass);
|
||||||
|
|
||||||
|
assert!(ir.is_constant());
|
||||||
|
assert_eq!(ir.get_value().unwrap().integer().unwrap(), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn float_folding() {
|
||||||
|
let term = Term::input("1.5 * 2.0 / 3.0").unwrap()[0].clone();
|
||||||
|
let mut context = Context::test();
|
||||||
|
let mut builder = Builder::default();
|
||||||
|
|
||||||
|
builder.build(term, &mut context);
|
||||||
|
let mut ir = builder.pop_ir().unwrap();
|
||||||
|
ir.improve(&mut context, pass);
|
||||||
|
|
||||||
|
assert!(ir.is_constant());
|
||||||
|
assert!(ir.get_value().unwrap().float().unwrap() - 1.0 < f64::EPSILON);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
mod builder;
|
mod builder;
|
||||||
|
mod constant_folding;
|
||||||
|
|
||||||
use crate::block::BlockIdx;
|
use crate::block::BlockIdx;
|
||||||
|
use crate::context::Context;
|
||||||
use crate::function::FunctionIdx;
|
use crate::function::FunctionIdx;
|
||||||
use crate::location::Location;
|
use crate::location::Location;
|
||||||
use crate::stable::StringIdx;
|
use crate::stable::StringIdx;
|
||||||
|
@ -11,7 +13,7 @@ use huia_parser::ast::unary::Operator as UnOp;
|
||||||
/// Describes the intermediate representation as converted from the Parser's AST.
|
/// Describes the intermediate representation as converted from the Parser's AST.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct IR {
|
pub struct IR {
|
||||||
location: Option<Location>,
|
location: Location,
|
||||||
result_type: TyIdx,
|
result_type: TyIdx,
|
||||||
kind: IRKind,
|
kind: IRKind,
|
||||||
}
|
}
|
||||||
|
@ -19,7 +21,7 @@ pub struct IR {
|
||||||
impl IR {
|
impl IR {
|
||||||
pub fn new_call(result_type: TyIdx, location: Location, arguments: Vec<IR>) -> IR {
|
pub fn new_call(result_type: TyIdx, location: Location, arguments: Vec<IR>) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::Call(arguments),
|
kind: IRKind::Call(arguments),
|
||||||
}
|
}
|
||||||
|
@ -27,7 +29,7 @@ impl IR {
|
||||||
|
|
||||||
pub fn new_constant(result_type: TyIdx, location: Location, value: Val) -> IR {
|
pub fn new_constant(result_type: TyIdx, location: Location, value: Val) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::Constant(value),
|
kind: IRKind::Constant(value),
|
||||||
}
|
}
|
||||||
|
@ -39,7 +41,7 @@ impl IR {
|
||||||
properties: Vec<(StringIdx, IR)>,
|
properties: Vec<(StringIdx, IR)>,
|
||||||
) -> IR {
|
) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::Constructor(properties),
|
kind: IRKind::Constructor(properties),
|
||||||
}
|
}
|
||||||
|
@ -47,7 +49,7 @@ impl IR {
|
||||||
|
|
||||||
pub fn new_function_ref(result_type: TyIdx, location: Location, function: FunctionIdx) -> IR {
|
pub fn new_function_ref(result_type: TyIdx, location: Location, function: FunctionIdx) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::FunctionRef(function),
|
kind: IRKind::FunctionRef(function),
|
||||||
}
|
}
|
||||||
|
@ -55,7 +57,7 @@ impl IR {
|
||||||
|
|
||||||
pub fn new_get_local(result_type: TyIdx, location: Location, name: StringIdx) -> IR {
|
pub fn new_get_local(result_type: TyIdx, location: Location, name: StringIdx) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::GetLocal(name),
|
kind: IRKind::GetLocal(name),
|
||||||
}
|
}
|
||||||
|
@ -63,7 +65,7 @@ impl IR {
|
||||||
|
|
||||||
pub fn new_get_property(result_type: TyIdx, location: Location, name: StringIdx) -> IR {
|
pub fn new_get_property(result_type: TyIdx, location: Location, name: StringIdx) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::GetProperty(name),
|
kind: IRKind::GetProperty(name),
|
||||||
}
|
}
|
||||||
|
@ -77,7 +79,7 @@ impl IR {
|
||||||
negative: BlockIdx,
|
negative: BlockIdx,
|
||||||
) -> IR {
|
) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::If(Box::new(test), positive, negative),
|
kind: IRKind::If(Box::new(test), positive, negative),
|
||||||
}
|
}
|
||||||
|
@ -87,7 +89,7 @@ impl IR {
|
||||||
let lhs = Box::new(lhs);
|
let lhs = Box::new(lhs);
|
||||||
let rhs = Box::new(rhs);
|
let rhs = Box::new(rhs);
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::Infix(op, lhs, rhs),
|
kind: IRKind::Infix(op, lhs, rhs),
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,7 @@ impl IR {
|
||||||
|
|
||||||
pub fn new_set_local(result_type: TyIdx, location: Location, name: StringIdx, value: IR) -> IR {
|
pub fn new_set_local(result_type: TyIdx, location: Location, name: StringIdx, value: IR) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::SetLocal(name, Box::new(value)),
|
kind: IRKind::SetLocal(name, Box::new(value)),
|
||||||
}
|
}
|
||||||
|
@ -107,7 +109,7 @@ impl IR {
|
||||||
properties: Vec<(StringIdx, IR)>,
|
properties: Vec<(StringIdx, IR)>,
|
||||||
) -> IR {
|
) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::SetProperties(properties),
|
kind: IRKind::SetProperties(properties),
|
||||||
}
|
}
|
||||||
|
@ -115,7 +117,7 @@ impl IR {
|
||||||
|
|
||||||
pub fn new_type_reference(result_type: TyIdx, location: Location, ty: TyIdx) -> IR {
|
pub fn new_type_reference(result_type: TyIdx, location: Location, ty: TyIdx) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::TypeReference(ty),
|
kind: IRKind::TypeReference(ty),
|
||||||
}
|
}
|
||||||
|
@ -123,7 +125,7 @@ impl IR {
|
||||||
|
|
||||||
pub fn new_unary(result_type: TyIdx, location: Location, op: UnOp, rhs: IR) -> IR {
|
pub fn new_unary(result_type: TyIdx, location: Location, op: UnOp, rhs: IR) -> IR {
|
||||||
IR {
|
IR {
|
||||||
location: Some(location),
|
location,
|
||||||
result_type,
|
result_type,
|
||||||
kind: IRKind::Unary(op, Box::new(rhs)),
|
kind: IRKind::Unary(op, Box::new(rhs)),
|
||||||
}
|
}
|
||||||
|
@ -136,6 +138,74 @@ impl IR {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_infix(&self) -> Option<(&BinOp, &IR, &IR)> {
|
||||||
|
match self.kind {
|
||||||
|
IRKind::Infix(ref op, ref lhs, ref rhs) => Some((op, lhs, rhs)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provided an "improver" function, possibly improve this node. Implies
|
||||||
|
/// improvement of child nodes as required.
|
||||||
|
///
|
||||||
|
/// Child nodes are visited *before* the receiving node to ensure that
|
||||||
|
/// improvements can bubble up.
|
||||||
|
pub fn improve<F: Fn(&mut IR, &mut Context)>(
|
||||||
|
&mut self,
|
||||||
|
mut context: &mut Context,
|
||||||
|
improver: F,
|
||||||
|
) {
|
||||||
|
match self.kind {
|
||||||
|
IRKind::Call(ref mut args) => {
|
||||||
|
for mut arg in args.iter_mut() {
|
||||||
|
improver(&mut arg, &mut context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IRKind::Constant(ref mut value) => match value {
|
||||||
|
Val::Array(ref mut elements) => {
|
||||||
|
for mut element in elements.iter_mut() {
|
||||||
|
improver(&mut element, &mut context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Val::Map(ref mut elements) => {
|
||||||
|
for (ref mut key, ref mut value) in elements.iter_mut() {
|
||||||
|
improver(key, &mut context);
|
||||||
|
improver(value, &mut context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
IRKind::Constructor(ref mut properties) => {
|
||||||
|
for (_key, ref mut value) in properties.iter_mut() {
|
||||||
|
improver(value, &mut context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IRKind::FunctionRef(..) => (),
|
||||||
|
IRKind::GetLocal(..) => (),
|
||||||
|
IRKind::GetProperty(..) => (),
|
||||||
|
IRKind::If(ref mut test, ..) => {
|
||||||
|
improver(test, &mut context);
|
||||||
|
}
|
||||||
|
IRKind::Infix(_, ref mut lhs, ref mut rhs) => {
|
||||||
|
improver(lhs, &mut context);
|
||||||
|
improver(rhs, &mut context);
|
||||||
|
}
|
||||||
|
IRKind::SetLocal(_, ref mut rhs) => {
|
||||||
|
improver(rhs, &mut context);
|
||||||
|
}
|
||||||
|
IRKind::SetProperties(ref mut properties) => {
|
||||||
|
for (_key, ref mut value) in properties.iter_mut() {
|
||||||
|
improver(value, &mut context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IRKind::TypeReference(..) => (),
|
||||||
|
IRKind::Unary(_, ref mut rhs) => {
|
||||||
|
improver(rhs, &mut context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
improver(self, &mut context);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_call(&self) -> bool {
|
pub fn is_call(&self) -> bool {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
IRKind::Call(..) => true,
|
IRKind::Call(..) => true,
|
||||||
|
@ -150,6 +220,13 @@ impl IR {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_constant_float(&self) -> bool {
|
||||||
|
match self.kind {
|
||||||
|
IRKind::Constant(ref value) => value.is_float(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_constuctor(&self) -> bool {
|
pub fn is_constuctor(&self) -> bool {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
IRKind::Constructor(..) => true,
|
IRKind::Constructor(..) => true,
|
||||||
|
@ -209,10 +286,25 @@ impl IR {
|
||||||
pub fn result_type(&self) -> TyIdx {
|
pub fn result_type(&self) -> TyIdx {
|
||||||
self.result_type.clone()
|
self.result_type.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> Option<&Val> {
|
||||||
|
match self.kind {
|
||||||
|
IRKind::Constant(ref value) => Some(value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_kind(&mut self, kind: IRKind) {
|
||||||
|
self.kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_result_type(&mut self, ty: TyIdx) {
|
||||||
|
self.result_type = ty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum IRKind {
|
pub enum IRKind {
|
||||||
/// Call top of the stack with arguments.
|
/// Call top of the stack with arguments.
|
||||||
Call(Vec<IR>),
|
Call(Vec<IR>),
|
||||||
/// A constant value of the specified type.
|
/// A constant value of the specified type.
|
||||||
|
@ -250,3 +342,80 @@ pub enum Val {
|
||||||
Array(Vec<IR>),
|
Array(Vec<IR>),
|
||||||
Map(Vec<(IR, IR)>),
|
Map(Vec<(IR, IR)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Val {
|
||||||
|
pub fn is_atom(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Val::Atom(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_boolean(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Val::Boolean(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_float(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Val::Float(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_integer(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Val::Integer(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_string(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Val::String(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_array(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Val::Array(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_map(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Val::Map(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array(&self) -> Option<Vec<&IR>> {
|
||||||
|
match self {
|
||||||
|
Val::Array(ref elements) => Some(elements.iter().collect()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map(&self) -> Option<Vec<&(IR, IR)>> {
|
||||||
|
match self {
|
||||||
|
Val::Map(ref elements) => Some(elements.iter().collect()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn float(&self) -> Option<f64> {
|
||||||
|
match self {
|
||||||
|
Val::Float(ref value) => Some(*value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn integer(&self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
Val::Integer(ref value) => Some(*value),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in a new issue