mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 07:17:50 +03:00
Merge branch 'trunk' of github.com:rtfeldman/roc into docs-parse-code-blocks
This commit is contained in:
commit
c53c5fdb33
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -3017,6 +3017,7 @@ dependencies = [
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_std",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"serde_json",
|
||||
@ -3365,6 +3366,7 @@ dependencies = [
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_solve",
|
||||
"roc_std",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"ven_ena",
|
||||
@ -3917,9 +3919,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.10.0"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d"
|
||||
checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
|
@ -16,6 +16,7 @@ bench = false
|
||||
|
||||
[features]
|
||||
default = ["target-x86", "llvm", "editor"]
|
||||
wasm-cli-run = []
|
||||
|
||||
# This is a separate feature because when we generate docs on Netlify,
|
||||
# it doesn't have LLVM installed. (Also, it doesn't need to do code gen.)
|
||||
@ -67,7 +68,7 @@ libc = "0.2"
|
||||
libloading = "0.6"
|
||||
|
||||
inkwell = { path = "../vendor/inkwell", optional = true }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -74,10 +74,30 @@ pub fn build_file<'a>(
|
||||
builtin_defs_map,
|
||||
)?;
|
||||
|
||||
use target_lexicon::Architecture;
|
||||
let emit_wasm = match target.architecture {
|
||||
Architecture::X86_64 => false,
|
||||
Architecture::Aarch64(_) => false,
|
||||
Architecture::Wasm32 => true,
|
||||
_ => panic!(
|
||||
"TODO gracefully handle unsupported architecture: {:?}",
|
||||
target.architecture
|
||||
),
|
||||
};
|
||||
|
||||
// TODO wasm host extension should be something else ideally
|
||||
// .bc does not seem to work because
|
||||
//
|
||||
// > Non-Emscripten WebAssembly hasn't implemented __builtin_return_address
|
||||
//
|
||||
// and zig does not currently emit `.a` webassembly static libraries
|
||||
let host_extension = if emit_wasm { "zig" } else { "o" };
|
||||
let app_extension = if emit_wasm { "bc" } else { "o" };
|
||||
|
||||
let path_to_platform = loaded.platform_path.clone();
|
||||
let app_o_file = Builder::new()
|
||||
.prefix("roc_app")
|
||||
.suffix(".o")
|
||||
.suffix(&format!(".{}", app_extension))
|
||||
.tempfile()
|
||||
.map_err(|err| {
|
||||
todo!("TODO Gracefully handle tempfile creation error {:?}", err);
|
||||
@ -131,7 +151,7 @@ pub fn build_file<'a>(
|
||||
arena,
|
||||
loaded,
|
||||
&roc_file_path,
|
||||
Triple::host(),
|
||||
target,
|
||||
app_o_file,
|
||||
opt_level,
|
||||
emit_debug_info,
|
||||
@ -173,12 +193,13 @@ pub fn build_file<'a>(
|
||||
let mut host_input_path = PathBuf::from(cwd);
|
||||
|
||||
host_input_path.push(&*path_to_platform);
|
||||
host_input_path.push("host.o");
|
||||
host_input_path.push("host");
|
||||
host_input_path.set_extension(host_extension);
|
||||
|
||||
// TODO we should no longer need to do this once we have platforms on
|
||||
// a package repository, as we can then get precompiled hosts from there.
|
||||
let rebuild_host_start = SystemTime::now();
|
||||
rebuild_host(host_input_path.as_path());
|
||||
rebuild_host(target, host_input_path.as_path());
|
||||
let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
|
||||
|
||||
if emit_debug_info {
|
||||
|
@ -15,7 +15,8 @@ use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use std::process::Command;
|
||||
use target_lexicon::Triple;
|
||||
use target_lexicon::BinaryFormat;
|
||||
use target_lexicon::{Architecture, Triple};
|
||||
|
||||
pub mod build;
|
||||
pub mod repl;
|
||||
@ -29,7 +30,9 @@ pub const CMD_DOCS: &str = "docs";
|
||||
pub const FLAG_DEBUG: &str = "debug";
|
||||
pub const FLAG_OPTIMIZE: &str = "optimize";
|
||||
pub const FLAG_LIB: &str = "lib";
|
||||
pub const FLAG_BACKEND: &str = "backend";
|
||||
pub const ROC_FILE: &str = "ROC_FILE";
|
||||
pub const BACKEND: &str = "BACKEND";
|
||||
pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
||||
pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
|
||||
|
||||
@ -50,6 +53,15 @@ pub fn build_app<'a>() -> App<'a> {
|
||||
.help("Optimize your compiled Roc program to run faster. (Optimization takes time to complete.)")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(FLAG_BACKEND)
|
||||
.long(FLAG_BACKEND)
|
||||
.help("Choose a different backend")
|
||||
// .requires(BACKEND)
|
||||
.default_value("llvm")
|
||||
.possible_values(&["llvm", "wasm", "asm"])
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(FLAG_LIB)
|
||||
.long(FLAG_LIB)
|
||||
@ -118,6 +130,15 @@ pub fn build_app<'a>() -> App<'a> {
|
||||
.requires(ROC_FILE)
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(FLAG_BACKEND)
|
||||
.long(FLAG_BACKEND)
|
||||
.help("Choose a different backend")
|
||||
// .requires(BACKEND)
|
||||
.default_value("llvm")
|
||||
.possible_values(&["llvm", "wasm", "asm"])
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(ROC_FILE)
|
||||
.help("The .roc file of an app to build and run")
|
||||
@ -159,11 +180,25 @@ pub enum BuildConfig {
|
||||
BuildAndRun { roc_file_arg_index: usize },
|
||||
}
|
||||
|
||||
fn wasm32_target_tripple() -> Triple {
|
||||
let mut triple = Triple::unknown();
|
||||
|
||||
triple.architecture = Architecture::Wasm32;
|
||||
triple.binary_format = BinaryFormat::Wasm;
|
||||
|
||||
triple
|
||||
}
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||
pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||
use build::build_file;
|
||||
use BuildConfig::*;
|
||||
|
||||
let target = match matches.value_of(FLAG_BACKEND) {
|
||||
Some("wasm") => wasm32_target_tripple(),
|
||||
_ => Triple::host(),
|
||||
};
|
||||
|
||||
let arena = Bump::new();
|
||||
let filename = matches.value_of(ROC_FILE).unwrap();
|
||||
|
||||
@ -205,7 +240,7 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io::
|
||||
let src_dir = path.parent().unwrap().canonicalize().unwrap();
|
||||
let res_binary_path = build_file(
|
||||
&arena,
|
||||
target,
|
||||
&target,
|
||||
src_dir,
|
||||
path,
|
||||
opt_level,
|
||||
@ -240,7 +275,14 @@ pub fn build(target: &Triple, matches: &ArgMatches, config: BuildConfig) -> io::
|
||||
Ok(outcome.status_code())
|
||||
}
|
||||
BuildAndRun { roc_file_arg_index } => {
|
||||
let mut cmd = Command::new(binary_path);
|
||||
let mut cmd = match target.architecture {
|
||||
Architecture::Wasm32 => Command::new("wasmtime"),
|
||||
_ => Command::new(&binary_path),
|
||||
};
|
||||
|
||||
if let Architecture::Wasm32 = target.architecture {
|
||||
cmd.arg(binary_path);
|
||||
}
|
||||
|
||||
// Forward all the arguments after the .roc file argument
|
||||
// to the new process. This way, you can do things like:
|
||||
|
@ -5,7 +5,6 @@ use roc_cli::{
|
||||
use std::fs::{self, FileType};
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
use roc_cli::build;
|
||||
@ -25,11 +24,7 @@ fn main() -> io::Result<()> {
|
||||
Some(arg_index) => {
|
||||
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
||||
|
||||
build(
|
||||
&Triple::host(),
|
||||
&matches,
|
||||
BuildConfig::BuildAndRun { roc_file_arg_index },
|
||||
)
|
||||
build(&matches, BuildConfig::BuildAndRun { roc_file_arg_index })
|
||||
}
|
||||
|
||||
None => {
|
||||
@ -40,7 +35,6 @@ fn main() -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
Some(CMD_BUILD) => Ok(build(
|
||||
&Triple::host(),
|
||||
matches.subcommand_matches(CMD_BUILD).unwrap(),
|
||||
BuildConfig::BuildOnly,
|
||||
)?),
|
||||
|
@ -222,7 +222,8 @@ fn jit_to_ast_help<'a>(
|
||||
let tags_map: roc_collections::all::MutMap<_, _> =
|
||||
tags_vec.iter().cloned().collect();
|
||||
|
||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||
let union_variant =
|
||||
union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes);
|
||||
|
||||
let size = layout.stack_size(env.ptr_bytes);
|
||||
use roc_mono::layout::WrappedVariant::*;
|
||||
@ -886,7 +887,8 @@ fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a>
|
||||
.map(|(a, b)| (a.clone(), b.to_vec()))
|
||||
.collect();
|
||||
|
||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||
let union_variant =
|
||||
union_sorted_tags_help(env.arena, tags_vec, None, env.subs, env.ptr_bytes);
|
||||
|
||||
match union_variant {
|
||||
UnionVariant::ByteUnion(tagnames) => {
|
||||
|
@ -132,7 +132,7 @@ pub fn gen_and_eval<'a>(
|
||||
let builder = context.create_builder();
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
let module = arena.alloc(roc_gen_llvm::llvm::build::module_from_builtins(
|
||||
&context, "",
|
||||
&context, "", ptr_bytes,
|
||||
));
|
||||
|
||||
// mark our zig-defined builtins as internal
|
||||
|
@ -108,6 +108,39 @@ mod cli_run {
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm-cli-run")]
|
||||
fn check_wasm_output_with_stdin(
|
||||
file: &Path,
|
||||
stdin: &[&str],
|
||||
executable_filename: &str,
|
||||
flags: &[&str],
|
||||
expected_ending: &str,
|
||||
) {
|
||||
let mut flags = flags.to_vec();
|
||||
flags.push("--backend=wasm");
|
||||
|
||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat());
|
||||
if !compile_out.stderr.is_empty() {
|
||||
panic!("{}", compile_out.stderr);
|
||||
}
|
||||
|
||||
assert!(compile_out.status.success(), "bad status {:?}", compile_out);
|
||||
|
||||
let out = run_cmd(
|
||||
"wasmtime",
|
||||
stdin,
|
||||
&[file.with_file_name(executable_filename).to_str().unwrap()],
|
||||
);
|
||||
|
||||
if !&out.stdout.ends_with(expected_ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:#?}",
|
||||
expected_ending, out
|
||||
);
|
||||
}
|
||||
assert!(out.status.success());
|
||||
}
|
||||
|
||||
/// This macro does two things.
|
||||
///
|
||||
/// First, it generates and runs a separate test for each of the given
|
||||
@ -223,13 +256,13 @@ mod cli_run {
|
||||
// expected_ending: "",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
// cli:"cli" => Example {
|
||||
// filename: "Echo.roc",
|
||||
// executable_filename: "echo",
|
||||
// stdin: &["Giovanni\n", "Giorgio\n"],
|
||||
// expected_ending: "Giovanni Giorgio!\n",
|
||||
// use_valgrind: true,
|
||||
// },
|
||||
cli:"cli" => Example {
|
||||
filename: "Echo.roc",
|
||||
executable_filename: "echo",
|
||||
stdin: &["Giovanni\n", "Giorgio\n"],
|
||||
expected_ending: "Hi, Giovanni Giorgio!\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
// custom_malloc:"custom-malloc" => Example {
|
||||
// filename: "Main.roc",
|
||||
// executable_filename: "custom-malloc-example",
|
||||
@ -255,9 +288,9 @@ mod cli_run {
|
||||
let benchmark = $benchmark;
|
||||
let file_name = examples_dir("benchmarks").join(benchmark.filename);
|
||||
|
||||
// TODO fix QuicksortApp and RBTreeCk and then remove this!
|
||||
// TODO fix QuicksortApp and then remove this!
|
||||
match benchmark.filename {
|
||||
"QuicksortApp.roc" | "RBTreeCk.roc" => {
|
||||
"QuicksortApp.roc" => {
|
||||
eprintln!("WARNING: skipping testing benchmark {} because the test is broken right now!", benchmark.filename);
|
||||
return;
|
||||
}
|
||||
@ -283,8 +316,48 @@ mod cli_run {
|
||||
benchmark.use_valgrind,
|
||||
);
|
||||
}
|
||||
|
||||
)*
|
||||
|
||||
#[cfg(feature = "wasm-cli-run")]
|
||||
mod wasm {
|
||||
use super::*;
|
||||
$(
|
||||
#[test]
|
||||
#[cfg_attr(not(debug_assertions), serial(benchmark))]
|
||||
fn $test_name() {
|
||||
let benchmark = $benchmark;
|
||||
let file_name = examples_dir("benchmarks").join(benchmark.filename);
|
||||
|
||||
// TODO fix QuicksortApp and then remove this!
|
||||
match benchmark.filename {
|
||||
"QuicksortApp.roc" | "TestBase64.roc" => {
|
||||
eprintln!("WARNING: skipping testing benchmark {} because the test is broken right now!", benchmark.filename);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Check with and without optimizations
|
||||
check_wasm_output_with_stdin(
|
||||
&file_name,
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&[],
|
||||
benchmark.expected_ending,
|
||||
);
|
||||
|
||||
check_wasm_output_with_stdin(
|
||||
&file_name,
|
||||
benchmark.stdin,
|
||||
benchmark.executable_filename,
|
||||
&["--optimize"],
|
||||
benchmark.expected_ending,
|
||||
);
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn all_benchmarks_have_tests() {
|
||||
@ -326,8 +399,8 @@ mod cli_run {
|
||||
rbtree_ck => Example {
|
||||
filename: "RBTreeCk.roc",
|
||||
executable_filename: "rbtree-ck",
|
||||
stdin: &[],
|
||||
expected_ending: "Node Black 0 {} Empty Empty\n",
|
||||
stdin: &["100"],
|
||||
expected_ending: "10\n",
|
||||
use_valgrind: true,
|
||||
},
|
||||
rbtree_insert => Example {
|
||||
|
@ -21,6 +21,7 @@ roc_mono = { path = "../mono" }
|
||||
roc_load = { path = "../load" }
|
||||
roc_gen_llvm = { path = "../gen_llvm", optional = true }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
roc_std = { path = "../../roc_std" }
|
||||
im = "14" # im and im-rc should always have the same version!
|
||||
im-rc = "14" # im and im-rc should always have the same version!
|
||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||
@ -29,7 +30,7 @@ libloading = "0.6"
|
||||
tempfile = "3.1.0"
|
||||
serde_json = "1.0"
|
||||
inkwell = { path = "../../vendor/inkwell", optional = true }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.5.1"
|
||||
@ -39,7 +40,7 @@ quickcheck = "0.8"
|
||||
quickcheck_macros = "0.8"
|
||||
|
||||
[features]
|
||||
default = ["llvm"]
|
||||
default = ["llvm", "target-webassembly"]
|
||||
target-arm = []
|
||||
target-aarch64 = []
|
||||
target-webassembly = []
|
||||
|
@ -26,6 +26,10 @@ pub fn link(
|
||||
link_type: LinkType,
|
||||
) -> io::Result<(Child, PathBuf)> {
|
||||
match target {
|
||||
Triple {
|
||||
architecture: Architecture::Wasm32,
|
||||
..
|
||||
} => link_wasm32(target, output_path, input_paths, link_type),
|
||||
Triple {
|
||||
operating_system: OperatingSystem::Linux,
|
||||
..
|
||||
@ -56,7 +60,7 @@ fn find_zig_str_path() -> PathBuf {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub fn build_zig_host(
|
||||
pub fn build_zig_host_native(
|
||||
env_path: &str,
|
||||
env_home: &str,
|
||||
emit_bin: &str,
|
||||
@ -86,7 +90,7 @@ pub fn build_zig_host(
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn build_zig_host(
|
||||
pub fn build_zig_host_native(
|
||||
env_path: &str,
|
||||
env_home: &str,
|
||||
emit_bin: &str,
|
||||
@ -158,21 +162,62 @@ pub fn build_zig_host(
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn rebuild_host(host_input_path: &Path) {
|
||||
pub fn build_zig_host_wasm32(
|
||||
env_path: &str,
|
||||
env_home: &str,
|
||||
emit_bin: &str,
|
||||
zig_host_src: &str,
|
||||
zig_str_path: &str,
|
||||
) -> Output {
|
||||
// NOTE currently just to get compiler warnings if the host code is invalid.
|
||||
// the produced artifact is not used
|
||||
//
|
||||
// NOTE we're emitting LLVM IR here (again, it is not actually used)
|
||||
//
|
||||
// we'd like to compile with `-target wasm32-wasi` but that is blocked on
|
||||
//
|
||||
// https://github.com/ziglang/zig/issues/9414
|
||||
Command::new("zig")
|
||||
.env_clear()
|
||||
.env("PATH", env_path)
|
||||
.env("HOME", env_home)
|
||||
.args(&[
|
||||
"build-obj",
|
||||
zig_host_src,
|
||||
emit_bin,
|
||||
"--pkg-begin",
|
||||
"str",
|
||||
zig_str_path,
|
||||
"--pkg-end",
|
||||
// include the zig runtime
|
||||
// "-fcompiler-rt",
|
||||
// include libc
|
||||
"--library",
|
||||
"c",
|
||||
"-target",
|
||||
"i386-linux-musl",
|
||||
// "wasm32-wasi",
|
||||
// "-femit-llvm-ir=/home/folkertdev/roc/roc/examples/benchmarks/platform/host.ll",
|
||||
])
|
||||
.output()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn rebuild_host(target: &Triple, host_input_path: &Path) {
|
||||
let c_host_src = host_input_path.with_file_name("host.c");
|
||||
let c_host_dest = host_input_path.with_file_name("c_host.o");
|
||||
let zig_host_src = host_input_path.with_file_name("host.zig");
|
||||
let rust_host_src = host_input_path.with_file_name("host.rs");
|
||||
let rust_host_dest = host_input_path.with_file_name("rust_host.o");
|
||||
let cargo_host_src = host_input_path.with_file_name("Cargo.toml");
|
||||
let host_dest = host_input_path.with_file_name("host.o");
|
||||
let host_dest_native = host_input_path.with_file_name("host.o");
|
||||
let host_dest_wasm = host_input_path.with_file_name("host.bc");
|
||||
|
||||
let env_path = env::var("PATH").unwrap_or_else(|_| "".to_string());
|
||||
let env_home = env::var("HOME").unwrap_or_else(|_| "".to_string());
|
||||
|
||||
if zig_host_src.exists() {
|
||||
// Compile host.zig
|
||||
let emit_bin = format!("-femit-bin={}", host_dest.to_str().unwrap());
|
||||
|
||||
let zig_str_path = find_zig_str_path();
|
||||
|
||||
@ -182,17 +227,31 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||
&zig_str_path
|
||||
);
|
||||
|
||||
validate_output(
|
||||
"host.zig",
|
||||
"zig",
|
||||
build_zig_host(
|
||||
&env_path,
|
||||
&env_home,
|
||||
&emit_bin,
|
||||
zig_host_src.to_str().unwrap(),
|
||||
zig_str_path.to_str().unwrap(),
|
||||
),
|
||||
);
|
||||
let output = match target.architecture {
|
||||
Architecture::Wasm32 => {
|
||||
let emit_bin = format!("-femit-llvm-ir={}", host_dest_wasm.to_str().unwrap());
|
||||
build_zig_host_wasm32(
|
||||
&env_path,
|
||||
&env_home,
|
||||
&emit_bin,
|
||||
zig_host_src.to_str().unwrap(),
|
||||
zig_str_path.to_str().unwrap(),
|
||||
)
|
||||
}
|
||||
Architecture::X86_64 => {
|
||||
let emit_bin = format!("-femit-bin={}", host_dest_native.to_str().unwrap());
|
||||
build_zig_host_native(
|
||||
&env_path,
|
||||
&env_home,
|
||||
&emit_bin,
|
||||
zig_host_src.to_str().unwrap(),
|
||||
zig_str_path.to_str().unwrap(),
|
||||
)
|
||||
}
|
||||
_ => panic!("Unsupported architecture {:?}", target.architecture),
|
||||
};
|
||||
|
||||
validate_output("host.zig", "zig", output)
|
||||
} else {
|
||||
// Compile host.c
|
||||
let output = Command::new("clang")
|
||||
@ -233,7 +292,7 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||
c_host_dest.to_str().unwrap(),
|
||||
"-lhost",
|
||||
"-o",
|
||||
host_dest.to_str().unwrap(),
|
||||
host_dest_native.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
@ -260,7 +319,7 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||
c_host_dest.to_str().unwrap(),
|
||||
rust_host_dest.to_str().unwrap(),
|
||||
"-o",
|
||||
host_dest.to_str().unwrap(),
|
||||
host_dest_native.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
@ -283,7 +342,7 @@ pub fn rebuild_host(host_input_path: &Path) {
|
||||
// Clean up c_host.o
|
||||
let output = Command::new("mv")
|
||||
.env_clear()
|
||||
.args(&[c_host_dest, host_dest])
|
||||
.args(&[c_host_dest, host_dest_native])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
@ -496,6 +555,38 @@ fn link_macos(
|
||||
))
|
||||
}
|
||||
|
||||
fn link_wasm32(
|
||||
_target: &Triple,
|
||||
output_path: PathBuf,
|
||||
input_paths: &[&str],
|
||||
_link_type: LinkType,
|
||||
) -> io::Result<(Child, PathBuf)> {
|
||||
let zig_str_path = find_zig_str_path();
|
||||
|
||||
let child =
|
||||
Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig")
|
||||
// .env_clear()
|
||||
// .env("PATH", &env_path)
|
||||
.args(&["build-exe"])
|
||||
.args(input_paths)
|
||||
.args([
|
||||
&format!("-femit-bin={}", output_path.to_str().unwrap()),
|
||||
// include libc
|
||||
"-lc",
|
||||
"-target",
|
||||
"wasm32-wasi",
|
||||
"--pkg-begin",
|
||||
"str",
|
||||
zig_str_path.to_str().unwrap(),
|
||||
"--pkg-end",
|
||||
// useful for debugging
|
||||
// "-femit-llvm-ir=/home/folkertdev/roc/roc/examples/benchmarks/platform/host.ll",
|
||||
])
|
||||
.spawn()?;
|
||||
|
||||
Ok((child, output_path))
|
||||
}
|
||||
|
||||
#[cfg(feature = "llvm")]
|
||||
pub fn module_to_dylib(
|
||||
module: &inkwell::module::Module,
|
||||
|
@ -29,7 +29,7 @@ pub fn gen_from_mono_module(
|
||||
arena: &bumpalo::Bump,
|
||||
mut loaded: MonomorphizedModule,
|
||||
roc_file_path: &Path,
|
||||
target: target_lexicon::Triple,
|
||||
target: &target_lexicon::Triple,
|
||||
app_o_file: &Path,
|
||||
opt_level: OptLevel,
|
||||
emit_debug_info: bool,
|
||||
@ -93,8 +93,9 @@ pub fn gen_from_mono_module(
|
||||
}
|
||||
|
||||
// Generate the binary
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
let context = Context::create();
|
||||
let module = arena.alloc(module_from_builtins(&context, "app"));
|
||||
let module = arena.alloc(module_from_builtins(&context, "app", ptr_bytes));
|
||||
|
||||
// strip Zig debug stuff
|
||||
// module.strip_debug_info();
|
||||
@ -134,7 +135,6 @@ pub fn gen_from_mono_module(
|
||||
let (mpm, _fpm) = roc_gen_llvm::llvm::build::construct_optimization_passes(module, opt_level);
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
let env = roc_gen_llvm::llvm::build::Env {
|
||||
arena,
|
||||
builder: &builder,
|
||||
@ -219,47 +219,79 @@ pub fn gen_from_mono_module(
|
||||
}
|
||||
}
|
||||
|
||||
// assemble the .ll into a .bc
|
||||
let _ = Command::new("llvm-as")
|
||||
.args(&[
|
||||
app_ll_dbg_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_bc_file.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
use target_lexicon::Architecture;
|
||||
match target.architecture {
|
||||
Architecture::X86_64 | Architecture::Aarch64(_) => {
|
||||
// assemble the .ll into a .bc
|
||||
let _ = Command::new("llvm-as")
|
||||
.args(&[
|
||||
app_ll_dbg_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_bc_file.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let llc_args = &[
|
||||
"-filetype=obj",
|
||||
app_bc_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_o_file.to_str().unwrap(),
|
||||
];
|
||||
let llc_args = &[
|
||||
"-filetype=obj",
|
||||
app_bc_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_o_file.to_str().unwrap(),
|
||||
];
|
||||
|
||||
// write the .o file. Note that this builds the .o for the local machine,
|
||||
// and ignores the `target_machine` entirely.
|
||||
//
|
||||
// different systems name this executable differently, so we shotgun for
|
||||
// the most common ones and then give up.
|
||||
let _: Result<std::process::Output, std::io::Error> =
|
||||
Command::new(format!("llc-{}", LLVM_VERSION))
|
||||
.args(llc_args)
|
||||
.output()
|
||||
.or_else(|_| Command::new("llc").args(llc_args).output())
|
||||
.map_err(|_| {
|
||||
panic!("We couldn't find llc-{} on your machine!", LLVM_VERSION);
|
||||
});
|
||||
// write the .o file. Note that this builds the .o for the local machine,
|
||||
// and ignores the `target_machine` entirely.
|
||||
//
|
||||
// different systems name this executable differently, so we shotgun for
|
||||
// the most common ones and then give up.
|
||||
let _: Result<std::process::Output, std::io::Error> =
|
||||
Command::new(format!("llc-{}", LLVM_VERSION))
|
||||
.args(llc_args)
|
||||
.output()
|
||||
.or_else(|_| Command::new("llc").args(llc_args).output())
|
||||
.map_err(|_| {
|
||||
panic!("We couldn't find llc-{} on your machine!", LLVM_VERSION);
|
||||
});
|
||||
}
|
||||
|
||||
Architecture::Wasm32 => {
|
||||
// assemble the .ll into a .bc
|
||||
let _ = Command::new("llvm-as")
|
||||
.args(&[
|
||||
app_ll_dbg_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_o_file.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
// Emit the .o file
|
||||
use target_lexicon::Architecture;
|
||||
match target.architecture {
|
||||
Architecture::X86_64 | Architecture::Aarch64(_) => {
|
||||
let reloc = RelocMode::Default;
|
||||
let model = CodeModel::Default;
|
||||
let target_machine =
|
||||
target::target_machine(target, convert_opt_level(opt_level), reloc, model)
|
||||
.unwrap();
|
||||
|
||||
let reloc = RelocMode::Default;
|
||||
let model = CodeModel::Default;
|
||||
let target_machine =
|
||||
target::target_machine(&target, convert_opt_level(opt_level), reloc, model).unwrap();
|
||||
|
||||
target_machine
|
||||
.write_to_file(env.module, FileType::Object, app_o_file)
|
||||
.expect("Writing .o file failed");
|
||||
target_machine
|
||||
.write_to_file(env.module, FileType::Object, app_o_file)
|
||||
.expect("Writing .o file failed");
|
||||
}
|
||||
Architecture::Wasm32 => {
|
||||
// Useful for debugging
|
||||
// module.print_to_file(app_ll_file);
|
||||
module.write_bitcode_to_path(app_o_file);
|
||||
}
|
||||
_ => panic!(
|
||||
"TODO gracefully handle unsupported architecture: {:?}",
|
||||
target.architecture
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
let emit_o_file = emit_o_file_start.elapsed().unwrap();
|
||||
|
@ -17,6 +17,10 @@ pub fn target_triple_str(target: &Triple) -> &'static str {
|
||||
operating_system: OperatingSystem::Linux,
|
||||
..
|
||||
} => "x86_64-unknown-linux-gnu",
|
||||
Triple {
|
||||
architecture: Architecture::Wasm32,
|
||||
..
|
||||
} => "wasm32-unknown-unknown",
|
||||
Triple {
|
||||
architecture: Architecture::Aarch64(_),
|
||||
operating_system: OperatingSystem::Linux,
|
||||
|
@ -20,7 +20,7 @@ pub fn build(b: *Builder) void {
|
||||
test_step.dependOn(&main_tests.step);
|
||||
|
||||
// LLVM IR
|
||||
const obj_name = "builtins";
|
||||
const obj_name = "builtins-64bit";
|
||||
const llvm_obj = b.addObject(obj_name, main_path);
|
||||
llvm_obj.setBuildMode(mode);
|
||||
llvm_obj.linkSystemLibrary("c");
|
||||
@ -30,6 +30,25 @@ pub fn build(b: *Builder) void {
|
||||
const ir = b.step("ir", "Build LLVM ir");
|
||||
ir.dependOn(&llvm_obj.step);
|
||||
|
||||
// LLVM IR 32-bit (wasm)
|
||||
var target = b.standardTargetOptions(.{});
|
||||
target.os_tag = std.Target.Os.Tag.linux;
|
||||
target.cpu_arch = std.Target.Cpu.Arch.i386;
|
||||
// target.abi = std.Target.Abi.none;
|
||||
target.abi = std.Target.Abi.musl;
|
||||
|
||||
const obj_name_32bit = "builtins-32bit";
|
||||
const llvm_obj_32bit = b.addObject(obj_name_32bit, main_path);
|
||||
llvm_obj_32bit.setBuildMode(mode);
|
||||
llvm_obj_32bit.linkSystemLibrary("c");
|
||||
llvm_obj_32bit.strip = true;
|
||||
llvm_obj_32bit.emit_llvm_ir = true;
|
||||
llvm_obj_32bit.emit_bin = false;
|
||||
llvm_obj_32bit.target = target;
|
||||
|
||||
const ir32bit = b.step("ir-32bit", "Build LLVM ir for 32-bit targets (wasm)");
|
||||
ir32bit.dependOn(&llvm_obj_32bit.step);
|
||||
|
||||
// Object File
|
||||
// TODO: figure out how to get this to emit symbols that are only scoped to linkage (global but hidden).
|
||||
// Also, zig has -ffunction-sections, but I am not sure how to add it here.
|
||||
|
@ -63,27 +63,21 @@ fn capacityOfLevel(input: usize) usize {
|
||||
// alignment of the key and value. The tag furthermore indicates
|
||||
// which has the biggest aligmnent. If both are the same, we put
|
||||
// the key first
|
||||
const Alignment = enum(u8) {
|
||||
Align16KeyFirst,
|
||||
Align16ValueFirst,
|
||||
Align8KeyFirst,
|
||||
Align8ValueFirst,
|
||||
const Alignment = extern struct {
|
||||
bits: u8,
|
||||
|
||||
const VALUE_BEFORE_KEY_FLAG = 0b1000_0000;
|
||||
|
||||
fn toU32(self: Alignment) u32 {
|
||||
switch (self) {
|
||||
.Align16KeyFirst => return 16,
|
||||
.Align16ValueFirst => return 16,
|
||||
.Align8KeyFirst => return 8,
|
||||
.Align8ValueFirst => return 8,
|
||||
}
|
||||
// xor to wipe the leftmost bit
|
||||
return self.bits ^ Alignment.VALUE_BEFORE_KEY_FLAG;
|
||||
}
|
||||
|
||||
fn keyFirst(self: Alignment) bool {
|
||||
switch (self) {
|
||||
.Align16KeyFirst => return true,
|
||||
.Align16ValueFirst => return false,
|
||||
.Align8KeyFirst => return true,
|
||||
.Align8ValueFirst => return false,
|
||||
if (self.bits & Alignment.VALUE_BEFORE_KEY_FLAG > 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -386,8 +380,8 @@ pub const RocDict = extern struct {
|
||||
};
|
||||
|
||||
// Dict.empty
|
||||
pub fn dictEmpty() callconv(.C) RocDict {
|
||||
return RocDict.empty();
|
||||
pub fn dictEmpty(dict: *RocDict) callconv(.C) void {
|
||||
dict.* = RocDict.empty();
|
||||
}
|
||||
|
||||
pub fn slotSize(key_size: usize, value_size: usize) usize {
|
||||
|
@ -313,6 +313,7 @@ pub const RocStr = extern struct {
|
||||
}
|
||||
|
||||
pub fn asU8ptr(self: RocStr) [*]u8 {
|
||||
|
||||
// Since this conditional would be prone to branch misprediction,
|
||||
// make sure it will compile to a cmov.
|
||||
// return if (self.isSmallStr() or self.isEmpty()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
||||
@ -429,18 +430,14 @@ fn strFromIntHelp(comptime T: type, int: T) RocStr {
|
||||
|
||||
// Str.fromFloat
|
||||
pub fn strFromFloatC(float: f64) callconv(.C) RocStr {
|
||||
// NOTE the compiled zig for float formatting seems to use LLVM11-specific features
|
||||
// hopefully we can use zig instead of snprintf in the future when we upgrade
|
||||
const c = @cImport({
|
||||
// See https://github.com/ziglang/zig/issues/515
|
||||
@cDefine("_NO_CRT_STDIO_INLINE", "1");
|
||||
@cInclude("stdio.h");
|
||||
});
|
||||
return @call(.{ .modifier = always_inline }, strFromFloatHelp, .{ f64, float });
|
||||
}
|
||||
|
||||
fn strFromFloatHelp(comptime T: type, float: T) RocStr {
|
||||
var buf: [100]u8 = undefined;
|
||||
const result = std.fmt.bufPrint(&buf, "{d}", .{float}) catch unreachable;
|
||||
|
||||
const result = c.snprintf(&buf, 100, "%f", float);
|
||||
|
||||
return RocStr.init(&buf, @intCast(usize, result));
|
||||
return RocStr.init(&buf, result.len);
|
||||
}
|
||||
|
||||
// Str.split
|
||||
|
@ -115,7 +115,7 @@ pub fn decref(
|
||||
|
||||
var bytes = bytes_or_null orelse return;
|
||||
|
||||
const isizes: [*]isize = @ptrCast([*]isize, @alignCast(8, bytes));
|
||||
const isizes: [*]isize = @ptrCast([*]isize, @alignCast(@sizeOf(isize), bytes));
|
||||
|
||||
const refcount = (isizes - 1)[0];
|
||||
const refcount_isize = @bitCast(isize, refcount);
|
||||
@ -128,6 +128,20 @@ pub fn decref(
|
||||
(isizes - 1)[0] = refcount - 1;
|
||||
}
|
||||
},
|
||||
8 => {
|
||||
if (refcount == REFCOUNT_ONE_ISIZE) {
|
||||
dealloc(bytes - 8, alignment);
|
||||
} else if (refcount_isize < 0) {
|
||||
(isizes - 1)[0] = refcount - 1;
|
||||
}
|
||||
},
|
||||
4 => {
|
||||
if (refcount == REFCOUNT_ONE_ISIZE) {
|
||||
dealloc(bytes - 4, alignment);
|
||||
} else if (refcount_isize < 0) {
|
||||
(isizes - 1)[0] = refcount - 1;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
// NOTE enums can currently have an alignment of < 8
|
||||
if (refcount == REFCOUNT_ONE_ISIZE) {
|
||||
|
@ -31,32 +31,59 @@ fn main() {
|
||||
let build_script_dir_path = fs::canonicalize(Path::new(".")).unwrap();
|
||||
let bitcode_path = build_script_dir_path.join("bitcode");
|
||||
|
||||
let src_obj_path = bitcode_path.join("builtins.o");
|
||||
let src_obj_path = bitcode_path.join("builtins-64bit.o");
|
||||
let src_obj = src_obj_path.to_str().expect("Invalid src object path");
|
||||
|
||||
let dest_ir_path = bitcode_path.join("builtins.ll");
|
||||
let dest_ir = dest_ir_path.to_str().expect("Invalid dest ir path");
|
||||
let dest_ir_path = bitcode_path.join("builtins-32bit.ll");
|
||||
let dest_ir_32bit = dest_ir_path.to_str().expect("Invalid dest ir path");
|
||||
|
||||
let dest_ir_path = bitcode_path.join("builtins-64bit.ll");
|
||||
let dest_ir_64bit = dest_ir_path.to_str().expect("Invalid dest ir path");
|
||||
|
||||
if use_build_script {
|
||||
println!("Compiling zig object & ir to: {} and {}", src_obj, dest_ir);
|
||||
println!(
|
||||
"Compiling zig object & ir to: {} and {}",
|
||||
src_obj, dest_ir_64bit
|
||||
);
|
||||
run_command_with_no_args(&bitcode_path, "./build.sh");
|
||||
} else {
|
||||
println!("Compiling zig object to: {}", src_obj);
|
||||
run_command(&bitcode_path, "zig", &["build", "object", "-Drelease=true"]);
|
||||
|
||||
println!("Compiling ir to: {}", dest_ir);
|
||||
println!("Compiling 64-bit ir to: {}", dest_ir_64bit);
|
||||
run_command(&bitcode_path, "zig", &["build", "ir", "-Drelease=true"]);
|
||||
|
||||
println!("Compiling 32-bit ir to: {}", dest_ir_32bit);
|
||||
run_command(
|
||||
&bitcode_path,
|
||||
"zig",
|
||||
&["build", "ir-32bit", "-Drelease=true"],
|
||||
);
|
||||
}
|
||||
|
||||
println!("Moving zig object to: {}", dest_obj);
|
||||
|
||||
run_command(&bitcode_path, "mv", &[src_obj, dest_obj]);
|
||||
|
||||
let dest_bc_path = bitcode_path.join("builtins.bc");
|
||||
let dest_bc = dest_bc_path.to_str().expect("Invalid dest bc path");
|
||||
println!("Compiling bitcode to: {}", dest_bc);
|
||||
let dest_bc_path = bitcode_path.join("builtins-32bit.bc");
|
||||
let dest_bc_32bit = dest_bc_path.to_str().expect("Invalid dest bc path");
|
||||
println!("Compiling 32-bit bitcode to: {}", dest_bc_32bit);
|
||||
|
||||
run_command(build_script_dir_path, "llvm-as", &[dest_ir, "-o", dest_bc]);
|
||||
run_command(
|
||||
&build_script_dir_path,
|
||||
"llvm-as",
|
||||
&[dest_ir_32bit, "-o", dest_bc_32bit],
|
||||
);
|
||||
|
||||
let dest_bc_path = bitcode_path.join("builtins-64bit.bc");
|
||||
let dest_bc_64bit = dest_bc_path.to_str().expect("Invalid dest bc path");
|
||||
println!("Compiling 64-bit bitcode to: {}", dest_bc_64bit);
|
||||
|
||||
run_command(
|
||||
&build_script_dir_path,
|
||||
"llvm-as",
|
||||
&[dest_ir_64bit, "-o", dest_bc_64bit],
|
||||
);
|
||||
|
||||
get_zig_files(bitcode_path.as_path(), &|path| {
|
||||
let path: &Path = path;
|
||||
|
@ -287,7 +287,7 @@ fn lowlevel_4(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def {
|
||||
fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let int_var = var_store.fresh();
|
||||
let int_precision_var = var_store.fresh();
|
||||
let body = Int(int_var, int_precision_var, i64::MAX.into());
|
||||
let body = int(int_var, int_precision_var, i64::MAX.into());
|
||||
|
||||
Def {
|
||||
annotation: None,
|
||||
@ -302,7 +302,7 @@ fn num_max_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
fn num_min_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let int_var = var_store.fresh();
|
||||
let int_precision_var = var_store.fresh();
|
||||
let body = Int(int_var, int_precision_var, i64::MIN.into());
|
||||
let body = int(int_var, int_precision_var, i64::MIN.into());
|
||||
|
||||
Def {
|
||||
annotation: None,
|
||||
@ -687,7 +687,7 @@ fn num_is_zero(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, Num(unbound_zero_var, 0)),
|
||||
(arg_var, num(unbound_zero_var, 0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
};
|
||||
@ -710,7 +710,7 @@ fn num_is_negative(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::NumGt,
|
||||
args: vec![
|
||||
(arg_var, Num(unbound_zero_var, 0)),
|
||||
(arg_var, num(unbound_zero_var, 0)),
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
@ -735,7 +735,7 @@ fn num_is_positive(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::NumGt,
|
||||
args: vec![
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, Num(unbound_zero_var, 0)),
|
||||
(arg_var, num(unbound_zero_var, 0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
};
|
||||
@ -758,14 +758,14 @@ fn num_is_odd(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(arg_var, Int(var_store.fresh(), var_store.fresh(), 1)),
|
||||
(arg_var, int(var_store.fresh(), var_store.fresh(), 1)),
|
||||
(
|
||||
arg_var,
|
||||
RunLowLevel {
|
||||
op: LowLevel::NumRemUnchecked,
|
||||
args: vec![
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, Num(unbound_two_var, 2)),
|
||||
(arg_var, num(unbound_two_var, 2)),
|
||||
],
|
||||
ret_var: arg_var,
|
||||
},
|
||||
@ -792,14 +792,14 @@ fn num_is_even(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(arg_var, Num(arg_num_var, 0)),
|
||||
(arg_var, num(arg_num_var, 0)),
|
||||
(
|
||||
arg_var,
|
||||
RunLowLevel {
|
||||
op: LowLevel::NumRemUnchecked,
|
||||
args: vec![
|
||||
(arg_var, Var(Symbol::ARG_1)),
|
||||
(arg_var, Num(arg_num_var, 2)),
|
||||
(arg_var, num(arg_num_var, 2)),
|
||||
],
|
||||
ret_var: arg_var,
|
||||
},
|
||||
@ -853,7 +853,7 @@ fn num_sqrt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::NumGte,
|
||||
args: vec![
|
||||
(float_var, Var(Symbol::ARG_1)),
|
||||
(float_var, Float(unbound_zero_var, precision_var, 0.0)),
|
||||
(float_var, float(unbound_zero_var, precision_var, 0.0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
}),
|
||||
@ -899,7 +899,7 @@ fn num_log(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::NumGt,
|
||||
args: vec![
|
||||
(float_var, Var(Symbol::ARG_1)),
|
||||
(float_var, Float(unbound_zero_var, precision_var, 0.0)),
|
||||
(float_var, float(unbound_zero_var, precision_var, 0.0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
}),
|
||||
@ -1139,7 +1139,7 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let int_var = var_store.fresh();
|
||||
let int_precision_var = var_store.fresh();
|
||||
let body = Int(int_var, int_precision_var, i128::MAX);
|
||||
let body = int(int_var, int_precision_var, i128::MAX);
|
||||
|
||||
let std = roc_builtins::std::types();
|
||||
let solved = std.get(&symbol).unwrap();
|
||||
@ -1172,7 +1172,7 @@ fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::Eq,
|
||||
args: vec![
|
||||
(len_var, Num(unbound_zero_var, 0)),
|
||||
(len_var, num(unbound_zero_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
@ -2051,7 +2051,7 @@ fn list_sum(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(closure_var, list_sum_add(num_var, var_store)),
|
||||
(num_var, Num(var_store.fresh(), 0)),
|
||||
(num_var, num(var_store.fresh(), 0)),
|
||||
],
|
||||
ret_var,
|
||||
};
|
||||
@ -2093,7 +2093,7 @@ fn list_product(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(closure_var, list_product_mul(num_var, var_store)),
|
||||
(num_var, Num(var_store.fresh(), 1)),
|
||||
(num_var, num(var_store.fresh(), 1)),
|
||||
],
|
||||
ret_var,
|
||||
};
|
||||
@ -2571,7 +2571,7 @@ fn num_rem(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(num_var, Num(unbound_zero_var, 0)),
|
||||
(num_var, num(unbound_zero_var, 0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
@ -2674,7 +2674,7 @@ fn num_div_float(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(num_var, Float(unbound_zero_var, precision_var, 0.0)),
|
||||
(num_var, float(unbound_zero_var, precision_var, 0.0)),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
},
|
||||
@ -2739,7 +2739,7 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
(num_var, Var(Symbol::ARG_2)),
|
||||
(
|
||||
num_var,
|
||||
Int(unbound_zero_var, unbound_zero_precision_var, 0),
|
||||
int(unbound_zero_var, unbound_zero_precision_var, 0),
|
||||
),
|
||||
],
|
||||
ret_var: bool_var,
|
||||
@ -2792,9 +2792,9 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let bool_var = var_store.fresh();
|
||||
let list_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
let zero_var = var_store.fresh();
|
||||
let zero_precision_var = var_store.fresh();
|
||||
let len_var = Variable::NAT;
|
||||
let zero_var = len_var;
|
||||
let zero_precision_var = Variable::NATURAL;
|
||||
let list_elem_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
@ -2809,7 +2809,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, Int(zero_var, zero_precision_var, 0)),
|
||||
(len_var, int(zero_var, zero_precision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
@ -2833,7 +2833,7 @@ fn list_first(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
op: LowLevel::ListGetUnsafe,
|
||||
args: vec![
|
||||
(list_var, Var(Symbol::ARG_1)),
|
||||
(len_var, Int(zero_var, zero_precision_var, 0)),
|
||||
(len_var, int(zero_var, zero_precision_var, 0)),
|
||||
],
|
||||
ret_var: list_elem_var,
|
||||
},
|
||||
@ -2890,7 +2890,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
RunLowLevel {
|
||||
op: LowLevel::NotEq,
|
||||
args: vec![
|
||||
(len_var, Int(num_var, num_precision_var, 0)),
|
||||
(len_var, int(num_var, num_precision_var, 0)),
|
||||
(
|
||||
len_var,
|
||||
RunLowLevel {
|
||||
@ -2929,7 +2929,7 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
ret_var: len_var,
|
||||
},
|
||||
),
|
||||
(arg_var, Int(num_var, num_precision_var, 1)),
|
||||
(arg_var, int(num_var, num_precision_var, 1)),
|
||||
],
|
||||
ret_var: len_var,
|
||||
},
|
||||
@ -3403,7 +3403,7 @@ fn num_bytes_to(symbol: Symbol, var_store: &mut VarStore, offset: i64, low_level
|
||||
add_var,
|
||||
RunLowLevel {
|
||||
ret_var: cast_var,
|
||||
args: vec![(cast_var, Num(var_store.fresh(), offset))],
|
||||
args: vec![(cast_var, num(var_store.fresh(), offset))],
|
||||
op: LowLevel::NumIntCast,
|
||||
},
|
||||
),
|
||||
@ -3489,3 +3489,18 @@ fn defn_help(
|
||||
loc_body: Box::new(no_region(body)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn int(num_var: Variable, precision_var: Variable, i: i128) -> Expr {
|
||||
Int(num_var, precision_var, i.to_string().into_boxed_str(), i)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn float(num_var: Variable, precision_var: Variable, f: f64) -> Expr {
|
||||
Float(num_var, precision_var, f.to_string().into_boxed_str(), f)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn num(num_var: Variable, i: i64) -> Expr {
|
||||
Num(num_var, i.to_string().into_boxed_str(), i)
|
||||
}
|
||||
|
@ -742,9 +742,9 @@ fn pattern_to_vars_by_symbol(
|
||||
}
|
||||
}
|
||||
|
||||
NumLiteral(_, _)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
|
@ -21,7 +21,7 @@ use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::Alias;
|
||||
use std::fmt::Debug;
|
||||
use std::{char, i64, u32};
|
||||
use std::{char, u32};
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq)]
|
||||
pub struct Output {
|
||||
@ -52,11 +52,11 @@ pub enum Expr {
|
||||
|
||||
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||
// stored in Int and Float below, which is strictly for better error messages
|
||||
Num(Variable, i64),
|
||||
Num(Variable, Box<str>, i64),
|
||||
|
||||
// Int and Float store a variable to generate better error messages
|
||||
Int(Variable, Variable, i128),
|
||||
Float(Variable, Variable, f64),
|
||||
Int(Variable, Variable, Box<str>, i128),
|
||||
Float(Variable, Variable, Box<str>, f64),
|
||||
Str(Box<str>),
|
||||
List {
|
||||
elem_var: Variable,
|
||||
@ -206,14 +206,23 @@ pub fn canonicalize_expr<'a>(
|
||||
use Expr::*;
|
||||
|
||||
let (expr, output) = match expr {
|
||||
ast::Expr::Num(string) => {
|
||||
let answer = num_expr_from_result(var_store, finish_parsing_int(*string), region, env);
|
||||
ast::Expr::Num(str) => {
|
||||
let answer = num_expr_from_result(
|
||||
var_store,
|
||||
finish_parsing_int(*str).map(|int| (*str, int)),
|
||||
region,
|
||||
env,
|
||||
);
|
||||
|
||||
(answer, Output::default())
|
||||
}
|
||||
ast::Expr::Float(string) => {
|
||||
let answer =
|
||||
float_expr_from_result(var_store, finish_parsing_float(string), region, env);
|
||||
ast::Expr::Float(str) => {
|
||||
let answer = float_expr_from_result(
|
||||
var_store,
|
||||
finish_parsing_float(str).map(|f| (*str, f)),
|
||||
region,
|
||||
env,
|
||||
);
|
||||
|
||||
(answer, Output::default())
|
||||
}
|
||||
@ -795,8 +804,16 @@ pub fn canonicalize_expr<'a>(
|
||||
is_negative,
|
||||
} => {
|
||||
// the minus sign is added before parsing, to get correct overflow/underflow behavior
|
||||
let result = finish_parsing_base(string, *base, *is_negative);
|
||||
let answer = int_expr_from_result(var_store, result, region, *base, env);
|
||||
let answer = match finish_parsing_base(string, *base, *is_negative) {
|
||||
Ok(int) => {
|
||||
// Done in this kinda round about way with intermediate variables
|
||||
// to keep borrowed values around and make this compile
|
||||
let int_string = int.to_string();
|
||||
let int_str = int_string.as_str();
|
||||
int_expr_from_result(var_store, Ok((int_str, int as i128)), region, *base, env)
|
||||
}
|
||||
Err(e) => int_expr_from_result(var_store, Err(e), region, *base, env),
|
||||
};
|
||||
|
||||
(answer, Output::default())
|
||||
}
|
||||
@ -1217,9 +1234,9 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||
match expr {
|
||||
// Num stores the `a` variable in `Num a`. Not the same as the variable
|
||||
// stored in Int and Float below, which is strictly for better error messages
|
||||
other @ Num(_, _)
|
||||
| other @ Int(_, _, _)
|
||||
| other @ Float(_, _, _)
|
||||
other @ Num(_, _, _)
|
||||
| other @ Int(_, _, _, _)
|
||||
| other @ Float(_, _, _, _)
|
||||
| other @ Str { .. }
|
||||
| other @ RuntimeError(_)
|
||||
| other @ EmptyRecord
|
||||
|
@ -382,9 +382,9 @@ fn fix_values_captured_in_closure_pattern(
|
||||
}
|
||||
}
|
||||
Identifier(_)
|
||||
| NumLiteral(_, _)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
| NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| Shadowed(_, _)
|
||||
@ -438,9 +438,9 @@ fn fix_values_captured_in_closure_expr(
|
||||
fix_values_captured_in_closure_expr(&mut loc_body.value, no_capture_symbols);
|
||||
}
|
||||
|
||||
Num(_, _)
|
||||
| Int(_, _, _)
|
||||
| Float(_, _, _)
|
||||
Num(_, _, _)
|
||||
| Int(_, _, _, _)
|
||||
| Float(_, _, _, _)
|
||||
| Str(_)
|
||||
| Var(_)
|
||||
| EmptyRecord
|
||||
|
@ -16,12 +16,12 @@ use std::i64;
|
||||
#[inline(always)]
|
||||
pub fn num_expr_from_result(
|
||||
var_store: &mut VarStore,
|
||||
result: Result<i64, (&str, IntErrorKind)>,
|
||||
result: Result<(&str, i64), (&str, IntErrorKind)>,
|
||||
region: Region,
|
||||
env: &mut Env,
|
||||
) -> Expr {
|
||||
match result {
|
||||
Ok(int) => Expr::Num(var_store.fresh(), int),
|
||||
Ok((str, num)) => Expr::Num(var_store.fresh(), (*str).into(), num),
|
||||
Err((raw, error)) => {
|
||||
// (Num *) compiles to Int if it doesn't
|
||||
// get specialized to something else first,
|
||||
@ -38,14 +38,14 @@ pub fn num_expr_from_result(
|
||||
#[inline(always)]
|
||||
pub fn int_expr_from_result(
|
||||
var_store: &mut VarStore,
|
||||
result: Result<i64, (&str, IntErrorKind)>,
|
||||
result: Result<(&str, i128), (&str, IntErrorKind)>,
|
||||
region: Region,
|
||||
base: Base,
|
||||
env: &mut Env,
|
||||
) -> Expr {
|
||||
// Int stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok(int) => Expr::Int(var_store.fresh(), var_store.fresh(), int.into()),
|
||||
Ok((str, int)) => Expr::Int(var_store.fresh(), var_store.fresh(), (*str).into(), int),
|
||||
Err((raw, error)) => {
|
||||
let runtime_error = InvalidInt(error, base, region, raw.into());
|
||||
|
||||
@ -59,13 +59,13 @@ pub fn int_expr_from_result(
|
||||
#[inline(always)]
|
||||
pub fn float_expr_from_result(
|
||||
var_store: &mut VarStore,
|
||||
result: Result<f64, (&str, FloatErrorKind)>,
|
||||
result: Result<(&str, f64), (&str, FloatErrorKind)>,
|
||||
region: Region,
|
||||
env: &mut Env,
|
||||
) -> Expr {
|
||||
// Float stores a variable to generate better error messages
|
||||
match result {
|
||||
Ok(float) => Expr::Float(var_store.fresh(), var_store.fresh(), float),
|
||||
Ok((str, float)) => Expr::Float(var_store.fresh(), var_store.fresh(), (*str).into(), float),
|
||||
Err((raw, error)) => {
|
||||
let runtime_error = InvalidFloat(error, region, raw.into());
|
||||
|
||||
|
@ -25,9 +25,9 @@ pub enum Pattern {
|
||||
ext_var: Variable,
|
||||
destructs: Vec<Located<RecordDestruct>>,
|
||||
},
|
||||
IntLiteral(Variable, i64),
|
||||
NumLiteral(Variable, i64),
|
||||
FloatLiteral(Variable, f64),
|
||||
IntLiteral(Variable, Box<str>, i64),
|
||||
NumLiteral(Variable, Box<str>, i64),
|
||||
FloatLiteral(Variable, Box<str>, f64),
|
||||
StrLiteral(Box<str>),
|
||||
Underscore,
|
||||
|
||||
@ -85,9 +85,9 @@ pub fn symbols_from_pattern_help(pattern: &Pattern, symbols: &mut Vec<Symbol>) {
|
||||
}
|
||||
}
|
||||
|
||||
NumLiteral(_, _)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| MalformedPattern(_, _)
|
||||
@ -185,13 +185,13 @@ pub fn canonicalize_pattern<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
FloatLiteral(string) => match pattern_type {
|
||||
WhenBranch => match finish_parsing_float(string) {
|
||||
FloatLiteral(str) => match pattern_type {
|
||||
WhenBranch => match finish_parsing_float(str) {
|
||||
Err(_error) => {
|
||||
let problem = MalformedPatternProblem::MalformedFloat;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(float) => Pattern::FloatLiteral(var_store.fresh(), float),
|
||||
Ok(float) => Pattern::FloatLiteral(var_store.fresh(), (*str).into(), float),
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
},
|
||||
@ -201,13 +201,13 @@ pub fn canonicalize_pattern<'a>(
|
||||
TopLevelDef | DefExpr => bad_underscore(env, region),
|
||||
},
|
||||
|
||||
NumLiteral(string) => match pattern_type {
|
||||
WhenBranch => match finish_parsing_int(string) {
|
||||
NumLiteral(str) => match pattern_type {
|
||||
WhenBranch => match finish_parsing_int(str) {
|
||||
Err(_error) => {
|
||||
let problem = MalformedPatternProblem::MalformedInt;
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(int) => Pattern::NumLiteral(var_store.fresh(), int),
|
||||
Ok(int) => Pattern::NumLiteral(var_store.fresh(), (*str).into(), int),
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
},
|
||||
@ -223,11 +223,10 @@ pub fn canonicalize_pattern<'a>(
|
||||
malformed_pattern(env, problem, region)
|
||||
}
|
||||
Ok(int) => {
|
||||
if *is_negative {
|
||||
Pattern::IntLiteral(var_store.fresh(), -int)
|
||||
} else {
|
||||
Pattern::IntLiteral(var_store.fresh(), int)
|
||||
}
|
||||
let sign_str = if *is_negative { "-" } else { "" };
|
||||
let int_str = format!("{}{}", sign_str, int.to_string()).into_boxed_str();
|
||||
let i = if *is_negative { -int } else { int };
|
||||
Pattern::IntLiteral(var_store.fresh(), int_str, i)
|
||||
}
|
||||
},
|
||||
ptype => unsupported_pattern(env, ptype, region),
|
||||
@ -473,9 +472,9 @@ fn add_bindings_from_patterns(
|
||||
answer.push((*symbol, *region));
|
||||
}
|
||||
}
|
||||
NumLiteral(_, _)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
| StrLiteral(_)
|
||||
| Underscore
|
||||
| Shadowed(_, _)
|
||||
|
@ -32,7 +32,7 @@ mod test_can {
|
||||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Float(_, _, actual) => {
|
||||
Expr::Float(_, _, _, actual) => {
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
actual => {
|
||||
@ -46,7 +46,7 @@ mod test_can {
|
||||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Int(_, _, actual) => {
|
||||
Expr::Int(_, _, _, actual) => {
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
actual => {
|
||||
@ -60,7 +60,7 @@ mod test_can {
|
||||
let actual_out = can_expr_with(&arena, test_home(), input);
|
||||
|
||||
match actual_out.loc_expr.value {
|
||||
Expr::Num(_, actual) => {
|
||||
Expr::Num(_, _, actual) => {
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
actual => {
|
||||
|
@ -96,8 +96,8 @@ pub fn constrain_expr(
|
||||
expected: Expected<Type>,
|
||||
) -> Constraint {
|
||||
match expr {
|
||||
Int(var, precision, _) => int_literal(*var, *precision, expected, region),
|
||||
Num(var, _) => exists(
|
||||
Int(var, precision, _, _) => int_literal(*var, *precision, expected, region),
|
||||
Num(var, _, _) => exists(
|
||||
vec![*var],
|
||||
Eq(
|
||||
crate::builtins::num_num(Type::Variable(*var)),
|
||||
@ -106,7 +106,7 @@ pub fn constrain_expr(
|
||||
region,
|
||||
),
|
||||
),
|
||||
Float(var, precision, _) => float_literal(*var, *precision, expected, region),
|
||||
Float(var, precision, _, _) => float_literal(*var, *precision, expected, region),
|
||||
EmptyRecord => constrain_empty_record(region, expected),
|
||||
Expr::Record { record_var, fields } => {
|
||||
if fields.is_empty() {
|
||||
|
@ -56,9 +56,9 @@ fn headers_from_annotation_help(
|
||||
| Shadowed(_, _)
|
||||
| MalformedPattern(_, _)
|
||||
| UnsupportedPattern(_)
|
||||
| NumLiteral(_, _)
|
||||
| IntLiteral(_, _)
|
||||
| FloatLiteral(_, _)
|
||||
| NumLiteral(_, _, _)
|
||||
| IntLiteral(_, _, _)
|
||||
| FloatLiteral(_, _, _)
|
||||
| StrLiteral(_) => true,
|
||||
|
||||
RecordDestructure { destructs, .. } => match annotation.value.shallow_dealias() {
|
||||
@ -143,7 +143,7 @@ pub fn constrain_pattern(
|
||||
);
|
||||
}
|
||||
|
||||
NumLiteral(var, _) => {
|
||||
NumLiteral(var, _, _) => {
|
||||
state.vars.push(*var);
|
||||
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
@ -154,7 +154,7 @@ pub fn constrain_pattern(
|
||||
));
|
||||
}
|
||||
|
||||
IntLiteral(precision_var, _) => {
|
||||
IntLiteral(precision_var, _, _) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Int,
|
||||
@ -163,7 +163,7 @@ pub fn constrain_pattern(
|
||||
));
|
||||
}
|
||||
|
||||
FloatLiteral(precision_var, _) => {
|
||||
FloatLiteral(precision_var, _, _) => {
|
||||
state.constraints.push(Constraint::Pattern(
|
||||
region,
|
||||
PatternCategory::Float,
|
||||
|
@ -21,7 +21,7 @@ roc_mono = { path = "../mono" }
|
||||
im = "14" # im and im-rc should always have the same version!
|
||||
im-rc = "14" # im and im-rc should always have the same version!
|
||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
libloading = "0.6"
|
||||
object = { version = "0.24", features = ["write"] }
|
||||
|
||||
|
@ -228,6 +228,7 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||
fn load_args<'a>(
|
||||
_symbol_map: &mut MutMap<Symbol, SymbolStorage<AArch64GeneralReg, AArch64FloatReg>>,
|
||||
_args: &'a [(Layout<'a>, Symbol)],
|
||||
_ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String> {
|
||||
Err("Loading args not yet implemented for AArch64".to_string())
|
||||
}
|
||||
@ -242,6 +243,20 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg> for AArch64Call {
|
||||
) -> Result<u32, String> {
|
||||
Err("Storing args not yet implemented for AArch64".to_string())
|
||||
}
|
||||
|
||||
fn return_struct<'a>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
_struct_offset: i32,
|
||||
_struct_size: u32,
|
||||
_field_layouts: &[Layout<'a>],
|
||||
_ret_reg: Option<AArch64GeneralReg>,
|
||||
) -> Result<(), String> {
|
||||
Err("Returning structs not yet implemented for AArch64".to_string())
|
||||
}
|
||||
|
||||
fn returns_via_arg_pointer(_ret_layout: &Layout) -> Result<bool, String> {
|
||||
Err("Returning via arg pointer not yet implemented for AArch64".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||
|
@ -10,6 +10,8 @@ use target_lexicon::Triple;
|
||||
pub mod aarch64;
|
||||
pub mod x86_64;
|
||||
|
||||
const PTR_SIZE: u32 = 64;
|
||||
|
||||
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
const GENERAL_PARAM_REGS: &'static [GeneralReg];
|
||||
const GENERAL_RETURN_REGS: &'static [GeneralReg];
|
||||
@ -49,6 +51,8 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
fn load_args<'a>(
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String>;
|
||||
|
||||
// store_args stores the args in registers and on the stack for function calling.
|
||||
@ -61,6 +65,19 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<u32, String>;
|
||||
|
||||
// return_struct returns a struct currently on the stack at `struct_offset`.
|
||||
// It does so using registers and stack as necessary.
|
||||
fn return_struct<'a>(
|
||||
buf: &mut Vec<'a, u8>,
|
||||
struct_offset: i32,
|
||||
struct_size: u32,
|
||||
field_layouts: &[Layout<'a>],
|
||||
ret_reg: Option<GeneralReg>,
|
||||
) -> Result<(), String>;
|
||||
|
||||
// returns true if the layout should be returned via an argument pointer.
|
||||
fn returns_via_arg_pointer(ret_layout: &Layout) -> Result<bool, String>;
|
||||
}
|
||||
|
||||
/// Assembler contains calls to the backend assembly generator.
|
||||
@ -160,8 +177,6 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum SymbolStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||
// These may need layout, but I am not sure.
|
||||
// I think whenever a symbol would be used, we specify layout anyways.
|
||||
GeneralReg(GeneralReg),
|
||||
FloatReg(FloatReg),
|
||||
Base(i32),
|
||||
@ -186,7 +201,7 @@ pub struct Backend64Bit<
|
||||
|
||||
last_seen_map: MutMap<Symbol, *const Stmt<'a>>,
|
||||
free_map: MutMap<*const Stmt<'a>, Vec<'a, Symbol>>,
|
||||
symbols_map: MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||
symbol_storage_map: MutMap<Symbol, SymbolStorage<GeneralReg, FloatReg>>,
|
||||
literal_map: MutMap<Symbol, Literal<'a>>,
|
||||
|
||||
// This should probably be smarter than a vec.
|
||||
@ -226,7 +241,7 @@ impl<
|
||||
relocs: bumpalo::vec!(in env.arena),
|
||||
last_seen_map: MutMap::default(),
|
||||
free_map: MutMap::default(),
|
||||
symbols_map: MutMap::default(),
|
||||
symbol_storage_map: MutMap::default(),
|
||||
literal_map: MutMap::default(),
|
||||
general_free_regs: bumpalo::vec![in env.arena],
|
||||
general_used_regs: bumpalo::vec![in env.arena],
|
||||
@ -248,7 +263,7 @@ impl<
|
||||
self.fn_call_stack_size = 0;
|
||||
self.last_seen_map.clear();
|
||||
self.free_map.clear();
|
||||
self.symbols_map.clear();
|
||||
self.symbol_storage_map.clear();
|
||||
self.buf.clear();
|
||||
self.general_used_callee_saved_regs.clear();
|
||||
self.general_free_regs.clear();
|
||||
@ -324,10 +339,14 @@ impl<
|
||||
Ok((out.into_bump_slice(), out_relocs.into_bump_slice()))
|
||||
}
|
||||
|
||||
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> Result<(), String> {
|
||||
CC::load_args(&mut self.symbols_map, args)?;
|
||||
fn load_args(
|
||||
&mut self,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String> {
|
||||
CC::load_args(&mut self.symbol_storage_map, args, ret_layout)?;
|
||||
// Update used and free regs.
|
||||
for (sym, storage) in &self.symbols_map {
|
||||
for (sym, storage) in &self.symbol_storage_map {
|
||||
match storage {
|
||||
SymbolStorage::GeneralReg(reg) | SymbolStorage::BaseAndGeneralReg(reg, _) => {
|
||||
self.general_free_regs.retain(|r| *r != *reg);
|
||||
@ -386,7 +405,7 @@ impl<
|
||||
// Put values in param regs or on top of the stack.
|
||||
let tmp_stack_size = CC::store_args(
|
||||
&mut self.buf,
|
||||
&self.symbols_map,
|
||||
&self.symbol_storage_map,
|
||||
args,
|
||||
arg_layouts,
|
||||
ret_layout,
|
||||
@ -421,7 +440,7 @@ impl<
|
||||
_cond_layout: &Layout<'a>, // cond_layout must be a integer due to potential jump table optimizations.
|
||||
branches: &'a [(u64, BranchInfo<'a>, Stmt<'a>)],
|
||||
default_branch: &(BranchInfo<'a>, &'a Stmt<'a>),
|
||||
_ret_layout: &Layout<'a>,
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String> {
|
||||
// Switches are a little complex due to keeping track of jumps.
|
||||
// In general I am trying to not have to loop over things multiple times or waste memory.
|
||||
@ -439,7 +458,7 @@ impl<
|
||||
let start_offset = ASM::jne_reg64_imm64_imm32(&mut self.buf, cond_reg, *val, 0);
|
||||
|
||||
// Build all statements in this branch.
|
||||
self.build_stmt(stmt)?;
|
||||
self.build_stmt(stmt, ret_layout)?;
|
||||
|
||||
// Build unconditional jump to the end of this switch.
|
||||
// Since we don't know the offset yet, set it to 0 and overwrite later.
|
||||
@ -463,7 +482,7 @@ impl<
|
||||
}
|
||||
let (branch_info, stmt) = default_branch;
|
||||
if let BranchInfo::None = branch_info {
|
||||
self.build_stmt(stmt)?;
|
||||
self.build_stmt(stmt, ret_layout)?;
|
||||
|
||||
// Update all return jumps to jump past the default case.
|
||||
let ret_offset = self.buf.len();
|
||||
@ -560,6 +579,61 @@ impl<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_struct(
|
||||
&mut self,
|
||||
sym: &Symbol,
|
||||
layout: &Layout<'a>,
|
||||
fields: &'a [Symbol],
|
||||
) -> Result<(), String> {
|
||||
if let Layout::Struct(field_layouts) = layout {
|
||||
let struct_size = layout.stack_size(PTR_SIZE);
|
||||
if struct_size > 0 {
|
||||
let offset = self.increase_stack_size(struct_size)?;
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::Base(offset));
|
||||
|
||||
let mut current_offset = offset;
|
||||
for (field, field_layout) in fields.iter().zip(field_layouts.iter()) {
|
||||
self.copy_symbol_to_stack_offset(current_offset, field, field_layout)?;
|
||||
let field_size = field_layout.stack_size(PTR_SIZE);
|
||||
current_offset += field_size as i32;
|
||||
}
|
||||
} else {
|
||||
self.symbol_storage_map.insert(*sym, SymbolStorage::Base(0));
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
// This is a single element struct. Just copy the single field to the stack.
|
||||
let struct_size = layout.stack_size(PTR_SIZE);
|
||||
let offset = self.increase_stack_size(struct_size)?;
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::Base(offset));
|
||||
self.copy_symbol_to_stack_offset(offset, &fields[0], layout)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn load_struct_at_index(
|
||||
&mut self,
|
||||
sym: &Symbol,
|
||||
structure: &Symbol,
|
||||
index: u64,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
) -> Result<(), String> {
|
||||
if let Some(SymbolStorage::Base(struct_offset)) = self.symbol_storage_map.get(structure) {
|
||||
let mut data_offset = *struct_offset;
|
||||
for i in 0..index {
|
||||
let field_size = field_layouts[i as usize].stack_size(PTR_SIZE);
|
||||
data_offset += field_size as i32;
|
||||
}
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::Base(data_offset));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("unknown struct: {:?}", structure))
|
||||
}
|
||||
}
|
||||
|
||||
fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String> {
|
||||
match lit {
|
||||
Literal::Int(x) => {
|
||||
@ -579,7 +653,7 @@ impl<
|
||||
}
|
||||
|
||||
fn free_symbol(&mut self, sym: &Symbol) {
|
||||
self.symbols_map.remove(sym);
|
||||
self.symbol_storage_map.remove(sym);
|
||||
for i in 0..self.general_used_regs.len() {
|
||||
let (reg, saved_sym) = self.general_used_regs[i];
|
||||
if saved_sym == *sym {
|
||||
@ -590,8 +664,8 @@ impl<
|
||||
}
|
||||
}
|
||||
|
||||
fn return_symbol(&mut self, sym: &Symbol) -> Result<(), String> {
|
||||
let val = self.symbols_map.get(sym);
|
||||
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String> {
|
||||
let val = self.symbol_storage_map.get(sym);
|
||||
match val {
|
||||
Some(SymbolStorage::GeneralReg(reg)) if *reg == CC::GENERAL_RETURN_REGS[0] => Ok(()),
|
||||
Some(SymbolStorage::GeneralReg(reg)) => {
|
||||
@ -605,6 +679,48 @@ impl<
|
||||
ASM::mov_freg64_freg64(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *reg);
|
||||
Ok(())
|
||||
}
|
||||
Some(SymbolStorage::Base(offset)) => match layout {
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
ASM::mov_reg64_base32(&mut self.buf, CC::GENERAL_RETURN_REGS[0], *offset);
|
||||
Ok(())
|
||||
}
|
||||
Layout::Builtin(Builtin::Float64) => {
|
||||
ASM::mov_freg64_base32(&mut self.buf, CC::FLOAT_RETURN_REGS[0], *offset);
|
||||
Ok(())
|
||||
}
|
||||
Layout::Struct(field_layouts) => {
|
||||
let struct_size = layout.stack_size(PTR_SIZE);
|
||||
if struct_size > 0 {
|
||||
let struct_offset = if let Some(SymbolStorage::Base(offset)) =
|
||||
self.symbol_storage_map.get(sym)
|
||||
{
|
||||
Ok(*offset)
|
||||
} else {
|
||||
Err(format!("unknown struct: {:?}", sym))
|
||||
}?;
|
||||
let ret_reg = if self.symbol_storage_map.contains_key(&Symbol::RET_POINTER)
|
||||
{
|
||||
Some(self.load_to_general_reg(&Symbol::RET_POINTER)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
CC::return_struct(
|
||||
&mut self.buf,
|
||||
struct_offset,
|
||||
struct_size,
|
||||
field_layouts,
|
||||
ret_reg,
|
||||
)
|
||||
} else {
|
||||
// Nothing to do for empty struct
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
x => Err(format!(
|
||||
"returning symbol with layout, {:?}, is not yet implemented",
|
||||
x
|
||||
)),
|
||||
},
|
||||
Some(x) => Err(format!(
|
||||
"returning symbol storage, {:?}, is not yet implemented",
|
||||
x
|
||||
@ -624,6 +740,25 @@ impl<
|
||||
CC: CallConv<GeneralReg, FloatReg>,
|
||||
> Backend64Bit<'a, GeneralReg, FloatReg, ASM, CC>
|
||||
{
|
||||
fn get_tmp_general_reg(&mut self) -> Result<GeneralReg, String> {
|
||||
if !self.general_free_regs.is_empty() {
|
||||
let free_reg = *self
|
||||
.general_free_regs
|
||||
.get(self.general_free_regs.len() - 1)
|
||||
.unwrap();
|
||||
if CC::general_callee_saved(&free_reg) {
|
||||
self.general_used_callee_saved_regs.insert(free_reg);
|
||||
}
|
||||
Ok(free_reg)
|
||||
} else if !self.general_used_regs.is_empty() {
|
||||
let (reg, sym) = self.general_used_regs.remove(0);
|
||||
self.free_to_stack(&sym)?;
|
||||
Ok(reg)
|
||||
} else {
|
||||
Err("completely out of general purpose registers".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn claim_general_reg(&mut self, sym: &Symbol) -> Result<GeneralReg, String> {
|
||||
let reg = if !self.general_free_regs.is_empty() {
|
||||
let free_reg = self.general_free_regs.pop().unwrap();
|
||||
@ -640,7 +775,7 @@ impl<
|
||||
}?;
|
||||
|
||||
self.general_used_regs.push((reg, *sym));
|
||||
self.symbols_map
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::GeneralReg(reg));
|
||||
Ok(reg)
|
||||
}
|
||||
@ -661,27 +796,28 @@ impl<
|
||||
}?;
|
||||
|
||||
self.float_used_regs.push((reg, *sym));
|
||||
self.symbols_map.insert(*sym, SymbolStorage::FloatReg(reg));
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::FloatReg(reg));
|
||||
Ok(reg)
|
||||
}
|
||||
|
||||
fn load_to_general_reg(&mut self, sym: &Symbol) -> Result<GeneralReg, String> {
|
||||
let val = self.symbols_map.remove(sym);
|
||||
let val = self.symbol_storage_map.remove(sym);
|
||||
match val {
|
||||
Some(SymbolStorage::GeneralReg(reg)) => {
|
||||
self.symbols_map
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::GeneralReg(reg));
|
||||
Ok(reg)
|
||||
}
|
||||
Some(SymbolStorage::Base(offset)) => {
|
||||
let reg = self.claim_general_reg(sym)?;
|
||||
self.symbols_map
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset));
|
||||
ASM::mov_reg64_base32(&mut self.buf, reg, offset as i32);
|
||||
Ok(reg)
|
||||
}
|
||||
Some(SymbolStorage::BaseAndGeneralReg(reg, offset)) => {
|
||||
self.symbols_map
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::BaseAndGeneralReg(reg, offset));
|
||||
Ok(reg)
|
||||
}
|
||||
@ -693,21 +829,22 @@ impl<
|
||||
}
|
||||
|
||||
fn load_to_float_reg(&mut self, sym: &Symbol) -> Result<FloatReg, String> {
|
||||
let val = self.symbols_map.remove(sym);
|
||||
let val = self.symbol_storage_map.remove(sym);
|
||||
match val {
|
||||
Some(SymbolStorage::FloatReg(reg)) => {
|
||||
self.symbols_map.insert(*sym, SymbolStorage::FloatReg(reg));
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::FloatReg(reg));
|
||||
Ok(reg)
|
||||
}
|
||||
Some(SymbolStorage::Base(offset)) => {
|
||||
let reg = self.claim_float_reg(sym)?;
|
||||
self.symbols_map
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset));
|
||||
ASM::mov_freg64_base32(&mut self.buf, reg, offset as i32);
|
||||
Ok(reg)
|
||||
}
|
||||
Some(SymbolStorage::BaseAndFloatReg(reg, offset)) => {
|
||||
self.symbols_map
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::BaseAndFloatReg(reg, offset));
|
||||
Ok(reg)
|
||||
}
|
||||
@ -719,54 +856,94 @@ impl<
|
||||
}
|
||||
|
||||
fn free_to_stack(&mut self, sym: &Symbol) -> Result<(), String> {
|
||||
let val = self.symbols_map.remove(sym);
|
||||
let val = self.symbol_storage_map.remove(sym);
|
||||
match val {
|
||||
Some(SymbolStorage::GeneralReg(reg)) => {
|
||||
let offset = self.increase_stack_size(8)? as i32;
|
||||
let offset = self.increase_stack_size(8)?;
|
||||
// For base addresssing, use the negative offset - 8.
|
||||
ASM::mov_base32_reg64(&mut self.buf, -offset - 8, reg);
|
||||
self.symbols_map
|
||||
.insert(*sym, SymbolStorage::Base(-offset - 8));
|
||||
ASM::mov_base32_reg64(&mut self.buf, offset, reg);
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::Base(offset));
|
||||
Ok(())
|
||||
}
|
||||
Some(SymbolStorage::FloatReg(reg)) => {
|
||||
let offset = self.increase_stack_size(8)? as i32;
|
||||
let offset = self.increase_stack_size(8)?;
|
||||
// For base addresssing, use the negative offset.
|
||||
ASM::mov_base32_freg64(&mut self.buf, -offset - 8, reg);
|
||||
self.symbols_map
|
||||
.insert(*sym, SymbolStorage::Base(-offset - 8));
|
||||
ASM::mov_base32_freg64(&mut self.buf, offset, reg);
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::Base(offset));
|
||||
Ok(())
|
||||
}
|
||||
Some(SymbolStorage::Base(offset)) => {
|
||||
self.symbols_map.insert(*sym, SymbolStorage::Base(offset));
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::Base(offset));
|
||||
Ok(())
|
||||
}
|
||||
Some(SymbolStorage::BaseAndGeneralReg(_, offset)) => {
|
||||
self.symbols_map.insert(*sym, SymbolStorage::Base(offset));
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::Base(offset));
|
||||
Ok(())
|
||||
}
|
||||
Some(SymbolStorage::BaseAndFloatReg(_, offset)) => {
|
||||
self.symbols_map.insert(*sym, SymbolStorage::Base(offset));
|
||||
self.symbol_storage_map
|
||||
.insert(*sym, SymbolStorage::Base(offset));
|
||||
Ok(())
|
||||
}
|
||||
None => Err(format!("Unknown symbol: {}", sym)),
|
||||
}
|
||||
}
|
||||
|
||||
/// increase_stack_size increase the current stack size and returns the offset of the stack.
|
||||
fn increase_stack_size(&mut self, amount: u32) -> Result<u32, String> {
|
||||
/// increase_stack_size increase the current stack size `amount` bytes.
|
||||
/// It returns base pointer relative offset of the new data.
|
||||
fn increase_stack_size(&mut self, amount: u32) -> Result<i32, String> {
|
||||
debug_assert!(amount > 0);
|
||||
let offset = self.stack_size;
|
||||
if let Some(new_size) = self.stack_size.checked_add(amount) {
|
||||
// Since stack size is u32, but the max offset is i32, if we pass i32 max, we have overflowed.
|
||||
if new_size > i32::MAX as u32 {
|
||||
Err("Ran out of stack space".to_string())
|
||||
} else {
|
||||
self.stack_size = new_size;
|
||||
let offset = -(self.stack_size as i32);
|
||||
Ok(offset)
|
||||
}
|
||||
} else {
|
||||
Err("Ran out of stack space".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_symbol_to_stack_offset(
|
||||
&mut self,
|
||||
to_offset: i32,
|
||||
sym: &Symbol,
|
||||
layout: &Layout<'a>,
|
||||
) -> Result<(), String> {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
let reg = self.load_to_general_reg(sym)?;
|
||||
ASM::mov_base32_reg64(&mut self.buf, to_offset, reg);
|
||||
Ok(())
|
||||
}
|
||||
Layout::Builtin(Builtin::Float64) => {
|
||||
let reg = self.load_to_float_reg(sym)?;
|
||||
ASM::mov_base32_freg64(&mut self.buf, to_offset, reg);
|
||||
Ok(())
|
||||
}
|
||||
Layout::Struct(_) if layout.safe_to_memcpy() => {
|
||||
let tmp_reg = self.get_tmp_general_reg()?;
|
||||
if let Some(SymbolStorage::Base(from_offset)) = self.symbol_storage_map.get(sym) {
|
||||
for i in 0..layout.stack_size(PTR_SIZE) as i32 {
|
||||
ASM::mov_reg64_base32(&mut self.buf, tmp_reg, from_offset + i);
|
||||
ASM::mov_base32_reg64(&mut self.buf, to_offset + i, tmp_reg);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("unknown struct: {:?}", sym))
|
||||
}
|
||||
}
|
||||
x => Err(format!(
|
||||
"copying data to the stack with layout, {:?}, not implemented yet",
|
||||
x
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage};
|
||||
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage, PTR_SIZE};
|
||||
use crate::Relocation;
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_collections::all::MutMap;
|
||||
@ -177,10 +177,18 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||
fn load_args<'a>(
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String> {
|
||||
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||
let mut general_i = 0;
|
||||
let mut float_i = 0;
|
||||
if X86_64SystemV::returns_via_arg_pointer(ret_layout)? {
|
||||
symbol_map.insert(
|
||||
Symbol::RET_POINTER,
|
||||
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[general_i]),
|
||||
);
|
||||
general_i += 1;
|
||||
}
|
||||
for (layout, sym) in args.iter() {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
@ -359,6 +367,22 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64SystemV {
|
||||
}
|
||||
Ok(stack_offset as u32)
|
||||
}
|
||||
|
||||
fn return_struct<'a>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
_struct_offset: i32,
|
||||
_struct_size: u32,
|
||||
_field_layouts: &[Layout<'a>],
|
||||
_ret_reg: Option<X86_64GeneralReg>,
|
||||
) -> Result<(), String> {
|
||||
Err("Returning structs not yet implemented for X86_64".to_string())
|
||||
}
|
||||
|
||||
fn returns_via_arg_pointer(ret_layout: &Layout) -> Result<bool, String> {
|
||||
// TODO: This may need to be more complex/extended to fully support the calling convention.
|
||||
// details here: https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf
|
||||
Ok(ret_layout.stack_size(PTR_SIZE) > 16)
|
||||
}
|
||||
}
|
||||
|
||||
impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||
@ -477,9 +501,18 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||
fn load_args<'a>(
|
||||
symbol_map: &mut MutMap<Symbol, SymbolStorage<X86_64GeneralReg, X86_64FloatReg>>,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String> {
|
||||
let mut base_offset = Self::SHADOW_SPACE_SIZE as i32 + 8; // 8 is the size of the pushed base pointer.
|
||||
for (i, (layout, sym)) in args.iter().enumerate() {
|
||||
let mut i = 0;
|
||||
if X86_64WindowsFastcall::returns_via_arg_pointer(ret_layout)? {
|
||||
symbol_map.insert(
|
||||
Symbol::RET_POINTER,
|
||||
SymbolStorage::GeneralReg(Self::GENERAL_PARAM_REGS[i]),
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
for (layout, sym) in args.iter() {
|
||||
if i < Self::GENERAL_PARAM_REGS.len() {
|
||||
match layout {
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
@ -496,6 +529,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||
));
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
} else {
|
||||
base_offset += match layout {
|
||||
Layout::Builtin(Builtin::Int64) => 8,
|
||||
@ -653,6 +687,22 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg> for X86_64WindowsFastcall {
|
||||
}
|
||||
Ok(stack_offset as u32)
|
||||
}
|
||||
|
||||
fn return_struct<'a>(
|
||||
_buf: &mut Vec<'a, u8>,
|
||||
_struct_offset: i32,
|
||||
_struct_size: u32,
|
||||
_field_layouts: &[Layout<'a>],
|
||||
_ret_reg: Option<X86_64GeneralReg>,
|
||||
) -> Result<(), String> {
|
||||
Err("Returning structs not yet implemented for X86_64WindowsFastCall".to_string())
|
||||
}
|
||||
|
||||
fn returns_via_arg_pointer(ret_layout: &Layout) -> Result<bool, String> {
|
||||
// TODO: This is not fully correct there are some exceptions for "vector" types.
|
||||
// details here: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160#return-values
|
||||
Ok(ret_layout.stack_size(PTR_SIZE) > 8)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -66,7 +66,11 @@ where
|
||||
|
||||
// load_args is used to let the backend know what the args are.
|
||||
// The backend should track these args so it can use them as needed.
|
||||
fn load_args(&mut self, args: &'a [(Layout<'a>, Symbol)]) -> Result<(), String>;
|
||||
fn load_args(
|
||||
&mut self,
|
||||
args: &'a [(Layout<'a>, Symbol)],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// Used for generating wrappers for malloc/realloc/free
|
||||
fn build_wrapped_jmp(&mut self) -> Result<(&'a [u8], u64), String>;
|
||||
@ -74,29 +78,29 @@ where
|
||||
/// build_proc creates a procedure and outputs it to the wrapped object writer.
|
||||
fn build_proc(&mut self, proc: Proc<'a>) -> Result<(&'a [u8], &[Relocation]), String> {
|
||||
self.reset();
|
||||
self.load_args(proc.args)?;
|
||||
self.load_args(proc.args, &proc.ret_layout)?;
|
||||
// let start = std::time::Instant::now();
|
||||
self.scan_ast(&proc.body);
|
||||
self.create_free_map();
|
||||
// let duration = start.elapsed();
|
||||
// println!("Time to calculate lifetimes: {:?}", duration);
|
||||
// println!("{:?}", self.last_seen_map());
|
||||
self.build_stmt(&proc.body)?;
|
||||
self.build_stmt(&proc.body, &proc.ret_layout)?;
|
||||
self.finalize()
|
||||
}
|
||||
|
||||
/// build_stmt builds a statement and outputs at the end of the buffer.
|
||||
fn build_stmt(&mut self, stmt: &Stmt<'a>) -> Result<(), String> {
|
||||
fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &Layout<'a>) -> Result<(), String> {
|
||||
match stmt {
|
||||
Stmt::Let(sym, expr, layout, following) => {
|
||||
self.build_expr(sym, expr, layout)?;
|
||||
self.free_symbols(stmt);
|
||||
self.build_stmt(following)?;
|
||||
self.build_stmt(following, ret_layout)?;
|
||||
Ok(())
|
||||
}
|
||||
Stmt::Ret(sym) => {
|
||||
self.load_literal_symbols(&[*sym])?;
|
||||
self.return_symbol(sym)?;
|
||||
self.return_symbol(sym, ret_layout)?;
|
||||
self.free_symbols(stmt);
|
||||
Ok(())
|
||||
}
|
||||
@ -218,6 +222,15 @@ where
|
||||
x => Err(format!("the call type, {:?}, is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
Expr::Struct(fields) => {
|
||||
self.load_literal_symbols(fields)?;
|
||||
self.create_struct(sym, layout, fields)
|
||||
}
|
||||
Expr::StructAtIndex {
|
||||
index,
|
||||
field_layouts,
|
||||
structure,
|
||||
} => self.load_struct_at_index(sym, structure, *index, field_layouts),
|
||||
x => Err(format!("the expression, {:?}, is not yet implemented", x)),
|
||||
}
|
||||
}
|
||||
@ -377,11 +390,28 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// create_struct creates a struct with the elements specified loaded into it as data.
|
||||
fn create_struct(
|
||||
&mut self,
|
||||
sym: &Symbol,
|
||||
layout: &Layout<'a>,
|
||||
fields: &'a [Symbol],
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// load_struct_at_index loads into `sym` the value at `index` in `structure`.
|
||||
fn load_struct_at_index(
|
||||
&mut self,
|
||||
sym: &Symbol,
|
||||
structure: &Symbol,
|
||||
index: u64,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// load_literal sets a symbol to be equal to a literal.
|
||||
fn load_literal(&mut self, sym: &Symbol, lit: &Literal<'a>) -> Result<(), String>;
|
||||
|
||||
/// return_symbol moves a symbol to the correct return location for the backend.
|
||||
fn return_symbol(&mut self, sym: &Symbol) -> Result<(), String>;
|
||||
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>) -> Result<(), String>;
|
||||
|
||||
/// free_symbols will free all symbols for the given statement.
|
||||
fn free_symbols(&mut self, stmt: &Stmt<'a>) {
|
||||
|
937
compiler/gen_dev/tests/dev_records.rs
Normal file
937
compiler/gen_dev/tests/dev_records.rs
Normal file
@ -0,0 +1,937 @@
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
|
||||
#[macro_use]
|
||||
mod helpers;
|
||||
|
||||
#[cfg(all(test, target_os = "linux", any(target_arch = "x86_64"/*, target_arch = "aarch64"*/)))]
|
||||
mod dev_records {
|
||||
#[test]
|
||||
fn basic_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ y: 17, x: 15, z: 19 }.x
|
||||
"#
|
||||
),
|
||||
15,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 15, y: 17, z: 19 }.y
|
||||
"#
|
||||
),
|
||||
17,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 15, y: 17, z: 19 }.z
|
||||
"#
|
||||
),
|
||||
19,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.x
|
||||
"#
|
||||
),
|
||||
15,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.a
|
||||
"#
|
||||
),
|
||||
12,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.b
|
||||
"#
|
||||
),
|
||||
15,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.y.c
|
||||
"#
|
||||
),
|
||||
2,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 15, y: { a: 12, b: 15, c: 2}, z: 19 }.z
|
||||
"#
|
||||
),
|
||||
19,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { y: 17.2, x: 15.1, z: 19.3 }
|
||||
|
||||
rec.x
|
||||
"#
|
||||
),
|
||||
15.1,
|
||||
f64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { y: 17.2, x: 15.1, z: 19.3 }
|
||||
|
||||
rec.y
|
||||
"#
|
||||
),
|
||||
17.2,
|
||||
f64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { y: 17.2, x: 15.1, z: 19.3 }
|
||||
|
||||
rec.z
|
||||
"#
|
||||
),
|
||||
19.3,
|
||||
f64
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn fn_record() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// getRec = \x -> { y: 17, x, z: 19 }
|
||||
|
||||
// (getRec 15).x
|
||||
// "#
|
||||
// ),
|
||||
// 15,
|
||||
// i64
|
||||
// );
|
||||
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// rec = { x: 15, y: 17, z: 19 }
|
||||
|
||||
// rec.y
|
||||
// "#
|
||||
// ),
|
||||
// 17,
|
||||
// i64
|
||||
// );
|
||||
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// rec = { x: 15, y: 17, z: 19 }
|
||||
|
||||
// rec.z
|
||||
// "#
|
||||
// ),
|
||||
// 19,
|
||||
// i64
|
||||
// );
|
||||
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// rec = { x: 15, y: 17, z: 19 }
|
||||
|
||||
// rec.z + rec.x
|
||||
// "#
|
||||
// ),
|
||||
// 34,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn def_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { y: 17, x: 15, z: 19 }
|
||||
|
||||
rec.x
|
||||
"#
|
||||
),
|
||||
15,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { x: 15, y: 17, z: 19 }
|
||||
|
||||
rec.y
|
||||
"#
|
||||
),
|
||||
17,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { x: 15, y: 17, z: 19 }
|
||||
|
||||
rec.z
|
||||
"#
|
||||
),
|
||||
19,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_on_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when { x: 0x2 } is
|
||||
{ x } -> x + 3
|
||||
"#
|
||||
),
|
||||
5,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_record_with_guard_pattern() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when { x: 0x2, y: 3.14 } is
|
||||
{ x: var } -> var + 3
|
||||
"#
|
||||
),
|
||||
5,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn let_with_record_pattern() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x } = { x: 0x2, y: 3.14 }
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
2,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_guard_pattern() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when { x: 0x2, y: 3.14 } is
|
||||
{ x: 0x4 } -> 5
|
||||
{ x } -> x + 3
|
||||
"#
|
||||
),
|
||||
5,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn twice_record_access() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = {a: 0x2, b: 0x3 }
|
||||
|
||||
x.a + x.b
|
||||
"#
|
||||
),
|
||||
5,
|
||||
i64
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn empty_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
v = {}
|
||||
|
||||
v
|
||||
"#
|
||||
),
|
||||
(),
|
||||
()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i64_record1_literal() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
{ x: 3 }
|
||||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn i64_record2_literal() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { x: 3, y: 5 }
|
||||
// "#
|
||||
// ),
|
||||
// (3, 5),
|
||||
// (i64, i64)
|
||||
// );
|
||||
// }
|
||||
|
||||
// // #[test]
|
||||
// // fn i64_record3_literal() {
|
||||
// // assert_evals_to!(
|
||||
// // indoc!(
|
||||
// // r#"
|
||||
// // { x: 3, y: 5, z: 17 }
|
||||
// // "#
|
||||
// // ),
|
||||
// // (3, 5, 17),
|
||||
// // (i64, i64, i64)
|
||||
// // );
|
||||
// // }
|
||||
|
||||
// #[test]
|
||||
// fn f64_record2_literal() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { x: 3.1, y: 5.1 }
|
||||
// "#
|
||||
// ),
|
||||
// (3.1, 5.1),
|
||||
// (f64, f64)
|
||||
// );
|
||||
// }
|
||||
|
||||
// // #[test]
|
||||
// // fn f64_record3_literal() {
|
||||
// // assert_evals_to!(
|
||||
// // indoc!(
|
||||
// // r#"
|
||||
// // { x: 3.1, y: 5.1, z: 17.1 }
|
||||
// // "#
|
||||
// // ),
|
||||
// // (3.1, 5.1, 17.1),
|
||||
// // (f64, f64, f64)
|
||||
// // );
|
||||
// // }
|
||||
|
||||
// // #[test]
|
||||
// // fn bool_record4_literal() {
|
||||
// // assert_evals_to!(
|
||||
// // indoc!(
|
||||
// // r#"
|
||||
// // record : { a : Bool, b : Bool, c : Bool, d : Bool }
|
||||
// // record = { a: True, b: True, c : True, d : Bool }
|
||||
|
||||
// // record
|
||||
// // "#
|
||||
// // ),
|
||||
// // (true, false, false, true),
|
||||
// // (bool, bool, bool, bool)
|
||||
// // );
|
||||
// // }
|
||||
|
||||
// #[test]
|
||||
// fn i64_record1_literal() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3 }
|
||||
// "#
|
||||
// ),
|
||||
// 3,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// // #[test]
|
||||
// // fn i64_record9_literal() {
|
||||
// // assert_evals_to!(
|
||||
// // indoc!(
|
||||
// // r#"
|
||||
// // { a: 3, b: 5, c: 17, d: 1, e: 9, f: 12, g: 13, h: 14, i: 15 }
|
||||
// // "#
|
||||
// // ),
|
||||
// // (3, 5, 17, 1, 9, 12, 13, 14, 15),
|
||||
// // (i64, i64, i64, i64, i64, i64, i64, i64, i64)
|
||||
// // );
|
||||
// // }
|
||||
|
||||
// // #[test]
|
||||
// // fn f64_record3_literal() {
|
||||
// // assert_evals_to!(
|
||||
// // indoc!(
|
||||
// // r#"
|
||||
// // { x: 3.1, y: 5.1, z: 17.1 }
|
||||
// // "#
|
||||
// // ),
|
||||
// // (3.1, 5.1, 17.1),
|
||||
// // (f64, f64, f64)
|
||||
// // );
|
||||
// // }
|
||||
|
||||
// #[test]
|
||||
// fn bool_literal() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// x : Bool
|
||||
// x = True
|
||||
|
||||
// x
|
||||
// "#
|
||||
// ),
|
||||
// true,
|
||||
// bool
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_when_use_default() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// app "test" provides [ main ] to "./platform"
|
||||
|
||||
// f = \r ->
|
||||
// when r is
|
||||
// { x: Blue, y ? 3 } -> y
|
||||
// { x: Red, y ? 5 } -> y
|
||||
|
||||
// main =
|
||||
// a = f { x: Blue, y: 7 }
|
||||
// b = f { x: Blue }
|
||||
// c = f { x: Red, y: 11 }
|
||||
// d = f { x: Red }
|
||||
|
||||
// a * b * c * d
|
||||
// "#
|
||||
// ),
|
||||
// 3 * 5 * 7 * 11,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_when_use_default_nested() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// f = \r ->
|
||||
// when r is
|
||||
// { x: Blue, y ? 3 } -> y
|
||||
// { x: Red, y ? 5 } -> y
|
||||
|
||||
// a = f { x: Blue, y: 7 }
|
||||
// b = f { x: Blue }
|
||||
// c = f { x: Red, y: 11 }
|
||||
// d = f { x: Red }
|
||||
|
||||
// a * b * c * d
|
||||
// "#
|
||||
// ),
|
||||
// 3 * 5 * 7 * 11,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_when_no_use_default() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// app "test" provides [ main ] to "./platform"
|
||||
|
||||
// f = \r ->
|
||||
// { x ? 10, y } = r
|
||||
// x + y
|
||||
|
||||
// main =
|
||||
// f { x: 4, y: 9 }
|
||||
// "#
|
||||
// ),
|
||||
// 13,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_when_no_use_default_nested() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// f = \r ->
|
||||
// { x ? 10, y } = r
|
||||
// x + y
|
||||
|
||||
// f { x: 4, y: 9 }
|
||||
// "#
|
||||
// ),
|
||||
// 13,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_let_use_default() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// app "test" provides [ main ] to "./platform"
|
||||
|
||||
// f = \r ->
|
||||
// { x ? 10, y } = r
|
||||
// x + y
|
||||
|
||||
// main =
|
||||
// f { y: 9 }
|
||||
// "#
|
||||
// ),
|
||||
// 19,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_let_no_use_default() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// app "test" provides [ main ] to "./platform"
|
||||
|
||||
// f = \r ->
|
||||
// { x ? 10, y } = r
|
||||
// x + y
|
||||
|
||||
// main =
|
||||
// f { x: 4, y: 9 }
|
||||
// "#
|
||||
// ),
|
||||
// 13,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_let_no_use_default_nested() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// f = \r ->
|
||||
// { x ? 10, y } = r
|
||||
// x + y
|
||||
|
||||
// f { x: 4, y: 9 }
|
||||
// "#
|
||||
// ),
|
||||
// 13,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_function_use_default() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// f = \{ x ? 10, y } -> x + y
|
||||
|
||||
// f { y: 9 }
|
||||
// "#
|
||||
// ),
|
||||
// 19,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// #[ignore]
|
||||
// fn optional_field_function_no_use_default() {
|
||||
// // blocked on https://github.com/rtfeldman/roc/issues/786
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// app "test" provides [ main ] to "./platform"
|
||||
|
||||
// f = \{ x ? 10, y } -> x + y
|
||||
|
||||
// main =
|
||||
// f { x: 4, y: 9 }
|
||||
// "#
|
||||
// ),
|
||||
// 13,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// #[ignore]
|
||||
// fn optional_field_function_no_use_default_nested() {
|
||||
// // blocked on https://github.com/rtfeldman/roc/issues/786
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// f = \{ x ? 10, y } -> x + y
|
||||
|
||||
// f { x: 4, y: 9 }
|
||||
// "#
|
||||
// ),
|
||||
// 13,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_singleton_record() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when { x : 4 } is
|
||||
// { x ? 3 } -> x
|
||||
// "#
|
||||
// ),
|
||||
// 4,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn optional_field_empty_record() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// when { } is
|
||||
// { x ? 3 } -> x
|
||||
// "#
|
||||
// ),
|
||||
// 3,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_2() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { x: 3, y: 5 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5],
|
||||
// [i64; 2]
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_3() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { x: 3, y: 5, z: 4 }
|
||||
// "#
|
||||
// ),
|
||||
// (3, 5, 4),
|
||||
// (i64, i64, i64)
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_4() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3, b: 5, c: 4, d: 2 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5, 4, 2],
|
||||
// [i64; 4]
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_5() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3, b: 5, c: 4, d: 2, e: 1 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5, 4, 2, 1],
|
||||
// [i64; 5]
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_6() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3, b: 5, c: 4, d: 2, e: 1, f: 7 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5, 4, 2, 1, 7],
|
||||
// [i64; 6]
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_7() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3, b: 5, c: 4, d: 2, e: 1, f: 7, g: 8 }
|
||||
// "#
|
||||
// ),
|
||||
// [3, 5, 4, 2, 1, 7, 8],
|
||||
// [i64; 7]
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_float_int() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 3.14, b: 0x1 }
|
||||
// "#
|
||||
// ),
|
||||
// (3.14, 0x1),
|
||||
// (f64, i64)
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_int_float() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 0x1, b: 3.14 }
|
||||
// "#
|
||||
// ),
|
||||
// (0x1, 3.14),
|
||||
// (i64, f64)
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_float_float() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 6.28, b: 3.14 }
|
||||
// "#
|
||||
// ),
|
||||
// (6.28, 3.14),
|
||||
// (f64, f64)
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_record_float_float_float() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { a: 6.28, b: 3.14, c: 0.1 }
|
||||
// "#
|
||||
// ),
|
||||
// (6.28, 3.14, 0.1),
|
||||
// (f64, f64, f64)
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn return_nested_record() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// { flag: 0x0, payload: { a: 6.28, b: 3.14, c: 0.1 } }
|
||||
// "#
|
||||
// ),
|
||||
// (0x0, (6.28, 3.14, 0.1)),
|
||||
// (i64, (f64, f64, f64))
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn accessor() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// .foo { foo: 4 } + .foo { bar: 6.28, foo: 3 }
|
||||
// "#
|
||||
// ),
|
||||
// 7,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn accessor_single_element_record() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// .foo { foo: 4 }
|
||||
// "#
|
||||
// ),
|
||||
// 4,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn update_record() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// rec = { foo: 42, bar: 6 }
|
||||
|
||||
// { rec & foo: rec.foo + 1 }
|
||||
// "#
|
||||
// ),
|
||||
// (6, 43),
|
||||
// (i64, i64)
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn update_single_element_record() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
rec = { foo: 42}
|
||||
|
||||
{ rec & foo: rec.foo + 1 }
|
||||
"#
|
||||
),
|
||||
43,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn booleans_in_record() {
|
||||
// assert_evals_to!(
|
||||
// indoc!("{ x: 1 == 1, y: 1 == 1 }"),
|
||||
// (true, true),
|
||||
// (bool, bool)
|
||||
// );
|
||||
// assert_evals_to!(
|
||||
// indoc!("{ x: 1 != 1, y: 1 == 1 }"),
|
||||
// (false, true),
|
||||
// (bool, bool)
|
||||
// );
|
||||
// assert_evals_to!(
|
||||
// indoc!("{ x: 1 == 1, y: 1 != 1 }"),
|
||||
// (true, false),
|
||||
// (bool, bool)
|
||||
// );
|
||||
// assert_evals_to!(
|
||||
// indoc!("{ x: 1 != 1, y: 1 != 1 }"),
|
||||
// (false, false),
|
||||
// (bool, bool)
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn alignment_in_record() {
|
||||
// assert_evals_to!(
|
||||
// indoc!("{ c: 32, b: if True then Red else if True then Green else Blue, a: 1 == 1 }"),
|
||||
// (32i64, true, 2u8),
|
||||
// (i64, bool, u8)
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn blue_and_present() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// f = \r ->
|
||||
// when r is
|
||||
// { x: Blue, y ? 3 } -> y
|
||||
// { x: Red, y ? 5 } -> y
|
||||
|
||||
// f { x: Blue, y: 7 }
|
||||
// "#
|
||||
// ),
|
||||
// 7,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn blue_and_absent() {
|
||||
// assert_evals_to!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// f = \r ->
|
||||
// when r is
|
||||
// { x: Blue, y ? 3 } -> y
|
||||
// { x: Red, y ? 5 } -> y
|
||||
|
||||
// f { x: Blue }
|
||||
// "#
|
||||
// ),
|
||||
// 3,
|
||||
// i64
|
||||
// );
|
||||
// }
|
||||
}
|
@ -16,12 +16,13 @@ roc_builtins = { path = "../builtins" }
|
||||
roc_unify = { path = "../unify" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_std = { path = "../../roc_std" }
|
||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||
im = "14" # im and im-rc should always have the same version!
|
||||
im-rc = "14" # im and im-rc should always have the same version!
|
||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
|
||||
[dev-dependencies]
|
||||
roc_can = { path = "../can" }
|
||||
@ -29,7 +30,6 @@ roc_parse = { path = "../parse" }
|
||||
roc_load = { path = "../load" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
roc_build = { path = "../build" }
|
||||
roc_std = { path = "../../roc_std" }
|
||||
pretty_assertions = "0.5.1"
|
||||
maplit = "1.0.1"
|
||||
indoc = "0.3.3"
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::llvm::bitcode::{call_bitcode_fn, call_void_bitcode_fn};
|
||||
@ -176,10 +177,22 @@ impl std::convert::TryFrom<u32> for PanicTagId {
|
||||
}
|
||||
|
||||
impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
||||
/// The integer type representing a pointer
|
||||
///
|
||||
/// on 64-bit systems, this is i64
|
||||
/// on 32-bit systems, this is i32
|
||||
pub fn ptr_int(&self) -> IntType<'ctx> {
|
||||
ptr_int(self.context, self.ptr_bytes)
|
||||
}
|
||||
|
||||
/// The integer type representing a RocList or RocStr when following the C ABI
|
||||
///
|
||||
/// on 64-bit systems, this is i128
|
||||
/// on 32-bit systems, this is i64
|
||||
pub fn str_list_c_abi(&self) -> IntType<'ctx> {
|
||||
crate::llvm::convert::str_list_int(self.context, self.ptr_bytes)
|
||||
}
|
||||
|
||||
pub fn small_str_bytes(&self) -> u32 {
|
||||
self.ptr_bytes * 2
|
||||
}
|
||||
@ -370,10 +383,18 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn module_from_builtins<'ctx>(ctx: &'ctx Context, module_name: &str) -> Module<'ctx> {
|
||||
pub fn module_from_builtins<'ctx>(
|
||||
ctx: &'ctx Context,
|
||||
module_name: &str,
|
||||
ptr_bytes: u32,
|
||||
) -> Module<'ctx> {
|
||||
// In the build script for the builtins module,
|
||||
// we compile the builtins into LLVM bitcode
|
||||
let bitcode_bytes: &[u8] = include_bytes!("../../../builtins/bitcode/builtins.bc");
|
||||
let bitcode_bytes: &[u8] = match ptr_bytes {
|
||||
8 => include_bytes!("../../../builtins/bitcode/builtins-64bit.bc"),
|
||||
4 => include_bytes!("../../../builtins/bitcode/builtins-32bit.bc"),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let memory_buffer = MemoryBuffer::create_from_memory_range(bitcode_bytes, module_name);
|
||||
|
||||
@ -689,11 +710,6 @@ pub fn float_with_precision<'a, 'ctx, 'env>(
|
||||
precision: &Builtin,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
match precision {
|
||||
Builtin::Decimal => call_bitcode_fn(
|
||||
env,
|
||||
&[env.context.f64_type().const_float(value).into()],
|
||||
bitcode::DEC_FROM_F64,
|
||||
),
|
||||
Builtin::Float64 => env.context.f64_type().const_float(value).into(),
|
||||
Builtin::Float32 => env.context.f32_type().const_float(value).into(),
|
||||
_ => panic!("Invalid layout for float literal = {:?}", precision),
|
||||
@ -718,6 +734,11 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||
_ => panic!("Invalid layout for float literal = {:?}", layout),
|
||||
},
|
||||
|
||||
Decimal(int) => env
|
||||
.context
|
||||
.i128_type()
|
||||
.const_int(int.0 as u64, false)
|
||||
.into(),
|
||||
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
|
||||
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
||||
Str(str_literal) => {
|
||||
@ -927,11 +948,7 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||
CallType::Foreign {
|
||||
foreign_symbol,
|
||||
ret_layout,
|
||||
} => {
|
||||
// we always initially invoke foreign symbols, but if there is nothing to clean up,
|
||||
// we emit a normal call
|
||||
build_foreign_symbol(env, scope, foreign_symbol, arguments, ret_layout)
|
||||
}
|
||||
} => build_foreign_symbol(env, scope, foreign_symbol, arguments, ret_layout),
|
||||
}
|
||||
}
|
||||
|
||||
@ -3931,6 +3948,8 @@ pub fn get_call_conventions(cc: target_lexicon::CallingConvention) -> u32 {
|
||||
SystemV => C_CALL_CONV,
|
||||
WasmBasicCAbi => C_CALL_CONV,
|
||||
WindowsFastcall => C_CALL_CONV,
|
||||
AppleAarch64 => C_CALL_CONV,
|
||||
_ => C_CALL_CONV,
|
||||
}
|
||||
}
|
||||
|
||||
@ -4429,6 +4448,11 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix me! I should be different in tests vs. user code!
|
||||
fn expect_failed() {
|
||||
panic!("An expectation failed!");
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn run_low_level<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
@ -4439,6 +4463,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
op: LowLevel,
|
||||
args: &[Symbol],
|
||||
update_mode: Option<UpdateMode>,
|
||||
// expect_failed: *const (),
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use LowLevel::*;
|
||||
|
||||
@ -5157,8 +5182,20 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
|
||||
bd.position_at_end(throw_block);
|
||||
|
||||
throw_exception(env, "assert failed!");
|
||||
let fn_ptr_type = context
|
||||
.void_type()
|
||||
.fn_type(&[], false)
|
||||
.ptr_type(AddressSpace::Generic);
|
||||
let fn_addr = env
|
||||
.ptr_int()
|
||||
.const_int(expect_failed as *const () as u64, false);
|
||||
let func: PointerValue<'ctx> =
|
||||
bd.build_int_to_ptr(fn_addr, fn_ptr_type, "cast_expect_failed_addr_to_ptr");
|
||||
let callable = CallableValue::try_from(func).unwrap();
|
||||
|
||||
bd.build_call(callable, &[], "call_expect_failed");
|
||||
|
||||
bd.build_unconditional_branch(then_block);
|
||||
bd.position_at_end(then_block);
|
||||
|
||||
cond
|
||||
@ -5170,89 +5207,171 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
fn build_foreign_symbol_return_result<'a, 'ctx, 'env>(
|
||||
/// A type that is valid according to the C ABI
|
||||
///
|
||||
/// As an example, structs that fit inside an integer type should
|
||||
/// (this does not currently happen here) be coerced to that integer type.
|
||||
fn to_cc_type<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &mut Scope<'a, 'ctx>,
|
||||
foreign: &roc_module::ident::ForeignSymbol,
|
||||
arguments: &[Symbol],
|
||||
return_type: BasicTypeEnum<'ctx>,
|
||||
) -> (FunctionValue<'ctx>, &'a [BasicValueEnum<'ctx>]) {
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena);
|
||||
|
||||
for arg in arguments.iter() {
|
||||
let (value, layout) = load_symbol_and_layout(scope, arg);
|
||||
arg_vals.push(value);
|
||||
let arg_type = basic_type_from_layout(env, layout);
|
||||
debug_assert_eq!(arg_type, value.get_type());
|
||||
arg_types.push(arg_type);
|
||||
layout: &Layout<'a>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
match layout {
|
||||
Layout::Builtin(builtin) => to_cc_type_builtin(env, builtin),
|
||||
_ => {
|
||||
// TODO this is almost certainly incorrect for bigger structs
|
||||
basic_type_from_layout(env, layout)
|
||||
}
|
||||
}
|
||||
|
||||
let function_type = return_type.fn_type(&arg_types, false);
|
||||
let function = get_foreign_symbol(env, foreign.clone(), function_type);
|
||||
|
||||
(function, arg_vals.into_bump_slice())
|
||||
}
|
||||
|
||||
fn build_foreign_symbol_write_result_into_ptr<'a, 'ctx, 'env>(
|
||||
fn to_cc_type_builtin<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &mut Scope<'a, 'ctx>,
|
||||
foreign: &roc_module::ident::ForeignSymbol,
|
||||
arguments: &[Symbol],
|
||||
return_pointer: PointerValue<'ctx>,
|
||||
) -> (FunctionValue<'ctx>, &'a [BasicValueEnum<'ctx>]) {
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
let mut arg_types = Vec::with_capacity_in(arguments.len() + 1, env.arena);
|
||||
|
||||
arg_vals.push(return_pointer.into());
|
||||
arg_types.push(return_pointer.get_type().into());
|
||||
|
||||
for arg in arguments.iter() {
|
||||
let (value, layout) = load_symbol_and_layout(scope, arg);
|
||||
arg_vals.push(value);
|
||||
let arg_type = basic_type_from_layout(env, layout);
|
||||
debug_assert_eq!(arg_type, value.get_type());
|
||||
arg_types.push(arg_type);
|
||||
builtin: &Builtin<'a>,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
match builtin {
|
||||
Builtin::Int128
|
||||
| Builtin::Int64
|
||||
| Builtin::Int32
|
||||
| Builtin::Int16
|
||||
| Builtin::Int8
|
||||
| Builtin::Int1
|
||||
| Builtin::Usize
|
||||
| Builtin::Decimal
|
||||
| Builtin::Float128
|
||||
| Builtin::Float64
|
||||
| Builtin::Float32
|
||||
| Builtin::Float16 => basic_type_from_builtin(env, builtin),
|
||||
Builtin::Str | Builtin::EmptyStr | Builtin::List(_) | Builtin::EmptyList => {
|
||||
env.str_list_c_abi().into()
|
||||
}
|
||||
Builtin::Dict(_, _) | Builtin::Set(_) | Builtin::EmptyDict | Builtin::EmptySet => {
|
||||
// TODO verify this is what actually happens
|
||||
basic_type_from_builtin(env, builtin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CCReturn {
|
||||
/// Return as normal
|
||||
Return,
|
||||
/// require an extra argument, a pointer
|
||||
/// where the result is written into
|
||||
/// returns void
|
||||
ByPointer,
|
||||
/// The return type is zero-sized
|
||||
Void,
|
||||
}
|
||||
|
||||
/// According to the C ABI, how should we return a value with the given layout?
|
||||
fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn {
|
||||
let return_size = layout.stack_size(env.ptr_bytes);
|
||||
let pass_result_by_pointer = return_size > 2 * env.ptr_bytes;
|
||||
|
||||
if return_size == 0 {
|
||||
CCReturn::Void
|
||||
} else if pass_result_by_pointer {
|
||||
CCReturn::ByPointer
|
||||
} else {
|
||||
CCReturn::Return
|
||||
}
|
||||
|
||||
let function_type = env.context.void_type().fn_type(&arg_types, false);
|
||||
let function = get_foreign_symbol(env, foreign.clone(), function_type);
|
||||
|
||||
(function, arg_vals.into_bump_slice())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn build_foreign_symbol<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &mut Scope<'a, 'ctx>,
|
||||
foreign: &roc_module::ident::ForeignSymbol,
|
||||
arguments: &[Symbol],
|
||||
argument_symbols: &[Symbol],
|
||||
ret_layout: &Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let ret_type = basic_type_from_layout(env, ret_layout);
|
||||
let return_pointer = env.builder.build_alloca(ret_type, "return_value");
|
||||
let builder = env.builder;
|
||||
let context = env.context;
|
||||
|
||||
// crude approximation of the C calling convention
|
||||
let pass_result_by_pointer = ret_layout.stack_size(env.ptr_bytes) > 2 * env.ptr_bytes;
|
||||
// Here we build two functions:
|
||||
//
|
||||
// - an C_CALL_CONV extern that will be provided by the host, e.g. `roc_fx_putLine`
|
||||
// This is just a type signature that we make available to the linker,
|
||||
// and can use in the wrapper
|
||||
// - a FAST_CALL_CONV wrapper that we make here, e.g. `roc_fx_putLine_fastcc_wrapper`
|
||||
|
||||
let (function, arguments) = if pass_result_by_pointer {
|
||||
build_foreign_symbol_write_result_into_ptr(env, scope, foreign, arguments, return_pointer)
|
||||
} else {
|
||||
build_foreign_symbol_return_result(env, scope, foreign, arguments, ret_type)
|
||||
let return_type = basic_type_from_layout(env, ret_layout);
|
||||
let cc_return = to_cc_return(env, ret_layout);
|
||||
|
||||
let mut cc_argument_types = Vec::with_capacity_in(argument_symbols.len() + 1, env.arena);
|
||||
let mut fastcc_argument_types = Vec::with_capacity_in(argument_symbols.len(), env.arena);
|
||||
let mut arguments = Vec::with_capacity_in(argument_symbols.len(), env.arena);
|
||||
|
||||
for symbol in argument_symbols {
|
||||
let (value, layout) = load_symbol_and_layout(scope, symbol);
|
||||
|
||||
cc_argument_types.push(to_cc_type(env, layout));
|
||||
|
||||
let basic_type = basic_type_from_layout(env, layout);
|
||||
fastcc_argument_types.push(basic_type);
|
||||
|
||||
arguments.push(value);
|
||||
}
|
||||
|
||||
let cc_type = match cc_return {
|
||||
CCReturn::Void => env.context.void_type().fn_type(&cc_argument_types, false),
|
||||
CCReturn::ByPointer => {
|
||||
cc_argument_types.push(return_type.ptr_type(AddressSpace::Generic).into());
|
||||
env.context.void_type().fn_type(&cc_argument_types, false)
|
||||
}
|
||||
CCReturn::Return => return_type.fn_type(&cc_argument_types, false),
|
||||
};
|
||||
|
||||
let call = env.builder.build_call(function, arguments, "tmp");
|
||||
let cc_function = get_foreign_symbol(env, foreign.clone(), cc_type);
|
||||
|
||||
// this is a foreign function, use c calling convention
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
let fastcc_type = return_type.fn_type(&fastcc_argument_types, false);
|
||||
|
||||
call.try_as_basic_value();
|
||||
let fastcc_function = add_func(
|
||||
env.module,
|
||||
&format!("{}_fastcc_wrapper", foreign.as_str()),
|
||||
fastcc_type,
|
||||
Linkage::Private,
|
||||
FAST_CALL_CONV,
|
||||
);
|
||||
|
||||
if pass_result_by_pointer {
|
||||
env.builder.build_load(return_pointer, "read_result")
|
||||
} else {
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
let old = builder.get_insert_block().unwrap();
|
||||
|
||||
let entry = context.append_basic_block(fastcc_function, "entry");
|
||||
{
|
||||
builder.position_at_end(entry);
|
||||
let return_pointer = env.builder.build_alloca(return_type, "return_value");
|
||||
|
||||
let fastcc_parameters = fastcc_function.get_params();
|
||||
let mut cc_arguments = Vec::with_capacity_in(fastcc_parameters.len() + 1, env.arena);
|
||||
|
||||
for (param, cc_type) in fastcc_parameters.into_iter().zip(cc_argument_types.iter()) {
|
||||
if param.get_type() == *cc_type {
|
||||
cc_arguments.push(param);
|
||||
} else {
|
||||
let as_cc_type = complex_bitcast(env.builder, param, *cc_type, "to_cc_type");
|
||||
cc_arguments.push(as_cc_type);
|
||||
}
|
||||
}
|
||||
|
||||
if let CCReturn::ByPointer = cc_return {
|
||||
cc_arguments.push(return_pointer.into());
|
||||
}
|
||||
|
||||
let call = env.builder.build_call(cc_function, &cc_arguments, "tmp");
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
let return_value = match cc_return {
|
||||
CCReturn::Return => call.try_as_basic_value().left().unwrap(),
|
||||
|
||||
CCReturn::ByPointer => env.builder.build_load(return_pointer, "read_result"),
|
||||
CCReturn::Void => return_type.const_zero(),
|
||||
};
|
||||
|
||||
builder.build_return(Some(&return_value));
|
||||
}
|
||||
|
||||
builder.position_at_end(old);
|
||||
let call = env.builder.build_call(fastcc_function, &arguments, "tmp");
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
return call.try_as_basic_value().left().unwrap();
|
||||
}
|
||||
|
||||
fn throw_on_overflow<'a, 'ctx, 'env>(
|
||||
|
@ -6,42 +6,39 @@ use crate::llvm::build::{
|
||||
complex_bitcast, load_symbol, load_symbol_and_layout, Env, RocFunctionCall, Scope,
|
||||
};
|
||||
use crate::llvm::build_list::{layout_width, pass_as_opaque};
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::convert::{basic_type_from_layout, zig_dict_type, zig_list_type};
|
||||
use crate::llvm::refcounting::Mode;
|
||||
use inkwell::attributes::{Attribute, AttributeLoc};
|
||||
use inkwell::context::Context;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::{BasicValue, BasicValueEnum, FunctionValue, StructValue};
|
||||
use inkwell::values::{BasicValue, BasicValueEnum, FunctionValue, IntValue, StructValue};
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||
|
||||
#[repr(u8)]
|
||||
enum Alignment {
|
||||
Align16KeyFirst = 0,
|
||||
Align16ValueFirst = 1,
|
||||
Align8KeyFirst = 2,
|
||||
Align8ValueFirst = 3,
|
||||
}
|
||||
#[repr(transparent)]
|
||||
struct Alignment(u8);
|
||||
|
||||
impl Alignment {
|
||||
fn from_key_value_layout(key: &Layout, value: &Layout, ptr_bytes: u32) -> Alignment {
|
||||
let key_align = key.alignment_bytes(ptr_bytes);
|
||||
let value_align = value.alignment_bytes(ptr_bytes);
|
||||
|
||||
if key_align >= value_align {
|
||||
match key_align.max(value_align) {
|
||||
8 => Alignment::Align8KeyFirst,
|
||||
16 => Alignment::Align16KeyFirst,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
match key_align.max(value_align) {
|
||||
8 => Alignment::Align8ValueFirst,
|
||||
16 => Alignment::Align16ValueFirst,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let mut bits = key_align.max(value_align) as u8;
|
||||
debug_assert!(bits == 4 || bits == 8 || bits == 16);
|
||||
|
||||
let value_before_key_flag = 0b1000_0000;
|
||||
|
||||
if key_align < value_align {
|
||||
bits |= value_before_key_flag;
|
||||
}
|
||||
|
||||
Alignment(bits)
|
||||
}
|
||||
|
||||
fn as_int_value<'ctx>(&self, context: &'ctx Context) -> IntValue<'ctx> {
|
||||
context.i8_type().const_int(self.0 as u64, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,12 +56,11 @@ pub fn dict_len<'a, 'ctx, 'env>(
|
||||
// let dict_as_int = dict_symbol_to_i128(env, scope, dict_symbol);
|
||||
let dict_as_zig_dict = dict_symbol_to_zig_dict(env, scope, dict_symbol);
|
||||
|
||||
let dict_ptr = env
|
||||
.builder
|
||||
.build_alloca(dict_as_zig_dict.get_type(), "dict_ptr");
|
||||
env.builder.build_store(dict_ptr, dict_as_zig_dict);
|
||||
|
||||
call_bitcode_fn(env, &[dict_ptr.into()], bitcode::DICT_LEN)
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[pass_dict_c_abi(env, dict_as_zig_dict.into())],
|
||||
bitcode::DICT_LEN,
|
||||
)
|
||||
}
|
||||
Layout::Builtin(Builtin::EmptyDict) => ctx.i64_type().const_zero().into(),
|
||||
_ => unreachable!("Invalid layout given to Dict.len : {:?}", dict_layout),
|
||||
@ -95,14 +91,11 @@ pub fn dict_insert<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
||||
let value_ptr = builder.build_alloca(value.get_type(), "value_ptr");
|
||||
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
env.builder.build_store(key_ptr, key);
|
||||
env.builder.build_store(value_ptr, value);
|
||||
|
||||
@ -114,10 +107,10 @@ pub fn dict_insert<'a, 'ctx, 'env>(
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let result_ptr = builder.build_alloca(zig_dict_type, "result_ptr");
|
||||
let result_ptr = builder.build_alloca(zig_dict_type(env), "result_ptr");
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
@ -128,7 +121,7 @@ pub fn dict_insert<'a, 'ctx, 'env>(
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
|
||||
key_width.into(),
|
||||
@ -157,13 +150,10 @@ pub fn dict_remove<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
||||
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
env.builder.build_store(key_ptr, key);
|
||||
|
||||
let key_width = env
|
||||
@ -174,10 +164,10 @@ pub fn dict_remove<'a, 'ctx, 'env>(
|
||||
.ptr_int()
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let result_ptr = builder.build_alloca(zig_dict_type, "result_ptr");
|
||||
let result_ptr = builder.build_alloca(zig_dict_type(env), "result_ptr");
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
@ -188,7 +178,7 @@ pub fn dict_remove<'a, 'ctx, 'env>(
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
|
||||
key_width.into(),
|
||||
@ -216,13 +206,10 @@ pub fn dict_contains<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
||||
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
env.builder.build_store(key_ptr, key);
|
||||
|
||||
let key_width = env
|
||||
@ -234,7 +221,7 @@ pub fn dict_contains<'a, 'ctx, 'env>(
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
@ -242,7 +229,7 @@ pub fn dict_contains<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
|
||||
key_width.into(),
|
||||
@ -265,13 +252,10 @@ pub fn dict_get<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
let key_ptr = builder.build_alloca(key.get_type(), "key_ptr");
|
||||
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
env.builder.build_store(key_ptr, key);
|
||||
|
||||
let key_width = env
|
||||
@ -283,7 +267,7 @@ pub fn dict_get<'a, 'ctx, 'env>(
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
@ -294,7 +278,7 @@ pub fn dict_get<'a, 'ctx, 'env>(
|
||||
let result = call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
env.builder.build_bitcast(key_ptr, u8_ptr, "to_u8_ptr"),
|
||||
key_width.into(),
|
||||
@ -376,13 +360,6 @@ pub fn dict_elements_rc<'a, 'ctx, 'env>(
|
||||
value_layout: &Layout<'a>,
|
||||
rc_operation: Mode,
|
||||
) {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
@ -392,7 +369,7 @@ pub fn dict_elements_rc<'a, 'ctx, 'env>(
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let (key_fn, value_fn) = match rc_operation {
|
||||
Mode::Inc => (
|
||||
@ -408,7 +385,7 @@ pub fn dict_elements_rc<'a, 'ctx, 'env>(
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
@ -429,12 +406,6 @@ pub fn dict_keys<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
let zig_list_type = env.module.get_struct_type("list.RocList").unwrap();
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
@ -444,16 +415,16 @@ pub fn dict_keys<'a, 'ctx, 'env>(
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
|
||||
|
||||
let list_ptr = builder.build_alloca(zig_list_type, "list_ptr");
|
||||
let list_ptr = builder.build_alloca(zig_list_type(env), "list_ptr");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
@ -475,6 +446,26 @@ pub fn dict_keys<'a, 'ctx, 'env>(
|
||||
env.builder.build_load(list_ptr, "load_keys_list")
|
||||
}
|
||||
|
||||
fn pass_dict_c_abi<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
dict: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
match env.ptr_bytes {
|
||||
4 => {
|
||||
let target_type = env.context.custom_width_int_type(96).into();
|
||||
|
||||
complex_bitcast(env.builder, dict, target_type, "to_i96")
|
||||
}
|
||||
8 => {
|
||||
let dict_ptr = env.builder.build_alloca(zig_dict_type(env), "dict_ptr");
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
|
||||
dict_ptr.into()
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn dict_union<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
@ -486,14 +477,6 @@ pub fn dict_union<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
|
||||
let dict1_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
let dict2_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
|
||||
env.builder.build_store(dict1_ptr, dict1);
|
||||
env.builder.build_store(dict2_ptr, dict2);
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
@ -503,7 +486,7 @@ pub fn dict_union<'a, 'ctx, 'env>(
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
@ -511,13 +494,13 @@ pub fn dict_union<'a, 'ctx, 'env>(
|
||||
let inc_key_fn = build_inc_wrapper(env, layout_ids, key_layout);
|
||||
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
|
||||
|
||||
let output_ptr = builder.build_alloca(zig_dict_type, "output_ptr");
|
||||
let output_ptr = builder.build_alloca(zig_dict_type(env), "output_ptr");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict1_ptr.into(),
|
||||
dict2_ptr.into(),
|
||||
pass_dict_c_abi(env, dict1),
|
||||
pass_dict_c_abi(env, dict2),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
@ -587,12 +570,6 @@ fn dict_intersect_or_difference<'a, 'ctx, 'env>(
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
|
||||
let dict1_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
let dict2_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
|
||||
env.builder.build_store(dict1_ptr, dict1);
|
||||
env.builder.build_store(dict2_ptr, dict2);
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
@ -602,7 +579,7 @@ fn dict_intersect_or_difference<'a, 'ctx, 'env>(
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
@ -615,8 +592,8 @@ fn dict_intersect_or_difference<'a, 'ctx, 'env>(
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict1_ptr.into(),
|
||||
dict2_ptr.into(),
|
||||
pass_dict_c_abi(env, dict1),
|
||||
pass_dict_c_abi(env, dict2),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
@ -645,24 +622,20 @@ pub fn dict_walk<'a, 'ctx, 'env>(
|
||||
let builder = env.builder;
|
||||
|
||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
|
||||
let accum_bt = basic_type_from_layout(env, accum_layout);
|
||||
let accum_ptr = builder.build_alloca(accum_bt, "accum_ptr");
|
||||
env.builder.build_store(accum_ptr, accum);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let output_ptr = builder.build_alloca(accum_bt, "output_ptr");
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
pass_dict_c_abi(env, dict),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -690,12 +663,8 @@ pub fn dict_values<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let zig_dict_type = super::convert::zig_dict_type(env);
|
||||
let zig_list_type = super::convert::zig_list_type(env);
|
||||
|
||||
let dict_ptr = builder.build_alloca(zig_dict_type, "dict_ptr");
|
||||
env.builder.build_store(dict_ptr, dict);
|
||||
|
||||
let key_width = env
|
||||
.ptr_int()
|
||||
.const_int(key_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
@ -705,7 +674,7 @@ pub fn dict_values<'a, 'ctx, 'env>(
|
||||
.const_int(value_layout.stack_size(env.ptr_bytes) as u64, false);
|
||||
|
||||
let alignment = Alignment::from_key_value_layout(key_layout, value_layout, env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let inc_value_fn = build_inc_wrapper(env, layout_ids, value_layout);
|
||||
|
||||
@ -714,7 +683,7 @@ pub fn dict_values<'a, 'ctx, 'env>(
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
dict_ptr.into(),
|
||||
pass_dict_c_abi(env, dict),
|
||||
alignment_iv.into(),
|
||||
key_width.into(),
|
||||
value_width.into(),
|
||||
@ -748,7 +717,7 @@ pub fn set_from_list<'a, 'ctx, 'env>(
|
||||
let list_alloca = builder.build_alloca(list.get_type(), "list_alloca");
|
||||
let list_ptr = env.builder.build_bitcast(
|
||||
list_alloca,
|
||||
env.context.i128_type().ptr_type(AddressSpace::Generic),
|
||||
env.str_list_c_abi().ptr_type(AddressSpace::Generic),
|
||||
"to_zig_list",
|
||||
);
|
||||
|
||||
@ -764,7 +733,7 @@ pub fn set_from_list<'a, 'ctx, 'env>(
|
||||
|
||||
let alignment =
|
||||
Alignment::from_key_value_layout(key_layout, &Layout::Struct(&[]), env.ptr_bytes);
|
||||
let alignment_iv = env.context.i8_type().const_int(alignment as u64, false);
|
||||
let alignment_iv = alignment.as_int_value(env.context);
|
||||
|
||||
let hash_fn = build_hash_wrapper(env, layout_ids, key_layout);
|
||||
let eq_fn = build_eq_wrapper(env, layout_ids, key_layout);
|
||||
@ -865,11 +834,11 @@ fn dict_symbol_to_zig_dict<'a, 'ctx, 'env>(
|
||||
) -> StructValue<'ctx> {
|
||||
let dict = load_symbol(scope, &symbol);
|
||||
|
||||
let zig_dict_type = env.module.get_struct_type("dict.RocDict").unwrap();
|
||||
|
||||
complex_bitcast(env.builder, dict, zig_dict_type.into(), "dict_to_zig_dict").into_struct_value()
|
||||
}
|
||||
|
||||
fn zig_dict_type<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> inkwell::types::StructType<'ctx> {
|
||||
env.module.get_struct_type("dict.RocDict").unwrap()
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
dict,
|
||||
crate::llvm::convert::zig_dict_type(env).into(),
|
||||
"dict_to_zig_dict",
|
||||
)
|
||||
.into_struct_value()
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ fn hash_builtin<'a, 'ctx, 'env>(
|
||||
// let zig deal with big vs small string
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[seed.into(), build_str::str_to_i128(env, val).into()],
|
||||
&[seed.into(), build_str::str_to_c_abi(env, val).into()],
|
||||
bitcode::DICT_HASH_STR,
|
||||
)
|
||||
.into_int_value()
|
||||
@ -785,15 +785,7 @@ fn hash_list<'a, 'ctx, 'env>(
|
||||
env.builder.build_store(result, answer);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(
|
||||
env.builder,
|
||||
env.context,
|
||||
parent,
|
||||
ptr,
|
||||
length,
|
||||
"current_index",
|
||||
loop_fn,
|
||||
);
|
||||
incrementing_elem_loop(env, parent, ptr, length, "current_index", loop_fn);
|
||||
|
||||
env.builder.build_unconditional_branch(done_block);
|
||||
|
||||
@ -886,7 +878,7 @@ fn hash_bitcode_fn<'a, 'ctx, 'env>(
|
||||
buffer: PointerValue<'ctx>,
|
||||
width: u32,
|
||||
) -> IntValue<'ctx> {
|
||||
let num_bytes = env.context.i64_type().const_int(width as u64, false);
|
||||
let num_bytes = env.ptr_int().const_int(width as u64, false);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
|
@ -21,12 +21,12 @@ fn list_returned_from_zig<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
output: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
// per the C ABI, our list objects are passed between functions as an i128
|
||||
// per the C ABI, our list objects are passed between functions as an i128/i64
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
output,
|
||||
super::convert::zig_list_type(env).into(),
|
||||
"from_i128",
|
||||
"from_str_list_int",
|
||||
)
|
||||
}
|
||||
|
||||
@ -54,11 +54,16 @@ fn pass_element_as_opaque<'a, 'ctx, 'env>(
|
||||
)
|
||||
}
|
||||
|
||||
fn pass_list_as_i128<'a, 'ctx, 'env>(
|
||||
fn pass_list_cc<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
complex_bitcast(env.builder, list, env.context.i128_type().into(), "to_i128")
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
list,
|
||||
env.str_list_c_abi().into(),
|
||||
"to_str_list_int",
|
||||
)
|
||||
}
|
||||
|
||||
pub fn layout_width<'a, 'ctx, 'env>(
|
||||
@ -139,7 +144,7 @@ pub fn list_join<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, outer_list),
|
||||
pass_list_cc(env, outer_list),
|
||||
env.alignment_intvalue(element_layout),
|
||||
layout_width(env, element_layout),
|
||||
],
|
||||
@ -172,7 +177,7 @@ pub fn list_reverse<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
env.alignment_intvalue(&element_layout),
|
||||
layout_width(env, &element_layout),
|
||||
],
|
||||
@ -227,7 +232,7 @@ pub fn list_append<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, original_wrapper.into()),
|
||||
pass_list_cc(env, original_wrapper.into()),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
layout_width(env, element_layout),
|
||||
@ -246,7 +251,7 @@ pub fn list_prepend<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, original_wrapper.into()),
|
||||
pass_list_cc(env, original_wrapper.into()),
|
||||
env.alignment_intvalue(element_layout),
|
||||
pass_element_as_opaque(env, element),
|
||||
layout_width(env, element_layout),
|
||||
@ -266,7 +271,7 @@ pub fn list_swap<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, original_wrapper.into()),
|
||||
pass_list_cc(env, original_wrapper.into()),
|
||||
env.alignment_intvalue(element_layout),
|
||||
layout_width(env, element_layout),
|
||||
index_1.into(),
|
||||
@ -288,7 +293,7 @@ pub fn list_drop<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, original_wrapper.into()),
|
||||
pass_list_cc(env, original_wrapper.into()),
|
||||
env.alignment_intvalue(element_layout),
|
||||
layout_width(env, element_layout),
|
||||
count.into(),
|
||||
@ -406,7 +411,7 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -437,7 +442,7 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -539,7 +544,7 @@ pub fn list_contains<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
pass_element_as_opaque(env, element),
|
||||
layout_width(env, element_layout),
|
||||
eq_fn,
|
||||
@ -562,7 +567,7 @@ pub fn list_keep_if<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -604,7 +609,7 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -648,7 +653,7 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -675,7 +680,7 @@ pub fn list_sort_with<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
compare_wrapper.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -698,7 +703,7 @@ pub fn list_map_with_index<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -722,7 +727,7 @@ pub fn list_map<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list),
|
||||
pass_list_cc(env, list),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -751,8 +756,8 @@ pub fn list_map2<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list1),
|
||||
pass_list_as_i128(env, list2),
|
||||
pass_list_cc(env, list1),
|
||||
pass_list_cc(env, list2),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -787,9 +792,9 @@ pub fn list_map3<'a, 'ctx, 'env>(
|
||||
call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, list1),
|
||||
pass_list_as_i128(env, list2),
|
||||
pass_list_as_i128(env, list3),
|
||||
pass_list_cc(env, list1),
|
||||
pass_list_cc(env, list2),
|
||||
pass_list_cc(env, list3),
|
||||
roc_function_call.caller.into(),
|
||||
pass_as_opaque(env, roc_function_call.data),
|
||||
roc_function_call.inc_n_data.into(),
|
||||
@ -824,8 +829,8 @@ pub fn list_concat<'a, 'ctx, 'env>(
|
||||
Layout::Builtin(Builtin::List(elem_layout)) => call_bitcode_fn_returns_list(
|
||||
env,
|
||||
&[
|
||||
pass_list_as_i128(env, first_list),
|
||||
pass_list_as_i128(env, second_list),
|
||||
pass_list_cc(env, first_list),
|
||||
pass_list_cc(env, second_list),
|
||||
env.alignment_intvalue(elem_layout),
|
||||
layout_width(env, elem_layout),
|
||||
],
|
||||
@ -913,9 +918,8 @@ where
|
||||
index_alloca
|
||||
}
|
||||
|
||||
pub fn incrementing_elem_loop<'ctx, LoopFn>(
|
||||
builder: &Builder<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
pub fn incrementing_elem_loop<'a, 'ctx, 'env, LoopFn>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
len: IntValue<'ctx>,
|
||||
@ -925,7 +929,9 @@ pub fn incrementing_elem_loop<'ctx, LoopFn>(
|
||||
where
|
||||
LoopFn: FnMut(IntValue<'ctx>, BasicValueEnum<'ctx>),
|
||||
{
|
||||
incrementing_index_loop(builder, ctx, parent, len, index_name, |index| {
|
||||
let builder = env.builder;
|
||||
|
||||
incrementing_index_loop(env, parent, len, index_name, |index| {
|
||||
// The pointer to the element in the list
|
||||
let elem_ptr = unsafe { builder.build_in_bounds_gep(ptr, &[index], "load_index") };
|
||||
|
||||
@ -937,9 +943,8 @@ where
|
||||
|
||||
// This helper simulates a basic for loop, where
|
||||
// and index increments up from 0 to some end value
|
||||
pub fn incrementing_index_loop<'ctx, LoopFn>(
|
||||
builder: &Builder<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
pub fn incrementing_index_loop<'a, 'ctx, 'env, LoopFn>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
end: IntValue<'ctx>,
|
||||
index_name: &str,
|
||||
@ -948,12 +953,15 @@ pub fn incrementing_index_loop<'ctx, LoopFn>(
|
||||
where
|
||||
LoopFn: FnMut(IntValue<'ctx>),
|
||||
{
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// constant 1i64
|
||||
let one = ctx.i64_type().const_int(1, false);
|
||||
let one = env.ptr_int().const_int(1, false);
|
||||
|
||||
// allocate a stack slot for the current index
|
||||
let index_alloca = builder.build_alloca(ctx.i64_type(), index_name);
|
||||
builder.build_store(index_alloca, ctx.i64_type().const_zero());
|
||||
let index_alloca = builder.build_alloca(env.ptr_int(), index_name);
|
||||
builder.build_store(index_alloca, env.ptr_int().const_zero());
|
||||
|
||||
let loop_bb = ctx.append_basic_block(parent, "loop");
|
||||
builder.build_unconditional_branch(loop_bb);
|
||||
|
@ -21,12 +21,12 @@ pub fn str_split<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
|
||||
let str_i128 = str_symbol_to_i128(env, scope, str_symbol);
|
||||
let delim_i128 = str_symbol_to_i128(env, scope, delimiter_symbol);
|
||||
let str_c_abi = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
let delim_c_abi = str_symbol_to_c_abi(env, scope, delimiter_symbol);
|
||||
|
||||
let segment_count = call_bitcode_fn(
|
||||
env,
|
||||
&[str_i128.into(), delim_i128.into()],
|
||||
&[str_c_abi.into(), delim_c_abi.into()],
|
||||
bitcode::STR_COUNT_SEGMENTS,
|
||||
)
|
||||
.into_int_value();
|
||||
@ -46,26 +46,34 @@ pub fn str_split<'a, 'ctx, 'env>(
|
||||
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[ret_list_ptr_zig_rocstr, str_i128.into(), delim_i128.into()],
|
||||
&[
|
||||
ret_list_ptr_zig_rocstr,
|
||||
str_c_abi.into(),
|
||||
delim_c_abi.into(),
|
||||
],
|
||||
bitcode::STR_STR_SPLIT_IN_PLACE,
|
||||
);
|
||||
|
||||
store_list(env, ret_list_ptr, segment_count)
|
||||
}
|
||||
|
||||
fn str_symbol_to_i128<'a, 'ctx, 'env>(
|
||||
fn str_symbol_to_c_abi<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
symbol: Symbol,
|
||||
) -> IntValue<'ctx> {
|
||||
let string = load_symbol(scope, &symbol);
|
||||
|
||||
let i128_type = env.context.i128_type().into();
|
||||
let target_type = match env.ptr_bytes {
|
||||
8 => env.context.i128_type().into(),
|
||||
4 => env.context.i64_type().into(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
complex_bitcast(env.builder, string, i128_type, "str_to_i128").into_int_value()
|
||||
complex_bitcast(env.builder, string, target_type, "str_to_c_abi").into_int_value()
|
||||
}
|
||||
|
||||
pub fn str_to_i128<'a, 'ctx, 'env>(
|
||||
pub fn str_to_c_abi<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
value: BasicValueEnum<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
@ -73,17 +81,19 @@ pub fn str_to_i128<'a, 'ctx, 'env>(
|
||||
|
||||
env.builder.build_store(cell, value);
|
||||
|
||||
let i128_ptr = env
|
||||
let target_type = match env.ptr_bytes {
|
||||
8 => env.context.i128_type(),
|
||||
4 => env.context.i64_type(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let target_type_ptr = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
cell,
|
||||
env.context.i128_type().ptr_type(AddressSpace::Generic),
|
||||
"cast",
|
||||
)
|
||||
.build_bitcast(cell, target_type.ptr_type(AddressSpace::Generic), "cast")
|
||||
.into_pointer_value();
|
||||
|
||||
env.builder
|
||||
.build_load(i128_ptr, "load_as_i128")
|
||||
.build_load(target_type_ptr, "load_as_c_abi")
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
@ -113,12 +123,12 @@ pub fn str_concat<'a, 'ctx, 'env>(
|
||||
str2_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
// swap the arguments; second argument comes before the second in the output string
|
||||
let str1_i128 = str_symbol_to_i128(env, scope, str1_symbol);
|
||||
let str2_i128 = str_symbol_to_i128(env, scope, str2_symbol);
|
||||
let str1_c_abi = str_symbol_to_c_abi(env, scope, str1_symbol);
|
||||
let str2_c_abi = str_symbol_to_c_abi(env, scope, str2_symbol);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[str1_i128.into(), str2_i128.into()],
|
||||
&[str1_c_abi.into(), str2_c_abi.into()],
|
||||
bitcode::STR_CONCAT,
|
||||
)
|
||||
}
|
||||
@ -132,8 +142,8 @@ pub fn str_join_with<'a, 'ctx, 'env>(
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
// dirty hack; pretend a `list` is a `str` that works because
|
||||
// they have the same stack layout `{ u8*, usize }`
|
||||
let list_i128 = str_symbol_to_i128(env, scope, list_symbol);
|
||||
let str_i128 = str_symbol_to_i128(env, scope, str_symbol);
|
||||
let list_i128 = str_symbol_to_c_abi(env, scope, list_symbol);
|
||||
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
@ -147,7 +157,7 @@ pub fn str_number_of_bytes<'a, 'ctx, 'env>(
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
str_symbol: Symbol,
|
||||
) -> IntValue<'ctx> {
|
||||
let str_i128 = str_symbol_to_i128(env, scope, str_symbol);
|
||||
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
|
||||
// the builtin will always return an u64
|
||||
let length =
|
||||
@ -165,8 +175,8 @@ pub fn str_starts_with<'a, 'ctx, 'env>(
|
||||
str_symbol: Symbol,
|
||||
prefix_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let str_i128 = str_symbol_to_i128(env, scope, str_symbol);
|
||||
let prefix_i128 = str_symbol_to_i128(env, scope, prefix_symbol);
|
||||
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
let prefix_i128 = str_symbol_to_c_abi(env, scope, prefix_symbol);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
@ -182,7 +192,7 @@ pub fn str_starts_with_code_point<'a, 'ctx, 'env>(
|
||||
str_symbol: Symbol,
|
||||
prefix_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let str_i128 = str_symbol_to_i128(env, scope, str_symbol);
|
||||
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
let prefix = load_symbol(scope, &prefix_symbol);
|
||||
|
||||
call_bitcode_fn(
|
||||
@ -199,8 +209,8 @@ pub fn str_ends_with<'a, 'ctx, 'env>(
|
||||
str_symbol: Symbol,
|
||||
prefix_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let str_i128 = str_symbol_to_i128(env, scope, str_symbol);
|
||||
let prefix_i128 = str_symbol_to_i128(env, scope, prefix_symbol);
|
||||
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
let prefix_i128 = str_symbol_to_c_abi(env, scope, prefix_symbol);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
@ -215,7 +225,7 @@ pub fn str_count_graphemes<'a, 'ctx, 'env>(
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
str_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let str_i128 = str_symbol_to_i128(env, scope, str_symbol);
|
||||
let str_i128 = str_symbol_to_c_abi(env, scope, str_symbol);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
@ -243,7 +253,7 @@ pub fn str_to_utf8<'a, 'ctx, 'env>(
|
||||
let string = complex_bitcast(
|
||||
env.builder,
|
||||
original_wrapper.into(),
|
||||
env.context.i128_type().into(),
|
||||
env.str_list_c_abi().into(),
|
||||
"to_utf8",
|
||||
);
|
||||
|
||||
@ -269,14 +279,13 @@ pub fn str_from_utf8_range<'a, 'ctx, 'env>(
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
list_wrapper.into(),
|
||||
env.context.i128_type().into(),
|
||||
env.str_list_c_abi().into(),
|
||||
"to_i128",
|
||||
),
|
||||
// TODO: This won't work for 32 bit targets!
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
count_and_start.into(),
|
||||
env.context.i128_type().into(),
|
||||
env.str_list_c_abi().into(),
|
||||
"to_i128",
|
||||
),
|
||||
result_ptr.into(),
|
||||
@ -324,7 +333,7 @@ pub fn str_from_utf8<'a, 'ctx, 'env>(
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
original_wrapper.into(),
|
||||
env.context.i128_type().into(),
|
||||
env.str_list_c_abi().into(),
|
||||
"to_i128",
|
||||
),
|
||||
result_ptr.into(),
|
||||
@ -371,8 +380,8 @@ pub fn str_equal<'a, 'ctx, 'env>(
|
||||
value1: BasicValueEnum<'ctx>,
|
||||
value2: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let str1_i128 = str_to_i128(env, value1);
|
||||
let str2_i128 = str_to_i128(env, value2);
|
||||
let str1_i128 = str_to_c_abi(env, value1);
|
||||
let str2_i128 = str_to_c_abi(env, value2);
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
|
@ -465,8 +465,8 @@ fn build_list_eq_help<'a, 'ctx, 'env>(
|
||||
let end = len1;
|
||||
|
||||
// allocate a stack slot for the current index
|
||||
let index_alloca = builder.build_alloca(ctx.i64_type(), "index");
|
||||
builder.build_store(index_alloca, ctx.i64_type().const_zero());
|
||||
let index_alloca = builder.build_alloca(env.ptr_int(), "index");
|
||||
builder.build_store(index_alloca, env.ptr_int().const_zero());
|
||||
|
||||
let loop_bb = ctx.append_basic_block(parent, "loop");
|
||||
let body_bb = ctx.append_basic_block(parent, "body");
|
||||
@ -521,8 +521,8 @@ fn build_list_eq_help<'a, 'ctx, 'env>(
|
||||
{
|
||||
env.builder.position_at_end(increment_bb);
|
||||
|
||||
// constant 1i64
|
||||
let one = ctx.i64_type().const_int(1, false);
|
||||
// constant 1isize
|
||||
let one = env.ptr_int().const_int(1, false);
|
||||
|
||||
let next_index = builder.build_int_add(curr_index, one, "nextindex");
|
||||
|
||||
|
@ -194,6 +194,20 @@ pub fn ptr_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The int type that the C ABI turns our RocList/RocStr into
|
||||
pub fn str_list_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> {
|
||||
match ptr_bytes {
|
||||
1 => ctx.i16_type(),
|
||||
2 => ctx.i32_type(),
|
||||
4 => ctx.i64_type(),
|
||||
8 => ctx.i128_type(),
|
||||
_ => panic!(
|
||||
"Invalid target: Roc does't support compiling to {}-bit systems.",
|
||||
ptr_bytes * 8
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zig_dict_type<'a, 'ctx, 'env>(
|
||||
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||
) -> StructType<'ctx> {
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::debug_info_init;
|
||||
use crate::llvm::build::{
|
||||
add_func, cast_basic_basic, cast_block_of_memory_to_tag, get_tag_id, get_tag_id_non_recursive,
|
||||
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX,
|
||||
tag_pointer_clear_tag_id, Env, FAST_CALL_CONV, LLVM_SADD_WITH_OVERFLOW_I32,
|
||||
LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX,
|
||||
};
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
|
||||
use crate::llvm::convert::{basic_type_from_layout, ptr_int};
|
||||
@ -170,7 +171,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
||||
None => {
|
||||
// inc and dec return void
|
||||
let fn_type = context.void_type().fn_type(
|
||||
&[context.i64_type().ptr_type(AddressSpace::Generic).into()],
|
||||
&[env.ptr_int().ptr_type(AddressSpace::Generic).into()],
|
||||
false,
|
||||
);
|
||||
|
||||
@ -243,6 +244,12 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
||||
|
||||
builder.build_conditional_branch(is_static_allocation, return_block, branch_block);
|
||||
|
||||
let add_intrinsic = match env.ptr_bytes {
|
||||
8 => LLVM_SADD_WITH_OVERFLOW_I64,
|
||||
4 => LLVM_SADD_WITH_OVERFLOW_I32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let add_with_overflow;
|
||||
|
||||
{
|
||||
@ -250,7 +257,7 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
||||
|
||||
add_with_overflow = env
|
||||
.call_intrinsic(
|
||||
LLVM_SADD_WITH_OVERFLOW_I64,
|
||||
add_intrinsic,
|
||||
&[
|
||||
refcount.into(),
|
||||
refcount_type.const_int(-1_i64 as u64, true).into(),
|
||||
@ -774,7 +781,7 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
|
||||
let is_non_empty = builder.build_int_compare(
|
||||
IntPredicate::UGT,
|
||||
len,
|
||||
ctx.i64_type().const_zero(),
|
||||
env.ptr_int().const_zero(),
|
||||
"len > 0",
|
||||
);
|
||||
|
||||
@ -803,15 +810,7 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
|
||||
);
|
||||
};
|
||||
|
||||
incrementing_elem_loop(
|
||||
env.builder,
|
||||
env.context,
|
||||
parent,
|
||||
ptr,
|
||||
len,
|
||||
"modify_rc_index",
|
||||
loop_fn,
|
||||
);
|
||||
incrementing_elem_loop(env, parent, ptr, len, "modify_rc_index", loop_fn);
|
||||
}
|
||||
|
||||
let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper);
|
||||
|
@ -832,6 +832,7 @@ struct State<'a> {
|
||||
pub exposed_types: SubsByModule,
|
||||
pub output_path: Option<&'a str>,
|
||||
pub platform_path: PlatformPath<'a>,
|
||||
pub ptr_bytes: u32,
|
||||
|
||||
pub headers_parsed: MutSet<ModuleId>,
|
||||
|
||||
@ -1467,6 +1468,7 @@ where
|
||||
|
||||
let mut state = State {
|
||||
root_id,
|
||||
ptr_bytes,
|
||||
platform_data: None,
|
||||
goal_phase,
|
||||
stdlib,
|
||||
@ -1978,7 +1980,10 @@ fn update<'a>(
|
||||
);
|
||||
|
||||
if state.goal_phase > Phase::SolveTypes {
|
||||
let layout_cache = state.layout_caches.pop().unwrap_or_default();
|
||||
let layout_cache = state
|
||||
.layout_caches
|
||||
.pop()
|
||||
.unwrap_or_else(|| LayoutCache::new(state.ptr_bytes));
|
||||
|
||||
let typechecked = TypeCheckedModule {
|
||||
module_id,
|
||||
|
@ -786,6 +786,9 @@ define_builtins! {
|
||||
|
||||
// used to initialize parameters in borrow.rs
|
||||
22 EMPTY_PARAM: "#empty_param"
|
||||
|
||||
// used by the dev backend to store the pointer to where to store large return types
|
||||
23 RET_POINTER: "#ret_pointer"
|
||||
}
|
||||
1 NUM: "Num" => {
|
||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||
|
@ -13,6 +13,7 @@ roc_types = { path = "../types" }
|
||||
roc_can = { path = "../can" }
|
||||
roc_unify = { path = "../unify" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_std = { path = "../../roc_std" }
|
||||
roc_problem = { path = "../problem" }
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
||||
|
@ -1169,7 +1169,7 @@ fn literal_spec(
|
||||
|
||||
match literal {
|
||||
Str(_) => new_static_string(builder, block),
|
||||
Int(_) | Float(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]),
|
||||
Int(_) | Float(_) | Decimal(_) | Bool(_) | Byte(_) => builder.add_make_tuple(block, &[]),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ use roc_collections::all::{MutMap, MutSet};
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_std::RocDec;
|
||||
|
||||
/// COMPILE CASES
|
||||
|
||||
@ -85,8 +86,8 @@ enum Test<'a> {
|
||||
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
|
||||
},
|
||||
IsInt(i128),
|
||||
// float patterns are stored as u64 so they are comparable/hashable
|
||||
IsFloat(u64),
|
||||
IsDecimal(RocDec),
|
||||
IsStr(Box<str>),
|
||||
IsBit(bool),
|
||||
IsByte {
|
||||
@ -126,6 +127,11 @@ impl<'a> Hash for Test<'a> {
|
||||
tag_id.hash(state);
|
||||
num_alts.hash(state);
|
||||
}
|
||||
IsDecimal(v) => {
|
||||
// TODO: Is this okay?
|
||||
state.write_u8(6);
|
||||
v.0.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -302,6 +308,7 @@ fn tests_are_complete_help(last_test: &Test, number_of_tests: usize) -> bool {
|
||||
Test::IsBit(_) => number_of_tests == 2,
|
||||
Test::IsInt(_) => false,
|
||||
Test::IsFloat(_) => false,
|
||||
Test::IsDecimal(_) => false,
|
||||
Test::IsStr(_) => false,
|
||||
}
|
||||
}
|
||||
@ -556,6 +563,7 @@ fn test_at_path<'a>(
|
||||
},
|
||||
IntLiteral(v) => IsInt(*v),
|
||||
FloatLiteral(v) => IsFloat(*v),
|
||||
DecimalLiteral(v) => IsDecimal(*v),
|
||||
StrLiteral(v) => IsStr(v.clone()),
|
||||
};
|
||||
|
||||
@ -823,6 +831,18 @@ fn to_relevant_branch_help<'a>(
|
||||
_ => None,
|
||||
},
|
||||
|
||||
DecimalLiteral(dec) => match test {
|
||||
IsDecimal(test_dec) if dec.0 == test_dec.0 => {
|
||||
start.extend(end);
|
||||
Some(Branch {
|
||||
goal: branch.goal,
|
||||
guard: branch.guard.clone(),
|
||||
patterns: start,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
|
||||
BitLiteral { value: bit, .. } => match test {
|
||||
IsBit(test_bit) if bit == *test_bit => {
|
||||
start.extend(end);
|
||||
@ -910,6 +930,7 @@ fn needs_tests(pattern: &Pattern) -> bool {
|
||||
| EnumLiteral { .. }
|
||||
| IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| StrLiteral(_) => true,
|
||||
}
|
||||
}
|
||||
@ -1279,6 +1300,14 @@ fn test_to_equality<'a>(
|
||||
(stores, lhs_symbol, rhs_symbol, None)
|
||||
}
|
||||
|
||||
Test::IsDecimal(test_dec) => {
|
||||
let lhs = Expr::Literal(Literal::Int(test_dec.0));
|
||||
let lhs_symbol = env.unique_symbol();
|
||||
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int128), lhs));
|
||||
|
||||
(stores, lhs_symbol, rhs_symbol, None)
|
||||
}
|
||||
|
||||
Test::IsByte {
|
||||
tag_id: test_byte, ..
|
||||
} => {
|
||||
|
@ -2,6 +2,7 @@ use crate::ir::DestructType;
|
||||
use roc_collections::all::{Index, MutMap};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_std::RocDec;
|
||||
|
||||
use self::Pattern::*;
|
||||
|
||||
@ -56,6 +57,7 @@ pub enum Literal {
|
||||
Bit(bool),
|
||||
Byte(u8),
|
||||
Float(u64),
|
||||
Decimal(RocDec),
|
||||
Str(Box<str>),
|
||||
}
|
||||
|
||||
@ -65,6 +67,7 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
|
||||
match pattern {
|
||||
IntLiteral(v) => Literal(Literal::Int(*v)),
|
||||
FloatLiteral(v) => Literal(Literal::Float(*v)),
|
||||
DecimalLiteral(v) => Literal(Literal::Decimal(*v)),
|
||||
StrLiteral(v) => Literal(Literal::Str(v.clone())),
|
||||
|
||||
// To make sure these are exhaustive, we have to "fake" a union here
|
||||
|
@ -14,6 +14,7 @@ use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||
use roc_problem::can::RuntimeError;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_std::RocDec;
|
||||
use roc_types::solved_types::SolvedType;
|
||||
use roc_types::subs::{Content, FlatType, Subs, Variable, VariableSubsSlice};
|
||||
use std::collections::HashMap;
|
||||
@ -1024,6 +1025,7 @@ pub enum Literal<'a> {
|
||||
// Literals
|
||||
Int(i128),
|
||||
Float(f64),
|
||||
Decimal(RocDec),
|
||||
Str(&'a str),
|
||||
/// Closed tag unions containing exactly two (0-arity) tags compile to Expr::Bool,
|
||||
/// so they can (at least potentially) be emitted as 1-bit machine bools.
|
||||
@ -1205,6 +1207,8 @@ impl<'a> Literal<'a> {
|
||||
match self {
|
||||
Int(lit) => alloc.text(format!("{}i64", lit)),
|
||||
Float(lit) => alloc.text(format!("{}f64", lit)),
|
||||
// TODO: Add proper Dec.to_str
|
||||
Decimal(lit) => alloc.text(format!("{}Dec", lit.0)),
|
||||
Bool(lit) => alloc.text(format!("{}", lit)),
|
||||
Byte(lit) => alloc.text(format!("{}u8", lit)),
|
||||
Str(lit) => alloc.text(format!("{:?}", lit)),
|
||||
@ -1702,7 +1706,7 @@ fn pattern_to_when<'a>(
|
||||
(symbol, Located::at_zero(wrapped_body))
|
||||
}
|
||||
|
||||
IntLiteral(_, _) | NumLiteral(_, _) | FloatLiteral(_, _) | StrLiteral(_) => {
|
||||
IntLiteral(_, _, _) | NumLiteral(_, _, _) | FloatLiteral(_, _, _) | StrLiteral(_) => {
|
||||
// These patters are refutable, and thus should never occur outside a `when` expression
|
||||
// They should have been replaced with `UnsupportedPattern` during canonicalization
|
||||
unreachable!("refutable pattern {:?} where irrefutable pattern is expected. This should never happen!", pattern.value)
|
||||
@ -2738,17 +2742,17 @@ pub fn with_hole<'a>(
|
||||
let arena = env.arena;
|
||||
|
||||
match can_expr {
|
||||
Int(_, precision, num) => {
|
||||
Int(_, precision, _, int) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, false) {
|
||||
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num)),
|
||||
Expr::Literal(Literal::Int(int)),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::UnsignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Int(num)),
|
||||
Expr::Literal(Literal::Int(int)),
|
||||
Layout::Builtin(int_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
@ -2756,20 +2760,26 @@ pub fn with_hole<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
Float(_, precision, num) => {
|
||||
Float(_, precision, float_str, float) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, precision, true) {
|
||||
IntOrFloat::BinaryFloatType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Expr::Literal(Literal::Float(float)),
|
||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(Builtin::Decimal),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
let dec = match RocDec::from_str(&float_str) {
|
||||
Some(d) => d,
|
||||
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", float_str),
|
||||
};
|
||||
Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Decimal(dec)),
|
||||
Layout::Builtin(Builtin::Decimal),
|
||||
hole,
|
||||
)
|
||||
}
|
||||
_ => unreachable!("unexpected float precision for integer"),
|
||||
}
|
||||
}
|
||||
@ -2781,9 +2791,8 @@ pub fn with_hole<'a>(
|
||||
hole,
|
||||
),
|
||||
|
||||
Num(var, num) => {
|
||||
Num(var, num_str, num) => {
|
||||
// first figure out what kind of number this is
|
||||
|
||||
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
|
||||
IntOrFloat::SignedIntType(precision) => Stmt::Let(
|
||||
assigned,
|
||||
@ -2803,12 +2812,18 @@ pub fn with_hole<'a>(
|
||||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType => Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Float(num as f64)),
|
||||
Layout::Builtin(Builtin::Decimal),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
let dec = match RocDec::from_str(&num_str) {
|
||||
Some(d) => d,
|
||||
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", num_str),
|
||||
};
|
||||
Stmt::Let(
|
||||
assigned,
|
||||
Expr::Literal(Literal::Decimal(dec)),
|
||||
Layout::Builtin(Builtin::Decimal),
|
||||
hole,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
LetNonRec(def, cont, _) => {
|
||||
@ -3109,7 +3124,8 @@ pub fn with_hole<'a>(
|
||||
mut fields,
|
||||
..
|
||||
} => {
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
||||
let sorted_fields =
|
||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
||||
|
||||
let mut field_symbols = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
@ -3444,7 +3460,8 @@ pub fn with_hole<'a>(
|
||||
loc_expr,
|
||||
..
|
||||
} => {
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
||||
let sorted_fields =
|
||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
||||
|
||||
let mut index = None;
|
||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
@ -3586,7 +3603,8 @@ pub fn with_hole<'a>(
|
||||
// This has the benefit that we don't need to do anything special for reference
|
||||
// counting
|
||||
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, record_var, env.subs);
|
||||
let sorted_fields =
|
||||
crate::layout::sort_record_fields(env.arena, record_var, env.subs, env.ptr_bytes);
|
||||
|
||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||
|
||||
@ -4190,7 +4208,8 @@ fn convert_tag_union<'a>(
|
||||
arena: &'a Bump,
|
||||
) -> Stmt<'a> {
|
||||
use crate::layout::UnionVariant::*;
|
||||
let res_variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs);
|
||||
let res_variant =
|
||||
crate::layout::union_sorted_tags(env.arena, variant_var, env.subs, env.ptr_bytes);
|
||||
let variant = match res_variant {
|
||||
Ok(cached) => cached,
|
||||
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
|
||||
@ -4526,7 +4545,7 @@ fn sorted_field_symbols<'a>(
|
||||
}
|
||||
};
|
||||
|
||||
let alignment = layout.alignment_bytes(8);
|
||||
let alignment = layout.alignment_bytes(env.ptr_bytes);
|
||||
|
||||
let symbol = possible_reuse_symbol(env, procs, &arg.value);
|
||||
field_symbols_temp.push((alignment, symbol, ((var, arg), &*env.arena.alloc(symbol))));
|
||||
@ -5573,6 +5592,7 @@ fn store_pattern_help<'a>(
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {
|
||||
@ -5707,6 +5727,7 @@ fn store_tag_pattern<'a>(
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
@ -5782,6 +5803,7 @@ fn store_newtype_pattern<'a>(
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
@ -5857,6 +5879,7 @@ fn store_record_destruct<'a>(
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {
|
||||
@ -6828,6 +6851,7 @@ pub enum Pattern<'a> {
|
||||
Underscore,
|
||||
IntLiteral(i128),
|
||||
FloatLiteral(u64),
|
||||
DecimalLiteral(RocDec),
|
||||
BitLiteral {
|
||||
value: bool,
|
||||
tag_name: TagName,
|
||||
@ -6904,8 +6928,26 @@ fn from_can_pattern_help<'a>(
|
||||
match can_pattern {
|
||||
Underscore => Ok(Pattern::Underscore),
|
||||
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
|
||||
IntLiteral(_, int) => Ok(Pattern::IntLiteral(*int as i128)),
|
||||
FloatLiteral(_, float) => Ok(Pattern::FloatLiteral(f64::to_bits(*float))),
|
||||
IntLiteral(_, _, int) => Ok(Pattern::IntLiteral(*int as i128)),
|
||||
FloatLiteral(var, float_str, float) => {
|
||||
// TODO: Can I reuse num_argument_to_int_or_float here if I pass in true?
|
||||
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, true) {
|
||||
IntOrFloat::SignedIntType(_) => {
|
||||
panic!("Invalid percision for float literal = {:?}", var)
|
||||
}
|
||||
IntOrFloat::UnsignedIntType(_) => {
|
||||
panic!("Invalid percision for float literal = {:?}", var)
|
||||
}
|
||||
IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(f64::to_bits(*float))),
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
let dec = match RocDec::from_str(float_str) {
|
||||
Some(d) => d,
|
||||
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", float_str),
|
||||
};
|
||||
Ok(Pattern::DecimalLiteral(dec))
|
||||
}
|
||||
}
|
||||
}
|
||||
StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())),
|
||||
Shadowed(region, ident) => Err(RuntimeError::Shadowing {
|
||||
original_region: *region,
|
||||
@ -6916,12 +6958,18 @@ fn from_can_pattern_help<'a>(
|
||||
// TODO preserve malformed problem information here?
|
||||
Err(RuntimeError::UnsupportedPattern(*region))
|
||||
}
|
||||
NumLiteral(var, num) => {
|
||||
NumLiteral(var, num_str, num) => {
|
||||
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) {
|
||||
IntOrFloat::SignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)),
|
||||
IntOrFloat::UnsignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)),
|
||||
IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(*num as u64)),
|
||||
IntOrFloat::DecimalFloatType => Ok(Pattern::FloatLiteral(*num as u64)),
|
||||
IntOrFloat::DecimalFloatType => {
|
||||
let dec = match RocDec::from_str(num_str) {
|
||||
Some(d) => d,
|
||||
None => panic!("Invalid decimal for float literal = {}. TODO: Make this a nice, user-friendly error message", num_str),
|
||||
};
|
||||
Ok(Pattern::DecimalLiteral(dec))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6934,7 +6982,8 @@ fn from_can_pattern_help<'a>(
|
||||
use crate::exhaustive::Union;
|
||||
use crate::layout::UnionVariant::*;
|
||||
|
||||
let res_variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs);
|
||||
let res_variant =
|
||||
crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs, env.ptr_bytes);
|
||||
|
||||
let variant = match res_variant {
|
||||
Ok(cached) => cached,
|
||||
@ -7353,7 +7402,8 @@ fn from_can_pattern_help<'a>(
|
||||
..
|
||||
} => {
|
||||
// sorted fields based on the type
|
||||
let sorted_fields = crate::layout::sort_record_fields(env.arena, *whole_var, env.subs);
|
||||
let sorted_fields =
|
||||
crate::layout::sort_record_fields(env.arena, *whole_var, env.subs, env.ptr_bytes);
|
||||
|
||||
// sorted fields based on the destruct
|
||||
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
|
||||
@ -7494,7 +7544,9 @@ fn from_can_record_destruct<'a>(
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IntPrecision {
|
||||
Usize,
|
||||
I128,
|
||||
I64,
|
||||
I32,
|
||||
@ -7530,6 +7582,7 @@ fn int_precision_to_builtin(precision: IntPrecision) -> Builtin<'static> {
|
||||
I32 => Builtin::Int32,
|
||||
I16 => Builtin::Int16,
|
||||
I8 => Builtin::Int8,
|
||||
Usize => Builtin::Usize,
|
||||
}
|
||||
}
|
||||
|
||||
@ -7628,16 +7681,8 @@ pub fn num_argument_to_int_or_float(
|
||||
Content::Alias(Symbol::NUM_NAT, _, _)
|
||||
| Content::Alias(Symbol::NUM_NATURAL, _, _)
|
||||
| Content::Alias(Symbol::NUM_AT_NATURAL, _, _) => {
|
||||
match ptr_bytes {
|
||||
1 => IntOrFloat::UnsignedIntType(IntPrecision::I8),
|
||||
2 => IntOrFloat::UnsignedIntType(IntPrecision::I16),
|
||||
4 => IntOrFloat::UnsignedIntType(IntPrecision::I32),
|
||||
8 => IntOrFloat::UnsignedIntType(IntPrecision::I64),
|
||||
_ => panic!(
|
||||
"Invalid target for Num type argument: Roc does't support compiling to {}-bit systems.",
|
||||
ptr_bytes * 8
|
||||
),
|
||||
}
|
||||
IntOrFloat::UnsignedIntType(IntPrecision::Usize)
|
||||
|
||||
}
|
||||
other => {
|
||||
panic!(
|
||||
|
@ -138,7 +138,8 @@ impl<'a> RawFunctionLayout<'a> {
|
||||
let fn_args = fn_args.into_bump_slice();
|
||||
let ret = arena.alloc(ret);
|
||||
|
||||
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?;
|
||||
let lambda_set =
|
||||
LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?;
|
||||
|
||||
Ok(Self::Function(fn_args, lambda_set, ret))
|
||||
}
|
||||
@ -516,6 +517,7 @@ impl<'a> LambdaSet<'a> {
|
||||
arena: &'a Bump,
|
||||
subs: &Subs,
|
||||
closure_var: Variable,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<Self, LayoutProblem> {
|
||||
let mut tags = std::vec::Vec::new();
|
||||
match roc_types::pretty_print::chase_ext_tag_union(subs, closure_var, &mut tags) {
|
||||
@ -529,6 +531,7 @@ impl<'a> LambdaSet<'a> {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
for (tag_name, variables) in tags.iter() {
|
||||
@ -545,7 +548,8 @@ impl<'a> LambdaSet<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let representation = arena.alloc(Self::make_representation(arena, subs, tags));
|
||||
let representation =
|
||||
arena.alloc(Self::make_representation(arena, subs, tags, ptr_bytes));
|
||||
|
||||
Ok(LambdaSet {
|
||||
set: set.into_bump_slice(),
|
||||
@ -568,9 +572,10 @@ impl<'a> LambdaSet<'a> {
|
||||
arena: &'a Bump,
|
||||
subs: &Subs,
|
||||
tags: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
|
||||
ptr_bytes: u32,
|
||||
) -> Layout<'a> {
|
||||
// otherwise, this is a closure with a payload
|
||||
let variant = union_sorted_tags_help(arena, tags, None, subs);
|
||||
let variant = union_sorted_tags_help(arena, tags, None, subs, ptr_bytes);
|
||||
|
||||
use UnionVariant::*;
|
||||
match variant {
|
||||
@ -648,6 +653,7 @@ pub enum Builtin<'a> {
|
||||
}
|
||||
|
||||
pub struct Env<'a, 'b> {
|
||||
ptr_bytes: u32,
|
||||
arena: &'a Bump,
|
||||
seen: Vec<'a, Variable>,
|
||||
subs: &'b Subs,
|
||||
@ -972,8 +978,9 @@ impl<'a> Layout<'a> {
|
||||
/// e.g. `identity : a -> a` could be specialized to `Bool -> Bool` or `Str -> Str`.
|
||||
/// Therefore in general it's invalid to store a map from variables to layouts
|
||||
/// But if we're careful when to invalidate certain keys, we still get some benefit
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutCache<'a> {
|
||||
ptr_bytes: u32,
|
||||
_marker: std::marker::PhantomData<&'a u8>,
|
||||
}
|
||||
|
||||
@ -985,6 +992,13 @@ pub enum CachedLayout<'a> {
|
||||
}
|
||||
|
||||
impl<'a> LayoutCache<'a> {
|
||||
pub fn new(ptr_bytes: u32) -> Self {
|
||||
Self {
|
||||
ptr_bytes,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_var(
|
||||
&mut self,
|
||||
arena: &'a Bump,
|
||||
@ -998,6 +1012,7 @@ impl<'a> LayoutCache<'a> {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes: self.ptr_bytes,
|
||||
};
|
||||
|
||||
Layout::from_var(&mut env, var)
|
||||
@ -1016,6 +1031,7 @@ impl<'a> LayoutCache<'a> {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes: self.ptr_bytes,
|
||||
};
|
||||
|
||||
RawFunctionLayout::from_var(&mut env, var)
|
||||
@ -1038,7 +1054,6 @@ impl<'a> Builtin<'a> {
|
||||
const I16_SIZE: u32 = std::mem::size_of::<i16>() as u32;
|
||||
const I8_SIZE: u32 = std::mem::size_of::<i8>() as u32;
|
||||
const I1_SIZE: u32 = std::mem::size_of::<bool>() as u32;
|
||||
const USIZE_SIZE: u32 = std::mem::size_of::<usize>() as u32;
|
||||
const DECIMAL_SIZE: u32 = std::mem::size_of::<i128>() as u32;
|
||||
const F128_SIZE: u32 = 16;
|
||||
const F64_SIZE: u32 = std::mem::size_of::<f64>() as u32;
|
||||
@ -1068,7 +1083,7 @@ impl<'a> Builtin<'a> {
|
||||
Int16 => Builtin::I16_SIZE,
|
||||
Int8 => Builtin::I8_SIZE,
|
||||
Int1 => Builtin::I1_SIZE,
|
||||
Usize => Builtin::USIZE_SIZE,
|
||||
Usize => pointer_size,
|
||||
Decimal => Builtin::DECIMAL_SIZE,
|
||||
Float128 => Builtin::F128_SIZE,
|
||||
Float64 => Builtin::F64_SIZE,
|
||||
@ -1095,7 +1110,7 @@ impl<'a> Builtin<'a> {
|
||||
Int16 => align_of::<i16>() as u32,
|
||||
Int8 => align_of::<i8>() as u32,
|
||||
Int1 => align_of::<bool>() as u32,
|
||||
Usize => align_of::<usize>() as u32,
|
||||
Usize => pointer_size,
|
||||
Decimal => align_of::<i128>() as u32,
|
||||
Float128 => align_of::<i128>() as u32,
|
||||
Float64 => align_of::<f64>() as u32,
|
||||
@ -1182,6 +1197,7 @@ fn layout_from_flat_type<'a>(
|
||||
|
||||
let arena = env.arena;
|
||||
let subs = env.subs;
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
match flat_type {
|
||||
Apply(symbol, args) => {
|
||||
@ -1273,7 +1289,7 @@ fn layout_from_flat_type<'a>(
|
||||
}
|
||||
}
|
||||
Func(_, closure_var, _) => {
|
||||
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var)?;
|
||||
let lambda_set = LambdaSet::from_var(env.arena, env.subs, closure_var, env.ptr_bytes)?;
|
||||
|
||||
Ok(lambda_set.runtime_representation())
|
||||
}
|
||||
@ -1299,8 +1315,6 @@ fn layout_from_flat_type<'a>(
|
||||
let mut pairs = Vec::from_iter_in(pairs_it, arena);
|
||||
|
||||
pairs.sort_by(|(label1, layout1), (label2, layout2)| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1320,14 +1334,14 @@ fn layout_from_flat_type<'a>(
|
||||
TagUnion(tags, ext_var) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
|
||||
Ok(layout_from_tag_union(arena, tags, subs))
|
||||
Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes))
|
||||
}
|
||||
FunctionOrTagUnion(tag_name, _, ext_var) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
|
||||
let tags = UnionTags::from_tag_name_index(tag_name);
|
||||
|
||||
Ok(layout_from_tag_union(arena, tags, subs))
|
||||
Ok(layout_from_tag_union(arena, tags, subs, env.ptr_bytes))
|
||||
}
|
||||
RecursiveTagUnion(rec_var, tags, ext_var) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, ext_var));
|
||||
@ -1377,8 +1391,6 @@ fn layout_from_flat_type<'a>(
|
||||
}
|
||||
|
||||
tag_layout.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1425,11 +1437,13 @@ pub fn sort_record_fields<'a>(
|
||||
arena: &'a Bump,
|
||||
var: Variable,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
|
||||
let mut env = Env {
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
let (it, _) = gather_fields_unsorted_iter(subs, RecordFields::empty(), var);
|
||||
@ -1445,6 +1459,8 @@ fn sort_record_fields_help<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
fields_map: impl Iterator<Item = (Lowercase, RecordField<Variable>)>,
|
||||
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
|
||||
// Sort the fields by label
|
||||
let mut sorted_fields = Vec::with_capacity_in(fields_map.size_hint().0, env.arena);
|
||||
|
||||
@ -1468,8 +1484,6 @@ fn sort_record_fields_help<'a>(
|
||||
|(label1, _, res_layout1), (label2, _, res_layout2)| match res_layout1 {
|
||||
Ok(layout1) | Err(layout1) => match res_layout2 {
|
||||
Ok(layout2) | Err(layout2) => {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1605,6 +1619,7 @@ pub fn union_sorted_tags<'a>(
|
||||
arena: &'a Bump,
|
||||
var: Variable,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Result<UnionVariant<'a>, LayoutProblem> {
|
||||
let var =
|
||||
if let Content::RecursionVar { structure, .. } = subs.get_content_without_compacting(var) {
|
||||
@ -1617,7 +1632,7 @@ pub fn union_sorted_tags<'a>(
|
||||
let result = match roc_types::pretty_print::chase_ext_tag_union(subs, var, &mut tags_vec) {
|
||||
Ok(()) | Err((_, Content::FlexVar(_))) | Err((_, Content::RecursionVar { .. })) => {
|
||||
let opt_rec_var = get_recursion_var(subs, var);
|
||||
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs)
|
||||
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs, ptr_bytes)
|
||||
}
|
||||
Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous),
|
||||
Err(other) => panic!("invalid content in tag union variable: {:?}", other),
|
||||
@ -1651,6 +1666,7 @@ fn union_sorted_tags_help_new<'a>(
|
||||
mut tags_vec: Vec<(&'_ TagName, VariableSubsSlice)>,
|
||||
opt_rec_var: Option<Variable>,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> UnionVariant<'a> {
|
||||
// sort up front; make sure the ordering stays intact!
|
||||
tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||
@ -1659,6 +1675,7 @@ fn union_sorted_tags_help_new<'a>(
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
match tags_vec.len() {
|
||||
@ -1708,8 +1725,6 @@ fn union_sorted_tags_help_new<'a>(
|
||||
}
|
||||
|
||||
layouts.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1793,8 +1808,6 @@ fn union_sorted_tags_help_new<'a>(
|
||||
}
|
||||
|
||||
arg_layouts.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -1867,6 +1880,7 @@ pub fn union_sorted_tags_help<'a>(
|
||||
mut tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
|
||||
opt_rec_var: Option<Variable>,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> UnionVariant<'a> {
|
||||
// sort up front; make sure the ordering stays intact!
|
||||
tags_vec.sort_unstable_by(|(a, _), (b, _)| a.cmp(b));
|
||||
@ -1875,6 +1889,7 @@ pub fn union_sorted_tags_help<'a>(
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
match tags_vec.len() {
|
||||
@ -1921,8 +1936,6 @@ pub fn union_sorted_tags_help<'a>(
|
||||
}
|
||||
|
||||
layouts.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -2005,8 +2018,6 @@ pub fn union_sorted_tags_help<'a>(
|
||||
}
|
||||
|
||||
arg_layouts.sort_by(|layout1, layout2| {
|
||||
let ptr_bytes = 8;
|
||||
|
||||
let size1 = layout1.alignment_bytes(ptr_bytes);
|
||||
let size2 = layout2.alignment_bytes(ptr_bytes);
|
||||
|
||||
@ -2091,7 +2102,12 @@ fn cheap_sort_tags<'a, 'b>(
|
||||
tags_vec
|
||||
}
|
||||
|
||||
fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Layout<'a> {
|
||||
fn layout_from_newtype<'a>(
|
||||
arena: &'a Bump,
|
||||
tags: UnionTags,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Layout<'a> {
|
||||
debug_assert!(tags.is_newtype_wrapper(subs));
|
||||
|
||||
let slice_index = tags.variables().into_iter().next().unwrap();
|
||||
@ -2109,6 +2125,7 @@ fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Lay
|
||||
arena,
|
||||
subs,
|
||||
seen: Vec::new_in(arena),
|
||||
ptr_bytes,
|
||||
};
|
||||
|
||||
match Layout::from_var(&mut env, var) {
|
||||
@ -2128,11 +2145,16 @@ fn layout_from_newtype<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Lay
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> Layout<'a> {
|
||||
fn layout_from_tag_union<'a>(
|
||||
arena: &'a Bump,
|
||||
tags: UnionTags,
|
||||
subs: &Subs,
|
||||
ptr_bytes: u32,
|
||||
) -> Layout<'a> {
|
||||
use UnionVariant::*;
|
||||
|
||||
if tags.is_newtype_wrapper(subs) {
|
||||
return layout_from_newtype(arena, tags, subs);
|
||||
return layout_from_newtype(arena, tags, subs, ptr_bytes);
|
||||
}
|
||||
|
||||
let tags_vec = cheap_sort_tags(arena, tags, subs);
|
||||
@ -2148,7 +2170,7 @@ fn layout_from_tag_union<'a>(arena: &'a Bump, tags: UnionTags, subs: &Subs) -> L
|
||||
}
|
||||
_ => {
|
||||
let opt_rec_var = None;
|
||||
let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs);
|
||||
let variant = union_sorted_tags_help_new(arena, tags_vec, opt_rec_var, subs, ptr_bytes);
|
||||
|
||||
match variant {
|
||||
Never => Layout::Union(UnionLayout::NonRecursive(&[])),
|
||||
|
@ -143,6 +143,8 @@ fn pattern_to_doc_help<'b>(
|
||||
Bit(false) => alloc.text("False"),
|
||||
Byte(b) => alloc.text(b.to_string()),
|
||||
Float(f) => alloc.text(f.to_string()),
|
||||
// TODO: Proper Dec.to_str
|
||||
Decimal(d) => alloc.text(d.0.to_string()),
|
||||
Str(s) => alloc.string(s.into()),
|
||||
},
|
||||
Ctor(union, tag_id, args) => {
|
||||
|
@ -92,14 +92,15 @@ mod test_reporting {
|
||||
let mut ident_ids = interns.all_ident_ids.remove(&home).unwrap();
|
||||
|
||||
// Populate Procs and Subs, and get the low-level Expr from the canonical Expr
|
||||
let mut layout_cache = LayoutCache::default();
|
||||
let ptr_bytes = 8;
|
||||
let mut layout_cache = LayoutCache::new(ptr_bytes);
|
||||
let mut mono_env = roc_mono::ir::Env {
|
||||
arena: &arena,
|
||||
subs: &mut subs,
|
||||
problems: &mut mono_problems,
|
||||
home,
|
||||
ident_ids: &mut ident_ids,
|
||||
ptr_bytes: 8,
|
||||
ptr_bytes,
|
||||
update_mode_counter: 0,
|
||||
// call_specialization_counter=0 is reserved
|
||||
call_specialization_counter: 1,
|
||||
|
@ -30,7 +30,7 @@ either = "1.6.1"
|
||||
indoc = "0.3.3"
|
||||
libc = "0.2"
|
||||
inkwell = { path = "../../vendor/inkwell" }
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
libloading = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -2398,7 +2398,7 @@ fn call_invalid_layout() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "assert failed!")]
|
||||
#[should_panic(expected = "An expectation failed!")]
|
||||
fn expect_fail() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -815,7 +815,7 @@ fn str_join_comma_single() {
|
||||
|
||||
#[test]
|
||||
fn str_from_float() {
|
||||
assert_evals_to!(r#"Str.fromFloat 3.14"#, RocStr::from("3.140000"), RocStr);
|
||||
assert_evals_to!(r#"Str.fromFloat 3.14"#, RocStr::from("3.14"), RocStr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -169,7 +169,7 @@ pub fn helper<'a>(
|
||||
}
|
||||
|
||||
let builder = context.create_builder();
|
||||
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app");
|
||||
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app", ptr_bytes);
|
||||
|
||||
let opt_level = if cfg!(debug_assertions) {
|
||||
OptLevel::Normal
|
||||
|
@ -28,7 +28,7 @@ bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||
either = "1.6.1"
|
||||
indoc = "0.3.3"
|
||||
libc = "0.2"
|
||||
target-lexicon = "0.10"
|
||||
target-lexicon = "0.12.2"
|
||||
libloading = "0.6"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -961,19 +961,19 @@ fn define_integer_types(subs: &mut Subs) {
|
||||
Variable::U8,
|
||||
);
|
||||
|
||||
// integer_type(
|
||||
// subs,
|
||||
// Symbol::NUM_AT_NATURAL,
|
||||
// Symbol::NUM_NATURAL,
|
||||
// Symbol::NUM_NAT,
|
||||
// Variable::AT_NATURAL,
|
||||
// Variable::NATURAL,
|
||||
// Variable::AT_INTEGER_NATURAL,
|
||||
// Variable::INTEGER_NATURAL,
|
||||
// Variable::AT_NUM_INTEGER_NATURAL,
|
||||
// Variable::NUM_INTEGER_NATURAL,
|
||||
// Variable::NAT,
|
||||
// );
|
||||
integer_type(
|
||||
subs,
|
||||
Symbol::NUM_AT_NATURAL,
|
||||
Symbol::NUM_NATURAL,
|
||||
Symbol::NUM_NAT,
|
||||
Variable::AT_NATURAL,
|
||||
Variable::NATURAL,
|
||||
Variable::AT_INTEGER_NATURAL,
|
||||
Variable::INTEGER_NATURAL,
|
||||
Variable::AT_NUM_INTEGER_NATURAL,
|
||||
Variable::NUM_INTEGER_NATURAL,
|
||||
Variable::NAT,
|
||||
);
|
||||
}
|
||||
|
||||
impl Subs {
|
||||
|
@ -78,6 +78,14 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
||||
* [Utopia](https://utopia.app/) integrated design and development environment for React. Design and code update each other, in real time.
|
||||
* [Paredit](https://calva.io/paredit/) structural clojure editing, navigation and selection. [Another overview](http://danmidwood.com/content/2014/11/21/animated-paredit.html)
|
||||
|
||||
### Project exploration
|
||||
|
||||
* Tree view or circle view (like Github Next) of project where exposed values and functions can be seen on hover.
|
||||
|
||||
#### Inspiration
|
||||
|
||||
* [Github Next](https://next.github.com/projects/repo-visualization) each file and folder is visualised as a circle: the circle’s color is the type of file, and the circle’s size represents the size of the file. Sidenote, a cool addition to this might be to use heatmap colors for the circles; circles for files that have had lots of commits could be more red, files with few commits would be blue.
|
||||
|
||||
### Voice Interaction Related
|
||||
|
||||
* We should label as many things as possible and expose jumps to those labels as shortkeys.
|
||||
@ -231,6 +239,11 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
||||
The API and documentation are meant to interface with humans.
|
||||
* [DocC](https://developer.apple.com/videos/play/wwdc2021/10166/) neat documentation approach for swift.
|
||||
|
||||
## Tutorials
|
||||
|
||||
* Inclusion of step-by-step tutrials in Roc libraries, platforms or business specific code.
|
||||
* Having to set up your own website for a tutorial can be a lot of work, making it easy to make quality tutorials would make for a more delightful experience.
|
||||
|
||||
## General Plugin Ideas
|
||||
|
||||
### Ideas
|
||||
|
1
examples/.gitignore
vendored
1
examples/.gitignore
vendored
@ -4,4 +4,3 @@ app
|
||||
libhost.a
|
||||
roc_app.ll
|
||||
roc_app.bc
|
||||
effect-example
|
||||
|
@ -1,4 +1,4 @@
|
||||
interface AStar exposes [ findPath, Model, initialModel, cheapestOpen, takeStep, reconstructPath ] imports [Quicksort]
|
||||
interface AStar exposes [ findPath, Model, initialModel, cheapestOpen, reconstructPath ] imports [Quicksort]
|
||||
|
||||
findPath = \costFn, moveFn, start, end ->
|
||||
astar costFn moveFn end (initialModel start)
|
||||
@ -111,24 +111,24 @@ astar = \costFn, moveFn, goal, model ->
|
||||
|
||||
astar costFn moveFn goal modelWithCosts
|
||||
|
||||
takeStep = \moveFn, _goal, model, current ->
|
||||
modelPopped =
|
||||
{ model &
|
||||
openSet: Set.remove model.openSet current,
|
||||
evaluated: Set.insert model.evaluated current,
|
||||
}
|
||||
|
||||
neighbors = moveFn current
|
||||
|
||||
newNeighbors = Set.difference neighbors modelPopped.evaluated
|
||||
|
||||
modelWithNeighbors = { modelPopped & openSet: Set.union modelPopped.openSet newNeighbors }
|
||||
|
||||
# a lot goes wrong here
|
||||
modelWithCosts =
|
||||
Set.walk newNeighbors (\n, m -> updateCost current n m) modelWithNeighbors
|
||||
|
||||
modelWithCosts
|
||||
# takeStep = \moveFn, _goal, model, current ->
|
||||
# modelPopped =
|
||||
# { model &
|
||||
# openSet: Set.remove model.openSet current,
|
||||
# evaluated: Set.insert model.evaluated current,
|
||||
# }
|
||||
#
|
||||
# neighbors = moveFn current
|
||||
#
|
||||
# newNeighbors = Set.difference neighbors modelPopped.evaluated
|
||||
#
|
||||
# modelWithNeighbors = { modelPopped & openSet: Set.union modelPopped.openSet newNeighbors }
|
||||
#
|
||||
# # a lot goes wrong here
|
||||
# modelWithCosts =
|
||||
# Set.walk newNeighbors (\n, m -> updateCost current n m) modelWithNeighbors
|
||||
#
|
||||
# modelWithCosts
|
||||
|
||||
|
||||
|
||||
|
@ -29,23 +29,33 @@ extern fn roc__mainForHost_1_Fx_caller(*const u8, [*]u8, [*]u8) void;
|
||||
extern fn roc__mainForHost_1_Fx_size() i64;
|
||||
extern fn roc__mainForHost_1_Fx_result_size() i64;
|
||||
|
||||
extern fn malloc(size: usize) callconv(.C) ?*c_void;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void;
|
||||
const Align = extern struct { a: usize, b: usize };
|
||||
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
_ = alignment;
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
export fn roc_realloc(c_ptr: *c_void, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size);
|
||||
_ = old_size;
|
||||
_ = alignment;
|
||||
|
||||
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
_ = alignment;
|
||||
|
||||
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
_ = tag_id;
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
@ -54,12 +64,9 @@ export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub export fn main() u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
pub fn main() u8 {
|
||||
const size = @intCast(usize, roc__mainForHost_size());
|
||||
const raw_output = std.heap.c_allocator.alloc(u8, size) catch unreachable;
|
||||
const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
|
||||
var output = @ptrCast([*]u8, raw_output);
|
||||
|
||||
defer {
|
||||
@ -71,17 +78,17 @@ pub export fn main() u8 {
|
||||
|
||||
roc__mainForHost_1_exposed(output);
|
||||
|
||||
const elements = @ptrCast([*]u64, @alignCast(8, output));
|
||||
|
||||
var flag = elements[0];
|
||||
const flag = @ptrCast(*u64, @alignCast(@alignOf(u64), output)).*;
|
||||
|
||||
if (flag == 0) {
|
||||
// all is well
|
||||
const closure_data_pointer = @ptrCast([*]u8, output[8..size]);
|
||||
const closure_data_pointer = @ptrCast([*]u8, output[@sizeOf(u64)..size]);
|
||||
|
||||
call_the_closure(closure_data_pointer);
|
||||
} else {
|
||||
const msg = @intToPtr([*:0]const u8, elements[1]);
|
||||
const ptr = @ptrCast(*u32, output + @sizeOf(u64));
|
||||
const msg = @intToPtr([*:0]const u8, ptr.*);
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
@ -92,6 +99,7 @@ pub export fn main() u8 {
|
||||
|
||||
const delta = to_seconds(ts2) - to_seconds(ts1);
|
||||
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
@ -103,7 +111,7 @@ fn to_seconds(tms: std.os.timespec) f64 {
|
||||
|
||||
fn call_the_closure(closure_data_pointer: [*]u8) void {
|
||||
const size = roc__mainForHost_1_Fx_result_size();
|
||||
const raw_output = std.heap.c_allocator.alloc(u8, @intCast(usize, size)) catch unreachable;
|
||||
const raw_output = std.heap.c_allocator.allocAdvanced(u8, @alignOf(u64), @intCast(usize, size), .at_least) catch unreachable;
|
||||
var output = @ptrCast([*]u8, raw_output);
|
||||
|
||||
defer {
|
||||
@ -135,7 +143,7 @@ pub export fn roc_fx_putInt(int: i64) i64 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
|
||||
export fn roc_fx_putLine(rocPath: str.RocStr) callconv(.C) void {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
for (rocPath.asSlice()) |char| {
|
||||
@ -143,8 +151,6 @@ pub export fn roc_fx_putLine(rocPath: str.RocStr) i64 {
|
||||
}
|
||||
|
||||
stdout.print("\n", .{}) catch unreachable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const GetInt = extern struct {
|
||||
|
1
examples/cli/.gitignore
vendored
Normal file
1
examples/cli/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
echo
|
18
examples/cli/Echo.roc
Normal file
18
examples/cli/Echo.roc
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env roc
|
||||
|
||||
app "echo"
|
||||
packages { base: "platform" }
|
||||
imports [ base.Task.{ Task, await }, base.Stdout, base.Stdin ]
|
||||
provides [ main ] to base
|
||||
|
||||
main : Task {} *
|
||||
main =
|
||||
{} <- await (Stdout.line "What's your first name?")
|
||||
|
||||
firstName <- await Stdin.line
|
||||
|
||||
{} <- await (Stdout.line "What's your last name?")
|
||||
|
||||
lastName <- await Stdin.line
|
||||
|
||||
Stdout.line "Hi, \(firstName) \(lastName)!"
|
BIN
examples/cli/cli-example
Executable file
BIN
examples/cli/cli-example
Executable file
Binary file not shown.
BIN
examples/cli/hello-world
Executable file
BIN
examples/cli/hello-world
Executable file
Binary file not shown.
21
examples/cli/platform/Cargo.lock
generated
Normal file
21
examples/cli/platform/Cargo.lock
generated
Normal file
@ -0,0 +1,21 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"roc_std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.1.0"
|
15
examples/cli/platform/Cargo.toml
Normal file
15
examples/cli/platform/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
authors = ["The Roc Contributors"]
|
||||
license = "UPL-1.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../roc_std" }
|
||||
libc = "0.2"
|
||||
|
||||
[workspace]
|
14
examples/cli/platform/Package-Config.roc
Normal file
14
examples/cli/platform/Package-Config.roc
Normal file
@ -0,0 +1,14 @@
|
||||
platform examples/cli
|
||||
requires {}{ main : Task {} [] } # TODO FIXME
|
||||
exposes []
|
||||
packages {}
|
||||
imports [ Task.{ Task } ]
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect
|
||||
{
|
||||
putLine : Str -> Effect {},
|
||||
getLine : Effect Str
|
||||
}
|
||||
|
||||
mainForHost : Task {} [] as Fx
|
||||
mainForHost = main
|
6
examples/cli/platform/Stdin.roc
Normal file
6
examples/cli/platform/Stdin.roc
Normal file
@ -0,0 +1,6 @@
|
||||
interface Stdin
|
||||
exposes [ line ]
|
||||
imports [ fx.Effect, Task ]
|
||||
|
||||
line : Task.Task Str *
|
||||
line = Effect.after Effect.getLine Task.succeed # TODO FIXME Effect.getLine should suffice
|
9
examples/cli/platform/Stdout.roc
Normal file
9
examples/cli/platform/Stdout.roc
Normal file
@ -0,0 +1,9 @@
|
||||
interface Stdout
|
||||
exposes [ line ]
|
||||
imports [ fx.Effect, Task.{ Task } ]
|
||||
|
||||
# line : Str -> Task.Task {} *
|
||||
# line = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {})
|
||||
|
||||
line : Str -> Task {} *
|
||||
line = \str -> Effect.map (Effect.putLine str) (\_ -> Ok {})
|
44
examples/cli/platform/Task.roc
Normal file
44
examples/cli/platform/Task.roc
Normal file
@ -0,0 +1,44 @@
|
||||
interface Task
|
||||
exposes [ Task, succeed, fail, await, map, onFail, attempt ]
|
||||
imports [ fx.Effect ]
|
||||
|
||||
|
||||
Task ok err : Effect.Effect (Result ok err)
|
||||
|
||||
|
||||
succeed : val -> Task val *
|
||||
succeed = \val ->
|
||||
Effect.always (Ok val)
|
||||
|
||||
|
||||
fail : err -> Task * err
|
||||
fail = \val ->
|
||||
Effect.always (Err val)
|
||||
|
||||
attempt : Task a b, (Result a b -> Task c d) -> Task c d
|
||||
attempt = \effect, transform ->
|
||||
Effect.after effect \result ->
|
||||
when result is
|
||||
Ok ok -> transform (Ok ok)
|
||||
Err err -> transform (Err err)
|
||||
|
||||
await : Task a err, (a -> Task b err) -> Task b err
|
||||
await = \effect, transform ->
|
||||
Effect.after effect \result ->
|
||||
when result is
|
||||
Ok a -> transform a
|
||||
Err err -> Task.fail err
|
||||
|
||||
onFail : Task ok a, (a -> Task ok b) -> Task ok b
|
||||
onFail = \effect, transform ->
|
||||
Effect.after effect \result ->
|
||||
when result is
|
||||
Ok a -> Task.succeed a
|
||||
Err err -> transform err
|
||||
|
||||
map : Task a err, (a -> b) -> Task b err
|
||||
map = \effect, transform ->
|
||||
Effect.after effect \result ->
|
||||
when result is
|
||||
Ok a -> Task.succeed (transform a)
|
||||
Err err -> Task.fail err
|
7
examples/cli/platform/host.c
Normal file
7
examples/cli/platform/host.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern int rust_main();
|
||||
|
||||
int main() {
|
||||
return rust_main();
|
||||
}
|
139
examples/cli/platform/src/lib.rs
Normal file
139
examples/cli/platform/src/lib.rs
Normal file
@ -0,0 +1,139 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::alloc::Layout;
|
||||
use core::ffi::c_void;
|
||||
use core::mem::MaybeUninit;
|
||||
use libc;
|
||||
use roc_std::{RocCallResult, RocStr};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed"]
|
||||
fn roc_main(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "roc__mainForHost_size"]
|
||||
fn roc_main_size() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_caller"]
|
||||
fn call_Fx(flags: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[link_name = "roc__mainForHost_1_Fx_size"]
|
||||
fn size_Fx() -> i64;
|
||||
|
||||
#[link_name = "roc__mainForHost_1_Fx_result_size"]
|
||||
fn size_Fx_result() -> i64;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
libc::malloc(size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
libc::realloc(c_ptr, new_size)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
libc::free(c_ptr)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
let size = unsafe { roc_main_size() } as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
|
||||
unsafe {
|
||||
// TODO allocate on the stack if it's under a certain size
|
||||
let buffer = std::alloc::alloc(layout);
|
||||
|
||||
roc_main(buffer);
|
||||
|
||||
let output = buffer as *mut RocCallResult<()>;
|
||||
|
||||
match (&*output).into() {
|
||||
Ok(()) => {
|
||||
let closure_data_ptr = buffer.offset(8);
|
||||
let result = call_the_closure(closure_data_ptr as *const u8);
|
||||
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
result
|
||||
}
|
||||
Err(msg) => {
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
panic!("Roc failed with message: {}", msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn call_the_closure(closure_data_ptr: *const u8) -> i64 {
|
||||
let size = size_Fx_result() as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
let buffer = std::alloc::alloc(layout) as *mut u8;
|
||||
|
||||
call_Fx(
|
||||
// This flags pointer will never get dereferenced
|
||||
MaybeUninit::uninit().as_ptr(),
|
||||
closure_data_ptr as *const u8,
|
||||
buffer as *mut u8,
|
||||
);
|
||||
|
||||
let output = &*(buffer as *mut RocCallResult<()>);
|
||||
|
||||
match output.into() {
|
||||
Ok(_) => {
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
0
|
||||
}
|
||||
Err(e) => panic!("failed with {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn roc_fx_getLine() -> RocStr {
|
||||
use std::io::{self, BufRead};
|
||||
|
||||
let stdin = io::stdin();
|
||||
let line1 = stdin.lock().lines().next().unwrap().unwrap();
|
||||
|
||||
RocStr::from_slice(line1.as_bytes())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn roc_fx_putLine(line: RocStr) -> () {
|
||||
let bytes = line.as_slice();
|
||||
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||
println!("{}", string);
|
||||
|
||||
// don't mess with the refcount!
|
||||
core::mem::forget(line);
|
||||
|
||||
()
|
||||
}
|
@ -19,23 +19,32 @@ comptime {
|
||||
}
|
||||
}
|
||||
|
||||
extern fn malloc(size: usize) callconv(.C) ?*c_void;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(u128)) u8, size: usize) callconv(.C) ?*c_void;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(u128)) u8) callconv(.C) void;
|
||||
const Align = extern struct { a: usize, b: usize };
|
||||
extern fn malloc(size: usize) callconv(.C) ?*align(@alignOf(Align)) c_void;
|
||||
extern fn realloc(c_ptr: [*]align(@alignOf(Align)) u8, size: usize) callconv(.C) ?*c_void;
|
||||
extern fn free(c_ptr: [*]align(@alignOf(Align)) u8) callconv(.C) void;
|
||||
|
||||
export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
_ = alignment;
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
export fn roc_realloc(c_ptr: *c_void, old_size: usize, new_size: usize, alignment: u32) callconv(.C) ?*c_void {
|
||||
return realloc(@alignCast(16, @ptrCast([*]u8, c_ptr)), new_size);
|
||||
_ = old_size;
|
||||
_ = alignment;
|
||||
|
||||
return realloc(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)), new_size);
|
||||
}
|
||||
|
||||
export fn roc_dealloc(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
free(@alignCast(16, @ptrCast([*]u8, c_ptr)));
|
||||
_ = alignment;
|
||||
|
||||
free(@alignCast(@alignOf(Align), @ptrCast([*]u8, c_ptr)));
|
||||
}
|
||||
|
||||
export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
_ = tag_id;
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
const msg = @ptrCast([*:0]const u8, c_ptr);
|
||||
stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg}) catch unreachable;
|
||||
@ -47,11 +56,11 @@ const Allocator = mem.Allocator;
|
||||
|
||||
extern fn roc__mainForHost_1_exposed(*RocCallResult) void;
|
||||
|
||||
const RocCallResult = extern struct { flag: usize, content: RocStr };
|
||||
const RocCallResult = extern struct { flag: u64, content: RocStr };
|
||||
|
||||
const Unit = extern struct {};
|
||||
|
||||
pub export fn main() i32 {
|
||||
pub fn main() u8 {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
const stderr = std.io.getStdErr().writer();
|
||||
|
||||
|
@ -584,6 +584,10 @@ impl RocStr {
|
||||
|
||||
let raw_ptr = Self::get_element_ptr(raw_ptr as *mut u8);
|
||||
|
||||
// write the refcount
|
||||
let refcount_ptr = raw_ptr as *mut isize;
|
||||
*(refcount_ptr.offset(-1)) = isize::MIN;
|
||||
|
||||
{
|
||||
// NOTE: using a memcpy here causes weird issues
|
||||
let target_ptr = raw_ptr as *mut u8;
|
||||
@ -832,11 +836,9 @@ impl RocDec {
|
||||
}
|
||||
};
|
||||
|
||||
let after_point = match parts.next() {
|
||||
Some(answer) if answer.len() <= Self::DECIMAL_PLACES as usize => answer,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
let opt_after_point = match parts.next() {
|
||||
Some(answer) if answer.len() <= Self::DECIMAL_PLACES as usize => Some(answer),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// There should have only been one "." in the string!
|
||||
@ -845,22 +847,27 @@ impl RocDec {
|
||||
}
|
||||
|
||||
// Calculate the low digits - the ones after the decimal point.
|
||||
let lo = match after_point.parse::<i128>() {
|
||||
Ok(answer) => {
|
||||
// Translate e.g. the 1 from 0.1 into 10000000000000000000
|
||||
// by "restoring" the elided trailing zeroes to the number!
|
||||
let trailing_zeroes = Self::DECIMAL_PLACES as usize - after_point.len();
|
||||
let lo = answer * 10i128.pow(trailing_zeroes as u32);
|
||||
let lo = match opt_after_point {
|
||||
Some(after_point) => {
|
||||
match after_point.parse::<i128>() {
|
||||
Ok(answer) => {
|
||||
// Translate e.g. the 1 from 0.1 into 10000000000000000000
|
||||
// by "restoring" the elided trailing zeroes to the number!
|
||||
let trailing_zeroes = Self::DECIMAL_PLACES as usize - after_point.len();
|
||||
let lo = answer * 10i128.pow(trailing_zeroes as u32);
|
||||
|
||||
if !before_point.starts_with('-') {
|
||||
lo
|
||||
} else {
|
||||
-lo
|
||||
if !before_point.starts_with('-') {
|
||||
lo
|
||||
} else {
|
||||
-lo
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
|
||||
// Calculate the high digits - the ones before the decimal point.
|
||||
|
Loading…
Reference in New Issue
Block a user