Merge branch 'trunk' into str-refcount

This commit is contained in:
Brendan Hansknecht 2020-09-30 15:12:17 -07:00
commit a2069a88db
19 changed files with 1792 additions and 188 deletions

View File

@ -24,7 +24,9 @@ That will help us improve this document for everyone who reads it in the future!
### LLVM installation on Linux
On some Linux systems we've seen the error "failed to run custom build command for x11".
On Ubuntu, running `sudo apt-get install cmake libx11-dev` fixed this.
On Ubuntu, running `sudo apt install pkg-config cmake libx11-dev` fixed this.
If you encounter `cannot find -lz` run `sudo apt install zlib1g-dev`.
### LLVM installation on macOS

3
Cargo.lock generated
View File

@ -579,6 +579,9 @@ dependencies = [
"fs_extra",
"handlebars",
"pulldown-cmark",
"roc_builtins",
"roc_collections",
"roc_load",
"serde",
"serde_derive",
"serde_json",

View File

@ -1,5 +1,5 @@
interface Bool
exposes [ not, isEq, isNe ]
interface Bool2
exposes [ not, and, or, xor, isEq, isNotEq ]
imports []
## Returns #False when given #True, and vice versa.
@ -76,7 +76,9 @@ xor : Bool, Bool -> Bool
##
## Note that `isEq` takes `'val` instead of `val`, which means `isEq` does not
## accept arguments whose types contain functions.
isEq : 'val, 'val -> Bool
# TODO: removed `'` from signature because parser does not support it yet
# Original signature: `isEq : 'val, 'val -> Bool`
isEq : val, val -> Bool
## Calls #eq on the given values, then calls #not on the result.
##
@ -84,4 +86,6 @@ isEq : 'val, 'val -> Bool
##
## Note that `isNotEq` takes `'val` instead of `val`, which means `isNotEq` does not
## accept arguments whose types contain functions.
isNotEq : 'val, 'val -> Bool
# TODO: removed `'` from signature because parser does not support it yet
# Original signature: `isNotEq : 'val, 'val -> Bool`
isNotEq : val, val -> Bool

View File

@ -1,5 +1,5 @@
interface List
exposes [ List, map, fold ]
interface List2
exposes [ List, single, empty, repeat, range, reverse, sort, map, mapWithIndex, mapOrCancel, mapOks, update, updater, allOks, append, prepend, concat, join, joinMap, oks, zip, zipMap, keepIf, dropIf, first, last, get, max, min, put, drop, append, prepend, dropLast, dropFirst, takeFirst, takeLast, split, sublist, walk, walkBackwards, walkUntil, walkBackwardsUntil, len, isEmpty, contains, all, any ]
imports []
## Types

View File

@ -1,16 +1,16 @@
interface Map
exposes [ Map, isEmpty ]
interface Map2
exposes [ isEmpty, map ]
imports []
isEmpty : Map * * -> Bool
## Convert each key and value in the #Map to something new, by calling a conversion
## function on each of them. Then return a new #Map of the converted keys and values.
##
##
## >>> Map.map {{ 3.14 => "pi", 1.0 => "one" }} \{ key, value } -> { key:
##
##
## >>> Map.map {[ "", "a", "bc" ]} Str.isEmpty
##
##
## `map` functions like this are common in Roc, and they all work similarly.
## See for example #Result.map, #List.map, and #Set.map.
map : List before, (before -> after) -> List after

View File

@ -1,4 +1,4 @@
interface Num
interface Num2
exposes [ Num, neg, abs, add, sub, mul, isOdd, isEven, isPositive, isNegative, isZero ]
imports []

View File

@ -1,5 +1,5 @@
interface Set
exposes [ Set, map, isEmpty ]
interface Set2
exposes [ empty, isEmpty, len, add, drop, map ]
imports []
@ -12,10 +12,14 @@ isEmpty : Set * -> Bool
len : Set * -> Len
add : Set 'elem, 'elem -> Set 'elem
# TODO: removed `'` from signature because parser does not support it yet
# Original signature: `add : Set 'elem, 'elem -> Set 'elem`
add : Set elem, elem -> Set elem
## Drops the given element from the set.
drop : Set 'elem, 'elem -> Set 'elem
# TODO: removed `'` from signature because parser does not support it yet
# Original signature: `drop : Set 'elem, 'elem -> Set 'elem`
drop : Set elem, elem -> Set elem
## Convert each element in the set to something new, by calling a conversion
## function on each of them. Then return a new set of the converted values.
@ -26,4 +30,6 @@ drop : Set 'elem, 'elem -> Set 'elem
##
## `map` functions like this are common in Roc, and they all work similarly.
## See for example #Result.map, #List.map, and #Map.map.
map : Set 'elem, ('before -> 'after) -> Set 'after
# TODO: removed `'` from signature because parser does not support it yet
# Original signature: `map : Set 'elem, ('before -> 'after) -> Set 'after`
map : Set elem, (before -> after) -> Set after

View File

@ -1,4 +1,6 @@
interface Str exposes [ Str, isEmpty, join ] imports []
interface Str2
exposes [ Str2, decimal, split, isEmpty, startsWith, endsWith, contains, anyGraphemes, allGraphemes, join, joinWith, padGraphemesStart, padGraphemesEnd, graphemes, reverseGraphemes, isCaseInsensitiveEq, isCaseInsensitiveNeq, walkGraphemes, isCapitalized, isAllUppercase, isAllLowercase, toUtf8, toUtf16, toUtf32, walkUtf8, walkUtf16, walkUtf32, walkRevUtf8, walkRevUtf16, walkRevUtf32 ]
imports []
## Types
## Dealing with text is a deep topic, so by design, Roc's `Str` module sticks
@ -98,7 +100,7 @@ interface Str exposes [ Str, isEmpty, join ] imports []
## A [Unicode](https://unicode.org) text value.
##
Str : [ @Str ]
Str2 : [ @Str ]
## Convert

View File

@ -1210,10 +1210,10 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>(
// the refcount of a new allocation is initially 1
// we assume that the allocation is indeed used (dead variables are eliminated)
let ref_count_one = ctx
.i64_type()
.const_int(crate::llvm::refcounting::REFCOUNT_1 as _, false);
builder.build_store(refcount_ptr, ref_count_one);
builder.build_store(
refcount_ptr,
crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes),
);
// store the value in the pointer
builder.build_store(list_element_ptr, value);
@ -2388,7 +2388,6 @@ where
Layout::Builtin(Builtin::List(MemoryMode::Refcounted, _)) => {
// no static guarantees, but all is not lost: we can check the refcount
// if it is one, we hold the final reference, and can mutate it in-place!
let builder = env.builder;
let ctx = env.context;
let ret_type = basic_type_from_layout(env.arena, ctx, list_layout, env.ptr_bytes);
@ -2400,7 +2399,7 @@ where
.build_load(refcount_ptr, "get_refcount")
.into_int_value();
let comparison = refcount_is_one_comparison(builder, env.context, refcount);
let comparison = refcount_is_one_comparison(env, refcount);
crate::llvm::build_list::build_basic_phi2(
env, parent, comparison, in_place, clone, ret_type,

View File

@ -1757,8 +1757,7 @@ pub fn allocate_list<'a, 'ctx, 'env>(
InPlace::Clone => {
// the refcount of a new list is initially 1
// we assume that the list is indeed used (dead variables are eliminated)
ctx.i64_type()
.const_int(crate::llvm::refcounting::REFCOUNT_1 as _, false)
crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes)
}
};

View File

@ -200,7 +200,6 @@ pub fn ptr_int(ctx: &Context, ptr_bytes: u32) -> IntType<'_> {
2 => ctx.i16_type(),
4 => ctx.i32_type(),
8 => ctx.i64_type(),
16 => ctx.i128_type(),
_ => panic!(
"Invalid target: Roc does't support compiling to {}-bit systems.",
ptr_bytes * 8

View File

@ -7,7 +7,6 @@ use crate::llvm::build_list::list_len;
use crate::llvm::convert::{basic_type_from_layout, block_of_memory, ptr_int};
use bumpalo::collections::Vec;
use inkwell::basic_block::BasicBlock;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::module::Linkage;
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
@ -15,9 +14,21 @@ use inkwell::{AddressSpace, IntPredicate};
use roc_module::symbol::Symbol;
use roc_mono::layout::{Builtin, Layout, MemoryMode};
pub const REFCOUNT_1: usize = isize::MIN as usize;
pub const REFCOUNT_MAX: usize = 0 as usize;
pub fn refcount_1(ctx: &Context, ptr_bytes: u32) -> IntValue<'_> {
match ptr_bytes {
1 => ctx.i8_type().const_int(i8::MIN as u64, false),
2 => ctx.i16_type().const_int(i16::MIN as u64, false),
4 => ctx.i32_type().const_int(i32::MIN as u64, false),
8 => ctx.i64_type().const_int(i64::MIN as u64, false),
_ => panic!(
"Invalid target: Roc does't support compiling to {}-bit systems.",
ptr_bytes * 8
),
}
}
pub fn decrement_refcount_layout<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
@ -644,6 +655,7 @@ fn increment_refcount_help<'a, 'ctx, 'env>(
) {
let builder = env.builder;
let ctx = env.context;
let refcount_type = ptr_int(ctx, env.ptr_bytes);
let refcount = env
.builder
@ -653,12 +665,12 @@ fn increment_refcount_help<'a, 'ctx, 'env>(
let max = builder.build_int_compare(
IntPredicate::EQ,
refcount,
ctx.i64_type().const_int(REFCOUNT_MAX as u64, false),
refcount_type.const_int(REFCOUNT_MAX as u64, false),
"refcount_max_check",
);
let incremented = builder.build_int_add(
refcount,
ctx.i64_type().const_int(1 as u64, false),
refcount_type.const_int(1 as u64, false),
"increment_refcount",
);
let selected = builder.build_select(max, refcount, incremented, "select_refcount");
@ -689,6 +701,7 @@ fn decrement_refcount_help<'a, 'ctx, 'env>(
) {
let builder = env.builder;
let ctx = env.context;
let refcount_type = ptr_int(ctx, env.ptr_bytes);
let refcount = env
.builder
@ -700,7 +713,7 @@ fn decrement_refcount_help<'a, 'ctx, 'env>(
LLVM_SADD_WITH_OVERFLOW_I64,
&[
refcount.into(),
ctx.i64_type().const_int((-1 as i64) as u64, false).into(),
refcount_type.const_int((-1 as i64) as u64, true).into(),
],
)
.into_struct_value();
@ -738,7 +751,7 @@ fn decrement_refcount_help<'a, 'ctx, 'env>(
let max = builder.build_int_compare(
IntPredicate::EQ,
refcount,
ctx.i64_type().const_int(REFCOUNT_MAX as u64, false),
refcount_type.const_int(REFCOUNT_MAX as u64, false),
"refcount_max_check",
);
let decremented = builder
@ -1137,16 +1150,14 @@ pub fn build_inc_union_help<'a, 'ctx, 'env>(
builder.build_return(None);
}
pub fn refcount_is_one_comparison<'ctx>(
builder: &Builder<'ctx>,
context: &'ctx Context,
pub fn refcount_is_one_comparison<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
refcount: IntValue<'ctx>,
) -> IntValue<'ctx> {
let refcount_one: IntValue<'ctx> = context.i64_type().const_int(REFCOUNT_1 as _, false);
builder.build_int_compare(
env.builder.build_int_compare(
IntPredicate::EQ,
refcount,
refcount_one,
refcount_1(env.context, env.ptr_bytes),
"refcount_one_check",
)
}
@ -1171,8 +1182,9 @@ fn get_refcount_ptr<'a, 'ctx, 'env>(
layout: &Layout<'a>,
ptr: PointerValue<'ctx>,
) -> PointerValue<'ctx> {
let refcount_type = ptr_int(env.context, env.ptr_bytes);
let ptr_as_int =
cast_basic_basic(env.builder, ptr.into(), env.context.i64_type().into()).into_int_value();
cast_basic_basic(env.builder, ptr.into(), refcount_type.into()).into_int_value();
get_refcount_ptr_help(env, layout, ptr_as_int)
}
@ -1192,20 +1204,19 @@ fn get_refcount_ptr_help<'a, 'ctx, 'env>(
_ => (env.ptr_bytes as u64).max(value_bytes),
};
// pointer to usize
let refcount_type = ptr_int(ctx, env.ptr_bytes);
// subtract offset, to access the refcount
let refcount_ptr = builder.build_int_sub(
ptr_as_int,
ctx.i64_type().const_int(offset, false),
refcount_type.const_int(offset, false),
"make_refcount_ptr",
);
// pointer to usize
let ptr_bytes = env.ptr_bytes;
let int_type = ptr_int(ctx, ptr_bytes);
builder.build_int_to_ptr(
refcount_ptr,
int_type.ptr_type(AddressSpace::Generic),
refcount_type.ptr_type(AddressSpace::Generic),
"get_refcount_ptr",
)
}

1660
compiler/load/src/docs.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,4 +10,5 @@
// and encouraging shortcuts here creates bad incentives. I would rather temporarily
// re-enable this when working on performance optimizations than have it block PRs.
#![allow(clippy::large_enum_variant)]
pub mod docs;
pub mod file;

1
docs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -13,3 +13,6 @@ serde_json = "1.0.39"
serde_derive = "1.0.75"
fs_extra = "1.2.0"
pulldown-cmark = { version = "0.8", default-features = false }
roc_load = { path = "../compiler/load" }
roc_builtins = { path = "../compiler/builtins" }
roc_collections = { path = "../compiler/collections" }

View File

@ -7,27 +7,9 @@ extern crate pulldown_cmark;
extern crate serde_json;
use std::error::Error;
use std::fs;
#[derive(Serialize)]
pub struct Package {
name: String,
version: String,
docs: String,
modules: Vec<Module>,
}
#[derive(Serialize, Clone)]
pub struct Module {
name: String,
docs: String,
entries: Vec<ModuleEntry>,
}
#[derive(Serialize, Clone)]
pub struct ModuleEntry {
name: String,
docs: String,
}
extern crate roc_load;
use roc_collections::all::MutMap;
use std::path::{Path, PathBuf};
#[derive(Serialize)]
pub struct Template {
@ -39,6 +21,12 @@ pub struct Template {
module_links: Vec<TemplateLink>,
}
#[derive(Serialize, Clone)]
pub struct ModuleEntry {
name: String,
docs: String,
}
#[derive(Serialize)]
pub struct TemplateLink {
name: String,
@ -53,132 +41,49 @@ pub struct TemplateLinkEntry {
}
fn main() -> Result<(), Box<dyn Error>> {
let package = Package {
let std_lib = roc_builtins::std::standard_stdlib();
let subs_by_module = MutMap::default();
let src_dir = Path::new("../compiler/builtins/docs");
let files = vec![
PathBuf::from(r"../compiler/builtins/docs/Bool.roc"),
PathBuf::from(r"../compiler/builtins/docs/Map.roc"),
// Not working
// PathBuf::from(r"../compiler/builtins/docs/List.roc"),
// Not working
// PathBuf::from(r"../compiler/builtins/docs/Num.roc"),
PathBuf::from(r"../compiler/builtins/docs/Set.roc"),
PathBuf::from(r"../compiler/builtins/docs/Str.roc"),
];
let mut modules_docs = vec![];
// Load each file is files vector
for filename in files {
let loaded = roc_load::docs::load(filename, &std_lib, src_dir, subs_by_module.clone())
.expect("TODO gracefully handle load failing");
modules_docs.push(loaded.module_docs);
}
let package = roc_load::docs::Documentation {
name: "roc/builtins".to_string(),
version: "1.0.0".to_string(),
docs: "Package introduction or README.".to_string(),
modules: vec![
Module {
name: "Str".to_string(),
docs: "Module introduction".to_string(),
entries: vec![
ModuleEntry {
name: "Str".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "isEmpty".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "append".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "prepend".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "concat".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "join".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "split".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "countGraphemes".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "foldGraphemes".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
],
},
Module {
name: "Bool".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![
ModuleEntry {
name: "isEq".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "isNeq".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
],
},
Module {
name: "Num".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![
ModuleEntry {
name: "add".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "sub".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
ModuleEntry {
name: "mul".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example."
.to_string(),
},
],
},
Module {
name: "List".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![],
},
Module {
name: "Set".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![],
},
Module {
name: "Map".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![],
},
Module {
name: "Result".to_string(),
docs: "Hello world, this is a **complicated** *very simple* example.".to_string(),
entries: vec![],
},
],
modules: modules_docs,
};
// Make sure the directories exists
// Remove old build folder
fs::remove_dir_all("./build")?;
// Make sure the output directories exists
fs::create_dir_all(format!("./build/{}/{}", package.name, package.version))?;
// Register handlebar template
// Register handlebars template
let mut handlebars = handlebars::Handlebars::new();
assert!(handlebars
.register_template_file("page", "./src/templates/page.hbs")
.is_ok());
let markdown_options = pulldown_cmark::Options::empty();
let markdown_options = pulldown_cmark::Options::all();
// Write each package's module docs
for module in &package.modules {
@ -198,11 +103,12 @@ fn main() -> Result<(), Box<dyn Error>> {
.into_iter()
.map(|entry| {
// Convert entry docs from markdown to html
let entry_docs_parser =
pulldown_cmark::Parser::new_ext(&entry.docs, markdown_options);
let mut entry_docs_html: String =
String::with_capacity(entry.docs.len() * 3 / 2);
pulldown_cmark::html::push_html(&mut entry_docs_html, entry_docs_parser);
let mut entry_docs_html: String = String::new();
if let Some(docs) = entry.docs {
let entry_docs_parser =
pulldown_cmark::Parser::new_ext(&docs, markdown_options);
pulldown_cmark::html::push_html(&mut entry_docs_html, entry_docs_parser);
}
ModuleEntry {
name: entry.name.clone(),

View File

@ -12,6 +12,10 @@ To run in release mode instead, do:
$ cargo run --release run Hello.roc
```
## Troubleshooting
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.
## Design Notes
This demonstrates the basic design of hosts: Roc code gets compiled into a pure

View File

@ -11,3 +11,7 @@ To run in release mode instead, do:
```bash
$ cargo run --release run Quicksort.roc
```
## Troubleshooting
If you encounter `cannot find -lc++`, run the following for ubuntu `sudo apt install libc++-dev`.