Merge branch 'trunk' into str_trim_left

This commit is contained in:
Folkert de Vries 2021-11-10 14:06:01 +01:00 committed by GitHub
commit eb1b6d97bf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 169 additions and 69 deletions

View file

@ -240,7 +240,7 @@ pub fn build_file<'a>(
app_o_file.to_str().unwrap(), app_o_file.to_str().unwrap(),
]; ];
if matches!(opt_level, OptLevel::Development) { if matches!(opt_level, OptLevel::Development) {
inputs.push(bitcode::OBJ_PATH); inputs.push(bitcode::BUILTINS_HOST_OBJ_PATH);
} }
let (mut child, _) = // TODO use lld let (mut child, _) = // TODO use lld

View file

@ -97,7 +97,7 @@ pub fn build_zig_host_native(
"build-exe", "build-exe",
"-fPIE", "-fPIE",
shared_lib_path.to_str().unwrap(), shared_lib_path.to_str().unwrap(),
bitcode::OBJ_PATH, bitcode::BUILTINS_HOST_OBJ_PATH,
]); ]);
} else { } else {
command.args(&["build-obj", "-fPIC"]); command.args(&["build-obj", "-fPIC"]);
@ -186,7 +186,7 @@ pub fn build_zig_host_native(
"build-exe", "build-exe",
"-fPIE", "-fPIE",
shared_lib_path.to_str().unwrap(), shared_lib_path.to_str().unwrap(),
bitcode::OBJ_PATH, bitcode::BUILTINS_HOST_OBJ_PATH,
]); ]);
} else { } else {
command.args(&["build-obj", "-fPIC"]); command.args(&["build-obj", "-fPIC"]);
@ -282,7 +282,7 @@ pub fn build_c_host_native(
if let Some(shared_lib_path) = shared_lib_path { if let Some(shared_lib_path) = shared_lib_path {
command.args(&[ command.args(&[
shared_lib_path.to_str().unwrap(), shared_lib_path.to_str().unwrap(),
bitcode::OBJ_PATH, bitcode::BUILTINS_HOST_OBJ_PATH,
"-fPIE", "-fPIE",
"-pie", "-pie",
"-lm", "-lm",
@ -878,7 +878,7 @@ fn link_wasm32(
let zig_str_path = find_zig_str_path(); let zig_str_path = find_zig_str_path();
let wasi_libc_path = find_wasi_libc_path(); let wasi_libc_path = find_wasi_libc_path();
let child = Command::new("zig9") let child = Command::new("zig")
// .env_clear() // .env_clear()
// .env("PATH", &env_path) // .env("PATH", &env_path)
.args(&["build-exe"]) .args(&["build-exe"])

View file

@ -59,8 +59,9 @@ fn generateLlvmIrFile(
// Generate Object File // Generate Object File
// TODO: figure out how to get this to emit symbols that are only scoped to linkage (global but hidden). // 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. // @bhansconnect: I believe anything with global scope will still be preserved by the linker even if it
// With both of those changes, unused zig functions will be cleaned up by the linker saving around 100k. // is never called. I think it could theoretically be called by a dynamic lib that links to the executable
// or something similar.
fn generateObjectFile( fn generateObjectFile(
b: *Builder, b: *Builder,
mode: std.builtin.Mode, mode: std.builtin.Mode,
@ -75,6 +76,7 @@ fn generateObjectFile(
obj.setOutputDir("."); obj.setOutputDir(".");
obj.strip = true; obj.strip = true;
obj.target = target; obj.target = target;
obj.link_function_sections = true;
const obj_step = b.step(step_name, "Build object file for linking"); const obj_step = b.step(step_name, "Build object file for linking");
obj_step.dependOn(&obj.step); obj_step.dependOn(&obj.step);
} }

View file

@ -1,10 +1,15 @@
use std::ops::Index; use std::ops::Index;
pub const OBJ_PATH: &str = env!( pub const BUILTINS_HOST_OBJ_PATH: &str = env!(
"BUILTINS_HOST_O", "BUILTINS_HOST_O",
"Env var BUILTINS_HOST_O not found. Is there a problem with the build script?" "Env var BUILTINS_HOST_O not found. Is there a problem with the build script?"
); );
pub const BUILTINS_WASM32_OBJ_PATH: &str = env!(
"BUILTINS_WASM32_O",
"Env var BUILTINS_WASM32_O not found. Is there a problem with the build script?"
);
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct IntrinsicName { pub struct IntrinsicName {
pub options: [&'static str; 14], pub options: [&'static str; 14],

View file

@ -11,7 +11,8 @@ use roc_mono::layout::{Layout, LayoutIds};
use crate::layout::WasmLayout; use crate::layout::WasmLayout;
use crate::storage::{Storage, StoredValue, StoredValueKind}; use crate::storage::{Storage, StoredValue, StoredValueKind};
use crate::wasm_module::linking::{ use crate::wasm_module::linking::{
DataSymbol, LinkingSection, RelocationSection, WasmObjectSymbol, WASM_SYM_UNDEFINED, DataSymbol, LinkingSection, RelocationSection, WasmObjectSymbol, WASM_SYM_BINDING_WEAK,
WASM_SYM_UNDEFINED,
}; };
use crate::wasm_module::sections::{ use crate::wasm_module::sections::{
CodeSection, DataMode, DataSection, DataSegment, ExportSection, FunctionSection, GlobalSection, CodeSection, DataMode, DataSection, DataSegment, ExportSection, FunctionSection, GlobalSection,
@ -21,7 +22,10 @@ use crate::wasm_module::{
code_builder, BlockType, CodeBuilder, ConstExpr, Export, ExportType, Global, GlobalType, code_builder, BlockType, CodeBuilder, ConstExpr, Export, ExportType, Global, GlobalType,
LocalId, Signature, SymInfo, ValueType, LocalId, Signature, SymInfo, ValueType,
}; };
use crate::{copy_memory, CopyMemoryConfig, Env, PTR_TYPE}; use crate::{
copy_memory, CopyMemoryConfig, Env, BUILTINS_IMPORT_MODULE_NAME, MEMORY_NAME, PTR_TYPE,
STACK_POINTER_NAME,
};
/// The memory address where the constants data will be loaded during module instantiation. /// The memory address where the constants data will be loaded during module instantiation.
/// We avoid address zero and anywhere near it. They're valid addresses but maybe bug-prone. /// We avoid address zero and anywhere near it. They're valid addresses but maybe bug-prone.
@ -31,8 +35,6 @@ const CONST_SEGMENT_BASE_ADDR: u32 = 1024;
/// Index of the data segment where we store constants /// Index of the data segment where we store constants
const CONST_SEGMENT_INDEX: usize = 0; const CONST_SEGMENT_INDEX: usize = 0;
const IMPORT_MODULE_BUILTINS: &str = "builtins";
pub struct WasmBackend<'a> { pub struct WasmBackend<'a> {
env: &'a Env<'a>, env: &'a Env<'a>,
@ -66,7 +68,7 @@ impl<'a> WasmBackend<'a> {
let num_procs = proc_symbols.len(); let num_procs = proc_symbols.len();
exports.push(Export { exports.push(Export {
name: "__linear_memory".to_string(), name: MEMORY_NAME.to_string(),
ty: ExportType::Mem, ty: ExportType::Mem,
index: 0, index: 0,
}); });
@ -78,10 +80,17 @@ impl<'a> WasmBackend<'a> {
}, },
init: ConstExpr::I32(MEMORY_INIT_SIZE as i32), init: ConstExpr::I32(MEMORY_INIT_SIZE as i32),
}; };
linker_symbols.push(SymInfo::Global(WasmObjectSymbol::Defined {
flags: 0, exports.push(Export {
name: STACK_POINTER_NAME.to_string(),
ty: ExportType::Global,
index: 0, index: 0,
name: "__stack_pointer".to_string(), });
linker_symbols.push(SymInfo::Global(WasmObjectSymbol::Defined {
flags: WASM_SYM_BINDING_WEAK,
index: 0,
name: STACK_POINTER_NAME.to_string(),
})); }));
let const_segment = DataSegment { let const_segment = DataSegment {
@ -759,7 +768,7 @@ impl<'a> WasmBackend<'a> {
let import_index = self.module.import.entries.len() as u32; let import_index = self.module.import.entries.len() as u32;
let import = Import { let import = Import {
module: IMPORT_MODULE_BUILTINS, module: BUILTINS_IMPORT_MODULE_NAME,
name: name.to_string(), name: name.to_string(),
description: ImportDesc::Func { signature_index }, description: ImportDesc::Func { signature_index },
}; };

View file

@ -21,6 +21,9 @@ const PTR_TYPE: ValueType = ValueType::I32;
pub const STACK_POINTER_GLOBAL_ID: u32 = 0; pub const STACK_POINTER_GLOBAL_ID: u32 = 0;
pub const FRAME_ALIGNMENT_BYTES: i32 = 16; pub const FRAME_ALIGNMENT_BYTES: i32 = 16;
pub const MEMORY_NAME: &str = "memory";
pub const BUILTINS_IMPORT_MODULE_NAME: &str = "builtins";
pub const STACK_POINTER_NAME: &str = "__stack_pointer";
pub struct Env<'a> { pub struct Env<'a> {
pub arena: &'a Bump, pub arena: &'a Bump,

View file

@ -2159,7 +2159,8 @@ fn list_literal_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, List<'a>>
min_indent, min_indent,
List::Open, List::Open,
List::Space, List::Space,
List::IndentEnd List::IndentEnd,
Expr::SpaceBefore
) )
.parse(arena, state)?; .parse(arena, state)?;

View file

@ -1261,53 +1261,46 @@ macro_rules! collection_trailing_sep {
#[macro_export] #[macro_export]
macro_rules! collection_trailing_sep_e { macro_rules! collection_trailing_sep_e {
($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $space_problem:expr, $indent_problem:expr) => { ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $space_problem:expr, $indent_problem:expr, $space_before:expr) => {
skip_first!( skip_first!(
$opening_brace, $opening_brace,
skip_first!( |arena, state| {
// We specifically allow space characters inside here, so that let (_, spaces, state) = space0_e($min_indent, $space_problem, $indent_problem)
// `[ ]` can be successfully parsed as an empty list, and then .parse(arena, state)?;
// changed by the formatter back into `[]`.
//
// We don't allow newlines or comments in the middle of empty
// roc_collections because those are normally stored in an Expr,
// and there's no Expr in which to store them in an empty collection!
//
// We could change the AST to add extra storage specifically to
// support empty literals containing newlines or comments, but this
// does not seem worth even the tiniest regression in compiler performance.
zero_or_more!($crate::parser::word1(b' ', |row, col| $space_problem(
crate::parser::BadInputError::LineTooLong,
row,
col
))),
|arena, state| {
let (_, elements, state) =
and!(
$crate::parser::trailing_sep_by0(
$delimiter,
$crate::blankspace::space0_before_optional_after(
$elem,
$min_indent,
$space_problem,
$indent_problem,
$indent_problem
)
),
$crate::blankspace::space0_e($min_indent, $space_problem, $indent_problem)
).parse(arena, state)?;
let (_,_, state) = let (_, (mut parsed_elems, mut final_comments), state) =
if elements.0.is_empty() { and!(
one_of_with_error![$open_problem; $closing_brace].parse(arena, state)? $crate::parser::trailing_sep_by0(
} else { $delimiter,
$closing_brace.parse(arena, state)? $crate::blankspace::space0_before_optional_after(
}; $elem,
$min_indent,
$space_problem,
$indent_problem,
$indent_problem
)
),
$crate::blankspace::space0_e($min_indent, $space_problem, $indent_problem)
).parse(arena, state)?;
let (_,_, state) =
if parsed_elems.is_empty() {
one_of_with_error![$open_problem; $closing_brace].parse(arena, state)?
} else {
$closing_brace.parse(arena, state)?
};
Ok((MadeProgress, elements, state)) if !spaces.is_empty() {
if let Some(first) = parsed_elems.first_mut() {
first.value = $space_before(arena.alloc(first.value), spaces)
} else {
assert!(final_comments.is_empty());
final_comments = spaces;
}
} }
)
Ok((MadeProgress, (parsed_elems, final_comments), state))
}
) )
}; };
} }

View file

@ -326,7 +326,8 @@ fn record_pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRec
min_indent, min_indent,
PRecord::Open, PRecord::Open,
PRecord::Space, PRecord::Space,
PRecord::IndentEnd PRecord::IndentEnd,
Pattern::SpaceBefore
) )
.parse(arena, state)?; .parse(arena, state)?;

View file

@ -26,7 +26,8 @@ fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TT
min_indent, min_indent,
TTagUnion::Open, TTagUnion::Open,
TTagUnion::Space, TTagUnion::Space,
TTagUnion::IndentEnd TTagUnion::IndentEnd,
Tag::SpaceBefore
) )
.parse(arena, state)?; .parse(arena, state)?;
@ -276,7 +277,8 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TReco
min_indent, min_indent,
TRecord::Open, TRecord::Open,
TRecord::Space, TRecord::Space,
TRecord::IndentEnd TRecord::IndentEnd,
AssignedField::SpaceBefore
) )
.parse(arena, state)?; .parse(arena, state)?;

View file

@ -1193,6 +1193,30 @@ mod test_parse {
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
} }
#[test]
fn newline_inside_empty_list() {
let arena = Bump::new();
let expected = List {
items: &[],
final_comments: &[Newline],
};
let actual = parse_expr_with(&arena, "[\n]");
assert_eq!(Ok(expected), actual);
}
#[test]
fn comment_inside_empty_list() {
let arena = Bump::new();
let expected = List {
items: &[],
final_comments: &[LineComment("comment")],
};
let actual = parse_expr_with(&arena, "[#comment\n]");
assert_eq!(Ok(expected), actual);
}
#[test] #[test]
fn packed_singleton_list() { fn packed_singleton_list() {
let arena = Bump::new(); let arena = Bump::new();
@ -1219,6 +1243,24 @@ mod test_parse {
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
} }
#[test]
fn newline_singleton_list() {
let arena = Bump::new();
let item = &*arena.alloc(Expr::SpaceAfter(
arena.alloc(Num("1")),
arena.alloc([Newline]),
));
let item = Expr::SpaceBefore(item, arena.alloc([Newline]));
let items = [&*arena.alloc(Located::new(1, 1, 0, 1, item))];
let expected = List {
items: &items,
final_comments: &[],
};
let actual = parse_expr_with(&arena, "[\n1\n]");
assert_eq!(Ok(expected), actual);
}
// FIELD ACCESS // FIELD ACCESS
#[test] #[test]

View file

@ -1330,7 +1330,7 @@ fn pow_int() {
} }
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-dev"))] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
fn atan() { fn atan() {
assert_evals_to!("Num.atan 10", 1.4711276743037347, f64); assert_evals_to!("Num.atan 10", 1.4711276743037347, f64);
} }

View file

@ -193,7 +193,10 @@ pub fn helper(
app_o_file.clone(), app_o_file.clone(),
// Long term we probably want a smarter way to link in zig builtins. // Long term we probably want a smarter way to link in zig builtins.
// With the current method all methods are kept and it adds about 100k to all outputs. // With the current method all methods are kept and it adds about 100k to all outputs.
&[app_o_file.to_str().unwrap(), bitcode::OBJ_PATH], &[
app_o_file.to_str().unwrap(),
bitcode::BUILTINS_HOST_OBJ_PATH,
],
LinkType::Dylib, LinkType::Dylib,
) )
.expect("failed to link dynamic library"); .expect("failed to link dynamic library");

View file

@ -367,7 +367,7 @@ pub fn helper_wasm<'a>(
use std::process::Command; use std::process::Command;
Command::new("zig9") Command::new("zig")
.current_dir(dir_path) .current_dir(dir_path)
.args(&[ .args(&[
"wasm-ld", "wasm-ld",

View file

@ -4,8 +4,12 @@ use std::hash::{Hash, Hasher};
use crate::helpers::from_wasm32_memory::FromWasm32Memory; use crate::helpers::from_wasm32_memory::FromWasm32Memory;
use crate::helpers::wasm32_test_result::Wasm32TestResult; use crate::helpers::wasm32_test_result::Wasm32TestResult;
use roc_builtins::bitcode;
use roc_can::builtins::builtin_defs_map; use roc_can::builtins::builtin_defs_map;
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_gen_wasm::MEMORY_NAME;
use tempfile::tempdir;
const TEST_WRAPPER_NAME: &str = "test_wrapper"; const TEST_WRAPPER_NAME: &str = "test_wrapper";
@ -143,8 +147,43 @@ pub fn helper_wasm<'a, T: Wasm32TestResult>(
use wasmer::{Instance, Module, Store}; use wasmer::{Instance, Module, Store};
let store = Store::default(); let store = Store::default();
// let module = Module::from_file(&store, &test_wasm_path).unwrap();
let wasmer_module = Module::from_binary(&store, &module_bytes).unwrap(); let wasmer_module = {
let dir = tempdir().unwrap();
let dirpath = dir.path();
let final_wasm_file = dirpath.join("final.wasm");
let app_o_file = dirpath.join("app.o");
// write the module to a file so the linker can access it
std::fs::write(&app_o_file, &module_bytes).unwrap();
std::process::Command::new("zig")
.args(&[
"wasm-ld",
// input files
app_o_file.to_str().unwrap(),
bitcode::BUILTINS_WASM32_OBJ_PATH,
// output
"-o",
final_wasm_file.to_str().unwrap(),
// we don't define `_start`
"--no-entry",
// If you only specify test_wrapper, it will stop at the call to UserApp_main_1
// But if you specify both exports, you get all the dependencies.
//
// It seems that it will not write out an export you didn't explicitly specify,
// even if it's a dependency of another export!
// In our case we always export main and test_wrapper so that's OK.
"--export",
"test_wrapper",
"--export",
"#UserApp_main_1",
])
.output()
.unwrap();
Module::from_file(&store, &final_wasm_file).unwrap()
};
// First, we create the `WasiEnv` // First, we create the `WasiEnv`
use wasmer_wasi::WasiState; use wasmer_wasi::WasiState;
@ -171,7 +210,7 @@ where
let instance = crate::helpers::wasm::helper_wasm(&arena, src, stdlib, &expected); let instance = crate::helpers::wasm::helper_wasm(&arena, src, stdlib, &expected);
let memory = instance.exports.get_memory("__linear_memory").unwrap(); let memory = instance.exports.get_memory(MEMORY_NAME).unwrap();
let test_wrapper = instance.exports.get_function(TEST_WRAPPER_NAME).unwrap(); let test_wrapper = instance.exports.get_function(TEST_WRAPPER_NAME).unwrap();

View file

@ -1,2 +1,2 @@
hello-web hello-web.wasm
*.wat *.wat