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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
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]]
|
[[package]]
|
||||||
name = "dtoa"
|
name = "dtoa"
|
||||||
version = "0.4.8"
|
version = "0.4.8"
|
||||||
|
@ -1008,6 +1017,16 @@ dependencies = [
|
||||||
"termcolor",
|
"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]]
|
[[package]]
|
||||||
name = "fake-simd"
|
name = "fake-simd"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -1221,9 +1240,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx-auxil"
|
name = "gfx-auxil"
|
||||||
version = "0.9.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ccf8711c9994dfa34337466bee3ae1462e172874c432ce4eb120ab2e98d39cf"
|
checksum = "1694991b11d642680e82075a75c7c2bd75556b805efa7660b705689f05b1ab1c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"gfx-hal",
|
"gfx-hal",
|
||||||
|
@ -1232,14 +1251,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx-backend-dx11"
|
name = "gfx-backend-dx11"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f839f27f8c8a6dc553ccca7f5b35a42009432bc25db9688bba7061cd394161f"
|
checksum = "8f9e453baf3aaef2b0c354ce0b3d63d76402e406a59b64b7182d123cfa6635ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"gfx-auxil",
|
"gfx-auxil",
|
||||||
"gfx-hal",
|
"gfx-hal",
|
||||||
|
"gfx-renderdoc",
|
||||||
"libloading 0.7.0",
|
"libloading 0.7.0",
|
||||||
"log",
|
"log",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
@ -1254,9 +1274,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx-backend-dx12"
|
name = "gfx-backend-dx12"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3937738b0da5839bba4e33980d29f9a06dbce184d04a3a08c9a949e7953700e3"
|
checksum = "61f09e9d8c2aa69e9a21eb83c0f5d1a286c6d37da011f796e550d180b08090ce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bit-set",
|
"bit-set",
|
||||||
|
@ -1264,6 +1284,7 @@ dependencies = [
|
||||||
"d3d12",
|
"d3d12",
|
||||||
"gfx-auxil",
|
"gfx-auxil",
|
||||||
"gfx-hal",
|
"gfx-hal",
|
||||||
|
"gfx-renderdoc",
|
||||||
"log",
|
"log",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"range-alloc",
|
"range-alloc",
|
||||||
|
@ -1276,9 +1297,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx-backend-empty"
|
name = "gfx-backend-empty"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ac55ada4bfcd35479b3421eea324d36d7da5f724e2f66ecb36d4efdb7041a5e"
|
checksum = "29c8f813c47791918aa00dc9c9ddf961d23fa8c2a5d869e6cb8ea84f944820f4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gfx-hal",
|
"gfx-hal",
|
||||||
"log",
|
"log",
|
||||||
|
@ -1287,9 +1308,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx-backend-gl"
|
name = "gfx-backend-gl"
|
||||||
version = "0.8.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0caa03d6e0b7b4f202aea1f20c3f3288cfa06d92d24cea9d69c9a7627967244a"
|
checksum = "6bae057fc3a0ab23ecf97ae51d4017d27d5ddf0aab16ee6dcb58981af88c3152"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
@ -1309,15 +1330,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx-backend-metal"
|
name = "gfx-backend-metal"
|
||||||
version = "0.8.2"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "340895ad544ba46433acb3bdabece0ef16f2dbedc030adbd7c9eaf2839fbed41"
|
checksum = "0de85808e2a98994c6af925253f8a9593bc57180ef1ea137deab6d35cc949517"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"block",
|
"block",
|
||||||
"cocoa-foundation",
|
"cocoa-foundation",
|
||||||
"copyless",
|
"copyless",
|
||||||
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"gfx-hal",
|
"gfx-hal",
|
||||||
|
@ -1334,15 +1356,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx-backend-vulkan"
|
name = "gfx-backend-vulkan"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a353fc6fdb42ec646de49bbb74e4870e37a7e680caf33f3ac0615c30b1146d94"
|
checksum = "a9861ec855acbbc65c0e4f966d761224886e811dc2c6d413a4776e9293d0e5c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"ash",
|
"ash",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"gfx-hal",
|
"gfx-hal",
|
||||||
|
"gfx-renderdoc",
|
||||||
"inplace_it",
|
"inplace_it",
|
||||||
"log",
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
|
@ -1355,16 +1378,28 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx-hal"
|
name = "gfx-hal"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d285bfd566f6b9134af908446ca350c0a1047495dfb9bbd826e701e8ee1d259"
|
checksum = "7fbb575ea793dd0507b3082f4f2cde62dc9f3cebd98f5cd49ba2a4da97a976fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"external-memory",
|
||||||
"naga",
|
"naga",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"thiserror",
|
"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]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
|
@ -1880,13 +1915,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metal"
|
name = "metal"
|
||||||
version = "0.22.0"
|
version = "0.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777"
|
checksum = "79d7d769f1c104b8388294d6594d491d2e21240636f5f94d37f8a0f3d7904450"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"block",
|
"block",
|
||||||
"cocoa-foundation",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"log",
|
"log",
|
||||||
"objc",
|
"objc",
|
||||||
|
@ -1956,9 +1991,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c8d74f2c7ace793a760165ac0679d6830809ad4e85f6886f72e4f8c4aa4291c5"
|
checksum = "ef670817eef03d356d5a509ea275e7dd3a78ea9e24261ea3cb2dfed1abb08f64"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-set",
|
"bit-set",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
@ -1967,6 +2002,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
|
"rose_tree",
|
||||||
"spirv_headers",
|
"spirv_headers",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
@ -2935,6 +2971,12 @@ dependencies = [
|
||||||
"winapi 0.3.9",
|
"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]]
|
[[package]]
|
||||||
name = "roc_build"
|
name = "roc_build"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -3451,6 +3493,15 @@ dependencies = [
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rose_tree"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "284de9dae38774e2813aaabd7e947b4a6fe9b8c58c2309f754a487cdd50de1c2"
|
||||||
|
dependencies = [
|
||||||
|
"petgraph",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
|
@ -4365,9 +4416,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu"
|
name = "wgpu"
|
||||||
version = "0.8.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "215fd50e66f794bd16683e7e0e0b9b53be265eb10fdf02276caf5de3e5743fcf"
|
checksum = "bd247f8b26fd3d42ef2f320d378025cd6e84d782ef749fab45cc3b981fbe3275"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
@ -4385,9 +4436,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core"
|
name = "wgpu-core"
|
||||||
version = "0.8.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d56c368fc0e6f3927c711d2b55a51ad4321218efc0239c4acf69e456ab70399"
|
checksum = "2af5c8acd3ae5781a277cdf65a17f3a7135de5ae782775620e74ea16c9d47770"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
@ -4415,18 +4466,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-types"
|
name = "wgpu-types"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa248d90c8e6832269b8955bf800e8241f942c25e18a235b7752226804d21556"
|
checksum = "4f5c9678cd533558e28b416d66947b099742df1939307478db54f867137f1b60"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu_glyph"
|
name = "wgpu_glyph"
|
||||||
version = "0.12.0"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "634570b440f4c24c2e6049ed01ec832c23d338dea3eca654d5760838017a1c8b"
|
checksum = "0fee8c96eda18195a7ad9989737183e0a357f14b15e98838c76abbcf56a5f970"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"glyph_brush",
|
"glyph_brush",
|
||||||
|
|
|
@ -214,8 +214,48 @@ fn jit_to_ast_help<'a>(
|
||||||
match variant {
|
match variant {
|
||||||
NonRecursive {
|
NonRecursive {
|
||||||
sorted_tag_layouts: tags_and_layouts,
|
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,
|
sorted_tag_layouts: tags_and_layouts,
|
||||||
} => {
|
} => {
|
||||||
Ok(run_jit_function_dynamic_type!(
|
Ok(run_jit_function_dynamic_type!(
|
||||||
|
|
|
@ -121,7 +121,7 @@ mod cli_run {
|
||||||
&example_file("hello-world", "Hello.roc"),
|
&example_file("hello-world", "Hello.roc"),
|
||||||
"hello-world",
|
"hello-world",
|
||||||
&[],
|
&[],
|
||||||
"Hello, World!!!!!!!!!!!!!\n",
|
"Hello, World!\n",
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ mod cli_run {
|
||||||
&example_file("hello-world", "Hello.roc"),
|
&example_file("hello-world", "Hello.roc"),
|
||||||
"hello-world",
|
"hello-world",
|
||||||
&[],
|
&[],
|
||||||
"Hello, World!!!!!!!!!!!!!\n",
|
"Hello, World!\n",
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,7 @@ pub fn gen_from_mono_module(
|
||||||
opt_level,
|
opt_level,
|
||||||
loaded.procedures,
|
loaded.procedures,
|
||||||
loaded.entry_point,
|
loaded.entry_point,
|
||||||
|
Some(&app_ll_file),
|
||||||
);
|
);
|
||||||
|
|
||||||
env.dibuilder.finalize();
|
env.dibuilder.finalize();
|
||||||
|
@ -193,7 +194,7 @@ pub fn gen_from_mono_module(
|
||||||
// run the debugir https://github.com/vaivaswatha/debugir tool
|
// run the debugir https://github.com/vaivaswatha/debugir tool
|
||||||
match Command::new("debugir")
|
match Command::new("debugir")
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.args(&[app_ll_file.to_str().unwrap()])
|
.args(&["-instnamer", app_ll_file.to_str().unwrap()])
|
||||||
.output()
|
.output()
|
||||||
{
|
{
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
|
|
@ -12,6 +12,7 @@ const Opaque = ?[*]u8;
|
||||||
const Inc = fn (?[*]u8) callconv(.C) void;
|
const Inc = fn (?[*]u8) callconv(.C) void;
|
||||||
const IncN = fn (?[*]u8, usize) callconv(.C) void;
|
const IncN = fn (?[*]u8, usize) callconv(.C) void;
|
||||||
const Dec = fn (?[*]u8) 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 {
|
pub const RocList = extern struct {
|
||||||
bytes: ?[*]u8,
|
bytes: ?[*]u8,
|
||||||
|
@ -405,11 +406,14 @@ pub fn listKeepOks(
|
||||||
before_width: usize,
|
before_width: usize,
|
||||||
result_width: usize,
|
result_width: usize,
|
||||||
after_width: usize,
|
after_width: usize,
|
||||||
|
has_tag_id: HasTagId,
|
||||||
dec_result: Dec,
|
dec_result: Dec,
|
||||||
) callconv(.C) RocList {
|
) callconv(.C) RocList {
|
||||||
|
const good_constructor: u16 = 1;
|
||||||
|
|
||||||
return listKeepResult(
|
return listKeepResult(
|
||||||
list,
|
list,
|
||||||
RocResult.isOk,
|
good_constructor,
|
||||||
caller,
|
caller,
|
||||||
data,
|
data,
|
||||||
inc_n_data,
|
inc_n_data,
|
||||||
|
@ -418,6 +422,7 @@ pub fn listKeepOks(
|
||||||
before_width,
|
before_width,
|
||||||
result_width,
|
result_width,
|
||||||
after_width,
|
after_width,
|
||||||
|
has_tag_id,
|
||||||
dec_result,
|
dec_result,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -432,11 +437,14 @@ pub fn listKeepErrs(
|
||||||
before_width: usize,
|
before_width: usize,
|
||||||
result_width: usize,
|
result_width: usize,
|
||||||
after_width: usize,
|
after_width: usize,
|
||||||
|
has_tag_id: HasTagId,
|
||||||
dec_result: Dec,
|
dec_result: Dec,
|
||||||
) callconv(.C) RocList {
|
) callconv(.C) RocList {
|
||||||
|
const good_constructor: u16 = 0;
|
||||||
|
|
||||||
return listKeepResult(
|
return listKeepResult(
|
||||||
list,
|
list,
|
||||||
RocResult.isErr,
|
good_constructor,
|
||||||
caller,
|
caller,
|
||||||
data,
|
data,
|
||||||
inc_n_data,
|
inc_n_data,
|
||||||
|
@ -445,13 +453,14 @@ pub fn listKeepErrs(
|
||||||
before_width,
|
before_width,
|
||||||
result_width,
|
result_width,
|
||||||
after_width,
|
after_width,
|
||||||
|
has_tag_id,
|
||||||
dec_result,
|
dec_result,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listKeepResult(
|
pub fn listKeepResult(
|
||||||
list: RocList,
|
list: RocList,
|
||||||
is_good_constructor: fn (RocResult) bool,
|
good_constructor: u16,
|
||||||
caller: Caller1,
|
caller: Caller1,
|
||||||
data: Opaque,
|
data: Opaque,
|
||||||
inc_n_data: IncN,
|
inc_n_data: IncN,
|
||||||
|
@ -460,6 +469,7 @@ pub fn listKeepResult(
|
||||||
before_width: usize,
|
before_width: usize,
|
||||||
result_width: usize,
|
result_width: usize,
|
||||||
after_width: usize,
|
after_width: usize,
|
||||||
|
has_tag_id: HasTagId,
|
||||||
dec_result: Dec,
|
dec_result: Dec,
|
||||||
) RocList {
|
) RocList {
|
||||||
if (list.bytes) |source_ptr| {
|
if (list.bytes) |source_ptr| {
|
||||||
|
@ -479,11 +489,14 @@ pub fn listKeepResult(
|
||||||
const before_element = source_ptr + (i * before_width);
|
const before_element = source_ptr + (i * before_width);
|
||||||
caller(data, before_element, temporary);
|
caller(data, before_element, temporary);
|
||||||
|
|
||||||
const result = utils.RocResult{ .bytes = temporary };
|
// a record { matched: bool, data: ?[*]u8 }
|
||||||
|
// for now, that data pointer is just the input `temporary` pointer
|
||||||
const after_element = temporary + @sizeOf(i64);
|
// this will change in the future to only return a pointer to the
|
||||||
if (is_good_constructor(result)) {
|
// payload of the tag
|
||||||
@memcpy(target_ptr + (kept * after_width), after_element, after_width);
|
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;
|
kept += 1;
|
||||||
} else {
|
} else {
|
||||||
dec_result(temporary);
|
dec_result(temporary);
|
||||||
|
@ -606,7 +619,9 @@ pub fn listWalkUntil(
|
||||||
accum: Opaque,
|
accum: Opaque,
|
||||||
alignment: u32,
|
alignment: u32,
|
||||||
element_width: usize,
|
element_width: usize,
|
||||||
|
continue_stop_width: usize,
|
||||||
accum_width: usize,
|
accum_width: usize,
|
||||||
|
has_tag_id: HasTagId,
|
||||||
dec: Dec,
|
dec: Dec,
|
||||||
output: Opaque,
|
output: Opaque,
|
||||||
) callconv(.C) void {
|
) callconv(.C) void {
|
||||||
|
@ -622,9 +637,10 @@ pub fn listWalkUntil(
|
||||||
return;
|
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| {
|
if (list.bytes) |source_ptr| {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
|
@ -636,10 +652,12 @@ pub fn listWalkUntil(
|
||||||
inc_n_data(data, 1);
|
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));
|
// [ Continue ..., Stop ]
|
||||||
if (usizes[0] != 0) {
|
const tag_id = has_tag_id(0, bytes_ptr);
|
||||||
|
|
||||||
|
if (!tag_id.matched) {
|
||||||
// decrement refcount of the remaining items
|
// decrement refcount of the remaining items
|
||||||
i += 1;
|
i += 1;
|
||||||
while (i < size) : (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);
|
utils.dealloc(bytes_ptr, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1009,7 +1027,25 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt
|
||||||
return output;
|
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(
|
pub fn listSet(
|
||||||
bytes: ?[*]u8,
|
bytes: ?[*]u8,
|
||||||
length: usize,
|
length: usize,
|
||||||
|
@ -1028,23 +1064,34 @@ pub fn listSet(
|
||||||
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes));
|
const ptr: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes));
|
||||||
|
|
||||||
if ((ptr - 1)[0] == utils.REFCOUNT_ONE) {
|
if ((ptr - 1)[0] == utils.REFCOUNT_ONE) {
|
||||||
|
return listSetInPlaceHelp(bytes, length, alignment, index, element, element_width, dec);
|
||||||
// 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;
|
|
||||||
} else {
|
} 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,
|
old_bytes: ?[*]u8,
|
||||||
length: usize,
|
length: usize,
|
||||||
alignment: u32,
|
alignment: u32,
|
||||||
|
@ -1053,8 +1100,6 @@ inline fn listSetClone(
|
||||||
element_width: usize,
|
element_width: usize,
|
||||||
dec: Dec,
|
dec: Dec,
|
||||||
) ?[*]u8 {
|
) ?[*]u8 {
|
||||||
@setCold(true);
|
|
||||||
|
|
||||||
const data_bytes = length * element_width;
|
const data_bytes = length * element_width;
|
||||||
|
|
||||||
var new_bytes = utils.allocateWithRefcount(data_bytes, alignment);
|
var new_bytes = utils.allocateWithRefcount(data_bytes, alignment);
|
||||||
|
|
|
@ -41,6 +41,7 @@ comptime {
|
||||||
exportListFn(list.listConcat, "concat");
|
exportListFn(list.listConcat, "concat");
|
||||||
exportListFn(list.listDrop, "drop");
|
exportListFn(list.listDrop, "drop");
|
||||||
exportListFn(list.listSet, "set");
|
exportListFn(list.listSet, "set");
|
||||||
|
exportListFn(list.listSetInPlace, "set_in_place");
|
||||||
exportListFn(list.listSwap, "swap");
|
exportListFn(list.listSwap, "swap");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ const InPlace = packed enum(u8) {
|
||||||
|
|
||||||
const SMALL_STR_MAX_LENGTH = small_string_size - 1;
|
const SMALL_STR_MAX_LENGTH = small_string_size - 1;
|
||||||
const small_string_size = 2 * @sizeOf(usize);
|
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 {
|
fn init_blank_small_string(comptime n: usize) [n]u8 {
|
||||||
var prime_list: [n]u8 = undefined;
|
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
|
// 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.
|
// small string, and returns a (pointer, len) tuple which points to them.
|
||||||
pub fn withCapacity(length: usize) RocStr {
|
pub fn withCapacity(length: usize) RocStr {
|
||||||
|
@ -203,8 +197,8 @@ pub const RocStr = extern struct {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: returns false for empty string!
|
||||||
pub fn isSmallStr(self: RocStr) bool {
|
pub fn isSmallStr(self: RocStr) bool {
|
||||||
// NOTE: returns False for empty string!
|
|
||||||
return @bitCast(isize, self.str_len) < 0;
|
return @bitCast(isize, self.str_len) < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +217,82 @@ pub const RocStr = extern struct {
|
||||||
return self.len() == 0;
|
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 {
|
pub fn isUnique(self: RocStr) bool {
|
||||||
// the empty list is unique (in the sense that copying it will not leak memory)
|
// the empty list is unique (in the sense that copying it will not leak memory)
|
||||||
if (self.isEmpty()) {
|
if (self.isEmpty()) {
|
||||||
|
@ -240,15 +310,13 @@ pub const RocStr = extern struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asSlice(self: RocStr) []u8 {
|
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()];
|
return self.asU8ptr()[0..self.len()];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asU8ptr(self: RocStr) [*]u8 {
|
pub fn asU8ptr(self: RocStr) [*]u8 {
|
||||||
// Since this conditional would be prone to branch misprediction,
|
// Since this conditional would be prone to branch misprediction,
|
||||||
// make sure it will compile to a cmov.
|
// 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
|
// 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_SORT_WITH: &str = "roc_builtins.list.sort_with";
|
||||||
pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
|
pub const LIST_CONCAT: &str = "roc_builtins.list.concat";
|
||||||
pub const LIST_SET: &str = "roc_builtins.list.set";
|
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_FROM_F64: &str = "roc_builtins.dec.from_f64";
|
||||||
pub const DEC_EQ: &str = "roc_builtins.dec.eq";
|
pub const DEC_EQ: &str = "roc_builtins.dec.eq";
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/// Helpers for interacting with the zig that generates bitcode
|
/// Helpers for interacting with the zig that generates bitcode
|
||||||
use crate::debug_info_init;
|
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::convert::basic_type_from_layout;
|
||||||
use crate::llvm::refcounting::{
|
use crate::llvm::refcounting::{
|
||||||
decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout,
|
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::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
use roc_module::symbol::Symbol;
|
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>(
|
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
@ -66,6 +66,127 @@ const ARGUMENT_SYMBOLS: [Symbol; 8] = [
|
||||||
Symbol::ARG_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>(
|
pub fn build_transform_caller<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
function: FunctionValue<'ctx>,
|
function: FunctionValue<'ctx>,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::llvm::bitcode::call_bitcode_fn;
|
use crate::llvm::bitcode::call_bitcode_fn;
|
||||||
use crate::llvm::build_dict::{
|
use crate::llvm::build_dict::{
|
||||||
dict_contains, dict_difference, dict_empty, dict_get, dict_insert, dict_intersection,
|
dict_contains, dict_difference, dict_empty, dict_get, dict_insert, dict_intersection,
|
||||||
|
@ -42,7 +44,9 @@ use inkwell::values::{
|
||||||
};
|
};
|
||||||
use inkwell::OptimizationLevel;
|
use inkwell::OptimizationLevel;
|
||||||
use inkwell::{AddressSpace, IntPredicate};
|
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_builtins::bitcode;
|
||||||
use roc_collections::all::{ImMap, MutMap, MutSet};
|
use roc_collections::all::{ImMap, MutMap, MutSet};
|
||||||
use roc_module::ident::TagName;
|
use roc_module::ident::TagName;
|
||||||
|
@ -832,8 +836,21 @@ pub fn build_exp_call<'a, 'ctx, 'env>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
CallType::LowLevel { op, update_mode: _ } => {
|
CallType::LowLevel { op, update_mode } => {
|
||||||
run_low_level(env, layout_ids, scope, parent, layout, *op, arguments)
|
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 {
|
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>(
|
pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
|
@ -909,7 +952,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
Struct(sorted_fields) => {
|
Struct(sorted_fields) => {
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
// Determine types
|
// Determine types
|
||||||
let num_fields = sorted_fields.len();
|
let num_fields = sorted_fields.len();
|
||||||
|
@ -929,67 +971,9 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
// Create the struct_type
|
// Create the struct_type
|
||||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
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
|
// Insert field exprs into struct_val
|
||||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate()).into()
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tag {
|
Tag {
|
||||||
|
@ -999,8 +983,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
tag_id,
|
tag_id,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
|
||||||
|
|
||||||
debug_assert!(*union_size > 1);
|
debug_assert!(*union_size > 1);
|
||||||
|
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
@ -1038,19 +1020,10 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
// Create the struct_type
|
// Create the struct_type
|
||||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
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
|
// Insert field exprs into struct_val
|
||||||
for (index, field_val) in field_vals.into_iter().enumerate() {
|
let struct_val =
|
||||||
struct_val = builder
|
struct_from_fields(env, struct_type, field_vals.into_iter().enumerate());
|
||||||
.build_insert_value(
|
|
||||||
struct_val,
|
|
||||||
field_val,
|
|
||||||
index as u32,
|
|
||||||
"insert_multi_tag_field",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// How we create tag values
|
// How we create tag values
|
||||||
//
|
//
|
||||||
|
@ -1076,9 +1049,21 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
// This tricks comes from
|
// This tricks comes from
|
||||||
// https://github.com/raviqqe/ssf/blob/bc32aae68940d5bddf5984128e85af75ca4f4686/ssf-llvm/src/expression_compiler.rs#L116
|
// 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 {
|
Tag {
|
||||||
arguments,
|
arguments,
|
||||||
|
@ -1087,8 +1072,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
tag_id,
|
tag_id,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
|
||||||
|
|
||||||
debug_assert!(*union_size > 1);
|
debug_assert!(*union_size > 1);
|
||||||
|
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
@ -1132,7 +1115,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the struct_type
|
// 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_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
let struct_ptr = env
|
let struct_ptr = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -1166,9 +1150,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
debug_assert_eq!(*tag_id, 0);
|
debug_assert_eq!(*tag_id, 0);
|
||||||
debug_assert_eq!(arguments.len(), fields.len());
|
debug_assert_eq!(arguments.len(), fields.len());
|
||||||
|
|
||||||
let struct_layout =
|
|
||||||
Layout::Union(UnionLayout::NonRecursive(env.arena.alloc([*fields])));
|
|
||||||
|
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
|
@ -1207,7 +1188,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the struct_type
|
// 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_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
let struct_ptr = env
|
let struct_ptr = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -1241,8 +1223,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
tag_id,
|
tag_id,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
let tag_struct_type = block_of_memory_slices(env.context, fields, env.ptr_bytes);
|
||||||
let tag_struct_type = basic_type_from_layout(env, &tag_layout);
|
|
||||||
if *tag_id == *nullable_id as u8 {
|
if *tag_id == *nullable_id as u8 {
|
||||||
let output_type = tag_struct_type.ptr_type(AddressSpace::Generic);
|
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
|
// 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_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
let struct_ptr = env
|
let struct_ptr = env
|
||||||
.builder
|
.builder
|
||||||
|
@ -1398,10 +1380,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the struct_type
|
// Create the struct_type
|
||||||
let data_ptr = reserve_with_refcount(
|
let data_ptr = reserve_with_refcount_union_as_block_of_memory(env, &[other_fields]);
|
||||||
env,
|
|
||||||
&Layout::Union(UnionLayout::NonRecursive(&[other_fields])),
|
|
||||||
);
|
|
||||||
|
|
||||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
let struct_ptr = env
|
let struct_ptr = env
|
||||||
|
@ -1506,6 +1485,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match union_layout {
|
match union_layout {
|
||||||
UnionLayout::NonRecursive(tag_layouts) => {
|
UnionLayout::NonRecursive(tag_layouts) => {
|
||||||
|
let index = *index - 1;
|
||||||
|
|
||||||
debug_assert!(argument.is_struct_value());
|
debug_assert!(argument.is_struct_value());
|
||||||
let field_layouts = tag_layouts[*tag_id as usize];
|
let field_layouts = tag_layouts[*tag_id as usize];
|
||||||
let struct_layout = Layout::Struct(field_layouts);
|
let struct_layout = Layout::Struct(field_layouts);
|
||||||
|
@ -1519,7 +1500,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = builder
|
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");
|
.expect("desired field did not decode");
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -1611,77 +1592,82 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
structure,
|
structure,
|
||||||
union_layout,
|
union_layout,
|
||||||
} => {
|
} => {
|
||||||
let builder = env.builder;
|
|
||||||
|
|
||||||
// cast the argument bytes into the desired shape for this tag
|
// cast the argument bytes into the desired shape for this tag
|
||||||
let (argument, _structure_layout) = load_symbol_and_layout(scope, structure);
|
let (argument, _structure_layout) = load_symbol_and_layout(scope, structure);
|
||||||
|
|
||||||
match union_layout {
|
get_tag_id(env, parent, union_layout, argument)
|
||||||
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");
|
|
||||||
|
|
||||||
let ctx = env.context;
|
pub fn get_tag_id<'a, 'ctx, 'env>(
|
||||||
let then_block = ctx.append_basic_block(parent, "then");
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
let else_block = ctx.append_basic_block(parent, "else");
|
parent: FunctionValue<'ctx>,
|
||||||
let cont_block = ctx.append_basic_block(parent, "cont");
|
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
|
let ctx = env.context;
|
||||||
.build_conditional_branch(is_null, then_block, else_block);
|
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.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
|
||||||
env.builder.position_at_end(else_block);
|
.build_conditional_branch(is_null, then_block, 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.position_at_end(then_block);
|
||||||
env.builder.build_load(result, "load_result")
|
let tag_id = ctx.i64_type().const_int(*nullable_id as u64, false);
|
||||||
}
|
env.builder.build_store(result, tag_id);
|
||||||
UnionLayout::NullableUnwrapped { nullable_id, .. } => {
|
env.builder.build_unconditional_branch(cont_block);
|
||||||
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(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>(
|
pub fn reserve_with_refcount<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout: &Layout<'a>,
|
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> {
|
) -> PointerValue<'ctx> {
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
|
||||||
let len_type = env.ptr_int();
|
let len_type = env.ptr_int();
|
||||||
|
|
||||||
let value_bytes = layout.stack_size(env.ptr_bytes);
|
let value_bytes_intvalue = len_type.const_int(stack_size as u64, false);
|
||||||
let value_bytes_intvalue = len_type.const_int(value_bytes as u64, false);
|
|
||||||
|
|
||||||
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes);
|
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>(
|
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>(
|
pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
||||||
env: &Env<'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>,
|
number_of_data_bytes: IntValue<'ctx>,
|
||||||
initial_refcount: IntValue<'ctx>,
|
initial_refcount: IntValue<'ctx>,
|
||||||
) -> PointerValue<'ctx> {
|
) -> PointerValue<'ctx> {
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
|
|
||||||
let value_type = basic_type_from_layout(env, layout);
|
|
||||||
let len_type = env.ptr_int();
|
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 = {
|
let ptr = {
|
||||||
// number of bytes we will allocated
|
// number of bytes we will allocated
|
||||||
|
@ -1776,7 +1797,7 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>(
|
||||||
"add_extra_bytes",
|
"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:
|
// 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>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
from_value: StructValue<'ctx>,
|
parent: FunctionValue<'ctx>,
|
||||||
|
union_layout: UnionLayout<'a>,
|
||||||
|
cond_value: BasicValueEnum<'ctx>,
|
||||||
) -> IntValue<'ctx> {
|
) -> IntValue<'ctx> {
|
||||||
let struct_type = env
|
let builder = env.builder;
|
||||||
.context
|
|
||||||
.struct_type(&[env.context.i64_type().into()], false);
|
|
||||||
|
|
||||||
let struct_value = complex_bitcast_struct_struct(
|
match union_layout {
|
||||||
env.builder,
|
UnionLayout::NonRecursive(_) => {
|
||||||
from_value,
|
let pointer = builder.build_alloca(cond_value.get_type(), "get_type");
|
||||||
struct_type,
|
builder.build_store(pointer, cond_value);
|
||||||
"extract_tag_discriminant_struct",
|
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
|
let ctx = env.context;
|
||||||
.build_extract_value(struct_value, 0, "")
|
let then_block = ctx.append_basic_block(parent, "then");
|
||||||
.expect("desired field did not decode")
|
let else_block = ctx.append_basic_block(parent, "else");
|
||||||
.into_int_value()
|
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>(
|
fn extract_tag_discriminant_ptr<'a, 'ctx, 'env>(
|
||||||
|
@ -2590,57 +2673,9 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||||
.into_int_value()
|
.into_int_value()
|
||||||
}
|
}
|
||||||
Layout::Union(variant) => {
|
Layout::Union(variant) => {
|
||||||
use UnionLayout::*;
|
cond_layout = Layout::Builtin(Builtin::Int64);
|
||||||
|
|
||||||
match variant {
|
extract_tag_discriminant(env, parent, variant, cond_value)
|
||||||
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"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Layout::Builtin(_) => cond_value.into_int_value(),
|
Layout::Builtin(_) => cond_value.into_int_value(),
|
||||||
other => todo!("Build switch value from layout: {:?}", other),
|
other => todo!("Build switch value from layout: {:?}", other),
|
||||||
|
@ -3187,8 +3222,9 @@ pub fn build_procedures<'a, 'ctx, 'env>(
|
||||||
opt_level: OptLevel,
|
opt_level: OptLevel,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
entry_point: EntryPoint<'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>(
|
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>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
entry_point: EntryPoint<'a>,
|
entry_point: EntryPoint<'a>,
|
||||||
) -> (&'static str, FunctionValue<'ctx>) {
|
) -> (&'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)
|
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,
|
opt_level: OptLevel,
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
entry_point: EntryPoint<'a>,
|
entry_point: EntryPoint<'a>,
|
||||||
|
debug_output_file: Option<&Path>,
|
||||||
) -> &'a ModSolutions {
|
) -> &'a ModSolutions {
|
||||||
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
let mut layout_ids = roc_mono::layout::LayoutIds::default();
|
||||||
let mut scope = Scope::default();
|
let mut scope = Scope::default();
|
||||||
|
@ -3265,13 +3302,22 @@ fn build_procedures_help<'a, 'ctx, 'env>(
|
||||||
);
|
);
|
||||||
|
|
||||||
fn_val.print_to_stderr();
|
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.",
|
"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,
|
mode,
|
||||||
);
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3803,6 +3849,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
|
||||||
env,
|
env,
|
||||||
layout_ids,
|
layout_ids,
|
||||||
roc_function_call,
|
roc_function_call,
|
||||||
|
result_layout,
|
||||||
list,
|
list,
|
||||||
element_layout,
|
element_layout,
|
||||||
default,
|
default,
|
||||||
|
@ -4196,6 +4243,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
layout: &Layout<'a>,
|
layout: &Layout<'a>,
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
args: &[Symbol],
|
args: &[Symbol],
|
||||||
|
update_mode: Option<UpdateMode>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
use LowLevel::*;
|
use LowLevel::*;
|
||||||
|
|
||||||
|
@ -4654,27 +4702,6 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
wrapper_struct,
|
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 => {
|
ListSet => {
|
||||||
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
|
let (list, list_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||||
let (index, _) = load_symbol_and_layout(scope, &args[1]);
|
let (index, _) = load_symbol_and_layout(scope, &args[1]);
|
||||||
|
@ -4692,6 +4719,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
index.into_int_value(),
|
index.into_int_value(),
|
||||||
element,
|
element,
|
||||||
element_layout,
|
element_layout,
|
||||||
|
update_mode.unwrap(),
|
||||||
),
|
),
|
||||||
_ => unreachable!("invalid dict layout"),
|
_ => unreachable!("invalid dict layout"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
use crate::llvm::bitcode::{
|
use crate::llvm::bitcode::{
|
||||||
build_dec_wrapper, build_eq_wrapper, build_inc_n_wrapper, build_inc_wrapper, call_bitcode_fn,
|
build_dec_wrapper, build_eq_wrapper, build_has_tag_id, build_inc_n_wrapper, build_inc_wrapper,
|
||||||
call_void_bitcode_fn,
|
call_bitcode_fn, call_void_bitcode_fn,
|
||||||
};
|
};
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
allocate_with_refcount_help, cast_basic_basic, complex_bitcast, Env, RocFunctionCall,
|
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::types::{BasicType, BasicTypeEnum, PointerType};
|
||||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||||
use inkwell::{AddressSpace, IntPredicate};
|
use inkwell::{AddressSpace, IntPredicate};
|
||||||
|
use morphic_lib::UpdateMode;
|
||||||
use roc_builtins::bitcode;
|
use roc_builtins::bitcode;
|
||||||
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
use roc_mono::layout::{Builtin, Layout, LayoutIds};
|
||||||
|
|
||||||
|
@ -350,6 +351,7 @@ pub fn list_set<'a, 'ctx, 'env>(
|
||||||
index: IntValue<'ctx>,
|
index: IntValue<'ctx>,
|
||||||
element: BasicValueEnum<'ctx>,
|
element: BasicValueEnum<'ctx>,
|
||||||
element_layout: &'a Layout<'a>,
|
element_layout: &'a Layout<'a>,
|
||||||
|
update_mode: UpdateMode,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
|
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),
|
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(
|
let new_bytes = call_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
|
@ -370,7 +377,7 @@ pub fn list_set<'a, 'ctx, 'env>(
|
||||||
layout_width(env, element_layout),
|
layout_width(env, element_layout),
|
||||||
dec_element_fn.as_global_value().as_pointer_value().into(),
|
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||||
],
|
],
|
||||||
&bitcode::LIST_SET,
|
&symbol,
|
||||||
);
|
);
|
||||||
|
|
||||||
store_list(env, new_bytes.into_pointer_value(), length)
|
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>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
layout_ids: &mut LayoutIds<'a>,
|
layout_ids: &mut LayoutIds<'a>,
|
||||||
roc_function_call: RocFunctionCall<'ctx>,
|
roc_function_call: RocFunctionCall<'ctx>,
|
||||||
|
function_call_return_layout: &Layout<'a>,
|
||||||
list: BasicValueEnum<'ctx>,
|
list: BasicValueEnum<'ctx>,
|
||||||
element_layout: &Layout<'a>,
|
element_layout: &Layout<'a>,
|
||||||
default: BasicValueEnum<'ctx>,
|
default: BasicValueEnum<'ctx>,
|
||||||
|
@ -450,6 +458,18 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ListWalk::WalkUntil | ListWalk::WalkBackwardsUntil => {
|
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);
|
let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout);
|
||||||
call_void_bitcode_fn(
|
call_void_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
|
@ -462,7 +482,9 @@ pub fn list_walk_generic<'a, 'ctx, 'env>(
|
||||||
pass_as_opaque(env, default_ptr),
|
pass_as_opaque(env, default_ptr),
|
||||||
env.alignment_intvalue(&element_layout),
|
env.alignment_intvalue(&element_layout),
|
||||||
layout_width(env, element_layout),
|
layout_width(env, element_layout),
|
||||||
|
layout_width(env, function_call_return_layout),
|
||||||
layout_width(env, default_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(),
|
dec_element_fn.as_global_value().as_pointer_value().into(),
|
||||||
pass_as_opaque(env, result_ptr),
|
pass_as_opaque(env, result_ptr),
|
||||||
],
|
],
|
||||||
|
@ -604,6 +626,18 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
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(
|
call_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
|
@ -616,6 +650,7 @@ pub fn list_keep_oks<'a, 'ctx, 'env>(
|
||||||
layout_width(env, before_layout),
|
layout_width(env, before_layout),
|
||||||
layout_width(env, result_layout),
|
layout_width(env, result_layout),
|
||||||
layout_width(env, after_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(),
|
dec_result_fn.as_global_value().as_pointer_value().into(),
|
||||||
],
|
],
|
||||||
bitcode::LIST_KEEP_OKS,
|
bitcode::LIST_KEEP_OKS,
|
||||||
|
@ -635,6 +670,18 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
let dec_result_fn = build_dec_wrapper(env, layout_ids, result_layout);
|
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(
|
call_bitcode_fn(
|
||||||
env,
|
env,
|
||||||
&[
|
&[
|
||||||
|
@ -647,6 +694,7 @@ pub fn list_keep_errs<'a, 'ctx, 'env>(
|
||||||
layout_width(env, before_layout),
|
layout_width(env, before_layout),
|
||||||
layout_width(env, result_layout),
|
layout_width(env, result_layout),
|
||||||
layout_width(env, after_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(),
|
dec_result_fn.as_global_value().as_pointer_value().into(),
|
||||||
],
|
],
|
||||||
bitcode::LIST_KEEP_ERRS,
|
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)
|
// we assume that the list is indeed used (dead variables are eliminated)
|
||||||
let rc1 = crate::llvm::refcounting::refcount_1(ctx, env.ptr_bytes);
|
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>(
|
pub fn store_list<'a, 'ctx, 'env>(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::llvm::bitcode::call_bitcode_fn;
|
use crate::llvm::bitcode::call_bitcode_fn;
|
||||||
use crate::llvm::build::Env;
|
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_list::{list_len, load_list_ptr};
|
||||||
use crate::llvm::build_str::str_equal;
|
use crate::llvm::build_str::str_equal;
|
||||||
use crate::llvm::convert::basic_type_from_layout;
|
use crate::llvm::convert::basic_type_from_layout;
|
||||||
|
@ -850,9 +850,10 @@ fn build_tag_eq_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match union_layout {
|
match union_layout {
|
||||||
NonRecursive(tags) => {
|
NonRecursive(tags) => {
|
||||||
// SAFETY we know that non-recursive tags cannot be NULL
|
let id1 =
|
||||||
let id1 = nonrec_tag_id(env, tag1.into_struct_value());
|
crate::llvm::build::get_tag_id(env, parent, union_layout, tag1).into_int_value();
|
||||||
let id2 = nonrec_tag_id(env, tag2.into_struct_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");
|
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()
|
.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>(
|
unsafe fn rec_tag_id_unsafe<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
tag: PointerValue<'ctx>,
|
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);
|
let block = block_of_memory_slices(env.context, &[fields], env.ptr_bytes);
|
||||||
block.ptr_type(AddressSpace::Generic).into()
|
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 => {
|
RecursivePointer => {
|
||||||
|
@ -118,7 +124,11 @@ pub fn block_of_memory<'ctx>(
|
||||||
ptr_bytes: u32,
|
ptr_bytes: u32,
|
||||||
) -> BasicTypeEnum<'ctx> {
|
) -> BasicTypeEnum<'ctx> {
|
||||||
// TODO make this dynamic
|
// 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)
|
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()
|
env.module.get_struct_type("str.RocStr").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn zig_dec_type<'a, 'ctx, 'env>(
|
pub fn zig_has_tag_id_type<'a, 'ctx, 'env>(
|
||||||
// env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
env: &crate::llvm::build::Env<'a, 'ctx, 'env>,
|
||||||
// ) -> StructType<'ctx> {
|
) -> StructType<'ctx> {
|
||||||
// env.module.get_struct_type("dec.RocDec").unwrap()
|
env.module.get_struct_type("list.HasTagId").unwrap()
|
||||||
// }
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use crate::debug_info_init;
|
use crate::debug_info_init;
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
add_func, cast_basic_basic, cast_block_of_memory_to_tag, Env, FAST_CALL_CONV,
|
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::build_list::{incrementing_elem_loop, list_len, load_list};
|
||||||
use crate::llvm::convert::{
|
use crate::llvm::convert::{basic_type_from_layout, block_of_memory_slices, ptr_int};
|
||||||
basic_type_from_layout, block_of_memory, block_of_memory_slices, ptr_int,
|
|
||||||
};
|
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use inkwell::basic_block::BasicBlock;
|
use inkwell::basic_block::BasicBlock;
|
||||||
use inkwell::context::Context;
|
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()) {
|
let function = match env.module.get_function(fn_name.as_str()) {
|
||||||
Some(function_value) => function_value,
|
Some(function_value) => function_value,
|
||||||
None => {
|
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);
|
let function_value = build_header(env, basic_type, mode, &fn_name);
|
||||||
|
|
||||||
modify_refcount_union_help(
|
modify_refcount_union_help(
|
||||||
|
@ -1640,19 +1638,11 @@ fn modify_refcount_union_help<'a, 'ctx, 'env>(
|
||||||
let wrapper_struct = arg_val.into_struct_value();
|
let wrapper_struct = arg_val.into_struct_value();
|
||||||
|
|
||||||
// read the tag_id
|
// read the tag_id
|
||||||
let tag_id = {
|
let tag_id = env
|
||||||
// the first element of the wrapping struct is an array of i64
|
.builder
|
||||||
let first_array = env
|
.build_extract_value(wrapper_struct, TAG_ID_INDEX, "read_tag_id")
|
||||||
.builder
|
.unwrap()
|
||||||
.build_extract_value(wrapper_struct, 0, "read_tag_id")
|
.into_int_value();
|
||||||
.unwrap()
|
|
||||||
.into_array_value();
|
|
||||||
|
|
||||||
env.builder
|
|
||||||
.build_extract_value(first_array, 0, "read_tag_id_2")
|
|
||||||
.unwrap()
|
|
||||||
.into_int_value()
|
|
||||||
};
|
|
||||||
|
|
||||||
let tag_id_u8 = env
|
let tag_id_u8 = env
|
||||||
.builder
|
.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));
|
let wrapper_type = basic_type_from_layout(env, &Layout::Struct(field_layouts));
|
||||||
|
|
||||||
debug_assert!(wrapper_type.is_struct_type());
|
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() {
|
for (i, field_layout) in field_layouts.iter().enumerate() {
|
||||||
if let Layout::RecursivePointer = field_layout {
|
if let Layout::RecursivePointer = field_layout {
|
||||||
|
|
|
@ -18,7 +18,6 @@ pub enum LowLevel {
|
||||||
ListLen,
|
ListLen,
|
||||||
ListGetUnsafe,
|
ListGetUnsafe,
|
||||||
ListSet,
|
ListSet,
|
||||||
ListSetInPlace,
|
|
||||||
ListSingle,
|
ListSingle,
|
||||||
ListRepeat,
|
ListRepeat,
|
||||||
ListReverse,
|
ListReverse,
|
||||||
|
@ -125,7 +124,6 @@ impl LowLevel {
|
||||||
| ListLen
|
| ListLen
|
||||||
| ListGetUnsafe
|
| ListGetUnsafe
|
||||||
| ListSet
|
| ListSet
|
||||||
| ListSetInPlace
|
|
||||||
| ListDrop
|
| ListDrop
|
||||||
| ListSingle
|
| ListSingle
|
||||||
| ListRepeat
|
| ListRepeat
|
||||||
|
|
|
@ -861,11 +861,13 @@ fn expr_spec(
|
||||||
union_layout,
|
union_layout,
|
||||||
} => match union_layout {
|
} => match union_layout {
|
||||||
UnionLayout::NonRecursive(_) => {
|
UnionLayout::NonRecursive(_) => {
|
||||||
|
// let index = (*index - 1) as u32;
|
||||||
|
let index = (*index - 1) as u32;
|
||||||
let tag_value_id = env.symbols[structure];
|
let tag_value_id = env.symbols[structure];
|
||||||
let tuple_value_id =
|
let tuple_value_id =
|
||||||
builder.add_unwrap_union(block, tag_value_id, *tag_id as u32)?;
|
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
|
// 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 {
|
match op {
|
||||||
ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]),
|
ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]),
|
||||||
ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
||||||
ListSetInPlace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]),
|
|
||||||
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
ListConcat => arena.alloc_slice_copy(&[owned, owned]),
|
ListConcat => arena.alloc_slice_copy(&[owned, owned]),
|
||||||
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
|
StrConcat => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
|
|
|
@ -2083,18 +2083,16 @@ fn specialize_external<'a>(
|
||||||
tag_id,
|
tag_id,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
debug_assert_eq!(field_layouts.len() - 1, captured.len());
|
debug_assert!(matches!(union_layout, UnionLayout::NonRecursive(_)));
|
||||||
// TODO check for field_layouts.len() == 1 and do a rename in that case?
|
debug_assert_eq!(field_layouts.len(), captured.len());
|
||||||
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;
|
|
||||||
|
|
||||||
// TODO therefore should the wrapped here not be RecordOrSingleTagUnion?
|
for (index, (symbol, _variable)) in captured.iter().enumerate() {
|
||||||
let expr = Expr::UnionAtIndex {
|
let expr = Expr::UnionAtIndex {
|
||||||
tag_id,
|
tag_id,
|
||||||
structure: Symbol::ARG_CLOSURE,
|
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,
|
union_layout,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4037,29 +4035,20 @@ fn construct_closure_data<'a>(
|
||||||
tag_name,
|
tag_name,
|
||||||
union_layout,
|
union_layout,
|
||||||
} => {
|
} => {
|
||||||
let tag_id_symbol = env.unique_symbol();
|
let expr = Expr::Tag {
|
||||||
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 {
|
|
||||||
tag_id,
|
tag_id,
|
||||||
tag_layout: union_layout,
|
tag_layout: union_layout,
|
||||||
union_size,
|
union_size,
|
||||||
tag_name,
|
tag_name,
|
||||||
arguments: tag_symbols.into_bump_slice(),
|
arguments: symbols,
|
||||||
};
|
};
|
||||||
|
|
||||||
let hole = Stmt::Let(
|
Stmt::Let(
|
||||||
assigned,
|
assigned,
|
||||||
expr2,
|
expr,
|
||||||
lambda_set.runtime_representation(),
|
lambda_set.runtime_representation(),
|
||||||
env.arena.alloc(hole),
|
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)) => {
|
ClosureRepresentation::Other(Layout::Struct(field_layouts)) => {
|
||||||
debug_assert_eq!(field_layouts.len(), symbols.len());
|
debug_assert_eq!(field_layouts.len(), symbols.len());
|
||||||
|
@ -4251,12 +4240,10 @@ fn convert_tag_union<'a>(
|
||||||
(tag, Layout::Union(union_layout))
|
(tag, Layout::Union(union_layout))
|
||||||
}
|
}
|
||||||
NonRecursive { sorted_tag_layouts } => {
|
NonRecursive { sorted_tag_layouts } => {
|
||||||
let tag_id_symbol = env.unique_symbol();
|
opt_tag_id_symbol = None;
|
||||||
opt_tag_id_symbol = Some(tag_id_symbol);
|
|
||||||
|
|
||||||
field_symbols = {
|
field_symbols = {
|
||||||
let mut temp = Vec::with_capacity_in(field_symbols_temp.len() + 1, arena);
|
let mut temp = Vec::with_capacity_in(field_symbols_temp.len(), arena);
|
||||||
temp.push(tag_id_symbol);
|
|
||||||
|
|
||||||
temp.extend(field_symbols_temp.iter().map(|r| r.1));
|
temp.extend(field_symbols_temp.iter().map(|r| r.1));
|
||||||
|
|
||||||
|
@ -7086,8 +7073,7 @@ fn from_can_pattern_help<'a>(
|
||||||
ctors.push(Ctor {
|
ctors.push(Ctor {
|
||||||
tag_id: TagId(i as u8),
|
tag_id: TagId(i as u8),
|
||||||
name: tag_name.clone(),
|
name: tag_name.clone(),
|
||||||
// don't include tag discriminant in arity
|
arity: args.len(),
|
||||||
arity: args.len() - 1,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7100,13 +7086,13 @@ fn from_can_pattern_help<'a>(
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
arguments.len(),
|
arguments.len(),
|
||||||
argument_layouts[1..].len(),
|
argument_layouts.len(),
|
||||||
"The {:?} tag got {} arguments, but its layout expects {}!",
|
"The {:?} tag got {} arguments, but its layout expects {}!",
|
||||||
tag_name,
|
tag_name,
|
||||||
arguments.len(),
|
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) {
|
for ((_, loc_pat), layout) in arguments.iter().zip(it) {
|
||||||
mono_args.push((
|
mono_args.push((
|
||||||
|
|
|
@ -109,6 +109,8 @@ impl<'a> UnionLayout<'a> {
|
||||||
pub fn layout_at(self, tag_id: u8, index: usize) -> Layout<'a> {
|
pub fn layout_at(self, tag_id: u8, index: usize) -> Layout<'a> {
|
||||||
let result = match self {
|
let result = match self {
|
||||||
UnionLayout::NonRecursive(tag_layouts) => {
|
UnionLayout::NonRecursive(tag_layouts) => {
|
||||||
|
let index = index - 1;
|
||||||
|
|
||||||
let field_layouts = tag_layouts[tag_id as usize];
|
let field_layouts = tag_layouts[tag_id as usize];
|
||||||
|
|
||||||
// this cannot be recursive; return immediately
|
// this cannot be recursive; return immediately
|
||||||
|
@ -565,16 +567,21 @@ impl<'a> Layout<'a> {
|
||||||
use UnionLayout::*;
|
use UnionLayout::*;
|
||||||
|
|
||||||
match variant {
|
match variant {
|
||||||
NonRecursive(fields) => fields
|
NonRecursive(fields) => {
|
||||||
.iter()
|
let data_size: u32 = fields
|
||||||
.map(|tag_layout| {
|
.iter()
|
||||||
tag_layout
|
.map(|tag_layout| {
|
||||||
.iter()
|
tag_layout
|
||||||
.map(|field| field.stack_size(pointer_size))
|
.iter()
|
||||||
.sum()
|
.map(|field| field.stack_size(pointer_size))
|
||||||
})
|
.sum()
|
||||||
.max()
|
})
|
||||||
.unwrap_or_default(),
|
.max()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// TEMPORARY
|
||||||
|
pointer_size + data_size
|
||||||
|
}
|
||||||
|
|
||||||
Recursive(_)
|
Recursive(_)
|
||||||
| NullableWrapped { .. }
|
| NullableWrapped { .. }
|
||||||
|
@ -1587,7 +1594,9 @@ pub fn union_sorted_tags_help<'a>(
|
||||||
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
|
let mut arg_layouts = Vec::with_capacity_in(arguments.len() + 1, arena);
|
||||||
|
|
||||||
// add the tag discriminant (size currently always hardcoded to i64)
|
// 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 {
|
for var in arguments {
|
||||||
match Layout::from_var(&mut env, var) {
|
match Layout::from_var(&mut env, var) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ fn hash_record() {
|
||||||
fn hash_result() {
|
fn hash_result() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"Dict.hashTestOnly 0 (List.get [ 0x1 ] 0) ",
|
"Dict.hashTestOnly 0 (List.get [ 0x1 ] 0) ",
|
||||||
2878521786781103245,
|
10806428154792634888,
|
||||||
u64
|
u64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ fn applied_tag_nothing_ir() {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
(i64, i64),
|
(i64, u8),
|
||||||
|(tag, _)| tag
|
|(_, tag)| tag
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ fn applied_tag_nothing() {
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
(i64, i64),
|
(i64, u8),
|
||||||
|(tag, _)| tag
|
|(_, tag)| tag
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ fn applied_tag_just() {
|
||||||
y
|
y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
(0, 0x4),
|
(0x4, 0),
|
||||||
(i64, i64)
|
(i64, u8)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,8 +74,8 @@ fn applied_tag_just_ir() {
|
||||||
y
|
y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
(0, 0x4),
|
(0x4, 0),
|
||||||
(i64, i64)
|
(i64, u8)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +96,8 @@ fn applied_tag_just_enum() {
|
||||||
y
|
y
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
(0, 2),
|
(2, 0),
|
||||||
(i64, u8)
|
(u8, i64)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,8 +633,8 @@ fn nested_tag_union() {
|
||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
(0, (0, 41)),
|
((41, 0), 0),
|
||||||
(i64, (i64, i64))
|
((i64, i64), i64)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -805,8 +805,8 @@ fn alignment_in_multi_tag_construction() {
|
||||||
|
|
||||||
#"
|
#"
|
||||||
),
|
),
|
||||||
(1, 32i64, true),
|
(32i64, true, 1),
|
||||||
(i64, i64, bool)
|
(i64, bool, i64)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -818,8 +818,8 @@ fn alignment_in_multi_tag_construction() {
|
||||||
x
|
x
|
||||||
#"
|
#"
|
||||||
),
|
),
|
||||||
(1, 32i64, true, 2u8),
|
(32i64, true, 2u8, 1),
|
||||||
(i64, i64, bool, u8)
|
(i64, bool, u8, i64)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1003,14 +1003,14 @@ fn applied_tag_function_result() {
|
||||||
x : List (Result Str *)
|
x : List (Result Str *)
|
||||||
x = List.map [ "a", "b" ] Ok
|
x = List.map [ "a", "b" ] Ok
|
||||||
|
|
||||||
x
|
List.keepOks x (\y -> y)
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
RocList::from_slice(&[
|
RocList::from_slice(&[
|
||||||
(1, RocStr::from_slice("a".as_bytes())),
|
(RocStr::from_slice("a".as_bytes())),
|
||||||
(1, RocStr::from_slice("b".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 ():
|
procedure Test.0 ():
|
||||||
let Test.28 = 0i64;
|
let Test.28 = 0i64;
|
||||||
let Test.31 = 0i64;
|
|
||||||
let Test.30 = 3i64;
|
let Test.30 = 3i64;
|
||||||
let Test.26 = Just Test.31 Test.30;
|
let Test.26 = Just Test.30;
|
||||||
let Test.29 = 1i64;
|
let Test.29 = 1i64;
|
||||||
let Test.27 = Nil Test.29;
|
let Test.27 = Nil Test.29;
|
||||||
let Test.12 = Cons Test.28 Test.26 Test.27;
|
let Test.12 = Cons Test.28 Test.26 Test.27;
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
procedure Num.42 (#Attr.2, #Attr.3):
|
procedure Num.42 (#Attr.2, #Attr.3):
|
||||||
let Test.17 = 0i64;
|
let Test.15 = 0i64;
|
||||||
let Test.13 = lowlevel NotEq #Attr.3 Test.17;
|
let Test.12 = lowlevel NotEq #Attr.3 Test.15;
|
||||||
if Test.13 then
|
if Test.12 then
|
||||||
let Test.16 = 1i64;
|
let Test.14 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
||||||
let Test.15 = lowlevel NumDivUnchecked #Attr.2 #Attr.3;
|
let Test.13 = Ok Test.14;
|
||||||
let Test.14 = Ok Test.16 Test.15;
|
ret Test.13;
|
||||||
ret Test.14;
|
|
||||||
else
|
else
|
||||||
let Test.12 = 0i64;
|
|
||||||
let Test.11 = Struct {};
|
let Test.11 = Struct {};
|
||||||
let Test.10 = Err Test.12 Test.11;
|
let Test.10 = Err Test.11;
|
||||||
ret Test.10;
|
ret Test.10;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
|
|
|
@ -3,9 +3,8 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.13 = 0i64;
|
|
||||||
let Test.12 = 41i64;
|
let Test.12 = 41i64;
|
||||||
let Test.1 = Just Test.13 Test.12;
|
let Test.1 = Just Test.12;
|
||||||
let Test.9 = 0i64;
|
let Test.9 = 0i64;
|
||||||
let Test.10 = GetTagId Test.1;
|
let Test.10 = GetTagId Test.1;
|
||||||
let Test.11 = lowlevel Eq Test.9 Test.10;
|
let Test.11 = lowlevel Eq Test.9 Test.10;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.10 = 0i64;
|
|
||||||
let Test.9 = 3i64;
|
let Test.9 = 3i64;
|
||||||
let Test.3 = Just Test.10 Test.9;
|
let Test.3 = Just Test.9;
|
||||||
let Test.6 = 0i64;
|
let Test.6 = 0i64;
|
||||||
let Test.7 = GetTagId Test.3;
|
let Test.7 = GetTagId Test.3;
|
||||||
let Test.8 = lowlevel Eq Test.6 Test.7;
|
let Test.8 = lowlevel Eq Test.6 Test.7;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.12 = 1i64;
|
|
||||||
let Test.10 = 1i64;
|
let Test.10 = 1i64;
|
||||||
let Test.11 = 2i64;
|
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;
|
let Test.9 = GetTagId Test.5;
|
||||||
switch Test.9:
|
switch Test.9:
|
||||||
case 2:
|
case 2:
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
procedure List.3 (#Attr.2, #Attr.3):
|
procedure List.3 (#Attr.2, #Attr.3):
|
||||||
let Test.15 = lowlevel ListLen #Attr.2;
|
let Test.13 = lowlevel ListLen #Attr.2;
|
||||||
let Test.11 = lowlevel NumLt #Attr.3 Test.15;
|
let Test.10 = lowlevel NumLt #Attr.3 Test.13;
|
||||||
if Test.11 then
|
if Test.10 then
|
||||||
let Test.14 = 1i64;
|
let Test.12 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
let Test.13 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let Test.11 = Ok Test.12;
|
||||||
let Test.12 = Ok Test.14 Test.13;
|
ret Test.11;
|
||||||
ret Test.12;
|
|
||||||
else
|
else
|
||||||
let Test.10 = 0i64;
|
|
||||||
let Test.9 = Struct {};
|
let Test.9 = Struct {};
|
||||||
let Test.8 = Err Test.10 Test.9;
|
let Test.8 = Err Test.9;
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.16 = 1i64;
|
let Test.14 = 1i64;
|
||||||
let Test.17 = 2i64;
|
let Test.15 = 2i64;
|
||||||
let Test.18 = 3i64;
|
let Test.16 = 3i64;
|
||||||
let Test.6 = Array [Test.16, Test.17, Test.18];
|
let Test.6 = Array [Test.14, Test.15, Test.16];
|
||||||
let Test.7 = 0i64;
|
let Test.7 = 0i64;
|
||||||
let Test.5 = CallByName List.3 Test.6 Test.7;
|
let Test.5 = CallByName List.3 Test.6 Test.7;
|
||||||
dec Test.6;
|
dec Test.6;
|
||||||
|
|
|
@ -3,11 +3,9 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.21 = 0i64;
|
let Test.21 = 41i64;
|
||||||
let Test.23 = 0i64;
|
let Test.20 = Just Test.21;
|
||||||
let Test.22 = 41i64;
|
let Test.2 = Just Test.20;
|
||||||
let Test.20 = Just Test.23 Test.22;
|
|
||||||
let Test.2 = Just Test.21 Test.20;
|
|
||||||
joinpoint Test.17:
|
joinpoint Test.17:
|
||||||
let Test.11 = 1i64;
|
let Test.11 = 1i64;
|
||||||
ret Test.11;
|
ret Test.11;
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
procedure List.3 (#Attr.2, #Attr.3):
|
procedure List.3 (#Attr.2, #Attr.3):
|
||||||
let Test.39 = lowlevel ListLen #Attr.2;
|
let Test.37 = lowlevel ListLen #Attr.2;
|
||||||
let Test.35 = lowlevel NumLt #Attr.3 Test.39;
|
let Test.34 = lowlevel NumLt #Attr.3 Test.37;
|
||||||
if Test.35 then
|
if Test.34 then
|
||||||
let Test.38 = 1i64;
|
let Test.36 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
let Test.37 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let Test.35 = Ok Test.36;
|
||||||
let Test.36 = Ok Test.38 Test.37;
|
ret Test.35;
|
||||||
ret Test.36;
|
|
||||||
else
|
else
|
||||||
let Test.34 = 0i64;
|
|
||||||
let Test.33 = Struct {};
|
let Test.33 = Struct {};
|
||||||
let Test.32 = Err Test.34 Test.33;
|
let Test.32 = Err Test.33;
|
||||||
ret Test.32;
|
ret Test.32;
|
||||||
|
|
||||||
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
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;
|
ret #Attr.2;
|
||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.40 = 0i64;
|
let Test.38 = 0i64;
|
||||||
let Test.30 = CallByName List.3 Test.2 Test.40;
|
let Test.30 = CallByName List.3 Test.2 Test.38;
|
||||||
let Test.31 = 0i64;
|
let Test.31 = 0i64;
|
||||||
let Test.29 = CallByName List.3 Test.2 Test.31;
|
let Test.29 = CallByName List.3 Test.2 Test.31;
|
||||||
let Test.8 = Struct {Test.29, Test.30};
|
let Test.8 = Struct {Test.29, Test.30};
|
||||||
|
@ -58,8 +56,8 @@ procedure Test.1 (Test.2):
|
||||||
jump Test.26;
|
jump Test.26;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.41 = 1i64;
|
let Test.39 = 1i64;
|
||||||
let Test.42 = 2i64;
|
let Test.40 = 2i64;
|
||||||
let Test.7 = Array [Test.41, Test.42];
|
let Test.7 = Array [Test.39, Test.40];
|
||||||
let Test.6 = CallByName Test.1 Test.7;
|
let Test.6 = CallByName Test.1 Test.7;
|
||||||
ret Test.6;
|
ret Test.6;
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
procedure List.3 (#Attr.2, #Attr.3):
|
procedure List.3 (#Attr.2, #Attr.3):
|
||||||
let Test.41 = lowlevel ListLen #Attr.2;
|
let Test.39 = lowlevel ListLen #Attr.2;
|
||||||
let Test.37 = lowlevel NumLt #Attr.3 Test.41;
|
let Test.36 = lowlevel NumLt #Attr.3 Test.39;
|
||||||
if Test.37 then
|
if Test.36 then
|
||||||
let Test.40 = 1i64;
|
let Test.38 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
let Test.39 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
let Test.37 = Ok Test.38;
|
||||||
let Test.38 = Ok Test.40 Test.39;
|
ret Test.37;
|
||||||
ret Test.38;
|
|
||||||
else
|
else
|
||||||
let Test.36 = 0i64;
|
|
||||||
let Test.35 = Struct {};
|
let Test.35 = Struct {};
|
||||||
let Test.34 = Err Test.36 Test.35;
|
let Test.34 = Err Test.35;
|
||||||
ret Test.34;
|
ret Test.34;
|
||||||
|
|
||||||
procedure List.4 (#Attr.2, #Attr.3, #Attr.4):
|
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 ():
|
procedure Test.0 ():
|
||||||
let Test.10 = 0i64;
|
let Test.10 = 0i64;
|
||||||
let Test.11 = 0i64;
|
let Test.11 = 0i64;
|
||||||
let Test.42 = 1i64;
|
let Test.40 = 1i64;
|
||||||
let Test.12 = Array [Test.42];
|
let Test.12 = Array [Test.40];
|
||||||
let Test.9 = CallByName Test.1 Test.10 Test.11 Test.12;
|
let Test.9 = CallByName Test.1 Test.10 Test.11 Test.12;
|
||||||
ret Test.9;
|
ret Test.9;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.24 (#Attr.2, #Attr.3):
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
let Test.30 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.29 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.30;
|
ret Test.29;
|
||||||
|
|
||||||
procedure Num.26 (#Attr.2, #Attr.3):
|
procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
let Test.25 = lowlevel NumMul #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):
|
procedure Test.7 (Test.10, #Attr.12):
|
||||||
let Test.4 = UnionAtIndex (Id 0) (Index 1) #Attr.12;
|
let Test.4 = UnionAtIndex (Id 0) (Index 1) #Attr.12;
|
||||||
let Test.29 = CallByName Num.24 Test.10 Test.4;
|
let Test.28 = CallByName Num.24 Test.10 Test.4;
|
||||||
ret Test.29;
|
ret Test.28;
|
||||||
|
|
||||||
procedure Test.8 (Test.11, #Attr.12):
|
procedure Test.8 (Test.11, #Attr.12):
|
||||||
let Test.6 = UnionAtIndex (Id 1) (Index 2) #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;
|
let Test.13 = CallByName Test.1 Test.14 Test.15;
|
||||||
ret Test.13;
|
ret Test.13;
|
||||||
in
|
in
|
||||||
let Test.28 = true;
|
let Test.27 = true;
|
||||||
if Test.28 then
|
if Test.27 then
|
||||||
let Test.32 = 0i64;
|
let Test.7 = ClosureTag(Test.7) Test.4;
|
||||||
let Test.7 = ClosureTag(Test.7) Test.32 Test.4;
|
|
||||||
jump Test.22 Test.7;
|
jump Test.22 Test.7;
|
||||||
else
|
else
|
||||||
let Test.27 = 1i64;
|
let Test.8 = ClosureTag(Test.8) Test.5 Test.6;
|
||||||
let Test.8 = ClosureTag(Test.8) Test.27 Test.5 Test.6;
|
|
||||||
jump Test.22 Test.8;
|
jump Test.22 Test.8;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
procedure Num.24 (#Attr.2, #Attr.3):
|
procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
let Test.26 = lowlevel NumAdd #Attr.2 #Attr.3;
|
let Test.25 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
ret Test.26;
|
ret Test.25;
|
||||||
|
|
||||||
procedure Num.26 (#Attr.2, #Attr.3):
|
procedure Num.26 (#Attr.2, #Attr.3):
|
||||||
let Test.21 = lowlevel NumMul #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):
|
procedure Test.6 (Test.8, #Attr.12):
|
||||||
let Test.4 = UnionAtIndex (Id 0) (Index 1) #Attr.12;
|
let Test.4 = UnionAtIndex (Id 0) (Index 1) #Attr.12;
|
||||||
let Test.25 = CallByName Num.24 Test.8 Test.4;
|
let Test.24 = CallByName Num.24 Test.8 Test.4;
|
||||||
ret Test.25;
|
ret Test.24;
|
||||||
|
|
||||||
procedure Test.7 (Test.9, #Attr.12):
|
procedure Test.7 (Test.9, #Attr.12):
|
||||||
let Test.5 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
let Test.5 = UnionAtIndex (Id 1) (Index 1) #Attr.12;
|
||||||
|
@ -35,12 +35,10 @@ procedure Test.0 ():
|
||||||
jump Test.15 Test.17;
|
jump Test.15 Test.17;
|
||||||
|
|
||||||
in
|
in
|
||||||
let Test.24 = true;
|
let Test.23 = true;
|
||||||
if Test.24 then
|
if Test.23 then
|
||||||
let Test.28 = 0i64;
|
let Test.6 = ClosureTag(Test.6) Test.4;
|
||||||
let Test.6 = ClosureTag(Test.6) Test.28 Test.4;
|
|
||||||
jump Test.19 Test.6;
|
jump Test.19 Test.6;
|
||||||
else
|
else
|
||||||
let Test.23 = 1i64;
|
let Test.7 = ClosureTag(Test.7) Test.5;
|
||||||
let Test.7 = ClosureTag(Test.7) Test.23 Test.5;
|
|
||||||
jump Test.19 Test.7;
|
jump Test.19 Test.7;
|
||||||
|
|
|
@ -3,11 +3,9 @@ procedure Num.24 (#Attr.2, #Attr.3):
|
||||||
ret Test.8;
|
ret Test.8;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.21 = 0i64;
|
let Test.21 = 41i64;
|
||||||
let Test.23 = 0i64;
|
let Test.20 = Just Test.21;
|
||||||
let Test.22 = 41i64;
|
let Test.2 = Just Test.20;
|
||||||
let Test.20 = Just Test.23 Test.22;
|
|
||||||
let Test.2 = Just Test.21 Test.20;
|
|
||||||
joinpoint Test.17:
|
joinpoint Test.17:
|
||||||
let Test.11 = 1i64;
|
let Test.11 = 1i64;
|
||||||
ret Test.11;
|
ret Test.11;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
procedure Test.1 (Test.5):
|
procedure Test.1 (Test.5):
|
||||||
let Test.20 = 1i64;
|
|
||||||
let Test.19 = 2i64;
|
let Test.19 = 2i64;
|
||||||
let Test.2 = Ok Test.20 Test.19;
|
let Test.2 = Ok Test.19;
|
||||||
joinpoint Test.9 Test.3:
|
joinpoint Test.9 Test.3:
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
in
|
in
|
||||||
|
|
|
@ -28,13 +28,13 @@ arraystring = "0.3.0"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
page_size = "0.4"
|
page_size = "0.4"
|
||||||
winit = "0.24"
|
winit = "0.24"
|
||||||
wgpu = "0.8"
|
wgpu = "0.9"
|
||||||
glyph_brush = "0.7"
|
glyph_brush = "0.7"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
zerocopy = "0.3"
|
zerocopy = "0.3"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
wgpu_glyph = "0.12"
|
wgpu_glyph = "0.13"
|
||||||
cgmath = "0.18.0"
|
cgmath = "0.18.0"
|
||||||
snafu = { version = "0.6", features = ["backtraces"] }
|
snafu = { version = "0.6", features = ["backtraces"] }
|
||||||
colored = "2"
|
colored = "2"
|
||||||
|
|
|
@ -31,7 +31,7 @@ Nice collection of research on innovative editors, [link](https://futureofcoding
|
||||||
* [Self](https://selflanguage.org/) programming language
|
* [Self](https://selflanguage.org/) programming language
|
||||||
* [Primitive](https://primitive.io/) code exploration in Virtual Reality
|
* [Primitive](https://primitive.io/) code exploration in Virtual Reality
|
||||||
* [Luna](https://www.luna-lang.org/) language for interactive data processing and visualization
|
* [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
|
### Debugging
|
||||||
|
|
||||||
* [VS code debug visualization](https://marketplace.visualstudio.com/items?itemName=hediet.debug-visualizer)
|
* [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.
|
* [Lamdu](http://www.lamdu.org/) live functional programming.
|
||||||
* [Sourcetrail](https://www.sourcetrail.com/) nice tree-like source explorer.
|
* [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.
|
* [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
|
### 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 []
|
imports []
|
||||||
provides [ main ] to base
|
provides [ main ] to base
|
||||||
|
|
||||||
greeting =
|
main = "Hello, World!\n"
|
||||||
hi = "Hello"
|
|
||||||
name = "World"
|
|
||||||
|
|
||||||
"\(hi), \(name)!!!!!!!!!!!!!"
|
|
||||||
|
|
||||||
main = greeting
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
void* roc_alloc(size_t size, unsigned int alignment) {
|
void* roc_alloc(size_t size, unsigned int alignment) {
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
|
@ -20,6 +22,29 @@ struct RocStr {
|
||||||
size_t len;
|
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 {
|
struct RocCallResult {
|
||||||
size_t flag;
|
size_t flag;
|
||||||
struct RocStr content;
|
struct RocStr content;
|
||||||
|
@ -27,50 +52,32 @@ struct RocCallResult {
|
||||||
|
|
||||||
extern void roc__mainForHost_1_exposed(struct RocCallResult *re);
|
extern void roc__mainForHost_1_exposed(struct RocCallResult *re);
|
||||||
|
|
||||||
const size_t MAX_STACK_STR_BYTES = 1024;
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// make space for the result
|
// Make space for the Roc call result
|
||||||
struct RocCallResult callresult;
|
struct RocCallResult call_result;
|
||||||
|
|
||||||
// call roc to populate the callresult
|
// Call Roc to populate call_result
|
||||||
roc__mainForHost_1_exposed(&callresult);
|
roc__mainForHost_1_exposed(&call_result);
|
||||||
struct RocStr str = callresult.content;
|
|
||||||
|
|
||||||
// Convert from RocStr to C string (null-terminated char*)
|
// Determine str_len and the str_bytes pointer,
|
||||||
size_t len = str.len;
|
// taking into account the small string optimization.
|
||||||
char* c_str;
|
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.
|
if (is_small_str(str)) {
|
||||||
// (Don't want a stack overflow!)
|
str_bytes = (char*)&str;
|
||||||
if (len <= MAX_STACK_STR_BYTES) {
|
|
||||||
c_str = (char*)alloca(len + 1);
|
|
||||||
} else {
|
} 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
|
return 1;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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