Async RPC support #540

Merged
sb10q merged 2 commits from mwojcik/nac3:async_rpc into master 2024-09-13 12:12:14 +08:00
3 changed files with 48 additions and 32 deletions
Showing only changes of commit 5a5656e983 - Show all commits

View File

@ -832,7 +832,6 @@ fn rpc_codegen_callback_fn<'ctx>(
let ptr_type = int8.ptr_type(AddressSpace::default()); let ptr_type = int8.ptr_type(AddressSpace::default());
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false); let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
let service_id = int32.const_int(fun.1 .0 as u64, false); let service_id = int32.const_int(fun.1 .0 as u64, false);
// -- setup rpc tags // -- setup rpc tags
let mut tag = Vec::new(); let mut tag = Vec::new();
@ -950,7 +949,11 @@ fn rpc_codegen_callback_fn<'ctx>(
) )
}); });
ctx.builder ctx.builder
.build_call(rpc_send_async, &[service_id.into(), tag_ptr.into(), args_ptr.into()], "rpc.send") .build_call(
rpc_send_async,
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
"rpc.send",
)
.unwrap(); .unwrap();
} else { } else {
let rpc_send = ctx.module.get_function("rpc_send").unwrap_or_else(|| { let rpc_send = ctx.module.get_function("rpc_send").unwrap_or_else(|| {
@ -980,12 +983,12 @@ fn rpc_codegen_callback_fn<'ctx>(
Ok(None) Ok(None)
} else { } else {
let result = format_rpc_ret(generator, ctx, fun.0.ret); let result = format_rpc_ret(generator, ctx, fun.0.ret);
if !result.is_some_and(|res| res.get_type().is_pointer_type()) { if !result.is_some_and(|res| res.get_type().is_pointer_type()) {
// An RPC returning an NDArray would not touch here. // An RPC returning an NDArray would not touch here.
call_stackrestore(ctx, stackptr); call_stackrestore(ctx, stackptr);
} }
Ok(result) Ok(result)
} }
} }

View File

@ -34,16 +34,12 @@ use nac3core::inkwell::{
targets::*, targets::*,
OptimizationLevel, OptimizationLevel,
}; };
use itertools::Itertools;
use nac3core::codegen::{gen_func_impl, CodeGenLLVMOptions, CodeGenTargetMachineOptions};
use nac3core::toplevel::builtins::get_exn_constructor; use nac3core::toplevel::builtins::get_exn_constructor;
use nac3core::typecheck::typedef::{into_var_map, TypeEnum, Unifier, VarMap}; use nac3core::typecheck::typedef::{into_var_map, TypeEnum, Unifier, VarMap};
use nac3parser::{ use nac3core::nac3parser::{
ast::{Constant, ExprKind, Located, Stmt, StmtKind, StrRef}, ast::{Constant, ExprKind, Located, Stmt, StmtKind, StrRef},
parser::parse_program, parser::parse_program,
}; };
use nac3core::toplevel::builtins::get_exn_constructor;
use nac3core::typecheck::typedef::{into_var_map, TypeEnum, Unifier, VarMap};
use pyo3::create_exception; use pyo3::create_exception;
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::{exceptions, types::PyBytes, types::PyDict, types::PySet}; use pyo3::{exceptions, types::PyBytes, types::PyDict, types::PySet};
@ -139,7 +135,7 @@ struct Nac3 {
string_store: Arc<RwLock<HashMap<String, i32>>>, string_store: Arc<RwLock<HashMap<String, i32>>>,
exception_ids: Arc<RwLock<HashMap<usize, usize>>>, exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
deferred_eval_store: DeferredEvaluationStore, deferred_eval_store: DeferredEvaluationStore,
/// LLVM-related options for code generzation. /// LLVM-related options for code generation.
llvm_options: CodeGenLLVMOptions, llvm_options: CodeGenLLVMOptions,
} }
@ -479,12 +475,24 @@ impl Nac3 {
match &stmt.node { match &stmt.node {
StmtKind::FunctionDef { decorator_list, .. } => { StmtKind::FunctionDef { decorator_list, .. } => {
if decorator_list.iter().any(|decorator| decorator_id_string(decorator) == Some("rpc".to_string())) { if decorator_list
store_fun.call1(py, (def_id.0.into_py(py), module.getattr(py, name.to_string().as_str()).unwrap())).unwrap(); .iter()
let is_async = decorator_list.iter().any( .any(|decorator| decorator_id_string(decorator) == Some("rpc".to_string()))
|decorator| decorator_get_flags(decorator).iter().any( {
|constant| *constant == Constant::Str("async".into()) store_fun
)); .call1(
py,
(
def_id.0.into_py(py),
module.getattr(py, name.to_string().as_str()).unwrap(),
),
)
.unwrap();
let is_async = decorator_list.iter().any(|decorator| {
decorator_get_flags(decorator)
.iter()
.any(|constant| *constant == Constant::Str("async".into()))
});
rpc_ids.push((None, def_id, is_async)); rpc_ids.push((None, def_id, is_async));
} }
} }
@ -493,11 +501,14 @@ impl Nac3 {
let class_obj = module.getattr(py, class_name.as_str()).unwrap(); let class_obj = module.getattr(py, class_name.as_str()).unwrap();
for stmt in body { for stmt in body {
if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node { if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node {
if decorator_list.iter().any(|decorator| decorator_id_string(decorator) == Some("rpc".to_string())) { if decorator_list.iter().any(|decorator| {
let is_async = decorator_list.iter().any( decorator_id_string(decorator) == Some("rpc".to_string())
|decorator| decorator_get_flags(decorator).iter().any( }) {
|constant| *constant == Constant::Str("async".into()) let is_async = decorator_list.iter().any(|decorator| {
)); decorator_get_flags(decorator)
.iter()
.any(|constant| *constant == Constant::Str("async".into()))
});
if name == &"__init__".into() { if name == &"__init__".into() {
return Err(CompileError::new_err(format!( return Err(CompileError::new_err(format!(
"compilation failed\n----------\nThe constructor of class {} should not be decorated with rpc decorator (at {})", "compilation failed\n----------\nThe constructor of class {} should not be decorated with rpc decorator (at {})",
@ -509,7 +520,7 @@ impl Nac3 {
} }
} }
} }
_ => () _ => (),
} }
let id = *name_to_pyid.get(&name).unwrap(); let id = *name_to_pyid.get(&name).unwrap();
@ -852,8 +863,8 @@ impl Nac3 {
} }
} }
/// Retrieves the Name.id from a decorator, supports decorators with arguments.

Please add documentation for these two functions.

Please add documentation for these two functions.
fn decorator_id_string(decorator: &Located<ExprKind>) -> Option<String> { fn decorator_id_string(decorator: &Located<ExprKind>) -> Option<String> {
/// Retrieves the Name.id from a decorator, supports decorators with arguments.
if let ExprKind::Name { id, .. } = decorator.node { if let ExprKind::Name { id, .. } = decorator.node {
// Bare decorator // Bare decorator
return Some(id.to_string()); return Some(id.to_string());
@ -867,11 +878,11 @@ fn decorator_id_string(decorator: &Located<ExprKind>) -> Option<String> {
None None
} }
/// Retrieves flags from a decorator, if any.
fn decorator_get_flags(decorator: &Located<ExprKind>) -> Vec<Constant> { fn decorator_get_flags(decorator: &Located<ExprKind>) -> Vec<Constant> {
/// Retrieves flags from a decorator, if any.
let mut flags = vec![]; let mut flags = vec![];
if let ExprKind::Call { keywords, .. } = &decorator.node { if let ExprKind::Call { keywords, .. } = &decorator.node {
for keyword in keywords.iter() { for keyword in keywords {
if keyword.node.arg != Some("flags".into()) { if keyword.node.arg != Some("flags".into()) {
continue; continue;
} }

View File

@ -1673,7 +1673,7 @@ impl TopLevelComposer {
// they may be changed with our use of placeholders // they may be changed with our use of placeholders
for (def, _) in definition_ast_list.iter().skip(self.builtin_num) { for (def, _) in definition_ast_list.iter().skip(self.builtin_num) {
if let TopLevelDef::Function { signature, var_id, .. } = &mut *def.write() { if let TopLevelDef::Function { signature, var_id, .. } = &mut *def.write() {
if let TypeEnum::TFunc(FunSignature { args, ret, vars}) = if let TypeEnum::TFunc(FunSignature { args, ret, vars }) =
unifier.get_ty(*signature).as_ref() unifier.get_ty(*signature).as_ref()
{ {
let new_var_ids = vars let new_var_ids = vars
@ -1894,7 +1894,8 @@ impl TopLevelComposer {
} = &mut *function_def } = &mut *function_def
{ {
let signature_ty_enum = unifier.get_ty(*signature); let signature_ty_enum = unifier.get_ty(*signature);
let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) = signature_ty_enum.as_ref() let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) =
signature_ty_enum.as_ref()
else { else {
unreachable!("must be typeenum::tfunc") unreachable!("must be typeenum::tfunc")
}; };
@ -2059,11 +2060,12 @@ impl TopLevelComposer {
} }
if !decorator_list.is_empty() { if !decorator_list.is_empty() {
if let ast::ExprKind::Call { func, .. } = &decorator_list[0].node { if let ast::ExprKind::Call { func, .. } = &decorator_list[0].node {
if matches!(&func.node, if matches!(&func.node,
ast::ExprKind::Name{ id, .. } if id == &"rpc".into()) { ast::ExprKind::Name{ id, .. } if id == &"rpc".into())
instance_to_symbol.insert(String::new(), simple_name.to_string()); {
continue; instance_to_symbol.insert(String::new(), simple_name.to_string());

What exactly is this for?

What exactly is this for?

Mimics the check above it - for decorators with arguments the Name is behind a Call as I mentioned before. Without it, it wouldn't be considered an external function and compiler would try to compile it together with the kernel.

Mimics the check above it - for decorators with arguments the Name is behind a Call as I mentioned before. Without it, it wouldn't be considered an external function and compiler would try to compile it together with the kernel.
} continue;
}
} }
} }