2024-09-06 19:14:06 +08:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::num::NonZeroUsize;
|
2024-09-09 19:22:14 +08:00
|
|
|
use std::path::Path;
|
|
|
|
use std::process::Command;
|
2024-09-06 19:14:06 +08:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
use parking_lot::Mutex;
|
2024-09-04 23:16:22 +08:00
|
|
|
use eframe::egui;
|
2024-09-05 19:07:44 +08:00
|
|
|
|
2024-09-06 19:14:06 +08:00
|
|
|
use nac3core::codegen;
|
|
|
|
use nac3core::inkwell;
|
|
|
|
use nac3core::nac3parser;
|
|
|
|
use nac3core::toplevel;
|
|
|
|
use nac3core::toplevel::composer;
|
|
|
|
use nac3core::typecheck::{type_inferencer, typedef};
|
|
|
|
|
|
|
|
mod basic_symbol_resolver;
|
|
|
|
use basic_symbol_resolver::{Resolver, ResolverInternal};
|
|
|
|
|
2024-09-11 15:22:50 +08:00
|
|
|
fn compile(code: &String, run_symbol: &String, output_filename: &Path) -> Result<(), String> {
|
2024-09-09 19:22:14 +08:00
|
|
|
let mut target_machine_options = codegen::CodeGenTargetMachineOptions::from_host();
|
|
|
|
target_machine_options.reloc_mode = inkwell::targets::RelocMode::PIC;
|
2024-09-06 19:14:06 +08:00
|
|
|
let llvm_options = codegen::CodeGenLLVMOptions {
|
|
|
|
opt_level: inkwell::OptimizationLevel::Default,
|
2024-09-09 19:22:14 +08:00
|
|
|
target: target_machine_options,
|
2024-09-06 19:14:06 +08:00
|
|
|
};
|
2024-09-09 19:22:14 +08:00
|
|
|
let context = inkwell::context::Context::create();
|
|
|
|
let target_machine = llvm_options
|
|
|
|
.target
|
|
|
|
.create_target_machine(llvm_options.opt_level)
|
|
|
|
.expect("couldn't create target machine");
|
2024-09-06 19:14:06 +08:00
|
|
|
let size_t = context
|
2024-09-09 19:22:14 +08:00
|
|
|
.ptr_sized_int_type(&target_machine.get_target_data(), None)
|
2024-09-06 19:14:06 +08:00
|
|
|
.get_bit_width();
|
|
|
|
let primitive: type_inferencer::PrimitiveStore =
|
|
|
|
composer::TopLevelComposer::make_primitives(size_t).0;
|
|
|
|
let (mut composer, builtins_def, builtins_ty) = composer::TopLevelComposer::new(
|
|
|
|
vec![],
|
|
|
|
vec![],
|
|
|
|
composer::ComposerConfig::default(),
|
|
|
|
size_t,
|
|
|
|
);
|
|
|
|
let internal_resolver: Arc<ResolverInternal> = ResolverInternal {
|
|
|
|
id_to_type: builtins_ty.into(),
|
|
|
|
id_to_def: builtins_def.into(),
|
|
|
|
module_globals: Mutex::default(),
|
|
|
|
str_store: Mutex::default(),
|
|
|
|
}
|
|
|
|
.into();
|
|
|
|
let resolver = Arc::new(Resolver(internal_resolver.clone()))
|
|
|
|
as Arc<dyn nac3core::symbol_resolver::SymbolResolver + Send + Sync>;
|
|
|
|
let irrt = codegen::irrt::load_irrt(&context, resolver.as_ref());
|
|
|
|
|
|
|
|
let parser_result =
|
|
|
|
match nac3parser::parser::parse_program(code.as_str(), String::from("cell1").into()) {
|
|
|
|
Ok(parser_result) => parser_result,
|
|
|
|
Err(err) => {
|
2024-09-07 10:06:51 +08:00
|
|
|
return Err(format!("parse error: {}", err));
|
2024-09-06 19:14:06 +08:00
|
|
|
}
|
|
|
|
};
|
2024-09-11 15:22:50 +08:00
|
|
|
for mut stmt in parser_result {
|
|
|
|
if let nac3parser::ast::StmtKind::FunctionDef { name, .. } = &mut stmt.node {
|
2024-09-12 12:25:31 +08:00
|
|
|
if name.to_string() == "run" {
|
2024-09-11 15:22:50 +08:00
|
|
|
*name = run_symbol.as_str().into();
|
|
|
|
}
|
|
|
|
}
|
2024-09-06 19:14:06 +08:00
|
|
|
match composer.register_top_level(stmt, Some(resolver.clone()), "__main__", true) {
|
|
|
|
Ok((name, def_id, ty)) => {
|
|
|
|
internal_resolver.add_id_def(name, def_id);
|
|
|
|
if let Some(ty) = ty {
|
|
|
|
internal_resolver.add_id_type(name, ty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => {
|
2024-09-07 10:06:51 +08:00
|
|
|
return Err(format!("composer error: {}", err));
|
2024-09-06 19:14:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let signature = typedef::FunSignature {
|
|
|
|
args: vec![],
|
|
|
|
ret: primitive.int32,
|
|
|
|
vars: typedef::VarMap::new(),
|
|
|
|
};
|
|
|
|
let mut store = codegen::concrete_type::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);
|
|
|
|
|
|
|
|
if let Err(errors) = composer.start_analysis(true) {
|
|
|
|
let error_count = errors.len();
|
2024-09-07 10:06:51 +08:00
|
|
|
let mut msg = format!("{error_count} error(s) occurred during top level analysis.");
|
2024-09-06 19:14:06 +08:00
|
|
|
|
|
|
|
for (error_i, error) in errors.iter().enumerate() {
|
|
|
|
let error_num = error_i + 1;
|
2024-09-07 10:06:51 +08:00
|
|
|
msg.push_str(&format!(
|
|
|
|
"\n=========== ERROR {error_num}/{error_count} ============\n{error}"
|
|
|
|
));
|
2024-09-06 19:14:06 +08:00
|
|
|
}
|
2024-09-07 10:06:51 +08:00
|
|
|
return Err(msg);
|
2024-09-06 19:14:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
let top_level = Arc::new(composer.make_top_level_context());
|
|
|
|
|
2024-09-11 15:22:50 +08:00
|
|
|
let run_id_def = match resolver.get_identifier_def(run_symbol.as_str().into()) {
|
2024-09-06 19:14:06 +08:00
|
|
|
Ok(run_id_def) => run_id_def,
|
|
|
|
Err(_) => {
|
2024-09-07 10:06:51 +08:00
|
|
|
return Err(format!("no run() entry point"));
|
2024-09-06 19:14:06 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
let instance = {
|
|
|
|
let defs = top_level.definitions.read();
|
|
|
|
let mut instance = defs[run_id_def.0].write();
|
|
|
|
let toplevel::TopLevelDef::Function {
|
|
|
|
instance_to_stmt,
|
|
|
|
instance_to_symbol,
|
|
|
|
..
|
|
|
|
} = &mut *instance
|
|
|
|
else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
2024-09-11 15:22:50 +08:00
|
|
|
instance_to_symbol.insert(String::new(), run_symbol.clone());
|
2024-09-06 19:14:06 +08:00
|
|
|
instance_to_stmt[""].clone()
|
|
|
|
};
|
|
|
|
|
|
|
|
let task = codegen::CodeGenTask {
|
|
|
|
subst: Vec::default(),
|
2024-09-11 15:22:50 +08:00
|
|
|
symbol_name: run_symbol.clone(),
|
2024-09-06 19:14:06 +08:00
|
|
|
body: instance.body,
|
|
|
|
signature,
|
|
|
|
resolver,
|
|
|
|
store,
|
|
|
|
unifier_index: instance.unifier_id,
|
|
|
|
calls: instance.calls,
|
|
|
|
id: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
let nthreads = if inkwell::support::is_multithreaded() {
|
|
|
|
std::thread::available_parallelism()
|
|
|
|
.map(NonZeroUsize::get)
|
|
|
|
.unwrap_or(1usize)
|
|
|
|
} else {
|
|
|
|
1
|
|
|
|
};
|
|
|
|
|
|
|
|
let membuffers: Arc<Mutex<Vec<Vec<u8>>>> = Arc::default();
|
|
|
|
let membuffer = membuffers.clone();
|
|
|
|
|
|
|
|
let f = Arc::new(codegen::WithCall::new(Box::new(move |module| {
|
|
|
|
let buffer = module.write_bitcode_to_memory();
|
|
|
|
let buffer = buffer.as_slice().into();
|
|
|
|
membuffer.lock().push(buffer);
|
|
|
|
})));
|
|
|
|
let threads = (0..nthreads)
|
|
|
|
.map(|i| {
|
|
|
|
Box::new(codegen::DefaultCodeGenerator::new(
|
|
|
|
format!("module{i}"),
|
|
|
|
size_t,
|
|
|
|
))
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
let (registry, handles) =
|
|
|
|
codegen::WorkerRegistry::create_workers(threads, top_level, &llvm_options, &f);
|
|
|
|
registry.add_task(task);
|
|
|
|
registry.wait_tasks_complete(handles);
|
|
|
|
|
|
|
|
// Link all modules together into `main`
|
|
|
|
let buffers = membuffers.lock();
|
|
|
|
let main = context
|
|
|
|
.create_module_from_ir(
|
|
|
|
inkwell::memory_buffer::MemoryBuffer::create_from_memory_range(&buffers[0], "main"),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
for buffer in buffers.iter().skip(1) {
|
|
|
|
let other = context
|
|
|
|
.create_module_from_ir(
|
|
|
|
inkwell::memory_buffer::MemoryBuffer::create_from_memory_range(buffer, "main"),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
main.link_in_module(other).unwrap();
|
|
|
|
}
|
|
|
|
main.link_in_module(irrt).unwrap();
|
|
|
|
|
2024-09-09 19:22:14 +08:00
|
|
|
// Private all functions except "run"
|
|
|
|
let mut function_iter = main.get_first_function();
|
|
|
|
while let Some(func) = function_iter {
|
2024-09-11 15:22:50 +08:00
|
|
|
if func.count_basic_blocks() > 0 && func.get_name().to_str().unwrap() != run_symbol {
|
2024-09-09 19:22:14 +08:00
|
|
|
func.set_linkage(inkwell::module::Linkage::Private);
|
|
|
|
}
|
|
|
|
function_iter = func.get_next_function();
|
|
|
|
}
|
|
|
|
|
|
|
|
let pass_options = inkwell::passes::PassBuilderOptions::create();
|
|
|
|
pass_options.set_merge_functions(true);
|
|
|
|
main.run_passes("default<O2>", &target_machine, pass_options)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
target_machine
|
|
|
|
.write_to_file(&main, inkwell::targets::FileType::Object, output_filename)
|
2024-09-06 19:14:06 +08:00
|
|
|
.unwrap();
|
2024-09-09 19:22:14 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
// The year is 2024, and compiler toolchains are still a trash fire.
|
|
|
|
fn link_with_lld(elf_filename: &Path, obj_filename: &Path) -> Result<(), String> {
|
|
|
|
let linker_args = vec![
|
|
|
|
"-shared".to_string(),
|
|
|
|
"--eh-frame-hdr".to_string(),
|
|
|
|
"-x".to_string(),
|
|
|
|
"-o".to_string(),
|
|
|
|
elf_filename.to_str().unwrap().to_string(),
|
|
|
|
obj_filename.to_str().unwrap().to_string(),
|
|
|
|
];
|
|
|
|
|
|
|
|
if let Ok(linker_status) = Command::new("ld.lld").args(linker_args).status() {
|
|
|
|
if !linker_status.success() {
|
|
|
|
return Err("failed to start linker".to_string());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err("linker returned non-zero status code".to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2024-09-06 19:14:06 +08:00
|
|
|
}
|
2024-09-04 23:16:22 +08:00
|
|
|
|
2024-09-09 19:22:14 +08:00
|
|
|
type RunFn = unsafe extern "C" fn() -> i32;
|
|
|
|
|
|
|
|
struct CellBin {
|
|
|
|
cell_id: usize,
|
|
|
|
// note destructor order
|
|
|
|
run_fn: Option<RunFn>,
|
|
|
|
library: Option<libloading::Library>,
|
|
|
|
directory: tempfile::TempDir,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CellBin {
|
|
|
|
fn new(cell_id: usize) -> Self {
|
|
|
|
Self {
|
|
|
|
cell_id,
|
|
|
|
run_fn: None,
|
|
|
|
library: None,
|
|
|
|
directory: tempfile::tempdir().unwrap(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn compile_and_load(&mut self, code: &String) -> Result<(), String> {
|
|
|
|
assert!(self.run_fn.is_none());
|
|
|
|
assert!(self.library.is_none());
|
|
|
|
let object = self.directory.path().join("module.o");
|
|
|
|
let library = self.directory.path().join("module.so");
|
2024-09-11 15:22:50 +08:00
|
|
|
let run_symbol = format!("__cells_run_{}", self.cell_id);
|
|
|
|
compile(code, &run_symbol, &object)?;
|
2024-09-09 19:22:14 +08:00
|
|
|
link_with_lld(&library, &object)?;
|
|
|
|
unsafe {
|
|
|
|
self.library = Some(libloading::Library::new(library).or_else(|e| Err(e.to_string()))?);
|
|
|
|
let raw_fun_ptr = self
|
|
|
|
.library
|
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
2024-09-11 15:22:50 +08:00
|
|
|
.get::<RunFn>(run_symbol.as_bytes())
|
2024-09-09 19:22:14 +08:00
|
|
|
.unwrap()
|
|
|
|
.try_as_raw_ptr()
|
|
|
|
.unwrap();
|
|
|
|
self.run_fn = Some(std::mem::transmute(raw_fun_ptr));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Cell {
|
2024-09-07 10:06:51 +08:00
|
|
|
code: String,
|
2024-09-09 19:22:14 +08:00
|
|
|
result: Result<CellBin, String>,
|
2024-09-07 10:06:51 +08:00
|
|
|
}
|
2024-09-06 19:14:06 +08:00
|
|
|
|
2024-09-09 19:22:14 +08:00
|
|
|
impl Cell {
|
2024-09-07 10:06:51 +08:00
|
|
|
fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
code: "".to_string(),
|
|
|
|
result: Err("".to_string()),
|
|
|
|
}
|
|
|
|
}
|
2024-09-04 23:16:22 +08:00
|
|
|
|
2024-09-07 10:06:51 +08:00
|
|
|
fn update(&mut self) {
|
2024-09-09 19:22:14 +08:00
|
|
|
let mut new_bin = CellBin::new(0usize);
|
|
|
|
self.result = new_bin
|
|
|
|
.compile_and_load(&self.code)
|
|
|
|
.and_then(|_| Ok(new_bin));
|
2024-09-07 10:06:51 +08:00
|
|
|
}
|
|
|
|
}
|
2024-09-04 23:16:22 +08:00
|
|
|
|
2024-09-09 19:22:14 +08:00
|
|
|
struct Cells {
|
|
|
|
cells: Vec<Cell>,
|
2024-09-07 10:06:51 +08:00
|
|
|
}
|
|
|
|
|
2024-09-09 19:22:14 +08:00
|
|
|
impl Cells {
|
2024-09-07 10:06:51 +08:00
|
|
|
fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
cells: vec![Cell::new()],
|
2024-09-05 19:07:44 +08:00
|
|
|
}
|
2024-09-07 10:06:51 +08:00
|
|
|
}
|
2024-09-05 19:07:44 +08:00
|
|
|
|
2024-09-07 10:06:51 +08:00
|
|
|
fn update(&mut self) {
|
|
|
|
for cell in self.cells.iter_mut() {
|
|
|
|
cell.update()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ensure_last(&mut self) {
|
|
|
|
if self.cells.last().unwrap().result.is_ok() {
|
|
|
|
self.cells.push(Cell::new());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
|
|
|
for (cell_i, cell) in self.cells.iter_mut().enumerate() {
|
2024-09-05 18:10:28 +08:00
|
|
|
let theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx());
|
|
|
|
let mut layouter = |ui: &egui::Ui, string: &str, wrap_width: f32| {
|
|
|
|
let mut layout_job =
|
|
|
|
egui_extras::syntax_highlighting::highlight(ui.ctx(), &theme, string, "Python");
|
|
|
|
layout_job.wrap.max_width = wrap_width;
|
|
|
|
layout_job.sections = layout_job
|
|
|
|
.sections
|
|
|
|
.iter()
|
|
|
|
.map(|layout_section| {
|
|
|
|
let mut section = layout_section.clone();
|
|
|
|
section.format.font_id = egui::FontId::monospace(16.0);
|
|
|
|
section
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
ui.fonts(|f| f.layout_job(layout_job))
|
|
|
|
};
|
2024-09-07 10:06:51 +08:00
|
|
|
ui.group(|ui| {
|
|
|
|
ui.horizontal(|ui| {
|
|
|
|
ui.label(format!("[{}]", cell_i));
|
|
|
|
ui.add(
|
|
|
|
egui::TextEdit::multiline(&mut cell.code)
|
|
|
|
.code_editor()
|
|
|
|
.desired_rows(4)
|
|
|
|
.lock_focus(true)
|
|
|
|
.desired_width(f32::INFINITY)
|
|
|
|
.layouter(&mut layouter)
|
|
|
|
.font(egui::FontId::monospace(16.0)),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
match &cell.result {
|
2024-09-09 19:22:14 +08:00
|
|
|
Ok(bin) => {
|
|
|
|
ui.label(format!("{}", unsafe { bin.run_fn.unwrap()() }));
|
2024-09-07 10:06:51 +08:00
|
|
|
}
|
|
|
|
Err(msg) => {
|
|
|
|
if !msg.is_empty() {
|
|
|
|
ui.colored_label(egui::Color32::from_rgb(255, 0, 0), msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() -> eframe::Result {
|
2024-09-09 19:22:14 +08:00
|
|
|
inkwell::targets::Target::initialize_native(&inkwell::targets::InitializationConfig::default())
|
|
|
|
.unwrap();
|
2024-09-07 10:06:51 +08:00
|
|
|
|
|
|
|
let options = eframe::NativeOptions {
|
|
|
|
viewport: egui::ViewportBuilder::default().with_inner_size([1024.0, 768.0]),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut cells = Cells::new();
|
|
|
|
|
|
|
|
eframe::run_simple_native("Cells", options, move |ctx, _frame| {
|
|
|
|
let submit_key = egui::KeyboardShortcut::new(egui::Modifiers::CTRL, egui::Key::Enter);
|
|
|
|
if ctx.input_mut(|input| input.consume_shortcut(&submit_key)) {
|
|
|
|
cells.update();
|
|
|
|
cells.ensure_last();
|
|
|
|
}
|
|
|
|
|
|
|
|
egui::CentralPanel::default().show(ctx, |ui| {
|
|
|
|
cells.ui(ui);
|
2024-09-04 23:16:22 +08:00
|
|
|
});
|
|
|
|
})
|
|
|
|
}
|
2024-09-09 19:22:14 +08:00
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "C" fn __nac3_personality(_state: u32, _exception_object: u32, _context: u32) -> u32 {
|
|
|
|
unimplemented!()
|
|
|
|
}
|