504 lines
19 KiB
Rust
504 lines
19 KiB
Rust
mod util;
|
|
|
|
use crate::ast::NodeId;
|
|
use crate::ast::assign_statement::AssignStatement;
|
|
use crate::ast::binary_expression::BinaryOperation;
|
|
use crate::ast::call::Call;
|
|
use crate::ast::compilation_unit::CompilationUnit;
|
|
use crate::ast::expression::Expression;
|
|
use crate::ast::expression_statement::ExpressionStatement;
|
|
use crate::ast::function::Function;
|
|
use crate::ast::let_statement::LetStatement;
|
|
use crate::ast::statement::Statement;
|
|
use crate::ir::ir_assign::IrAssign;
|
|
use crate::ir::ir_binary_operation::{IrBinaryOperation, IrBinaryOperator};
|
|
use crate::ir::ir_block::{IrBlock, IrBlockId};
|
|
use crate::ir::ir_call::IrCall;
|
|
use crate::ir::ir_expression::IrExpression;
|
|
use crate::ir::ir_function::IrFunction;
|
|
use crate::ir::ir_operation::IrOperation;
|
|
use crate::ir::ir_parameter::{IrParameter, IrParameterId};
|
|
use crate::ir::ir_return::IrReturn;
|
|
use crate::ir::ir_statement::IrStatement;
|
|
use crate::ir::ir_type_info::IrTypeInfo;
|
|
use crate::ir::ir_variable::{IrVariable, IrVariableId};
|
|
use crate::lowering::util::{return_type_info_to_ir_type_info, to_ir_type_info};
|
|
use crate::semantic_analysis::analysis_context::AnalysisContext;
|
|
use crate::semantic_analysis::symbol::{Symbol, SymbolId};
|
|
use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId};
|
|
use std::collections::HashMap;
|
|
use std::ops::Neg;
|
|
use std::rc::Rc;
|
|
|
|
pub struct LowerToIrResult {
|
|
pub functions: Vec<IrFunction>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct LowerToIrContext<'a> {
|
|
symbols: &'a [Symbol],
|
|
nodes_to_symbols: &'a HashMap<NodeId, SymbolId>,
|
|
type_infos: &'a [TypeInfo],
|
|
symbols_to_type_infos: &'a HashMap<SymbolId, TypeInfoId>,
|
|
nodes_to_type_infos: &'a HashMap<NodeId, TypeInfoId>,
|
|
ir_functions: Vec<IrFunction>,
|
|
}
|
|
|
|
pub fn lower_to_ir_compilation_unit(
|
|
compilation_unit: &CompilationUnit,
|
|
analysis_context: &AnalysisContext,
|
|
) -> LowerToIrResult {
|
|
let mut ctx = LowerToIrContext {
|
|
symbols: analysis_context.symbols(),
|
|
nodes_to_symbols: analysis_context.nodes_to_symbols(),
|
|
type_infos: analysis_context.type_infos(),
|
|
symbols_to_type_infos: analysis_context.symbols_to_type_infos(),
|
|
nodes_to_type_infos: analysis_context.nodes_to_type_infos(),
|
|
ir_functions: Vec::new(),
|
|
};
|
|
|
|
for function in compilation_unit.functions() {
|
|
lower_to_ir_function(function, &mut ctx);
|
|
}
|
|
|
|
LowerToIrResult {
|
|
functions: ctx.ir_functions,
|
|
}
|
|
}
|
|
|
|
pub fn lower_to_ir_synthetic_function(
|
|
statement: &Statement,
|
|
analysis_ctx: &AnalysisContext,
|
|
fqn: Rc<str>,
|
|
) -> IrFunction {
|
|
let mut ctx = LowerToIrContext {
|
|
symbols: analysis_ctx.symbols(),
|
|
nodes_to_symbols: analysis_ctx.nodes_to_symbols(),
|
|
type_infos: analysis_ctx.type_infos(),
|
|
symbols_to_type_infos: analysis_ctx.symbols_to_type_infos(),
|
|
nodes_to_type_infos: analysis_ctx.nodes_to_type_infos(),
|
|
ir_functions: Vec::new(),
|
|
};
|
|
|
|
let mut fn_ctx = LowerToIrFunctionContext::new();
|
|
|
|
lower_to_ir_statement(statement, &mut ctx, &mut fn_ctx, true);
|
|
fn_ctx.finish_block();
|
|
|
|
// infer return type from statement
|
|
let maybe_return_ir_type_info = match statement {
|
|
Statement::Let(_) | Statement::Assign(_) => None,
|
|
Statement::Expression(expression_statement) => {
|
|
let type_info_id =
|
|
ctx.nodes_to_type_infos[&expression_statement.expression().node_id()];
|
|
let type_info = &ctx.type_infos[type_info_id];
|
|
return_type_info_to_ir_type_info(type_info)
|
|
}
|
|
};
|
|
|
|
IrFunction::new(
|
|
fqn,
|
|
fn_ctx.ir_parameters,
|
|
fn_ctx.ir_variables,
|
|
maybe_return_ir_type_info,
|
|
fn_ctx.blocks,
|
|
)
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct LowerToIrFunctionContext {
|
|
blocks: Vec<IrBlock>,
|
|
ir_variables: Vec<IrVariable>,
|
|
symbols_to_variables: HashMap<SymbolId, IrVariableId>,
|
|
ir_parameters: Vec<IrParameter>,
|
|
symbols_to_parameters: HashMap<SymbolId, IrParameterId>,
|
|
current_block_statements: Vec<IrStatement>,
|
|
t_var_counter: usize,
|
|
}
|
|
|
|
impl LowerToIrFunctionContext {
|
|
fn new() -> Self {
|
|
Self {
|
|
blocks: Vec::new(),
|
|
ir_variables: Vec::new(),
|
|
symbols_to_variables: HashMap::new(),
|
|
ir_parameters: Vec::new(),
|
|
symbols_to_parameters: HashMap::new(),
|
|
current_block_statements: Vec::new(),
|
|
t_var_counter: 0,
|
|
}
|
|
}
|
|
|
|
fn current_block_id(&self) -> IrBlockId {
|
|
self.blocks.len()
|
|
}
|
|
|
|
fn finish_block(&mut self) {
|
|
let current_block_id = self.current_block_id();
|
|
let ir_block = IrBlock::new(
|
|
current_block_id,
|
|
&format!("#{}", current_block_id),
|
|
std::mem::take(&mut self.current_block_statements),
|
|
);
|
|
self.blocks.push(ir_block);
|
|
}
|
|
|
|
fn insert_ir_parameter(
|
|
&mut self,
|
|
ir_parameter: IrParameter,
|
|
maybe_associated_symbol_id: Option<SymbolId>,
|
|
) -> IrParameterId {
|
|
self.ir_parameters.push(ir_parameter);
|
|
let ir_parameter_id = self.ir_parameters.len() - 1;
|
|
if let Some(associated_symbol_id) = maybe_associated_symbol_id {
|
|
self.symbols_to_parameters
|
|
.insert(associated_symbol_id, ir_parameter_id);
|
|
}
|
|
ir_parameter_id
|
|
}
|
|
|
|
fn insert_ir_variable(
|
|
&mut self,
|
|
ir_variable: IrVariable,
|
|
maybe_associated_symbol_id: Option<SymbolId>,
|
|
) -> IrVariableId {
|
|
self.ir_variables.push(ir_variable);
|
|
let ir_variable_id = self.ir_variables.len() - 1;
|
|
if let Some(associated_symbol_id) = maybe_associated_symbol_id {
|
|
self.symbols_to_variables
|
|
.insert(associated_symbol_id, ir_variable_id);
|
|
}
|
|
ir_variable_id
|
|
}
|
|
|
|
fn make_t_var(&mut self, ir_type_info: IrTypeInfo) -> IrVariableId {
|
|
let t_var_id = self.t_var_counter;
|
|
self.t_var_counter += 1;
|
|
let t_var_ir_variable = IrVariable::new(&format!("t_{}", t_var_id), ir_type_info);
|
|
self.insert_ir_variable(t_var_ir_variable, None)
|
|
}
|
|
}
|
|
|
|
fn lower_to_ir_function(function: &Function, ctx: &mut LowerToIrContext) {
|
|
let mut fn_ctx = LowerToIrFunctionContext::new();
|
|
|
|
lower_to_ir_parameters(function, ctx, &mut fn_ctx);
|
|
|
|
// get various function info
|
|
let function_symbol_id = ctx.nodes_to_symbols[&function.node_id()];
|
|
let function_symbol = match &ctx.symbols[function_symbol_id] {
|
|
Symbol::Function(function_symbol) => function_symbol,
|
|
_ => panic!("Expected FunctionSymbol"),
|
|
};
|
|
let function_type_info_id = ctx.symbols_to_type_infos[&function_symbol_id];
|
|
let function_type_info = match &ctx.type_infos[function_type_info_id] {
|
|
TypeInfo::Function(function_type_info) => function_type_info,
|
|
_ => panic!("Expected FunctionTypeInfo"),
|
|
};
|
|
let return_type_info = &ctx.type_infos[function_type_info.return_type_id()];
|
|
let is_void_function = match return_type_info {
|
|
TypeInfo::Void => true,
|
|
_ => false,
|
|
};
|
|
|
|
// lower function body
|
|
let n_statements = function.statements().len();
|
|
for (i, statement) in function.statements().iter().enumerate() {
|
|
let can_return_value = i == n_statements - 1 && !is_void_function;
|
|
lower_to_ir_statement(statement, ctx, &mut fn_ctx, can_return_value);
|
|
}
|
|
fn_ctx.finish_block();
|
|
|
|
let ir_function = IrFunction::new(
|
|
function_symbol.fqn_owned(),
|
|
fn_ctx.ir_parameters,
|
|
fn_ctx.ir_variables,
|
|
return_type_info_to_ir_type_info(return_type_info),
|
|
fn_ctx.blocks,
|
|
);
|
|
|
|
ctx.ir_functions.push(ir_function);
|
|
}
|
|
|
|
fn lower_to_ir_parameters(
|
|
function: &Function,
|
|
ctx: &LowerToIrContext,
|
|
fn_ctx: &mut LowerToIrFunctionContext,
|
|
) {
|
|
let n_parameters = function.parameters().len() as isize;
|
|
for (i, parameter) in function.parameters().iter().enumerate() {
|
|
let parameter_type_info_id = ctx.nodes_to_type_infos[¶meter.node_id()];
|
|
let parameter_type_info = &ctx.type_infos[parameter_type_info_id];
|
|
let ir_parameter = IrParameter::new(
|
|
parameter.declared_name(),
|
|
to_ir_type_info(parameter_type_info),
|
|
n_parameters.neg() + (i as isize),
|
|
);
|
|
fn_ctx.insert_ir_parameter(
|
|
ir_parameter,
|
|
Some(ctx.nodes_to_symbols[¶meter.node_id()]),
|
|
);
|
|
}
|
|
}
|
|
|
|
fn lower_to_ir_statement(
|
|
statement: &Statement,
|
|
ctx: &LowerToIrContext,
|
|
fn_ctx: &mut LowerToIrFunctionContext,
|
|
can_return_value: bool,
|
|
) {
|
|
match statement {
|
|
Statement::Let(let_statement) => {
|
|
lower_to_ir_let_statement(let_statement, ctx, fn_ctx);
|
|
}
|
|
Statement::Expression(expression_statement) => {
|
|
lower_to_ir_expression_statement(expression_statement, ctx, fn_ctx, can_return_value);
|
|
}
|
|
Statement::Assign(assign_statement) => {
|
|
lower_to_ir_assign_statement(assign_statement, ctx, fn_ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn lower_binary_operator(binary_operation: &BinaryOperation) -> IrBinaryOperator {
|
|
match binary_operation {
|
|
BinaryOperation::Multiply => IrBinaryOperator::Multiply,
|
|
BinaryOperation::Divide => IrBinaryOperator::Divide,
|
|
BinaryOperation::Modulo => IrBinaryOperator::Modulo,
|
|
BinaryOperation::Add => IrBinaryOperator::Add,
|
|
BinaryOperation::Subtract => IrBinaryOperator::Subtract,
|
|
BinaryOperation::LeftShift => IrBinaryOperator::LeftShift,
|
|
BinaryOperation::RightShift => IrBinaryOperator::RightShift,
|
|
BinaryOperation::BitwiseAnd => IrBinaryOperator::BitwiseAnd,
|
|
BinaryOperation::BitwiseXor => IrBinaryOperator::BitwiseXor,
|
|
BinaryOperation::BitwiseOr => IrBinaryOperator::BitwiseOr,
|
|
}
|
|
}
|
|
|
|
fn lower_to_ir_let_statement(
|
|
let_statement: &LetStatement,
|
|
ctx: &LowerToIrContext,
|
|
fn_ctx: &mut LowerToIrFunctionContext,
|
|
) {
|
|
let symbol_id = ctx.nodes_to_symbols[&let_statement.node_id()];
|
|
|
|
let type_info_id = ctx.nodes_to_type_infos[&let_statement.node_id()];
|
|
let type_info = &ctx.type_infos[type_info_id];
|
|
|
|
let ir_variable = IrVariable::new(let_statement.declared_name(), to_ir_type_info(type_info));
|
|
let ir_variable_id = fn_ctx.insert_ir_variable(ir_variable, Some(symbol_id));
|
|
|
|
let initializer_ir_operation =
|
|
lower_expression_to_ir_operation(let_statement.initializer(), ctx, fn_ctx);
|
|
|
|
let ir_assign = IrAssign::new(ir_variable_id, initializer_ir_operation);
|
|
let ir_statement = IrStatement::Assign(ir_assign);
|
|
fn_ctx.current_block_statements.push(ir_statement);
|
|
}
|
|
|
|
fn lower_to_ir_expression_statement(
|
|
expression_statement: &ExpressionStatement,
|
|
ctx: &LowerToIrContext,
|
|
fn_ctx: &mut LowerToIrFunctionContext,
|
|
returns_value: bool,
|
|
) {
|
|
if returns_value {
|
|
let ir_expression =
|
|
lower_expression_to_ir_expression(expression_statement.expression(), ctx, fn_ctx);
|
|
let ir_statement = IrStatement::Return(IrReturn::new(Some(ir_expression)));
|
|
fn_ctx.current_block_statements.push(ir_statement);
|
|
} else {
|
|
// This block executes when the expression has side effects. For now, we just throw the
|
|
// result away in a temporary variable.
|
|
let ir_operation =
|
|
lower_expression_to_ir_operation(expression_statement.expression(), ctx, fn_ctx);
|
|
|
|
let result_type_info_id =
|
|
ctx.nodes_to_type_infos[&expression_statement.expression().node_id()];
|
|
let result_type_info = &ctx.type_infos[result_type_info_id];
|
|
let t_var_ir_variable_id = fn_ctx.make_t_var(to_ir_type_info(result_type_info));
|
|
|
|
let ir_statement = IrStatement::Assign(IrAssign::new(t_var_ir_variable_id, ir_operation));
|
|
fn_ctx.current_block_statements.push(ir_statement);
|
|
}
|
|
}
|
|
|
|
fn lower_to_ir_assign_statement(
|
|
assign_statement: &AssignStatement,
|
|
ctx: &LowerToIrContext,
|
|
fn_ctx: &mut LowerToIrFunctionContext,
|
|
) {
|
|
match assign_statement.destination() {
|
|
Expression::Identifier(identifier) => {
|
|
let destination_symbol_id = ctx.nodes_to_symbols[&identifier.node_id()];
|
|
let destination_ir_variable_id = fn_ctx.symbols_to_variables[&destination_symbol_id];
|
|
|
|
let ir_operation =
|
|
lower_expression_to_ir_operation(assign_statement.value(), ctx, fn_ctx);
|
|
let ir_assign = IrAssign::new(destination_ir_variable_id, ir_operation);
|
|
let ir_statement = IrStatement::Assign(ir_assign);
|
|
fn_ctx.current_block_statements.push(ir_statement);
|
|
}
|
|
_ => panic!("Assigning to non-Identifiers is not yet supported."),
|
|
}
|
|
}
|
|
|
|
fn lower_expression_to_ir_operation(
|
|
expression: &Expression,
|
|
ctx: &LowerToIrContext,
|
|
fn_ctx: &mut LowerToIrFunctionContext,
|
|
) -> IrOperation {
|
|
match expression {
|
|
Expression::Binary(binary_expression) => {
|
|
let lhs = lower_expression_to_ir_expression(binary_expression.lhs(), ctx, fn_ctx);
|
|
let rhs = lower_expression_to_ir_expression(binary_expression.rhs(), ctx, fn_ctx);
|
|
IrOperation::Binary(IrBinaryOperation::new(
|
|
lhs,
|
|
rhs,
|
|
lower_binary_operator(binary_expression.op()),
|
|
))
|
|
}
|
|
Expression::Negative(negative_expression) => {
|
|
let operand =
|
|
lower_expression_to_ir_expression(negative_expression.operand(), ctx, fn_ctx);
|
|
IrOperation::Binary(IrBinaryOperation::new(
|
|
operand,
|
|
IrExpression::Int(-1),
|
|
IrBinaryOperator::Multiply,
|
|
))
|
|
}
|
|
Expression::Call(call) => IrOperation::Call(lower_to_ir_call(call, ctx, fn_ctx)),
|
|
Expression::Identifier(identifier) => {
|
|
let identifier_symbol_id = ctx.nodes_to_symbols[&identifier.node_id()];
|
|
let identifier_ir_variable_id = fn_ctx.symbols_to_variables[&identifier_symbol_id];
|
|
let ir_expression = IrExpression::Variable(identifier_ir_variable_id);
|
|
IrOperation::Load(ir_expression)
|
|
}
|
|
Expression::Integer(integer_literal) => {
|
|
IrOperation::Load(IrExpression::Int(integer_literal.value()))
|
|
}
|
|
Expression::Double(double_literal) => {
|
|
IrOperation::Load(IrExpression::Double(double_literal.value()))
|
|
}
|
|
Expression::String(string_literal) => {
|
|
IrOperation::Load(IrExpression::String(string_literal.content().into()))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn lower_expression_to_ir_expression(
|
|
expression: &Expression,
|
|
ctx: &LowerToIrContext,
|
|
fn_ctx: &mut LowerToIrFunctionContext,
|
|
) -> IrExpression {
|
|
match expression {
|
|
Expression::Binary(binary_expression) => {
|
|
// make binary operation
|
|
let lhs = lower_expression_to_ir_expression(binary_expression.lhs(), ctx, fn_ctx);
|
|
let rhs = lower_expression_to_ir_expression(binary_expression.rhs(), ctx, fn_ctx);
|
|
let ir_operation = IrOperation::Binary(IrBinaryOperation::new(
|
|
lhs,
|
|
rhs,
|
|
lower_binary_operator(binary_expression.op()),
|
|
));
|
|
|
|
// make destination temp var
|
|
let result_type_info_id = ctx.nodes_to_type_infos[&binary_expression.node_id()];
|
|
let result_type_info = &ctx.type_infos[result_type_info_id];
|
|
let destination_ir_variable_id = fn_ctx.make_t_var(to_ir_type_info(result_type_info));
|
|
|
|
// make assign statement to destination temp var
|
|
let ir_assign = IrAssign::new(destination_ir_variable_id, ir_operation);
|
|
fn_ctx
|
|
.current_block_statements
|
|
.push(IrStatement::Assign(ir_assign));
|
|
|
|
// return location of temp var
|
|
IrExpression::Variable(destination_ir_variable_id)
|
|
}
|
|
Expression::Negative(negative_expression) => {
|
|
let operand =
|
|
lower_expression_to_ir_expression(negative_expression.operand(), ctx, fn_ctx);
|
|
let negative_one = IrExpression::Int(-1);
|
|
let ir_operation = IrOperation::Binary(IrBinaryOperation::new(
|
|
operand,
|
|
negative_one,
|
|
IrBinaryOperator::Multiply,
|
|
));
|
|
let result_type_info_id = ctx.nodes_to_type_infos[&negative_expression.node_id()];
|
|
let result_type_info = &ctx.type_infos[result_type_info_id];
|
|
let destination_ir_variable_id = fn_ctx.make_t_var(to_ir_type_info(result_type_info));
|
|
let ir_assign = IrAssign::new(destination_ir_variable_id, ir_operation);
|
|
|
|
// push the statement which does the multiply by negative one
|
|
fn_ctx
|
|
.current_block_statements
|
|
.push(IrStatement::Assign(ir_assign));
|
|
|
|
IrExpression::Variable(destination_ir_variable_id)
|
|
}
|
|
Expression::Call(call) => {
|
|
let ir_call = lower_to_ir_call(call, ctx, fn_ctx);
|
|
|
|
// make temp var
|
|
let return_type_info_id = ctx.nodes_to_type_infos[&call.node_id()];
|
|
let return_type_info = &ctx.type_infos[return_type_info_id];
|
|
let return_ir_type_info = to_ir_type_info(return_type_info);
|
|
let t_var_ir_variable_id = fn_ctx.make_t_var(return_ir_type_info);
|
|
|
|
// assign call to temp var, return temp var expression
|
|
let ir_operation = IrOperation::Call(ir_call);
|
|
let ir_assign = IrAssign::new(t_var_ir_variable_id, ir_operation);
|
|
fn_ctx
|
|
.current_block_statements
|
|
.push(IrStatement::Assign(ir_assign));
|
|
|
|
// return an expression referencing the temp var
|
|
IrExpression::Variable(t_var_ir_variable_id)
|
|
}
|
|
Expression::Identifier(identifier) => {
|
|
let rhs_symbol_id = ctx.nodes_to_symbols[&identifier.node_id()];
|
|
if let Some(rhs_ir_variable_id) = fn_ctx.symbols_to_variables.get(&rhs_symbol_id) {
|
|
IrExpression::Variable(*rhs_ir_variable_id)
|
|
} else if let Some(rhs_ir_parameter_id) =
|
|
fn_ctx.symbols_to_parameters.get(&rhs_symbol_id)
|
|
{
|
|
IrExpression::Parameter(*rhs_ir_parameter_id)
|
|
} else {
|
|
println!("Dump:\n{:#?}\n{:#?}", ctx, fn_ctx);
|
|
panic!(
|
|
"Could not find parameter or variable for symbol_id {}",
|
|
rhs_symbol_id
|
|
);
|
|
}
|
|
}
|
|
Expression::Integer(integer_literal) => IrExpression::Int(integer_literal.value()),
|
|
Expression::Double(double_literal) => IrExpression::Double(double_literal.value()),
|
|
Expression::String(string_literal) => IrExpression::String(string_literal.content().into()),
|
|
}
|
|
}
|
|
|
|
fn lower_to_ir_call(
|
|
call: &Call,
|
|
ctx: &LowerToIrContext,
|
|
fn_ctx: &mut LowerToIrFunctionContext,
|
|
) -> IrCall {
|
|
let callee_symbol_id = ctx.nodes_to_symbols[&call.callee().node_id()];
|
|
let callee_symbol = &ctx.symbols[callee_symbol_id];
|
|
match callee_symbol {
|
|
Symbol::Function(function_symbol) => {
|
|
let arguments = call
|
|
.arguments()
|
|
.iter()
|
|
.map(|e| lower_expression_to_ir_expression(e, ctx, fn_ctx))
|
|
.collect();
|
|
IrCall::new(
|
|
function_symbol.fqn_owned(),
|
|
arguments,
|
|
function_symbol.is_extern(),
|
|
)
|
|
}
|
|
_ => panic!("Expected function symbol"),
|
|
}
|
|
}
|