mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 07:14:46 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into editor-let-value
This commit is contained in:
commit
3397d780b8
100 changed files with 4704 additions and 1253 deletions
|
@ -175,7 +175,7 @@ Installing LLVM's prebuilt binaries doesn't seem to be enough for the `llvm-sys`
|
|||
on Windows. After lots of help from [**@IanMacKenzie**](https://github.com/IanMacKenzie) (thank you, Ian!), here's what worked for me:
|
||||
|
||||
1. I downloaded and installed [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (a full Visual Studio install should work tool; the Build Tools are just the CLI tools, which is all I wanted)
|
||||
1. In the installation configuration, under "additional components" I had to check both "C++ ATL for latest v142 build tools (x86 & x64)" and also "C++/CLI support for v142 build tools"
|
||||
1. In the installation configuration, under "additional components" I had to check both "C++ ATL for latest v142 build tools (x86 & x64)" and also "C++/CLI support for v142 build tools" [note: as of September 2021 this should no longer be necessary - the next time anyone tries this, please try it without this step and make a PR to delete this step if it's no longer needed!]
|
||||
1. I launched the "x64 Native Tools Command Prompt for Visual Studio 2019" application (note: not the similarly-named "x86" one!)
|
||||
1. Make sure [Python 2.7](https://www.python.org/) and [CMake 3.17](http://cmake.org/) are installed on your system.
|
||||
1. I followed most of the steps under LLVM's [building from source instructions](https://github.com/llvm/llvm-project#getting-the-source-code-and-building-llvm) up to the `cmake -G ...` command, which didn't work for me. Instead, at that point I did the following step.
|
||||
|
|
747
Cargo.lock
generated
747
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
42
Earthfile
42
Earthfile
|
@ -8,7 +8,7 @@ install-other-libs:
|
|||
FROM +prep-debian
|
||||
RUN apt -y install wget git
|
||||
RUN apt -y install libxcb-shape0-dev libxcb-xfixes0-dev # for editor clipboard
|
||||
RUN apt -y install libc++-dev libc++abi-dev g++ libunwind-dev pkg-config libx11-dev zlib1g-dev
|
||||
RUN apt -y install libunwind-dev pkg-config libx11-dev zlib1g-dev
|
||||
|
||||
install-zig-llvm-valgrind-clippy-rustfmt:
|
||||
FROM +install-other-libs
|
||||
|
@ -48,49 +48,18 @@ install-zig-llvm-valgrind-clippy-rustfmt:
|
|||
ENV RUSTC_WRAPPER=/usr/local/cargo/bin/sccache
|
||||
ENV SCCACHE_DIR=/earthbuild/sccache_dir
|
||||
ENV CARGO_INCREMENTAL=0 # no need to recompile package when using new function
|
||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||
cargo install cargo-chef
|
||||
|
||||
deps-image:
|
||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||
SAVE IMAGE roc-deps:latest
|
||||
|
||||
copy-dirs:
|
||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||
# If you edit this, make sure to update copy-dirs-and-cache below.
|
||||
COPY --dir cli compiler docs editor roc_std vendor examples Cargo.toml Cargo.lock ./
|
||||
|
||||
copy-dirs-and-cache:
|
||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||
COPY +save-cache/target ./target
|
||||
COPY +save-cache/cargo_home $CARGO_HOME
|
||||
# This needs to be kept in sync with copy-dirs above.
|
||||
# The reason this is at the end is to maximize caching.
|
||||
# Lines above this should be cached even if the code changes.
|
||||
COPY --dir cli compiler docs editor roc_std vendor examples Cargo.toml Cargo.lock ./
|
||||
|
||||
prepare-cache:
|
||||
FROM +copy-dirs
|
||||
RUN cargo chef prepare
|
||||
SAVE ARTIFACT recipe.json
|
||||
|
||||
save-cache:
|
||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||
COPY +prepare-cache/recipe.json ./
|
||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||
cargo chef cook && sccache --show-stats # for clippy
|
||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||
cargo chef cook --release --tests && sccache --show-stats
|
||||
SAVE ARTIFACT target
|
||||
SAVE ARTIFACT $CARGO_HOME cargo_home
|
||||
|
||||
test-zig:
|
||||
FROM +install-zig-llvm-valgrind-clippy-rustfmt
|
||||
COPY --dir compiler/builtins/bitcode ./
|
||||
RUN cd bitcode && ./run-tests.sh
|
||||
|
||||
check-clippy:
|
||||
FROM +copy-dirs-and-cache
|
||||
FROM +copy-dirs
|
||||
RUN cargo clippy -V
|
||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||
cargo clippy -- -D warnings
|
||||
|
@ -106,8 +75,11 @@ check-typos:
|
|||
RUN typos
|
||||
|
||||
test-rust:
|
||||
FROM +copy-dirs-and-cache
|
||||
FROM +copy-dirs
|
||||
ENV RUST_BACKTRACE=1
|
||||
# run one of the benchmarks to make sure the host is compiled
|
||||
# not pre-compiling the host can cause race conditions
|
||||
RUN echo "4" | cargo run --release examples/benchmarks/NQueens.roc
|
||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||
cargo test --release && sccache --show-stats
|
||||
|
||||
|
@ -132,7 +104,7 @@ test-all:
|
|||
|
||||
# compile everything needed for benchmarks and output a self-contained folder
|
||||
prep-bench-folder:
|
||||
FROM +copy-dirs-and-cache
|
||||
FROM +copy-dirs
|
||||
ARG BENCH_SUFFIX=branch
|
||||
RUN cargo criterion -V
|
||||
RUN --mount=type=cache,target=$SCCACHE_DIR cd cli && cargo criterion --no-run
|
||||
|
|
|
@ -59,7 +59,7 @@ esac
|
|||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
|
||||
add-apt-repository "${REPO_NAME}"
|
||||
apt-get update
|
||||
apt-get install -y clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION libc++abi-dev libunwind-dev libc6-dbg libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
|
||||
apt-get install -y clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION libc6-dbg libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
|
||||
|
||||
wget https://sourceware.org/pub/valgrind/valgrind-3.16.1.tar.bz2
|
||||
tar -xf valgrind-3.16.1.tar.bz2
|
||||
|
|
|
@ -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,9 +68,12 @@ 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"
|
||||
|
||||
wasmer = "2.0.0"
|
||||
wasmer-wasi = "2.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "0.5.1"
|
||||
maplit = "1.0.1"
|
||||
|
|
|
@ -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,30 @@ 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 => {
|
||||
// If possible, report the generated executable name relative to the current dir.
|
||||
let generated_filename = binary_path
|
||||
.strip_prefix(env::current_dir().unwrap())
|
||||
.unwrap_or(&binary_path);
|
||||
|
||||
// No need to waste time freeing this memory,
|
||||
// since the process is about to exit anyway.
|
||||
std::mem::forget(arena);
|
||||
|
||||
let args = std::env::args()
|
||||
.skip(roc_file_arg_index)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
run_with_wasmer(generated_filename, &args);
|
||||
return Ok(0);
|
||||
}
|
||||
_ => 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:
|
||||
|
@ -306,3 +364,26 @@ fn roc_run(cmd: &mut Command) -> io::Result<i32> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_with_wasmer(wasm_path: &std::path::Path, args: &[String]) {
|
||||
use wasmer::{Instance, Module, Store};
|
||||
|
||||
let store = Store::default();
|
||||
let module = Module::from_file(&store, &wasm_path).unwrap();
|
||||
|
||||
// First, we create the `WasiEnv`
|
||||
use wasmer_wasi::WasiState;
|
||||
let mut wasi_env = WasiState::new("hello").args(args).finalize().unwrap();
|
||||
|
||||
// Then, we get the import object related to our WASI
|
||||
// and attach it to the Wasm instance.
|
||||
let import_object = wasi_env
|
||||
.import_object(&module)
|
||||
.unwrap_or_else(|_| wasmer::imports!());
|
||||
|
||||
let instance = Instance::new(&module, &import_object).unwrap();
|
||||
|
||||
let start = instance.exports.get_function("_start").unwrap();
|
||||
|
||||
start.call(&[]).unwrap();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -179,7 +179,7 @@ pub fn gen_and_eval<'a>(
|
|||
interns,
|
||||
module,
|
||||
ptr_bytes,
|
||||
is_gen_test: false,
|
||||
is_gen_test: true, // so roc_panic is generated
|
||||
// important! we don't want any procedures to get the C calling convention
|
||||
exposed_to_host: MutSet::default(),
|
||||
};
|
||||
|
@ -197,6 +197,9 @@ pub fn gen_and_eval<'a>(
|
|||
|
||||
env.dibuilder.finalize();
|
||||
|
||||
// we don't use the debug info, and it causes weird errors.
|
||||
module.strip_debug_info();
|
||||
|
||||
// Uncomment this to see the module's un-optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
|
|
|
@ -108,6 +108,35 @@ 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 path = file.with_file_name(executable_filename);
|
||||
let stdout = crate::run_with_wasmer(&path, stdin);
|
||||
|
||||
if !stdout.ends_with(expected_ending) {
|
||||
panic!(
|
||||
"expected output to end with {:?} but instead got {:#?}",
|
||||
expected_ending, stdout
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// This macro does two things.
|
||||
///
|
||||
/// First, it generates and runs a separate test for each of the given
|
||||
|
@ -223,13 +252,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 +284,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 +312,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 +395,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 {
|
||||
|
@ -492,3 +561,58 @@ mod cli_run {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm-cli-run")]
|
||||
fn run_with_wasmer(wasm_path: &std::path::Path, stdin: &[&str]) -> String {
|
||||
use std::io::Write;
|
||||
use wasmer::{Instance, Module, Store};
|
||||
|
||||
let store = Store::default();
|
||||
let module = Module::from_file(&store, &wasm_path).unwrap();
|
||||
|
||||
let mut fake_stdin = wasmer_wasi::Pipe::new();
|
||||
let fake_stdout = wasmer_wasi::Pipe::new();
|
||||
let fake_stderr = wasmer_wasi::Pipe::new();
|
||||
|
||||
for line in stdin {
|
||||
write!(fake_stdin, "{}", line).unwrap();
|
||||
}
|
||||
|
||||
// First, we create the `WasiEnv`
|
||||
use wasmer_wasi::WasiState;
|
||||
let mut wasi_env = WasiState::new("hello")
|
||||
.stdin(Box::new(fake_stdin))
|
||||
.stdout(Box::new(fake_stdout))
|
||||
.stderr(Box::new(fake_stderr))
|
||||
.finalize()
|
||||
.unwrap();
|
||||
|
||||
// Then, we get the import object related to our WASI
|
||||
// and attach it to the Wasm instance.
|
||||
let import_object = wasi_env
|
||||
.import_object(&module)
|
||||
.unwrap_or_else(|_| wasmer::imports!());
|
||||
|
||||
let instance = Instance::new(&module, &import_object).unwrap();
|
||||
|
||||
let start = instance.exports.get_function("_start").unwrap();
|
||||
|
||||
match start.call(&[]) {
|
||||
Ok(_) => {
|
||||
let mut state = wasi_env.state.lock().unwrap();
|
||||
|
||||
match state.fs.stdout_mut() {
|
||||
Ok(Some(stdout)) => {
|
||||
let mut buf = String::new();
|
||||
stdout.read_to_string(&mut buf).unwrap();
|
||||
|
||||
return buf;
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Something went wrong running a wasm test:\n{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
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();
|
||||
|
@ -124,6 +125,7 @@ pub fn gen_from_mono_module(
|
|||
|| name.starts_with("roc_builtins.dec")
|
||||
|| name.starts_with("list.RocList")
|
||||
|| name.starts_with("dict.RocDict")
|
||||
|| name.contains("decref")
|
||||
{
|
||||
function.add_attribute(AttributeLoc::Function, enum_attr);
|
||||
}
|
||||
|
@ -134,7 +136,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,6 +220,9 @@ pub fn gen_from_mono_module(
|
|||
}
|
||||
}
|
||||
|
||||
use target_lexicon::Architecture;
|
||||
match target.architecture {
|
||||
Architecture::X86_64 | Architecture::Aarch64(_) => {
|
||||
// assemble the .ll into a .bc
|
||||
let _ = Command::new("llvm-as")
|
||||
.args(&[
|
||||
|
@ -249,18 +253,47 @@ pub fn gen_from_mono_module(
|
|||
.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();
|
||||
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");
|
||||
}
|
||||
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,
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
zig build-obj src/main.zig -O ReleaseFast -femit-llvm-ir=builtins.ll -femit-bin=builtins.o --strip
|
|
@ -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,30 @@ 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(.{});
|
||||
|
||||
// 32-bit x86, useful for debugging
|
||||
// target.os_tag = std.Target.Os.Tag.linux;
|
||||
// target.cpu_arch = std.Target.Cpu.Arch.i386;
|
||||
// target.abi = std.Target.Abi.musl;
|
||||
|
||||
// 32-bit wasm
|
||||
target.os_tag = std.Target.Os.Tag.wasi;
|
||||
target.cpu_arch = std.Target.Cpu.Arch.wasm32;
|
||||
target.abi = std.Target.Abi.none;
|
||||
|
||||
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.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.
|
||||
|
|
|
@ -9,12 +9,12 @@ const RocList = @import("list.zig").RocList;
|
|||
|
||||
const INITIAL_SEED = 0xc70f6907;
|
||||
|
||||
const InPlace = packed enum(u8) {
|
||||
const InPlace = enum(u8) {
|
||||
InPlace,
|
||||
Clone,
|
||||
};
|
||||
|
||||
const Slot = packed enum(u8) {
|
||||
const Slot = enum(u8) {
|
||||
Empty,
|
||||
Filled,
|
||||
PreviouslyFilled,
|
||||
|
@ -63,27 +63,24 @@ 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 = packed enum(u8) {
|
||||
Align16KeyFirst,
|
||||
Align16ValueFirst,
|
||||
Align8KeyFirst,
|
||||
Align8ValueFirst,
|
||||
const Alignment = extern struct {
|
||||
bits: u8,
|
||||
|
||||
const VALUE_BEFORE_KEY_FLAG: u8 = 0b1000_0000;
|
||||
|
||||
fn toU32(self: Alignment) u32 {
|
||||
switch (self) {
|
||||
.Align16KeyFirst => return 16,
|
||||
.Align16ValueFirst => return 16,
|
||||
.Align8KeyFirst => return 8,
|
||||
.Align8ValueFirst => return 8,
|
||||
if (self.bits >= VALUE_BEFORE_KEY_FLAG) {
|
||||
return self.bits ^ Alignment.VALUE_BEFORE_KEY_FLAG;
|
||||
} else {
|
||||
return self.bits;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -359,7 +356,7 @@ pub const RocDict = extern struct {
|
|||
// hash the key, and modulo by the maximum size
|
||||
// (so we get an in-bounds index)
|
||||
const hash = hash_fn(seed, key);
|
||||
const index = capacityOfLevel(current_level - 1) + (hash % current_level_size);
|
||||
const index = capacityOfLevel(current_level - 1) + @intCast(usize, (hash % current_level_size));
|
||||
|
||||
switch (self.getSlot(index, key_width, value_width)) {
|
||||
Slot.Empty, Slot.PreviouslyFilled => {
|
||||
|
@ -386,8 +383,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 {
|
||||
|
@ -426,7 +423,7 @@ pub fn dictInsert(input: RocDict, alignment: Alignment, key: Opaque, key_width:
|
|||
}
|
||||
|
||||
const hash = hash_fn(seed, key);
|
||||
const index = capacityOfLevel(current_level - 1) + (hash % current_level_size);
|
||||
const index = capacityOfLevel(current_level - 1) + @intCast(usize, (hash % current_level_size));
|
||||
assert(index < result.capacity());
|
||||
|
||||
switch (result.getSlot(index, key_width, value_width)) {
|
||||
|
|
|
@ -180,8 +180,8 @@ pub const Wyhash = struct {
|
|||
}
|
||||
|
||||
pub fn final(self: *Wyhash) u64 {
|
||||
const seed = self.state.seed;
|
||||
const rem_len = @intCast(u5, self.buf_len);
|
||||
// const seed = self.state.seed;
|
||||
// const rem_len = @intCast(u5, self.buf_len);
|
||||
const rem_key = self.buf[0..self.buf_len];
|
||||
|
||||
return self.state.final(rem_key);
|
||||
|
|
|
@ -109,6 +109,7 @@ comptime {
|
|||
const utils = @import("utils.zig");
|
||||
comptime {
|
||||
exportUtilsFn(utils.test_panic, "test_panic");
|
||||
exportUtilsFn(utils.decrefC, "decref");
|
||||
|
||||
@export(utils.panic, .{ .name = "roc_builtins.utils." ++ "panic", .linkage = .Weak });
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const utils = @import("utils.zig");
|
||||
const roc_mem = @import("mem.zig");
|
||||
const RocList = @import("list.zig").RocList;
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
@ -10,7 +9,7 @@ const expectEqual = testing.expectEqual;
|
|||
const expectError = testing.expectError;
|
||||
const expect = testing.expect;
|
||||
|
||||
const InPlace = packed enum(u8) {
|
||||
const InPlace = enum(u8) {
|
||||
InPlace,
|
||||
Clone,
|
||||
};
|
||||
|
@ -52,7 +51,7 @@ pub const RocStr = extern struct {
|
|||
return result;
|
||||
}
|
||||
|
||||
pub fn initBig(in_place: InPlace, number_of_chars: u64) RocStr {
|
||||
pub fn initBig(_: InPlace, number_of_chars: usize) RocStr {
|
||||
const first_element = utils.allocateWithRefcount(number_of_chars, @sizeOf(usize));
|
||||
|
||||
return RocStr{
|
||||
|
@ -222,7 +221,7 @@ pub const RocStr = extern struct {
|
|||
// null-terminated strings. Otherwise, we need to allocate and copy a new
|
||||
// null-terminated string, which has a much higher performance cost!
|
||||
fn isNullTerminated(self: RocStr) bool {
|
||||
const len = self.len();
|
||||
const length = self.len();
|
||||
const longest_small_str = @sizeOf(RocStr) - 1;
|
||||
|
||||
// NOTE: We want to compare length here, *NOT* check for is_small_str!
|
||||
|
@ -231,7 +230,7 @@ pub const RocStr = extern struct {
|
|||
//
|
||||
// (The other branch dereferences the bytes pointer, which is not safe
|
||||
// to do for the empty string.)
|
||||
if (len <= longest_small_str) {
|
||||
if (length <= longest_small_str) {
|
||||
// If we're a small string, then usually the next byte after the
|
||||
// end of the string will be zero. (Small strings set all their
|
||||
// unused bytes to 0, so that comparison for equality can be fast.)
|
||||
|
@ -242,7 +241,7 @@ pub const RocStr = extern struct {
|
|||
// Also, if we are exactly a maximum-length small string,
|
||||
// then the next byte is off the end of the struct;
|
||||
// in that case, we are also not null-terminated!
|
||||
return len != 0 and len != longest_small_str;
|
||||
return length != 0 and length != longest_small_str;
|
||||
} else {
|
||||
// This is a big string, and it's not empty, so we can safely
|
||||
// dereference the pointer.
|
||||
|
@ -253,8 +252,8 @@ pub const RocStr = extern struct {
|
|||
//
|
||||
// If we have excess capacity, then we can safely read the next
|
||||
// byte after the end of the string. Maybe it happens to be zero!
|
||||
if (capacity_or_refcount > @intCast(isize, len)) {
|
||||
return self.str_bytes[len] == 0;
|
||||
if (capacity_or_refcount > @intCast(isize, length)) {
|
||||
return self.str_bytes[length] == 0;
|
||||
} else {
|
||||
// This string was refcounted or immortal; we can't safely read
|
||||
// the next byte, so assume the string is not null-terminated.
|
||||
|
@ -267,10 +266,10 @@ pub const RocStr = extern struct {
|
|||
// Returns 0 for refcounted stirngs and immortal strings.
|
||||
// Returns the stored capacity value for all other strings.
|
||||
pub fn capacity(self: RocStr) usize {
|
||||
const len = self.len();
|
||||
const length = self.len();
|
||||
const longest_small_str = @sizeOf(RocStr) - 1;
|
||||
|
||||
if (len <= longest_small_str) {
|
||||
if (length <= longest_small_str) {
|
||||
// Note that although empty strings technically have the full
|
||||
// capacity of a small string available, they aren't marked as small
|
||||
// strings, so if you want to make use of that capacity, you need
|
||||
|
@ -314,9 +313,17 @@ 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));
|
||||
// return if (self.isSmallStr() or self.isEmpty()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
||||
if (self.isSmallStr() or self.isEmpty()) {
|
||||
const as_int = @ptrToInt(&self);
|
||||
const as_ptr = @intToPtr([*]u8, as_int);
|
||||
return as_ptr;
|
||||
} else {
|
||||
return @ptrCast([*]u8, self.str_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Given a pointer to some bytes, write the first (len) bytes of this
|
||||
|
@ -408,7 +415,7 @@ pub fn strFromIntC(int: i64) callconv(.C) RocStr {
|
|||
|
||||
fn strFromIntHelp(comptime T: type, int: T) RocStr {
|
||||
// determine maximum size for this T
|
||||
comptime const size = comptime blk: {
|
||||
const size = comptime blk: {
|
||||
// the string representation of the minimum i128 value uses at most 40 characters
|
||||
var buf: [40]u8 = undefined;
|
||||
var result = std.fmt.bufPrint(&buf, "{}", .{std.math.minInt(T)}) catch unreachable;
|
||||
|
@ -423,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
|
||||
|
@ -785,8 +788,6 @@ pub fn countGraphemeClusters(string: RocStr) callconv(.C) usize {
|
|||
return count;
|
||||
}
|
||||
|
||||
fn rocStrFromLiteral(bytes_arr: *const []u8) RocStr {}
|
||||
|
||||
test "countGraphemeClusters: empty string" {
|
||||
const count = countGraphemeClusters(RocStr.empty());
|
||||
try expectEqual(count, 0);
|
||||
|
@ -867,7 +868,6 @@ pub fn startsWith(string: RocStr, prefix: RocStr) callconv(.C) bool {
|
|||
|
||||
// Str.startsWithCodePt
|
||||
pub fn startsWithCodePt(string: RocStr, prefix: u32) callconv(.C) bool {
|
||||
const bytes_len = string.len();
|
||||
const bytes_ptr = string.asU8ptr();
|
||||
|
||||
var buffer: [4]u8 = undefined;
|
||||
|
@ -1266,7 +1266,7 @@ pub fn numberOfNextCodepointBytes(ptr: [*]u8, len: usize, index: usize) Utf8Deco
|
|||
|
||||
// Return types for validateUtf8Bytes
|
||||
// Values must be in alphabetical order. That is, lowest values are the first alphabetically.
|
||||
pub const Utf8ByteProblem = packed enum(u8) {
|
||||
pub const Utf8ByteProblem = enum(u8) {
|
||||
CodepointTooLarge = 0,
|
||||
EncodesSurrogateHalf = 1,
|
||||
ExpectedContinuation = 2,
|
||||
|
|
|
@ -45,7 +45,10 @@ fn testing_roc_dealloc(c_ptr: *c_void, _: u32) callconv(.C) void {
|
|||
std.testing.allocator.destroy(ptr);
|
||||
}
|
||||
|
||||
fn testing_roc_panic(c_ptr: *c_void, _: u32) callconv(.C) void {
|
||||
fn testing_roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
|
||||
_ = c_ptr;
|
||||
_ = tag_id;
|
||||
|
||||
@panic("Roc paniced");
|
||||
}
|
||||
|
||||
|
@ -69,12 +72,14 @@ pub fn panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
|||
// indirection because otherwise zig creats an alias to the panic function which our LLVM code
|
||||
// does not know how to deal with
|
||||
pub fn test_panic(c_ptr: *c_void, alignment: u32) callconv(.C) void {
|
||||
const cstr = @ptrCast([*:0]u8, c_ptr);
|
||||
_ = c_ptr;
|
||||
_ = alignment;
|
||||
// const cstr = @ptrCast([*:0]u8, c_ptr);
|
||||
|
||||
// const stderr = std.io.getStdErr().writer();
|
||||
// stderr.print("Roc panicked: {s}!\n", .{cstr}) catch unreachable;
|
||||
|
||||
std.c.exit(1);
|
||||
// std.c.exit(1);
|
||||
}
|
||||
|
||||
pub const Inc = fn (?[*]u8) callconv(.C) void;
|
||||
|
@ -99,6 +104,19 @@ pub const IntWidth = enum(u8) {
|
|||
Usize,
|
||||
};
|
||||
|
||||
pub fn decrefC(
|
||||
bytes_or_null: ?[*]isize,
|
||||
alignment: u32,
|
||||
) callconv(.C) void {
|
||||
// IMPORTANT: bytes_or_null is this case is expected to be a pointer to the refcount
|
||||
// (NOT the start of the data, or the start of the allocation)
|
||||
|
||||
// this is of course unsafe, but we trust what we get from the llvm side
|
||||
var bytes = @ptrCast([*]isize, bytes_or_null);
|
||||
|
||||
return @call(.{ .modifier = always_inline }, decref_ptr_to_refcount, .{ bytes, alignment });
|
||||
}
|
||||
|
||||
pub fn decref(
|
||||
bytes_or_null: ?[*]u8,
|
||||
data_bytes: usize,
|
||||
|
@ -110,72 +128,75 @@ 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);
|
||||
decref_ptr_to_refcount(isizes - 1, alignment);
|
||||
}
|
||||
|
||||
inline fn decref_ptr_to_refcount(
|
||||
refcount_ptr: [*]isize,
|
||||
alignment: u32,
|
||||
) void {
|
||||
const refcount: isize = refcount_ptr[0];
|
||||
const extra_bytes = std.math.max(alignment, @sizeOf(usize));
|
||||
|
||||
switch (alignment) {
|
||||
16 => {
|
||||
if (refcount == REFCOUNT_ONE_ISIZE) {
|
||||
dealloc(bytes - 16, 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) {
|
||||
dealloc(bytes - 8, alignment);
|
||||
} else if (refcount_isize < 0) {
|
||||
(isizes - 1)[0] = refcount - 1;
|
||||
}
|
||||
},
|
||||
dealloc(@ptrCast([*]u8, refcount_ptr) - (extra_bytes - @sizeOf(usize)), alignment);
|
||||
} else if (refcount < 0) {
|
||||
refcount_ptr[0] = refcount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocateWithRefcount(
|
||||
data_bytes: usize,
|
||||
alignment: u32,
|
||||
element_alignment: u32,
|
||||
) [*]u8 {
|
||||
const result_in_place = false;
|
||||
const alignment = std.math.max(@sizeOf(usize), element_alignment);
|
||||
const first_slot_offset = std.math.max(@sizeOf(usize), element_alignment);
|
||||
const length = alignment + data_bytes;
|
||||
|
||||
switch (alignment) {
|
||||
16 => {
|
||||
const length = 2 * @sizeOf(usize) + data_bytes;
|
||||
|
||||
var new_bytes: [*]align(16) u8 = @alignCast(16, alloc(length, alignment));
|
||||
|
||||
var as_usize_array = @ptrCast([*]usize, new_bytes);
|
||||
if (result_in_place) {
|
||||
as_usize_array[0] = 0;
|
||||
as_usize_array[1] = @intCast(usize, number_of_slots);
|
||||
} else {
|
||||
as_usize_array[0] = 0;
|
||||
as_usize_array[1] = REFCOUNT_ONE;
|
||||
}
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + 2 * @sizeOf(usize);
|
||||
const first_slot = as_u8_array + first_slot_offset;
|
||||
|
||||
return first_slot;
|
||||
},
|
||||
8 => {
|
||||
var raw = alloc(length, alignment);
|
||||
var new_bytes: [*]align(8) u8 = @alignCast(8, raw);
|
||||
|
||||
var as_isize_array = @ptrCast([*]isize, new_bytes);
|
||||
as_isize_array[0] = REFCOUNT_ONE_ISIZE;
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + first_slot_offset;
|
||||
|
||||
return first_slot;
|
||||
},
|
||||
4 => {
|
||||
var raw = alloc(length, alignment);
|
||||
var new_bytes: [*]align(@alignOf(isize)) u8 = @alignCast(@alignOf(isize), raw);
|
||||
|
||||
var as_isize_array = @ptrCast([*]isize, new_bytes);
|
||||
as_isize_array[0] = REFCOUNT_ONE_ISIZE;
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + first_slot_offset;
|
||||
|
||||
return first_slot;
|
||||
},
|
||||
else => {
|
||||
const length = @sizeOf(usize) + data_bytes;
|
||||
|
||||
var new_bytes: [*]align(8) u8 = @alignCast(8, alloc(length, alignment));
|
||||
|
||||
var as_isize_array = @ptrCast([*]isize, new_bytes);
|
||||
if (result_in_place) {
|
||||
as_isize_array[0] = @intCast(isize, number_of_slots);
|
||||
} else {
|
||||
as_isize_array[0] = REFCOUNT_ONE_ISIZE;
|
||||
}
|
||||
|
||||
var as_u8_array = @ptrCast([*]u8, new_bytes);
|
||||
const first_slot = as_u8_array + @sizeOf(usize);
|
||||
|
||||
return first_slot;
|
||||
// const stdout = std.io.getStdOut().writer();
|
||||
// stdout.print("alignment: {d}", .{alignment}) catch unreachable;
|
||||
// @panic("allocateWithRefcount with invalid alignment");
|
||||
unreachable;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -226,7 +247,7 @@ pub const RocResult = extern struct {
|
|||
}
|
||||
};
|
||||
|
||||
pub const Ordering = packed enum(u8) {
|
||||
pub const Ordering = enum(u8) {
|
||||
EQ = 0,
|
||||
GT = 1,
|
||||
LT = 2,
|
||||
|
|
|
@ -24,39 +24,55 @@ fn main() {
|
|||
return;
|
||||
}
|
||||
|
||||
let big_sur_path = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib";
|
||||
let use_build_script = Path::new(big_sur_path).exists();
|
||||
|
||||
// "." is relative to where "build.rs" is
|
||||
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);
|
||||
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;
|
||||
|
@ -92,25 +108,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn run_command_with_no_args<P: AsRef<Path>>(path: P, command_str: &str) {
|
||||
let output_result = Command::new(OsStr::new(&command_str))
|
||||
.current_dir(path)
|
||||
.output();
|
||||
match output_result {
|
||||
Ok(output) => match output.status.success() {
|
||||
true => (),
|
||||
false => {
|
||||
let error_str = match str::from_utf8(&output.stderr) {
|
||||
Ok(stderr) => stderr.to_string(),
|
||||
Err(_) => format!("Failed to run \"{}\"", command_str),
|
||||
};
|
||||
panic!("{} failed: {}", command_str, error_str);
|
||||
}
|
||||
},
|
||||
Err(reason) => panic!("{} failed: {}", command_str, reason),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_zig_files(dir: &Path, cb: &dyn Fn(&Path)) -> io::Result<()> {
|
||||
if dir.is_dir() {
|
||||
for entry in fs::read_dir(dir)? {
|
||||
|
|
|
@ -82,3 +82,4 @@ pub const DEC_MUL_WITH_OVERFLOW: &str = "roc_builtins.dec.mul_with_overflow";
|
|||
pub const DEC_DIV: &str = "roc_builtins.dec.div";
|
||||
|
||||
pub const UTILS_TEST_PANIC: &str = "roc_builtins.utils.test_panic";
|
||||
pub const UTILS_DECREF: &str = "roc_builtins.utils.decref";
|
||||
|
|
|
@ -985,11 +985,11 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
|
||||
// Dict module
|
||||
|
||||
// Dict.hashTestOnly : Nat, v -> Nat
|
||||
// Dict.hashTestOnly : U64, v -> U64
|
||||
add_top_level_function_type!(
|
||||
Symbol::DICT_TEST_HASH,
|
||||
vec![u64_type(), flex(TVAR2)],
|
||||
Box::new(nat_type())
|
||||
Box::new(u64_type())
|
||||
);
|
||||
|
||||
// len : Dict * * -> Nat
|
||||
|
|
|
@ -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();
|
||||
|
@ -1166,13 +1166,13 @@ fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
let bool_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
let unbound_zero_var = var_store.fresh();
|
||||
let len_var = Variable::NAT;
|
||||
let unbound_zero_var = Variable::NATURAL;
|
||||
|
||||
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,
|
||||
};
|
||||
|
@ -2198,7 +2198,22 @@ fn dict_hash_test_only(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
|
||||
/// Dict.len : Dict * * -> Nat
|
||||
fn dict_len(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
lowlevel_1(symbol, LowLevel::DictSize, var_store)
|
||||
let arg1_var = var_store.fresh();
|
||||
let ret_var = Variable::NAT;
|
||||
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::DictSize,
|
||||
args: vec![(arg1_var, Var(Symbol::ARG_1))],
|
||||
ret_var,
|
||||
};
|
||||
|
||||
defn(
|
||||
symbol,
|
||||
vec![(arg1_var, Symbol::ARG_1)],
|
||||
var_store,
|
||||
body,
|
||||
ret_var,
|
||||
)
|
||||
}
|
||||
|
||||
/// Dict.empty : Dict * *
|
||||
|
@ -2571,7 +2586,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 +2689,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 +2754,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 +2807,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 +2824,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 +2848,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,
|
||||
},
|
||||
|
@ -2873,9 +2888,9 @@ fn list_last(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
let arg_var = var_store.fresh();
|
||||
let bool_var = var_store.fresh();
|
||||
let list_var = var_store.fresh();
|
||||
let len_var = var_store.fresh();
|
||||
let num_var = var_store.fresh();
|
||||
let num_precision_var = var_store.fresh();
|
||||
let len_var = Variable::NAT;
|
||||
let num_var = len_var;
|
||||
let num_precision_var = Variable::NATURAL;
|
||||
let list_elem_var = var_store.fresh();
|
||||
let ret_var = var_store.fresh();
|
||||
|
||||
|
@ -2890,7 +2905,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 +2944,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 +3418,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 +3504,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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1689,7 +1706,11 @@ pub fn tag_pointer_read_tag_id<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let mask: u64 = 0b0000_0111;
|
||||
let mask: u64 = match env.ptr_bytes {
|
||||
8 => 0b0000_0111,
|
||||
4 => 0b0000_0011,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let ptr_int = env.ptr_int();
|
||||
|
||||
|
@ -1708,11 +1729,17 @@ pub fn tag_pointer_clear_tag_id<'a, 'ctx, 'env>(
|
|||
) -> PointerValue<'ctx> {
|
||||
let ptr_int = env.ptr_int();
|
||||
|
||||
let tag_id_bits_mask = match env.ptr_bytes {
|
||||
8 => 3,
|
||||
4 => 2,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let as_int = env.builder.build_ptr_to_int(pointer, ptr_int, "to_int");
|
||||
|
||||
let mask = {
|
||||
let a = env.ptr_int().const_all_ones();
|
||||
let tag_id_bits = env.ptr_int().const_int(3, false);
|
||||
let tag_id_bits = env.ptr_int().const_int(tag_id_bits_mask, false);
|
||||
env.builder.build_left_shift(a, tag_id_bits, "make_mask")
|
||||
};
|
||||
|
||||
|
@ -3085,21 +3112,6 @@ pub fn get_sjlj_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValu
|
|||
.into_pointer_value()
|
||||
}
|
||||
|
||||
pub fn get_sjlj_message_buffer<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> PointerValue<'ctx> {
|
||||
let type_ = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let global = match env.module.get_global("roc_sjlj_message_buffer") {
|
||||
Some(global) => global,
|
||||
None => env
|
||||
.module
|
||||
.add_global(type_, None, "roc_sjlj_message_buffer"),
|
||||
};
|
||||
|
||||
global.set_initializer(&type_.const_zero());
|
||||
|
||||
global.as_pointer_value()
|
||||
}
|
||||
|
||||
fn set_jump_and_catch_long_jump<'a, 'ctx, 'env, F, T>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
|
@ -3931,6 +3943,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 +4443,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 +4458,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
op: LowLevel,
|
||||
args: &[Symbol],
|
||||
update_mode: Option<UpdateMode>,
|
||||
// expect_failed: *const (),
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use LowLevel::*;
|
||||
|
||||
|
@ -4713,7 +4733,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
complex_bitcast(
|
||||
env.builder,
|
||||
list.into(),
|
||||
env.context.i128_type().into(),
|
||||
env.str_list_c_abi().into(),
|
||||
"to_i128",
|
||||
),
|
||||
position,
|
||||
|
@ -4731,7 +4751,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
complex_bitcast(
|
||||
env.builder,
|
||||
list.into(),
|
||||
env.context.i128_type().into(),
|
||||
env.str_list_c_abi().into(),
|
||||
"to_i128",
|
||||
),
|
||||
position,
|
||||
|
@ -5155,9 +5175,36 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
bd.build_conditional_branch(condition, then_block, throw_block);
|
||||
|
||||
{
|
||||
bd.position_at_end(throw_block);
|
||||
|
||||
throw_exception(env, "assert failed!");
|
||||
match env.ptr_bytes {
|
||||
8 => {
|
||||
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);
|
||||
}
|
||||
4 => {
|
||||
// temporary WASM implementation
|
||||
throw_exception(env, "An expectation failed!");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
bd.position_at_end(then_block);
|
||||
|
||||
|
@ -5170,89 +5217,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
let fastcc_type = return_type.fn_type(&fastcc_argument_types, false);
|
||||
|
||||
let fastcc_function = add_func(
|
||||
env.module,
|
||||
&format!("{}_fastcc_wrapper", foreign.as_str()),
|
||||
fastcc_type,
|
||||
Linkage::Private,
|
||||
FAST_CALL_CONV,
|
||||
);
|
||||
|
||||
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);
|
||||
|
||||
call.try_as_basic_value();
|
||||
let return_value = match cc_return {
|
||||
CCReturn::Return => call.try_as_basic_value().left().unwrap(),
|
||||
|
||||
if pass_result_by_pointer {
|
||||
env.builder.build_load(return_pointer, "read_result")
|
||||
} else {
|
||||
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>(
|
||||
|
@ -5928,7 +6057,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
|||
NumAbs => env.call_intrinsic(LLVM_FABS_F64, &[arg.into()]),
|
||||
NumSqrtUnchecked => env.call_intrinsic(LLVM_SQRT_F64, &[arg.into()]),
|
||||
NumLogUnchecked => env.call_intrinsic(LLVM_LOG_F64, &[arg.into()]),
|
||||
NumRound => env.call_intrinsic(LLVM_LROUND_I64_F64, &[arg.into()]),
|
||||
NumRound => call_bitcode_fn(env, &[arg.into()], bitcode::NUM_ROUND),
|
||||
NumSin => env.call_intrinsic(LLVM_SIN_F64, &[arg.into()]),
|
||||
NumCos => env.call_intrinsic(LLVM_COS_F64, &[arg.into()]),
|
||||
NumToFloat => arg.into(), /* Converting from Float to Float is a no-op */
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,8 +47,6 @@ pub fn dict_len<'a, 'ctx, 'env>(
|
|||
scope: &Scope<'a, 'ctx>,
|
||||
dict_symbol: Symbol,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let ctx = env.context;
|
||||
|
||||
let (_, dict_layout) = load_symbol_and_layout(scope, &dict_symbol);
|
||||
|
||||
match dict_layout {
|
||||
|
@ -59,14 +54,17 @@ 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);
|
||||
let length_i64 = call_bitcode_fn(
|
||||
env,
|
||||
&[pass_dict_c_abi(env, dict_as_zig_dict.into())],
|
||||
bitcode::DICT_LEN,
|
||||
);
|
||||
|
||||
call_bitcode_fn(env, &[dict_ptr.into()], bitcode::DICT_LEN)
|
||||
env.builder
|
||||
.build_int_cast(length_i64.into_int_value(), env.ptr_int(), "to_usize")
|
||||
.into()
|
||||
}
|
||||
Layout::Builtin(Builtin::EmptyDict) => ctx.i64_type().const_zero().into(),
|
||||
Layout::Builtin(Builtin::EmptyDict) => env.ptr_int().const_zero().into(),
|
||||
_ => unreachable!("Invalid layout given to Dict.len : {:?}", dict_layout),
|
||||
}
|
||||
}
|
||||
|
@ -95,14 +93,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 +109,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 +123,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 +152,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 +166,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 +180,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 +208,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 +223,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 +231,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 +254,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 +269,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 +280,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 +362,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 +371,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 +387,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 +408,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 +417,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 +448,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 +479,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 +488,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 +496,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 +572,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 +581,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 +594,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 +624,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 +665,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 +676,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 +685,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 +719,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 +735,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 +836,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> {
|
||||
|
|
|
@ -139,8 +139,10 @@ pub fn add_default_roc_externs(env: &Env<'_, '_, '_>) {
|
|||
}
|
||||
}
|
||||
|
||||
if env.is_gen_test {
|
||||
add_sjlj_roc_panic(env)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_sjlj_roc_panic(env: &Env<'_, '_, '_>) {
|
||||
let ctx = env.context;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::debug_info_init;
|
||||
use crate::llvm::bitcode::call_void_bitcode_fn;
|
||||
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, 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,
|
||||
);
|
||||
|
||||
|
@ -211,123 +212,30 @@ impl<'ctx> PointerToRefcount<'ctx> {
|
|||
) {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
let refcount_type = ptr_int(ctx, env.ptr_bytes);
|
||||
|
||||
let entry = ctx.append_basic_block(parent, "entry");
|
||||
builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, parent);
|
||||
|
||||
let refcount_ptr = {
|
||||
let raw_refcount_ptr = parent.get_nth_param(0).unwrap();
|
||||
debug_assert!(raw_refcount_ptr.is_pointer_value());
|
||||
Self {
|
||||
value: raw_refcount_ptr.into_pointer_value(),
|
||||
}
|
||||
};
|
||||
let alignment = env.context.i32_type().const_int(alignment as _, false);
|
||||
|
||||
let refcount = refcount_ptr.get_refcount(env);
|
||||
|
||||
let is_static_allocation = builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
refcount,
|
||||
env.ptr_int().const_zero(),
|
||||
"is_static_allocation",
|
||||
);
|
||||
|
||||
// build blocks
|
||||
let branch_block = ctx.append_basic_block(parent, "branch");
|
||||
let then_block = ctx.append_basic_block(parent, "then");
|
||||
let else_block = ctx.append_basic_block(parent, "else");
|
||||
let return_block = ctx.append_basic_block(parent, "return");
|
||||
|
||||
builder.build_conditional_branch(is_static_allocation, return_block, branch_block);
|
||||
|
||||
let add_with_overflow;
|
||||
|
||||
{
|
||||
builder.position_at_end(branch_block);
|
||||
|
||||
add_with_overflow = env
|
||||
.call_intrinsic(
|
||||
LLVM_SADD_WITH_OVERFLOW_I64,
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
refcount.into(),
|
||||
refcount_type.const_int(-1_i64 as u64, true).into(),
|
||||
env.builder.build_bitcast(
|
||||
parent.get_nth_param(0).unwrap(),
|
||||
env.ptr_int().ptr_type(AddressSpace::Generic),
|
||||
"foo",
|
||||
),
|
||||
alignment.into(),
|
||||
],
|
||||
)
|
||||
.into_struct_value();
|
||||
|
||||
let has_overflowed = builder
|
||||
.build_extract_value(add_with_overflow, 1, "has_overflowed")
|
||||
.unwrap();
|
||||
|
||||
let has_overflowed_comparison = builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
has_overflowed.into_int_value(),
|
||||
ctx.bool_type().const_int(1_u64, false),
|
||||
"has_overflowed",
|
||||
roc_builtins::bitcode::UTILS_DECREF,
|
||||
);
|
||||
|
||||
// TODO what would be most optimial for the branch predictor
|
||||
//
|
||||
// are most refcounts 1 most of the time? or not?
|
||||
builder.build_conditional_branch(has_overflowed_comparison, then_block, else_block);
|
||||
}
|
||||
|
||||
// build then block
|
||||
{
|
||||
builder.position_at_end(then_block);
|
||||
if !env.is_gen_test {
|
||||
let ptr = builder.build_pointer_cast(
|
||||
refcount_ptr.value,
|
||||
ctx.i8_type().ptr_type(AddressSpace::Generic),
|
||||
"cast_to_i8_ptr",
|
||||
);
|
||||
|
||||
match alignment {
|
||||
n if env.ptr_bytes == n => {
|
||||
// the refcount ptr is also the ptr to the allocated region
|
||||
env.call_dealloc(ptr, alignment);
|
||||
}
|
||||
n if 2 * env.ptr_bytes == n => {
|
||||
// we need to step back another ptr_bytes to get the allocated ptr
|
||||
let allocated = Self::from_ptr_to_data(env, ptr);
|
||||
env.call_dealloc(allocated.value, alignment);
|
||||
}
|
||||
n => unreachable!("invalid extra_bytes {:?}", n),
|
||||
}
|
||||
}
|
||||
builder.build_unconditional_branch(return_block);
|
||||
}
|
||||
|
||||
// build else block
|
||||
{
|
||||
builder.position_at_end(else_block);
|
||||
|
||||
let max = builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
refcount,
|
||||
refcount_type.const_int(REFCOUNT_MAX as u64, false),
|
||||
"refcount_max_check",
|
||||
);
|
||||
let decremented = builder
|
||||
.build_extract_value(add_with_overflow, 0, "decrement_refcount")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
let selected = builder.build_select(max, refcount, decremented, "select_refcount");
|
||||
|
||||
refcount_ptr.set_refcount(env, selected.into_int_value());
|
||||
|
||||
builder.build_unconditional_branch(return_block);
|
||||
}
|
||||
|
||||
{
|
||||
builder.position_at_end(return_block);
|
||||
builder.build_return(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn modify_refcount_struct<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -774,7 +682,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 +711,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,
|
||||
|
|
|
@ -831,6 +831,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;
|
||||
|
@ -694,10 +695,8 @@ impl<'a> Procs<'a> {
|
|||
layout: ProcLayout<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
) {
|
||||
let tuple = (name, layout);
|
||||
|
||||
// If we've already specialized this one, no further work is needed.
|
||||
if self.specialized.contains_key(&tuple) {
|
||||
if self.specialized.contains_key(&(name, layout)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -707,15 +706,12 @@ impl<'a> Procs<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
// We're done with that tuple, so move layout back out to avoid cloning it.
|
||||
let (name, layout) = tuple;
|
||||
|
||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
||||
|
||||
// This should only be called when pending_specializations is Some.
|
||||
// Otherwise, it's being called in the wrong pass!
|
||||
match &mut self.pending_specializations {
|
||||
Some(pending_specializations) => {
|
||||
let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
|
||||
|
||||
// register the pending specialization, so this gets code genned later
|
||||
if self.module_thunks.contains(&name) {
|
||||
debug_assert!(layout.arguments.is_empty());
|
||||
|
@ -736,7 +732,26 @@ impl<'a> Procs<'a> {
|
|||
// (We had a bug around this before this system existed!)
|
||||
self.specialized.insert((symbol, layout), InProgress);
|
||||
|
||||
match specialize(env, self, symbol, layout_cache, pending, partial_proc) {
|
||||
// See https://github.com/rtfeldman/roc/issues/1600
|
||||
//
|
||||
// The annotation variable is the generic/lifted/top-level annotation.
|
||||
// It is connected to the variables of the function's body
|
||||
//
|
||||
// fn_var is the variable representing the type that we actually need for the
|
||||
// function right here.
|
||||
//
|
||||
// For some reason, it matters that we unify with the original variable. Extracting
|
||||
// that variable into a SolvedType and then introducing it again severs some
|
||||
// connection that turns out to be important
|
||||
match specialize_variable(
|
||||
env,
|
||||
self,
|
||||
symbol,
|
||||
layout_cache,
|
||||
fn_var,
|
||||
Default::default(),
|
||||
partial_proc,
|
||||
) {
|
||||
Ok((proc, _ignore_layout)) => {
|
||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
||||
// closure. We only specialize functions, storing this value with a closure
|
||||
|
@ -1010,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.
|
||||
|
@ -1191,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)),
|
||||
|
@ -1688,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)
|
||||
|
@ -2448,13 +2466,57 @@ fn specialize_solved_type<'a>(
|
|||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||
partial_proc: PartialProc<'a>,
|
||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||
specialize_variable_help(
|
||||
env,
|
||||
procs,
|
||||
proc_name,
|
||||
layout_cache,
|
||||
|env| introduce_solved_type_to_subs(env, &solved_type),
|
||||
host_exposed_aliases,
|
||||
partial_proc,
|
||||
)
|
||||
}
|
||||
|
||||
fn specialize_variable<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
proc_name: Symbol,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
fn_var: Variable,
|
||||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||
partial_proc: PartialProc<'a>,
|
||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>> {
|
||||
specialize_variable_help(
|
||||
env,
|
||||
procs,
|
||||
proc_name,
|
||||
layout_cache,
|
||||
|_| fn_var,
|
||||
host_exposed_aliases,
|
||||
partial_proc,
|
||||
)
|
||||
}
|
||||
|
||||
fn specialize_variable_help<'a, F>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
proc_name: Symbol,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
fn_var_thunk: F,
|
||||
host_exposed_aliases: BumpMap<Symbol, SolvedType>,
|
||||
partial_proc: PartialProc<'a>,
|
||||
) -> Result<SpecializeSuccess<'a>, SpecializeFailure<'a>>
|
||||
where
|
||||
F: FnOnce(&mut Env<'a, '_>) -> Variable,
|
||||
{
|
||||
// add the specializations that other modules require of us
|
||||
use roc_solve::solve::instantiate_rigids;
|
||||
|
||||
let snapshot = env.subs.snapshot();
|
||||
let cache_snapshot = layout_cache.snapshot();
|
||||
|
||||
let fn_var = introduce_solved_type_to_subs(env, &solved_type);
|
||||
// important: evaluate after the snapshot has been created!
|
||||
let fn_var = fn_var_thunk(env);
|
||||
|
||||
// for debugging only
|
||||
let raw = layout_cache
|
||||
|
@ -2680,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,
|
||||
),
|
||||
|
@ -2698,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(
|
||||
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::Float(num as f64)),
|
||||
Expr::Literal(Literal::Decimal(dec)),
|
||||
Layout::Builtin(Builtin::Decimal),
|
||||
hole,
|
||||
),
|
||||
)
|
||||
}
|
||||
_ => unreachable!("unexpected float precision for integer"),
|
||||
}
|
||||
}
|
||||
|
@ -2723,7 +2791,9 @@ pub fn with_hole<'a>(
|
|||
hole,
|
||||
),
|
||||
|
||||
Num(var, num) => match num_argument_to_int_or_float(env.subs, env.ptr_bytes, var, false) {
|
||||
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,
|
||||
Expr::Literal(Literal::Int(num.into())),
|
||||
|
@ -2742,13 +2812,20 @@ pub fn with_hole<'a>(
|
|||
Layout::Builtin(float_precision_to_builtin(precision)),
|
||||
hole,
|
||||
),
|
||||
IntOrFloat::DecimalFloatType => Stmt::Let(
|
||||
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::Float(num as f64)),
|
||||
Expr::Literal(Literal::Decimal(dec)),
|
||||
Layout::Builtin(Builtin::Decimal),
|
||||
hole,
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
LetNonRec(def, cont, _) => {
|
||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||
if let Closure {
|
||||
|
@ -3047,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);
|
||||
|
@ -3382,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);
|
||||
|
@ -3524,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);
|
||||
|
||||
|
@ -4128,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(_)) => {
|
||||
|
@ -4464,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))));
|
||||
|
@ -5511,6 +5592,7 @@ fn store_pattern_help<'a>(
|
|||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {
|
||||
|
@ -5645,6 +5727,7 @@ fn store_tag_pattern<'a>(
|
|||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
|
@ -5720,6 +5803,7 @@ fn store_newtype_pattern<'a>(
|
|||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
|
@ -5795,6 +5879,7 @@ fn store_record_destruct<'a>(
|
|||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| DecimalLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {
|
||||
|
@ -6766,6 +6851,7 @@ pub enum Pattern<'a> {
|
|||
Underscore,
|
||||
IntLiteral(i128),
|
||||
FloatLiteral(u64),
|
||||
DecimalLiteral(RocDec),
|
||||
BitLiteral {
|
||||
value: bool,
|
||||
tag_name: TagName,
|
||||
|
@ -6842,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,
|
||||
|
@ -6854,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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6872,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,
|
||||
|
@ -7291,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);
|
||||
|
@ -7432,7 +7544,9 @@ fn from_can_record_destruct<'a>(
|
|||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IntPrecision {
|
||||
Usize,
|
||||
I128,
|
||||
I64,
|
||||
I32,
|
||||
|
@ -7468,6 +7582,7 @@ fn int_precision_to_builtin(precision: IntPrecision) -> Builtin<'static> {
|
|||
I32 => Builtin::Int32,
|
||||
I16 => Builtin::Int16,
|
||||
I8 => Builtin::Int8,
|
||||
Usize => Builtin::Usize,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7566,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!(
|
||||
|
@ -7865,7 +7972,15 @@ fn union_lambda_set_to_switch<'a>(
|
|||
assigned: Symbol,
|
||||
hole: &'a Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
debug_assert!(!lambda_set.is_empty());
|
||||
if lambda_set.is_empty() {
|
||||
// NOTE this can happen if there is a type error somewhere. Since the lambda set is empty,
|
||||
// there is really nothing we can do here. We generate a runtime error here which allows
|
||||
// code gen to proceed. We then assume that we hit another (more descriptive) error before
|
||||
// hitting this one
|
||||
|
||||
let msg = "a Lambda Set isempty. Most likely there is a type error in your program.";
|
||||
return Stmt::RuntimeError(msg);
|
||||
}
|
||||
|
||||
let join_point_id = JoinPointId(env.unique_symbol());
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
@ -554,10 +558,10 @@ impl<'a> LambdaSet<'a> {
|
|||
}
|
||||
|
||||
Ok(()) | Err((_, Content::FlexVar(_))) => {
|
||||
// TODO hack for builting functions.
|
||||
// this can happen when there is a type error somewhere
|
||||
Ok(LambdaSet {
|
||||
set: &[],
|
||||
representation: arena.alloc(Layout::Struct(&[])),
|
||||
representation: arena.alloc(Layout::Union(UnionLayout::NonRecursive(&[]))),
|
||||
})
|
||||
}
|
||||
_ => panic!("called LambdaSet.from_var on invalid input"),
|
||||
|
@ -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,14 +30,19 @@ 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"
|
||||
wasmer = "2.0.0"
|
||||
wasmer-wasi = "2.0.0"
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
|
||||
maplit = "1.0.1"
|
||||
quickcheck = "0.8"
|
||||
quickcheck_macros = "0.8"
|
||||
tokio = { version = "0.2", features = ["blocking", "fs", "sync", "rt-threaded"] }
|
||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
wasm-cli-run = []
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#[cfg(test)]
|
||||
mod gen_compare {
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
// use crate::assert_wasm_evals_to as assert_evals_to;
|
||||
use indoc::indoc;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
use indoc::indoc;
|
||||
use roc_std::RocStr;
|
||||
use roc_std::{RocList, RocStr};
|
||||
|
||||
#[test]
|
||||
fn dict_empty_len() {
|
||||
|
@ -158,8 +157,8 @@ fn keys() {
|
|||
Dict.keys myDict
|
||||
"#
|
||||
),
|
||||
&[0, 1, 2],
|
||||
&[i64]
|
||||
RocList::from_slice(&[0, 1, 2]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -179,8 +178,8 @@ fn values() {
|
|||
Dict.values myDict
|
||||
"#
|
||||
),
|
||||
&[100, 200, 300],
|
||||
&[i64]
|
||||
RocList::from_slice(&[100, 200, 300]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -197,8 +196,8 @@ fn from_list_with_fold() {
|
|||
Dict.values myDict
|
||||
"#
|
||||
),
|
||||
&[2, 3, 1],
|
||||
&[i64]
|
||||
RocList::from_slice(&[2, 3, 1]),
|
||||
RocList<i64>
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -242,8 +241,8 @@ fn small_str_keys() {
|
|||
Dict.keys myDict
|
||||
"#
|
||||
),
|
||||
&[RocStr::from("c"), RocStr::from("a"), RocStr::from("b"),],
|
||||
&[RocStr]
|
||||
RocList::from_slice(&[RocStr::from("c"), RocStr::from("a"), RocStr::from("b"),],),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -263,12 +262,12 @@ fn big_str_keys() {
|
|||
Dict.keys myDict
|
||||
"#
|
||||
),
|
||||
&[
|
||||
RocList::from_slice(&[
|
||||
RocStr::from("Leverage agile frameworks to provide a robust"),
|
||||
RocStr::from("to corporate strategy foster collaborative thinking to"),
|
||||
RocStr::from("synopsis for high level overviews. Iterative approaches"),
|
||||
],
|
||||
&[RocStr]
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -287,12 +286,12 @@ fn big_str_values() {
|
|||
Dict.values myDict
|
||||
"#
|
||||
),
|
||||
&[
|
||||
RocList::from_slice(&[
|
||||
RocStr::from("Leverage agile frameworks to provide a robust"),
|
||||
RocStr::from("to corporate strategy foster collaborative thinking to"),
|
||||
RocStr::from("synopsis for high level overviews. Iterative approaches"),
|
||||
],
|
||||
&[RocStr]
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -363,8 +362,8 @@ fn union_prefer_first() {
|
|||
Dict.values myDict
|
||||
"#
|
||||
),
|
||||
&[100],
|
||||
&[i64]
|
||||
RocList::from_slice(&[100]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -423,8 +422,8 @@ fn intersection_prefer_first() {
|
|||
|> Dict.values
|
||||
"#
|
||||
),
|
||||
&[4, 2],
|
||||
&[i64]
|
||||
RocList::from_slice(&[4, 2]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -483,8 +482,8 @@ fn difference_prefer_first() {
|
|||
|> Dict.values
|
||||
"#
|
||||
),
|
||||
&[5, 3, 1],
|
||||
&[i64]
|
||||
RocList::from_slice(&[5, 3, 1]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
// use crate::assert_wasm_evals_to as assert_evals_to;
|
||||
use indoc::indoc;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
use crate::helpers::with_larger_debug_stack;
|
||||
//use crate::assert_wasm_evals_to as assert_evals_to;
|
||||
use core::ffi::c_void;
|
||||
use indoc::indoc;
|
||||
use roc_std::{RocList, RocStr};
|
||||
|
@ -865,9 +865,12 @@ fn list_repeat() {
|
|||
RocList<i64>
|
||||
);
|
||||
|
||||
let empty_lists: &'static [&'static [i64]] = &[&[], &[]];
|
||||
assert_evals_to!(
|
||||
"List.repeat 2 []",
|
||||
RocList::from_slice(&[RocList::default(), RocList::default()]),
|
||||
RocList<RocList<i64>>
|
||||
);
|
||||
|
||||
assert_evals_to!("List.repeat 2 []", empty_lists, &'static [&'static [i64]]);
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -878,8 +881,8 @@ fn list_repeat() {
|
|||
List.repeat 2 noStrs
|
||||
"#
|
||||
),
|
||||
empty_lists,
|
||||
&'static [&'static [i64]]
|
||||
RocList::from_slice(&[RocList::default(), RocList::default()]),
|
||||
RocList<RocList<i64>>
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -1419,10 +1422,8 @@ fn gen_wrap_first() {
|
|||
wrapFirst [ 1, 2 ]
|
||||
"#
|
||||
),
|
||||
// RocList::from_slice(&[1]),
|
||||
// RocList<i64>
|
||||
&[1],
|
||||
&'static [i64]
|
||||
RocList::from_slice(&[1]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1897,34 +1898,54 @@ fn list_product() {
|
|||
#[test]
|
||||
fn list_keep_oks() {
|
||||
assert_evals_to!("List.keepOks [] (\\x -> x)", 0, i64);
|
||||
assert_evals_to!("List.keepOks [1,2] (\\x -> Ok x)", &[1, 2], &[i64]);
|
||||
assert_evals_to!("List.keepOks [1,2] (\\x -> x % 2)", &[1, 0], &[i64]);
|
||||
assert_evals_to!("List.keepOks [Ok 1, Err 2] (\\x -> x)", &[1], &[i64]);
|
||||
assert_evals_to!(
|
||||
"List.keepOks [1,2] (\\x -> Ok x)",
|
||||
RocList::from_slice(&[1, 2]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.keepOks [1,2] (\\x -> x % 2)",
|
||||
RocList::from_slice(&[1, 0]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
"List.keepOks [Ok 1, Err 2] (\\x -> x)",
|
||||
RocList::from_slice(&[1]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_keep_errs() {
|
||||
assert_evals_to!("List.keepErrs [] (\\x -> x)", 0, i64);
|
||||
assert_evals_to!("List.keepErrs [1,2] (\\x -> Err x)", &[1, 2], &[i64]);
|
||||
assert_evals_to!(
|
||||
"List.keepErrs [1,2] (\\x -> Err x)",
|
||||
RocList::from_slice(&[1, 2]),
|
||||
RocList<i64>
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
List.keepErrs [0,1,2] (\x -> x % 0 |> Result.mapErr (\_ -> 32))
|
||||
"#
|
||||
),
|
||||
&[32, 32, 32],
|
||||
&[i64]
|
||||
RocList::from_slice(&[32, 32, 32]),
|
||||
RocList<i64>
|
||||
);
|
||||
|
||||
assert_evals_to!("List.keepErrs [Ok 1, Err 2] (\\x -> x)", &[2], &[i64]);
|
||||
assert_evals_to!(
|
||||
"List.keepErrs [Ok 1, Err 2] (\\x -> x)",
|
||||
RocList::from_slice(&[2]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_map_with_index() {
|
||||
assert_evals_to!(
|
||||
"List.mapWithIndex [0,0,0] (\\index, x -> index + x)",
|
||||
&[0, 1, 2],
|
||||
&[i64]
|
||||
"List.mapWithIndex [0,0,0] (\\index, x -> Num.intCast index + x)",
|
||||
RocList::from_slice(&[0, 1, 2]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1935,11 +1956,15 @@ fn cleanup_because_exception() {
|
|||
indoc!(
|
||||
r#"
|
||||
x = [ 1,2 ]
|
||||
5 + Num.maxInt + 3 + List.len x
|
||||
|
||||
five : I64
|
||||
five = 5
|
||||
|
||||
five + Num.maxInt + 3 + (Num.intCast (List.len x))
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[false; 1]),
|
||||
RocList<bool>
|
||||
9,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#[cfg(test)]
|
||||
mod gen_num {
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
// use crate::assert_wasm_evals_to as assert_evals_to;
|
||||
use indoc::indoc;
|
||||
use roc_std::{RocDec, RocOrder};
|
||||
|
||||
|
|
|
@ -891,6 +891,7 @@ fn overflow_frees_list() {
|
|||
n : I64
|
||||
n = 9_223_372_036_854_775_807 + (Num.intCast (List.len myList))
|
||||
|
||||
index : Nat
|
||||
index = Num.intCast n
|
||||
|
||||
List.get myList index
|
||||
|
@ -1018,7 +1019,7 @@ fn specialize_closure() {
|
|||
y = [1]
|
||||
|
||||
f = \{} -> x
|
||||
g = \{} -> x + List.len y
|
||||
g = \{} -> x + Num.intCast (List.len y)
|
||||
|
||||
[ f, g ]
|
||||
|
||||
|
@ -2292,8 +2293,8 @@ fn build_then_apply_closure() {
|
|||
(\_ -> x) {}
|
||||
"#
|
||||
),
|
||||
"long string that is malloced",
|
||||
&'static str
|
||||
RocStr::from_slice(b"long string that is malloced"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2398,7 +2399,7 @@ fn call_invalid_layout() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "assert failed!")]
|
||||
#[should_panic(expected = "An expectation failed!")]
|
||||
fn expect_fail() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -2683,26 +2684,50 @@ fn list_walk_until() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn int_literal_not_specialized() {
|
||||
fn int_literal_not_specialized_with_annotation() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1600
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
|
||||
satisfy : (U8 -> Bool) -> Str
|
||||
main =
|
||||
satisfy : (U8 -> Str) -> Str
|
||||
satisfy = \_ -> "foo"
|
||||
|
||||
myEq : a, a -> Str
|
||||
myEq = \_, _ -> "bar"
|
||||
|
||||
p1 : Num * -> Str
|
||||
p1 = (\u -> myEq u 64)
|
||||
|
||||
when satisfy p1 is
|
||||
_ -> 32
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_literal_not_specialized_no_annotation() {
|
||||
// see https://github.com/rtfeldman/roc/issues/1600
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main : I64
|
||||
main =
|
||||
p1 = (\u -> u == 97)
|
||||
satisfy : (U8 -> Str) -> Str
|
||||
satisfy = \_ -> "foo"
|
||||
|
||||
satisfyA = satisfy p1
|
||||
myEq : a, a -> Str
|
||||
myEq = \_, _ -> "bar"
|
||||
|
||||
when satisfyA is
|
||||
p1 = (\u -> myEq u 64)
|
||||
|
||||
when satisfy p1 is
|
||||
_ -> 32
|
||||
"#
|
||||
),
|
||||
|
@ -2736,3 +2761,21 @@ fn unresolved_tvar_when_capture_is_unused() {
|
|||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Roc failed with message: ")]
|
||||
fn value_not_exposed_hits_panic() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [ main ] to "./platform"
|
||||
|
||||
main : I64
|
||||
main =
|
||||
Str.toInt 32
|
||||
"#
|
||||
),
|
||||
32,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
// use crate::assert_wasm_evals_to as assert_evals_to;
|
||||
use indoc::indoc;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
use indoc::indoc;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
use indoc::indoc;
|
||||
use roc_std::RocList;
|
||||
|
||||
#[test]
|
||||
fn empty_len() {
|
||||
|
@ -38,8 +38,8 @@ fn single_to_list() {
|
|||
Set.toList (Set.single 42)
|
||||
"#
|
||||
),
|
||||
&[42],
|
||||
&[i64]
|
||||
RocList::from_slice(&[42]),
|
||||
RocList<i64>
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -48,8 +48,8 @@ fn single_to_list() {
|
|||
Set.toList (Set.single 1)
|
||||
"#
|
||||
),
|
||||
&[1],
|
||||
&[i64]
|
||||
RocList::from_slice(&[1]),
|
||||
RocList<i64>
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -58,8 +58,8 @@ fn single_to_list() {
|
|||
Set.toList (Set.single 1.0)
|
||||
"#
|
||||
),
|
||||
&[1.0],
|
||||
&[f64]
|
||||
RocList::from_slice(&[1.0]),
|
||||
RocList<f64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -75,8 +75,8 @@ fn insert() {
|
|||
|> Set.toList
|
||||
"#
|
||||
),
|
||||
&[0, 1, 2],
|
||||
&[i64]
|
||||
RocList::from_slice(&[0, 1, 2]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -93,8 +93,8 @@ fn remove() {
|
|||
|> Set.toList
|
||||
"#
|
||||
),
|
||||
&[0],
|
||||
&[i64]
|
||||
RocList::from_slice(&[0]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -113,8 +113,8 @@ fn union() {
|
|||
|> Set.toList
|
||||
"#
|
||||
),
|
||||
&[4, 2, 3, 1],
|
||||
&[i64]
|
||||
RocList::from_slice(&[4, 2, 3, 1]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -133,8 +133,8 @@ fn difference() {
|
|||
|> Set.toList
|
||||
"#
|
||||
),
|
||||
&[2],
|
||||
&[i64]
|
||||
RocList::from_slice(&[2]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -153,8 +153,8 @@ fn intersection() {
|
|||
|> Set.toList
|
||||
"#
|
||||
),
|
||||
&[1],
|
||||
&[i64]
|
||||
RocList::from_slice(&[1]),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -196,8 +196,6 @@ fn contains() {
|
|||
|
||||
#[test]
|
||||
fn from_list() {
|
||||
let empty_list: &'static [i64] = &[];
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -206,8 +204,8 @@ fn from_list() {
|
|||
|> Set.toList
|
||||
"#
|
||||
),
|
||||
&[4, 2, 3, 1],
|
||||
&[i64]
|
||||
RocList::from_slice(&[4, 2, 3, 1]),
|
||||
RocList<i64>
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -218,8 +216,8 @@ fn from_list() {
|
|||
|> Set.toList
|
||||
"#
|
||||
),
|
||||
empty_list,
|
||||
&[i64]
|
||||
RocList::default(),
|
||||
RocList<i64>
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -233,7 +231,7 @@ fn from_list() {
|
|||
|> Set.toList
|
||||
"#
|
||||
),
|
||||
empty_list,
|
||||
&[i64]
|
||||
RocList::default(),
|
||||
RocList<i64>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,33 +3,7 @@
|
|||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
use indoc::indoc;
|
||||
use roc_std::RocStr;
|
||||
use std::cmp::min;
|
||||
|
||||
const ROC_STR_MEM_SIZE: usize = core::mem::size_of::<RocStr>();
|
||||
|
||||
fn small_str(str: &str) -> [u8; ROC_STR_MEM_SIZE] {
|
||||
let mut bytes: [u8; ROC_STR_MEM_SIZE] = Default::default();
|
||||
|
||||
let mut index: usize = 0;
|
||||
while index < ROC_STR_MEM_SIZE {
|
||||
bytes[index] = 0;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
let str_bytes = str.as_bytes();
|
||||
|
||||
let output_len: usize = min(str_bytes.len(), ROC_STR_MEM_SIZE);
|
||||
index = 0;
|
||||
while index < output_len {
|
||||
bytes[index] = str_bytes[index];
|
||||
index += 1;
|
||||
}
|
||||
|
||||
bytes[ROC_STR_MEM_SIZE - 1] = 0b1000_0000 ^ (output_len as u8);
|
||||
|
||||
bytes
|
||||
}
|
||||
use roc_std::{RocList, RocStr};
|
||||
|
||||
#[test]
|
||||
fn str_split_bigger_delimiter_small_str() {
|
||||
|
@ -78,8 +52,8 @@ fn str_split_str_concat_repeated() {
|
|||
|
||||
"#
|
||||
),
|
||||
"JJJJJJJJJJJJJJJJJJJJJJJJJ",
|
||||
&'static str
|
||||
RocStr::from_slice(b"JJJJJJJJJJJJJJJJJJJJJJJJJ"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -96,8 +70,8 @@ fn str_split_small_str_bigger_delimiter() {
|
|||
_ -> ""
|
||||
"#
|
||||
),
|
||||
small_str("JJJ"),
|
||||
[u8; ROC_STR_MEM_SIZE]
|
||||
RocStr::from_slice(b"JJJ"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -109,8 +83,11 @@ fn str_split_big_str_small_delimiter() {
|
|||
Str.split "01234567789abcdefghi?01234567789abcdefghi" "?"
|
||||
"#
|
||||
),
|
||||
&["01234567789abcdefghi", "01234567789abcdefghi"],
|
||||
&'static [&'static str]
|
||||
RocList::from_slice(&[
|
||||
RocStr::from_slice(b"01234567789abcdefghi"),
|
||||
RocStr::from_slice(b"01234567789abcdefghi")
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -119,8 +96,11 @@ fn str_split_big_str_small_delimiter() {
|
|||
Str.split "01234567789abcdefghi 3ch 01234567789abcdefghi" "3ch"
|
||||
"#
|
||||
),
|
||||
&["01234567789abcdefghi ", " 01234567789abcdefghi"],
|
||||
&'static [&'static str]
|
||||
RocList::from_slice(&[
|
||||
RocStr::from_slice(b"01234567789abcdefghi "),
|
||||
RocStr::from_slice(b" 01234567789abcdefghi")
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -132,8 +112,12 @@ fn str_split_small_str_small_delimiter() {
|
|||
Str.split "J!J!J" "!"
|
||||
"#
|
||||
),
|
||||
&[small_str("J"), small_str("J"), small_str("J")],
|
||||
&'static [[u8; ROC_STR_MEM_SIZE]]
|
||||
RocList::from_slice(&[
|
||||
RocStr::from_slice(b"J"),
|
||||
RocStr::from_slice(b"J"),
|
||||
RocStr::from_slice(b"J")
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -147,8 +131,8 @@ fn str_split_bigger_delimiter_big_strs() {
|
|||
"than the delimiter which happens to be very very long"
|
||||
"#
|
||||
),
|
||||
&["string to split is shorter"],
|
||||
&'static [&'static str]
|
||||
RocList::from_slice(&[RocStr::from_slice(b"string to split is shorter")]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -160,9 +144,9 @@ fn str_split_empty_strs() {
|
|||
Str.split "" ""
|
||||
"#
|
||||
),
|
||||
&[small_str("")],
|
||||
&'static [[u8; ROC_STR_MEM_SIZE]]
|
||||
)
|
||||
RocList::from_slice(&[RocStr::from_slice(b"")]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -173,8 +157,8 @@ fn str_split_minimal_example() {
|
|||
Str.split "a," ","
|
||||
"#
|
||||
),
|
||||
&[small_str("a"), small_str("")],
|
||||
&'static [[u8; ROC_STR_MEM_SIZE]]
|
||||
RocList::from_slice(&[RocStr::from_slice(b"a"), RocStr::from_slice(b"")]),
|
||||
RocList<RocStr>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -201,8 +185,12 @@ fn str_split_small_str_big_delimiter() {
|
|||
"---- ---- ---- ---- ----"
|
||||
"#
|
||||
),
|
||||
&[small_str("1"), small_str("2"), small_str("")],
|
||||
&'static [[u8; ROC_STR_MEM_SIZE]]
|
||||
RocList::from_slice(&[
|
||||
RocStr::from_slice(b"1"),
|
||||
RocStr::from_slice(b"2"),
|
||||
RocStr::from_slice(b"")
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -216,8 +204,12 @@ fn str_split_small_str_20_char_delimiter() {
|
|||
"|-- -- -- -- -- -- |"
|
||||
"#
|
||||
),
|
||||
&[small_str("3"), small_str("4"), small_str("")],
|
||||
&'static [[u8; ROC_STR_MEM_SIZE]]
|
||||
RocList::from_slice(&[
|
||||
RocStr::from_slice(b"3"),
|
||||
RocStr::from_slice(b"4"),
|
||||
RocStr::from_slice(b"")
|
||||
]),
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -231,14 +223,14 @@ fn str_concat_big_to_big() {
|
|||
"Second string that is also fairly long. Two long strings test things that might not appear with short strings."
|
||||
"#
|
||||
),
|
||||
"First string that is fairly long. Longer strings make for different errors. Second string that is also fairly long. Two long strings test things that might not appear with short strings.",
|
||||
&'static str
|
||||
RocStr::from_slice(b"First string that is fairly long. Longer strings make for different errors. Second string that is also fairly long. Two long strings test things that might not appear with short strings."),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn small_str_literal() {
|
||||
assert_evals_to!(
|
||||
assert_llvm_evals_to!(
|
||||
"\"JJJJJJJJJJJJJJJ\"",
|
||||
[
|
||||
0x4a,
|
||||
|
@ -267,7 +259,7 @@ fn small_str_zeroed_literal() {
|
|||
// Verifies that we zero out unused bytes in the string.
|
||||
// This is important so that string equality tests don't randomly
|
||||
// fail due to unused memory being there!
|
||||
assert_evals_to!(
|
||||
assert_llvm_evals_to!(
|
||||
"\"J\"",
|
||||
[
|
||||
0x4a,
|
||||
|
@ -293,7 +285,7 @@ fn small_str_zeroed_literal() {
|
|||
|
||||
#[test]
|
||||
fn small_str_concat_empty_first_arg() {
|
||||
assert_evals_to!(
|
||||
assert_llvm_evals_to!(
|
||||
r#"Str.concat "" "JJJJJJJJJJJJJJJ""#,
|
||||
[
|
||||
0x4a,
|
||||
|
@ -319,7 +311,7 @@ fn small_str_concat_empty_first_arg() {
|
|||
|
||||
#[test]
|
||||
fn small_str_concat_empty_second_arg() {
|
||||
assert_evals_to!(
|
||||
assert_llvm_evals_to!(
|
||||
r#"Str.concat "JJJJJJJJJJJJJJJ" """#,
|
||||
[
|
||||
0x4a,
|
||||
|
@ -347,14 +339,14 @@ fn small_str_concat_empty_second_arg() {
|
|||
fn small_str_concat_small_to_big() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "abc" " this is longer than 15 chars""#,
|
||||
"abc this is longer than 15 chars",
|
||||
&'static str
|
||||
RocStr::from_slice(b"abc this is longer than 15 chars"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn small_str_concat_small_to_small_staying_small() {
|
||||
assert_evals_to!(
|
||||
assert_llvm_evals_to!(
|
||||
r#"Str.concat "J" "JJJJJJJJJJJJJJ""#,
|
||||
[
|
||||
0x4a,
|
||||
|
@ -382,14 +374,14 @@ fn small_str_concat_small_to_small_staying_small() {
|
|||
fn small_str_concat_small_to_small_overflow_to_big() {
|
||||
assert_evals_to!(
|
||||
r#"Str.concat "abcdefghijklm" "nopqrstuvwxyz""#,
|
||||
"abcdefghijklmnopqrstuvwxyz",
|
||||
&'static str
|
||||
RocStr::from_slice(b"abcdefghijklmnopqrstuvwxyz"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn str_concat_empty() {
|
||||
assert_evals_to!(r#"Str.concat "" """#, "", &'static str);
|
||||
assert_evals_to!(r#"Str.concat "" """#, RocStr::default(), RocStr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -511,10 +503,18 @@ fn str_from_int() {
|
|||
);
|
||||
|
||||
let max = format!("{}", i64::MAX);
|
||||
assert_evals_to!(r#"Str.fromInt Num.maxInt"#, &max, &'static str);
|
||||
assert_evals_to!(
|
||||
r#"Str.fromInt Num.maxInt"#,
|
||||
RocStr::from_slice(max.as_bytes()),
|
||||
RocStr
|
||||
);
|
||||
|
||||
let min = format!("{}", i64::MIN);
|
||||
assert_evals_to!(r#"Str.fromInt Num.minInt"#, &min, &'static str);
|
||||
assert_evals_to!(
|
||||
r#"Str.fromInt Num.minInt"#,
|
||||
RocStr::from_slice(min.as_bytes()),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -785,8 +785,8 @@ fn nested_recursive_literal() {
|
|||
printExpr expr
|
||||
"#
|
||||
),
|
||||
"Add (Add (Val 3) (Val 1)) (Add (Val 1) (Var 1))",
|
||||
&'static str
|
||||
RocStr::from_slice(b"Add (Add (Val 3) (Val 1)) (Add (Val 1) (Var 1))"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -815,19 +815,23 @@ 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]
|
||||
fn str_to_utf8() {
|
||||
assert_evals_to!(r#"Str.toUtf8 "hello""#, &[104, 101, 108, 108, 111], &[u8]);
|
||||
assert_evals_to!(
|
||||
r#"Str.toUtf8 "hello""#,
|
||||
RocList::from_slice(&[104, 101, 108, 108, 111]),
|
||||
RocList<u8>
|
||||
);
|
||||
assert_evals_to!(
|
||||
r#"Str.toUtf8 "this is a long string""#,
|
||||
&[
|
||||
RocList::from_slice(&[
|
||||
116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 108, 111, 110, 103, 32, 115, 116, 114,
|
||||
105, 110, 103
|
||||
],
|
||||
&[u8]
|
||||
]),
|
||||
RocList<u8>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use crate::assert_evals_to;
|
||||
use crate::assert_llvm_evals_to;
|
||||
// use crate::assert_wasm_evals_to as assert_evals_to;
|
||||
use indoc::indoc;
|
||||
use roc_std::{RocList, RocStr};
|
||||
|
||||
|
@ -517,8 +517,8 @@ fn if_guard_multiple() {
|
|||
{ a: f 0, b: f 1, c: f 2, d: f 4 }
|
||||
"#
|
||||
),
|
||||
(0, 1, 2, 0),
|
||||
(i64, i64, i64, i64)
|
||||
[0, 1, 2, 0],
|
||||
[i64; 4]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -925,7 +925,7 @@ fn alignment_in_single_tag_pattern_match() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn alignment_in_multi_tag_construction() {
|
||||
fn alignment_in_multi_tag_construction_two() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"#
|
||||
|
@ -939,7 +939,10 @@ fn alignment_in_multi_tag_construction() {
|
|||
((32i64, true), 1),
|
||||
((i64, bool), u8)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alignment_in_multi_tag_construction_three() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r"#
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use inkwell::module::Module;
|
||||
use libloading::Library;
|
||||
use roc_build::link::module_to_dylib;
|
||||
use roc_build::program::FunctionIterator;
|
||||
|
@ -7,7 +8,9 @@ use roc_collections::all::{MutMap, MutSet};
|
|||
use roc_gen_llvm::llvm::externs::add_default_roc_externs;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_std::{RocDec, RocList, RocOrder, RocStr};
|
||||
use roc_types::subs::VarStore;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
fn promote_expr_to_module(src: &str) -> String {
|
||||
let mut buffer = String::from("app \"test\" provides [ main ] to \"./platform\"\n\nmain =\n");
|
||||
|
@ -27,16 +30,17 @@ pub fn test_builtin_defs(symbol: Symbol, var_store: &mut VarStore) -> Option<Def
|
|||
|
||||
// this is not actually dead code, but only used by cfg_test modules
|
||||
// so "normally" it is dead, only at testing time is it used
|
||||
#[allow(dead_code)]
|
||||
#[inline(never)]
|
||||
pub fn helper<'a>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_llvm_module<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: &'a roc_builtins::std::StdLib,
|
||||
is_gen_test: bool,
|
||||
ignore_problems: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> (&'static str, String, Library) {
|
||||
target: &Triple,
|
||||
opt_level: OptLevel,
|
||||
) -> (&'static str, String, &'a Module<'a>) {
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
|
@ -53,7 +57,6 @@ pub fn helper<'a>(
|
|||
module_src = &temp;
|
||||
}
|
||||
|
||||
let target = target_lexicon::Triple::host();
|
||||
let ptr_bytes = target.pointer_width().unwrap().bytes() as u32;
|
||||
|
||||
let exposed_types = MutMap::default();
|
||||
|
@ -169,13 +172,7 @@ pub fn helper<'a>(
|
|||
}
|
||||
|
||||
let builder = context.create_builder();
|
||||
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app");
|
||||
|
||||
let opt_level = if cfg!(debug_assertions) {
|
||||
OptLevel::Normal
|
||||
} else {
|
||||
OptLevel::Optimize
|
||||
};
|
||||
let module = roc_gen_llvm::llvm::build::module_from_builtins(context, "app", ptr_bytes);
|
||||
|
||||
let module = arena.alloc(module);
|
||||
let (module_pass, function_pass) =
|
||||
|
@ -255,10 +252,318 @@ pub fn helper<'a>(
|
|||
// Uncomment this to see the module's optimized LLVM instruction output:
|
||||
// env.module.print_to_stderr();
|
||||
|
||||
let lib = module_to_dylib(env.module, &target, opt_level)
|
||||
.expect("Error loading compiled dylib for test");
|
||||
(main_fn_name, delayed_errors.join("\n"), env.module)
|
||||
}
|
||||
|
||||
(main_fn_name, delayed_errors.join("\n"), lib)
|
||||
#[allow(dead_code)]
|
||||
#[inline(never)]
|
||||
pub fn helper<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: &'a roc_builtins::std::StdLib,
|
||||
is_gen_test: bool,
|
||||
ignore_problems: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> (&'static str, String, Library) {
|
||||
let target = target_lexicon::Triple::host();
|
||||
|
||||
let opt_level = if cfg!(debug_assertions) {
|
||||
OptLevel::Normal
|
||||
} else {
|
||||
OptLevel::Optimize
|
||||
};
|
||||
|
||||
let (main_fn_name, delayed_errors, module) = create_llvm_module(
|
||||
arena,
|
||||
src,
|
||||
stdlib,
|
||||
is_gen_test,
|
||||
ignore_problems,
|
||||
context,
|
||||
&target,
|
||||
opt_level,
|
||||
);
|
||||
|
||||
let lib =
|
||||
module_to_dylib(module, &target, opt_level).expect("Error loading compiled dylib for test");
|
||||
|
||||
(main_fn_name, delayed_errors, lib)
|
||||
}
|
||||
|
||||
fn wasm32_target_tripple() -> Triple {
|
||||
use target_lexicon::{Architecture, BinaryFormat};
|
||||
|
||||
let mut triple = Triple::unknown();
|
||||
|
||||
triple.architecture = Architecture::Wasm32;
|
||||
triple.binary_format = BinaryFormat::Wasm;
|
||||
|
||||
triple
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn helper_wasm<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
src: &str,
|
||||
stdlib: &'a roc_builtins::std::StdLib,
|
||||
_is_gen_test: bool,
|
||||
ignore_problems: bool,
|
||||
context: &'a inkwell::context::Context,
|
||||
) -> wasmer::Instance {
|
||||
let target = wasm32_target_tripple();
|
||||
|
||||
let opt_level = if cfg!(debug_assertions) {
|
||||
OptLevel::Normal
|
||||
} else {
|
||||
OptLevel::Optimize
|
||||
};
|
||||
|
||||
let is_gen_test = false;
|
||||
let (_main_fn_name, _delayed_errors, llvm_module) = create_llvm_module(
|
||||
arena,
|
||||
src,
|
||||
stdlib,
|
||||
is_gen_test,
|
||||
ignore_problems,
|
||||
context,
|
||||
&target,
|
||||
opt_level,
|
||||
);
|
||||
|
||||
use inkwell::targets::{InitializationConfig, Target, TargetTriple};
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let dir_path = dir.path();
|
||||
// let zig_global_cache_path = std::path::PathBuf::from("/home/folkertdev/roc/wasm/mess");
|
||||
|
||||
let test_a_path = dir_path.join("test.a");
|
||||
let test_wasm_path = dir_path.join("libmain.wasm");
|
||||
|
||||
Target::initialize_webassembly(&InitializationConfig::default());
|
||||
|
||||
let triple = TargetTriple::create("wasm32-unknown-unknown-wasm");
|
||||
|
||||
llvm_module.set_triple(&triple);
|
||||
llvm_module.set_source_file_name("Test.roc");
|
||||
|
||||
let target_machine = Target::from_name("wasm32")
|
||||
.unwrap()
|
||||
.create_target_machine(
|
||||
&triple,
|
||||
"",
|
||||
"", // TODO: this probably should be TargetMachine::get_host_cpu_features() to enable all features.
|
||||
inkwell::OptimizationLevel::None,
|
||||
inkwell::targets::RelocMode::Default,
|
||||
inkwell::targets::CodeModel::Default,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let file_type = inkwell::targets::FileType::Object;
|
||||
|
||||
target_machine
|
||||
.write_to_file(llvm_module, file_type, &test_a_path)
|
||||
.unwrap();
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
// Command::new("/opt/wasi-sdk/bin/clang")
|
||||
|
||||
/*
|
||||
Command::new("zig")
|
||||
.current_dir(dir_path)
|
||||
.args(&[
|
||||
"cc",
|
||||
"/home/folkertdev/roc/wasm/libmain.a",
|
||||
test_a_path.to_str().unwrap(),
|
||||
"-target",
|
||||
"wasm32-wasi",
|
||||
"-o",
|
||||
test_wasm_path.to_str().unwrap(),
|
||||
"--sysroot=/opt/wasi-sdk/share/wasi-sysroot/",
|
||||
"-Xlinker", "--export-dynamic",
|
||||
// "-Xlinker", "--allow-undefined"
|
||||
// "--global-cache-dir",
|
||||
// zig_global_cache_path.to_str().unwrap(),
|
||||
])
|
||||
.status()
|
||||
.unwrap();
|
||||
*/
|
||||
|
||||
Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig")
|
||||
.current_dir(dir_path)
|
||||
.args(&[
|
||||
"wasm-ld",
|
||||
"/home/folkertdev/roc/wasm/libmain.a",
|
||||
"/home/folkertdev/roc/wasm/libc.a",
|
||||
test_a_path.to_str().unwrap(),
|
||||
"-o",
|
||||
test_wasm_path.to_str().unwrap(),
|
||||
"--export-dynamic",
|
||||
"--allow-undefined",
|
||||
"--no-entry",
|
||||
])
|
||||
.status()
|
||||
.unwrap();
|
||||
|
||||
/*
|
||||
Command::new("/home/folkertdev/Downloads/zig-linux-x86_64-0.9.0-dev.848+d5ef5da59/zig")
|
||||
.current_dir(dir_path)
|
||||
.args(&[
|
||||
"build-lib",
|
||||
"/home/folkertdev/roc/wasm/libmain.a",
|
||||
test_a_path.to_str().unwrap(),
|
||||
"-target",
|
||||
"wasm32-wasi",
|
||||
"-dynamic",
|
||||
"-lc",
|
||||
// "--global-cache-dir",
|
||||
// zig_global_cache_path.to_str().unwrap(),
|
||||
])
|
||||
.status()
|
||||
.unwrap();
|
||||
*/
|
||||
|
||||
// now, do wasmer stuff
|
||||
|
||||
use wasmer::{Function, Instance, Module, Store};
|
||||
|
||||
let store = Store::default();
|
||||
let module = Module::from_file(&store, &test_wasm_path).unwrap();
|
||||
|
||||
// First, we create the `WasiEnv`
|
||||
use wasmer_wasi::WasiState;
|
||||
let mut wasi_env = WasiState::new("hello")
|
||||
// .args(&["world"])
|
||||
// .env("KEY", "Value")
|
||||
.finalize()
|
||||
.unwrap();
|
||||
|
||||
// Then, we get the import object related to our WASI
|
||||
// and attach it to the Wasm instance.
|
||||
let mut import_object = wasi_env
|
||||
.import_object(&module)
|
||||
.unwrap_or_else(|_| wasmer::imports!());
|
||||
|
||||
{
|
||||
let mut exts = wasmer::Exports::new();
|
||||
|
||||
let main_function = Function::new_native(&store, fake_wasm_main_function);
|
||||
let ext = wasmer::Extern::Function(main_function);
|
||||
exts.insert("main", ext);
|
||||
|
||||
let main_function = Function::new_native(&store, wasm_roc_panic);
|
||||
let ext = wasmer::Extern::Function(main_function);
|
||||
exts.insert("roc_panic", ext);
|
||||
|
||||
import_object.register("env", exts);
|
||||
}
|
||||
|
||||
Instance::new(&module, &import_object).unwrap()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn wasm_roc_panic(address: u32, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let mut string = "";
|
||||
|
||||
MEMORY.with(|f| {
|
||||
let memory = f.borrow().unwrap();
|
||||
|
||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(address);
|
||||
let width = 100;
|
||||
let c_ptr = (ptr.deref(memory, 0, width)).unwrap();
|
||||
|
||||
use libc::c_char;
|
||||
use std::ffi::CStr;
|
||||
let slice = unsafe { CStr::from_ptr(c_ptr as *const _ as *const c_char) };
|
||||
string = slice.to_str().unwrap();
|
||||
});
|
||||
|
||||
panic!("Roc failed with message: {:?}", string)
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
thread_local! {
|
||||
pub static MEMORY: RefCell<Option<&'static wasmer::Memory>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn fake_wasm_main_function(_: u32, _: u32) -> u32 {
|
||||
panic!("wasm entered the main function; this should never happen!")
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn assert_wasm_evals_to_help<T>(src: &str, ignore_problems: bool) -> Result<T, String>
|
||||
where
|
||||
T: FromWasmMemory,
|
||||
{
|
||||
let arena = bumpalo::Bump::new();
|
||||
let context = inkwell::context::Context::create();
|
||||
|
||||
// NOTE the stdlib must be in the arena; just taking a reference will segfault
|
||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||
|
||||
let is_gen_test = true;
|
||||
let instance = crate::helpers::eval::helper_wasm(
|
||||
&arena,
|
||||
src,
|
||||
stdlib,
|
||||
is_gen_test,
|
||||
ignore_problems,
|
||||
&context,
|
||||
);
|
||||
|
||||
let memory = instance.exports.get_memory("memory").unwrap();
|
||||
|
||||
crate::helpers::eval::MEMORY.with(|f| {
|
||||
*f.borrow_mut() = Some(unsafe { std::mem::transmute(memory) });
|
||||
});
|
||||
|
||||
let test_wrapper = instance.exports.get_function("test_wrapper").unwrap();
|
||||
|
||||
match test_wrapper.call(&[]) {
|
||||
Err(e) => Err(format!("call to `test_wrapper`: {:?}", e)),
|
||||
Ok(result) => {
|
||||
let address = match result[0] {
|
||||
wasmer::Value::I32(a) => a,
|
||||
_ => panic!(),
|
||||
};
|
||||
|
||||
let output = <T as crate::helpers::eval::FromWasmMemory>::decode(
|
||||
memory,
|
||||
// skip the RocCallResult tag id
|
||||
address as u32 + 8,
|
||||
);
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_wasm_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $ignore_problems:expr) => {
|
||||
match $crate::helpers::eval::assert_wasm_evals_to_help::<$ty>($src, $ignore_problems) {
|
||||
Err(msg) => panic!("Wasm test failed: {:?}", msg),
|
||||
Ok(actual) => {
|
||||
#[allow(clippy::bool_assert_comparison)]
|
||||
assert_eq!($transform(actual), $expected, "Wasm test failed")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity, false);
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
@ -288,41 +593,282 @@ macro_rules! assert_llvm_evals_to {
|
|||
let expected = $expected;
|
||||
#[allow(clippy::redundant_closure_call)]
|
||||
let given = $transform(success);
|
||||
assert_eq!(&given, &expected);
|
||||
assert_eq!(&given, &expected, "LLVM test failed");
|
||||
};
|
||||
run_jit_function!(lib, main_fn_name, $ty, transform, errors)
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity, false);
|
||||
};
|
||||
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty) => {{
|
||||
assert_evals_to!($src, $expected, $ty, (|val| val));
|
||||
assert_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity);
|
||||
}};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
// Same as above, except with an additional transformation argument.
|
||||
{
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
#[cfg(feature = "wasm-cli-run")]
|
||||
$crate::assert_wasm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn identity<T>(value: T) -> T {
|
||||
value
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assert_non_opt_evals_to {
|
||||
($src:expr, $expected:expr, $ty:ty) => {{
|
||||
assert_llvm_evals_to!($src, $expected, $ty, (|val| val));
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $crate::helpers::eval::identity);
|
||||
}};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||
// Same as above, except with an additional transformation argument.
|
||||
{
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform, false);
|
||||
}
|
||||
};
|
||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {{
|
||||
assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
||||
$crate::assert_llvm_evals_to!($src, $expected, $ty, $transform);
|
||||
}};
|
||||
}
|
||||
|
||||
pub trait FromWasmMemory: Sized {
|
||||
const SIZE_OF_WASM: usize;
|
||||
const ALIGN_OF_WASM: usize;
|
||||
const ACTUAL_WIDTH: usize = if (Self::SIZE_OF_WASM % Self::ALIGN_OF_WASM) == 0 {
|
||||
Self::SIZE_OF_WASM
|
||||
} else {
|
||||
Self::SIZE_OF_WASM + (Self::ALIGN_OF_WASM - (Self::SIZE_OF_WASM % Self::ALIGN_OF_WASM))
|
||||
};
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! from_wasm_memory_primitive_decode {
|
||||
($type_name:ident) => {
|
||||
const SIZE_OF_WASM: usize = core::mem::size_of::<$type_name>();
|
||||
const ALIGN_OF_WASM: usize = core::mem::align_of::<$type_name>();
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
let mut output: MaybeUninit<Self> = MaybeUninit::uninit();
|
||||
let width = std::mem::size_of::<Self>();
|
||||
|
||||
let ptr = output.as_mut_ptr();
|
||||
let raw_ptr = ptr as *mut u8;
|
||||
let slice = unsafe { std::slice::from_raw_parts_mut(raw_ptr, width) };
|
||||
|
||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(offset as u32);
|
||||
let foobar = (ptr.deref(memory, 0, width as u32)).unwrap();
|
||||
let wasm_slice = unsafe { std::mem::transmute(foobar) };
|
||||
|
||||
slice.copy_from_slice(wasm_slice);
|
||||
|
||||
unsafe { output.assume_init() }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! from_wasm_memory_primitive {
|
||||
($($type_name:ident ,)+) => {
|
||||
$(
|
||||
impl FromWasmMemory for $type_name {
|
||||
from_wasm_memory_primitive_decode!($type_name);
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
from_wasm_memory_primitive!(
|
||||
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, f32, f64, bool, RocDec, RocOrder,
|
||||
);
|
||||
|
||||
impl FromWasmMemory for () {
|
||||
const SIZE_OF_WASM: usize = 0;
|
||||
const ALIGN_OF_WASM: usize = 0;
|
||||
|
||||
fn decode(_: &wasmer::Memory, _: u32) -> Self {}
|
||||
}
|
||||
|
||||
impl FromWasmMemory for RocStr {
|
||||
const SIZE_OF_WASM: usize = 8;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let bytes = <u64 as FromWasmMemory>::decode(memory, offset);
|
||||
|
||||
let length = (bytes >> 32) as u32;
|
||||
let elements = bytes as u32;
|
||||
|
||||
if length == 0 {
|
||||
RocStr::default()
|
||||
} else if (length as i32) < 0 {
|
||||
// this is a small string
|
||||
let last_byte = bytes.to_ne_bytes()[7];
|
||||
let actual_length = (last_byte ^ 0b1000_0000) as usize;
|
||||
|
||||
let slice = &bytes.to_ne_bytes()[..actual_length as usize];
|
||||
RocStr::from_slice(slice)
|
||||
} else {
|
||||
// this is a big string
|
||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(elements);
|
||||
let foobar = (ptr.deref(memory, 0, length)).unwrap();
|
||||
let wasm_slice = unsafe { std::mem::transmute(foobar) };
|
||||
|
||||
RocStr::from_slice(wasm_slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasmMemory + Clone> FromWasmMemory for RocList<T> {
|
||||
const SIZE_OF_WASM: usize = 8;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let bytes = <u64 as FromWasmMemory>::decode(memory, offset);
|
||||
|
||||
let length = (bytes >> 32) as u32;
|
||||
let elements = bytes as u32;
|
||||
|
||||
let mut items = Vec::with_capacity(length as usize);
|
||||
|
||||
for i in 0..length {
|
||||
let item = <T as FromWasmMemory>::decode(
|
||||
memory,
|
||||
elements + i * <T as FromWasmMemory>::SIZE_OF_WASM as u32,
|
||||
);
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
RocList::from_slice(&items)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasmMemory + Clone> FromWasmMemory for &'_ [T] {
|
||||
const SIZE_OF_WASM: usize = 8;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let bytes = <u64 as FromWasmMemory>::decode(memory, offset);
|
||||
|
||||
let length = (bytes >> 32) as u32;
|
||||
let elements = bytes as u32;
|
||||
|
||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(elements);
|
||||
let width = <T as FromWasmMemory>::SIZE_OF_WASM as u32 * length;
|
||||
let foobar = (ptr.deref(memory, 0, width)).unwrap();
|
||||
let wasm_slice =
|
||||
unsafe { std::slice::from_raw_parts(foobar as *const _ as *const _, length as usize) };
|
||||
|
||||
wasm_slice
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasmMemory> FromWasmMemory for &'_ T {
|
||||
const SIZE_OF_WASM: usize = 4;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let elements = <u32 as FromWasmMemory>::decode(memory, offset);
|
||||
|
||||
let actual = <T as FromWasmMemory>::decode(memory, elements);
|
||||
|
||||
let b = Box::new(actual);
|
||||
|
||||
std::boxed::Box::<T>::leak(b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasmMemory + Clone, const N: usize> FromWasmMemory for [T; N] {
|
||||
const SIZE_OF_WASM: usize = N * T::SIZE_OF_WASM;
|
||||
const ALIGN_OF_WASM: usize = T::ALIGN_OF_WASM;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
let ptr: wasmer::WasmPtr<u8, wasmer::Array> = wasmer::WasmPtr::new(offset);
|
||||
let width = <T as FromWasmMemory>::SIZE_OF_WASM as u32 * N as u32;
|
||||
let foobar = (ptr.deref(memory, 0, width)).unwrap();
|
||||
let wasm_slice: &[T; N] = unsafe { &*(foobar as *const _ as *const [T; N]) };
|
||||
|
||||
wasm_slice.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWasmMemory for usize {
|
||||
const SIZE_OF_WASM: usize = 4;
|
||||
const ALIGN_OF_WASM: usize = 4;
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
<u32 as FromWasmMemory>::decode(memory, offset) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromWasmMemory, U: FromWasmMemory> FromWasmMemory for (T, U) {
|
||||
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM;
|
||||
const ALIGN_OF_WASM: usize = max2(T::SIZE_OF_WASM, U::SIZE_OF_WASM);
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
assert!(
|
||||
T::ALIGN_OF_WASM >= U::ALIGN_OF_WASM,
|
||||
"this function does not handle alignment"
|
||||
);
|
||||
|
||||
let t = <T as FromWasmMemory>::decode(memory, offset);
|
||||
|
||||
let u = <U as FromWasmMemory>::decode(memory, offset + T::ACTUAL_WIDTH as u32);
|
||||
|
||||
(t, u)
|
||||
}
|
||||
}
|
||||
|
||||
const fn max2(a: usize, b: usize) -> usize {
|
||||
if a > b {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
const fn max3(a: usize, b: usize, c: usize) -> usize {
|
||||
max2(max2(a, b), c)
|
||||
}
|
||||
|
||||
impl<T: FromWasmMemory, U: FromWasmMemory, V: FromWasmMemory> FromWasmMemory for (T, U, V) {
|
||||
const SIZE_OF_WASM: usize = T::SIZE_OF_WASM + U::SIZE_OF_WASM + V::SIZE_OF_WASM;
|
||||
const ALIGN_OF_WASM: usize = max3(T::SIZE_OF_WASM, U::SIZE_OF_WASM, V::SIZE_OF_WASM);
|
||||
|
||||
fn decode(memory: &wasmer::Memory, offset: u32) -> Self {
|
||||
assert!(
|
||||
T::ALIGN_OF_WASM >= U::ALIGN_OF_WASM,
|
||||
"this function does not handle alignment"
|
||||
);
|
||||
|
||||
assert!(
|
||||
U::ALIGN_OF_WASM >= V::ALIGN_OF_WASM,
|
||||
"this function does not handle alignment"
|
||||
);
|
||||
|
||||
let t = <T as FromWasmMemory>::decode(memory, offset);
|
||||
|
||||
let u = <U as FromWasmMemory>::decode(memory, offset + T::ACTUAL_WIDTH as u32);
|
||||
|
||||
let v = <V as FromWasmMemory>::decode(
|
||||
memory,
|
||||
offset + T::ACTUAL_WIDTH as u32 + U::ACTUAL_WIDTH as u32,
|
||||
);
|
||||
|
||||
(t, u, v)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,5 +1,6 @@
|
|||
pub const NOTHING_OPENED: &str =
|
||||
"Execute `cargo run edit` from the root folder of the repo to try the editor.";
|
||||
|
||||
pub const START_TIP: &str = r#"Currently supported: lists, records, string, numbers and value definitions.
|
||||
|
||||
Use `Ctrl+Shift+Up` or `Cmd+Shift+Up` to select surrounding expression.
|
||||
|
|
1
examples/.gitignore
vendored
1
examples/.gitignore
vendored
|
@ -4,4 +4,3 @@ app
|
|||
libhost.a
|
||||
roc_app.ll
|
||||
roc_app.bc
|
||||
effect-example
|
||||
|
|
2
examples/benchmarks/.gitignore
vendored
2
examples/benchmarks/.gitignore
vendored
|
@ -1,6 +1,8 @@
|
|||
app
|
||||
*.o
|
||||
*.dSYM
|
||||
*.ll
|
||||
*.bc
|
||||
libhost.a
|
||||
roc_app.ll
|
||||
roc_app.bc
|
||||
|
|
|
@ -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);
|
||||
|
||||
()
|
||||
}
|
|
@ -12,10 +12,6 @@ To run in release mode instead, do:
|
|||
$ cargo run --release Hello.roc
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.
|
||||
|
||||
## Design Notes
|
||||
|
||||
This demonstrates the basic design of hosts: Roc code gets compiled into a pure
|
||||
|
|
|
@ -12,10 +12,6 @@ To run in release mode instead, do:
|
|||
$ cargo run --release Hello.roc
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.
|
||||
|
||||
## Design Notes
|
||||
|
||||
This demonstrates the basic design of hosts: Roc code gets compiled into a pure
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -11,7 +11,3 @@ To run in release mode instead, do:
|
|||
```bash
|
||||
$ cargo run --release Quicksort.roc
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.
|
||||
|
|
|
@ -5,14 +5,10 @@ pkgs.stdenv.mkDerivation {
|
|||
src = pkgs.fetchFromGitHub {
|
||||
owner = "vaivaswatha";
|
||||
repo = "debugir";
|
||||
rev = "ed454ba264f30d2a70264357a31d94db3dd676eb";
|
||||
sha256 = "08hrn66zn5pa8jk45msl9ipa8d1p7r9gmpknh41fyjr6c7qpmfrk";
|
||||
rev = "db871e6cee7f653e284b226e2567a2574635247c";
|
||||
sha256 = "0rgh9gawf92mjya1plxlgi9azkwca3gq8qa5hri18k4b7sbjm6lx";
|
||||
};
|
||||
buildInputs = with pkgs; [
|
||||
cmake
|
||||
libxml2
|
||||
llvmPackages_12.llvm.dev
|
||||
];
|
||||
buildInputs = with pkgs; [ cmake libxml2 llvmPackages_12.llvm.dev ];
|
||||
buildPhase = ''
|
||||
mkdir build
|
||||
cd build
|
||||
|
|
|
@ -44,6 +44,12 @@ pub struct RocList<T> {
|
|||
length: usize,
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for RocList<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self::from_slice(self.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Storage {
|
||||
ReadOnly,
|
||||
|
@ -584,6 +590,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;
|
||||
|
@ -610,7 +620,9 @@ impl RocStr {
|
|||
}
|
||||
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
if self.is_small_str() {
|
||||
if self.is_empty() {
|
||||
&[]
|
||||
} else if self.is_small_str() {
|
||||
unsafe { core::slice::from_raw_parts(self.get_small_str_ptr(), self.len()) }
|
||||
} else {
|
||||
unsafe { core::slice::from_raw_parts(self.elements, self.length) }
|
||||
|
@ -714,7 +726,7 @@ impl Clone for RocStr {
|
|||
|
||||
impl Drop for RocStr {
|
||||
fn drop(&mut self) {
|
||||
if !self.is_small_str() {
|
||||
if !self.is_small_str() && !self.is_empty() {
|
||||
let storage_ptr = self.get_storage_ptr_mut();
|
||||
|
||||
unsafe {
|
||||
|
@ -832,11 +844,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,7 +855,9 @@ impl RocDec {
|
|||
}
|
||||
|
||||
// Calculate the low digits - the ones after the decimal point.
|
||||
let lo = match after_point.parse::<i128>() {
|
||||
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!
|
||||
|
@ -861,6 +873,9 @@ impl RocDec {
|
|||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
|
||||
// Calculate the high digits - the ones before the decimal point.
|
||||
|
|
14
shell.nix
14
shell.nix
|
@ -17,6 +17,7 @@ let
|
|||
|
||||
linuxInputs = with pkgs;
|
||||
lib.optionals stdenv.isLinux [
|
||||
glibc_multi
|
||||
valgrind
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
|
@ -49,7 +50,6 @@ let
|
|||
zig
|
||||
|
||||
# lib deps
|
||||
glibc_multi
|
||||
libffi
|
||||
libxml2
|
||||
ncurses
|
||||
|
@ -70,13 +70,9 @@ in pkgs.mkShell {
|
|||
|
||||
# Additional Env vars
|
||||
LLVM_SYS_120_PREFIX = "${llvmPkgs.llvm.dev}";
|
||||
NIXOS_GLIBC_PATH = "${pkgs.glibc_multi.out}/lib";
|
||||
NIXOS_GLIBC_PATH =
|
||||
if pkgs.stdenv.isLinux then "${pkgs.glibc_multi.out}/lib" else "";
|
||||
LD_LIBRARY_PATH = with pkgs;
|
||||
lib.makeLibraryPath ([
|
||||
pkg-config
|
||||
stdenv.cc.cc.lib
|
||||
libffi
|
||||
ncurses
|
||||
zlib
|
||||
] ++ linuxInputs);
|
||||
lib.makeLibraryPath
|
||||
([ pkg-config stdenv.cc.cc.lib libffi ncurses zlib ] ++ linuxInputs);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue