mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Merge branch 'trunk' into dev-backend-num-is-zero
This commit is contained in:
commit
10afadd810
21 changed files with 726 additions and 506 deletions
62
Cargo.lock
generated
62
Cargo.lock
generated
|
@ -245,6 +245,18 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitvec"
|
||||||
|
version = "0.22.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5237f00a8c86130a0cc317830e558b966dd7850d48a953d998c813f01a41b527"
|
||||||
|
dependencies = [
|
||||||
|
"funty",
|
||||||
|
"radium",
|
||||||
|
"tap",
|
||||||
|
"wyz",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block"
|
name = "block"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -1274,6 +1286,12 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "funty"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1847abb9cb65d566acd5942e94aea9c8f547ad02c98e1649326fc0e8910b8b1e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
@ -2512,6 +2530,28 @@ dependencies = [
|
||||||
"ttf-parser 0.12.3",
|
"ttf-parser 0.12.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packed_struct"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c48e482b9a59ad6c2cdb06f7725e7bd33fe3525baaf4699fde7bfea6a5b77b1"
|
||||||
|
dependencies = [
|
||||||
|
"bitvec",
|
||||||
|
"packed_struct_codegen",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packed_struct_codegen"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56e3692b867ec1d48ccb441e951637a2cc3130d0912c0059e48319e1c83e44bc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "page_size"
|
name = "page_size"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -2887,6 +2927,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radium"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
@ -3345,6 +3391,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"object 0.26.2",
|
"object 0.26.2",
|
||||||
|
"packed_struct",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -4017,6 +4064,12 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tap"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.2"
|
version = "0.12.2"
|
||||||
|
@ -5049,6 +5102,15 @@ dependencies = [
|
||||||
"rand_core 0.6.3",
|
"rand_core 0.6.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wyz"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "129e027ad65ce1453680623c3fb5163cbf7107bfe1aa32257e7d0e63f9ced188"
|
||||||
|
dependencies = [
|
||||||
|
"tap",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x11-clipboard"
|
name = "x11-clipboard"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
|
|
@ -810,17 +810,13 @@ fn type_to_variable<'a>(
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let mut arg_vars = Vec::with_capacity(args.len());
|
let mut arg_vars = Vec::with_capacity(args.len());
|
||||||
let mut new_aliases = BumpMap::new_in(arena);
|
|
||||||
|
|
||||||
for (arg, arg_type_id) in args.iter(mempool) {
|
for (_, arg_type_id) in args.iter(mempool) {
|
||||||
let arg_type = mempool.get(*arg_type_id);
|
let arg_type = mempool.get(*arg_type_id);
|
||||||
|
|
||||||
let arg_var = type_to_variable(arena, mempool, subs, rank, pools, cached, arg_type);
|
let arg_var = type_to_variable(arena, mempool, subs, rank, pools, cached, arg_type);
|
||||||
|
|
||||||
let arg_str = arg.as_str(mempool);
|
arg_vars.push(arg_var);
|
||||||
|
|
||||||
arg_vars.push((roc_module::ident::Lowercase::from(arg_str), arg_var));
|
|
||||||
new_aliases.insert(arg_str, arg_var);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, []);
|
let arg_vars = AliasVariables::insert_into_subs(subs, arg_vars, []);
|
||||||
|
|
39
cli/src/format.rs
Normal file
39
cli/src/format.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use bumpalo::collections::String;
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use roc_fmt::def::fmt_def;
|
||||||
|
use roc_fmt::module::fmt_module;
|
||||||
|
use roc_parse::{
|
||||||
|
module::{self, module_defs},
|
||||||
|
parser::{Parser, State},
|
||||||
|
};
|
||||||
|
use roc_reporting::user_error;
|
||||||
|
|
||||||
|
pub fn format(files: Vec<PathBuf>) {
|
||||||
|
for file in files {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
let src = std::fs::read_to_string(&file).unwrap();
|
||||||
|
|
||||||
|
match module::parse_header(&arena, State::new(src.as_bytes())) {
|
||||||
|
Ok((result, state)) => {
|
||||||
|
let mut buf = String::new_in(&arena);
|
||||||
|
|
||||||
|
fmt_module(&mut buf, &result);
|
||||||
|
|
||||||
|
match module_defs().parse(&arena, state) {
|
||||||
|
Ok((_, loc_defs, _)) => {
|
||||||
|
for loc_def in loc_defs {
|
||||||
|
fmt_def(&mut buf, arena.alloc(loc_def.value), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => user_error!("Unexpected parse failure when parsing this for defs formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::write(&file, buf).unwrap();
|
||||||
|
}
|
||||||
|
Err(error) => user_error!("Unexpected parse failure when parsing this for module header formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, error)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,14 +9,17 @@ use roc_load::file::LoadingProblem;
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::OptLevel;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use target_lexicon::BinaryFormat;
|
use target_lexicon::BinaryFormat;
|
||||||
use target_lexicon::{Architecture, OperatingSystem, Triple, X86_32Architecture};
|
use target_lexicon::{Architecture, OperatingSystem, Triple, X86_32Architecture};
|
||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
|
mod format;
|
||||||
pub mod repl;
|
pub mod repl;
|
||||||
|
pub use format::format;
|
||||||
|
|
||||||
pub const CMD_BUILD: &str = "build";
|
pub const CMD_BUILD: &str = "build";
|
||||||
pub const CMD_REPL: &str = "repl";
|
pub const CMD_REPL: &str = "repl";
|
||||||
|
@ -24,6 +27,7 @@ pub const CMD_EDIT: &str = "edit";
|
||||||
pub const CMD_DOCS: &str = "docs";
|
pub const CMD_DOCS: &str = "docs";
|
||||||
pub const CMD_CHECK: &str = "check";
|
pub const CMD_CHECK: &str = "check";
|
||||||
pub const CMD_VERSION: &str = "version";
|
pub const CMD_VERSION: &str = "version";
|
||||||
|
pub const CMD_FORMAT: &str = "format";
|
||||||
|
|
||||||
pub const FLAG_DEBUG: &str = "debug";
|
pub const FLAG_DEBUG: &str = "debug";
|
||||||
pub const FLAG_DEV: &str = "dev";
|
pub const FLAG_DEV: &str = "dev";
|
||||||
|
@ -35,6 +39,7 @@ pub const FLAG_LINK: &str = "roc-linker";
|
||||||
pub const FLAG_PRECOMPILED: &str = "precompiled-host";
|
pub const FLAG_PRECOMPILED: &str = "precompiled-host";
|
||||||
pub const FLAG_VALGRIND: &str = "valgrind";
|
pub const FLAG_VALGRIND: &str = "valgrind";
|
||||||
pub const ROC_FILE: &str = "ROC_FILE";
|
pub const ROC_FILE: &str = "ROC_FILE";
|
||||||
|
pub const ROC_DIR: &str = "ROC_DIR";
|
||||||
pub const BACKEND: &str = "BACKEND";
|
pub const BACKEND: &str = "BACKEND";
|
||||||
pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
||||||
pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
|
pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
|
||||||
|
@ -111,6 +116,14 @@ pub fn build_app<'a>() -> App<'a> {
|
||||||
.subcommand(App::new(CMD_REPL)
|
.subcommand(App::new(CMD_REPL)
|
||||||
.about("Launch the interactive Read Eval Print Loop (REPL)")
|
.about("Launch the interactive Read Eval Print Loop (REPL)")
|
||||||
)
|
)
|
||||||
|
.subcommand(App::new(CMD_FORMAT)
|
||||||
|
.about("Format Roc code")
|
||||||
|
.arg(
|
||||||
|
Arg::new(DIRECTORY_OR_FILES)
|
||||||
|
.index(1)
|
||||||
|
.multiple_values(true)
|
||||||
|
.required(false))
|
||||||
|
)
|
||||||
.subcommand(App::new(CMD_VERSION)
|
.subcommand(App::new(CMD_VERSION)
|
||||||
.about("Print version information")
|
.about("Print version information")
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use roc_cli::build::check_file;
|
use roc_cli::build::check_file;
|
||||||
use roc_cli::{
|
use roc_cli::{
|
||||||
build_app, docs, repl, BuildConfig, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT, CMD_REPL,
|
build_app, docs, format, repl, BuildConfig, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT,
|
||||||
CMD_VERSION, DIRECTORY_OR_FILES, FLAG_TIME, ROC_FILE,
|
CMD_FORMAT, CMD_REPL, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_TIME, ROC_FILE,
|
||||||
};
|
};
|
||||||
use roc_load::file::LoadingProblem;
|
use roc_load::file::LoadingProblem;
|
||||||
use std::fs::{self, FileType};
|
use std::fs::{self, FileType};
|
||||||
|
@ -101,7 +101,10 @@ fn main() -> io::Result<()> {
|
||||||
match maybe_values {
|
match maybe_values {
|
||||||
None => {
|
None => {
|
||||||
let mut os_string_values: Vec<OsString> = Vec::new();
|
let mut os_string_values: Vec<OsString> = Vec::new();
|
||||||
read_all_roc_files(&OsStr::new("./").to_os_string(), &mut os_string_values)?;
|
read_all_roc_files(
|
||||||
|
&std::env::current_dir()?.as_os_str().to_os_string(),
|
||||||
|
&mut os_string_values,
|
||||||
|
)?;
|
||||||
for os_string in os_string_values {
|
for os_string in os_string_values {
|
||||||
values.push(os_string);
|
values.push(os_string);
|
||||||
}
|
}
|
||||||
|
@ -125,6 +128,44 @@ fn main() -> io::Result<()> {
|
||||||
|
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
Some(CMD_FORMAT) => {
|
||||||
|
let maybe_values = matches
|
||||||
|
.subcommand_matches(CMD_FORMAT)
|
||||||
|
.unwrap()
|
||||||
|
.values_of_os(DIRECTORY_OR_FILES);
|
||||||
|
|
||||||
|
let mut values: Vec<OsString> = Vec::new();
|
||||||
|
|
||||||
|
match maybe_values {
|
||||||
|
None => {
|
||||||
|
let mut os_string_values: Vec<OsString> = Vec::new();
|
||||||
|
read_all_roc_files(
|
||||||
|
&std::env::current_dir()?.as_os_str().to_os_string(),
|
||||||
|
&mut os_string_values,
|
||||||
|
)?;
|
||||||
|
for os_string in os_string_values {
|
||||||
|
values.push(os_string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(os_values) => {
|
||||||
|
for os_str in os_values {
|
||||||
|
values.push(os_str.to_os_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut roc_files = Vec::new();
|
||||||
|
|
||||||
|
// Populate roc_files
|
||||||
|
for os_str in values {
|
||||||
|
let metadata = fs::metadata(os_str.clone())?;
|
||||||
|
roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
format(roc_files);
|
||||||
|
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
Some(CMD_VERSION) => {
|
Some(CMD_VERSION) => {
|
||||||
println!("roc {}", concatcp!(include_str!("../../version.txt"), "\n"));
|
println!("roc {}", concatcp!(include_str!("../../version.txt"), "\n"));
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ target-lexicon = "0.12.2"
|
||||||
# Probably just need to specify an extra field that used to be implicit or something.
|
# Probably just need to specify an extra field that used to be implicit or something.
|
||||||
# When fixed also update the version of object in the linker.
|
# When fixed also update the version of object in the linker.
|
||||||
object = { version = "0.26.2", features = ["write"] }
|
object = { version = "0.26.2", features = ["write"] }
|
||||||
|
packed_struct = "0.10.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage};
|
use crate::generic64::{Assembler, CallConv, RegTrait, SymbolStorage};
|
||||||
use crate::Relocation;
|
use crate::Relocation;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
|
use packed_struct::prelude::*;
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::Layout;
|
use roc_mono::layout::Layout;
|
||||||
|
@ -42,8 +43,16 @@ pub enum AArch64GeneralReg {
|
||||||
/// This can mean Zero or Stack Pointer depending on the context.
|
/// This can mean Zero or Stack Pointer depending on the context.
|
||||||
ZRSP = 31,
|
ZRSP = 31,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegTrait for AArch64GeneralReg {}
|
impl RegTrait for AArch64GeneralReg {}
|
||||||
|
|
||||||
|
impl AArch64GeneralReg {
|
||||||
|
#[inline(always)]
|
||||||
|
fn id(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum AArch64FloatReg {}
|
pub enum AArch64FloatReg {}
|
||||||
|
@ -518,90 +527,165 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
|
|
||||||
impl AArch64Assembler {}
|
impl AArch64Assembler {}
|
||||||
|
|
||||||
/// AArch64Instruction, maps all instructions to an enum.
|
// Instructions
|
||||||
/// Decoding the function should be cheap because we will always inline.
|
// ARM manual section C3
|
||||||
/// All of the operations should resolved by constants, leave just some bit manipulation.
|
// https://developer.arm.com/documentation/ddi0487/ga
|
||||||
/// Enums may not be complete since we will only add what we need.
|
// Map all instructions to a packed struct.
|
||||||
#[derive(Debug)]
|
|
||||||
enum AArch64Instruction {
|
trait Aarch64Bytes: PackedStruct {
|
||||||
_Reserved,
|
#[inline(always)]
|
||||||
_SVE,
|
fn bytes(&self) -> [u8; 4] {
|
||||||
DPImm(DPImmGroup),
|
let mut bytes: [u8; 4] = [0, 0, 0, 0];
|
||||||
Branch(BranchGroup),
|
|
||||||
LdStr(LdStrGroup),
|
self.pack_to_slice(&mut bytes).unwrap();
|
||||||
DPReg(DPRegGroup),
|
|
||||||
_DPFloat,
|
bytes.reverse();
|
||||||
|
|
||||||
|
bytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(PackedStruct, Debug)]
|
||||||
enum BranchGroup {
|
#[packed_struct(endian = "msb")]
|
||||||
UnconditionBranchReg {
|
pub struct MoveWideImmediate {
|
||||||
opc: u8,
|
|
||||||
op2: u8,
|
|
||||||
op3: u8,
|
|
||||||
reg_n: AArch64GeneralReg,
|
|
||||||
op4: u8,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum DPRegGroup {
|
|
||||||
AddSubShifted {
|
|
||||||
sf: bool,
|
sf: bool,
|
||||||
subtract: bool,
|
opc: Integer<u8, packed_bits::Bits<2>>,
|
||||||
set_flags: bool,
|
fixed: Integer<u8, packed_bits::Bits<6>>, // = 0b100101,
|
||||||
shift: u8,
|
hw: Integer<u8, packed_bits::Bits<2>>,
|
||||||
reg_m: AArch64GeneralReg,
|
|
||||||
imm6: u8,
|
|
||||||
reg_n: AArch64GeneralReg,
|
|
||||||
reg_d: AArch64GeneralReg,
|
|
||||||
},
|
|
||||||
Logical {
|
|
||||||
sf: bool,
|
|
||||||
op: DPRegLogicalOp,
|
|
||||||
shift: u8,
|
|
||||||
reg_m: AArch64GeneralReg,
|
|
||||||
imm6: u8,
|
|
||||||
reg_n: AArch64GeneralReg,
|
|
||||||
reg_d: AArch64GeneralReg,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum DPImmGroup {
|
|
||||||
AddSubImm {
|
|
||||||
sf: bool,
|
|
||||||
subtract: bool,
|
|
||||||
set_flags: bool,
|
|
||||||
shift: bool,
|
|
||||||
imm12: u16,
|
|
||||||
reg_n: AArch64GeneralReg,
|
|
||||||
reg_d: AArch64GeneralReg,
|
|
||||||
},
|
|
||||||
MoveWide {
|
|
||||||
sf: bool,
|
|
||||||
opc: u8,
|
|
||||||
hw: u8,
|
|
||||||
imm16: u16,
|
imm16: u16,
|
||||||
reg_d: AArch64GeneralReg,
|
reg_d: Integer<u8, packed_bits::Bits<5>>, // AArch64GeneralReg
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl Aarch64Bytes for MoveWideImmediate {}
|
||||||
enum LdStrGroup {
|
|
||||||
UnsignedImm {
|
impl MoveWideImmediate {
|
||||||
size: u8,
|
#[inline(always)]
|
||||||
v: bool,
|
fn new(opc: u8, rd: AArch64GeneralReg, imm16: u16, hw: u8, sf: bool) -> Self {
|
||||||
opc: u8,
|
// TODO: revisit this is we change where we want to check the shift
|
||||||
|
// currently this is done in the assembler above
|
||||||
|
// assert!(shift % 16 == 0 && shift <= 48);
|
||||||
|
debug_assert!(hw <= 0b11);
|
||||||
|
debug_assert!(opc <= 0b11);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
reg_d: rd.id().into(),
|
||||||
|
imm16,
|
||||||
|
hw: hw.into(),
|
||||||
|
opc: opc.into(),
|
||||||
|
sf,
|
||||||
|
fixed: 0b100101.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PackedStruct, Debug)]
|
||||||
|
#[packed_struct(endian = "msb")]
|
||||||
|
pub struct ArithmeticImmediate {
|
||||||
|
sf: bool,
|
||||||
|
op: bool, // add or subtract
|
||||||
|
s: bool,
|
||||||
|
fixed: Integer<u8, packed_bits::Bits<6>>, // = 0b100010,
|
||||||
|
sh: bool, // shift
|
||||||
|
imm12: Integer<u16, packed_bits::Bits<12>>,
|
||||||
|
reg_n: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
reg_d: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aarch64Bytes for ArithmeticImmediate {}
|
||||||
|
|
||||||
|
impl ArithmeticImmediate {
|
||||||
|
#[inline(always)]
|
||||||
|
fn new(
|
||||||
|
op: bool,
|
||||||
|
s: bool,
|
||||||
|
rd: AArch64GeneralReg,
|
||||||
|
rn: AArch64GeneralReg,
|
||||||
imm12: u16,
|
imm12: u16,
|
||||||
reg_n: AArch64GeneralReg,
|
sh: bool,
|
||||||
reg_t: AArch64GeneralReg,
|
) -> Self {
|
||||||
},
|
debug_assert!(imm12 <= 0xFFF);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
reg_d: rd.id().into(),
|
||||||
|
reg_n: rn.id().into(),
|
||||||
|
imm12: imm12.into(),
|
||||||
|
sh,
|
||||||
|
s,
|
||||||
|
op,
|
||||||
|
// true for 64 bit addition
|
||||||
|
// false for 32 bit addition
|
||||||
|
sf: true,
|
||||||
|
fixed: 0b100010.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum ShiftType {
|
||||||
|
LSL = 0,
|
||||||
|
LSR = 1,
|
||||||
|
ASR = 2,
|
||||||
|
ROR = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShiftType {
|
||||||
|
#[inline(always)]
|
||||||
|
fn id(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PackedStruct)]
|
||||||
|
#[packed_struct(endian = "msb")]
|
||||||
|
pub struct ArithmeticShifted {
|
||||||
|
sf: bool,
|
||||||
|
op: bool, // add or subtract
|
||||||
|
s: bool,
|
||||||
|
fixed: Integer<u8, packed_bits::Bits<5>>, // = 0b01011,
|
||||||
|
shift: Integer<u8, packed_bits::Bits<2>>, // shift
|
||||||
|
fixed2: bool, // = 0b0,
|
||||||
|
reg_m: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
imm6: Integer<u8, packed_bits::Bits<6>>,
|
||||||
|
reg_n: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
reg_d: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aarch64Bytes for ArithmeticShifted {}
|
||||||
|
|
||||||
|
impl ArithmeticShifted {
|
||||||
|
#[inline(always)]
|
||||||
|
fn new(
|
||||||
|
op: bool,
|
||||||
|
s: bool,
|
||||||
|
shift: ShiftType,
|
||||||
|
imm6: u8,
|
||||||
|
rm: AArch64GeneralReg,
|
||||||
|
rn: AArch64GeneralReg,
|
||||||
|
rd: AArch64GeneralReg,
|
||||||
|
) -> Self {
|
||||||
|
debug_assert!(imm6 <= 0b111111);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
reg_d: rd.id().into(),
|
||||||
|
reg_n: rn.id().into(),
|
||||||
|
imm6: imm6.into(),
|
||||||
|
reg_m: rm.id().into(),
|
||||||
|
fixed2: false,
|
||||||
|
shift: shift.id().into(),
|
||||||
|
fixed: 0b01011.into(),
|
||||||
|
s,
|
||||||
|
op,
|
||||||
|
// true for 64 bit addition
|
||||||
|
// false for 32 bit addition
|
||||||
|
sf: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
enum DPRegLogicalOp {
|
enum LogicalOp {
|
||||||
AND,
|
AND,
|
||||||
BIC,
|
BIC,
|
||||||
ORR,
|
ORR,
|
||||||
|
@ -612,155 +696,142 @@ enum DPRegLogicalOp {
|
||||||
BICS,
|
BICS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PackedStruct)]
|
||||||
|
#[packed_struct(endian = "msb")]
|
||||||
|
pub struct LogicalShiftedRegister {
|
||||||
|
sf: bool,
|
||||||
|
op: Integer<u8, packed_bits::Bits<2>>,
|
||||||
|
fixed: Integer<u8, packed_bits::Bits<5>>, // = 0b01010,
|
||||||
|
shift: Integer<u8, packed_bits::Bits<2>>, // shift
|
||||||
|
n: bool,
|
||||||
|
reg_m: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
imm6: Integer<u8, packed_bits::Bits<6>>,
|
||||||
|
reg_n: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
reg_d: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aarch64Bytes for LogicalShiftedRegister {}
|
||||||
|
|
||||||
|
impl LogicalShiftedRegister {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn build_instruction(inst: AArch64Instruction) -> [u8; 4] {
|
fn new(
|
||||||
let mut out: u32 = 0;
|
op: LogicalOp,
|
||||||
match inst {
|
shift: ShiftType,
|
||||||
AArch64Instruction::Branch(branch) => {
|
imm6: u8,
|
||||||
out |= 0b101 << 26;
|
rm: AArch64GeneralReg,
|
||||||
match branch {
|
rn: AArch64GeneralReg,
|
||||||
BranchGroup::UnconditionBranchReg {
|
rd: AArch64GeneralReg,
|
||||||
opc,
|
) -> Self {
|
||||||
op2,
|
|
||||||
op3,
|
|
||||||
reg_n,
|
|
||||||
op4,
|
|
||||||
} => {
|
|
||||||
debug_assert!(opc <= 0b1111);
|
|
||||||
debug_assert!(op2 <= 0b11111);
|
|
||||||
debug_assert!(op3 <= 0b111111);
|
|
||||||
debug_assert!(op4 <= 0b1111);
|
|
||||||
out |= 0b1101011 << 25;
|
|
||||||
out |= (opc as u32) << 21;
|
|
||||||
out |= (op2 as u32) << 16;
|
|
||||||
out |= (op3 as u32) << 10;
|
|
||||||
out |= (reg_n as u32) << 5;
|
|
||||||
out |= op4 as u32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AArch64Instruction::DPImm(dpimm) => {
|
|
||||||
out |= 0b100 << 26;
|
|
||||||
match dpimm {
|
|
||||||
DPImmGroup::MoveWide {
|
|
||||||
sf,
|
|
||||||
opc,
|
|
||||||
hw,
|
|
||||||
imm16,
|
|
||||||
reg_d,
|
|
||||||
} => {
|
|
||||||
out |= (sf as u32) << 31;
|
|
||||||
out |= (opc as u32) << 29;
|
|
||||||
out |= 0b101 << 23;
|
|
||||||
out |= (hw as u32) << 21;
|
|
||||||
out |= (imm16 as u32) << 5;
|
|
||||||
out |= reg_d as u32;
|
|
||||||
}
|
|
||||||
DPImmGroup::AddSubImm {
|
|
||||||
sf,
|
|
||||||
subtract,
|
|
||||||
set_flags,
|
|
||||||
shift,
|
|
||||||
imm12,
|
|
||||||
reg_n,
|
|
||||||
reg_d,
|
|
||||||
} => {
|
|
||||||
debug_assert!(imm12 <= 0xFFF);
|
|
||||||
out |= (sf as u32) << 31;
|
|
||||||
out |= (subtract as u32) << 30;
|
|
||||||
out |= (set_flags as u32) << 29;
|
|
||||||
out |= 0b010 << 23;
|
|
||||||
out |= (shift as u32) << 22;
|
|
||||||
out |= (imm12 as u32) << 10;
|
|
||||||
out |= (reg_n as u32) << 5;
|
|
||||||
out |= reg_d as u32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AArch64Instruction::DPReg(dpreg) => {
|
|
||||||
out |= 0b101 << 25;
|
|
||||||
match dpreg {
|
|
||||||
DPRegGroup::Logical {
|
|
||||||
sf,
|
|
||||||
op,
|
|
||||||
shift,
|
|
||||||
reg_m,
|
|
||||||
imm6,
|
|
||||||
reg_n,
|
|
||||||
reg_d,
|
|
||||||
} => {
|
|
||||||
debug_assert!(shift <= 0b11);
|
|
||||||
debug_assert!(imm6 <= 0b111111);
|
debug_assert!(imm6 <= 0b111111);
|
||||||
let (opc, n) = match op {
|
|
||||||
DPRegLogicalOp::AND => (0b00, 0),
|
let (op, n) = match op {
|
||||||
DPRegLogicalOp::BIC => (0b00, 1),
|
LogicalOp::AND => (0b00, false),
|
||||||
DPRegLogicalOp::ORR => (0b01, 0),
|
LogicalOp::BIC => (0b00, true),
|
||||||
DPRegLogicalOp::ORN => (0b01, 1),
|
LogicalOp::ORR => (0b01, false),
|
||||||
DPRegLogicalOp::EOR => (0b10, 0),
|
LogicalOp::ORN => (0b01, true),
|
||||||
DPRegLogicalOp::EON => (0b10, 1),
|
LogicalOp::EOR => (0b10, false),
|
||||||
DPRegLogicalOp::ANDS => (0b11, 0),
|
LogicalOp::EON => (0b10, true),
|
||||||
DPRegLogicalOp::BICS => (0b11, 1),
|
LogicalOp::ANDS => (0b11, false),
|
||||||
|
LogicalOp::BICS => (0b11, true),
|
||||||
};
|
};
|
||||||
out |= (sf as u32) << 31;
|
|
||||||
out |= opc << 29;
|
Self {
|
||||||
out |= (shift as u32) << 22;
|
reg_d: rd.id().into(),
|
||||||
out |= n << 21;
|
reg_n: rn.id().into(),
|
||||||
out |= (reg_m as u32) << 16;
|
imm6: imm6.into(),
|
||||||
out |= (imm6 as u32) << 10;
|
reg_m: rm.id().into(),
|
||||||
out |= (reg_n as u32) << 5;
|
n,
|
||||||
out |= reg_d as u32;
|
shift: shift.id().into(),
|
||||||
}
|
fixed: 0b01010.into(),
|
||||||
DPRegGroup::AddSubShifted {
|
op: op.into(),
|
||||||
sf,
|
// true for 64 bit addition
|
||||||
subtract,
|
// false for 32 bit addition
|
||||||
set_flags,
|
sf: true,
|
||||||
shift,
|
|
||||||
reg_m,
|
|
||||||
imm6,
|
|
||||||
reg_n,
|
|
||||||
reg_d,
|
|
||||||
} => {
|
|
||||||
debug_assert!(shift <= 0b11);
|
|
||||||
debug_assert!(imm6 <= 0b111111);
|
|
||||||
out |= (sf as u32) << 31;
|
|
||||||
out |= (subtract as u32) << 30;
|
|
||||||
out |= (set_flags as u32) << 29;
|
|
||||||
out |= 0b1 << 24;
|
|
||||||
out |= (shift as u32) << 22;
|
|
||||||
out |= (reg_m as u32) << 16;
|
|
||||||
out |= (imm6 as u32) << 10;
|
|
||||||
out |= (reg_n as u32) << 5;
|
|
||||||
out |= reg_d as u32;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AArch64Instruction::LdStr(ldstr) => {
|
|
||||||
out |= 0b1 << 27;
|
#[derive(PackedStruct)]
|
||||||
match ldstr {
|
pub struct UnconditionalBranchRegister {
|
||||||
LdStrGroup::UnsignedImm {
|
fixed: Integer<u8, packed_bits::Bits<7>>,
|
||||||
size,
|
z: bool,
|
||||||
v,
|
fixed2: bool,
|
||||||
opc,
|
op: Integer<u8, packed_bits::Bits<2>>,
|
||||||
imm12,
|
fixed3: Integer<u8, packed_bits::Bits<5>>,
|
||||||
reg_n,
|
fixed4: Integer<u8, packed_bits::Bits<4>>,
|
||||||
reg_t,
|
a: bool,
|
||||||
} => {
|
m: bool,
|
||||||
|
rn: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
fixed5: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aarch64Bytes for UnconditionalBranchRegister {}
|
||||||
|
|
||||||
|
impl UnconditionalBranchRegister {
|
||||||
|
#[inline(always)]
|
||||||
|
fn new(op: u8, rn: AArch64GeneralReg) -> Self {
|
||||||
|
debug_assert!(op <= 0b11);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
fixed5: 0b00000.into(),
|
||||||
|
rn: rn.id().into(),
|
||||||
|
m: false,
|
||||||
|
a: false,
|
||||||
|
fixed4: 0b0000.into(),
|
||||||
|
fixed3: 0b11111.into(),
|
||||||
|
op: op.into(),
|
||||||
|
fixed2: false,
|
||||||
|
z: false,
|
||||||
|
fixed: 0b1101011.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses unsigned Offset
|
||||||
|
// opc = 0b01 means load
|
||||||
|
// opc = 0b00 means store
|
||||||
|
#[derive(PackedStruct, Debug)]
|
||||||
|
#[packed_struct(endian = "msb")]
|
||||||
|
pub struct LoadStoreRegisterImmediate {
|
||||||
|
size: Integer<u8, packed_bits::Bits<2>>,
|
||||||
|
fixed: Integer<u8, packed_bits::Bits<3>>, // = 0b111,
|
||||||
|
fixed2: bool,
|
||||||
|
fixed3: Integer<u8, packed_bits::Bits<2>>,
|
||||||
|
opc: Integer<u8, packed_bits::Bits<2>>,
|
||||||
|
imm12: Integer<u16, packed_bits::Bits<12>>,
|
||||||
|
rn: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
rt: Integer<u8, packed_bits::Bits<5>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Aarch64Bytes for LoadStoreRegisterImmediate {}
|
||||||
|
|
||||||
|
impl LoadStoreRegisterImmediate {
|
||||||
|
#[inline(always)]
|
||||||
|
fn new(size: u8, opc: u8, imm12: u16, rn: AArch64GeneralReg, rt: AArch64GeneralReg) -> Self {
|
||||||
debug_assert!(size <= 0b11);
|
debug_assert!(size <= 0b11);
|
||||||
debug_assert!(imm12 <= 0xFFF);
|
debug_assert!(imm12 <= 0xFFF);
|
||||||
out |= (size as u32) << 30;
|
|
||||||
out |= 0b11 << 28;
|
Self {
|
||||||
out |= (v as u32) << 26;
|
rt: rt.id().into(),
|
||||||
out |= 0b1 << 24;
|
rn: rn.id().into(),
|
||||||
out |= (opc as u32) << 22;
|
imm12: imm12.into(),
|
||||||
out |= (imm12 as u32) << 10;
|
opc: opc.into(),
|
||||||
out |= (reg_n as u32) << 5;
|
fixed3: 0b01.into(),
|
||||||
out |= reg_t as u32;
|
fixed2: false,
|
||||||
|
fixed: 0b111.into(),
|
||||||
|
size: size.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn new_load(size: u8, imm12: u16, rn: AArch64GeneralReg, rt: AArch64GeneralReg) -> Self {
|
||||||
|
Self::new(size, 0b01, imm12, rn, rt)
|
||||||
}
|
}
|
||||||
x => unimplemented!("The instruction, {:?}, has not be implemented yet", x),
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn new_store(size: u8, imm12: u16, rn: AArch64GeneralReg, rt: AArch64GeneralReg) -> Self {
|
||||||
|
Self::new(size, 0b00, imm12, rn, rt)
|
||||||
}
|
}
|
||||||
out.to_le_bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Below here are the functions for all of the assembly instructions.
|
// Below here are the functions for all of the assembly instructions.
|
||||||
|
@ -777,17 +848,9 @@ fn add_reg64_reg64_imm12(
|
||||||
src: AArch64GeneralReg,
|
src: AArch64GeneralReg,
|
||||||
imm12: u16,
|
imm12: u16,
|
||||||
) {
|
) {
|
||||||
buf.extend(&build_instruction(AArch64Instruction::DPImm(
|
let inst = ArithmeticImmediate::new(false, false, dst, src, imm12, false);
|
||||||
DPImmGroup::AddSubImm {
|
|
||||||
sf: true,
|
buf.extend(inst.bytes());
|
||||||
subtract: false,
|
|
||||||
set_flags: false,
|
|
||||||
shift: false,
|
|
||||||
imm12,
|
|
||||||
reg_n: src,
|
|
||||||
reg_d: dst,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `ADD Xd, Xm, Xn` -> Add Xm and Xn and place the result into Xd.
|
/// `ADD Xd, Xm, Xn` -> Add Xm and Xn and place the result into Xd.
|
||||||
|
@ -798,18 +861,9 @@ fn add_reg64_reg64_reg64(
|
||||||
src1: AArch64GeneralReg,
|
src1: AArch64GeneralReg,
|
||||||
src2: AArch64GeneralReg,
|
src2: AArch64GeneralReg,
|
||||||
) {
|
) {
|
||||||
buf.extend(&build_instruction(AArch64Instruction::DPReg(
|
let inst = ArithmeticShifted::new(false, false, ShiftType::LSL, 0, src1, src2, dst);
|
||||||
DPRegGroup::AddSubShifted {
|
|
||||||
sf: true,
|
buf.extend(inst.bytes());
|
||||||
subtract: false,
|
|
||||||
set_flags: false,
|
|
||||||
shift: 0,
|
|
||||||
reg_m: src1,
|
|
||||||
imm6: 0,
|
|
||||||
reg_n: src2,
|
|
||||||
reg_d: dst,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `LDR Xt, [Xn, #offset]` -> Load Xn + Offset Xt. ZRSP is SP.
|
/// `LDR Xt, [Xn, #offset]` -> Load Xn + Offset Xt. ZRSP is SP.
|
||||||
|
@ -821,66 +875,41 @@ fn ldr_reg64_imm12(
|
||||||
base: AArch64GeneralReg,
|
base: AArch64GeneralReg,
|
||||||
imm12: u16,
|
imm12: u16,
|
||||||
) {
|
) {
|
||||||
debug_assert!(imm12 <= 0xFFF);
|
let inst = LoadStoreRegisterImmediate::new_load(0b11, imm12, base, dst);
|
||||||
buf.extend(&build_instruction(AArch64Instruction::LdStr(
|
|
||||||
LdStrGroup::UnsignedImm {
|
buf.extend(inst.bytes());
|
||||||
size: 0b11,
|
|
||||||
v: false,
|
|
||||||
opc: 0b01,
|
|
||||||
imm12,
|
|
||||||
reg_n: base,
|
|
||||||
reg_t: dst,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MOV Xd, Xm` -> Move Xm to Xd.
|
/// `MOV Xd, Xm` -> Move Xm to Xd.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mov_reg64_reg64(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, src: AArch64GeneralReg) {
|
fn mov_reg64_reg64(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, src: AArch64GeneralReg) {
|
||||||
// MOV is equvalent to `ORR Xd, XZR, XM` in AARCH64.
|
// MOV is equvalent to `ORR Xd, XZR, XM` in AARCH64.
|
||||||
buf.extend(&build_instruction(AArch64Instruction::DPReg(
|
let inst = LogicalShiftedRegister::new(
|
||||||
DPRegGroup::Logical {
|
LogicalOp::ORR,
|
||||||
sf: true,
|
ShiftType::LSL,
|
||||||
op: DPRegLogicalOp::ORR,
|
0,
|
||||||
shift: 0,
|
src,
|
||||||
reg_m: src,
|
AArch64GeneralReg::ZRSP,
|
||||||
imm6: 0,
|
dst,
|
||||||
reg_n: AArch64GeneralReg::ZRSP,
|
);
|
||||||
reg_d: dst,
|
|
||||||
},
|
buf.extend(inst.bytes());
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MOVK Xd, imm16` -> Keeps Xd and moves an optionally shifted imm16 to Xd.
|
/// `MOVK Xd, imm16` -> Keeps Xd and moves an optionally shifted imm16 to Xd.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn movk_reg64_imm16(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, imm16: u16, hw: u8) {
|
fn movk_reg64_imm16(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, imm16: u16, hw: u8) {
|
||||||
debug_assert!(hw <= 0b11);
|
let inst = MoveWideImmediate::new(0b11, dst, imm16, hw, true);
|
||||||
// MOV is equvalent to `ORR Xd, XZR, XM` in AARCH64.
|
|
||||||
buf.extend(&build_instruction(AArch64Instruction::DPImm(
|
buf.extend(inst.bytes());
|
||||||
DPImmGroup::MoveWide {
|
|
||||||
sf: true,
|
|
||||||
opc: 0b11,
|
|
||||||
hw,
|
|
||||||
imm16,
|
|
||||||
reg_d: dst,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MOVZ Xd, imm16` -> Zeros Xd and moves an optionally shifted imm16 to Xd.
|
/// `MOVZ Xd, imm16` -> Zeros Xd and moves an optionally shifted imm16 to Xd.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn movz_reg64_imm16(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, imm16: u16, hw: u8) {
|
fn movz_reg64_imm16(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, imm16: u16, hw: u8) {
|
||||||
debug_assert!(hw <= 0b11);
|
let inst = MoveWideImmediate::new(0b10, dst, imm16, hw, true);
|
||||||
// MOV is equvalent to `ORR Xd, XZR, XM` in AARCH64.
|
|
||||||
buf.extend(&build_instruction(AArch64Instruction::DPImm(
|
buf.extend(inst.bytes());
|
||||||
DPImmGroup::MoveWide {
|
|
||||||
sf: true,
|
|
||||||
opc: 0b10,
|
|
||||||
hw,
|
|
||||||
imm16,
|
|
||||||
reg_d: dst,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `STR Xt, [Xn, #offset]` -> Store Xt to Xn + Offset. ZRSP is SP.
|
/// `STR Xt, [Xn, #offset]` -> Store Xt to Xn + Offset. ZRSP is SP.
|
||||||
|
@ -892,17 +921,9 @@ fn str_reg64_imm12(
|
||||||
base: AArch64GeneralReg,
|
base: AArch64GeneralReg,
|
||||||
imm12: u16,
|
imm12: u16,
|
||||||
) {
|
) {
|
||||||
debug_assert!(imm12 <= 0xFFF);
|
let inst = LoadStoreRegisterImmediate::new_store(0b11, imm12, base, src);
|
||||||
buf.extend(&build_instruction(AArch64Instruction::LdStr(
|
|
||||||
LdStrGroup::UnsignedImm {
|
buf.extend(inst.bytes());
|
||||||
size: 0b11,
|
|
||||||
v: false,
|
|
||||||
opc: 0b00,
|
|
||||||
imm12,
|
|
||||||
reg_n: base,
|
|
||||||
reg_t: src,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `SUB Xd, Xn, imm12` -> Subtract Xn and imm12 and place the result into Xd.
|
/// `SUB Xd, Xn, imm12` -> Subtract Xn and imm12 and place the result into Xd.
|
||||||
|
@ -913,31 +934,17 @@ fn sub_reg64_reg64_imm12(
|
||||||
src: AArch64GeneralReg,
|
src: AArch64GeneralReg,
|
||||||
imm12: u16,
|
imm12: u16,
|
||||||
) {
|
) {
|
||||||
buf.extend(&build_instruction(AArch64Instruction::DPImm(
|
let inst = ArithmeticImmediate::new(true, false, dst, src, imm12, false);
|
||||||
DPImmGroup::AddSubImm {
|
|
||||||
sf: true,
|
buf.extend(inst.bytes());
|
||||||
subtract: true,
|
|
||||||
set_flags: false,
|
|
||||||
shift: false,
|
|
||||||
imm12,
|
|
||||||
reg_n: src,
|
|
||||||
reg_d: dst,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `RET Xn` -> Return to the address stored in Xn.
|
/// `RET Xn` -> Return to the address stored in Xn.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn ret_reg64(buf: &mut Vec<'_, u8>, xn: AArch64GeneralReg) {
|
fn ret_reg64(buf: &mut Vec<'_, u8>, xn: AArch64GeneralReg) {
|
||||||
buf.extend(&build_instruction(AArch64Instruction::Branch(
|
let inst = UnconditionalBranchRegister::new(0b10, xn);
|
||||||
BranchGroup::UnconditionBranchReg {
|
|
||||||
opc: 0b0010,
|
buf.extend(inst.bytes());
|
||||||
op2: 0b11111,
|
|
||||||
op3: 0b000000,
|
|
||||||
reg_n: xn,
|
|
||||||
op4: 0b000,
|
|
||||||
},
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -82,6 +82,28 @@ pub fn build_module<'a>(
|
||||||
Object::new(BinaryFormat::Elf, Architecture::Aarch64, Endianness::Little),
|
Object::new(BinaryFormat::Elf, Architecture::Aarch64, Endianness::Little),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Triple {
|
||||||
|
architecture: TargetArch::Aarch64(_),
|
||||||
|
binary_format: TargetBF::Macho,
|
||||||
|
..
|
||||||
|
} if cfg!(feature = "target-aarch64") => {
|
||||||
|
let backend: Backend64Bit<
|
||||||
|
aarch64::AArch64GeneralReg,
|
||||||
|
aarch64::AArch64FloatReg,
|
||||||
|
aarch64::AArch64Assembler,
|
||||||
|
aarch64::AArch64Call,
|
||||||
|
> = Backend::new(env)?;
|
||||||
|
build_object(
|
||||||
|
env,
|
||||||
|
procedures,
|
||||||
|
backend,
|
||||||
|
Object::new(
|
||||||
|
BinaryFormat::MachO,
|
||||||
|
Architecture::Aarch64,
|
||||||
|
Endianness::Little,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
x => Err(format! {
|
x => Err(format! {
|
||||||
"the target, {:?}, is not yet implemented",
|
"the target, {:?}, is not yet implemented",
|
||||||
x}),
|
x}),
|
||||||
|
|
|
@ -7,7 +7,7 @@ use roc_module::symbol::Symbol;
|
||||||
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
use roc_mono::ir::{CallType, Expr, JoinPointId, Literal, Proc, Stmt};
|
||||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||||
|
|
||||||
use crate::layout::WasmLayout;
|
use crate::layout::{StackMemoryFormat, WasmLayout};
|
||||||
use crate::low_level::{decode_low_level, LowlevelBuildResult};
|
use crate::low_level::{decode_low_level, LowlevelBuildResult};
|
||||||
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
use crate::storage::{Storage, StoredValue, StoredValueKind};
|
||||||
use crate::wasm_module::linking::{
|
use crate::wasm_module::linking::{
|
||||||
|
@ -237,7 +237,9 @@ impl<'a> WasmBackend<'a> {
|
||||||
|
|
||||||
fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &Layout<'a>) -> Result<(), String> {
|
fn build_stmt(&mut self, stmt: &Stmt<'a>, ret_layout: &Layout<'a>) -> Result<(), String> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Let(sym, expr, layout, following) => {
|
Stmt::Let(_, _, _, _) => {
|
||||||
|
let mut current_stmt = stmt;
|
||||||
|
while let Stmt::Let(sym, expr, layout, following) = current_stmt {
|
||||||
let wasm_layout = WasmLayout::new(layout);
|
let wasm_layout = WasmLayout::new(layout);
|
||||||
|
|
||||||
let kind = match following {
|
let kind = match following {
|
||||||
|
@ -263,7 +265,10 @@ impl<'a> WasmBackend<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.build_stmt(following, ret_layout)?;
|
current_stmt = *following;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.build_stmt(current_stmt, ret_layout)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -803,15 +808,35 @@ impl<'a> WasmBackend<'a> {
|
||||||
Some(ret_layout.value_type())
|
Some(ret_layout.value_type())
|
||||||
};
|
};
|
||||||
|
|
||||||
// Zig's "fast calling convention" packs structs into CPU registers (stack machine slots) if possible.
|
|
||||||
// If they're small enough they can go into an I32 or I64. If they're big, they're pointers (I32).
|
|
||||||
for arg in arguments {
|
for arg in arguments {
|
||||||
param_types.push(match self.storage.get(arg) {
|
match self.storage.get(arg) {
|
||||||
StoredValue::StackMemory { size, .. } if *size > 4 && *size <= 8 => {
|
StoredValue::StackMemory { size, format, .. } => {
|
||||||
ValueType::I64
|
use StackMemoryFormat::*;
|
||||||
|
|
||||||
|
match format {
|
||||||
|
Aggregate => {
|
||||||
|
// Zig's "fast calling convention" packs structs into CPU registers
|
||||||
|
// (stack machine slots) if possible. If they're small enough they
|
||||||
|
// can go into an I32 or I64. If they're big, they're pointers (I32).
|
||||||
|
if *size > 4 && *size <= 8 {
|
||||||
|
param_types.push(ValueType::I64)
|
||||||
|
} else {
|
||||||
|
// either
|
||||||
|
//
|
||||||
|
// - this is a small value, that fits in an i32
|
||||||
|
// - this is a big value, we pass a memory address
|
||||||
|
param_types.push(ValueType::I32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Int128 | Float128 | Decimal => {
|
||||||
|
// these types are passed as 2 i64s
|
||||||
|
param_types.push(ValueType::I64);
|
||||||
|
param_types.push(ValueType::I64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stored => param_types.push(stored.value_type()),
|
||||||
}
|
}
|
||||||
stored => stored.value_type(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let signature_index = self.module.types.insert(Signature {
|
let signature_index = self.module.types.insert(Signature {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use roc_builtins::bitcode::{self, FloatWidth};
|
||||||
use roc_module::low_level::{LowLevel, LowLevel::*};
|
use roc_module::low_level::{LowLevel, LowLevel::*};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
|
||||||
use crate::layout::WasmLayout;
|
use crate::layout::{StackMemoryFormat, WasmLayout};
|
||||||
use crate::storage::Storage;
|
use crate::storage::Storage;
|
||||||
use crate::wasm_module::{
|
use crate::wasm_module::{
|
||||||
CodeBuilder,
|
CodeBuilder,
|
||||||
|
@ -63,12 +63,21 @@ pub fn decode_low_level<'a>(
|
||||||
return NotImplemented;
|
return NotImplemented;
|
||||||
}
|
}
|
||||||
|
|
||||||
NumAdd => match ret_layout.value_type() {
|
NumAdd => match ret_layout {
|
||||||
|
WasmLayout::Primitive(value_type, _) => match value_type {
|
||||||
I32 => code_builder.i32_add(),
|
I32 => code_builder.i32_add(),
|
||||||
I64 => code_builder.i64_add(),
|
I64 => code_builder.i64_add(),
|
||||||
F32 => code_builder.f32_add(),
|
F32 => code_builder.f32_add(),
|
||||||
F64 => code_builder.f64_add(),
|
F64 => code_builder.f64_add(),
|
||||||
},
|
},
|
||||||
|
WasmLayout::StackMemory { format, .. } => match format {
|
||||||
|
StackMemoryFormat::Aggregate => return NotImplemented,
|
||||||
|
StackMemoryFormat::Int128 => return NotImplemented,
|
||||||
|
StackMemoryFormat::Float128 => return NotImplemented,
|
||||||
|
StackMemoryFormat::Decimal => return BuiltinCall(bitcode::DEC_ADD_WITH_OVERFLOW),
|
||||||
|
},
|
||||||
|
WasmLayout::HeapMemory { .. } => return NotImplemented,
|
||||||
|
},
|
||||||
NumAddWrap => match ret_layout.value_type() {
|
NumAddWrap => match ret_layout.value_type() {
|
||||||
I32 => {
|
I32 => {
|
||||||
code_builder.i32_add();
|
code_builder.i32_add();
|
||||||
|
|
|
@ -247,21 +247,51 @@ impl<'a> Storage<'a> {
|
||||||
} => {
|
} => {
|
||||||
let (local_id, offset) = location.local_and_offset(self.stack_frame_pointer);
|
let (local_id, offset) = location.local_and_offset(self.stack_frame_pointer);
|
||||||
|
|
||||||
// Load the address of the value
|
code_builder.get_local(local_id);
|
||||||
|
|
||||||
|
if format == StackMemoryFormat::Aggregate {
|
||||||
|
if offset != 0 {
|
||||||
|
code_builder.i32_const(offset as i32);
|
||||||
|
code_builder.i32_add();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// It's one of the 128-bit numbers, all of which we load as two i64's
|
||||||
|
// (Mark the same Symbol twice. Shouldn't matter except for debugging.)
|
||||||
|
code_builder.i64_load(Align::Bytes8, offset);
|
||||||
|
code_builder.set_top_symbol(sym);
|
||||||
|
|
||||||
|
code_builder.get_local(local_id);
|
||||||
|
code_builder.i64_load(Align::Bytes8, offset + 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
code_builder.set_top_symbol(sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// stack memory values are returned by pointer. e.g. a roc function
|
||||||
|
///
|
||||||
|
/// add : I128, I128 -> I128
|
||||||
|
///
|
||||||
|
/// is given the wasm type
|
||||||
|
///
|
||||||
|
/// add : (i32, i64, i64, i64, i64) -> nil
|
||||||
|
///
|
||||||
|
/// The returned value is written to the address passed as the first argument
|
||||||
|
fn load_return_address_ccc(&mut self, code_builder: &mut CodeBuilder, sym: Symbol) {
|
||||||
|
let storage = self.get(&sym).to_owned();
|
||||||
|
match storage {
|
||||||
|
StoredValue::VirtualMachineStack { .. } | StoredValue::Local { .. } => {
|
||||||
|
unreachable!("these storage types are not returned by writing to a pointer")
|
||||||
|
}
|
||||||
|
StoredValue::StackMemory { location, .. } => {
|
||||||
|
let (local_id, offset) = location.local_and_offset(self.stack_frame_pointer);
|
||||||
|
|
||||||
code_builder.get_local(local_id);
|
code_builder.get_local(local_id);
|
||||||
if offset != 0 {
|
if offset != 0 {
|
||||||
code_builder.i32_const(offset as i32);
|
code_builder.i32_const(offset as i32);
|
||||||
code_builder.i32_add();
|
code_builder.i32_add();
|
||||||
}
|
}
|
||||||
|
|
||||||
if format != StackMemoryFormat::Aggregate {
|
|
||||||
// It's one of the 128-bit numbers, all of which we load as two i64's
|
|
||||||
// Mark the same Symbol twice in the VM value stack! Shouldn't matter except debug.
|
|
||||||
code_builder.i64_load(Align::Bytes8, offset);
|
|
||||||
code_builder.set_top_symbol(sym);
|
|
||||||
code_builder.i64_load(Align::Bytes8, offset + 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
code_builder.set_top_symbol(sym);
|
code_builder.set_top_symbol(sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,9 +329,8 @@ impl<'a> Storage<'a> {
|
||||||
|
|
||||||
if return_layout.is_stack_memory() {
|
if return_layout.is_stack_memory() {
|
||||||
// Load the address where the return value should be written
|
// Load the address where the return value should be written
|
||||||
// Apparently for return values we still use a pointer to stack memory
|
self.load_return_address_ccc(code_builder, return_symbol);
|
||||||
self.load_symbol_ccc(code_builder, return_symbol);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
for sym in symbols {
|
for sym in symbols {
|
||||||
if let StoredValue::StackMemory {
|
if let StoredValue::StackMemory {
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl std::ops::Deref for ModuleName {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An uncapitalized identifier, such as a field name or local variable
|
/// An uncapitalized identifier, such as a field name or local variable
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct Lowercase(IdentStr);
|
pub struct Lowercase(IdentStr);
|
||||||
|
|
||||||
/// A capitalized identifier, such as a tag name or module name
|
/// A capitalized identifier, such as a tag name or module name
|
||||||
|
|
|
@ -100,15 +100,17 @@ impl Pools {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, rank: Rank) -> &mut Vec<Variable> {
|
pub fn get_mut(&mut self, rank: Rank) -> &mut Vec<Variable> {
|
||||||
self.0
|
match self.0.get_mut(rank.into_usize()) {
|
||||||
.get_mut(rank.into_usize())
|
Some(reference) => reference,
|
||||||
.unwrap_or_else(|| panic!("Compiler bug: could not find pool at rank {}", rank))
|
None => panic!("Compiler bug: could not find pool at rank {}", rank),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, rank: Rank) -> &Vec<Variable> {
|
pub fn get(&self, rank: Rank) -> &Vec<Variable> {
|
||||||
self.0
|
match self.0.get(rank.into_usize()) {
|
||||||
.get(rank.into_usize())
|
Some(reference) => reference,
|
||||||
.unwrap_or_else(|| panic!("Compiler bug: could not find pool at rank {}", rank))
|
None => panic!("Compiler bug: could not find pool at rank {}", rank),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> std::slice::Iter<'_, Vec<Variable>> {
|
pub fn iter(&self) -> std::slice::Iter<'_, Vec<Variable>> {
|
||||||
|
@ -628,18 +630,6 @@ fn type_to_var(
|
||||||
type_to_variable(subs, rank, pools, &arena, typ)
|
type_to_variable(subs, rank, pools, &arena, typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Abusing existing functions for our purposes
|
|
||||||
/// this is to put a solved type back into subs
|
|
||||||
pub fn insert_type_into_subs(subs: &mut Subs, typ: &Type) -> Variable {
|
|
||||||
let rank = Rank::NONE;
|
|
||||||
let mut pools = Pools::default();
|
|
||||||
|
|
||||||
// capacity based on the false hello world program
|
|
||||||
let arena = bumpalo::Bump::with_capacity(4 * 1024);
|
|
||||||
|
|
||||||
type_to_variable(subs, rank, &mut pools, &arena, typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn type_to_variable<'a>(
|
fn type_to_variable<'a>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
rank: Rank,
|
rank: Rank,
|
||||||
|
@ -652,14 +642,13 @@ fn type_to_variable<'a>(
|
||||||
match typ {
|
match typ {
|
||||||
Variable(var) => *var,
|
Variable(var) => *var,
|
||||||
Apply(symbol, args) => {
|
Apply(symbol, args) => {
|
||||||
let mut new_arg_vars = Vec::with_capacity_in(args.len(), arena);
|
let arg_vars = VariableSubsSlice::reserve_into_subs(subs, args.len());
|
||||||
|
|
||||||
for arg in args {
|
for (i, arg) in (arg_vars.slice.start as usize..).zip(args) {
|
||||||
let var = type_to_variable(subs, rank, pools, arena, arg);
|
let var = type_to_variable(subs, rank, pools, arena, arg);
|
||||||
new_arg_vars.push(var);
|
subs.variables[i] = var;
|
||||||
}
|
}
|
||||||
|
|
||||||
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
|
||||||
let flat_type = FlatType::Apply(*symbol, arg_vars);
|
let flat_type = FlatType::Apply(*symbol, arg_vars);
|
||||||
let content = Content::Structure(flat_type);
|
let content = Content::Structure(flat_type);
|
||||||
|
|
||||||
|
@ -669,16 +658,14 @@ fn type_to_variable<'a>(
|
||||||
EmptyTagUnion => Variable::EMPTY_TAG_UNION,
|
EmptyTagUnion => Variable::EMPTY_TAG_UNION,
|
||||||
|
|
||||||
// This case is important for the rank of boolean variables
|
// This case is important for the rank of boolean variables
|
||||||
Function(arg_vars, closure_type, ret_type) => {
|
Function(args, closure_type, ret_type) => {
|
||||||
let mut new_arg_vars = Vec::with_capacity_in(arg_vars.len(), arena);
|
let arg_vars = VariableSubsSlice::reserve_into_subs(subs, args.len());
|
||||||
|
|
||||||
for arg in arg_vars {
|
for (i, arg) in (arg_vars.slice.start as usize..).zip(args) {
|
||||||
let var = type_to_variable(subs, rank, pools, arena, arg);
|
let var = type_to_variable(subs, rank, pools, arena, arg);
|
||||||
new_arg_vars.push(var);
|
subs.variables[i] = var;
|
||||||
}
|
}
|
||||||
|
|
||||||
let arg_vars = VariableSubsSlice::insert_into_subs(subs, new_arg_vars);
|
|
||||||
|
|
||||||
let ret_var = type_to_variable(subs, rank, pools, arena, ret_type);
|
let ret_var = type_to_variable(subs, rank, pools, arena, ret_type);
|
||||||
let closure_var = type_to_variable(subs, rank, pools, arena, closure_type);
|
let closure_var = type_to_variable(subs, rank, pools, arena, closure_type);
|
||||||
let content = Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var));
|
let content = Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var));
|
||||||
|
@ -801,10 +788,10 @@ fn type_to_variable<'a>(
|
||||||
|
|
||||||
let mut arg_vars = Vec::with_capacity_in(args.len(), arena);
|
let mut arg_vars = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for (arg, arg_type) in args {
|
for (_, arg_type) in args {
|
||||||
let arg_var = type_to_variable(subs, rank, pools, arena, arg_type);
|
let arg_var = type_to_variable(subs, rank, pools, arena, arg_type);
|
||||||
|
|
||||||
arg_vars.push((arg.clone(), arg_var));
|
arg_vars.push(arg_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lambda_set_variables_it = lambda_set_variables
|
let lambda_set_variables_it = lambda_set_variables
|
||||||
|
@ -829,10 +816,10 @@ fn type_to_variable<'a>(
|
||||||
} => {
|
} => {
|
||||||
let mut arg_vars = Vec::with_capacity_in(args.len(), arena);
|
let mut arg_vars = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for (arg, arg_type) in args {
|
for (_, arg_type) in args {
|
||||||
let arg_var = type_to_variable(subs, rank, pools, arena, arg_type);
|
let arg_var = type_to_variable(subs, rank, pools, arena, arg_type);
|
||||||
|
|
||||||
arg_vars.push((arg.clone(), arg_var));
|
arg_vars.push(arg_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
let lambda_set_variables_it = lambda_set_variables
|
let lambda_set_variables_it = lambda_set_variables
|
||||||
|
@ -884,14 +871,13 @@ fn type_to_union_tags<'a>(
|
||||||
|
|
||||||
let mut tag_vars = Vec::with_capacity_in(tags.len(), arena);
|
let mut tag_vars = Vec::with_capacity_in(tags.len(), arena);
|
||||||
|
|
||||||
let mut tag_argument_vars = Vec::with_capacity_in(tags.len(), arena);
|
|
||||||
for (tag, tag_argument_types) in tags {
|
for (tag, tag_argument_types) in tags {
|
||||||
for arg_type in tag_argument_types {
|
let new_slice = VariableSubsSlice::reserve_into_subs(subs, tag_argument_types.len());
|
||||||
let new_var = type_to_variable(subs, rank, pools, arena, arg_type);
|
|
||||||
tag_argument_vars.push(new_var);
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_slice = VariableSubsSlice::insert_into_subs(subs, tag_argument_vars.drain(..));
|
for (i, arg) in (new_slice.slice.start as usize..).zip(tag_argument_types) {
|
||||||
|
let var = type_to_variable(subs, rank, pools, arena, arg);
|
||||||
|
subs.variables[i] = var;
|
||||||
|
}
|
||||||
|
|
||||||
tag_vars.push((tag.clone(), new_slice));
|
tag_vars.push((tag.clone(), new_slice));
|
||||||
}
|
}
|
||||||
|
@ -1654,12 +1640,14 @@ fn deep_copy_var_help(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn register(subs: &mut Subs, rank: Rank, pools: &mut Pools, content: Content) -> Variable {
|
fn register(subs: &mut Subs, rank: Rank, pools: &mut Pools, content: Content) -> Variable {
|
||||||
let var = subs.fresh(Descriptor {
|
let descriptor = Descriptor {
|
||||||
content,
|
content,
|
||||||
rank,
|
rank,
|
||||||
mark: Mark::NONE,
|
mark: Mark::NONE,
|
||||||
copy: OptVariable::NONE,
|
copy: OptVariable::NONE,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
let var = subs.fresh(descriptor);
|
||||||
|
|
||||||
pools.get_mut(rank).push(var);
|
pools.get_mut(rank).push(var);
|
||||||
|
|
||||||
|
|
|
@ -609,7 +609,7 @@ fn gen_float_eq() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
fn gen_add_dec() {
|
fn gen_add_dec() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
|
@ -199,7 +199,7 @@ fn find_names_needed(
|
||||||
}
|
}
|
||||||
Alias(_symbol, args, _actual) => {
|
Alias(_symbol, args, _actual) => {
|
||||||
// only find names for named parameters!
|
// only find names for named parameters!
|
||||||
for var_index in args.variables().into_iter().take(args.len()) {
|
for var_index in args.into_iter().take(args.len()) {
|
||||||
let var = subs[var_index];
|
let var = subs[var_index];
|
||||||
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
find_names_needed(var, subs, roots, root_appearances, names_taken);
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,6 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
||||||
debug_assert_eq!(args.len(), 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
let arg_var_index = args
|
let arg_var_index = args
|
||||||
.variables()
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next()
|
.next()
|
||||||
.expect("Num was not applied to a type argument!");
|
.expect("Num was not applied to a type argument!");
|
||||||
|
@ -337,7 +336,7 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
||||||
_ => write_parens!(write_parens, buf, {
|
_ => write_parens!(write_parens, buf, {
|
||||||
write_symbol(env, *symbol, buf);
|
write_symbol(env, *symbol, buf);
|
||||||
|
|
||||||
for var_index in args.variables() {
|
for var_index in args.into_iter() {
|
||||||
let var = subs[var_index];
|
let var = subs[var_index];
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
write_content(
|
write_content(
|
||||||
|
|
|
@ -229,7 +229,7 @@ impl SolvedType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_var(subs: &Subs, var: Variable) -> Self {
|
fn from_var(subs: &Subs, var: Variable) -> Self {
|
||||||
let mut seen = RecursionVars::default();
|
let mut seen = RecursionVars::default();
|
||||||
Self::from_var_help(subs, &mut seen, var)
|
Self::from_var_help(subs, &mut seen, var)
|
||||||
}
|
}
|
||||||
|
@ -254,16 +254,17 @@ impl SolvedType {
|
||||||
Alias(symbol, args, actual_var) => {
|
Alias(symbol, args, actual_var) => {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for (name_index, var_index) in args.named_type_arguments() {
|
for var_index in args.named_type_arguments() {
|
||||||
let arg_var = subs[var_index];
|
let arg_var = subs[var_index];
|
||||||
|
|
||||||
new_args.push((
|
let node = Self::from_var_help(subs, recursion_vars, arg_var);
|
||||||
subs[name_index].clone(),
|
|
||||||
Self::from_var_help(subs, recursion_vars, arg_var),
|
// NOTE we fake the lowercase here: the user will never get to see it anyway
|
||||||
));
|
new_args.push((Lowercase::default(), node));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut solved_lambda_sets = Vec::with_capacity(0);
|
let mut solved_lambda_sets = Vec::with_capacity(0);
|
||||||
|
|
||||||
for var_index in args.unnamed_type_arguments() {
|
for var_index in args.unnamed_type_arguments() {
|
||||||
let var = subs[var_index];
|
let var = subs[var_index];
|
||||||
|
|
||||||
|
|
|
@ -797,7 +797,7 @@ fn integer_type(
|
||||||
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
||||||
});
|
});
|
||||||
|
|
||||||
let vars = AliasVariables::insert_into_subs(subs, [("range".into(), signed64)], []);
|
let vars = AliasVariables::insert_into_subs(subs, [signed64], []);
|
||||||
subs.set_content(num_integer_signed64, {
|
subs.set_content(num_integer_signed64, {
|
||||||
Content::Alias(Symbol::NUM_INTEGER, vars, at_signed64)
|
Content::Alias(Symbol::NUM_INTEGER, vars, at_signed64)
|
||||||
});
|
});
|
||||||
|
@ -812,7 +812,7 @@ fn integer_type(
|
||||||
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
Content::Structure(FlatType::TagUnion(tags, Variable::EMPTY_TAG_UNION))
|
||||||
});
|
});
|
||||||
|
|
||||||
let vars = AliasVariables::insert_into_subs(subs, [("range".into(), integer_signed64)], []);
|
let vars = AliasVariables::insert_into_subs(subs, [integer_signed64], []);
|
||||||
subs.set_content(num_integer_signed64, {
|
subs.set_content(num_integer_signed64, {
|
||||||
Content::Alias(Symbol::NUM_NUM, vars, at_num_integer_signed64)
|
Content::Alias(Symbol::NUM_NUM, vars, at_num_integer_signed64)
|
||||||
});
|
});
|
||||||
|
@ -1358,7 +1358,7 @@ impl From<Content> for Descriptor {
|
||||||
static_assertions::assert_eq_size!([u8; 4 * 8], Content);
|
static_assertions::assert_eq_size!([u8; 4 * 8], Content);
|
||||||
static_assertions::assert_eq_size!([u8; 4 * 8], (Variable, Option<Lowercase>));
|
static_assertions::assert_eq_size!([u8; 4 * 8], (Variable, Option<Lowercase>));
|
||||||
static_assertions::assert_eq_size!([u8; 3 * 8], (Symbol, AliasVariables, Variable));
|
static_assertions::assert_eq_size!([u8; 3 * 8], (Symbol, AliasVariables, Variable));
|
||||||
static_assertions::assert_eq_size!([u8; 12], AliasVariables);
|
static_assertions::assert_eq_size!([u8; 8], AliasVariables);
|
||||||
static_assertions::assert_eq_size!([u8; 3 * 8], FlatType);
|
static_assertions::assert_eq_size!([u8; 3 * 8], FlatType);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -1382,29 +1382,26 @@ pub enum Content {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
pub struct AliasVariables {
|
pub struct AliasVariables {
|
||||||
pub lowercases_start: u32,
|
|
||||||
pub variables_start: u32,
|
pub variables_start: u32,
|
||||||
pub lowercases_len: u16,
|
pub all_variables_len: u16,
|
||||||
pub variables_len: u16,
|
|
||||||
|
/// an alias has type variables and lambda set variables
|
||||||
|
pub type_variables_len: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AliasVariables {
|
impl AliasVariables {
|
||||||
pub const fn names(&self) -> SubsSlice<Lowercase> {
|
|
||||||
SubsSlice::new(self.lowercases_start, self.lowercases_len)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn variables(&self) -> VariableSubsSlice {
|
pub const fn variables(&self) -> VariableSubsSlice {
|
||||||
VariableSubsSlice {
|
VariableSubsSlice {
|
||||||
slice: SubsSlice::new(self.variables_start, self.variables_len),
|
slice: SubsSlice::new(self.variables_start, self.all_variables_len),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn len(&self) -> usize {
|
pub const fn len(&self) -> usize {
|
||||||
self.lowercases_len as usize
|
self.type_variables_len as usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn is_empty(&self) -> bool {
|
pub const fn is_empty(&self) -> bool {
|
||||||
self.lowercases_len == 0
|
self.type_variables_len == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_variables(
|
pub fn replace_variables(
|
||||||
|
@ -1416,24 +1413,21 @@ impl AliasVariables {
|
||||||
subs.variables.extend(variables);
|
subs.variables.extend(variables);
|
||||||
let variables_len = (subs.variables.len() - variables_start as usize) as u16;
|
let variables_len = (subs.variables.len() - variables_start as usize) as u16;
|
||||||
|
|
||||||
debug_assert_eq!(variables_len, self.variables_len);
|
debug_assert_eq!(variables_len, self.all_variables_len);
|
||||||
|
|
||||||
self.variables_start = variables_start;
|
self.variables_start = variables_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn named_type_arguments(
|
pub fn named_type_arguments(&self) -> impl Iterator<Item = SubsIndex<Variable>> {
|
||||||
&self,
|
self.variables()
|
||||||
) -> impl Iterator<Item = (SubsIndex<Lowercase>, SubsIndex<Variable>)> {
|
.into_iter()
|
||||||
let names = self.names();
|
.take(self.type_variables_len as usize)
|
||||||
let vars = self.variables();
|
|
||||||
|
|
||||||
names.into_iter().zip(vars.into_iter())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unnamed_type_arguments(&self) -> impl Iterator<Item = SubsIndex<Variable>> {
|
pub fn unnamed_type_arguments(&self) -> impl Iterator<Item = SubsIndex<Variable>> {
|
||||||
self.variables()
|
self.variables()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.skip(self.lowercases_len as usize)
|
.skip(self.type_variables_len as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_into_subs<I1, I2>(
|
pub fn insert_into_subs<I1, I2>(
|
||||||
|
@ -1442,38 +1436,41 @@ impl AliasVariables {
|
||||||
unnamed_arguments: I2,
|
unnamed_arguments: I2,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
I1: IntoIterator<Item = (Lowercase, Variable)>,
|
I1: IntoIterator<Item = Variable>,
|
||||||
I2: IntoIterator<Item = Variable>,
|
I2: IntoIterator<Item = Variable>,
|
||||||
{
|
{
|
||||||
let lowercases_start = subs.field_names.len() as u32;
|
|
||||||
let variables_start = subs.variables.len() as u32;
|
let variables_start = subs.variables.len() as u32;
|
||||||
|
|
||||||
let it1 = type_arguments.into_iter();
|
subs.variables.extend(type_arguments);
|
||||||
let it2 = unnamed_arguments.into_iter();
|
|
||||||
|
|
||||||
subs.variables
|
let type_variables_len = (subs.variables.len() as u32 - variables_start) as u16;
|
||||||
.reserve(it1.size_hint().0 + it2.size_hint().0);
|
|
||||||
subs.field_names.reserve(it1.size_hint().0);
|
|
||||||
|
|
||||||
for (field_name, var) in it1 {
|
subs.variables.extend(unnamed_arguments);
|
||||||
subs.field_names.push(field_name);
|
|
||||||
subs.variables.push(var);
|
let all_variables_len = (subs.variables.len() as u32 - variables_start) as u16;
|
||||||
|
|
||||||
|
if type_variables_len == 3 {
|
||||||
|
panic!();
|
||||||
}
|
}
|
||||||
|
|
||||||
subs.variables.extend(it2);
|
|
||||||
|
|
||||||
let lowercases_len = (subs.field_names.len() as u32 - lowercases_start) as u16;
|
|
||||||
let variables_len = (subs.variables.len() as u32 - variables_start) as u16;
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
lowercases_start,
|
|
||||||
variables_start,
|
variables_start,
|
||||||
lowercases_len,
|
type_variables_len,
|
||||||
variables_len,
|
all_variables_len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for AliasVariables {
|
||||||
|
type Item = <VariableSubsSlice as IntoIterator>::Item;
|
||||||
|
|
||||||
|
type IntoIter = <VariableSubsSlice as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.variables().into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Content {
|
impl Content {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_number(&self) -> bool {
|
pub fn is_number(&self) -> bool {
|
||||||
|
@ -1548,6 +1545,15 @@ impl VariableSubsSlice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reserve_into_subs(subs: &mut Subs, length: usize) -> Self {
|
||||||
|
let start = subs.variables.len() as u32;
|
||||||
|
|
||||||
|
subs.variables
|
||||||
|
.extend(std::iter::repeat(Variable::NULL).take(length));
|
||||||
|
|
||||||
|
Self::new(start, length as u16)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_into_subs<I>(subs: &mut Subs, input: I) -> Self
|
pub fn insert_into_subs<I>(subs: &mut Subs, input: I) -> Self
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = Variable>,
|
I: IntoIterator<Item = Variable>,
|
||||||
|
@ -2081,7 +2087,7 @@ fn occurs(
|
||||||
let mut new_seen = seen.clone();
|
let mut new_seen = seen.clone();
|
||||||
new_seen.insert(root_var);
|
new_seen.insert(root_var);
|
||||||
|
|
||||||
for var_index in args.variables().into_iter() {
|
for var_index in args.into_iter() {
|
||||||
let var = subs[var_index];
|
let var = subs[var_index];
|
||||||
short_circuit_help(subs, root_var, &new_seen, var)?;
|
short_circuit_help(subs, root_var, &new_seen, var)?;
|
||||||
}
|
}
|
||||||
|
@ -2265,7 +2271,7 @@ fn explicit_substitute(
|
||||||
in_var
|
in_var
|
||||||
}
|
}
|
||||||
Alias(symbol, args, actual) => {
|
Alias(symbol, args, actual) => {
|
||||||
for index in args.variables().into_iter() {
|
for index in args.into_iter() {
|
||||||
let var = subs[index];
|
let var = subs[index];
|
||||||
let new_var = explicit_substitute(subs, from, to, var, seen);
|
let new_var = explicit_substitute(subs, from, to, var, seen);
|
||||||
subs[index] = new_var;
|
subs[index] = new_var;
|
||||||
|
@ -2322,10 +2328,7 @@ fn get_var_names(
|
||||||
|
|
||||||
RigidVar(name) => add_name(subs, 0, name, var, RigidVar, taken_names),
|
RigidVar(name) => add_name(subs, 0, name, var, RigidVar, taken_names),
|
||||||
|
|
||||||
Alias(_, args, _) => args
|
Alias(_, args, _) => args.into_iter().fold(taken_names, |answer, arg_var| {
|
||||||
.variables()
|
|
||||||
.into_iter()
|
|
||||||
.fold(taken_names, |answer, arg_var| {
|
|
||||||
get_var_names(subs, subs[arg_var], answer)
|
get_var_names(subs, subs[arg_var], answer)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -2528,15 +2531,14 @@ fn content_to_err_type(
|
||||||
Alias(symbol, args, aliased_to) => {
|
Alias(symbol, args, aliased_to) => {
|
||||||
let err_type = var_to_err_type(subs, state, aliased_to);
|
let err_type = var_to_err_type(subs, state, aliased_to);
|
||||||
|
|
||||||
let mut err_args = Vec::with_capacity(args.names().len());
|
let mut err_args = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
for (name_index, var_index) in args.named_type_arguments() {
|
for var_index in args.into_iter() {
|
||||||
let name = subs[name_index].clone();
|
|
||||||
let var = subs[var_index];
|
let var = subs[var_index];
|
||||||
|
|
||||||
let arg = var_to_err_type(subs, state, var);
|
let arg = var_to_err_type(subs, state, var);
|
||||||
|
|
||||||
err_args.push((name, arg));
|
err_args.push(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorType::Alias(symbol, err_args, Box::new(err_type))
|
ErrorType::Alias(symbol, err_args, Box::new(err_type))
|
||||||
|
@ -2989,7 +2991,6 @@ impl StorageSubs {
|
||||||
offsets: &StorageSubsOffsets,
|
offsets: &StorageSubsOffsets,
|
||||||
mut alias_variables: AliasVariables,
|
mut alias_variables: AliasVariables,
|
||||||
) -> AliasVariables {
|
) -> AliasVariables {
|
||||||
alias_variables.lowercases_start += offsets.field_names;
|
|
||||||
alias_variables.variables_start += offsets.variables;
|
alias_variables.variables_start += offsets.variables;
|
||||||
|
|
||||||
alias_variables
|
alias_variables
|
||||||
|
@ -3357,9 +3358,9 @@ fn deep_copy_var_to_help<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
Alias(symbol, mut args, real_type_var) => {
|
Alias(symbol, mut args, real_type_var) => {
|
||||||
let mut new_vars = Vec::with_capacity_in(args.variables().len(), arena);
|
let mut new_vars = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
for var_index in args.variables() {
|
for var_index in args.into_iter() {
|
||||||
let var = source[var_index];
|
let var = source[var_index];
|
||||||
let new_var = deep_copy_var_to_help(arena, visited, source, target, max_rank, var);
|
let new_var = deep_copy_var_to_help(arena, visited, source, target, max_rank, var);
|
||||||
|
|
||||||
|
@ -3368,12 +3369,6 @@ fn deep_copy_var_to_help<'a>(
|
||||||
|
|
||||||
args.replace_variables(target, new_vars);
|
args.replace_variables(target, new_vars);
|
||||||
|
|
||||||
let lowercases = &source.field_names[args.lowercases_start as usize..]
|
|
||||||
[..args.lowercases_len as usize];
|
|
||||||
|
|
||||||
args.lowercases_start = target.field_names.len() as u32;
|
|
||||||
target.field_names.extend(lowercases.iter().cloned());
|
|
||||||
|
|
||||||
let new_real_type_var =
|
let new_real_type_var =
|
||||||
deep_copy_var_to_help(arena, visited, source, target, max_rank, real_type_var);
|
deep_copy_var_to_help(arena, visited, source, target, max_rank, real_type_var);
|
||||||
let new_content = Alias(symbol, args, new_real_type_var);
|
let new_content = Alias(symbol, args, new_real_type_var);
|
||||||
|
|
|
@ -1241,7 +1241,7 @@ pub enum ErrorType {
|
||||||
TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
TagUnion(SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||||
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
RecursiveTagUnion(Box<ErrorType>, SendMap<TagName, Vec<ErrorType>>, TypeExt),
|
||||||
Function(Vec<ErrorType>, Box<ErrorType>, Box<ErrorType>),
|
Function(Vec<ErrorType>, Box<ErrorType>, Box<ErrorType>),
|
||||||
Alias(Symbol, Vec<(Lowercase, ErrorType)>, Box<ErrorType>),
|
Alias(Symbol, Vec<ErrorType>, Box<ErrorType>),
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1303,7 +1303,7 @@ fn write_error_type_help(
|
||||||
Alias(Symbol::NUM_NUM, mut arguments, _actual) => {
|
Alias(Symbol::NUM_NUM, mut arguments, _actual) => {
|
||||||
debug_assert!(arguments.len() == 1);
|
debug_assert!(arguments.len() == 1);
|
||||||
|
|
||||||
let argument = arguments.remove(0).1;
|
let argument = arguments.remove(0);
|
||||||
|
|
||||||
match argument {
|
match argument {
|
||||||
Type(Symbol::NUM_INTEGER, _) => {
|
Type(Symbol::NUM_INTEGER, _) => {
|
||||||
|
@ -1421,7 +1421,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
||||||
Alias(Symbol::NUM_NUM, mut arguments, _actual) => {
|
Alias(Symbol::NUM_NUM, mut arguments, _actual) => {
|
||||||
debug_assert!(arguments.len() == 1);
|
debug_assert!(arguments.len() == 1);
|
||||||
|
|
||||||
let argument = arguments.remove(0).1;
|
let argument = arguments.remove(0);
|
||||||
|
|
||||||
match argument {
|
match argument {
|
||||||
Type(Symbol::NUM_INTEGER, _) => {
|
Type(Symbol::NUM_INTEGER, _) => {
|
||||||
|
@ -1456,7 +1456,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
|
||||||
for arg in arguments {
|
for arg in arguments {
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
|
|
||||||
write_debug_error_type_help(arg.1, buf, Parens::InTypeParam);
|
write_debug_error_type_help(arg, buf, Parens::InTypeParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
// useful for debugging
|
// useful for debugging
|
||||||
|
|
|
@ -209,7 +209,8 @@ fn unify_alias(
|
||||||
|
|
||||||
problems
|
problems
|
||||||
} else {
|
} else {
|
||||||
mismatch!("{}", symbol)
|
dbg!(args.len(), other_args.len());
|
||||||
|
mismatch!("{:?}", symbol)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unify_pool(subs, pool, real_var, *other_real_var)
|
unify_pool(subs, pool, real_var, *other_real_var)
|
||||||
|
|
|
@ -471,12 +471,7 @@ fn read_main_roc_file(project_dir_path_opt: Option<&Path>) -> (PathStr, String)
|
||||||
let project_dir_path_str = path_to_string(project_dir_path);
|
let project_dir_path_str = path_to_string(project_dir_path);
|
||||||
|
|
||||||
if let Some(&roc_file_name) = roc_file_names.first() {
|
if let Some(&roc_file_name) = roc_file_names.first() {
|
||||||
let full_roc_file_path_str = vec![
|
let full_roc_file_path_str = path_to_string(&project_dir_path.join(roc_file_name));
|
||||||
project_dir_path_str.clone(),
|
|
||||||
"/".to_owned(),
|
|
||||||
roc_file_name.clone(),
|
|
||||||
]
|
|
||||||
.join("");
|
|
||||||
let file_as_str = std::fs::read_to_string(&Path::new(&full_roc_file_path_str))
|
let file_as_str = std::fs::read_to_string(&Path::new(&full_roc_file_path_str))
|
||||||
.unwrap_or_else(|err| panic!("In the provided project {:?}, I found the roc file {}, but I failed to read it: {}", &project_dir_path_str, &full_roc_file_path_str, err));
|
.unwrap_or_else(|err| panic!("In the provided project {:?}, I found the roc file {}, but I failed to read it: {}", &project_dir_path_str, &full_roc_file_path_str, err));
|
||||||
|
|
||||||
|
|
|
@ -1493,7 +1493,7 @@ pub fn to_doc<'b>(
|
||||||
parens,
|
parens,
|
||||||
alloc.symbol_foreign_qualified(symbol),
|
alloc.symbol_foreign_qualified(symbol),
|
||||||
args.into_iter()
|
args.into_iter()
|
||||||
.map(|(_, arg)| to_doc(alloc, Parens::InTypeParam, arg))
|
.map(|arg| to_doc(alloc, Parens::InTypeParam, arg))
|
||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
@ -1657,10 +1657,7 @@ fn to_diff<'b>(
|
||||||
}
|
}
|
||||||
|
|
||||||
(Alias(symbol1, args1, _), Alias(symbol2, args2, _)) if symbol1 == symbol2 => {
|
(Alias(symbol1, args1, _), Alias(symbol2, args2, _)) if symbol1 == symbol2 => {
|
||||||
// TODO remove collects
|
let args_diff = traverse(alloc, Parens::InTypeParam, args1, args2);
|
||||||
let a1 = args1.into_iter().map(|(_, v)| v).collect::<Vec<_>>();
|
|
||||||
let a2 = args2.into_iter().map(|(_, v)| v).collect::<Vec<_>>();
|
|
||||||
let args_diff = traverse(alloc, Parens::InTypeParam, a1, a2);
|
|
||||||
let left = report_text::apply(
|
let left = report_text::apply(
|
||||||
alloc,
|
alloc,
|
||||||
parens,
|
parens,
|
||||||
|
@ -1729,8 +1726,8 @@ fn to_diff<'b>(
|
||||||
ErrorType::Alias(Symbol::NUM_NUM, args, _) => {
|
ErrorType::Alias(Symbol::NUM_NUM, args, _) => {
|
||||||
matches!(
|
matches!(
|
||||||
&args.get(0),
|
&args.get(0),
|
||||||
Some((_, ErrorType::Type(Symbol::NUM_INTEGER, _)))
|
Some(ErrorType::Type(Symbol::NUM_INTEGER, _))
|
||||||
| Some((_, ErrorType::Alias(Symbol::NUM_INTEGER, _, _)))
|
| Some(ErrorType::Alias(Symbol::NUM_INTEGER, _, _))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
|
@ -1750,8 +1747,8 @@ fn to_diff<'b>(
|
||||||
ErrorType::Alias(Symbol::NUM_NUM, args, _) => {
|
ErrorType::Alias(Symbol::NUM_NUM, args, _) => {
|
||||||
matches!(
|
matches!(
|
||||||
&args.get(0),
|
&args.get(0),
|
||||||
Some((_, ErrorType::Type(Symbol::NUM_FLOATINGPOINT, _)))
|
Some(ErrorType::Type(Symbol::NUM_FLOATINGPOINT, _))
|
||||||
| Some((_, ErrorType::Alias(Symbol::NUM_FLOATINGPOINT, _, _)))
|
| Some(ErrorType::Alias(Symbol::NUM_FLOATINGPOINT, _, _))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue