mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 16:44:33 +00:00
repl: refactor LLVM-specific code under an optional Cargo feature
This commit is contained in:
parent
33e6afe83c
commit
bbe82fcf25
4 changed files with 244 additions and 194 deletions
|
@ -5,18 +5,22 @@ version = "0.1.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[features]
|
||||||
bumpalo = {version = "3.8.0", features = ["collections"]}
|
default = ["llvm"]
|
||||||
inkwell = {path = "../vendor/inkwell"}# TODO
|
llvm = ["inkwell", "libloading", "roc_gen_llvm", "roc_build/llvm"]
|
||||||
libloading = "0.7.1" # TODO
|
|
||||||
target-lexicon = "0.12.2"
|
|
||||||
|
|
||||||
roc_build = {path = "../compiler/build", default-features = false, features = ["llvm"]}# TODO
|
[dependencies]
|
||||||
|
bumpalo = {version = "3.8.0", features = ["collections"]}
|
||||||
|
inkwell = {path = "../vendor/inkwell", optional = true}
|
||||||
|
libloading = {version = "0.7.1", optional = true}
|
||||||
|
target-lexicon = "0.12.2"
|
||||||
|
|
||||||
|
roc_build = {path = "../compiler/build", default-features = false}
|
||||||
roc_builtins = {path = "../compiler/builtins"}
|
roc_builtins = {path = "../compiler/builtins"}
|
||||||
roc_can = {path = "../compiler/can"}
|
roc_can = {path = "../compiler/can"}
|
||||||
roc_collections = {path = "../compiler/collections"}
|
roc_collections = {path = "../compiler/collections"}
|
||||||
roc_fmt = {path = "../compiler/fmt"}
|
roc_fmt = {path = "../compiler/fmt"}
|
||||||
roc_gen_llvm = {path = "../compiler/gen_llvm"}# TODO
|
roc_gen_llvm = {path = "../compiler/gen_llvm", optional = true}
|
||||||
roc_load = {path = "../compiler/load"}
|
roc_load = {path = "../compiler/load"}
|
||||||
roc_module = {path = "../compiler/module"}
|
roc_module = {path = "../compiler/module"}
|
||||||
roc_mono = {path = "../compiler/mono"}
|
roc_mono = {path = "../compiler/mono"}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use libloading::Library;
|
use std::cmp::{max_by_key, min_by_key};
|
||||||
|
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type};
|
|
||||||
use roc_module::called_via::CalledVia;
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
|
@ -15,7 +15,12 @@ use roc_parse::ast::{AssignedField, Collection, Expr, StrLiteral};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable};
|
use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable};
|
||||||
use std::cmp::{max_by_key, min_by_key};
|
|
||||||
|
#[cfg(feature = "llvm")]
|
||||||
|
use roc_gen_llvm::{run_jit_function, run_jit_function_dynamic_type};
|
||||||
|
|
||||||
|
#[cfg(feature = "llvm")]
|
||||||
|
type AppExecutable = libloading::Library;
|
||||||
|
|
||||||
use super::app_memory::AppMemory;
|
use super::app_memory::AppMemory;
|
||||||
|
|
||||||
|
@ -43,7 +48,7 @@ pub enum ToAstProblem {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub unsafe fn jit_to_ast<'a, M: AppMemory>(
|
pub unsafe fn jit_to_ast<'a, M: AppMemory>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
lib: Library,
|
app: AppExecutable,
|
||||||
main_fn_name: &str,
|
main_fn_name: &str,
|
||||||
layout: ProcLayout<'a>,
|
layout: ProcLayout<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
|
@ -68,7 +73,7 @@ pub unsafe fn jit_to_ast<'a, M: AppMemory>(
|
||||||
result,
|
result,
|
||||||
} => {
|
} => {
|
||||||
// this is a thunk
|
// this is a thunk
|
||||||
jit_to_ast_help(&env, lib, main_fn_name, &result, content)
|
jit_to_ast_help(&env, app, main_fn_name, &result, content)
|
||||||
}
|
}
|
||||||
_ => Err(ToAstProblem::FunctionLayout),
|
_ => Err(ToAstProblem::FunctionLayout),
|
||||||
}
|
}
|
||||||
|
@ -261,7 +266,7 @@ const OPAQUE_FUNCTION: Expr = Expr::Var {
|
||||||
|
|
||||||
fn jit_to_ast_help<'a, M: AppMemory>(
|
fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
env: &Env<'a, 'a, M>,
|
env: &Env<'a, 'a, M>,
|
||||||
lib: Library,
|
app: AppExecutable,
|
||||||
main_fn_name: &str,
|
main_fn_name: &str,
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
content: &'a Content,
|
content: &'a Content,
|
||||||
|
@ -269,7 +274,7 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
let (newtype_containers, content) = unroll_newtypes(env, content);
|
let (newtype_containers, content) = unroll_newtypes(env, content);
|
||||||
let content = unroll_aliases(env, content);
|
let content = unroll_aliases(env, content);
|
||||||
let result = match layout {
|
let result = match layout {
|
||||||
Layout::Builtin(Builtin::Bool) => Ok(run_jit_function!(lib, main_fn_name, bool, |num| {
|
Layout::Builtin(Builtin::Bool) => Ok(run_jit_function!(app, main_fn_name, bool, |num| {
|
||||||
bool_to_ast(env, num, content)
|
bool_to_ast(env, num, content)
|
||||||
})),
|
})),
|
||||||
Layout::Builtin(Builtin::Int(int_width)) => {
|
Layout::Builtin(Builtin::Int(int_width)) => {
|
||||||
|
@ -277,7 +282,7 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
|
|
||||||
macro_rules! helper {
|
macro_rules! helper {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
run_jit_function!(lib, main_fn_name, $ty, |num| num_to_ast(
|
run_jit_function!(app, main_fn_name, $ty, |num| num_to_ast(
|
||||||
env,
|
env,
|
||||||
number_literal_to_ast(env.arena, num),
|
number_literal_to_ast(env.arena, num),
|
||||||
content
|
content
|
||||||
|
@ -288,7 +293,7 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
let result = match int_width {
|
let result = match int_width {
|
||||||
U8 | I8 => {
|
U8 | I8 => {
|
||||||
// NOTE: this is does not handle 8-bit numbers yet
|
// NOTE: this is does not handle 8-bit numbers yet
|
||||||
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content))
|
run_jit_function!(app, main_fn_name, u8, |num| byte_to_ast(env, num, content))
|
||||||
}
|
}
|
||||||
U16 => helper!(u16),
|
U16 => helper!(u16),
|
||||||
U32 => helper!(u32),
|
U32 => helper!(u32),
|
||||||
|
@ -307,7 +312,7 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
|
|
||||||
macro_rules! helper {
|
macro_rules! helper {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
run_jit_function!(lib, main_fn_name, $ty, |num| num_to_ast(
|
run_jit_function!(app, main_fn_name, $ty, |num| num_to_ast(
|
||||||
env,
|
env,
|
||||||
number_literal_to_ast(env.arena, num),
|
number_literal_to_ast(env.arena, num),
|
||||||
content
|
content
|
||||||
|
@ -324,13 +329,13 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::Str) => Ok(run_jit_function!(
|
Layout::Builtin(Builtin::Str) => Ok(run_jit_function!(
|
||||||
lib,
|
app,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
&'static str,
|
&'static str,
|
||||||
|string: &'static str| { str_to_ast(env.arena, env.arena.alloc(string)) }
|
|string: &'static str| { str_to_ast(env.arena, env.arena.alloc(string)) }
|
||||||
)),
|
)),
|
||||||
Layout::Builtin(Builtin::List(elem_layout)) => Ok(run_jit_function!(
|
Layout::Builtin(Builtin::List(elem_layout)) => Ok(run_jit_function!(
|
||||||
lib,
|
app,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
(usize, usize),
|
(usize, usize),
|
||||||
|(addr, len): (usize, usize)| { list_to_ast(env, addr, len, elem_layout, content) }
|
|(addr, len): (usize, usize)| { list_to_ast(env, addr, len, elem_layout, content) }
|
||||||
|
@ -391,7 +396,7 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
let result_stack_size = layout.stack_size(env.target_info);
|
let result_stack_size = layout.stack_size(env.target_info);
|
||||||
|
|
||||||
run_jit_function_dynamic_type!(
|
run_jit_function_dynamic_type!(
|
||||||
lib,
|
app,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
result_stack_size as usize,
|
result_stack_size as usize,
|
||||||
|bytes_addr: usize| { struct_addr_to_ast(bytes_addr as usize) }
|
|bytes_addr: usize| { struct_addr_to_ast(bytes_addr as usize) }
|
||||||
|
@ -400,7 +405,7 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
Layout::Union(UnionLayout::NonRecursive(_)) => {
|
Layout::Union(UnionLayout::NonRecursive(_)) => {
|
||||||
let size = layout.stack_size(env.target_info);
|
let size = layout.stack_size(env.target_info);
|
||||||
Ok(run_jit_function_dynamic_type!(
|
Ok(run_jit_function_dynamic_type!(
|
||||||
lib,
|
app,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
size as usize,
|
size as usize,
|
||||||
|addr: usize| {
|
|addr: usize| {
|
||||||
|
@ -414,7 +419,7 @@ fn jit_to_ast_help<'a, M: AppMemory>(
|
||||||
| Layout::Union(UnionLayout::NullableWrapped { .. }) => {
|
| Layout::Union(UnionLayout::NullableWrapped { .. }) => {
|
||||||
let size = layout.stack_size(env.target_info);
|
let size = layout.stack_size(env.target_info);
|
||||||
Ok(run_jit_function_dynamic_type!(
|
Ok(run_jit_function_dynamic_type!(
|
||||||
lib,
|
app,
|
||||||
main_fn_name,
|
main_fn_name,
|
||||||
size as usize,
|
size as usize,
|
||||||
|addr: usize| {
|
|addr: usize| {
|
||||||
|
|
|
@ -1,23 +1,30 @@
|
||||||
use crate::app_memory::AppMemoryInternal;
|
|
||||||
use crate::eval;
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context; // TODO
|
use std::path::{Path, PathBuf};
|
||||||
use inkwell::module::Linkage; // TODO
|
use std::str::from_utf8_unchecked;
|
||||||
use roc_build::{link::module_to_dylib, program::FunctionIterator};
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
use roc_can::builtins::builtin_defs_map;
|
use roc_can::builtins::builtin_defs_map;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_fmt::annotation::Formattable;
|
use roc_fmt::annotation::Formattable;
|
||||||
use roc_fmt::annotation::{Newlines, Parens};
|
use roc_fmt::annotation::{Newlines, Parens};
|
||||||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||||
use roc_load::file::LoadingProblem;
|
use roc_load::file::LoadingProblem;
|
||||||
|
use roc_load::file::MonomorphizedModule;
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::OptLevel;
|
||||||
|
use roc_parse::ast::Expr;
|
||||||
use roc_parse::parser::SyntaxError;
|
use roc_parse::parser::SyntaxError;
|
||||||
use roc_region::all::LineInfo;
|
use roc_region::all::LineInfo;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
use roc_types::pretty_print::{content_to_string, name_all_type_vars};
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::str::from_utf8_unchecked;
|
#[cfg(feature = "llvm")]
|
||||||
use target_lexicon::Triple;
|
use {
|
||||||
|
inkwell::context::Context, inkwell::module::Linkage, roc_build::link::module_to_dylib,
|
||||||
|
roc_build::program::FunctionIterator,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::app_memory::AppMemoryInternal;
|
||||||
|
use crate::eval::{self, ToAstProblem};
|
||||||
|
|
||||||
pub enum ReplOutput {
|
pub enum ReplOutput {
|
||||||
Problems(Vec<String>),
|
Problems(Vec<String>),
|
||||||
|
@ -29,29 +36,201 @@ pub fn gen_and_eval<'a>(
|
||||||
target: Triple,
|
target: Triple,
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
) -> Result<ReplOutput, SyntaxError<'a>> {
|
) -> Result<ReplOutput, SyntaxError<'a>> {
|
||||||
|
if cfg!(feature = "llvm") {
|
||||||
|
gen_and_eval_llvm(src, target, opt_level)
|
||||||
|
} else {
|
||||||
|
todo!("REPL must be compiled with LLVM feature for now")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_and_eval_llvm<'a>(
|
||||||
|
src: &[u8],
|
||||||
|
target: Triple,
|
||||||
|
opt_level: OptLevel,
|
||||||
|
) -> Result<ReplOutput, SyntaxError<'a>> {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let target_info = TargetInfo::from(&target);
|
||||||
|
|
||||||
|
let loaded = match compile_to_mono(&arena, src, target_info) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(prob_strings) => {
|
||||||
|
return Ok(ReplOutput::Problems(prob_strings));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let MonomorphizedModule {
|
||||||
|
procedures,
|
||||||
|
entry_point,
|
||||||
|
interns,
|
||||||
|
exposed_to_host,
|
||||||
|
mut subs,
|
||||||
|
module_id: home,
|
||||||
|
..
|
||||||
|
} = loaded;
|
||||||
|
|
||||||
|
let context = Context::create();
|
||||||
|
let builder = context.create_builder();
|
||||||
|
let module = arena.alloc(roc_gen_llvm::llvm::build::module_from_builtins(
|
||||||
|
&target, &context, "",
|
||||||
|
));
|
||||||
|
|
||||||
|
// mark our zig-defined builtins as internal
|
||||||
|
for function in FunctionIterator::from_module(module) {
|
||||||
|
let name = function.get_name().to_str().unwrap();
|
||||||
|
if name.starts_with("roc_builtins") {
|
||||||
|
function.set_linkage(Linkage::Internal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_assert_eq!(exposed_to_host.values.len(), 1);
|
||||||
|
let (main_fn_symbol, main_fn_var) = exposed_to_host.values.iter().next().unwrap();
|
||||||
|
let main_fn_symbol = *main_fn_symbol;
|
||||||
|
let main_fn_var = *main_fn_var;
|
||||||
|
|
||||||
|
// pretty-print the expr type string for later.
|
||||||
|
name_all_type_vars(main_fn_var, &mut subs);
|
||||||
|
let content = subs.get_content_without_compacting(main_fn_var);
|
||||||
|
let expr_type_str = content_to_string(content, &subs, home, &interns);
|
||||||
|
|
||||||
|
let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
|
||||||
|
Some(layout) => *layout,
|
||||||
|
None => {
|
||||||
|
return Ok(ReplOutput::NoProblems {
|
||||||
|
expr: "<function>".to_string(),
|
||||||
|
expr_type: expr_type_str,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let module = arena.alloc(module);
|
||||||
|
let (module_pass, function_pass) =
|
||||||
|
roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level);
|
||||||
|
|
||||||
|
let (dibuilder, compile_unit) = roc_gen_llvm::llvm::build::Env::new_debug_info(module);
|
||||||
|
|
||||||
|
// Compile and add all the Procs before adding main
|
||||||
|
let env = roc_gen_llvm::llvm::build::Env {
|
||||||
|
arena: &arena,
|
||||||
|
builder: &builder,
|
||||||
|
dibuilder: &dibuilder,
|
||||||
|
compile_unit: &compile_unit,
|
||||||
|
context: &context,
|
||||||
|
interns,
|
||||||
|
module,
|
||||||
|
target_info,
|
||||||
|
is_gen_test: true, // so roc_panic is generated
|
||||||
|
// important! we don't want any procedures to get the C calling convention
|
||||||
|
exposed_to_host: MutSet::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
||||||
|
// platform to provide them.
|
||||||
|
add_default_roc_externs(&env);
|
||||||
|
|
||||||
|
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
||||||
|
&env,
|
||||||
|
opt_level,
|
||||||
|
procedures,
|
||||||
|
entry_point,
|
||||||
|
);
|
||||||
|
|
||||||
|
env.dibuilder.finalize();
|
||||||
|
|
||||||
|
// we don't use the debug info, and it causes weird errors.
|
||||||
|
module.strip_debug_info();
|
||||||
|
|
||||||
|
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
||||||
|
// env.module.print_to_stderr();
|
||||||
|
|
||||||
|
if main_fn.verify(true) {
|
||||||
|
function_pass.run_on(&main_fn);
|
||||||
|
} else {
|
||||||
|
panic!("Main function {} failed LLVM verification in build. Uncomment things nearby to see more details.", main_fn_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_pass.run_on(env.module);
|
||||||
|
|
||||||
|
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||||
|
// env.module.print_to_stderr();
|
||||||
|
|
||||||
|
// Verify the module
|
||||||
|
if let Err(errors) = env.module.verify() {
|
||||||
|
panic!(
|
||||||
|
"Errors defining module:\n{}\n\nUncomment things nearby to see more details.",
|
||||||
|
errors.to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lib = module_to_dylib(env.module, &target, opt_level)
|
||||||
|
.expect("Error loading compiled dylib for test");
|
||||||
|
|
||||||
|
let res_answer = unsafe {
|
||||||
|
eval::jit_to_ast(
|
||||||
|
&arena,
|
||||||
|
lib,
|
||||||
|
main_fn_name,
|
||||||
|
main_fn_layout,
|
||||||
|
content,
|
||||||
|
&env.interns,
|
||||||
|
home,
|
||||||
|
&subs,
|
||||||
|
target_info,
|
||||||
|
&AppMemoryInternal,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let formatted = format_answer(&arena, res_answer, expr_type_str);
|
||||||
|
Ok(formatted)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_answer(
|
||||||
|
arena: &Bump,
|
||||||
|
res_answer: Result<Expr, ToAstProblem>,
|
||||||
|
expr_type_str: String,
|
||||||
|
) -> ReplOutput {
|
||||||
|
let mut expr = roc_fmt::Buf::new_in(arena);
|
||||||
|
|
||||||
|
use eval::ToAstProblem::*;
|
||||||
|
match res_answer {
|
||||||
|
Ok(answer) => {
|
||||||
|
answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0);
|
||||||
|
}
|
||||||
|
Err(FunctionLayout) => {
|
||||||
|
expr.indent(0);
|
||||||
|
expr.push_str("<function>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplOutput::NoProblems {
|
||||||
|
expr: expr.into_bump_str().to_string(),
|
||||||
|
expr_type: expr_type_str,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_to_mono<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
src: &[u8],
|
||||||
|
target_info: TargetInfo,
|
||||||
|
) -> Result<MonomorphizedModule<'a>, Vec<String>> {
|
||||||
use roc_reporting::report::{
|
use roc_reporting::report::{
|
||||||
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
can_problem, mono_problem, type_problem, RocDocAllocator, DEFAULT_PALETTE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let arena = Bump::new();
|
|
||||||
|
|
||||||
// SAFETY: we've already verified that this is valid UTF-8 during parsing.
|
// SAFETY: we've already verified that this is valid UTF-8 during parsing.
|
||||||
let src_str: &str = unsafe { from_utf8_unchecked(src) };
|
let src_str: &str = unsafe { from_utf8_unchecked(src) };
|
||||||
|
|
||||||
let stdlib = roc_builtins::std::standard_stdlib();
|
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||||
let filename = PathBuf::from("REPL.roc");
|
let filename = PathBuf::from("REPL.roc");
|
||||||
let src_dir = Path::new("fake/test/path");
|
let src_dir = Path::new("fake/test/path");
|
||||||
|
|
||||||
let module_src = promote_expr_to_module(src_str);
|
let module_src = arena.alloc(promote_expr_to_module(src_str));
|
||||||
|
|
||||||
let target_info = TargetInfo::from(&target);
|
|
||||||
|
|
||||||
let exposed_types = MutMap::default();
|
let exposed_types = MutMap::default();
|
||||||
let loaded = roc_load::file::load_and_monomorphize_from_str(
|
let loaded = roc_load::file::load_and_monomorphize_from_str(
|
||||||
&arena,
|
arena,
|
||||||
filename,
|
filename,
|
||||||
&module_src,
|
module_src,
|
||||||
&stdlib,
|
stdlib,
|
||||||
src_dir,
|
src_dir,
|
||||||
exposed_types,
|
exposed_types,
|
||||||
target_info,
|
target_info,
|
||||||
|
@ -61,46 +240,43 @@ pub fn gen_and_eval<'a>(
|
||||||
let mut loaded = match loaded {
|
let mut loaded = match loaded {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(LoadingProblem::FormattedReport(report)) => {
|
Err(LoadingProblem::FormattedReport(report)) => {
|
||||||
return Ok(ReplOutput::Problems(vec![report]));
|
return Err(vec![report]);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
panic!("error while loading module: {:?}", e)
|
panic!("error while loading module: {:?}", e)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
use roc_load::file::MonomorphizedModule;
|
|
||||||
let MonomorphizedModule {
|
let MonomorphizedModule {
|
||||||
procedures,
|
|
||||||
entry_point,
|
|
||||||
interns,
|
interns,
|
||||||
exposed_to_host,
|
|
||||||
mut subs,
|
|
||||||
module_id: home,
|
|
||||||
sources,
|
sources,
|
||||||
|
can_problems,
|
||||||
|
type_problems,
|
||||||
|
mono_problems,
|
||||||
..
|
..
|
||||||
} = loaded;
|
} = &mut loaded;
|
||||||
|
|
||||||
let mut lines = Vec::new();
|
let mut lines = Vec::new();
|
||||||
|
|
||||||
for (home, (module_path, src)) in sources {
|
for (home, (module_path, src)) in sources.iter() {
|
||||||
let can_problems = loaded.can_problems.remove(&home).unwrap_or_default();
|
let can_probs = can_problems.remove(home).unwrap_or_default();
|
||||||
let type_problems = loaded.type_problems.remove(&home).unwrap_or_default();
|
let type_probs = type_problems.remove(home).unwrap_or_default();
|
||||||
let mono_problems = loaded.mono_problems.remove(&home).unwrap_or_default();
|
let mono_probs = mono_problems.remove(home).unwrap_or_default();
|
||||||
|
|
||||||
let error_count = can_problems.len() + type_problems.len() + mono_problems.len();
|
let error_count = can_probs.len() + type_probs.len() + mono_probs.len();
|
||||||
|
|
||||||
if error_count == 0 {
|
if error_count == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let line_info = LineInfo::new(&module_src);
|
let line_info = LineInfo::new(module_src);
|
||||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||||
let palette = DEFAULT_PALETTE;
|
let palette = DEFAULT_PALETTE;
|
||||||
|
|
||||||
// Report parsing and canonicalization problems
|
// Report parsing and canonicalization problems
|
||||||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
let alloc = RocDocAllocator::new(&src_lines, *home, interns);
|
||||||
|
|
||||||
for problem in can_problems.into_iter() {
|
for problem in can_probs.into_iter() {
|
||||||
let report = can_problem(&alloc, &line_info, module_path.clone(), problem);
|
let report = can_problem(&alloc, &line_info, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
@ -109,7 +285,7 @@ pub fn gen_and_eval<'a>(
|
||||||
lines.push(buf);
|
lines.push(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in type_problems {
|
for problem in type_probs {
|
||||||
if let Some(report) = type_problem(&alloc, &line_info, module_path.clone(), problem) {
|
if let Some(report) = type_problem(&alloc, &line_info, module_path.clone(), problem) {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
@ -119,7 +295,7 @@ pub fn gen_and_eval<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for problem in mono_problems {
|
for problem in mono_probs {
|
||||||
let report = mono_problem(&alloc, &line_info, module_path.clone(), problem);
|
let report = mono_problem(&alloc, &line_info, module_path.clone(), problem);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
@ -130,143 +306,9 @@ pub fn gen_and_eval<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !lines.is_empty() {
|
if !lines.is_empty() {
|
||||||
Ok(ReplOutput::Problems(lines))
|
Err(lines)
|
||||||
} else {
|
} else {
|
||||||
let context = Context::create();
|
Ok(loaded)
|
||||||
let builder = context.create_builder();
|
|
||||||
let module = arena.alloc(roc_gen_llvm::llvm::build::module_from_builtins(
|
|
||||||
&target, &context, "",
|
|
||||||
));
|
|
||||||
|
|
||||||
// mark our zig-defined builtins as internal
|
|
||||||
for function in FunctionIterator::from_module(module) {
|
|
||||||
let name = function.get_name().to_str().unwrap();
|
|
||||||
if name.starts_with("roc_builtins") {
|
|
||||||
function.set_linkage(Linkage::Internal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert_eq!(exposed_to_host.values.len(), 1);
|
|
||||||
let (main_fn_symbol, main_fn_var) = exposed_to_host.values.iter().next().unwrap();
|
|
||||||
let main_fn_symbol = *main_fn_symbol;
|
|
||||||
let main_fn_var = *main_fn_var;
|
|
||||||
|
|
||||||
// pretty-print the expr type string for later.
|
|
||||||
name_all_type_vars(main_fn_var, &mut subs);
|
|
||||||
let content = subs.get_content_without_compacting(main_fn_var);
|
|
||||||
let expr_type_str = content_to_string(content, &subs, home, &interns);
|
|
||||||
|
|
||||||
let (_, main_fn_layout) = match procedures.keys().find(|(s, _)| *s == main_fn_symbol) {
|
|
||||||
Some(layout) => *layout,
|
|
||||||
None => {
|
|
||||||
return Ok(ReplOutput::NoProblems {
|
|
||||||
expr: "<function>".to_string(),
|
|
||||||
expr_type: expr_type_str,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------
|
|
||||||
START OF LLVM-SPECIFIC STUFF
|
|
||||||
--------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
let module = arena.alloc(module);
|
|
||||||
let (module_pass, function_pass) =
|
|
||||||
roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level);
|
|
||||||
|
|
||||||
let (dibuilder, compile_unit) = roc_gen_llvm::llvm::build::Env::new_debug_info(module);
|
|
||||||
|
|
||||||
// Compile and add all the Procs before adding main
|
|
||||||
let env = roc_gen_llvm::llvm::build::Env {
|
|
||||||
arena: &arena,
|
|
||||||
builder: &builder,
|
|
||||||
dibuilder: &dibuilder,
|
|
||||||
compile_unit: &compile_unit,
|
|
||||||
context: &context,
|
|
||||||
interns,
|
|
||||||
module,
|
|
||||||
target_info,
|
|
||||||
is_gen_test: true, // so roc_panic is generated
|
|
||||||
// important! we don't want any procedures to get the C calling convention
|
|
||||||
exposed_to_host: MutSet::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add roc_alloc, roc_realloc, and roc_dealloc, since the repl has no
|
|
||||||
// platform to provide them.
|
|
||||||
add_default_roc_externs(&env);
|
|
||||||
|
|
||||||
let (main_fn_name, main_fn) = roc_gen_llvm::llvm::build::build_procedures_return_main(
|
|
||||||
&env,
|
|
||||||
opt_level,
|
|
||||||
procedures,
|
|
||||||
entry_point,
|
|
||||||
);
|
|
||||||
|
|
||||||
env.dibuilder.finalize();
|
|
||||||
|
|
||||||
// we don't use the debug info, and it causes weird errors.
|
|
||||||
module.strip_debug_info();
|
|
||||||
|
|
||||||
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
|
||||||
// env.module.print_to_stderr();
|
|
||||||
|
|
||||||
if main_fn.verify(true) {
|
|
||||||
function_pass.run_on(&main_fn);
|
|
||||||
} else {
|
|
||||||
panic!("Main function {} failed LLVM verification in build. Uncomment things nearby to see more details.", main_fn_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
module_pass.run_on(env.module);
|
|
||||||
|
|
||||||
// Uncomment this to see the module's optimized LLVM instruction output:
|
|
||||||
// env.module.print_to_stderr();
|
|
||||||
|
|
||||||
// Verify the module
|
|
||||||
if let Err(errors) = env.module.verify() {
|
|
||||||
panic!(
|
|
||||||
"Errors defining module:\n{}\n\nUncomment things nearby to see more details.",
|
|
||||||
errors.to_string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let lib = module_to_dylib(env.module, &target, opt_level)
|
|
||||||
.expect("Error loading compiled dylib for test");
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------
|
|
||||||
END OF LLVM-SPECIFIC STUFF
|
|
||||||
--------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
let res_answer = unsafe {
|
|
||||||
eval::jit_to_ast(
|
|
||||||
&arena,
|
|
||||||
lib,
|
|
||||||
main_fn_name,
|
|
||||||
main_fn_layout,
|
|
||||||
content,
|
|
||||||
&env.interns,
|
|
||||||
home,
|
|
||||||
&subs,
|
|
||||||
target_info,
|
|
||||||
&AppMemoryInternal,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let mut expr = roc_fmt::Buf::new_in(&arena);
|
|
||||||
|
|
||||||
use eval::ToAstProblem::*;
|
|
||||||
match res_answer {
|
|
||||||
Ok(answer) => {
|
|
||||||
answer.format_with_options(&mut expr, Parens::NotNeeded, Newlines::Yes, 0);
|
|
||||||
}
|
|
||||||
Err(FunctionLayout) => {
|
|
||||||
expr.indent(0);
|
|
||||||
expr.push_str("<function>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ReplOutput::NoProblems {
|
|
||||||
expr: expr.into_bump_str().to_string(),
|
|
||||||
expr_type: expr_type_str,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
mod app_memory;
|
mod app_memory;
|
||||||
// mod debug; TODO: Is this in the right place? Seems to be specifically for solve.rs
|
|
||||||
mod eval;
|
mod eval;
|
||||||
pub mod gen;
|
pub mod gen;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue