nac3core/codegen: avoid sending unifiers

Previously, we have to copy types from one unification table to another,
and make the table sendable. This requires cloning (processing) the
whole table 3 times per function call which is not efficient and uses
more memory than required when the unification table is large.

We now use a concrete type table to only copy the type we need. This
reduces the overhead as we only need to process the unification table
for once (when we do the function codegen), and reduces memory usage by
a bit (but not noticeable when the unification table is small, i.e. the
types are simple).
This commit is contained in:
pca006132 2021-10-17 13:02:18 +08:00
parent 9850cbe313
commit 279f47f633
8 changed files with 405 additions and 161 deletions

View File

@ -19,7 +19,10 @@ use rustpython_parser::{
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use nac3core::{ use nac3core::{
codegen::{CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry}, codegen::{
concrete_type::ConcreteTypeStore, CodeGenTask, DefaultCodeGenerator, WithCall,
WorkerRegistry,
},
symbol_resolver::SymbolResolver, symbol_resolver::SymbolResolver,
toplevel::{composer::TopLevelComposer, DefinitionId, GenCall, TopLevelContext, TopLevelDef}, toplevel::{composer::TopLevelComposer, DefinitionId, GenCall, TopLevelContext, TopLevelDef},
typecheck::typedef::{FunSignature, FuncArg}, typecheck::typedef::{FunSignature, FuncArg},
@ -354,6 +357,21 @@ impl Nac3 {
) )
.unwrap(); .unwrap();
let signature = FunSignature {
args: vec![],
ret: self.primitive.none,
vars: HashMap::new(),
};
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature = store.from_signature(
&mut self.composer.unifier,
&self.primitive,
&signature,
&mut cache,
);
let signature = store.add_cty(signature);
self.composer.start_analysis(true).unwrap(); self.composer.start_analysis(true).unwrap();
self.top_level = Some(Arc::new(self.composer.make_top_level_context())); self.top_level = Some(Arc::new(self.composer.make_top_level_context()));
let top_level = self.top_level.as_ref().unwrap(); let top_level = self.top_level.as_ref().unwrap();
@ -373,18 +391,14 @@ impl Nac3 {
} }
}; };
let signature = FunSignature {
args: vec![],
ret: self.primitive.none,
vars: HashMap::new(),
};
let task = CodeGenTask { let task = CodeGenTask {
subst: Default::default(), subst: Default::default(),
symbol_name: "__modinit__".to_string(), symbol_name: "__modinit__".to_string(),
body: instance.body, body: instance.body,
signature, signature,
resolver, resolver,
unifier: top_level.unifiers.read()[instance.unifier_id].clone(), store,
unifier_index: instance.unifier_id,
calls: instance.calls, calls: instance.calls,
}; };
let isa = self.isa; let isa = self.isa;

View File

