mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Merge remote-tracking branch 'origin/trunk' into add-dec-types
This commit is contained in:
commit
bba1d1b4fe
43 changed files with 953 additions and 688 deletions
109
Cargo.lock
generated
109
Cargo.lock
generated
|
@ -967,6 +967,15 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "drm-fourcc"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebbf3a5ed4671aabffefce172ff43d69c1f27dd2c6aea28e5212a70f32ada0cf"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.8"
|
||||
|
@ -1008,6 +1017,16 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "external-memory"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4dfe8d292b014422776a8c516862d2bff8a81b223a4461dfdc45f3862dc9d39"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"drm-fourcc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
|
@ -1221,9 +1240,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-auxil"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccf8711c9994dfa34337466bee3ae1462e172874c432ce4eb120ab2e98d39cf"
|
||||
checksum = "1694991b11d642680e82075a75c7c2bd75556b805efa7660b705689f05b1ab1c"
|
||||
dependencies = [
|
||||
"fxhash",
|
||||
"gfx-hal",
|
||||
|
@ -1232,14 +1251,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-dx11"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f839f27f8c8a6dc553ccca7f5b35a42009432bc25db9688bba7061cd394161f"
|
||||
checksum = "8f9e453baf3aaef2b0c354ce0b3d63d76402e406a59b64b7182d123cfa6635ae"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"gfx-auxil",
|
||||
"gfx-hal",
|
||||
"gfx-renderdoc",
|
||||
"libloading 0.7.0",
|
||||
"log",
|
||||
"parking_lot",
|
||||
|
@ -1254,9 +1274,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-dx12"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3937738b0da5839bba4e33980d29f9a06dbce184d04a3a08c9a949e7953700e3"
|
||||
checksum = "61f09e9d8c2aa69e9a21eb83c0f5d1a286c6d37da011f796e550d180b08090ce"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-set",
|
||||
|
@ -1264,6 +1284,7 @@ dependencies = [
|
|||
"d3d12",
|
||||
"gfx-auxil",
|
||||
"gfx-hal",
|
||||
"gfx-renderdoc",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"range-alloc",
|
||||
|
@ -1276,9 +1297,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-empty"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ac55ada4bfcd35479b3421eea324d36d7da5f724e2f66ecb36d4efdb7041a5e"
|
||||
checksum = "29c8f813c47791918aa00dc9c9ddf961d23fa8c2a5d869e6cb8ea84f944820f4"
|
||||
dependencies = [
|
||||
"gfx-hal",
|
||||
"log",
|
||||
|
@ -1287,9 +1308,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-gl"
|
||||
version = "0.8.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0caa03d6e0b7b4f202aea1f20c3f3288cfa06d92d24cea9d69c9a7627967244a"
|
||||
checksum = "6bae057fc3a0ab23ecf97ae51d4017d27d5ddf0aab16ee6dcb58981af88c3152"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
|
@ -1309,15 +1330,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-metal"
|
||||
version = "0.8.2"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "340895ad544ba46433acb3bdabece0ef16f2dbedc030adbd7c9eaf2839fbed41"
|
||||
checksum = "0de85808e2a98994c6af925253f8a9593bc57180ef1ea137deab6d35cc949517"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"block",
|
||||
"cocoa-foundation",
|
||||
"copyless",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
"fxhash",
|
||||
"gfx-hal",
|
||||
|
@ -1334,15 +1356,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-backend-vulkan"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a353fc6fdb42ec646de49bbb74e4870e37a7e680caf33f3ac0615c30b1146d94"
|
||||
checksum = "a9861ec855acbbc65c0e4f966d761224886e811dc2c6d413a4776e9293d0e5c0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"ash",
|
||||
"byteorder",
|
||||
"core-graphics-types",
|
||||
"gfx-hal",
|
||||
"gfx-renderdoc",
|
||||
"inplace_it",
|
||||
"log",
|
||||
"naga",
|
||||
|
@ -1355,16 +1378,28 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gfx-hal"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d285bfd566f6b9134af908446ca350c0a1047495dfb9bbd826e701e8ee1d259"
|
||||
checksum = "7fbb575ea793dd0507b3082f4f2cde62dc9f3cebd98f5cd49ba2a4da97a976fd"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"external-memory",
|
||||
"naga",
|
||||
"raw-window-handle",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gfx-renderdoc"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8027995e247e2426d3a00d13f5191dd56c314bff02dc4b54cbf727f1ba9c40a"
|
||||
dependencies = [
|
||||
"libloading 0.7.0",
|
||||
"log",
|
||||
"renderdoc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.24.0"
|
||||
|
@ -1880,13 +1915,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "metal"
|
||||
version = "0.22.0"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777"
|
||||
checksum = "79d7d769f1c104b8388294d6594d491d2e21240636f5f94d37f8a0f3d7904450"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"block",
|
||||
"cocoa-foundation",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
"log",
|
||||
"objc",
|
||||
|
@ -1956,9 +1991,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.4.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d74f2c7ace793a760165ac0679d6830809ad4e85f6886f72e4f8c4aa4291c5"
|
||||
checksum = "ef670817eef03d356d5a509ea275e7dd3a78ea9e24261ea3cb2dfed1abb08f64"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
|
@ -1967,6 +2002,7 @@ dependencies = [
|
|||
"log",
|
||||
"num-traits",
|
||||
"petgraph",
|
||||
"rose_tree",
|
||||
"spirv_headers",
|
||||
"thiserror",
|
||||
]
|
||||
|
@ -2935,6 +2971,12 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "renderdoc-sys"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
|
||||
|
||||
[[package]]
|
||||
name = "roc_build"
|
||||
version = "0.1.0"
|
||||
|
@ -3451,6 +3493,15 @@ dependencies = [
|
|||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rose_tree"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284de9dae38774e2813aaabd7e947b4a6fe9b8c58c2309f754a487cdd50de1c2"
|
||||
dependencies = [
|
||||
"petgraph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.19"
|
||||
|
@ -4365,9 +4416,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wgpu"
|
||||
version = "0.8.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "215fd50e66f794bd16683e7e0e0b9b53be265eb10fdf02276caf5de3e5743fcf"
|
||||
checksum = "bd247f8b26fd3d42ef2f320d378025cd6e84d782ef749fab45cc3b981fbe3275"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"js-sys",
|
||||
|
@ -4385,9 +4436,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.8.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d56c368fc0e6f3927c711d2b55a51ad4321218efc0239c4acf69e456ab70399"
|
||||
checksum = "2af5c8acd3ae5781a277cdf65a17f3a7135de5ae782775620e74ea16c9d47770"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
|
@ -4415,18 +4466,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wgpu-types"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa248d90c8e6832269b8955bf800e8241f942c25e18a235b7752226804d21556"
|
||||
checksum = "4f5c9678cd533558e28b416d66947b099742df1939307478db54f867137f1b60"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wgpu_glyph"
|
||||
version = "0.12.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634570b440f4c24c2e6049ed01ec832c23d338dea3eca654d5760838017a1c8b"
|
||||
checksum = "0fee8c96eda18195a7ad9989737183e0a357f14b15e98838c76abbcf56a5f970"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"glyph_brush",
|
||||
|
|
|
@ -214,8 +214,48 @@ fn jit_to_ast_help<'a>(
|
|||
match variant {
|
||||
NonRecursive {
|
||||
sorted_tag_layouts: tags_and_layouts,
|
||||
} => {
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
size as usize,
|
||||
|ptr: *const u8| {
|
||||
// Because this is a `Wrapped`, the first 8 bytes encode the tag ID
|
||||
let offset = tags_and_layouts
|
||||
.iter()
|
||||
.map(|(_, fields)| {
|
||||
fields
|
||||
.iter()
|
||||
.map(|l| l.stack_size(env.ptr_bytes))
|
||||
.sum()
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
let tag_id = *(ptr.add(offset as usize) as *const i64);
|
||||
|
||||
// use the tag ID as an index, to get its name and layout of any arguments
|
||||
let (tag_name, arg_layouts) =
|
||||
&tags_and_layouts[tag_id as usize];
|
||||
|
||||
let tag_expr = tag_name_to_expr(env, tag_name);
|
||||
let loc_tag_expr =
|
||||
&*env.arena.alloc(Located::at_zero(tag_expr));
|
||||
|
||||
let variables = &tags[tag_name];
|
||||
|
||||
debug_assert_eq!(arg_layouts.len(), variables.len());
|
||||
|
||||
// NOTE assumes the data bytes are the first bytes
|
||||
let it = variables.iter().copied().zip(arg_layouts.iter());
|
||||
let output = sequence_of_expr(env, ptr, it);
|
||||
let output = output.into_bump_slice();
|
||||
|
||||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||
}
|
||||
))
|
||||
}
|
||||
| Recursive {
|
||||
Recursive {
|
||||
sorted_tag_layouts: tags_and_layouts,
|
||||
} => {
|
||||
Ok(run_jit_function_dynamic_type!(
|
||||
|
|
|
@ -121,7 +121,7 @@ mod cli_run {
|
|||
&example_file("hello-world", "Hello.roc"),
|
||||
"hello-world",
|
||||
&[],
|
||||
"Hello, World!!!!!!!!!!!!!\n",
|
||||
"Hello, World!\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ mod cli_run {
|
|||
&example_file("hello-world", "Hello.roc"),
|
||||
"hello-world",
|
||||
&[],
|
||||
"Hello, World!!!!!!!!!!!!!\n",
|
||||
"Hello, World!\n",
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ pub fn gen_from_mono_module(
|
|||
opt_level,
|
||||
loaded.procedures,
|
||||
loaded.entry_point,
|
||||
Some(&app_ll_file),
|
||||
);
|
||||
|
||||
env.dibuilder.finalize();
|
||||
|
@ -193,7 +194,7 @@ pub fn gen_from_mono_module(
|
|||
// run the debugir https://github.com/vaivaswatha/debugir tool
|
||||
match Command::new("debugir")
|
||||
.env_clear()
|
||||
.args(&[app_ll_file.to_str().unwrap()])
|
||||
.args(&["-instnamer", app_ll_file.to_str().unwrap()])
|
||||
.output()
|
||||
{
|
||||
Ok(_) => {}
|
||||
|
|
|
@ -12,6 +12,7 @@ const Opaque = ?[*]u8;
|
|||
const Inc = fn (?[*]u8) callconv(.C) void;
|
||||
const IncN = fn (?[*]u8, usize) callconv(.C) void;
|
||||
const Dec = fn (?[*]u8) callconv(.C) void;
|
||||
const HasTagId = fn (u16, ?[*]u8) callconv(.C) extern struct { matched: bool, data: ?[*]u8 };
|
||||
|
||||
pub const RocList = extern struct {
|
||||
bytes: ?[*]u8,
|
||||
|
@ -405,11 +406,14 @@ pub fn listKeepOks(
|
|||
before_width: usize,
|
||||
result_width: usize,
|
||||
after_width: usize,
|
||||
has_tag_id: HasTagId,
|
||||
dec_result: Dec,
|
||||
) callconv(.C) RocList {
|
||||
const good_constructor: u16 = 1;
|
||||
|
||||
return listKeepResult(
|
||||
list,
|
||||
RocResult.isOk,
|
||||
good_constructor,
|
||||
caller,
|
||||
data,
|
||||
inc_n_data,
|
||||
|
@ -418,6 +422,7 @@ pub fn listKeepOks(
|
|||
before_width,
|
||||
result_width,
|
||||
after_width,
|
||||
has_tag_id,
|
||||
dec_result,
|
||||
);
|
||||
}
|
||||
|
@ -432,11 +437,14 @@ pub fn listKeepErrs(
|
|||
before_width: usize,
|
||||
result_width: usize,
|
||||
after_width: usize,
|
||||
has_tag_id: HasTagId,
|
||||
dec_result: Dec,
|
||||
) callconv(.C) RocList {
|
||||
const good_constructor: u16 = 0;
|
||||
|
||||
return listKeepResult(
|
||||
list,
|
||||
RocResult.isErr,
|
||||
good_constructor,
|
||||
caller,
|
||||
data,
|
||||
inc_n_data,
|
||||
|
@ -445,13 +453,14 @@ pub fn listKeepErrs(
|
|||
before_width,
|
||||
result_width,
|
||||
after_width,
|
||||
has_tag_id,
|
||||
dec_result,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn listKeepResult(
|
||||
list: RocList,
|
||||
is_good_constructor: fn (RocResult) bool,
|
||||
good_constructor: u16,
|
||||
caller: Caller1,
|
||||
data: Opaque,
|
||||
inc_n_data: IncN,
|
||||
|
@ -460,6 +469,7 @@ pub fn listKeepResult(
|
|||
before_width: usize,
|
||||
result_width: usize,
|
||||
after_width: usize,
|
||||
has_tag_id: HasTagId,
|
||||
dec_result: Dec,
|
||||
) RocList {
|
||||
if (list.bytes) |source_ptr| {
|
||||
|
@ -479,11 +489,14 @@ pub fn listKeepResult(
|
|||
const before_element = source_ptr + (i * before_width);
|
||||
caller(data, before_element, temporary);
|
||||
|
||||
const result = utils.RocResult{ .bytes = temporary };
|
||||
|
||||
const after_element = temporary + @sizeOf(i64);
|
||||
if (is_good_constructor(result)) {
|
||||
@memcpy(target_ptr + (kept * after_width), after_element, after_width);
|
||||
// a record { matched: bool, data: ?[*]u8 }
|
||||
// for now, that data pointer is just the input `temporary` pointer
|
||||
// this will change in the future to only return a pointer to the
|
||||
// payload of the tag
|
||||
const answer = has_tag_id(good_constructor, temporary);
|
||||
if (answer.matched) {
|
||||
const contents = (answer.data orelse unreachable);
|
||||
@memcpy(target_ptr + (kept * after_width), contents, after_width);
|
||||
kept += 1;
|
||||
} else {
|
||||
dec_result(temporary);
|
||||
|
@ -606,7 +619,9 @@ pub fn listWalkUntil(
|
|||
accum: Opaque,
|
||||
alignment: u32,
|
||||
element_width: usize,
|
||||
continue_stop_width: usize,
|
||||
accum_width: usize,
|
||||
has_tag_id: HasTagId,
|
||||
dec: Dec,
|
||||
output: Opaque,
|
||||
) callconv(.C) void {
|
||||
|
@ -622,9 +637,10 @@ pub fn listWalkUntil(
|
|||
return;
|
||||
}
|
||||
|
||||
const bytes_ptr: [*]u8 = utils.alloc(TAG_WIDTH + accum_width, alignment);
|
||||
const bytes_ptr: [*]u8 = utils.alloc(continue_stop_width, alignment);
|
||||
|
||||
@memcpy(bytes_ptr + TAG_WIDTH, accum orelse unreachable, accum_width);
|
||||
// NOTE: assumes data bytes are the first bytes in a tag
|
||||
@memcpy(bytes_ptr, accum orelse unreachable, accum_width);
|
||||
|
||||
if (list.bytes) |source_ptr| {
|
||||
var i: usize = 0;
|
||||
|
@ -636,10 +652,12 @@ pub fn listWalkUntil(
|
|||
inc_n_data(data, 1);
|
||||
}
|
||||
|
||||
caller(data, element, bytes_ptr + TAG_WIDTH, bytes_ptr);
|
||||
caller(data, element, bytes_ptr, bytes_ptr);
|
||||
|
||||
const usizes: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes_ptr));
|
||||
if (usizes[0] != 0) {
|
||||
// [ Continue ..., Stop ]
|
||||
const tag_id = has_tag_id(0, bytes_ptr);
|
||||
|
||||
if (!tag_id.matched) {
|
||||
// decrement refcount of the remaining items
|
||||
i += 1;
|
||||
while (i < size) : (i += 1) {
|
||||
|
@ -650,7 +668,7 @@ pub fn listWalkUntil(
|
|||
}
|
||||
}
|
||||
|
||||
@memcpy(output orelse unreachable, bytes_ptr + TAG_WIDTH, accum_width);
|
||||
@memcpy(output orelse unreachable, bytes_ptr, accum_width);
|
||||
utils.dealloc(bytes_ptr, alignment);
|
||||
}
|
||||
|
||||
|
@ -1009,7 +1027,25 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt
|
|||
return output;
|
||||
}
|
||||
|
||||
// input: RocList,
|
||||
pub fn listSetInPlace(
|
||||
bytes: ?[*]u8,
|
||||
length: usize,
|
||||
alignment: u32,
|
||||
index: usize,
|
||||
element: Opaque,
|
||||
element_width: usize,
|
||||
dec: Dec,
|
||||
) callconv(.C) ?[*]u8 {
|
||||
// INVARIANT: bounds checking happens on the roc side
|
||||
//
|
||||
// at the time of writing, the function is implemented roughly as
|
||||
// `if inBounds then LowLevelListGet input index item else input`
|
||||
// so we don't do a bounds check here. Hence, the list is also non-empty,
|
||||
// because inserting into an empty list is always out of bounds
|
||||
|
||||
return listSetInPlaceHelp(bytes, length, alignment, index, element, element_width, dec);
|
||||
}
|
||||
|
||||
pub fn listSet(
|
||||
bytes: ?[*]u8,
|
||||
length: usize,
|
||||
|
@ -1028,23 +1064,34 @@ pub fn listSet(
|
|||
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes));
|
||||
|
||||
if ((ptr - 1)[0] == utils.REFCOUNT_ONE) {
|
||||
|
||||
// the element we will replace
|
||||
var element_at_index = (bytes orelse undefined) + (index * element_width);
|
||||
|
||||
// decrement its refcount
|
||||
dec(element_at_index);
|
||||
|
||||
// copy in the new element
|
||||
@memcpy(element_at_index, element orelse undefined, element_width);
|
||||
|
||||
return bytes;
|
||||
return listSetInPlaceHelp(bytes, length, alignment, index, element, element_width, dec);
|
||||
} else {
|
||||
return listSetClone(bytes, length, alignment, index, element, element_width, dec);
|
||||
return listSetImmutable(bytes, length, alignment, index, element, element_width, dec);
|
||||
}
|
||||
}
|
||||
|
||||
inline fn listSetClone(
|
||||
inline fn listSetInPlaceHelp(
|
||||
bytes: ?[*]u8,
|
||||
length: usize,
|
||||
alignment: u32,
|
||||
index: usize,
|
||||
element: Opaque,
|
||||
element_width: usize,
|
||||
dec: Dec,
|
||||
) ?[*]u8 {
|
||||
// the element we will replace
|
||||
var element_at_index = (bytes orelse undefined) + (index * element_width);
|
||||
|
||||
// decrement its refcount
|
||||
dec(element_at_index);
|
||||
|
||||
// copy in the new element
|
||||
@memcpy(element_at_index, element orelse undefined, element_width);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
inline fn listSetImmutable(
|
||||
old_bytes: ?[*]u8,
|
||||
length: usize,
|
||||
alignment: u32,
|
||||
|
@ -1053,8 +1100,6 @@ inline fn listSetClone(
|
|||
element_width: usize,
|
||||
dec: Dec,
|
||||
) ?[*]u8 {
|
||||
@setCold(true);
|
||||
|
||||
const data_bytes = length * element_width;
|
||||
|
||||
var new_bytes = utils.allocateWithRefcount(data_bytes, alignment);
|
||||
|
|
|
@ -41,6 +41,7 @@ comptime {
|
|||
exportListFn(list.listConcat, "concat");
|
||||
exportListFn(list.listDrop, "drop");
|
||||
exportListFn(list.listSet, "set");
|
||||
exportListFn(list.listSetInPlace, "set_in_place");
|
||||
exportListFn(list.listSwap, "swap");
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ const InPlace = packed enum(u8) {
|
|||
|
||||
const SMALL_STR_MAX_LENGTH = small_string_size - 1;
|
||||
const small_string_size = 2 * @sizeOf(usize);
|
||||
const blank_small_string: [16]u8 = init_blank_small_string(small_string_size);
|
||||
const blank_small_string: [@sizeOf(RocStr)]u8 = init_blank_small_string(small_string_size);
|
||||
|
||||
fn init_blank_small_string(comptime n: usize) [n]u8 {
|
||||
var prime_list: [n]u8 = undefined;
|
||||
|
@ -85,12 +85,6 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn toSlice(self: RocStr) []u8 {
|
||||
const str_bytes_ptr: [*]u8 = self.str_bytes orelse unreachable;
|
||||
const str_bytes: []u8 = str_bytes_ptr[0..self.str_len];
|
||||
return str_bytes;
|
||||
}
|
||||
|
||||
// This takes ownership of the pointed-to bytes if they won't fit in a
|
||||
// small string, and returns a (pointer, len) tuple which points to them.
|
||||
pub fn withCapacity(length: usize) RocStr {
|
||||
|
@ -203,8 +197,8 @@ pub const RocStr = extern struct {
|
|||
return result;
|
||||
}
|
||||
|
||||
// NOTE: returns false for empty string!
|
||||
pub fn isSmallStr(self: RocStr) bool {
|
||||
// NOTE: returns False for empty string!
|
||||
return @bitCast(isize, self.str_len) < 0;
|
||||
}
|
||||
|
||||
|
@ -223,6 +217,82 @@ pub const RocStr = extern struct {
|
|||
return self.len() == 0;
|
||||
}
|
||||
|
||||
// If a string happens to be null-terminated already, then we can pass its
|
||||
// bytes directly to functions (e.g. for opening files) that require
|
||||
// null-terminated strings. Otherwise, we need to allocate and copy a new
|
||||
// null-terminated string, which has a much higher performance cost!
|
||||
fn isNullTerminated(self: RocStr) bool {
|
||||
const len = self.len();
|
||||
const longest_small_str = @sizeOf(RocStr) - 1;
|
||||
|
||||
// NOTE: We want to compare length here, *NOT* check for is_small_str!
|
||||
// This is because we explicitly want the empty string to be handled in
|
||||
// this branch, even though the empty string is not a small string.
|
||||
//
|
||||
// (The other branch dereferences the bytes pointer, which is not safe
|
||||
// to do for the empty string.)
|
||||
if (len <= longest_small_str) {
|
||||
// If we're a small string, then usually the next byte after the
|
||||
// end of the string will be zero. (Small strings set all their
|
||||
// unused bytes to 0, so that comparison for equality can be fast.)
|
||||
//
|
||||
// However, empty strings are *not* null terminated, so if this is
|
||||
// empty, it should return false.
|
||||
//
|
||||
// Also, if we are exactly a maximum-length small string,
|
||||
// then the next byte is off the end of the struct;
|
||||
// in that case, we are also not null-terminated!
|
||||
return len != 0 and len != longest_small_str;
|
||||
} else {
|
||||
// This is a big string, and it's not empty, so we can safely
|
||||
// dereference the pointer.
|
||||
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, self.str_bytes));
|
||||
const capacity_or_refcount: isize = (ptr - 1)[0];
|
||||
|
||||
// If capacity_or_refcount is positive, then it's a capacity value.
|
||||
//
|
||||
// If we have excess capacity, then we can safely read the next
|
||||
// byte after the end of the string. Maybe it happens to be zero!
|
||||
if (capacity_or_refcount > @intCast(isize, len)) {
|
||||
return self.str_bytes[len] == 0;
|
||||
} else {
|
||||
// This string was refcounted or immortal; we can't safely read
|
||||
// the next byte, so assume the string is not null-terminated.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns (@sizeOf(RocStr) - 1) for small strings and the empty string.
|
||||
// Returns 0 for refcounted stirngs and immortal strings.
|
||||
// Returns the stored capacity value for all other strings.
|
||||
pub fn capacity(self: RocStr) usize {
|
||||
const len = self.len();
|
||||
const longest_small_str = @sizeOf(RocStr) - 1;
|
||||
|
||||
if (len <= longest_small_str) {
|
||||
// Note that although empty strings technically have the full
|
||||
// capacity of a small string available, they aren't marked as small
|
||||
// strings, so if you want to make use of that capacity, you need
|
||||
// to first change its flag to mark it as a small string!
|
||||
return longest_small_str;
|
||||
} else {
|
||||
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, self.str_bytes));
|
||||
const capacity_or_refcount: isize = (ptr - 1)[0];
|
||||
|
||||
if (capacity_or_refcount > 0) {
|
||||
// If capacity_or_refcount is positive, that means it's a
|
||||
// capacity value.
|
||||
return capacity_or_refcount;
|
||||
} else {
|
||||
// This is either a refcount or else this big string is stored
|
||||
// in a readonly section; either way, it has no capacity,
|
||||
// because we cannot mutate it in-place!
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isUnique(self: RocStr) bool {
|
||||
// the empty list is unique (in the sense that copying it will not leak memory)
|
||||
if (self.isEmpty()) {
|
||||
|
@ -240,15 +310,13 @@ pub const RocStr = extern struct {
|
|||
}
|
||||
|
||||
pub fn asSlice(self: RocStr) []u8 {
|
||||
// Since this conditional would be prone to branch misprediction,
|
||||
// make sure it will compile to a cmov.
|
||||
return self.asU8ptr()[0..self.len()];
|
||||
}
|
||||
|
||||
pub fn asU8ptr(self: RocStr) [*]u8 {
|
||||
// Since this conditional would be prone to branch misprediction,
|
||||
// make sure it will compile to a cmov.
|
||||
return if (self.isSmallStr() or self.isEmpty()) (&@bitCast([16]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
||||
return if (self.isSmallStr() or self.isEmpty()) (&@bitCast([@sizeOf(RocStr)]u8, self)) else (@ptrCast([*]u8, self.str_bytes));
|
||||
}
|
||||
|
||||
// Given a pointer to some bytes, write the first (len) bytes of this
|
||||
|
|
|
@ -65,6 +65,7 @@ pub const LIST_REVERSE: &str = "roc_builtins.list.reverse";
|
|||
pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with";
|
||||
pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
|
||||
pub const LIST_SET: &str = "roc_builtins.list.set";
|
||||
pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place";
|
||||
|
||||
pub const DEC_FROM_F64: &str = "roc_builtins.dec.from_f64";
|
||||
pub const DEC_EQ: &str = "roc_builtins.dec.eq";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/// Helpers for interacting with the zig that generates bitcode
|
||||
use crate::debug_info_init;
|
||||
use crate::llvm::build::{Env, C_CALL_CONV, FAST_CALL_CONV};
|
||||
use crate::llvm::build::{struct_from_fields, Env, C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX};
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
use crate::llvm::refcounting::{
|
||||
decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout,
|
||||
|
@ -10,7 +10,7 @@ use inkwell::types::{BasicType, BasicTypeEnum};
|
|||
use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
|
||||
use inkwell::AddressSpace;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Layout, LayoutIds};
|
||||
use roc_mono::layout::{Layout, LayoutIds, UnionLayout};
|
||||
|
||||
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
|
@ -66,6 +66,127 @@ const ARGUMENT_SYMBOLS: [Symbol; 8] = [
|
|||
Symbol::ARG_8,
|
||||
];
|
||||
|
||||
pub fn build_has_tag_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
function: FunctionValue<'ctx>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let fn_name: &str = &format!("{}_has_tag_id", function.get_name().to_string_lossy());
|
||||
|
||||
// currently the code assumes we're dealing with a non-recursive layout
|
||||
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_)));
|
||||
|
||||
match env.module.get_function(fn_name) {
|
||||
Some(function_value) => function_value,
|
||||
None => build_has_tag_id_help(env, union_layout, &fn_name),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_has_tag_id_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
fn_name: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
let i8_ptr_type = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
let argument_types: &[BasicTypeEnum] = &[env.context.i16_type().into(), i8_ptr_type.into()];
|
||||
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let output_type = crate::llvm::convert::zig_has_tag_id_type(env);
|
||||
|
||||
let function_value = crate::llvm::refcounting::build_header_help(
|
||||
env,
|
||||
&fn_name,
|
||||
output_type.into(),
|
||||
&argument_types,
|
||||
);
|
||||
|
||||
// called from zig, must use C calling convention
|
||||
function_value.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let kind_id = Attribute::get_named_enum_kind_id("alwaysinline");
|
||||
debug_assert!(kind_id > 0);
|
||||
let attr = env.context.create_enum_attribute(kind_id, 1);
|
||||
function_value.add_attribute(AttributeLoc::Function, attr);
|
||||
|
||||
let entry = env.context.append_basic_block(function_value, "entry");
|
||||
env.builder.position_at_end(entry);
|
||||
|
||||
debug_info_init!(env, function_value);
|
||||
|
||||
let it = function_value.get_param_iter();
|
||||
|
||||
let arguments =
|
||||
bumpalo::collections::Vec::from_iter_in(it.take(argument_types.len()), env.arena);
|
||||
|
||||
for (argument, name) in arguments.iter().zip(ARGUMENT_SYMBOLS.iter()) {
|
||||
argument.set_name(name.ident_string(&env.interns));
|
||||
}
|
||||
|
||||
match arguments.as_slice() {
|
||||
[tag_id, tag_value_ptr] => {
|
||||
let tag_type = basic_type_from_layout(env, &Layout::Union(union_layout));
|
||||
|
||||
let argument_cast = env
|
||||
.builder
|
||||
.build_bitcast(
|
||||
*tag_value_ptr,
|
||||
tag_type.ptr_type(AddressSpace::Generic),
|
||||
"load_opaque",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
let tag_value = env.builder.build_load(argument_cast, "get_value");
|
||||
|
||||
let actual_tag_id = {
|
||||
let tag_id_i64 =
|
||||
crate::llvm::build::get_tag_id(env, function_value, &union_layout, tag_value)
|
||||
.into_int_value();
|
||||
|
||||
env.builder
|
||||
.build_int_cast(tag_id_i64, env.context.i16_type(), "to_i16")
|
||||
};
|
||||
|
||||
let answer = env.builder.build_int_compare(
|
||||
inkwell::IntPredicate::EQ,
|
||||
tag_id.into_int_value(),
|
||||
actual_tag_id,
|
||||
"compare",
|
||||
);
|
||||
|
||||
let tag_data_ptr = {
|
||||
let data_index = env
|
||||
.context
|
||||
.i64_type()
|
||||
.const_int(TAG_DATA_INDEX as u64, false);
|
||||
|
||||
let ptr = unsafe {
|
||||
env.builder.build_gep(
|
||||
tag_value_ptr.into_pointer_value(),
|
||||
&[data_index],
|
||||
"get_data_ptr",
|
||||
)
|
||||
};
|
||||
env.builder.build_bitcast(ptr, i8_ptr_type, "to_opaque")
|
||||
};
|
||||
|
||||
let field_vals = [(0, answer.into()), (1, tag_data_ptr)];
|
||||
|
||||
let output = struct_from_fields(env, output_type, field_vals.iter().copied());
|
||||
|
||||
env.builder.build_return(Some(&output));
|
||||
|
||||
env.builder.position_at_end(block);
|
||||
env.builder
|
||||
.set_current_debug_location(env.context, di_location);
|
||||
|
||||
function_value
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_transform_caller<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
function: FunctionValue<'ctx>,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build_dict::{
|
||||
dict_contains, dict_difference, dict_empty, dict_get, dict_insert, dict_intersection,
|
||||
|
@ -42,7 +44,9 @@ use inkwell::values::{
|
|||
};
|
||||
use inkwell::OptimizationLevel;
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use morphic_lib::{CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions};
|
||||
use morphic_lib::{
|
||||
CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions, UpdateMode, UpdateModeVar,
|
||||
};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_collections::all::{ImMap, MutMap, MutSet};
|
||||
use roc_module::ident::TagName;
|
||||
|
@ -832,8 +836,21 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
|||
)
|
||||
}
|
||||
|
||||
CallType::LowLevel { op, update_mode: _ } => {
|
||||
run_low_level(env, layout_ids, scope, parent, layout, *op, arguments)
|
||||
CallType::LowLevel { op, update_mode } => {
|
||||
let bytes = update_mode.to_bytes();
|
||||
let update_var = UpdateModeVar(&bytes);
|
||||
let update_mode = func_spec_solutions.update_mode(update_var).ok();
|
||||
|
||||
run_low_level(
|
||||
env,
|
||||
layout_ids,
|
||||
scope,
|
||||
parent,
|
||||
layout,
|
||||
*op,
|
||||
arguments,
|
||||
update_mode,
|
||||
)
|
||||
}
|
||||
|
||||
CallType::HigherOrderLowLevel {
|
||||
|
@ -883,6 +900,32 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
pub const TAG_ID_INDEX: u32 = 1;
|
||||
pub const TAG_DATA_INDEX: u32 = 0;
|
||||
|
||||
pub fn struct_from_fields<'a, 'ctx, 'env, I>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
struct_type: StructType<'ctx>,
|
||||
values: I,
|
||||
) -> StructValue<'ctx>
|
||||
where
|
||||
I: Iterator<Item = (usize, BasicValueEnum<'ctx>)>,
|
||||
{
|
||||
let mut struct_value = struct_type.const_zero().into();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in values {
|
||||
let index: u32 = index as u32;
|
||||
|
||||
struct_value = env
|
||||
.builder
|
||||
.build_insert_value(struct_value, field_val, index, "insert_record_field")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
struct_value.into_struct_value()
|
||||
}
|
||||
|
||||
pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
@ -909,7 +952,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
Struct(sorted_fields) => {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Determine types
|
||||
let num_fields = sorted_fields.len();
|
||||
|
@ -929,67 +971,9 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
// Create the struct_type
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let mut struct_val = struct_type.const_zero().into();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, field_val, index as u32, "insert_record_field")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
||||
}
|
||||
|
||||
Tag {
|
||||
union_size,
|
||||
arguments,
|
||||
tag_layout,
|
||||
..
|
||||
} if *union_size == 1 && matches!(tag_layout, UnionLayout::NonRecursive(_)) => {
|
||||
let it = arguments.iter();
|
||||
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
// Determine types
|
||||
let num_fields = arguments.len() + 1;
|
||||
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||
|
||||
for field_symbol in it {
|
||||
let (val, field_layout) = load_symbol_and_layout(scope, field_symbol);
|
||||
if !field_layout.is_dropped_because_empty() {
|
||||
let field_type = basic_type_from_layout(env, &field_layout);
|
||||
|
||||
field_types.push(field_type);
|
||||
field_vals.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
// If the struct has only one field that isn't zero-sized,
|
||||
// unwrap it. This is what the layout expects us to do.
|
||||
if field_vals.len() == 1 {
|
||||
field_vals.pop().unwrap()
|
||||
} else {
|
||||
// Create the struct_type
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let mut struct_val = struct_type.const_zero().into();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_val,
|
||||
field_val,
|
||||
index as u32,
|
||||
"insert_single_tag_field",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
BasicValueEnum::StructValue(struct_val.into_struct_value())
|
||||
}
|
||||
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate()).into()
|
||||
}
|
||||
|
||||
Tag {
|
||||
|
@ -999,8 +983,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
tag_id,
|
||||
..
|
||||
} => {
|
||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
||||
|
||||
debug_assert!(*union_size > 1);
|
||||
|
||||
let ctx = env.context;
|
||||
|
@ -1038,19 +1020,10 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
// Create the struct_type
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let mut struct_val = struct_type.const_zero().into();
|
||||
|
||||
// Insert field exprs into struct_val
|
||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_val,
|
||||
field_val,
|
||||
index as u32,
|
||||
"insert_multi_tag_field",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
let struct_val =
|
||||
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate());
|
||||
|
||||
// How we create tag values
|
||||
//
|
||||
|
@ -1076,9 +1049,21 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
// This tricks comes from
|
||||
// https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116
|
||||
|
||||
let internal_type = basic_type_from_layout(env, &tag_layout);
|
||||
let internal_type = block_of_memory(env.context, layout, env.ptr_bytes);
|
||||
|
||||
cast_tag_to_block_of_memory(builder, struct_val.into_struct_value(), internal_type)
|
||||
let data = cast_tag_to_block_of_memory(builder, struct_val, internal_type);
|
||||
let wrapper_type = env
|
||||
.context
|
||||
.struct_type(&[data.get_type(), env.context.i64_type().into()], false);
|
||||
|
||||
let tag_id_intval = env.context.i64_type().const_int(*tag_id as u64, false);
|
||||
|
||||
let field_vals = [
|
||||
(TAG_ID_INDEX as usize, tag_id_intval.into()),
|
||||
(TAG_DATA_INDEX as usize, data),
|
||||
];
|
||||
|
||||
struct_from_fields(env, wrapper_type, field_vals.iter().copied()).into()
|
||||
}
|
||||
Tag {
|
||||
arguments,
|
||||
|
@ -1087,8 +1072,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
tag_id,
|
||||
..
|
||||
} => {
|
||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
||||
|
||||
debug_assert!(*union_size > 1);
|
||||
|
||||
let ctx = env.context;
|
||||
|
@ -1132,7 +1115,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
// Create the struct_type
|
||||
let data_ptr = reserve_with_refcount(env, &tag_layout);
|
||||
let data_ptr = reserve_with_refcount_union_as_block_of_memory(env, fields);
|
||||
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
|
@ -1166,9 +1150,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
debug_assert_eq!(*tag_id, 0);
|
||||
debug_assert_eq!(arguments.len(), fields.len());
|
||||
|
||||
let struct_layout =
|
||||
Layout::Union(UnionLayout::NonRecursive(env.arena.alloc([*fields])));
|
||||
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
|
@ -1207,7 +1188,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
// Create the struct_type
|
||||
let data_ptr = reserve_with_refcount(env, &struct_layout);
|
||||
let data_ptr = reserve_with_refcount_union_as_block_of_memory(env, &[fields]);
|
||||
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
|
@ -1241,8 +1223,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
tag_id,
|
||||
..
|
||||
} => {
|
||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
||||
let tag_struct_type = basic_type_from_layout(env, &tag_layout);
|
||||
let tag_struct_type = block_of_memory_slices(env.context, fields, env.ptr_bytes);
|
||||
if *tag_id == *nullable_id as u8 {
|
||||
let output_type = tag_struct_type.ptr_type(AddressSpace::Generic);
|
||||
|
||||
|
@ -1299,7 +1280,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
// Create the struct_type
|
||||
let data_ptr = reserve_with_refcount(env, &tag_layout);
|
||||
let data_ptr = reserve_with_refcount_union_as_block_of_memory(env, fields);
|
||||
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let struct_ptr = env
|
||||
.builder
|
||||
|
@ -1398,10 +1380,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
}
|
||||
|
||||
// Create the struct_type
|
||||
let data_ptr = reserve_with_refcount(
|
||||
env,
|
||||
&Layout::Union(UnionLayout::NonRecursive(&[other_fields])),
|
||||
);
|
||||
let data_ptr = reserve_with_refcount_union_as_block_of_memory(env, &[other_fields]);
|
||||
|
||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||
let struct_ptr = env
|
||||
|
@ -1506,6 +1485,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
|
||||
match union_layout {
|
||||
UnionLayout::NonRecursive(tag_layouts) => {
|
||||
let index = *index - 1;
|
||||
|
||||
debug_assert!(argument.is_struct_value());
|
||||
let field_layouts = tag_layouts[*tag_id as usize];
|
||||
let struct_layout = Layout::Struct(field_layouts);
|
||||
|
@ -1519,7 +1500,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
);
|
||||
|
||||
let result = builder
|
||||
.build_extract_value(struct_value, *index as u32, "")
|
||||
.build_extract_value(struct_value, index as u32, "")
|
||||
.expect("desired field did not decode");
|
||||
|
||||
result
|
||||
|
@ -1611,77 +1592,82 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
structure,
|
||||
union_layout,
|
||||
} => {
|
||||
let builder = env.builder;
|
||||
|
||||
// cast the argument bytes into the desired shape for this tag
|
||||
let (argument, _structure_layout) = load_symbol_and_layout(scope, structure);
|
||||
|
||||
match union_layout {
|
||||
UnionLayout::NonRecursive(_) => {
|
||||
let pointer = builder.build_alloca(argument.get_type(), "get_type");
|
||||
builder.build_store(pointer, argument);
|
||||
let tag_id_pointer = builder.build_bitcast(
|
||||
pointer,
|
||||
env.context.i64_type().ptr_type(AddressSpace::Generic),
|
||||
"tag_id_pointer",
|
||||
);
|
||||
builder.build_load(tag_id_pointer.into_pointer_value(), "load_tag_id")
|
||||
}
|
||||
UnionLayout::Recursive(_) => {
|
||||
let pointer = argument.into_pointer_value();
|
||||
let tag_id_pointer = builder.build_bitcast(
|
||||
pointer,
|
||||
env.context.i64_type().ptr_type(AddressSpace::Generic),
|
||||
"tag_id_pointer",
|
||||
);
|
||||
builder.build_load(tag_id_pointer.into_pointer_value(), "load_tag_id")
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped(_) => env.context.i64_type().const_zero().into(),
|
||||
UnionLayout::NullableWrapped { nullable_id, .. } => {
|
||||
let argument_ptr = argument.into_pointer_value();
|
||||
let is_null = env.builder.build_is_null(argument_ptr, "is_null");
|
||||
get_tag_id(env, parent, union_layout, argument)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ctx = env.context;
|
||||
let then_block = ctx.append_basic_block(parent, "then");
|
||||
let else_block = ctx.append_basic_block(parent, "else");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
||||
pub fn get_tag_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
union_layout: &UnionLayout<'a>,
|
||||
argument: BasicValueEnum<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
match union_layout {
|
||||
UnionLayout::NonRecursive(_) => {
|
||||
let tag = argument.into_struct_value();
|
||||
|
||||
let result = builder.build_alloca(ctx.i64_type(), "result");
|
||||
builder
|
||||
.build_extract_value(tag, TAG_ID_INDEX, "get_tag_id")
|
||||
.unwrap()
|
||||
}
|
||||
UnionLayout::Recursive(_) => {
|
||||
let pointer = argument.into_pointer_value();
|
||||
let tag_id_pointer = builder.build_bitcast(
|
||||
pointer,
|
||||
env.context.i64_type().ptr_type(AddressSpace::Generic),
|
||||
"tag_id_pointer",
|
||||
);
|
||||
builder.build_load(tag_id_pointer.into_pointer_value(), "load_tag_id")
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped(_) => env.context.i64_type().const_zero().into(),
|
||||
UnionLayout::NullableWrapped { nullable_id, .. } => {
|
||||
let argument_ptr = argument.into_pointer_value();
|
||||
let is_null = env.builder.build_is_null(argument_ptr, "is_null");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_null, then_block, else_block);
|
||||
let ctx = env.context;
|
||||
let then_block = ctx.append_basic_block(parent, "then");
|
||||
let else_block = ctx.append_basic_block(parent, "else");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
||||
|
||||
{
|
||||
env.builder.position_at_end(then_block);
|
||||
let tag_id = ctx.i64_type().const_int(*nullable_id as u64, false);
|
||||
env.builder.build_store(result, tag_id);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
let result = builder.build_alloca(ctx.i64_type(), "result");
|
||||
|
||||
{
|
||||
env.builder.position_at_end(else_block);
|
||||
let tag_id = extract_tag_discriminant_ptr(env, argument_ptr);
|
||||
env.builder.build_store(result, tag_id);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
env.builder
|
||||
.build_conditional_branch(is_null, then_block, else_block);
|
||||
|
||||
env.builder.position_at_end(cont_block);
|
||||
|
||||
env.builder.build_load(result, "load_result")
|
||||
}
|
||||
UnionLayout::NullableUnwrapped { nullable_id, .. } => {
|
||||
let argument_ptr = argument.into_pointer_value();
|
||||
let is_null = env.builder.build_is_null(argument_ptr, "is_null");
|
||||
|
||||
let ctx = env.context;
|
||||
|
||||
let then_value = ctx.i64_type().const_int(*nullable_id as u64, false);
|
||||
let else_value = ctx.i64_type().const_int(!*nullable_id as u64, false);
|
||||
|
||||
env.builder
|
||||
.build_select(is_null, then_value, else_value, "select_tag_id")
|
||||
}
|
||||
{
|
||||
env.builder.position_at_end(then_block);
|
||||
let tag_id = ctx.i64_type().const_int(*nullable_id as u64, false);
|
||||
env.builder.build_store(result, tag_id);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(else_block);
|
||||
let tag_id = extract_tag_discriminant_ptr(env, argument_ptr);
|
||||
env.builder.build_store(result, tag_id);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
env.builder.position_at_end(cont_block);
|
||||
|
||||
env.builder.build_load(result, "load_result")
|
||||
}
|
||||
UnionLayout::NullableUnwrapped { nullable_id, .. } => {
|
||||
let argument_ptr = argument.into_pointer_value();
|
||||
let is_null = env.builder.build_is_null(argument_ptr, "is_null");
|
||||
|
||||
let ctx = env.context;
|
||||
|
||||
let then_value = ctx.i64_type().const_int(*nullable_id as u64, false);
|
||||
let else_value = ctx.i64_type().const_int(!*nullable_id as u64, false);
|
||||
|
||||
env.builder
|
||||
.build_select(is_null, then_value, else_value, "select_tag_id")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1728,17 +1714,52 @@ fn lookup_at_index_ptr<'a, 'ctx, 'env>(
|
|||
pub fn reserve_with_refcount<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'a>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let stack_size = layout.stack_size(env.ptr_bytes);
|
||||
let alignment_bytes = layout.alignment_bytes(env.ptr_bytes);
|
||||
|
||||
let basic_type = basic_type_from_layout(env, layout);
|
||||
|
||||
reserve_with_refcount_help(env, basic_type, stack_size, alignment_bytes)
|
||||
}
|
||||
|
||||
fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
fields: &[&[Layout<'a>]],
|
||||
) -> PointerValue<'ctx> {
|
||||
let basic_type = block_of_memory_slices(env.context, fields, env.ptr_bytes);
|
||||
|
||||
let stack_size = fields
|
||||
.iter()
|
||||
.map(|tag| tag.iter().map(|l| l.stack_size(env.ptr_bytes)).sum())
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
let alignment_bytes = fields
|
||||
.iter()
|
||||
.map(|tag| tag.iter().map(|l| l.alignment_bytes(env.ptr_bytes)))
|
||||
.flatten()
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
reserve_with_refcount_help(env, basic_type, stack_size, alignment_bytes)
|
||||
}
|
||||
|
||||
fn reserve_with_refcount_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
basic_type: impl BasicType<'ctx>,
|
||||
stack_size: u32,
|
||||
alignment_bytes: u32,
|
||||
) -> PointerValue<'ctx> {
|
||||
let ctx = env.context;
|
||||
|
||||
let len_type = env.ptr_int();
|
||||
|
||||
let value_bytes = layout.stack_size(env.ptr_bytes);
|
||||
let value_bytes_intvalue = len_type.const_int(value_bytes as u64, false);
|
||||
let value_bytes_intvalue = len_type.const_int(stack_size as u64, false);
|
||||
|
||||
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes);
|
||||
|
||||
allocate_with_refcount_help(env, layout, value_bytes_intvalue, rc1)
|
||||
allocate_with_refcount_help(env, basic_type, alignment_bytes, value_bytes_intvalue, rc1)
|
||||
}
|
||||
|
||||
pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
||||
|
@ -1756,17 +1777,17 @@ pub fn allocate_with_refcount<'a, 'ctx, 'env>(
|
|||
|
||||
pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout: &Layout<'a>,
|
||||
value_type: impl BasicType<'ctx>,
|
||||
alignment_bytes: u32,
|
||||
number_of_data_bytes: IntValue<'ctx>,
|
||||
initial_refcount: IntValue<'ctx>,
|
||||
) -> PointerValue<'ctx> {
|
||||
let builder = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
let value_type = basic_type_from_layout(env, layout);
|
||||
let len_type = env.ptr_int();
|
||||
|
||||
let extra_bytes = layout.alignment_bytes(env.ptr_bytes).max(env.ptr_bytes);
|
||||
let extra_bytes = alignment_bytes.max(env.ptr_bytes);
|
||||
|
||||
let ptr = {
|
||||
// number of bytes we will allocated
|
||||
|
@ -1776,7 +1797,7 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
|||
"add_extra_bytes",
|
||||
);
|
||||
|
||||
env.call_alloc(number_of_bytes, layout.alignment_bytes(env.ptr_bytes))
|
||||
env.call_alloc(number_of_bytes, alignment_bytes)
|
||||
};
|
||||
|
||||
// We must return a pointer to the first element:
|
||||
|
@ -2478,25 +2499,87 @@ pub fn complex_bitcast<'ctx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_tag_discriminant_struct<'a, 'ctx, 'env>(
|
||||
pub fn extract_tag_discriminant<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
from_value: StructValue<'ctx>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
union_layout: UnionLayout<'a>,
|
||||
cond_value: BasicValueEnum<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let struct_type = env
|
||||
.context
|
||||
.struct_type(&[env.context.i64_type().into()], false);
|
||||
let builder = env.builder;
|
||||
|
||||
let struct_value = complex_bitcast_struct_struct(
|
||||
env.builder,
|
||||
from_value,
|
||||
struct_type,
|
||||
"extract_tag_discriminant_struct",
|
||||
);
|
||||
match union_layout {
|
||||
UnionLayout::NonRecursive(_) => {
|
||||
let pointer = builder.build_alloca(cond_value.get_type(), "get_type");
|
||||
builder.build_store(pointer, cond_value);
|
||||
let tag_id_pointer = builder.build_bitcast(
|
||||
pointer,
|
||||
env.context.i64_type().ptr_type(AddressSpace::Generic),
|
||||
"tag_id_pointer",
|
||||
);
|
||||
builder
|
||||
.build_load(tag_id_pointer.into_pointer_value(), "load_tag_id")
|
||||
.into_int_value()
|
||||
}
|
||||
UnionLayout::Recursive(_) => {
|
||||
let pointer = cond_value.into_pointer_value();
|
||||
let tag_id_pointer = builder.build_bitcast(
|
||||
pointer,
|
||||
env.context.i64_type().ptr_type(AddressSpace::Generic),
|
||||
"tag_id_pointer",
|
||||
);
|
||||
builder
|
||||
.build_load(tag_id_pointer.into_pointer_value(), "load_tag_id")
|
||||
.into_int_value()
|
||||
}
|
||||
UnionLayout::NonNullableUnwrapped(_) => env.context.i64_type().const_zero(),
|
||||
UnionLayout::NullableWrapped { nullable_id, .. } => {
|
||||
let argument_ptr = cond_value.into_pointer_value();
|
||||
let is_null = env.builder.build_is_null(argument_ptr, "is_null");
|
||||
|
||||
env.builder
|
||||
.build_extract_value(struct_value, 0, "")
|
||||
.expect("desired field did not decode")
|
||||
.into_int_value()
|
||||
let ctx = env.context;
|
||||
let then_block = ctx.append_basic_block(parent, "then");
|
||||
let else_block = ctx.append_basic_block(parent, "else");
|
||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
||||
|
||||
let result = builder.build_alloca(ctx.i64_type(), "result");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(is_null, then_block, else_block);
|
||||
|
||||
{
|
||||
env.builder.position_at_end(then_block);
|
||||
let tag_id = ctx.i64_type().const_int(nullable_id as u64, false);
|
||||
env.builder.build_store(result, tag_id);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
{
|
||||
env.builder.position_at_end(else_block);
|
||||
let tag_id = extract_tag_discriminant_ptr(env, argument_ptr);
|
||||
env.builder.build_store(result, tag_id);
|
||||
env.builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
env.builder.position_at_end(cont_block);
|
||||
|
||||
env.builder
|
||||
.build_load(result, "load_result")
|
||||
.into_int_value()
|
||||
}
|
||||
UnionLayout::NullableUnwrapped { nullable_id, .. } => {
|
||||
let argument_ptr = cond_value.into_pointer_value();
|
||||
let is_null = env.builder.build_is_null(argument_ptr, "is_null");
|
||||
|
||||
let ctx = env.context;
|
||||
|
||||
let then_value = ctx.i64_type().const_int(nullable_id as u64, false);
|
||||
let else_value = ctx.i64_type().const_int(!nullable_id as u64, false);
|
||||
|
||||
env.builder
|
||||
.build_select(is_null, then_value, else_value, "select_tag_id")
|
||||
.into_int_value()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_tag_discriminant_ptr<'a, 'ctx, 'env>(
|
||||
|
@ -2590,57 +2673,9 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
|||
.into_int_value()
|
||||
}
|
||||
Layout::Union(variant) => {
|
||||
use UnionLayout::*;
|
||||
cond_layout = Layout::Builtin(Builtin::Int64);
|
||||
|
||||
match variant {
|
||||
NonRecursive(_) => {
|
||||
// we match on the discriminant, not the whole Tag
|
||||
cond_layout = Layout::Builtin(Builtin::Int64);
|
||||
let full_cond = cond_value.into_struct_value();
|
||||
|
||||
extract_tag_discriminant_struct(env, full_cond)
|
||||
}
|
||||
Recursive(_) => {
|
||||
// we match on the discriminant, not the whole Tag
|
||||
cond_layout = Layout::Builtin(Builtin::Int64);
|
||||
|
||||
debug_assert!(cond_value.is_pointer_value());
|
||||
extract_tag_discriminant_ptr(env, cond_value.into_pointer_value())
|
||||
}
|
||||
NonNullableUnwrapped(_) => unreachable!("there is no tag to switch on"),
|
||||
NullableWrapped { nullable_id, .. } => {
|
||||
// we match on the discriminant, not the whole Tag
|
||||
cond_layout = Layout::Builtin(Builtin::Int64);
|
||||
let full_cond_ptr = cond_value.into_pointer_value();
|
||||
|
||||
let comparison: IntValue =
|
||||
env.builder.build_is_null(full_cond_ptr, "is_null_cond");
|
||||
|
||||
let when_null = || {
|
||||
env.context
|
||||
.i64_type()
|
||||
.const_int(nullable_id as u64, false)
|
||||
.into()
|
||||
};
|
||||
let when_not_null = || extract_tag_discriminant_ptr(env, full_cond_ptr).into();
|
||||
|
||||
crate::llvm::build_list::build_basic_phi2(
|
||||
env,
|
||||
parent,
|
||||
comparison,
|
||||
when_null,
|
||||
when_not_null,
|
||||
BasicTypeEnum::IntType(env.context.i64_type()),
|
||||
)
|
||||
.into_int_value()
|
||||
}
|
||||
NullableUnwrapped { .. } => {
|
||||
// there are only two options, so we do a `tag_id == 0` check and branch on that
|
||||
unreachable!(
|
||||
"we never switch on the tag id directly for NullableUnwrapped unions"
|
||||
)
|
||||
}
|
||||
}
|
||||
extract_tag_discriminant(env, parent, variant, cond_value)
|
||||
}
|
||||
Layout::Builtin(_) => cond_value.into_int_value(),
|
||||
other => todo!("Build switch value from layout: {:?}", other),
|
||||
|
@ -3187,8 +3222,9 @@ pub fn build_procedures<'a, 'ctx, 'env>(
|
|||
opt_level: OptLevel,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||
entry_point: EntryPoint<'a>,
|
||||
debug_output_file: Option<&Path>,
|
||||
) {
|
||||
build_procedures_help(env, opt_level, procedures, entry_point);
|
||||
build_procedures_help(env, opt_level, procedures, entry_point, debug_output_file);
|
||||
}
|
||||
|
||||
pub fn build_procedures_return_main<'a, 'ctx, 'env>(
|
||||
|
@ -3197,7 +3233,7 @@ pub fn build_procedures_return_main<'a, 'ctx, 'env>(
|
|||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||
entry_point: EntryPoint<'a>,
|
||||
) -> (&'static str, FunctionValue<'ctx>) {
|
||||
let mod_solutions = build_procedures_help(env, opt_level, procedures, entry_point);
|
||||
let mod_solutions = build_procedures_help(env, opt_level, procedures, entry_point, None);
|
||||
|
||||
promote_to_main_function(env, mod_solutions, entry_point.symbol, entry_point.layout)
|
||||
}
|
||||
|
@ -3207,6 +3243,7 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
|||
opt_level: OptLevel,
|
||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||
entry_point: EntryPoint<'a>,
|
||||
debug_output_file: Option<&Path>,
|
||||
) -> &'a ModSolutions {
|
||||
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||
let mut scope = Scope::default();
|
||||
|
@ -3265,13 +3302,22 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
|||
);
|
||||
|
||||
fn_val.print_to_stderr();
|
||||
// module.print_to_stderr();
|
||||
|
||||
panic!(
|
||||
if let Some(app_ll_file) = debug_output_file {
|
||||
env.module.print_to_file(&app_ll_file).unwrap();
|
||||
|
||||
panic!(
|
||||
r"😱 LLVM errors when defining function {:?}; I wrote the full LLVM IR to {:?}",
|
||||
fn_val.get_name().to_str().unwrap(),
|
||||
app_ll_file,
|
||||
);
|
||||
} else {
|
||||
panic!(
|
||||
"The preceding code was from {:?}, which failed LLVM verification in {} build.",
|
||||
fn_val.get_name().to_str().unwrap(),
|
||||
fn_val.get_name().to_str().unwrap(),
|
||||
mode,
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3803,6 +3849,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
|||
env,
|
||||
layout_ids,
|
||||
roc_function_call,
|
||||
result_layout,
|
||||
list,
|
||||
element_layout,
|
||||
default,
|
||||
|
@ -4196,6 +4243,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
layout: &Layout<'a>,
|
||||
op: LowLevel,
|
||||
args: &[Symbol],
|
||||
update_mode: Option<UpdateMode>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use LowLevel::*;
|
||||
|
||||
|
@ -4654,27 +4702,6 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
wrapper_struct,
|
||||
)
|
||||
}
|
||||
ListSetInPlace => {
|
||||
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||
let (index, _) = load_symbol_and_layout(scope, &args[1]);
|
||||
let (element, _) = load_symbol_and_layout(scope, &args[2]);
|
||||
|
||||
match list_layout {
|
||||
Layout::Builtin(Builtin::EmptyList) => {
|
||||
// no elements, so nothing to remove
|
||||
empty_list(env)
|
||||
}
|
||||
Layout::Builtin(Builtin::List(element_layout)) => list_set(
|
||||
env,
|
||||
layout_ids,
|
||||
list,
|
||||
index.into_int_value(),
|
||||
element,
|
||||
element_layout,
|
||||
),
|
||||
_ => unreachable!("invalid dict layout"),
|
||||
}
|
||||
}
|
||||
ListSet => {
|
||||
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||
let (index, _) = load_symbol_and_layout(scope, &args[1]);
|
||||
|
@ -4692,6 +4719,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
index.into_int_value(),
|
||||
element,
|
||||
element_layout,
|
||||
update_mode.unwrap(),
|
||||
),
|
||||
_ => unreachable!("invalid dict layout"),
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(clippy::too_many_arguments)]
|
||||
use crate::llvm::bitcode::{
|
||||
build_dec_wrapper, build_eq_wrapper, build_inc_n_wrapper, build_inc_wrapper, call_bitcode_fn,
|
||||
call_void_bitcode_fn,
|
||||
build_dec_wrapper, build_eq_wrapper, build_has_tag_id, build_inc_n_wrapper, build_inc_wrapper,
|
||||
call_bitcode_fn, call_void_bitcode_fn,
|
||||
};
|
||||
use crate::llvm::build::{
|
||||
allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, RocFunctionCall,
|
||||
|
@ -13,6 +13,7 @@ use inkwell::context::Context;
|
|||
use inkwell::types::{BasicType, BasicTypeEnum, PointerType};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use morphic_lib::UpdateMode;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||
|
||||
|
@ -350,6 +351,7 @@ pub fn list_set<'a, 'ctx, 'env>(
|
|||
index: IntValue<'ctx>,
|
||||
element: BasicValueEnum<'ctx>,
|
||||
element_layout: &'a Layout<'a>,
|
||||
update_mode: UpdateMode,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
|
||||
|
||||
|
@ -359,6 +361,11 @@ pub fn list_set<'a, 'ctx, 'env>(
|
|||
env.context.i8_type().ptr_type(AddressSpace::Generic),
|
||||
);
|
||||
|
||||
let symbol = match update_mode {
|
||||
UpdateMode::InPlace => bitcode::LIST_SET_IN_PLACE,
|
||||
UpdateMode::Immutable => bitcode::LIST_SET,
|
||||
};
|
||||
|
||||
let new_bytes = call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
|
@ -370,7 +377,7 @@ pub fn list_set<'a, 'ctx, 'env>(
|
|||
layout_width(env, element_layout),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
&bitcode::LIST_SET,
|
||||
&symbol,
|
||||
);
|
||||
|
||||
store_list(env, new_bytes.into_pointer_value(), length)
|
||||
|
@ -410,6 +417,7 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
|
|||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
roc_function_call: RocFunctionCall<'ctx>,
|
||||
function_call_return_layout: &Layout<'a>,
|
||||
list: BasicValueEnum<'ctx>,
|
||||
element_layout: &Layout<'a>,
|
||||
default: BasicValueEnum<'ctx>,
|
||||
|
@ -450,6 +458,18 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
|
|||
);
|
||||
}
|
||||
ListWalk::WalkUntil | ListWalk::WalkBackwardsUntil => {
|
||||
let function = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.unwrap()
|
||||
.get_parent()
|
||||
.unwrap();
|
||||
|
||||
let has_tag_id = match function_call_return_layout {
|
||||
Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
|
||||
call_void_bitcode_fn(
|
||||
env,
|
||||
|
@ -462,7 +482,9 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
|
|||
pass_as_opaque(env, default_ptr),
|
||||
env.alignment_intvalue(&element_layout),
|
||||
layout_width(env, element_layout),
|
||||
layout_width(env, function_call_return_layout),
|
||||
layout_width(env, default_layout),
|
||||
has_tag_id.as_global_value().as_pointer_value().into(),
|
||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||
pass_as_opaque(env, result_ptr),
|
||||
],
|
||||
|
@ -604,6 +626,18 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
|
|||
) -> BasicValueEnum<'ctx> {
|
||||
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
||||
|
||||
let function = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.unwrap()
|
||||
.get_parent()
|
||||
.unwrap();
|
||||
|
||||
let has_tag_id = match result_layout {
|
||||
Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
|
@ -616,6 +650,7 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
|
|||
layout_width(env, before_layout),
|
||||
layout_width(env, result_layout),
|
||||
layout_width(env, after_layout),
|
||||
has_tag_id.as_global_value().as_pointer_value().into(),
|
||||
dec_result_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_KEEP_OKS,
|
||||
|
@ -635,6 +670,18 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
|
|||
) -> BasicValueEnum<'ctx> {
|
||||
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
||||
|
||||
let function = env
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.unwrap()
|
||||
.get_parent()
|
||||
.unwrap();
|
||||
|
||||
let has_tag_id = match result_layout {
|
||||
Layout::Union(union_layout) => build_has_tag_id(env, function, *union_layout),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
call_bitcode_fn(
|
||||
env,
|
||||
&[
|
||||
|
@ -647,6 +694,7 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
|
|||
layout_width(env, before_layout),
|
||||
layout_width(env, result_layout),
|
||||
layout_width(env, after_layout),
|
||||
has_tag_id.as_global_value().as_pointer_value().into(),
|
||||
dec_result_fn.as_global_value().as_pointer_value().into(),
|
||||
],
|
||||
bitcode::LIST_KEEP_ERRS,
|
||||
|
@ -1080,7 +1128,9 @@ pub fn allocate_list<'a, 'ctx, 'env>(
|
|||
// we assume that the list is indeed used (dead variables are eliminated)
|
||||
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes);
|
||||
|
||||
allocate_with_refcount_help(env, elem_layout, number_of_data_bytes, rc1)
|
||||
let basic_type = basic_type_from_layout(env, elem_layout);
|
||||
let alignment_bytes = elem_layout.alignment_bytes(env.ptr_bytes);
|
||||
allocate_with_refcount_help(env, basic_type, alignment_bytes, number_of_data_bytes, rc1)
|
||||
}
|
||||
|
||||
pub fn store_list<'a, 'ctx, 'env>(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::llvm::bitcode::call_bitcode_fn;
|
||||
use crate::llvm::build::Env;
|
||||
use crate::llvm::build::{cast_block_of_memory_to_tag, complex_bitcast, FAST_CALL_CONV};
|
||||
use crate::llvm::build::{cast_block_of_memory_to_tag, FAST_CALL_CONV};
|
||||
use crate::llvm::build_list::{list_len, load_list_ptr};
|
||||
use crate::llvm::build_str::str_equal;
|
||||
use crate::llvm::convert::basic_type_from_layout;
|
||||
|
@ -850,9 +850,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
|||
|
||||
match union_layout {
|
||||
NonRecursive(tags) => {
|
||||
// SAFETY we know that non-recursive tags cannot be NULL
|
||||
let id1 = nonrec_tag_id(env, tag1.into_struct_value());
|
||||
let id2 = nonrec_tag_id(env, tag2.into_struct_value());
|
||||
let id1 =
|
||||
crate::llvm::build::get_tag_id(env, parent, union_layout, tag1).into_int_value();
|
||||
let id2 =
|
||||
crate::llvm::build::get_tag_id(env, parent, union_layout, tag2).into_int_value();
|
||||
|
||||
let compare_tag_fields = ctx.append_basic_block(parent, "compare_tag_fields");
|
||||
|
||||
|
@ -1216,19 +1217,6 @@ fn eq_ptr_to_struct<'a, 'ctx, 'env>(
|
|||
.into_int_value()
|
||||
}
|
||||
|
||||
fn nonrec_tag_id<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
tag: StructValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
complex_bitcast(
|
||||
env.builder,
|
||||
tag.into(),
|
||||
env.context.i64_type().into(),
|
||||
"load_tag_id",
|
||||
)
|
||||
.into_int_value()
|
||||
}
|
||||
|
||||
unsafe fn rec_tag_id_unsafe<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
tag: PointerValue<'ctx>,
|
||||
|
|
|
@ -50,7 +50,13 @@ pub fn basic_type_from_layout<'a, 'ctx, 'env>(
|
|||
let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes);
|
||||
block.ptr_type(AddressSpace::Generic).into()
|
||||
}
|
||||
NonRecursive(_) => block_of_memory(env.context, layout, env.ptr_bytes),
|
||||
NonRecursive(_) => {
|
||||
let data = block_of_memory(env.context, layout, env.ptr_bytes);
|
||||
|
||||
env.context
|
||||
.struct_type(&[data, env.context.i64_type().into()], false)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
RecursivePointer => {
|
||||
|
@ -118,7 +124,11 @@ pub fn block_of_memory<'ctx>(
|
|||
ptr_bytes: u32,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
// TODO make this dynamic
|
||||
let union_size = layout.stack_size(ptr_bytes as u32);
|
||||
let mut union_size = layout.stack_size(ptr_bytes as u32);
|
||||
|
||||
if let Layout::Union(UnionLayout::NonRecursive { .. }) = layout {
|
||||
union_size -= ptr_bytes;
|
||||
}
|
||||
|
||||
block_of_memory_help(context, union_size)
|
||||
}
|
||||
|
@ -182,8 +192,8 @@ pub fn zig_str_type<'a, 'ctx, 'env>(
|
|||
env.module.get_struct_type("str.RocStr").unwrap()
|
||||
}
|
||||
|
||||
// pub fn zig_dec_type<'a, 'ctx, 'env>(
|
||||
// env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||
// ) -> StructType<'ctx> {
|
||||
// env.module.get_struct_type("dec.RocDec").unwrap()
|
||||
// }
|
||||
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(
|
||||
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||
) -> StructType<'ctx> {
|
||||
env.module.get_struct_type("list.HasTagId").unwrap()
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use crate::debug_info_init;
|
||||
use crate::llvm::build::{
|
||||
add_func, cast_basic_basic, cast_block_of_memory_to_tag, Env, FAST_CALL_CONV,
|
||||
LLVM_SADD_WITH_OVERFLOW_I64,
|
||||
LLVM_SADD_WITH_OVERFLOW_I64, TAG_DATA_INDEX, TAG_ID_INDEX,
|
||||
};
|
||||
use crate::llvm::build_list::{incrementing_elem_loop, list_len, load_list};
|
||||
use crate::llvm::convert::{
|
||||
basic_type_from_layout, block_of_memory, block_of_memory_slices, ptr_int,
|
||||
};
|
||||
use crate::llvm::convert::{basic_type_from_layout, block_of_memory_slices, ptr_int};
|
||||
use bumpalo::collections::Vec;
|
||||
use inkwell::basic_block::BasicBlock;
|
||||
use inkwell::context::Context;
|
||||
|
@ -1584,7 +1582,7 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
|
|||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
None => {
|
||||
let basic_type = block_of_memory(env.context, &layout, env.ptr_bytes);
|
||||
let basic_type = basic_type_from_layout(env, &layout);
|
||||
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||
|
||||
modify_refcount_union_help(
|
||||
|
@ -1640,19 +1638,11 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
|||
let wrapper_struct = arg_val.into_struct_value();
|
||||
|
||||
// read the tag_id
|
||||
let tag_id = {
|
||||
// the first element of the wrapping struct is an array of i64
|
||||
let first_array = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, 0, "read_tag_id")
|
||||
.unwrap()
|
||||
.into_array_value();
|
||||
|
||||
env.builder
|
||||
.build_extract_value(first_array, 0, "read_tag_id_2")
|
||||
.unwrap()
|
||||
.into_int_value()
|
||||
};
|
||||
let tag_id = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, TAG_ID_INDEX, "read_tag_id")
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
let tag_id_u8 = env
|
||||
.builder
|
||||
|
@ -1680,7 +1670,12 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
|||
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
|
||||
|
||||
debug_assert!(wrapper_type.is_struct_type());
|
||||
let wrapper_struct = cast_block_of_memory_to_tag(env.builder, wrapper_struct, wrapper_type);
|
||||
let data_bytes = env
|
||||
.builder
|
||||
.build_extract_value(wrapper_struct, TAG_DATA_INDEX, "read_tag_id")
|
||||
.unwrap()
|
||||
.into_struct_value();
|
||||
let wrapper_struct = cast_block_of_memory_to_tag(env.builder, data_bytes, wrapper_type);
|
||||
|
||||
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||
if let Layout::RecursivePointer = field_layout {
|
||||
|
|
|
@ -18,7 +18,6 @@ pub enum LowLevel {
|
|||
ListLen,
|
||||
ListGetUnsafe,
|
||||
ListSet,
|
||||
ListSetInPlace,
|
||||
ListSingle,
|
||||
ListRepeat,
|
||||
ListReverse,
|
||||
|
@ -125,7 +124,6 @@ impl LowLevel {
|
|||
| ListLen
|
||||
| ListGetUnsafe
|
||||
| ListSet
|
||||
| ListSetInPlace
|
||||
| ListDrop
|
||||
| ListSingle
|
||||
| ListRepeat
|
||||
|
|
|
@ -861,11 +861,13 @@ fn expr_spec(
|
|||
union_layout,
|
||||
} => match union_layout {
|
||||
UnionLayout::NonRecursive(_) => {
|
||||
// let index = (*index - 1) as u32;
|
||||
let index = (*index - 1) as u32;
|
||||
let tag_value_id = env.symbols[structure];
|
||||
let tuple_value_id =
|
||||
builder.add_unwrap_union(block, tag_value_id, *tag_id as u32)?;
|
||||
|
||||
builder.add_get_tuple_field(block, tuple_value_id, *index as u32)
|
||||
builder.add_get_tuple_field(block, tuple_value_id, index)
|
||||
}
|
||||
_ => {
|
||||
// for the moment recursive tag unions don't quite work
|
||||
|
|
|
@ -760,7 +760,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
match op {
|
||||
ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]),
|
||||
ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||
ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||
ListConcat => arena.alloc_slice_copy(&[owned, owned]),
|
||||
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||
|
|
|
@ -2083,18 +2083,16 @@ fn specialize_external<'a>(
|
|||
tag_id,
|
||||
..
|
||||
} => {
|
||||
debug_assert_eq!(field_layouts.len() - 1, captured.len());
|
||||
// TODO check for field_layouts.len() == 1 and do a rename in that case?
|
||||
for (mut index, (symbol, _variable)) in captured.iter().enumerate() {
|
||||
// the field layouts do store the tag, but the tag value is
|
||||
// not captured. So we drop the layout of the tag ID here
|
||||
index += 1;
|
||||
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_)));
|
||||
debug_assert_eq!(field_layouts.len(), captured.len());
|
||||
|
||||
// TODO therefore should the wrapped here not be RecordOrSingleTagUnion?
|
||||
for (index, (symbol, _variable)) in captured.iter().enumerate() {
|
||||
let expr = Expr::UnionAtIndex {
|
||||
tag_id,
|
||||
structure: Symbol::ARG_CLOSURE,
|
||||
index: index as _,
|
||||
// union at index still expects the index to be +1; it thinks
|
||||
// the tag id is stored
|
||||
index: index as u64 + 1,
|
||||
union_layout,
|
||||
};
|
||||
|
||||
|
@ -4037,29 +4035,20 @@ fn construct_closure_data<'a>(
|
|||
tag_name,
|
||||
union_layout,
|
||||
} => {
|
||||
let tag_id_symbol = env.unique_symbol();
|
||||
let mut tag_symbols = Vec::with_capacity_in(symbols.len() + 1, env.arena);
|
||||
tag_symbols.push(tag_id_symbol);
|
||||
tag_symbols.extend(symbols);
|
||||
|
||||
let expr1 = Expr::Literal(Literal::Int(tag_id as i128));
|
||||
let expr2 = Expr::Tag {
|
||||
let expr = Expr::Tag {
|
||||
tag_id,
|
||||
tag_layout: union_layout,
|
||||
union_size,
|
||||
tag_name,
|
||||
arguments: tag_symbols.into_bump_slice(),
|
||||
arguments: symbols,
|
||||
};
|
||||
|
||||
let hole = Stmt::Let(
|
||||
Stmt::Let(
|
||||
assigned,
|
||||
expr2,
|
||||
expr,
|
||||
lambda_set.runtime_representation(),
|
||||
env.arena.alloc(hole),
|
||||
);
|
||||
|
||||
let hole = env.arena.alloc(hole);
|
||||
Stmt::Let(tag_id_symbol, expr1, Layout::Builtin(Builtin::Int64), hole)
|
||||
)
|
||||
}
|
||||
ClosureRepresentation::Other(Layout::Struct(field_layouts)) => {
|
||||
debug_assert_eq!(field_layouts.len(), symbols.len());
|
||||
|
@ -4251,12 +4240,10 @@ fn convert_tag_union<'a>(
|
|||
(tag, Layout::Union(union_layout))
|
||||
}
|
||||
NonRecursive { sorted_tag_layouts } => {
|
||||
let tag_id_symbol = env.unique_symbol();
|
||||
opt_tag_id_symbol = Some(tag_id_symbol);
|
||||
opt_tag_id_symbol = None;
|
||||
|
||||
field_symbols = {
|
||||
let mut temp = Vec::with_capacity_in(field_symbols_temp.len() + 1, arena);
|
||||
temp.push(tag_id_symbol);
|
||||
let mut temp = Vec::with_capacity_in(field_symbols_temp.len(), arena);
|
||||
|
||||
temp.extend(field_symbols_temp.iter().map(|r| r.1));
|
||||
|
||||
|
@ -7086,8 +7073,7 @@ fn from_can_pattern_help<'a>(
|
|||
ctors.push(Ctor {
|
||||
tag_id: TagId(i as u8),
|
||||
name: tag_name.clone(),
|
||||
// don't include tag discriminant in arity
|
||||
arity: args.len() - 1,
|
||||
arity: args.len(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -7100,13 +7086,13 @@ fn from_can_pattern_help<'a>(
|
|||
|
||||
debug_assert_eq!(
|
||||
arguments.len(),
|
||||
argument_layouts[1..].len(),
|
||||
argument_layouts.len(),
|
||||
"The {:?} tag got {} arguments, but its layout expects {}!",
|
||||
tag_name,
|
||||
arguments.len(),
|
||||
argument_layouts[1..].len(),
|
||||
argument_layouts.len(),
|
||||
);
|
||||
let it = argument_layouts[1..].iter();
|
||||
let it = argument_layouts.iter();
|
||||
|
||||
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
||||
mono_args.push((
|
||||
|
|
|
@ -109,6 +109,8 @@ impl<'a> UnionLayout<'a> {
|
|||
pub fn layout_at(self, tag_id: u8, index: usize) -> Layout<'a> {
|
||||
let result = match self {
|
||||
UnionLayout::NonRecursive(tag_layouts) => {
|
||||
let index = index - 1;
|
||||
|
||||
let field_layouts = tag_layouts[tag_id as usize];
|
||||
|
||||
// this cannot be recursive; return immediately
|
||||
|
@ -565,16 +567,21 @@ impl<'a> Layout<'a> {
|
|||
use UnionLayout::*;
|
||||
|
||||
match variant {
|
||||
NonRecursive(fields) => fields
|
||||
.iter()
|
||||
.map(|tag_layout| {
|
||||
tag_layout
|
||||
.iter()
|
||||
.map(|field| field.stack_size(pointer_size))
|
||||
.sum()
|
||||
})
|
||||
.max()
|
||||
.unwrap_or_default(),
|
||||
NonRecursive(fields) => {
|
||||
let data_size: u32 = fields
|
||||
.iter()
|
||||
.map(|tag_layout| {
|
||||
tag_layout
|
||||
.iter()
|
||||
.map(|field| field.stack_size(pointer_size))
|
||||
.sum()
|
||||
})
|
||||
.max()
|
||||
.unwrap_or_default();
|
||||
|
||||
// TEMPORARY
|
||||
pointer_size + data_size
|
||||
}
|
||||
|
||||
Recursive(_)
|
||||
| NullableWrapped { .. }
|
||||
|
@ -1587,7 +1594,9 @@ pub fn union_sorted_tags_help<'a>(
|
|||
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
|
||||
|
||||
// add the tag discriminant (size currently always hardcoded to i64)
|
||||
arg_layouts.push(Layout::Builtin(TAG_SIZE));
|
||||
if is_recursive {
|
||||
arg_layouts.push(Layout::Builtin(TAG_SIZE));
|
||||
}
|
||||
|
||||
for var in arguments {
|
||||
match Layout::from_var(&mut env, var) {
|
||||
|
|
|
@ -37,7 +37,7 @@ fn hash_record() {
|
|||
fn hash_result() {
|
||||
assert_evals_to!(
|
||||
"Dict.hashTestOnly 0 (List.get [ 0x1 ] 0) ",
|
||||
2878521786781103245,
|
||||
10806428154792634888,
|
||||
u64
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ fn applied_tag_nothing_ir() {
|
|||
"#
|
||||
),
|
||||
1,
|
||||
(i64, i64),
|
||||
|(tag, _)| tag
|
||||
(i64, u8),
|
||||
|(_, tag)| tag
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,8 @@ fn applied_tag_nothing() {
|
|||
"#
|
||||
),
|
||||
1,
|
||||
(i64, i64),
|
||||
|(tag, _)| tag
|
||||
(i64, u8),
|
||||
|(_, tag)| tag
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -56,8 +56,8 @@ fn applied_tag_just() {
|
|||
y
|
||||
"#
|
||||
),
|
||||
(0, 0x4),
|
||||
(i64, i64)
|
||||
(0x4, 0),
|
||||
(i64, u8)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -74,8 +74,8 @@ fn applied_tag_just_ir() {
|
|||
y
|
||||
"#
|
||||
),
|
||||
(0, 0x4),
|
||||
(i64, i64)
|
||||
(0x4, 0),
|
||||
(i64, u8)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -96,8 +96,8 @@ fn applied_tag_just_enum() {
|
|||
y
|
||||
"#
|
||||
),
|
||||
(0, 2),
|
||||
(i64, u8)
|
||||
(2, 0),
|
||||
(u8, i64)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -633,8 +633,8 @@ fn nested_tag_union() {
|
|||
x
|
||||
"#
|
||||
),
|
||||
(0, (0, 41)),
|
||||
(i64, (i64, i64))
|
||||
((41, 0), 0),
|
||||
((i64, i64), i64)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
|
@ -805,8 +805,8 @@ fn alignment_in_multi_tag_construction() {
|
|||
|
||||
#"
|
||||
),
|
||||
(1, 32i64, true),
|
||||
(i64, i64, bool)
|
||||
(32i64, true, 1),
|
||||
(i64, bool, i64)
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
|
@ -818,8 +818,8 @@ fn alignment_in_multi_tag_construction() {
|
|||
x
|
||||
#"
|
||||
),
|
||||
(1, 32i64, true, 2u8),
|
||||
(i64, i64, bool, u8)
|
||||
(32i64, true, 2u8, 1),
|
||||
(i64, bool, u8, i64)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1003,14 +1003,14 @@ fn applied_tag_function_result() {
|
|||
x : List (Result Str *)
|
||||
x = List.map [ "a", "b" ] Ok
|
||||
|
||||
x
|
||||
List.keepOks x (\y -> y)
|
||||
"#
|
||||
),
|
||||
RocList::from_slice(&[
|
||||
(1, RocStr::from_slice("a".as_bytes())),
|
||||
(1, RocStr::from_slice("b".as_bytes()))
|
||||
(RocStr::from_slice("a".as_bytes())),
|
||||
(RocStr::from_slice("b".as_bytes()))
|
||||
]),
|
||||
RocList<(i64, RocStr)>
|
||||
RocList<RocStr>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,8 @@ procedure Test.3 (Test.4):
|
|||
|
||||
procedure Test.0 ():
|
||||
let Test.28 = 0i64;
|
||||
let Test.31 = 0i64;
|
||||
let Test.30 = 3i64;
|
||||
let Test.26 = Just Test.31 Test.30;
|
||||
let Test.26 = Just Test.30;
|
||||
let Test.29 = 1i64;
|
||||
let Test.27 = Nil Test.29;
|
||||
let Test.12 = Cons Test.28 Test.26 Test.27;
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
procedure Num.42 (#Attr.2, #Attr.3):
|
||||
let Test.17 = 0i64;
|
||||
let Test.13 = lowlevel NotEq #Attr.3 Test.17;
|
||||
if Test.13 then
|
||||
let Test.16 = 1i64;
|
||||
let Test.15 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
||||
let Test.14 = Ok Test.16 Test.15;
|
||||
ret Test.14;
|
||||
let Test.15 = 0i64;
|
||||
let Test.12 = lowlevel NotEq #Attr.3 Test.15;
|
||||
if Test.12 then
|
||||
let Test.14 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
||||
let Test.13 = Ok Test.14;
|
||||
ret Test.13;
|
||||
else
|
||||
let Test.12 = 0i64;
|
||||
let Test.11 = Struct {};
|
||||
let Test.10 = Err Test.12 Test.11;
|
||||
let Test.10 = Err Test.11;
|
||||
ret Test.10;
|
||||
|
||||
procedure Test.0 ():
|
||||
|
|
|
@ -3,9 +3,8 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.6;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.13 = 0i64;
|
||||
let Test.12 = 41i64;
|
||||
let Test.1 = Just Test.13 Test.12;
|
||||
let Test.1 = Just Test.12;
|
||||
let Test.9 = 0i64;
|
||||
let Test.10 = GetTagId Test.1;
|
||||
let Test.11 = lowlevel Eq Test.9 Test.10;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
procedure Test.0 ():
|
||||
let Test.10 = 0i64;
|
||||
let Test.9 = 3i64;
|
||||
let Test.3 = Just Test.10 Test.9;
|
||||
let Test.3 = Just Test.9;
|
||||
let Test.6 = 0i64;
|
||||
let Test.7 = GetTagId Test.3;
|
||||
let Test.8 = lowlevel Eq Test.6 Test.7;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
procedure Test.0 ():
|
||||
let Test.12 = 1i64;
|
||||
let Test.10 = 1i64;
|
||||
let Test.11 = 2i64;
|
||||
let Test.5 = These Test.12 Test.10 Test.11;
|
||||
let Test.5 = These Test.10 Test.11;
|
||||
let Test.9 = GetTagId Test.5;
|
||||
switch Test.9:
|
||||
case 2:
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
procedure List.3 (#Attr.2, #Attr.3):
|
||||
let Test.15 = lowlevel ListLen #Attr.2;
|
||||
let Test.11 = lowlevel NumLt #Attr.3 Test.15;
|
||||
if Test.11 then
|
||||
let Test.14 = 1i64;
|
||||
let Test.13 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let Test.12 = Ok Test.14 Test.13;
|
||||
ret Test.12;
|
||||
let Test.13 = lowlevel ListLen #Attr.2;
|
||||
let Test.10 = lowlevel NumLt #Attr.3 Test.13;
|
||||
if Test.10 then
|
||||
let Test.12 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let Test.11 = Ok Test.12;
|
||||
ret Test.11;
|
||||
else
|
||||
let Test.10 = 0i64;
|
||||
let Test.9 = Struct {};
|
||||
let Test.8 = Err Test.10 Test.9;
|
||||
let Test.8 = Err Test.9;
|
||||
ret Test.8;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.16 = 1i64;
|
||||
let Test.17 = 2i64;
|
||||
let Test.18 = 3i64;
|
||||
let Test.6 = Array [Test.16, Test.17, Test.18];
|
||||
let Test.14 = 1i64;
|
||||
let Test.15 = 2i64;
|
||||
let Test.16 = 3i64;
|
||||
let Test.6 = Array [Test.14, Test.15, Test.16];
|
||||
let Test.7 = 0i64;
|
||||
let Test.5 = CallByName List.3 Test.6 Test.7;
|
||||
dec Test.6;
|
||||
|
|
|
@ -3,11 +3,9 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.21 = 0i64;
|
||||
let Test.23 = 0i64;
|
||||
let Test.22 = 41i64;
|
||||
let Test.20 = Just Test.23 Test.22;
|
||||
let Test.2 = Just Test.21 Test.20;
|
||||
let Test.21 = 41i64;
|
||||
let Test.20 = Just Test.21;
|
||||
let Test.2 = Just Test.20;
|
||||
joinpoint Test.17:
|
||||
let Test.11 = 1i64;
|
||||
ret Test.11;
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
procedure List.3 (#Attr.2, #Attr.3):
|
||||
let Test.39 = lowlevel ListLen #Attr.2;
|
||||
let Test.35 = lowlevel NumLt #Attr.3 Test.39;
|
||||
if Test.35 then
|
||||
let Test.38 = 1i64;
|
||||
let Test.37 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let Test.36 = Ok Test.38 Test.37;
|
||||
ret Test.36;
|
||||
let Test.37 = lowlevel ListLen #Attr.2;
|
||||
let Test.34 = lowlevel NumLt #Attr.3 Test.37;
|
||||
if Test.34 then
|
||||
let Test.36 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let Test.35 = Ok Test.36;
|
||||
ret Test.35;
|
||||
else
|
||||
let Test.34 = 0i64;
|
||||
let Test.33 = Struct {};
|
||||
let Test.32 = Err Test.34 Test.33;
|
||||
let Test.32 = Err Test.33;
|
||||
ret Test.32;
|
||||
|
||||
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
||||
|
@ -22,8 +20,8 @@ procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
|||
ret #Attr.2;
|
||||
|
||||
procedure Test.1 (Test.2):
|
||||
let Test.40 = 0i64;
|
||||
let Test.30 = CallByName List.3 Test.2 Test.40;
|
||||
let Test.38 = 0i64;
|
||||
let Test.30 = CallByName List.3 Test.2 Test.38;
|
||||
let Test.31 = 0i64;
|
||||
let Test.29 = CallByName List.3 Test.2 Test.31;
|
||||
let Test.8 = Struct {Test.29, Test.30};
|
||||
|
@ -58,8 +56,8 @@ procedure Test.1 (Test.2):
|
|||
jump Test.26;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.41 = 1i64;
|
||||
let Test.42 = 2i64;
|
||||
let Test.7 = Array [Test.41, Test.42];
|
||||
let Test.39 = 1i64;
|
||||
let Test.40 = 2i64;
|
||||
let Test.7 = Array [Test.39, Test.40];
|
||||
let Test.6 = CallByName Test.1 Test.7;
|
||||
ret Test.6;
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
procedure List.3 (#Attr.2, #Attr.3):
|
||||
let Test.41 = lowlevel ListLen #Attr.2;
|
||||
let Test.37 = lowlevel NumLt #Attr.3 Test.41;
|
||||
if Test.37 then
|
||||
let Test.40 = 1i64;
|
||||
let Test.39 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let Test.38 = Ok Test.40 Test.39;
|
||||
ret Test.38;
|
||||
let Test.39 = lowlevel ListLen #Attr.2;
|
||||
let Test.36 = lowlevel NumLt #Attr.3 Test.39;
|
||||
if Test.36 then
|
||||
let Test.38 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
let Test.37 = Ok Test.38;
|
||||
ret Test.37;
|
||||
else
|
||||
let Test.36 = 0i64;
|
||||
let Test.35 = Struct {};
|
||||
let Test.34 = Err Test.36 Test.35;
|
||||
let Test.34 = Err Test.35;
|
||||
ret Test.34;
|
||||
|
||||
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
||||
|
@ -56,7 +54,7 @@ procedure Test.1 (Test.2, Test.3, Test.4):
|
|||
procedure Test.0 ():
|
||||
let Test.10 = 0i64;
|
||||
let Test.11 = 0i64;
|
||||
let Test.42 = 1i64;
|
||||
let Test.12 = Array [Test.42];
|
||||
let Test.40 = 1i64;
|
||||
let Test.12 = Array [Test.40];
|
||||
let Test.9 = CallByName Test.1 Test.10 Test.11 Test.12;
|
||||
ret Test.9;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.30 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.30;
|
||||
let Test.29 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.29;
|
||||
|
||||
procedure Num.26 (#Attr.2, #Attr.3):
|
||||
let Test.25 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
|
@ -23,8 +23,8 @@ procedure Test.1 (Test.2, Test.3):
|
|||
|
||||
procedure Test.7 (Test.10, #Attr.12):
|
||||
let Test.4 = UnionAtIndex (Id 0) (Index 1) #Attr.12;
|
||||
let Test.29 = CallByName Num.24 Test.10 Test.4;
|
||||
ret Test.29;
|
||||
let Test.28 = CallByName Num.24 Test.10 Test.4;
|
||||
ret Test.28;
|
||||
|
||||
procedure Test.8 (Test.11, #Attr.12):
|
||||
let Test.6 = UnionAtIndex (Id 1) (Index 2) #Attr.12;
|
||||
|
@ -44,12 +44,10 @@ procedure Test.0 ():
|
|||
let Test.13 = CallByName Test.1 Test.14 Test.15;
|
||||
ret Test.13;
|
||||
in
|
||||
let Test.28 = true;
|
||||
if Test.28 then
|
||||
let Test.32 = 0i64;
|
||||
let Test.7 = ClosureTag(Test.7) Test.32 Test.4;
|
||||
let Test.27 = true;
|
||||
if Test.27 then
|
||||
let Test.7 = ClosureTag(Test.7) Test.4;
|
||||
jump Test.22 Test.7;
|
||||
else
|
||||
let Test.27 = 1i64;
|
||||
let Test.8 = ClosureTag(Test.8) Test.27 Test.5 Test.6;
|
||||
let Test.8 = ClosureTag(Test.8) Test.5 Test.6;
|
||||
jump Test.22 Test.8;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Test.26 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.26;
|
||||
let Test.25 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Test.25;
|
||||
|
||||
procedure Num.26 (#Attr.2, #Attr.3):
|
||||
let Test.21 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
|
@ -8,8 +8,8 @@ procedure Num.26 (#Attr.2, #Attr.3):
|
|||
|
||||
procedure Test.6 (Test.8, #Attr.12):
|
||||
let Test.4 = UnionAtIndex (Id 0) (Index 1) #Attr.12;
|
||||
let Test.25 = CallByName Num.24 Test.8 Test.4;
|
||||
ret Test.25;
|
||||
let Test.24 = CallByName Num.24 Test.8 Test.4;
|
||||
ret Test.24;
|
||||
|
||||
procedure Test.7 (Test.9, #Attr.12):
|
||||
let Test.5 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||
|
@ -35,12 +35,10 @@ procedure Test.0 ():
|
|||
jump Test.15 Test.17;
|
||||
|
||||
in
|
||||
let Test.24 = true;
|
||||
if Test.24 then
|
||||
let Test.28 = 0i64;
|
||||
let Test.6 = ClosureTag(Test.6) Test.28 Test.4;
|
||||
let Test.23 = true;
|
||||
if Test.23 then
|
||||
let Test.6 = ClosureTag(Test.6) Test.4;
|
||||
jump Test.19 Test.6;
|
||||
else
|
||||
let Test.23 = 1i64;
|
||||
let Test.7 = ClosureTag(Test.7) Test.23 Test.5;
|
||||
let Test.7 = ClosureTag(Test.7) Test.5;
|
||||
jump Test.19 Test.7;
|
||||
|
|
|
@ -3,11 +3,9 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
|||
ret Test.8;
|
||||
|
||||
procedure Test.0 ():
|
||||
let Test.21 = 0i64;
|
||||
let Test.23 = 0i64;
|
||||
let Test.22 = 41i64;
|
||||
let Test.20 = Just Test.23 Test.22;
|
||||
let Test.2 = Just Test.21 Test.20;
|
||||
let Test.21 = 41i64;
|
||||
let Test.20 = Just Test.21;
|
||||
let Test.2 = Just Test.20;
|
||||
joinpoint Test.17:
|
||||
let Test.11 = 1i64;
|
||||
ret Test.11;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
procedure Test.1 (Test.5):
|
||||
let Test.20 = 1i64;
|
||||
let Test.19 = 2i64;
|
||||
let Test.2 = Ok Test.20 Test.19;
|
||||
let Test.2 = Ok Test.19;
|
||||
joinpoint Test.9 Test.3:
|
||||
ret Test.3;
|
||||
in
|
||||
|
|
|
@ -28,13 +28,13 @@ arraystring = "0.3.0"
|
|||
libc = "0.2"
|
||||
page_size = "0.4"
|
||||
winit = "0.24"
|
||||
wgpu = "0.8"
|
||||
wgpu = "0.9"
|
||||
glyph_brush = "0.7"
|
||||
log = "0.4"
|
||||
zerocopy = "0.3"
|
||||
env_logger = "0.8"
|
||||
futures = "0.3"
|
||||
wgpu_glyph = "0.12"
|
||||
wgpu_glyph = "0.13"
|
||||
cgmath = "0.18.0"
|
||||
snafu = { version = "0.6", features = ["backtraces"] }
|
||||
colored = "2"
|
||||
|
|
|
@ -31,7 +31,7 @@ Nice collection of research on innovative editors, [link](https://futureofcoding
|
|||
* [Self](https://selflanguage.org/) programming language
|
||||
* [Primitive](https://primitive.io/) code exploration in Virtual Reality
|
||||
* [Luna](https://www.luna-lang.org/) language for interactive data processing and visualization
|
||||
|
||||
* [Hazel Livelits](https://hazel.org/papers/livelits-paper.pdf) interactive plugins, see GIF's [here](https://twitter.com/disconcision/status/1408155781120376833).
|
||||
### Debugging
|
||||
|
||||
* [VS code debug visualization](https://marketplace.visualstudio.com/items?itemName=hediet.debug-visualizer)
|
||||
|
@ -70,6 +70,7 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
|
|||
* [Lamdu](http://www.lamdu.org/) live functional programming.
|
||||
* [Sourcetrail](https://www.sourcetrail.com/) nice tree-like source explorer.
|
||||
* [Unisonweb](https://www.unisonweb.org), definition based [editor](https://twitter.com/shojberg/status/1364666092598288385) as opposed to file based.
|
||||
* [Utopia](https://utopia.app/) integrated design and development environment for React. Design and code update each other, in real time.
|
||||
|
||||
### Voice Interaction Related
|
||||
|
||||
|
|
1
examples/hello-fast/.gitignore
vendored
1
examples/hello-fast/.gitignore
vendored
|
@ -1 +0,0 @@
|
|||
hello-world
|
|
@ -1,6 +0,0 @@
|
|||
app "hello-world"
|
||||
packages { base: "platform" }
|
||||
imports []
|
||||
provides [ main ] to base
|
||||
|
||||
main = "Hello, World!\n"
|
|
@ -1,48 +0,0 @@
|
|||
# Hello, World!
|
||||
|
||||
To run, `cd` into this directory and run:
|
||||
|
||||
```bash
|
||||
$ cargo run run Hello.roc
|
||||
```
|
||||
|
||||
To run in release mode instead, do:
|
||||
|
||||
```bash
|
||||
$ 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
|
||||
function (in this case, a thunk that always returns `"Hello, World!"`) and
|
||||
then the host calls that function. Fundamentally, that's the whole idea! The host
|
||||
might not even have a `main` - it could be a library, a plugin, anything.
|
||||
Everything else is built on this basic "hosts calling linked pure functions" design.
|
||||
|
||||
For example, things get more interesting when the compiled Roc function returns
|
||||
a `Task` - that is, a tagged union data structure containing function pointers
|
||||
to callback closures. This lets the Roc pure function describe arbitrary
|
||||
chainable effects, which the host can interpret to perform I/O as requested by
|
||||
the Roc program. (The tagged union `Task` would have a variant for each supported
|
||||
I/O operation.)
|
||||
|
||||
In this trivial example, it's very easy to line up the API between the host and
|
||||
the Roc program. In a more involved host, this would be much trickier - especially
|
||||
if the API were changing frequently during development.
|
||||
|
||||
The idea there is to have a first-class concept of "glue code" which host authors
|
||||
can write (it would be plain Roc code, but with some extra keywords that aren't
|
||||
available in normal modules - kinda like `port module` in Elm), and which
|
||||
describe both the Roc-host/C boundary as well as the Roc-host/Roc-app boundary.
|
||||
Roc application authors only care about the Roc-host/Roc-app portion, and the
|
||||
host author only cares about the Roc-host/C bounary when implementing the host.
|
||||
|
||||
Using this glue code, the Roc compiler can generate C header files describing the
|
||||
boundary. This not only gets us host compatibility with C compilers, but also
|
||||
Rust FFI for free, because [`rust-bindgen`](https://github.com/rust-lang/rust-bindgen)
|
||||
generates correct Rust FFI bindings from C headers.
|
|
@ -1,10 +0,0 @@
|
|||
platform examples/hello-world
|
||||
requires {}{ main : Str }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [ mainForHost ]
|
||||
effects fx.Effect {}
|
||||
|
||||
mainForHost : Str
|
||||
mainForHost = main
|
|
@ -1,44 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
void* roc_alloc(size_t size, unsigned int alignment) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void* roc_realloc(void* ptr, size_t old_size, size_t new_size, unsigned int alignment) {
|
||||
return realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
void roc_dealloc(void* ptr, unsigned int alignment) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
struct RocStr {
|
||||
char* bytes;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct RocCallResult {
|
||||
size_t flag;
|
||||
struct RocStr content;
|
||||
};
|
||||
|
||||
extern void roc__mainForHost_1_exposed(struct RocCallResult *re);
|
||||
|
||||
int main() {
|
||||
// Make space for the result
|
||||
struct RocCallResult callresult;
|
||||
|
||||
// Call roc to populate the callresult
|
||||
roc__mainForHost_1_exposed(&callresult);
|
||||
|
||||
struct RocStr str = callresult.content;
|
||||
|
||||
// Write to stdout
|
||||
write(1, &str.bytes, 14);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -3,10 +3,4 @@ app "hello-world"
|
|||
imports []
|
||||
provides [ main ] to base
|
||||
|
||||
greeting =
|
||||
hi = "Hello"
|
||||
name = "World"
|
||||
|
||||
"\(hi), \(name)!!!!!!!!!!!!!"
|
||||
|
||||
main = greeting
|
||||
main = "Hello, World!\n"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void* roc_alloc(size_t size, unsigned int alignment) {
|
||||
return malloc(size);
|
||||
|
@ -20,6 +22,29 @@ struct RocStr {
|
|||
size_t len;
|
||||
};
|
||||
|
||||
bool is_small_str(struct RocStr str) {
|
||||
return ((ssize_t)str.len) < 0;
|
||||
}
|
||||
|
||||
// Determine the length of the string, taking into
|
||||
// account the small string optimization
|
||||
size_t roc_str_len(struct RocStr str) {
|
||||
char* bytes = (char*)&str;
|
||||
char last_byte = bytes[sizeof(str) - 1];
|
||||
char last_byte_xored = last_byte ^ 0b10000000;
|
||||
size_t small_len = (size_t)(last_byte_xored);
|
||||
size_t big_len = str.len;
|
||||
|
||||
// Avoid branch misprediction costs by always
|
||||
// determining both small_len and big_len,
|
||||
// so this compiles to a cmov instruction.
|
||||
if (is_small_str(str)) {
|
||||
return small_len;
|
||||
} else {
|
||||
return big_len;
|
||||
}
|
||||
}
|
||||
|
||||
struct RocCallResult {
|
||||
size_t flag;
|
||||
struct RocStr content;
|
||||
|
@ -27,50 +52,32 @@ struct RocCallResult {
|
|||
|
||||
extern void roc__mainForHost_1_exposed(struct RocCallResult *re);
|
||||
|
||||
const size_t MAX_STACK_STR_BYTES = 1024;
|
||||
|
||||
int main() {
|
||||
// make space for the result
|
||||
struct RocCallResult callresult;
|
||||
// Make space for the Roc call result
|
||||
struct RocCallResult call_result;
|
||||
|
||||
// call roc to populate the callresult
|
||||
roc__mainForHost_1_exposed(&callresult);
|
||||
struct RocStr str = callresult.content;
|
||||
// Call Roc to populate call_result
|
||||
roc__mainForHost_1_exposed(&call_result);
|
||||
|
||||
// Convert from RocStr to C string (null-terminated char*)
|
||||
size_t len = str.len;
|
||||
char* c_str;
|
||||
// Determine str_len and the str_bytes pointer,
|
||||
// taking into account the small string optimization.
|
||||
struct RocStr str = call_result.content;
|
||||
size_t str_len = roc_str_len(str);
|
||||
char* str_bytes;
|
||||
|
||||
// Allocate on the stack unless the string is particularly big.
|
||||
// (Don't want a stack overflow!)
|
||||
if (len <= MAX_STACK_STR_BYTES) {
|
||||
c_str = (char*)alloca(len + 1);
|
||||
if (is_small_str(str)) {
|
||||
str_bytes = (char*)&str;
|
||||
} else {
|
||||
c_str = (char*)malloc(len + 1);
|
||||
str_bytes = str.bytes;
|
||||
}
|
||||
|
||||
memcpy(c_str, str.bytes, len);
|
||||
// Write to stdout
|
||||
if (write(1, str_bytes, str_len) >= 0) {
|
||||
// Writing succeeded!
|
||||
return 0;
|
||||
} else {
|
||||
printf("Error writing to stdout: %s\n", strerror(errno));
|
||||
|
||||
// null-terminate
|
||||
c_str[len] = 0;
|
||||
|
||||
// Print the string to stdout
|
||||
printf("%s\n", c_str);
|
||||
|
||||
// Pointer to the beginning of the RocStr's actual allocation, which is
|
||||
// the size_t immediately preceding the first stored byte.
|
||||
size_t* str_base_ptr = (size_t*)str.bytes - 1;
|
||||
|
||||
// If *str_base_ptr is equal to 0, then the string is in the
|
||||
// read-only data section of the binary, and can't be freed!
|
||||
if (*str_base_ptr != 0) {
|
||||
roc_dealloc(str_base_ptr, 8);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If we malloc'd c_str, free it.
|
||||
if (len > MAX_STACK_STR_BYTES) {
|
||||
free(c_str);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue