This commit is contained in:
Edgar 2024-01-30 14:05:14 -03:00
parent fed52c0327
commit 167a7cf001
7 changed files with 504 additions and 55 deletions

2
Cargo.lock generated
View file

@ -563,6 +563,8 @@ dependencies = [
"edlang_ast", "edlang_ast",
"edlang_check", "edlang_check",
"edlang_codegen_mlir", "edlang_codegen_mlir",
"edlang_ir",
"edlang_lowering",
"edlang_parser", "edlang_parser",
"edlang_session", "edlang_session",
"tracing", "tracing",

View file

@ -17,6 +17,8 @@ color-eyre = "0.6.2"
edlang_ast = { version = "0.1.0", path = "../edlang_ast" } edlang_ast = { version = "0.1.0", path = "../edlang_ast" }
edlang_check = { version = "0.1.0", path = "../edlang_check" } edlang_check = { version = "0.1.0", path = "../edlang_check" }
edlang_codegen_mlir = { version = "0.1.0", path = "../edlang_codegen_mlir" } edlang_codegen_mlir = { version = "0.1.0", path = "../edlang_codegen_mlir" }
edlang_ir = { version = "0.1.0", path = "../edlang_ir" }
edlang_lowering = { version = "0.1.0", path = "../edlang_lowering" }
edlang_parser = { version = "0.1.0", path = "../edlang_parser" } edlang_parser = { version = "0.1.0", path = "../edlang_parser" }
edlang_session = { version = "0.1.0", path = "../edlang_session" } edlang_session = { version = "0.1.0", path = "../edlang_session" }
tracing = { workspace = true } tracing = { workspace = true }

View file

@ -3,6 +3,7 @@ use std::{error::Error, path::PathBuf, time::Instant};
use ariadne::Source; use ariadne::Source;
use clap::Parser; use clap::Parser;
use edlang_codegen_mlir::linker::{link_binary, link_shared_lib}; use edlang_codegen_mlir::linker::{link_binary, link_shared_lib};
use edlang_lowering::{lower_module, IdGenerator};
use edlang_session::{DebugInfo, OptLevel, Session}; use edlang_session::{DebugInfo, OptLevel, Session};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -24,6 +25,9 @@ pub struct CompilerArgs {
#[arg(long, default_value_t = false)] #[arg(long, default_value_t = false)]
ast: bool, ast: bool,
#[arg(long, default_value_t = false)]
ir: bool,
} }
pub fn main() -> Result<(), Box<dyn Error>> { pub fn main() -> Result<(), Box<dyn Error>> {
@ -81,6 +85,14 @@ pub fn main() -> Result<(), Box<dyn Error>> {
return Ok(()); return Ok(());
} }
let mut gen = IdGenerator::new(0);
let ir = lower_module(&mut gen, &module);
if args.ir {
println!("{:#?}", ir);
return Ok(());
}
if args.mlir { if args.mlir {
println!("{}", edlang_codegen_mlir::compile_mlir(&session, &module)?); println!("{}", edlang_codegen_mlir::compile_mlir(&session, &module)?);
return Ok(()); return Ok(());

View file

@ -1,59 +1,80 @@
// Based on a cfg // Based on a cfg
use std::collections::BTreeMap;
use edlang_span::Span; use edlang_span::Span;
use smallvec::SmallVec; use smallvec::SmallVec;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub mod scalar_int;
pub struct Body {
pub locals: SmallVec<[Local; 4]>, #[derive(Debug, Clone)]
pub blocks: SmallVec<[BasicBlock; 8]>, pub struct ModuleBody {
pub debug_info: SmallVec<[DebugInfo; 4]>, pub module_id: usize,
pub functions: BTreeMap<DefId, Body>,
pub modules: BTreeMap<DefId, Self>,
pub span: Span,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] /// Definition id.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DefId {
pub module_id: usize,
pub id: usize,
}
#[derive(Debug, Clone)]
pub struct Body {
pub def_id: DefId,
pub is_pub: bool,
pub is_extern: bool,
pub ret_type: Option<TypeInfo>,
pub locals: SmallVec<[Local; 4]>,
pub blocks: SmallVec<[BasicBlock; 8]>,
pub fn_span: Span,
}
#[derive(Debug, Clone)]
pub struct DebugInfo { pub struct DebugInfo {
pub id: usize, pub id: usize,
pub span: Span, pub span: Span,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub struct BasicBlock { pub struct BasicBlock {
pub id: usize, pub id: usize,
pub statements: SmallVec<[Statement; 8]>, pub statements: SmallVec<[Statement; 8]>,
pub terminator: Terminator, pub terminator: Terminator,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub struct Local { pub struct Local {
pub id: usize,
pub mutable: bool, pub mutable: bool,
pub debug_info: Option<usize>, pub span: Option<Span>,
pub ty: usize, pub ty: TypeInfo,
pub kind: LocalKind, pub kind: LocalKind,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] #[derive(Debug, Clone, Copy)]
pub enum LocalKind { pub enum LocalKind {
Temp, Temp,
Arg, Arg,
ReturnPointer, ReturnPointer,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub struct Statement { pub struct Statement {
pub id: usize, pub span: Option<Span>,
pub debug_info: Option<usize>,
pub kind: StatementKind, pub kind: StatementKind,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum StatementKind { pub enum StatementKind {
Assign(Place, RValue), Assign(Place, RValue),
StorageLive(usize), StorageLive(usize),
StorageDead(usize), StorageDead(usize),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum Terminator { pub enum Terminator {
Target(usize), Target(usize),
Return, Return,
@ -68,24 +89,23 @@ pub enum Terminator {
Unreachable, Unreachable,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub struct TypeInfo { pub struct TypeInfo {
pub id: usize, pub span: Option<Span>,
pub debug_info: Option<usize>,
pub kind: TypeKind, pub kind: TypeKind,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum TypeKind { pub enum TypeKind {
Bool, Bool,
Char, Char,
Int(IntTy), Int(IntTy),
Uint(UintTy), Uint(UintTy),
Float(FloatTY), Float(FloatTy),
FuncDef { name: String, args: Vec<Self> }, FuncDef { name: String, args: Vec<Self> },
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum IntTy { pub enum IntTy {
I128, I128,
I64, I64,
@ -95,7 +115,7 @@ pub enum IntTy {
Isize, Isize,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum UintTy { pub enum UintTy {
U128, U128,
U64, U64,
@ -105,57 +125,51 @@ pub enum UintTy {
Usize, Usize,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum FloatTY { pub enum FloatTy {
F32, F32,
F64, F64,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub struct ConstData { pub struct ConstData {
pub debug_info: Option<usize>, pub span: Option<Span>,
pub type_info: usize, pub type_info: TypeInfo,
pub kind: ConstKind, pub kind: ConstKind,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum ConstKind { pub enum ConstKind {
Value(ValueTree), Value(ValueTree),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] #[derive(Debug, Clone)]
pub struct ScalarInt {
pub data: u128,
pub size: u8,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ValueTree { pub enum ValueTree {
Leaf(ScalarInt), Leaf(ConstValue),
Branch(Vec<Self>), Branch(Vec<Self>),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum RValue { pub enum RValue {
Use(Operand), Use(Operand),
BinOp(BinOp, Operand, Operand), BinOp(BinOp, Operand, Operand),
UnOp(UnOp, Operand), UnOp(UnOp, Operand),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub enum Operand { pub enum Operand {
Copy(Place), Copy(Place),
Move(Place), Move(Place),
Constant(ConstData), Constant(ConstData),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone)]
pub struct Place { pub struct Place {
pub local: usize, pub local: usize,
pub projection: SmallVec<[PlaceElem; 1]>, pub projection: SmallVec<[PlaceElem; 1]>,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] #[derive(Debug, Clone, Copy)]
pub enum PlaceElem { pub enum PlaceElem {
Deref, Deref,
Field { field_idx: usize, type_info: usize }, Field { field_idx: usize, type_info: usize },
@ -204,11 +218,11 @@ impl TypeKind {
} }
pub fn get_f32() -> Self { pub fn get_f32() -> Self {
Self::Float(FloatTY::F32) Self::Float(FloatTy::F32)
} }
pub fn get_f64() -> Self { pub fn get_f64() -> Self {
Self::Float(FloatTY::F64) Self::Float(FloatTy::F64)
} }
pub fn get_bool() -> Self { pub fn get_bool() -> Self {
@ -220,7 +234,7 @@ impl TypeKind {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] #[derive(Debug, Clone, Copy)]
pub enum BinOp { pub enum BinOp {
Add, Add,
Sub, Sub,
@ -241,8 +255,25 @@ pub enum BinOp {
Offset, Offset,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] #[derive(Debug, Clone, Copy)]
pub enum UnOp { pub enum UnOp {
Not, Not,
Neg, Neg,
} }
#[derive(Debug, Clone, Copy)]
pub enum ConstValue {
Bool(bool),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
F32(f32),
F64(f64),
}

View file

@ -0,0 +1,59 @@
use std::num::NonZeroU8;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
#[repr(packed(1))]
pub struct ScalarInt {
pub data: u128,
/// Size in bytes
pub size: NonZeroU8,
}
impl ScalarInt {
/// Size in bytes
#[inline]
pub fn size(self) -> u64 {
self.size.get().into()
}
#[inline]
pub fn try_from_int(i: impl Into<i128>, size_in_bytes: u64) -> Option<Self> {
let i = i.into();
// `into` performed sign extension, we have to truncate
let truncated = truncate(size_in_bytes, i as u128);
if sign_extend(size_in_bytes, truncated) as i128 == i {
Some(Self {
data: truncated,
size: NonZeroU8::new(size_in_bytes as u8).unwrap(),
})
} else {
None
}
}
}
#[inline]
pub fn sign_extend(size: u64, value: u128) -> u128 {
let size = size * 8;
if size == 0 {
// Truncated until nothing is left.
return 0;
}
// Sign-extend it.
let shift = 128 - size;
// Shift the unsigned value to the left, then shift back to the right as signed
// (essentially fills with sign bit on the left).
(((value << shift) as i128) >> shift) as u128
}
/// Truncates `value` to `self` bits.
#[inline]
pub fn truncate(size: u64, value: u128) -> u128 {
let size = size * 8;
if size == 0 {
// Truncated until nothing is left.
return 0;
}
let shift = 128 - size;
// Truncate (shift left to drop out leftover values, shift right to fill with zeroes).
(value << shift) >> shift
}

View file

@ -1,14 +1,350 @@
pub fn add(left: usize, right: usize) -> usize { use std::collections::HashMap;
left + right
use edlang_ast as ast;
use edlang_ir as ir;
use ir::{DefId, Local, Operand, Place, Statement, TypeInfo};
pub struct IdGenerator {
pub current_id: usize,
pub module_id: usize,
} }
#[cfg(test)] impl IdGenerator {
mod tests { pub const fn new(module_id: usize) -> Self {
use super::*; Self {
current_id: 0,
module_id: 0,
}
}
#[test] pub fn next_id(&mut self) -> usize {
fn it_works() { self.current_id += 1;
let result = add(2, 2); self.current_id
assert_eq!(result, 4); }
pub fn next_defid(&mut self) -> DefId {
let id = self.next_id();
DefId {
module_id: self.module_id,
id,
}
}
}
pub fn lower_module(gen: &mut IdGenerator, module: &ast::Module) -> ir::ModuleBody {
let mut body = ir::ModuleBody {
module_id: gen.next_id(),
functions: Default::default(),
modules: Default::default(),
span: module.span,
};
let mut module_gen_id = IdGenerator::new(body.module_id);
for stmt in &module.contents {
match stmt {
ast::ModuleStatement::Function(func) => {
let res = lower_function(&mut module_gen_id, func);
body.functions.insert(res.def_id, res);
}
ast::ModuleStatement::Constant(_) => todo!(),
ast::ModuleStatement::Struct(_) => todo!(),
ast::ModuleStatement::Module(_) => todo!(),
}
}
body
}
struct BodyBuilder {
pub body: ir::Body,
pub statements: Vec<ir::Statement>,
pub locals: HashMap<String, usize>,
pub ret_local: Option<usize>,
}
fn lower_function(gen: &mut IdGenerator, func: &ast::Function) -> ir::Body {
let body = ir::Body {
def_id: gen.next_defid(),
ret_type: func.return_type.as_ref().map(|x| lower_type(gen, x)),
locals: Default::default(),
blocks: Default::default(),
fn_span: func.span,
is_pub: func.is_public,
is_extern: func.is_extern,
};
let mut builder = BodyBuilder {
body,
statements: Vec::new(),
locals: HashMap::new(),
ret_local: None,
};
// store args ret
if let Some(ret_type) = func.return_type.as_ref() {
let ty = lower_type(gen, ret_type);
let local = Local {
mutable: false,
span: None,
ty,
kind: ir::LocalKind::ReturnPointer,
};
builder.ret_local = Some(builder.body.locals.len());
builder.body.locals.push(local);
}
for arg in &func.params {
let ty = lower_type(gen, &arg.arg_type);
let local = Local {
mutable: false,
span: Some(arg.span),
ty,
kind: ir::LocalKind::Arg,
};
builder
.locals
.insert(arg.name.name.clone(), builder.locals.len());
builder.body.locals.push(local);
}
for stmt in &func.body.body {
match stmt {
ast::Statement::Let(info) => lower_let(gen, &mut builder, info),
ast::Statement::Assign(_) => todo!(),
ast::Statement::For(_) => todo!(),
ast::Statement::While(_) => todo!(),
ast::Statement::If(_) => todo!(),
ast::Statement::Return(info) => lower_return(gen, &mut builder, info),
ast::Statement::FnCall(_) => todo!(),
}
}
builder.body
}
fn lower_let(gen: &mut IdGenerator, builder: &mut BodyBuilder, info: &ast::LetStmt) {
let rvalue = lower_expr(builder, &info.value, Some(&lower_type(gen, &info.r#type)));
let local = ir::Local {
mutable: info.is_mut,
span: Some(info.span),
ty: lower_type(gen, &info.r#type),
kind: ir::LocalKind::Temp,
};
let id = builder.body.locals.len();
builder.locals.insert(info.name.name.clone(), id);
builder.body.locals.push(local);
builder.statements.push(ir::Statement {
span: Some(info.span),
kind: ir::StatementKind::StorageLive(id),
});
builder.statements.push(ir::Statement {
span: Some(info.span),
kind: ir::StatementKind::Assign(
Place {
local: id,
projection: Default::default(),
},
rvalue,
),
});
}
fn lower_expr(
builder: &mut BodyBuilder,
info: &ast::Expression,
type_hint: Option<&TypeInfo>,
) -> ir::RValue {
match info {
ast::Expression::Value(info) => ir::RValue::Use(lower_value(builder, info, type_hint)),
ast::Expression::FnCall(_) => todo!(),
ast::Expression::Unary(_, _) => todo!(),
ast::Expression::Binary(_, _, _) => todo!(),
}
}
fn lower_value(
builder: &mut BodyBuilder,
info: &ast::ValueExpr,
type_hint: Option<&TypeInfo>,
) -> Operand {
match info {
ast::ValueExpr::Bool { value, span } => ir::Operand::Constant(ir::ConstData {
span: Some(*span),
type_info: ir::TypeInfo {
span: None,
kind: ir::TypeKind::Bool,
},
kind: ir::ConstKind::Value(ir::ValueTree::Leaf(ir::ConstValue::Bool(*value))),
}),
ast::ValueExpr::Char { value, span } => ir::Operand::Constant(ir::ConstData {
span: Some(*span),
type_info: ir::TypeInfo {
span: None,
kind: ir::TypeKind::Char,
},
kind: ir::ConstKind::Value(ir::ValueTree::Leaf(ir::ConstValue::U32((*value) as u32))),
}),
ast::ValueExpr::Int { value, span } => {
let (ty, val) = match type_hint {
Some(type_hint) => match &type_hint.kind {
ir::TypeKind::Int(type_hint) => match type_hint {
ir::IntTy::I128 => (
ir::TypeKind::Int(ir::IntTy::I128),
ir::ConstValue::I128((*value) as i128),
),
ir::IntTy::I64 => (
ir::TypeKind::Int(ir::IntTy::I64),
ir::ConstValue::I64((*value) as i64),
),
ir::IntTy::I32 => (
ir::TypeKind::Int(ir::IntTy::I32),
ir::ConstValue::I32((*value) as i32),
),
ir::IntTy::I16 => (
ir::TypeKind::Int(ir::IntTy::I16),
ir::ConstValue::I16((*value) as i16),
),
ir::IntTy::I8 => (
ir::TypeKind::Int(ir::IntTy::I8),
ir::ConstValue::I8((*value) as i8),
),
ir::IntTy::Isize => todo!(),
},
ir::TypeKind::Uint(type_hint) => match type_hint {
ir::UintTy::U128 => (
ir::TypeKind::Uint(ir::UintTy::U128),
ir::ConstValue::U128(*value),
),
ir::UintTy::U64 => (
ir::TypeKind::Uint(ir::UintTy::U64),
ir::ConstValue::U64((*value) as u64),
),
ir::UintTy::U32 => (
ir::TypeKind::Uint(ir::UintTy::U32),
ir::ConstValue::U32((*value) as u32),
),
ir::UintTy::U16 => (
ir::TypeKind::Uint(ir::UintTy::U16),
ir::ConstValue::U16((*value) as u16),
),
ir::UintTy::U8 => (
ir::TypeKind::Uint(ir::UintTy::U8),
ir::ConstValue::U8((*value) as u8),
),
_ => todo!(),
},
_ => unreachable!(),
},
None => todo!(),
};
ir::Operand::Constant(ir::ConstData {
span: Some(*span),
type_info: ir::TypeInfo {
span: None,
kind: ty,
},
kind: ir::ConstKind::Value(ir::ValueTree::Leaf(val)),
})
}
ast::ValueExpr::Float { value, span } => todo!(),
ast::ValueExpr::Str { value, span } => todo!(),
ast::ValueExpr::Path(info) => {
// add deref info to path
Operand::Move(lower_path(builder, info))
}
}
}
fn lower_return(gen: &mut IdGenerator, builder: &mut BodyBuilder, info: &ast::ReturnStmt) {
let ret_type = builder.body.ret_type.clone();
if let Some(value) = &info.value {
let rvalue = lower_expr(builder, value, ret_type.as_ref());
let ret_local = builder.ret_local.unwrap();
builder.statements.push(Statement {
span: Some(info.span),
kind: ir::StatementKind::Assign(
Place {
local: ret_local,
projection: Default::default(),
},
rvalue,
),
})
}
let statements = std::mem::take(&mut builder.statements);
builder.body.blocks.push(ir::BasicBlock {
id: builder.body.blocks.len(),
statements: statements.into(),
terminator: ir::Terminator::Return,
});
}
fn lower_path(builder: &mut BodyBuilder, info: &ast::PathExpr) -> ir::Place {
let local = *builder
.locals
.get(&info.first.name)
.expect("local not found");
Place {
local,
projection: Default::default(), // todo, field array deref
}
}
pub fn lower_type(gen: &mut IdGenerator, t: &ast::Type) -> ir::TypeInfo {
match t.name.name.as_str() {
"u8" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Uint(ir::UintTy::U8),
},
"u16" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Uint(ir::UintTy::U16),
},
"u32" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Uint(ir::UintTy::U32),
},
"u64" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Uint(ir::UintTy::U64),
},
"u128" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Uint(ir::UintTy::U128),
},
"i8" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Int(ir::IntTy::I8),
},
"i16" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Int(ir::IntTy::I16),
},
"i32" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Int(ir::IntTy::I32),
},
"i64" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Int(ir::IntTy::I64),
},
"i128" => ir::TypeInfo {
span: Some(t.span),
kind: ir::TypeKind::Int(ir::IntTy::I128),
},
_ => todo!(),
} }
} }

7
programs/simple.ed Normal file
View file

@ -0,0 +1,7 @@
mod Main {
fn main() -> i32 {
let x: i32 = 2;
return x;
}
}