@ -0,0 +1,277 @@
use crate::{
symbol_resolver::SymbolValue,
toplevel::DefinitionId,
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
},
};
use rustpython_parser::ast::StrRef;
use std::collections::HashMap;
pub struct ConcreteTypeStore {
store: Vec<ConcreteTypeEnum>,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ConcreteType(usize);
#[derive(Clone, Debug)]
pub struct ConcreteFuncArg {
pub name: StrRef,
pub ty: ConcreteType,
pub default_value: Option<SymbolValue>,
}
#[derive(Clone, Debug)]
pub enum Primitive {
Int32,
Int64,
Float,
Bool,
None,
}
#[derive(Debug)]
pub enum ConcreteTypeEnum {
TPrimitive(Primitive),
TTuple {
ty: Vec<ConcreteType>,
},
TList {
ty: ConcreteType,
},
TObj {
obj_id: DefinitionId,
fields: HashMap<StrRef, ConcreteType>,
params: HashMap<u32, ConcreteType>,
},
TVirtual {
ty: ConcreteType,
},
TFunc {
args: Vec<ConcreteFuncArg>,
ret: ConcreteType,
vars: HashMap<u32, ConcreteType>,
},
}
impl ConcreteTypeStore {
pub fn new() -> ConcreteTypeStore {
ConcreteTypeStore {
store: vec![
ConcreteTypeEnum::TPrimitive(Primitive::Int32),
ConcreteTypeEnum::TPrimitive(Primitive::Int64),
ConcreteTypeEnum::TPrimitive(Primitive::Float),
ConcreteTypeEnum::TPrimitive(Primitive::Bool),
ConcreteTypeEnum::TPrimitive(Primitive::None),
],
}
}
pub fn get(&self, cty: ConcreteType) -> &ConcreteTypeEnum {
&self.store[cty.0]
}
pub fn from_signature(
&mut self,
unifier: &mut Unifier,
primitives: &PrimitiveStore,
signature: &FunSignature,
cache: &mut HashMap<Type, Option<ConcreteType>>,
) -> ConcreteTypeEnum {
ConcreteTypeEnum::TFunc {
args: signature
.args
.iter()
.map(|arg| ConcreteFuncArg {
name: arg.name,
ty: self.from_unifier_type(unifier, primitives, arg.ty, cache),
default_value: arg.default_value.clone(),
})
.collect(),
ret: self.from_unifier_type(unifier, primitives, signature.ret, cache),
vars: signature
.vars
.iter()
.map(|(id, ty)| (*id, self.from_unifier_type(unifier, primitives, *ty, cache)))
.collect(),
}
}
pub fn from_unifier_type(
&mut self,
unifier: &mut Unifier,
primitives: &PrimitiveStore,
ty: Type,
cache: &mut HashMap<Type, Option<ConcreteType>>,
) -> ConcreteType {
let ty = unifier.get_representative(ty);
if unifier.unioned(ty, primitives.int32) {
ConcreteType(0)
} else if unifier.unioned(ty, primitives.int64) {
ConcreteType(1)
} else if unifier.unioned(ty, primitives.float) {
ConcreteType(2)
} else if unifier.unioned(ty, primitives.bool) {
ConcreteType(3)
} else if unifier.unioned(ty, primitives.none) {
ConcreteType(4)
} else if let Some(cty) = cache.get(&ty) {
if let Some(cty) = cty {
*cty
} else {
let index = self.store.len();
// placeholder
self.store.push(ConcreteTypeEnum::TPrimitive(Primitive::Int32));
let result = ConcreteType(index);
cache.insert(ty, Some(result));
result
}
} else {
cache.insert(ty, None);
let ty_enum = unifier.get_ty(ty);
let result = match &*ty_enum {
TypeEnum::TTuple { ty } => ConcreteTypeEnum::TTuple {
ty: ty
.iter()
.map(|t| self.from_unifier_type(unifier, primitives, *t, cache))
.collect(),
},
TypeEnum::TList { ty } => ConcreteTypeEnum::TList {
ty: self.from_unifier_type(unifier, primitives, *ty, cache),
},
TypeEnum::TObj { obj_id, fields, params } => ConcreteTypeEnum::TObj {
obj_id: *obj_id,
fields: fields
.borrow()
.iter()
.map(|(name, ty)| {
(*name, self.from_unifier_type(unifier, primitives, *ty, cache))
})
.collect(),
params: params
.borrow()
.iter()
.map(|(id, ty)| {
(*id, self.from_unifier_type(unifier, primitives, *ty, cache))
})
.collect(),
},
TypeEnum::TVirtual { ty } => ConcreteTypeEnum::TVirtual {
ty: self.from_unifier_type(unifier, primitives, *ty, cache),
},
TypeEnum::TFunc(signature) => {
let signature = signature.borrow();
self.from_signature(unifier, primitives, &*signature, cache)
}
_ => unreachable!(),
};
let index = if let Some(ConcreteType(index)) = cache.get(&ty).unwrap() {
self.store[*index] = result;
*index
} else {
self.store.push(result);
self.store.len() - 1
};
cache.insert(ty, Some(ConcreteType(index)));
ConcreteType(index)
}
}
pub fn to_unifier_type(
&self,
unifier: &mut Unifier,
primitives: &PrimitiveStore,
cty: ConcreteType,
cache: &mut HashMap<ConcreteType, Option<Type>>,
) -> Type {
if let Some(ty) = cache.get_mut(&cty) {
return if let Some(ty) = ty {
*ty
} else {
*ty = Some(unifier.get_fresh_var().0);
ty.unwrap()
};
}
cache.insert(cty, None);
let result = match &self.store[cty.0] {
ConcreteTypeEnum::TPrimitive(primitive) => {
let ty = match primitive {
Primitive::Int32 => primitives.int32,
Primitive::Int64 => primitives.int64,
Primitive::Float => primitives.float,
Primitive::Bool => primitives.bool,
Primitive::None => primitives.none,
};
*cache.get_mut(&cty).unwrap() = Some(ty);
return ty;
}
ConcreteTypeEnum::TTuple { ty } => TypeEnum::TTuple {
ty: ty
.iter()
.map(|cty| self.to_unifier_type(unifier, primitives, *cty, cache))
.collect(),
},
ConcreteTypeEnum::TList { ty } => {
TypeEnum::TList { ty: self.to_unifier_type(unifier, primitives, *ty, cache) }
}
ConcreteTypeEnum::TVirtual { ty } => {
TypeEnum::TVirtual { ty: self.to_unifier_type(unifier, primitives, *ty, cache) }
}
ConcreteTypeEnum::TObj { obj_id, fields, params } => TypeEnum::TObj {
obj_id: *obj_id,
fields: fields
.iter()
.map(|(name, cty)| {
(*name, self.to_unifier_type(unifier, primitives, *cty, cache))
})
.collect::<HashMap<_, _>>()
.into(),
params: params
.iter()
.map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache)))
.collect::<HashMap<_, _>>()
.into(),
},
ConcreteTypeEnum::TFunc { args, ret, vars } => TypeEnum::TFunc(
FunSignature {
args: args
.iter()
.map(|arg| FuncArg {
name: arg.name,
ty: self.to_unifier_type(unifier, primitives, arg.ty, cache),
default_value: arg.default_value.clone(),
})
.collect(),
ret: self.to_unifier_type(unifier, primitives, *ret, cache),
vars: vars
.iter()
.map(|(id, cty)| {
(*id, self.to_unifier_type(unifier, primitives, *cty, cache))
})
.collect::<HashMap<_, _>>(),
}
.into(),
),
};
let result = unifier.add_ty(result);
if let Some(ty) = cache.get(&cty).unwrap() {
unifier.unify(*ty, result).unwrap();
}
cache.insert(cty, Some(result));
result
}
pub fn add_cty(&mut self, cty: ConcreteTypeEnum) -> ConcreteType {
self.store.push(cty);
ConcreteType(self.store.len() - 1)
}
}
impl Default for ConcreteTypeStore {
fn default() -> Self {
Self::new()
}
}

View File

@ -1,10 +1,13 @@
use std::{collections::HashMap, convert::TryInto, iter::once}; use std::{collections::HashMap, convert::TryInto, iter::once};
use crate::{ use crate::{
codegen::{get_llvm_type, CodeGenContext, CodeGenTask}, codegen::{
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
get_llvm_type, CodeGenContext, CodeGenTask,
},
symbol_resolver::SymbolValue, symbol_resolver::SymbolValue,
toplevel::{DefinitionId, TopLevelDef}, toplevel::{DefinitionId, TopLevelDef},
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier}, typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum},
}; };
use inkwell::{ use inkwell::{
types::{BasicType, BasicTypeEnum}, types::{BasicType, BasicTypeEnum},
@ -265,20 +268,9 @@ pub fn gen_func_instance<'ctx, 'a>(
instance_to_symbol.insert(key, symbol.clone()); instance_to_symbol.insert(key, symbol.clone());
let key = ctx.get_subst_key(obj.map(|a| a.0), sign, Some(var_id)); let key = ctx.get_subst_key(obj.map(|a| a.0), sign, Some(var_id));
let instance = instance_to_stmt.get(&key).unwrap(); let instance = instance_to_stmt.get(&key).unwrap();
let unifiers = ctx.top_level.unifiers.read();
let (unifier, primitives) = &unifiers[instance.unifier_id];
let mut unifier = Unifier::from_shared_unifier(unifier);
let mut type_cache = [ let mut store = ConcreteTypeStore::new();
(ctx.primitives.int32, primitives.int32), let mut cache = HashMap::new();
(ctx.primitives.int64, primitives.int64),
(ctx.primitives.float, primitives.float),
(ctx.primitives.bool, primitives.bool),
(ctx.primitives.none, primitives.none),
]
.iter()
.map(|(a, b)| (ctx.unifier.get_representative(*a), unifier.get_representative(*b)))
.collect();
let subst = sign let subst = sign
.vars .vars
@ -286,38 +278,27 @@ pub fn gen_func_instance<'ctx, 'a>(
.map(|(id, ty)| { .map(|(id, ty)| {
( (
*instance.subst.get(id).unwrap(), *instance.subst.get(id).unwrap(),
unifier.copy_from(&mut ctx.unifier, *ty, &mut type_cache), store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, *ty, &mut cache),
) )
}) })
.collect(); .collect();
let mut signature = FunSignature { let mut signature =
args: sign store.from_signature(&mut ctx.unifier, &ctx.primitives, sign, &mut cache);
.args
.iter()
.map(|arg| FuncArg {
name: arg.name,
ty: unifier.copy_from(&mut ctx.unifier, arg.ty, &mut type_cache),
default_value: arg.default_value.clone(),
})
.collect(),
ret: unifier.copy_from(&mut ctx.unifier, sign.ret, &mut type_cache),
vars: sign
.vars
.iter()
.map(|(id, ty)| {
(*id, unifier.copy_from(&mut ctx.unifier, *ty, &mut type_cache))
})
.collect(),
};
if let Some(obj) = &obj { if let Some(obj) = &obj {
signature let zelf =
.args store.from_unifier_type(&mut ctx.unifier, &ctx.primitives, obj.0, &mut cache);
.insert(0, FuncArg { name: "self".into(), ty: obj.0, default_value: None }); if let ConcreteTypeEnum::TFunc { args, .. } = &mut signature {
args.insert(
0,
ConcreteFuncArg { name: "self".into(), ty: zelf, default_value: None },
)
} else {
unreachable!()
} }
}
let unifier = (unifier.get_shared_unifier(), *primitives); let signature = store.add_cty(signature);
ctx.registry.add_task(CodeGenTask { ctx.registry.add_task(CodeGenTask {
symbol_name: symbol.clone(), symbol_name: symbol.clone(),
@ -326,7 +307,8 @@ pub fn gen_func_instance<'ctx, 'a>(
calls: instance.calls.clone(), calls: instance.calls.clone(),
subst, subst,
signature, signature,
unifier, store,
unifier_index: instance.unifier_id,
}); });
symbol symbol
}) })

View File

@ -3,7 +3,7 @@ use crate::{
toplevel::{TopLevelContext, TopLevelDef}, toplevel::{TopLevelContext, TopLevelDef},
typecheck::{ typecheck::{
type_inferencer::{CodeLocation, PrimitiveStore}, type_inferencer::{CodeLocation, PrimitiveStore},
typedef::{CallId, FunSignature, SharedUnifier, Type, TypeEnum, Unifier}, typedef::{CallId, FuncArg, Type, TypeEnum, Unifier},
}, },
}; };
use crossbeam::channel::{unbounded, Receiver, Sender}; use crossbeam::channel::{unbounded, Receiver, Sender};
@ -27,6 +27,7 @@ use std::sync::{
}; };
use std::thread; use std::thread;
pub mod concrete_type;
mod expr; mod expr;
mod generator; mod generator;
mod stmt; mod stmt;
@ -34,6 +35,7 @@ mod stmt;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
pub use generator::{CodeGenerator, DefaultCodeGenerator}; pub use generator::{CodeGenerator, DefaultCodeGenerator};
pub struct CodeGenContext<'ctx, 'a> { pub struct CodeGenContext<'ctx, 'a> {
@ -198,12 +200,13 @@ impl WorkerRegistry {
} }
pub struct CodeGenTask { pub struct CodeGenTask {
pub subst: Vec<(Type, Type)>, pub subst: Vec<(Type, ConcreteType)>,
pub store: ConcreteTypeStore,
pub symbol_name: String, pub symbol_name: String,
pub signature: FunSignature, pub signature: ConcreteType,
pub body: Arc<Vec<Stmt<Option<Type>>>>, pub body: Arc<Vec<Stmt<Option<Type>>>>,
pub calls: Arc<HashMap<CodeLocation, CallId>>, pub calls: Arc<HashMap<CodeLocation, CallId>>,
pub unifier: (SharedUnifier, PrimitiveStore), pub unifier_index: usize,
pub resolver: Arc<dyn SymbolResolver + Send + Sync>, pub resolver: Arc<dyn SymbolResolver + Send + Sync>,
} }
@ -218,7 +221,8 @@ fn get_llvm_type<'ctx>(
// we assume the type cache should already contain primitive types, // we assume the type cache should already contain primitive types,
// and they should be passed by value instead of passing as pointer. // and they should be passed by value instead of passing as pointer.
type_cache.get(&unifier.get_representative(ty)).cloned().unwrap_or_else(|| { type_cache.get(&unifier.get_representative(ty)).cloned().unwrap_or_else(|| {
match &*unifier.get_ty(ty) { let ty = unifier.get_ty(ty);
match &*ty {
TObj { obj_id, fields, .. } => { TObj { obj_id, fields, .. } => {
// a struct with fields in the order of declaration // a struct with fields in the order of declaration
let top_level_defs = top_level.definitions.read(); let top_level_defs = top_level.definitions.read();
@ -252,7 +256,7 @@ fn get_llvm_type<'ctx>(
ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into() ctx.struct_type(&fields, false).ptr_type(AddressSpace::Generic).into()
} }
TVirtual { .. } => unimplemented!(), TVirtual { .. } => unimplemented!(),
_ => unreachable!(), _ => unreachable!("{}", ty.get_type_name()),
} }
}) })
} }
@ -267,14 +271,16 @@ pub fn gen_func<'ctx, G: CodeGenerator + ?Sized>(
top_level_ctx: Arc<TopLevelContext>, top_level_ctx: Arc<TopLevelContext>,
) -> (Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>) { ) -> (Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>) {
let (mut unifier, primitives) = { let (mut unifier, primitives) = {
let (unifier, primitives) = task.unifier; let (unifier, primitives) = &top_level_ctx.unifiers.read()[task.unifier_index];
(Unifier::from_shared_unifier(&unifier), primitives) (Unifier::from_shared_unifier(unifier), *primitives)
}; };
let mut cache = HashMap::new();
for (a, b) in task.subst.iter() { for (a, b) in task.subst.iter() {
// this should be unification between variables and concrete types // this should be unification between variables and concrete types
// and should not cause any problem... // and should not cause any problem...
unifier.unify(*a, *b).unwrap(); let b = task.store.to_unifier_type(&mut unifier, &primitives, *b, &mut cache);
unifier.unify(*a, b).unwrap();
} }
// rebuild primitive store with unique representatives // rebuild primitive store with unique representatives
@ -296,25 +302,33 @@ pub fn gen_func<'ctx, G: CodeGenerator + ?Sized>(
.cloned() .cloned()
.collect(); .collect();
let params = task let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } =
.signature task.store.get(task.signature)
.args {
(
args.iter()
.map(|arg| FuncArg {
name: arg.name,
ty: task.store.to_unifier_type(&mut unifier, &primitives, arg.ty, &mut cache),
default_value: arg.default_value.clone(),
})
.collect_vec(),
task.store.to_unifier_type(&mut unifier, &primitives, *ret, &mut cache),
)
} else {
unreachable!()
};
let params = args
.iter() .iter()
.map(|arg| { .map(|arg| {
get_llvm_type(context, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, arg.ty) get_llvm_type(context, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, arg.ty)
}) })
.collect_vec(); .collect_vec();
let fn_type = if unifier.unioned(task.signature.ret, primitives.none) { let fn_type = if unifier.unioned(ret, primitives.none) {
context.void_type().fn_type(&params, false) context.void_type().fn_type(&params, false)
} else { } else {
get_llvm_type( get_llvm_type(context, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, ret)
context,
&mut unifier,
top_level_ctx.as_ref(),
&mut type_cache,
task.signature.ret,
)
.fn_type(&params, false) .fn_type(&params, false)
}; };
@ -335,7 +349,7 @@ pub fn gen_func<'ctx, G: CodeGenerator + ?Sized>(
let body_bb = context.append_basic_block(fn_val, "body"); let body_bb = context.append_basic_block(fn_val, "body");
let mut var_assignment = HashMap::new(); let mut var_assignment = HashMap::new();
for (n, arg) in task.signature.args.iter().enumerate() { for (n, arg) in args.iter().enumerate() {
let param = fn_val.get_nth_param(n as u32).unwrap(); let param = fn_val.get_nth_param(n as u32).unwrap();
let alloca = builder.build_alloca( let alloca = builder.build_alloca(
get_llvm_type(context, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, arg.ty), get_llvm_type(context, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, arg.ty),

View File

@ -1,5 +1,8 @@
use crate::{ use crate::{
codegen::{CodeGenTask, WithCall, WorkerRegistry, CodeGenContext, DefaultCodeGenerator}, codegen::{
concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenTask, DefaultCodeGenerator,
WithCall, WorkerRegistry,
},
location::Location, location::Location,
symbol_resolver::SymbolResolver, symbol_resolver::SymbolResolver,
toplevel::{ toplevel::{
@ -34,11 +37,21 @@ impl Resolver {
} }
impl SymbolResolver for Resolver { impl SymbolResolver for Resolver {
fn get_symbol_type(&self, _: &mut Unifier, _: &[Arc<RwLock<TopLevelDef>>], _: &PrimitiveStore, str: StrRef) -> Option<Type> { fn get_symbol_type(
&self,
_: &mut Unifier,
_: &[Arc<RwLock<TopLevelDef>>],
_: &PrimitiveStore,
str: StrRef,
) -> Option<Type> {
self.id_to_type.get(&str).cloned() self.id_to_type.get(&str).cloned()
} }
fn get_symbol_value<'ctx, 'a>(&self, _: StrRef, _: &mut CodeGenContext<'ctx, 'a>) -> Option<BasicValueEnum<'ctx>> { fn get_symbol_value<'ctx, 'a>(
&self,
_: StrRef,
_: &mut CodeGenContext<'ctx, 'a>,
) -> Option<BasicValueEnum<'ctx>> {
unimplemented!() unimplemented!()
} }
@ -82,6 +95,11 @@ fn test_primitives() {
vars: HashMap::new(), vars: HashMap::new(),
}; };
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature = store.from_signature(&mut unifier, &primitives, &signature, &mut cache);
let signature = store.add_cty(signature);
let mut function_data = FunctionData { let mut function_data = FunctionData {
resolver: resolver.clone(), resolver: resolver.clone(),
bound_variables: Vec::new(), bound_variables: Vec::new(),
@ -116,15 +134,14 @@ fn test_primitives() {
personality_symbol: None, personality_symbol: None,
}); });
let unifier = (unifier.get_shared_unifier(), primitives);
let task = CodeGenTask { let task = CodeGenTask {
subst: Default::default(), subst: Default::default(),
symbol_name: "testing".into(), symbol_name: "testing".into(),
body: Arc::new(statements), body: Arc::new(statements),
resolver, unifier_index: 0,
unifier,
calls: Arc::new(calls), calls: Arc::new(calls),
resolver,
store,
signature, signature,
}; };
let f = Arc::new(WithCall::new(Box::new(|module| { let f = Arc::new(WithCall::new(Box::new(|module| {
@ -216,6 +233,10 @@ fn test_simple_call() {
vars: HashMap::new(), vars: HashMap::new(),
}; };
let fun_ty = unifier.add_ty(TypeEnum::TFunc(RefCell::new(signature.clone()))); let fun_ty = unifier.add_ty(TypeEnum::TFunc(RefCell::new(signature.clone())));
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature = store.from_signature(&mut unifier, &primitives, &signature, &mut cache);
let signature = store.add_cty(signature);
let foo_id = top_level.definitions.read().len(); let foo_id = top_level.definitions.read().len();
top_level.definitions.write().push(Arc::new(RwLock::new(TopLevelDef::Function { top_level.definitions.write().push(Arc::new(RwLock::new(TopLevelDef::Function {
@ -305,16 +326,15 @@ fn test_simple_call() {
personality_symbol: None, personality_symbol: None,
}); });
let unifier = (unifier.get_shared_unifier(), primitives);
let task = CodeGenTask { let task = CodeGenTask {
subst: Default::default(), subst: Default::default(),
symbol_name: "testing".to_string(), symbol_name: "testing".to_string(),
body: Arc::new(statements_1), body: Arc::new(statements_1),
resolver,
unifier,
calls: Arc::new(calls1), calls: Arc::new(calls1),
unifier_index: 0,
resolver,
signature, signature,
store,
}; };
let f = Arc::new(WithCall::new(Box::new(|module| { let f = Arc::new(WithCall::new(Box::new(|module| {
let expected = indoc! {" let expected = indoc! {"

View File

@ -13,7 +13,7 @@ use parking_lot::RwLock;
use rustpython_parser::ast::{Expr, StrRef}; use rustpython_parser::ast::{Expr, StrRef};
use inkwell::values::BasicValueEnum; use inkwell::values::BasicValueEnum;
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Debug)]
pub enum SymbolValue { pub enum SymbolValue {
I32(i32), I32(i32),
I64(i64), I64(i64),

View File

@ -125,78 +125,6 @@ impl Unifier {
} }
} }
// copy concrete type from a type in another unifier
// note that we are constructing a new type this way
// this can handle recursive types
pub fn copy_from(
&mut self,
unifier: &mut Unifier,
ty: Type,
type_cache: &mut HashMap<Type, Type>,
) -> Type {
let representative = unifier.get_representative(ty);
type_cache.get(&representative).cloned().unwrap_or_else(|| {
// put in a placeholder first to handle possible recursive type
let placeholder = self.get_fresh_var().0;
type_cache.insert(representative, placeholder);
let ty = match &*self.get_ty(ty) {
TypeEnum::TVar { .. } | TypeEnum::TRigidVar { .. } | TypeEnum::TCall(..) => {
unreachable!()
}
TypeEnum::TObj { obj_id, fields, params } => TypeEnum::TObj {
obj_id: *obj_id,
fields: RefCell::new(
fields
.borrow()
.iter()
.map(|(name, ty)| (*name, self.copy_from(unifier, *ty, type_cache)))
.collect(),
),
params: RefCell::new(
params
.borrow()
.iter()
.map(|(id, ty)| (*id, self.copy_from(unifier, *ty, type_cache)))
.collect(),
),
},
TypeEnum::TList { ty } => {
TypeEnum::TList { ty: self.copy_from(unifier, *ty, type_cache) }
}
TypeEnum::TFunc(fun) => {
let fun = fun.borrow();
TypeEnum::TFunc(RefCell::new(FunSignature {
args: fun
.args
.iter()
.map(|arg| FuncArg {
name: arg.name,
ty: self.copy_from(unifier, arg.ty, type_cache),
default_value: arg.default_value.clone(),
})
.collect(),
ret: self.copy_from(unifier, fun.ret, type_cache),
vars: fun
.vars
.iter()
.map(|(id, ty)| (*id, self.copy_from(unifier, *ty, type_cache)))
.collect(),
}))
}
TypeEnum::TTuple { ty } => TypeEnum::TTuple {
ty: ty.iter().map(|ty| self.copy_from(unifier, *ty, type_cache)).collect(),
},
TypeEnum::TVirtual { ty } => {
TypeEnum::TVirtual { ty: self.copy_from(unifier, *ty, type_cache) }
}
};
let ty = self.add_ty(ty);
self.unify_impl(placeholder, ty, false).unwrap();
type_cache.insert(representative, ty);
ty
})
}
/// Determine if the two types are the same /// Determine if the two types are the same
pub fn unioned(&mut self, a: Type, b: Type) -> bool { pub fn unioned(&mut self, a: Type, b: Type) -> bool {
self.unification_table.unioned(a, b) self.unification_table.unioned(a, b)

View File

@ -10,7 +10,10 @@ use std::fs;
use std::{collections::HashMap, path::Path, sync::Arc, time::SystemTime}; use std::{collections::HashMap, path::Path, sync::Arc, time::SystemTime};
use nac3core::{ use nac3core::{
codegen::{CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry}, codegen::{
concrete_type::ConcreteTypeStore, CodeGenTask, DefaultCodeGenerator, WithCall,
WorkerRegistry,
},
symbol_resolver::SymbolResolver, symbol_resolver::SymbolResolver,
toplevel::{composer::TopLevelComposer, TopLevelDef}, toplevel::{composer::TopLevelComposer, TopLevelDef},
typecheck::typedef::FunSignature, typecheck::typedef::FunSignature,
@ -73,6 +76,16 @@ fn main() {
} }
} }
let signature = FunSignature {
args: vec![],
ret: primitive.int32,
vars: HashMap::new(),
};
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature = store.from_signature(&mut composer.unifier, &primitive, &signature, &mut cache);
let signature = store.add_cty(signature);
composer.start_analysis(true).unwrap(); composer.start_analysis(true).unwrap();
let analysis_time = SystemTime::now(); let analysis_time = SystemTime::now();
println!( println!(
@ -100,11 +113,6 @@ fn main() {
unreachable!() unreachable!()
} }
}; };
let signature = FunSignature {
args: vec![],
ret: primitive.int32,
vars: HashMap::new(),
};
let task = CodeGenTask { let task = CodeGenTask {
subst: Default::default(), subst: Default::default(),
@ -112,7 +120,8 @@ fn main() {
body: instance.body, body: instance.body,
signature, signature,
resolver, resolver,
unifier: top_level.unifiers.read()[instance.unifier_id].clone(), store,
unifier_index: instance.unifier_id,
calls: instance.calls, calls: instance.calls,
}; };
let f = Arc::new(WithCall::new(Box::new(move |module| { let f = Arc::new(WithCall::new(Box::new(move |module| {