mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 10:02:38 +03:00
Merge branch 'main' into abilities-syntax
This commit is contained in:
commit
7e8a151604
@ -38,7 +38,7 @@ Execute `cargo fmt --all` to fix the formatting.
|
|||||||
If you make changes to [Roc's Standard Library](https://www.roc-lang.org/builtins/Str), you can add comments to the code following [the CommonMark Spec](https://spec.commonmark.org/current/) to further explain your intentions. You can view these changes locally with:
|
If you make changes to [Roc's Standard Library](https://www.roc-lang.org/builtins/Str), you can add comments to the code following [the CommonMark Spec](https://spec.commonmark.org/current/) to further explain your intentions. You can view these changes locally with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo run docs crates/compiler/builtins/roc
|
cargo run docs crates/compiler/builtins/roc/main.roc
|
||||||
```
|
```
|
||||||
|
|
||||||
This command will generate the documentation in the [`generated-docs`](generated-docs) directory.
|
This command will generate the documentation in the [`generated-docs`](generated-docs) directory.
|
||||||
|
@ -120,8 +120,7 @@ fn main() -> io::Result<()> {
|
|||||||
let input_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
|
let input_path = matches.get_one::<PathBuf>(ROC_FILE).unwrap();
|
||||||
let target = matches
|
let target = matches
|
||||||
.get_one::<String>(FLAG_TARGET)
|
.get_one::<String>(FLAG_TARGET)
|
||||||
.map(|s| Target::from_str(s).ok())
|
.and_then(|s| Target::from_str(s).ok())
|
||||||
.flatten()
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
roc_linker::generate_stub_lib(
|
roc_linker::generate_stub_lib(
|
||||||
input_path,
|
input_path,
|
||||||
@ -132,8 +131,7 @@ fn main() -> io::Result<()> {
|
|||||||
Some((CMD_BUILD, matches)) => {
|
Some((CMD_BUILD, matches)) => {
|
||||||
let target = matches
|
let target = matches
|
||||||
.get_one::<String>(FLAG_TARGET)
|
.get_one::<String>(FLAG_TARGET)
|
||||||
.map(|s| Target::from_str(s).ok())
|
.and_then(|s| Target::from_str(s).ok())
|
||||||
.flatten()
|
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let link_type = match (matches.get_flag(FLAG_LIB), matches.get_flag(FLAG_NO_LINK)) {
|
let link_type = match (matches.get_flag(FLAG_LIB), matches.get_flag(FLAG_NO_LINK)) {
|
||||||
(true, false) => LinkType::Dylib,
|
(true, false) => LinkType::Dylib,
|
||||||
|
@ -725,7 +725,7 @@ mod cli_run {
|
|||||||
Arg::PlainText("81"),
|
Arg::PlainText("81"),
|
||||||
],
|
],
|
||||||
&[],
|
&[],
|
||||||
"4\n",
|
"4.000000000000001\n",
|
||||||
UseValgrind::No,
|
UseValgrind::No,
|
||||||
TestCliCommands::Run,
|
TestCliCommands::Run,
|
||||||
)
|
)
|
||||||
|
194
crates/compiler/DESIGN.md
Normal file
194
crates/compiler/DESIGN.md
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
# Compiler Design
|
||||||
|
|
||||||
|
The current Roc compiler is designed as a pipelining compiler parallelizable
|
||||||
|
across Roc modules.
|
||||||
|
|
||||||
|
Roc's compilation pipeline consists of a few major components, which form the
|
||||||
|
table of contents for this document.
|
||||||
|
|
||||||
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
|
|
||||||
|
- [Parsing](#parsing)
|
||||||
|
- [Canonicalization](#canonicalization)
|
||||||
|
- [Symbol Resolution](#symbol-resolution)
|
||||||
|
- [Type-alias normalization](#type-alias-normalization)
|
||||||
|
- [Closure naming](#closure-naming)
|
||||||
|
- [Constraint Generation](#constraint-generation)
|
||||||
|
- [(Mutually-)recursive definitions](#mutually-recursive-definitions)
|
||||||
|
- [Type Solving](#type-solving)
|
||||||
|
- [Unification](#unification)
|
||||||
|
- [Type Inference](#type-inference)
|
||||||
|
- [Recursive Types](#recursive-types)
|
||||||
|
- [Lambda Sets](#lambda-sets)
|
||||||
|
- [Ability Collection](#ability-collection)
|
||||||
|
- [Ability Specialization](#ability-specialization)
|
||||||
|
- [Ability Derivation](#ability-derivation)
|
||||||
|
- [Exhaustiveness Checking](#exhaustiveness-checking)
|
||||||
|
- [Debugging](#debugging)
|
||||||
|
- [IR Generation](#ir-generation)
|
||||||
|
- [Memory Layouts](#memory-layouts)
|
||||||
|
- [Compiling Calls](#compiling-calls)
|
||||||
|
- [Decision Trees](#decision-trees)
|
||||||
|
- [Tail-call Optimization](#tail-call-optimization)
|
||||||
|
- [Reference-count insertion](#reference-count-insertion)
|
||||||
|
- [Reusing Memory Allocations](#reusing-memory-allocations)
|
||||||
|
- [Debugging](#debugging-1)
|
||||||
|
- [LLVM Code Generator](#llvm-code-generator)
|
||||||
|
- [Morphic Analysis](#morphic-analysis)
|
||||||
|
- [C ABI](#c-abi)
|
||||||
|
- [Test Harness](#test-harness)
|
||||||
|
- [Debugging](#debugging-2)
|
||||||
|
- [WASM Code Generator](#wasm-code-generator)
|
||||||
|
- [WASM Interpreter](#wasm-interpreter)
|
||||||
|
- [Debugging](#debugging-3)
|
||||||
|
- [Dev Code Generator](#dev-code-generator)
|
||||||
|
- [Debugging](#debugging-4)
|
||||||
|
- [Builtins](#builtins)
|
||||||
|
- [Compiler Driver](#compiler-driver)
|
||||||
|
- [Caching types](#caching-types)
|
||||||
|
- [Repl](#repl)
|
||||||
|
- [`test` and `dbg`](#test-and-dbg)
|
||||||
|
- [Formatter](#formatter)
|
||||||
|
- [Glue](#glue)
|
||||||
|
- [Active areas of research / help wanted](#active-areas-of-research--help-wanted)
|
||||||
|
|
||||||
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
|
## Parsing
|
||||||
|
|
||||||
|
Roc's parsers are designed as [combinators](https://en.wikipedia.org/wiki/Parser_combinator).
|
||||||
|
A list of Roc's parse AST and combinators can be found in [the root parse
|
||||||
|
file](./parse/src/parser.rs).
|
||||||
|
|
||||||
|
Combinators enable parsing to compose as functions would - for example, the
|
||||||
|
`one_of` combinator supports attempting multiple parsing strategies, and
|
||||||
|
succeeding on the first one; the `and_then` combinator chains two parsers
|
||||||
|
together, failing if either parser in the sequence fails.
|
||||||
|
|
||||||
|
Since Roc is an indentation-sensitive language, parsing must be cognizant and
|
||||||
|
deligent about handling indentation and de-indentation levels. Most parsing
|
||||||
|
functions take a `min_indent` parameter that specifies the minimum indentation
|
||||||
|
of the scope an expression should be parsed in. Generally, failing to reach
|
||||||
|
`min_indent` indicates that an expression has ended (but perhaps too early).
|
||||||
|
|
||||||
|
## Canonicalization
|
||||||
|
|
||||||
|
After parsing a Roc program into an AST, the AST is transformed into a [canonical
|
||||||
|
form](./can/src/expr.rs) AST. This may seem a bit redundant - why build another
|
||||||
|
tree, when we already have the AST? Canonicalization performs a few analyses
|
||||||
|
to catch user errors, and sets up the state necessary to solve the types in a
|
||||||
|
program. Among other things, canonicalization
|
||||||
|
|
||||||
|
- Uniquely identifies names (think variable and function names). Along the way,
|
||||||
|
canonicalization builds a graph of all variables' references, and catches
|
||||||
|
unused definitions, undefined definitions, and shadowed definitions.
|
||||||
|
- Resolves type signatures, including aliases, into a form suitable for type
|
||||||
|
solving.
|
||||||
|
- Determines the order definitions are used in, if they are defined
|
||||||
|
out-of-order.
|
||||||
|
- Eliminates syntax sugar (for example, renaming `+` to the function call `add`
|
||||||
|
and converting backpassing to function calls).
|
||||||
|
- Collects declared abilities, and ability implementations defined for opaque
|
||||||
|
types. Derived abilities for opaque types are elaborated during
|
||||||
|
canonicalization.
|
||||||
|
|
||||||
|
### Symbol Resolution
|
||||||
|
|
||||||
|
Identifiers, like variable names, are resolved to [Symbol](./module/src/symbol.rs)s.
|
||||||
|
|
||||||
|
Currently, a symbol is a 64-bit value with
|
||||||
|
- the bottom 32 bits defining the [ModuleId](./module/src/ident.rs) the symbol
|
||||||
|
is defined in
|
||||||
|
- the top 32 bits defining the [IdentId](./module/src/ident.rs) of the symbol
|
||||||
|
in the module
|
||||||
|
|
||||||
|
A symbol is unique per identifier name and the scope
|
||||||
|
that the identifier has been declared in. Symbols are how the rest of the
|
||||||
|
compiler refers to value definitions - since the unique scope and identifier
|
||||||
|
name is disambiguated when symbols are created, referencing symbols requires no
|
||||||
|
further name resolution.
|
||||||
|
|
||||||
|
As symbols are constructed, canonicalization also keeps track of all references
|
||||||
|
to a given symbol. This simplifies catching unused definitions, undefined
|
||||||
|
definitions, and shadowing, to an index into an array.
|
||||||
|
|
||||||
|
### Type-alias normalization
|
||||||
|
|
||||||
|
### Closure naming
|
||||||
|
|
||||||
|
## Constraint Generation
|
||||||
|
|
||||||
|
### (Mutually-)recursive definitions
|
||||||
|
|
||||||
|
## Type Solving
|
||||||
|
|
||||||
|
### Unification
|
||||||
|
|
||||||
|
### Type Inference
|
||||||
|
|
||||||
|
### Recursive Types
|
||||||
|
|
||||||
|
### Lambda Sets
|
||||||
|
|
||||||
|
### Ability Collection
|
||||||
|
|
||||||
|
### Ability Specialization
|
||||||
|
|
||||||
|
### Ability Derivation
|
||||||
|
|
||||||
|
### Exhaustiveness Checking
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
## IR Generation
|
||||||
|
|
||||||
|
### Memory Layouts
|
||||||
|
|
||||||
|
### Compiling Calls
|
||||||
|
|
||||||
|
### Decision Trees
|
||||||
|
|
||||||
|
### Tail-call Optimization
|
||||||
|
|
||||||
|
### Reference-count insertion
|
||||||
|
|
||||||
|
### Reusing Memory Allocations
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
## LLVM Code Generator
|
||||||
|
|
||||||
|
### Morphic Analysis
|
||||||
|
|
||||||
|
### C ABI
|
||||||
|
|
||||||
|
### Test Harness
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
## WASM Code Generator
|
||||||
|
|
||||||
|
### WASM Interpreter
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
## Dev Code Generator
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
## Builtins
|
||||||
|
|
||||||
|
## Compiler Driver
|
||||||
|
|
||||||
|
### Caching types
|
||||||
|
|
||||||
|
## Repl
|
||||||
|
|
||||||
|
## `test` and `dbg`
|
||||||
|
|
||||||
|
## Formatter
|
||||||
|
|
||||||
|
## Glue
|
||||||
|
|
||||||
|
## Active areas of research / help wanted
|
@ -1,147 +1,8 @@
|
|||||||
# The Roc Compiler
|
# The Roc Compiler
|
||||||
|
|
||||||
Here's how the compiler is laid out.
|
For an overview of the design and architecture of the compiler, see
|
||||||
|
[DESIGN.md](./DESIGN.md). If you want to dive into the
|
||||||
## Parsing
|
implementation or get some tips on debugging the compiler, see below
|
||||||
|
|
||||||
The main goal of parsing is to take a plain old String (such as the contents a .roc source file read from the filesystem) and translate that String into an `Expr` value.
|
|
||||||
|
|
||||||
`Expr` is an `enum` defined in the `expr` module. An `Expr` represents a Roc expression.
|
|
||||||
|
|
||||||
For example, parsing would translate this string...
|
|
||||||
|
|
||||||
"1 + 2"
|
|
||||||
|
|
||||||
...into this `Expr` value:
|
|
||||||
|
|
||||||
BinOp(Int(1), Plus, Int(2))
|
|
||||||
|
|
||||||
> Technically it would be `Box::new(Int(1))` and `Box::new(Int(2))`, but that's beside the point for now.
|
|
||||||
|
|
||||||
This `Expr` representation of the expression is useful for things like:
|
|
||||||
|
|
||||||
- Checking that all variables are declared before they're used
|
|
||||||
- Type checking
|
|
||||||
|
|
||||||
> As of this writing, the compiler doesn't do any of those things yet. They'll be added later!
|
|
||||||
|
|
||||||
Since the parser is only concerned with translating String values into Expr values, it will happily translate syntactically valid strings into expressions that won't work at runtime.
|
|
||||||
|
|
||||||
For example, parsing will translate this string:
|
|
||||||
|
|
||||||
not "foo", "bar"
|
|
||||||
|
|
||||||
...into this `Expr`:
|
|
||||||
|
|
||||||
CallByName("not", vec!["foo", "bar"])
|
|
||||||
|
|
||||||
Now we may know that `not` takes a `Bool` and returns another `Bool`, but the parser doesn't know that.
|
|
||||||
|
|
||||||
The parser only knows how to translate a `String` into an `Expr`; it's the job of other parts of the compiler to figure out if `Expr` values have problems like type mismatches and non-exhaustive patterns.
|
|
||||||
|
|
||||||
That said, the parser can still run into syntax errors. This won't parse:
|
|
||||||
|
|
||||||
if then 5 then else then
|
|
||||||
|
|
||||||
This is gibberish to the parser, so it will produce an error rather than an `Expr`.
|
|
||||||
|
|
||||||
Roc's parser is implemented using the [`marwes/combine`](http://github.com/marwes/combine-language/) crate.
|
|
||||||
|
|
||||||
## Evaluating
|
|
||||||
|
|
||||||
One of the useful things we can do with an `Expr` is to evaluate it.
|
|
||||||
|
|
||||||
The process of evaluation is basically to transform an `Expr` into the simplest `Expr` we can that's still equivalent to the original.
|
|
||||||
|
|
||||||
For example, let's say we had this code:
|
|
||||||
|
|
||||||
"1 + 8 - 3"
|
|
||||||
|
|
||||||
The parser will translate this into the following `Expr`:
|
|
||||||
|
|
||||||
BinOp(
|
|
||||||
Int(1),
|
|
||||||
Plus,
|
|
||||||
BinOp(Int(8), Minus, Int(3))
|
|
||||||
)
|
|
||||||
|
|
||||||
The `eval` function will take this `Expr` and translate it into this much simpler `Expr`:
|
|
||||||
|
|
||||||
Int(6)
|
|
||||||
|
|
||||||
At this point it's become so simple that we can display it to the end user as the number `6`. So running `parse` and then `eval` on the original Roc string of `1 + 8 - 3` will result in displaying `6` as the final output.
|
|
||||||
|
|
||||||
> The `expr` module includes an `impl fmt::Display for Expr` that takes care of translating `Int(6)` into `6`, `Char('x')` as `'x'`, and so on.
|
|
||||||
|
|
||||||
`eval` accomplishes this by doing a `match` on an `Expr` and resolving every operation it encounters. For example, when it first sees this:
|
|
||||||
|
|
||||||
BinOp(
|
|
||||||
Int(1),
|
|
||||||
Plus,
|
|
||||||
BinOp(Int(8), Minus, Int(3))
|
|
||||||
)
|
|
||||||
|
|
||||||
The first thing it does is to call `eval` on the right `Expr` values on either side of the `Plus`. That results in:
|
|
||||||
|
|
||||||
1. Calling `eval` on `Int(1)`, which returns `Int(1)` since it can't be reduced any further.
|
|
||||||
2. Calling `eval` on `BinOp(Int(8), Minus, Int(3))`, which in fact can be reduced further.
|
|
||||||
|
|
||||||
Since the second call to `eval` will match on another `BinOp`, it's once again going to recursively call `eval` on both of its `Expr` values. Since those are both `Int` values, though, their `eval` calls will return them right away without doing anything else.
|
|
||||||
|
|
||||||
Now that it's evaluated the expressions on either side of the `Minus`, `eval` will look at the particular operator being applied to those expressions (in this case, a minus operator) and check to see if the expressions it was given work with that operation.
|
|
||||||
|
|
||||||
> Remember, this `Expr` value potentially came directly from the parser. `eval` can't be sure any type checking has been done on it!
|
|
||||||
|
|
||||||
If `eval` detects a non-numeric `Expr` value (that is, the `Expr` is not `Int` or `Frac`) on either side of the `Minus`, then it will immediately give an error and halt the evaluation. This sort of runtime type error is common to dynamic languages, and you can think of `eval` as being a dynamic evaluation of Roc code that hasn't necessarily been type-checked.
|
|
||||||
|
|
||||||
Assuming there's no type problem, `eval` can go ahead and run the Rust code of `8 - 3` and store the result in an `Int` expr.
|
|
||||||
|
|
||||||
That concludes our original recursive call to `eval`, after which point we'll be evaluating this expression:
|
|
||||||
|
|
||||||
BinOp(
|
|
||||||
Int(1),
|
|
||||||
Plus,
|
|
||||||
Int(5)
|
|
||||||
)
|
|
||||||
|
|
||||||
This will work the same way as `Minus` did, and will reduce down to `Int(6)`.
|
|
||||||
|
|
||||||
## Optimization philosophy
|
|
||||||
|
|
||||||
Focus on optimizations which are only safe in the absence of side effects, and leave the rest to LLVM.
|
|
||||||
|
|
||||||
This focus may lead to some optimizations becoming transitively in scope. For example, some deforestation
|
|
||||||
examples in the MSR paper benefit from multiple rounds of interleaved deforestation, beta-reduction, and inlining.
|
|
||||||
To get those benefits, we'd have to do some inlining and beta-reduction that we could otherwise leave to LLVM's
|
|
||||||
inlining and constant propagation/folding.
|
|
||||||
|
|
||||||
Even if we're doing those things, it may still make sense to have LLVM do a pass for them as well, since
|
|
||||||
early LLVM optimization passes may unlock later opportunities for inlining and constant propagation/folding.
|
|
||||||
|
|
||||||
## Inlining
|
|
||||||
|
|
||||||
If a function is called exactly once (it's a helper function), presumably we always want to inline those.
|
|
||||||
If a function is "small enough" it's probably worth inlining too.
|
|
||||||
|
|
||||||
## Fusion
|
|
||||||
|
|
||||||
<https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/deforestation-short-cut.pdf>
|
|
||||||
|
|
||||||
Basic approach:
|
|
||||||
|
|
||||||
Do list stuff using `build` passing Cons Nil (like a cons list) and then do foldr/build substitution/reduction.
|
|
||||||
Afterwards, we can do a separate pass to flatten nested Cons structures into properly initialized RRBTs.
|
|
||||||
This way we get both deforestation and efficient RRBT construction. Should work for the other collection types too.
|
|
||||||
|
|
||||||
It looks like we need to do some amount of inlining and beta reductions on the Roc side, rather than
|
|
||||||
leaving all of those to LLVM.
|
|
||||||
|
|
||||||
Advanced approach:
|
|
||||||
|
|
||||||
Express operations like map and filter in terms of toStream and fromStream, to unlock more deforestation.
|
|
||||||
More info on here:
|
|
||||||
|
|
||||||
<https://wiki.haskell.org/GHC_optimisations#Fusion>
|
|
||||||
|
|
||||||
## Getting started with the code
|
## Getting started with the code
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ pub fn build(b: *Builder) void {
|
|||||||
// Tests
|
// Tests
|
||||||
var main_tests = b.addTest(main_path);
|
var main_tests = b.addTest(main_path);
|
||||||
main_tests.setBuildMode(mode);
|
main_tests.setBuildMode(mode);
|
||||||
main_tests.linkSystemLibrary("c");
|
main_tests.linkLibC();
|
||||||
const test_step = b.step("test", "Run tests");
|
const test_step = b.step("test", "Run tests");
|
||||||
test_step.dependOn(&main_tests.step);
|
test_step.dependOn(&main_tests.step);
|
||||||
|
|
||||||
|
442
crates/compiler/builtins/bitcode/src/compiler_rt.zig
Normal file
442
crates/compiler/builtins/bitcode/src/compiler_rt.zig
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
const math = std.math;
|
||||||
|
|
||||||
|
// Eventually, we need to statically ingest compiler-rt and get it working with the surgical linker, then these should not be needed anymore.
|
||||||
|
// Until then, we are manually ingesting used parts of compiler-rt here.
|
||||||
|
//
|
||||||
|
// Taken from
|
||||||
|
// https://github.com/ziglang/zig/tree/4976b58ab16069f8d3267b69ed030f29685c1abe/lib/compiler_rt/
|
||||||
|
// Thank you Zig Contributors!
|
||||||
|
|
||||||
|
// Libcalls that involve u128 on Windows x86-64 are expected by LLVM to use the
|
||||||
|
// calling convention of @Vector(2, u64), rather than what's standard.
|
||||||
|
pub const want_windows_v2u64_abi = builtin.os.tag == .windows and builtin.cpu.arch == .x86_64 and @import("builtin").object_format != .c;
|
||||||
|
|
||||||
|
const v2u64 = @Vector(2, u64);
|
||||||
|
|
||||||
|
// Export it as weak incase it is already linked in by something else.
|
||||||
|
comptime {
|
||||||
|
@export(__muloti4, .{ .name = "__muloti4", .linkage = .Weak });
|
||||||
|
if (want_windows_v2u64_abi) {
|
||||||
|
@export(__divti3_windows_x86_64, .{ .name = "__divti3", .linkage = .Weak });
|
||||||
|
@export(__modti3_windows_x86_64, .{ .name = "__modti3", .linkage = .Weak });
|
||||||
|
@export(__umodti3_windows_x86_64, .{ .name = "__umodti3", .linkage = .Weak });
|
||||||
|
@export(__udivti3_windows_x86_64, .{ .name = "__udivti3", .linkage = .Weak });
|
||||||
|
@export(__fixdfti_windows_x86_64, .{ .name = "__fixdfti", .linkage = .Weak });
|
||||||
|
@export(__fixsfti_windows_x86_64, .{ .name = "__fixsfti", .linkage = .Weak });
|
||||||
|
@export(__fixunsdfti_windows_x86_64, .{ .name = "__fixunsdfti", .linkage = .Weak });
|
||||||
|
@export(__fixunssfti_windows_x86_64, .{ .name = "__fixunssfti", .linkage = .Weak });
|
||||||
|
} else {
|
||||||
|
@export(__divti3, .{ .name = "__divti3", .linkage = .Weak });
|
||||||
|
@export(__modti3, .{ .name = "__modti3", .linkage = .Weak });
|
||||||
|
@export(__umodti3, .{ .name = "__umodti3", .linkage = .Weak });
|
||||||
|
@export(__udivti3, .{ .name = "__udivti3", .linkage = .Weak });
|
||||||
|
@export(__fixdfti, .{ .name = "__fixdfti", .linkage = .Weak });
|
||||||
|
@export(__fixsfti, .{ .name = "__fixsfti", .linkage = .Weak });
|
||||||
|
@export(__fixunsdfti, .{ .name = "__fixunsdfti", .linkage = .Weak });
|
||||||
|
@export(__fixunssfti, .{ .name = "__fixunssfti", .linkage = .Weak });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
|
||||||
|
if (2 * @bitSizeOf(i128) <= @bitSizeOf(usize)) {
|
||||||
|
return muloXi4_genericFast(i128, a, b, overflow);
|
||||||
|
} else {
|
||||||
|
return muloXi4_genericSmall(i128, a, b, overflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __divti3(a: i128, b: i128) callconv(.C) i128 {
|
||||||
|
return div(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __divti3_windows_x86_64(a: v2u64, b: v2u64) callconv(.C) v2u64 {
|
||||||
|
return @bitCast(v2u64, div(@bitCast(i128, a), @bitCast(i128, b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn div(a: i128, b: i128) i128 {
|
||||||
|
const s_a = a >> (128 - 1);
|
||||||
|
const s_b = b >> (128 - 1);
|
||||||
|
|
||||||
|
const an = (a ^ s_a) -% s_a;
|
||||||
|
const bn = (b ^ s_b) -% s_b;
|
||||||
|
|
||||||
|
const r = udivmod(u128, @bitCast(u128, an), @bitCast(u128, bn), null);
|
||||||
|
const s = s_a ^ s_b;
|
||||||
|
return (@bitCast(i128, r) ^ s) -% s;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __udivti3(a: u128, b: u128) callconv(.C) u128 {
|
||||||
|
return udivmod(u128, a, b, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __udivti3_windows_x86_64(a: v2u64, b: v2u64) callconv(.C) v2u64 {
|
||||||
|
return @bitCast(v2u64, udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __umodti3(a: u128, b: u128) callconv(.C) u128 {
|
||||||
|
var r: u128 = undefined;
|
||||||
|
_ = udivmod(u128, a, b, &r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __umodti3_windows_x86_64(a: v2u64, b: v2u64) callconv(.C) v2u64 {
|
||||||
|
var r: u128 = undefined;
|
||||||
|
_ = udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), &r);
|
||||||
|
return @bitCast(v2u64, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __modti3(a: i128, b: i128) callconv(.C) i128 {
|
||||||
|
return mod(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __modti3_windows_x86_64(a: v2u64, b: v2u64) callconv(.C) v2u64 {
|
||||||
|
return @bitCast(v2u64, mod(@bitCast(i128, a), @bitCast(i128, b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn mod(a: i128, b: i128) i128 {
|
||||||
|
const s_a = a >> (128 - 1); // s = a < 0 ? -1 : 0
|
||||||
|
const s_b = b >> (128 - 1); // s = b < 0 ? -1 : 0
|
||||||
|
|
||||||
|
const an = (a ^ s_a) -% s_a; // negate if s == -1
|
||||||
|
const bn = (b ^ s_b) -% s_b; // negate if s == -1
|
||||||
|
|
||||||
|
var r: u128 = undefined;
|
||||||
|
_ = udivmod(u128, @bitCast(u128, an), @bitCast(u128, bn), &r);
|
||||||
|
return (@bitCast(i128, r) ^ s_a) -% s_a; // negate if s == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __fixdfti(a: f64) callconv(.C) i128 {
|
||||||
|
return floatToInt(i128, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __fixdfti_windows_x86_64(a: f64) callconv(.C) v2u64 {
|
||||||
|
return @bitCast(v2u64, floatToInt(i128, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __fixsfti(a: f32) callconv(.C) i128 {
|
||||||
|
return floatToInt(i128, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __fixsfti_windows_x86_64(a: f32) callconv(.C) v2u64 {
|
||||||
|
return @bitCast(v2u64, floatToInt(i128, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __fixunsdfti(a: f64) callconv(.C) u128 {
|
||||||
|
return floatToInt(u128, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __fixunsdfti_windows_x86_64(a: f64) callconv(.C) v2u64 {
|
||||||
|
return @bitCast(v2u64, floatToInt(u128, a));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn __fixunssfti(a: f32) callconv(.C) u128 {
|
||||||
|
return floatToInt(u128, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn __fixunssfti_windows_x86_64(a: f32) callconv(.C) v2u64 {
|
||||||
|
return @bitCast(v2u64, floatToInt(u128, a));
|
||||||
|
}
|
||||||
|
// mulo - multiplication overflow
|
||||||
|
// * return a*%b.
|
||||||
|
// * return if a*b overflows => 1 else => 0
|
||||||
|
// - muloXi4_genericSmall as default
|
||||||
|
// - muloXi4_genericFast for 2*bitsize <= usize
|
||||||
|
|
||||||
|
inline fn muloXi4_genericSmall(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST {
|
||||||
|
overflow.* = 0;
|
||||||
|
const min = math.minInt(ST);
|
||||||
|
var res: ST = a *% b;
|
||||||
|
// Hacker's Delight section Overflow subsection Multiplication
|
||||||
|
// case a=-2^{31}, b=-1 problem, because
|
||||||
|
// on some machines a*b = -2^{31} with overflow
|
||||||
|
// Then -2^{31}/-1 overflows and any result is possible.
|
||||||
|
// => check with a<0 and b=-2^{31}
|
||||||
|
if ((a < 0 and b == min) or (a != 0 and @divTrunc(res, a) != b))
|
||||||
|
overflow.* = 1;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn muloXi4_genericFast(comptime ST: type, a: ST, b: ST, overflow: *c_int) ST {
|
||||||
|
overflow.* = 0;
|
||||||
|
const EST = switch (ST) {
|
||||||
|
i32 => i64,
|
||||||
|
i64 => i128,
|
||||||
|
i128 => i256,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
const min = math.minInt(ST);
|
||||||
|
const max = math.maxInt(ST);
|
||||||
|
var res: EST = @as(EST, a) * @as(EST, b);
|
||||||
|
//invariant: -2^{bitwidth(EST)} < res < 2^{bitwidth(EST)-1}
|
||||||
|
if (res < min or max < res)
|
||||||
|
overflow.* = 1;
|
||||||
|
return @truncate(ST, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const native_endian = builtin.cpu.arch.endian();
|
||||||
|
const low = switch (native_endian) {
|
||||||
|
.Big => 1,
|
||||||
|
.Little => 0,
|
||||||
|
};
|
||||||
|
const high = 1 - low;
|
||||||
|
|
||||||
|
pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: ?*DoubleInt) DoubleInt {
|
||||||
|
// @setRuntimeSafety(builtin.is_test);
|
||||||
|
|
||||||
|
const double_int_bits = @typeInfo(DoubleInt).Int.bits;
|
||||||
|
const single_int_bits = @divExact(double_int_bits, 2);
|
||||||
|
const SingleInt = std.meta.Int(.unsigned, single_int_bits);
|
||||||
|
const SignedDoubleInt = std.meta.Int(.signed, double_int_bits);
|
||||||
|
const Log2SingleInt = std.math.Log2Int(SingleInt);
|
||||||
|
|
||||||
|
const n = @bitCast([2]SingleInt, a);
|
||||||
|
const d = @bitCast([2]SingleInt, b);
|
||||||
|
var q: [2]SingleInt = undefined;
|
||||||
|
var r: [2]SingleInt = undefined;
|
||||||
|
var sr: c_uint = undefined;
|
||||||
|
// special cases, X is unknown, K != 0
|
||||||
|
if (n[high] == 0) {
|
||||||
|
if (d[high] == 0) {
|
||||||
|
// 0 X
|
||||||
|
// ---
|
||||||
|
// 0 X
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
rem.* = n[low] % d[low];
|
||||||
|
}
|
||||||
|
return n[low] / d[low];
|
||||||
|
}
|
||||||
|
// 0 X
|
||||||
|
// ---
|
||||||
|
// K X
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
rem.* = n[low];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// n[high] != 0
|
||||||
|
if (d[low] == 0) {
|
||||||
|
if (d[high] == 0) {
|
||||||
|
// K X
|
||||||
|
// ---
|
||||||
|
// 0 0
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
rem.* = n[high] % d[low];
|
||||||
|
}
|
||||||
|
return n[high] / d[low];
|
||||||
|
}
|
||||||
|
// d[high] != 0
|
||||||
|
if (n[low] == 0) {
|
||||||
|
// K 0
|
||||||
|
// ---
|
||||||
|
// K 0
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
r[high] = n[high] % d[high];
|
||||||
|
r[low] = 0;
|
||||||
|
rem.* = @bitCast(DoubleInt, r);
|
||||||
|
}
|
||||||
|
return n[high] / d[high];
|
||||||
|
}
|
||||||
|
// K K
|
||||||
|
// ---
|
||||||
|
// K 0
|
||||||
|
if ((d[high] & (d[high] - 1)) == 0) {
|
||||||
|
// d is a power of 2
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
r[low] = n[low];
|
||||||
|
r[high] = n[high] & (d[high] - 1);
|
||||||
|
rem.* = @bitCast(DoubleInt, r);
|
||||||
|
}
|
||||||
|
return n[high] >> @intCast(Log2SingleInt, @ctz(SingleInt, d[high]));
|
||||||
|
}
|
||||||
|
// K K
|
||||||
|
// ---
|
||||||
|
// K 0
|
||||||
|
sr = @bitCast(c_uint, @as(c_int, @clz(SingleInt, d[high])) - @as(c_int, @clz(SingleInt, n[high])));
|
||||||
|
// 0 <= sr <= single_int_bits - 2 or sr large
|
||||||
|
if (sr > single_int_bits - 2) {
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
rem.* = a;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sr += 1;
|
||||||
|
// 1 <= sr <= single_int_bits - 1
|
||||||
|
// q.all = a << (double_int_bits - sr);
|
||||||
|
q[low] = 0;
|
||||||
|
q[high] = n[low] << @intCast(Log2SingleInt, single_int_bits - sr);
|
||||||
|
// r.all = a >> sr;
|
||||||
|
r[high] = n[high] >> @intCast(Log2SingleInt, sr);
|
||||||
|
r[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
|
||||||
|
} else {
|
||||||
|
// d[low] != 0
|
||||||
|
if (d[high] == 0) {
|
||||||
|
// K X
|
||||||
|
// ---
|
||||||
|
// 0 K
|
||||||
|
if ((d[low] & (d[low] - 1)) == 0) {
|
||||||
|
// d is a power of 2
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
rem.* = n[low] & (d[low] - 1);
|
||||||
|
}
|
||||||
|
if (d[low] == 1) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
sr = @ctz(SingleInt, d[low]);
|
||||||
|
q[high] = n[high] >> @intCast(Log2SingleInt, sr);
|
||||||
|
q[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
|
||||||
|
return @bitCast(DoubleInt, q);
|
||||||
|
}
|
||||||
|
// K X
|
||||||
|
// ---
|
||||||
|
// 0 K
|
||||||
|
sr = 1 + single_int_bits + @as(c_uint, @clz(SingleInt, d[low])) - @as(c_uint, @clz(SingleInt, n[high]));
|
||||||
|
// 2 <= sr <= double_int_bits - 1
|
||||||
|
// q.all = a << (double_int_bits - sr);
|
||||||
|
// r.all = a >> sr;
|
||||||
|
if (sr == single_int_bits) {
|
||||||
|
q[low] = 0;
|
||||||
|
q[high] = n[low];
|
||||||
|
r[high] = 0;
|
||||||
|
r[low] = n[high];
|
||||||
|
} else if (sr < single_int_bits) {
|
||||||
|
// 2 <= sr <= single_int_bits - 1
|
||||||
|
q[low] = 0;
|
||||||
|
q[high] = n[low] << @intCast(Log2SingleInt, single_int_bits - sr);
|
||||||
|
r[high] = n[high] >> @intCast(Log2SingleInt, sr);
|
||||||
|
r[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
|
||||||
|
} else {
|
||||||
|
// single_int_bits + 1 <= sr <= double_int_bits - 1
|
||||||
|
q[low] = n[low] << @intCast(Log2SingleInt, double_int_bits - sr);
|
||||||
|
q[high] = (n[high] << @intCast(Log2SingleInt, double_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr - single_int_bits));
|
||||||
|
r[high] = 0;
|
||||||
|
r[low] = n[high] >> @intCast(Log2SingleInt, sr - single_int_bits);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// K X
|
||||||
|
// ---
|
||||||
|
// K K
|
||||||
|
sr = @bitCast(c_uint, @as(c_int, @clz(SingleInt, d[high])) - @as(c_int, @clz(SingleInt, n[high])));
|
||||||
|
// 0 <= sr <= single_int_bits - 1 or sr large
|
||||||
|
if (sr > single_int_bits - 1) {
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
rem.* = a;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
sr += 1;
|
||||||
|
// 1 <= sr <= single_int_bits
|
||||||
|
// q.all = a << (double_int_bits - sr);
|
||||||
|
// r.all = a >> sr;
|
||||||
|
q[low] = 0;
|
||||||
|
if (sr == single_int_bits) {
|
||||||
|
q[high] = n[low];
|
||||||
|
r[high] = 0;
|
||||||
|
r[low] = n[high];
|
||||||
|
} else {
|
||||||
|
r[high] = n[high] >> @intCast(Log2SingleInt, sr);
|
||||||
|
r[low] = (n[high] << @intCast(Log2SingleInt, single_int_bits - sr)) | (n[low] >> @intCast(Log2SingleInt, sr));
|
||||||
|
q[high] = n[low] << @intCast(Log2SingleInt, single_int_bits - sr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Not a special case
|
||||||
|
// q and r are initialized with:
|
||||||
|
// q.all = a << (double_int_bits - sr);
|
||||||
|
// r.all = a >> sr;
|
||||||
|
// 1 <= sr <= double_int_bits - 1
|
||||||
|
var carry: u32 = 0;
|
||||||
|
var r_all: DoubleInt = undefined;
|
||||||
|
while (sr > 0) : (sr -= 1) {
|
||||||
|
// r:q = ((r:q) << 1) | carry
|
||||||
|
r[high] = (r[high] << 1) | (r[low] >> (single_int_bits - 1));
|
||||||
|
r[low] = (r[low] << 1) | (q[high] >> (single_int_bits - 1));
|
||||||
|
q[high] = (q[high] << 1) | (q[low] >> (single_int_bits - 1));
|
||||||
|
q[low] = (q[low] << 1) | carry;
|
||||||
|
// carry = 0;
|
||||||
|
// if (r.all >= b)
|
||||||
|
// {
|
||||||
|
// r.all -= b;
|
||||||
|
// carry = 1;
|
||||||
|
// }
|
||||||
|
r_all = @bitCast(DoubleInt, r);
|
||||||
|
const s: SignedDoubleInt = @bitCast(SignedDoubleInt, b -% r_all -% 1) >> (double_int_bits - 1);
|
||||||
|
carry = @intCast(u32, s & 1);
|
||||||
|
r_all -= b & @bitCast(DoubleInt, s);
|
||||||
|
r = @bitCast([2]SingleInt, r_all);
|
||||||
|
}
|
||||||
|
const q_all = (@bitCast(DoubleInt, q) << 1) | carry;
|
||||||
|
if (maybe_rem) |rem| {
|
||||||
|
rem.* = r_all;
|
||||||
|
}
|
||||||
|
return q_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn floatToInt(comptime I: type, a: anytype) I {
|
||||||
|
const Log2Int = math.Log2Int;
|
||||||
|
const Int = @import("std").meta.Int;
|
||||||
|
const F = @TypeOf(a);
|
||||||
|
const float_bits = @typeInfo(F).Float.bits;
|
||||||
|
const int_bits = @typeInfo(I).Int.bits;
|
||||||
|
const rep_t = Int(.unsigned, float_bits);
|
||||||
|
const sig_bits = math.floatMantissaBits(F);
|
||||||
|
const exp_bits = math.floatExponentBits(F);
|
||||||
|
const fractional_bits = floatFractionalBits(F);
|
||||||
|
|
||||||
|
// const implicit_bit = if (F != f80) (@as(rep_t, 1) << sig_bits) else 0;
|
||||||
|
const implicit_bit = @as(rep_t, 1) << sig_bits;
|
||||||
|
const max_exp = (1 << (exp_bits - 1));
|
||||||
|
const exp_bias = max_exp - 1;
|
||||||
|
const sig_mask = (@as(rep_t, 1) << sig_bits) - 1;
|
||||||
|
|
||||||
|
// Break a into sign, exponent, significand
|
||||||
|
const a_rep: rep_t = @bitCast(rep_t, a);
|
||||||
|
const negative = (a_rep >> (float_bits - 1)) != 0;
|
||||||
|
const exponent = @intCast(i32, (a_rep << 1) >> (sig_bits + 1)) - exp_bias;
|
||||||
|
const significand: rep_t = (a_rep & sig_mask) | implicit_bit;
|
||||||
|
|
||||||
|
// If the exponent is negative, the result rounds to zero.
|
||||||
|
if (exponent < 0) return 0;
|
||||||
|
|
||||||
|
// If the value is too large for the integer type, saturate.
|
||||||
|
switch (@typeInfo(I).Int.signedness) {
|
||||||
|
.unsigned => {
|
||||||
|
if (negative) return 0;
|
||||||
|
if (@intCast(c_uint, exponent) >= @minimum(int_bits, max_exp)) return math.maxInt(I);
|
||||||
|
},
|
||||||
|
.signed => if (@intCast(c_uint, exponent) >= @minimum(int_bits - 1, max_exp)) {
|
||||||
|
return if (negative) math.minInt(I) else math.maxInt(I);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// If 0 <= exponent < sig_bits, right shift to get the result.
|
||||||
|
// Otherwise, shift left.
|
||||||
|
var result: I = undefined;
|
||||||
|
if (exponent < fractional_bits) {
|
||||||
|
result = @intCast(I, significand >> @intCast(Log2Int(rep_t), fractional_bits - exponent));
|
||||||
|
} else {
|
||||||
|
result = @intCast(I, significand) << @intCast(Log2Int(I), exponent - fractional_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((@typeInfo(I).Int.signedness == .signed) and negative)
|
||||||
|
return ~result +% 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of fractional bits in the mantissa of floating point type T.
|
||||||
|
pub inline fn floatFractionalBits(comptime T: type) comptime_int {
|
||||||
|
comptime std.debug.assert(@typeInfo(T) == .Float);
|
||||||
|
|
||||||
|
// standard IEEE floats have an implicit 0.m or 1.m integer part
|
||||||
|
// f80 is special and has an explicitly stored bit in the MSB
|
||||||
|
// this function corresponds to `MANT_DIG - 1' from C
|
||||||
|
return switch (@typeInfo(T).Float.bits) {
|
||||||
|
16 => 10,
|
||||||
|
32 => 23,
|
||||||
|
64 => 52,
|
||||||
|
80 => 63,
|
||||||
|
128 => 112,
|
||||||
|
else => @compileError("unknown floating point type " ++ @typeName(T)),
|
||||||
|
};
|
||||||
|
}
|
@ -44,6 +44,8 @@ pub const RocDec = extern struct {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: If Str.toDec eventually supports more error types, return errors here.
|
||||||
|
// For now, just return null which will give the default error.
|
||||||
pub fn fromStr(roc_str: RocStr) ?RocDec {
|
pub fn fromStr(roc_str: RocStr) ?RocDec {
|
||||||
if (roc_str.isEmpty()) {
|
if (roc_str.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
@ -79,7 +81,8 @@ pub const RocDec = extern struct {
|
|||||||
|
|
||||||
var after_str_len = (length - 1) - pi;
|
var after_str_len = (length - 1) - pi;
|
||||||
if (after_str_len > decimal_places) {
|
if (after_str_len > decimal_places) {
|
||||||
@panic("TODO runtime exception for too many decimal places!");
|
// TODO: runtime exception for too many decimal places!
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
var diff_decimal_places = decimal_places - after_str_len;
|
var diff_decimal_places = decimal_places - after_str_len;
|
||||||
|
|
||||||
@ -96,7 +99,8 @@ pub const RocDec = extern struct {
|
|||||||
var result: i128 = undefined;
|
var result: i128 = undefined;
|
||||||
var overflowed = @mulWithOverflow(i128, before, one_point_zero_i128, &result);
|
var overflowed = @mulWithOverflow(i128, before, one_point_zero_i128, &result);
|
||||||
if (overflowed) {
|
if (overflowed) {
|
||||||
@panic("TODO runtime exception for overflow!");
|
// TODO: runtime exception for overflow!
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
before_val_i128 = result;
|
before_val_i128 = result;
|
||||||
}
|
}
|
||||||
@ -107,7 +111,8 @@ pub const RocDec = extern struct {
|
|||||||
var result: i128 = undefined;
|
var result: i128 = undefined;
|
||||||
var overflowed = @addWithOverflow(i128, before, after, &result);
|
var overflowed = @addWithOverflow(i128, before, after, &result);
|
||||||
if (overflowed) {
|
if (overflowed) {
|
||||||
@panic("TODO runtime exception for overflow!");
|
// TODO: runtime exception for overflow!
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
break :blk .{ .num = result };
|
break :blk .{ .num = result };
|
||||||
} else {
|
} else {
|
||||||
@ -184,7 +189,7 @@ pub const RocDec = extern struct {
|
|||||||
position += 1;
|
position += 1;
|
||||||
|
|
||||||
const trailing_zeros: u6 = count_trailing_zeros_base10(num);
|
const trailing_zeros: u6 = count_trailing_zeros_base10(num);
|
||||||
if (trailing_zeros == decimal_places) {
|
if (trailing_zeros >= decimal_places) {
|
||||||
// add just a single zero if all decimal digits are zero
|
// add just a single zero if all decimal digits are zero
|
||||||
str_bytes[position] = '0';
|
str_bytes[position] = '0';
|
||||||
position += 1;
|
position += 1;
|
||||||
@ -851,6 +856,14 @@ test "fromStr: .123.1" {
|
|||||||
try expectEqual(dec, null);
|
try expectEqual(dec, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "toStr: 100.00" {
|
||||||
|
var dec: RocDec = .{ .num = 100000000000000000000 };
|
||||||
|
var res_roc_str = dec.toStr();
|
||||||
|
|
||||||
|
const res_slice: []const u8 = "100.0"[0..];
|
||||||
|
try expectEqualSlices(u8, res_slice, res_roc_str.asSlice());
|
||||||
|
}
|
||||||
|
|
||||||
test "toStr: 123.45" {
|
test "toStr: 123.45" {
|
||||||
var dec: RocDec = .{ .num = 123450000000000000000 };
|
var dec: RocDec = .{ .num = 123450000000000000000 };
|
||||||
var res_roc_str = dec.toStr();
|
var res_roc_str = dec.toStr();
|
||||||
|
@ -5,6 +5,10 @@ const utils = @import("utils.zig");
|
|||||||
const expect = @import("expect.zig");
|
const expect = @import("expect.zig");
|
||||||
const panic_utils = @import("panic.zig");
|
const panic_utils = @import("panic.zig");
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
_ = @import("compiler_rt.zig");
|
||||||
|
}
|
||||||
|
|
||||||
const ROC_BUILTINS = "roc_builtins";
|
const ROC_BUILTINS = "roc_builtins";
|
||||||
const NUM = "num";
|
const NUM = "num";
|
||||||
const STR = "str";
|
const STR = "str";
|
||||||
@ -81,8 +85,12 @@ comptime {
|
|||||||
num.exportPow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".pow_int.");
|
num.exportPow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".pow_int.");
|
||||||
num.exportDivCeil(T, ROC_BUILTINS ++ "." ++ NUM ++ ".div_ceil.");
|
num.exportDivCeil(T, ROC_BUILTINS ++ "." ++ NUM ++ ".div_ceil.");
|
||||||
|
|
||||||
num.exportRoundF32(T, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f32.");
|
num.exportRound(f32, T, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f32.");
|
||||||
num.exportRoundF64(T, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f64.");
|
num.exportRound(f64, T, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f64.");
|
||||||
|
num.exportFloor(f32, T, ROC_BUILTINS ++ "." ++ NUM ++ ".floor_f32.");
|
||||||
|
num.exportFloor(f64, T, ROC_BUILTINS ++ "." ++ NUM ++ ".floor_f64.");
|
||||||
|
num.exportCeiling(f32, T, ROC_BUILTINS ++ "." ++ NUM ++ ".ceiling_f32.");
|
||||||
|
num.exportCeiling(f64, T, ROC_BUILTINS ++ "." ++ NUM ++ ".ceiling_f64.");
|
||||||
|
|
||||||
num.exportAddWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_with_overflow.");
|
num.exportAddWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_with_overflow.");
|
||||||
num.exportAddOrPanic(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_or_panic.");
|
num.exportAddOrPanic(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_or_panic.");
|
||||||
@ -122,6 +130,8 @@ comptime {
|
|||||||
|
|
||||||
num.exportPow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".pow.");
|
num.exportPow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".pow.");
|
||||||
num.exportLog(T, ROC_BUILTINS ++ "." ++ NUM ++ ".log.");
|
num.exportLog(T, ROC_BUILTINS ++ "." ++ NUM ++ ".log.");
|
||||||
|
num.exportFAbs(T, ROC_BUILTINS ++ "." ++ NUM ++ ".fabs.");
|
||||||
|
num.exportSqrt(T, ROC_BUILTINS ++ "." ++ NUM ++ ".sqrt.");
|
||||||
|
|
||||||
num.exportAddWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_with_overflow.");
|
num.exportAddWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".add_with_overflow.");
|
||||||
num.exportSubWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".sub_with_overflow.");
|
num.exportSubWithOverflow(T, ROC_BUILTINS ++ "." ++ NUM ++ ".sub_with_overflow.");
|
||||||
@ -274,60 +284,3 @@ test "" {
|
|||||||
|
|
||||||
testing.refAllDecls(@This());
|
testing.refAllDecls(@This());
|
||||||
}
|
}
|
||||||
|
|
||||||
// For unclear reasons, sometimes this function is not linked in on some machines.
|
|
||||||
// Therefore we provide it as LLVM bitcode and mark it as externally linked during our LLVM codegen
|
|
||||||
//
|
|
||||||
// Taken from
|
|
||||||
// https://github.com/ziglang/zig/blob/85755c51d529e7d9b406c6bdf69ce0a0f33f3353/lib/std/special/compiler_rt/muloti4.zig
|
|
||||||
//
|
|
||||||
// Thank you Zig Contributors!
|
|
||||||
|
|
||||||
// Export it as weak incase it is already linked in by something else.
|
|
||||||
comptime {
|
|
||||||
if (builtin.target.os.tag != .windows) {
|
|
||||||
@export(__muloti4, .{ .name = "__muloti4", .linkage = .Weak });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn __muloti4(a: i128, b: i128, overflow: *c_int) callconv(.C) i128 {
|
|
||||||
// @setRuntimeSafety(std.builtin.is_test);
|
|
||||||
|
|
||||||
const min = @bitCast(i128, @as(u128, 1 << (128 - 1)));
|
|
||||||
const max = ~min;
|
|
||||||
overflow.* = 0;
|
|
||||||
|
|
||||||
const r = a *% b;
|
|
||||||
if (a == min) {
|
|
||||||
if (b != 0 and b != 1) {
|
|
||||||
overflow.* = 1;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
if (b == min) {
|
|
||||||
if (a != 0 and a != 1) {
|
|
||||||
overflow.* = 1;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sa = a >> (128 - 1);
|
|
||||||
const abs_a = (a ^ sa) -% sa;
|
|
||||||
const sb = b >> (128 - 1);
|
|
||||||
const abs_b = (b ^ sb) -% sb;
|
|
||||||
|
|
||||||
if (abs_a < 2 or abs_b < 2) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sa == sb) {
|
|
||||||
if (abs_a > @divTrunc(max, abs_b)) {
|
|
||||||
overflow.* = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (abs_a > @divTrunc(min, -abs_b)) {
|
|
||||||
overflow.* = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
@ -152,7 +152,7 @@ pub fn exportAtan(comptime T: type, comptime name: []const u8) void {
|
|||||||
pub fn exportSin(comptime T: type, comptime name: []const u8) void {
|
pub fn exportSin(comptime T: type, comptime name: []const u8) void {
|
||||||
comptime var f = struct {
|
comptime var f = struct {
|
||||||
fn func(input: T) callconv(.C) T {
|
fn func(input: T) callconv(.C) T {
|
||||||
return @sin(input);
|
return math.sin(input);
|
||||||
}
|
}
|
||||||
}.func;
|
}.func;
|
||||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
@ -161,7 +161,7 @@ pub fn exportSin(comptime T: type, comptime name: []const u8) void {
|
|||||||
pub fn exportCos(comptime T: type, comptime name: []const u8) void {
|
pub fn exportCos(comptime T: type, comptime name: []const u8) void {
|
||||||
comptime var f = struct {
|
comptime var f = struct {
|
||||||
fn func(input: T) callconv(.C) T {
|
fn func(input: T) callconv(.C) T {
|
||||||
return @cos(input);
|
return math.cos(input);
|
||||||
}
|
}
|
||||||
}.func;
|
}.func;
|
||||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
@ -170,25 +170,52 @@ pub fn exportCos(comptime T: type, comptime name: []const u8) void {
|
|||||||
pub fn exportLog(comptime T: type, comptime name: []const u8) void {
|
pub fn exportLog(comptime T: type, comptime name: []const u8) void {
|
||||||
comptime var f = struct {
|
comptime var f = struct {
|
||||||
fn func(input: T) callconv(.C) T {
|
fn func(input: T) callconv(.C) T {
|
||||||
return @log(input);
|
return math.ln(input);
|
||||||
}
|
}
|
||||||
}.func;
|
}.func;
|
||||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exportRoundF32(comptime T: type, comptime name: []const u8) void {
|
pub fn exportFAbs(comptime T: type, comptime name: []const u8) void {
|
||||||
comptime var f = struct {
|
comptime var f = struct {
|
||||||
fn func(input: f32) callconv(.C) T {
|
fn func(input: T) callconv(.C) T {
|
||||||
return @floatToInt(T, (@round(input)));
|
return math.absFloat(input);
|
||||||
}
|
}
|
||||||
}.func;
|
}.func;
|
||||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exportRoundF64(comptime T: type, comptime name: []const u8) void {
|
pub fn exportSqrt(comptime T: type, comptime name: []const u8) void {
|
||||||
comptime var f = struct {
|
comptime var f = struct {
|
||||||
fn func(input: f64) callconv(.C) T {
|
fn func(input: T) callconv(.C) T {
|
||||||
return @floatToInt(T, (@round(input)));
|
return math.sqrt(input);
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exportRound(comptime F: type, comptime T: type, comptime name: []const u8) void {
|
||||||
|
comptime var f = struct {
|
||||||
|
fn func(input: F) callconv(.C) T {
|
||||||
|
return @floatToInt(T, (math.round(input)));
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exportFloor(comptime F: type, comptime T: type, comptime name: []const u8) void {
|
||||||
|
comptime var f = struct {
|
||||||
|
fn func(input: F) callconv(.C) T {
|
||||||
|
return @floatToInt(T, (math.floor(input)));
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exportCeiling(comptime F: type, comptime T: type, comptime name: []const u8) void {
|
||||||
|
comptime var f = struct {
|
||||||
|
fn func(input: F) callconv(.C) T {
|
||||||
|
return @floatToInt(T, (math.ceil(input)));
|
||||||
}
|
}
|
||||||
}.func;
|
}.func;
|
||||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
|
@ -305,6 +305,10 @@ pub fn isUnique(
|
|||||||
|
|
||||||
const isizes: [*]isize = @intToPtr([*]isize, masked_ptr);
|
const isizes: [*]isize = @intToPtr([*]isize, masked_ptr);
|
||||||
|
|
||||||
|
if (DEBUG_INCDEC and builtin.target.cpu.arch != .wasm32) {
|
||||||
|
std.debug.print("| is unique {*}\n", .{&bytes[0]});
|
||||||
|
}
|
||||||
|
|
||||||
const refcount = (isizes - 1)[0];
|
const refcount = (isizes - 1)[0];
|
||||||
|
|
||||||
return refcount == REFCOUNT_ONE_ISIZE;
|
return refcount == REFCOUNT_ONE_ISIZE;
|
||||||
|
@ -95,7 +95,7 @@ Dict k v := {
|
|||||||
# TODO: define Eq and Hash that are unordered. Only if value implements hash/eq?
|
# TODO: define Eq and Hash that are unordered. Only if value implements hash/eq?
|
||||||
metadata : List I8,
|
metadata : List I8,
|
||||||
dataIndices : List Nat,
|
dataIndices : List Nat,
|
||||||
data : List (T k v),
|
data : List (k, v),
|
||||||
size : Nat,
|
size : Nat,
|
||||||
} | k implements Hash & Eq
|
} | k implements Hash & Eq
|
||||||
implements [
|
implements [
|
||||||
@ -175,12 +175,12 @@ single = \k, v ->
|
|||||||
## |> Dict.insert 2 "Two"
|
## |> Dict.insert 2 "Two"
|
||||||
## |> Dict.insert 3 "Three"
|
## |> Dict.insert 3 "Three"
|
||||||
## |> Dict.insert 4 "Four"
|
## |> Dict.insert 4 "Four"
|
||||||
## |> Bool.isEq (Dict.fromList [T 1 "One", T 2 "Two", T 3 "Three", T 4 "Four"])
|
## |> Bool.isEq (Dict.fromList [(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")])
|
||||||
## ```
|
## ```
|
||||||
fromList : List (T k v) -> Dict k v | k implements Hash & Eq
|
fromList : List (k, v) -> Dict k v | k Hash & Eq
|
||||||
fromList = \data ->
|
fromList = \data ->
|
||||||
# TODO: make this efficient. Should just set data and then set all indicies in the hashmap.
|
# TODO: make this efficient. Should just set data and then set all indicies in the hashmap.
|
||||||
List.walk data (empty {}) (\dict, T k v -> insert dict k v)
|
List.walk data (empty {}) (\dict, (k, v) -> insert dict k v)
|
||||||
|
|
||||||
## Returns the number of values in the dictionary.
|
## Returns the number of values in the dictionary.
|
||||||
## ```
|
## ```
|
||||||
@ -238,7 +238,7 @@ clear = \@Dict { metadata, dataIndices, data } ->
|
|||||||
## ```
|
## ```
|
||||||
walk : Dict k v, state, (state, k, v -> state) -> state | k implements Hash & Eq
|
walk : Dict k v, state, (state, k, v -> state) -> state | k implements Hash & Eq
|
||||||
walk = \@Dict { data }, initialState, transform ->
|
walk = \@Dict { data }, initialState, transform ->
|
||||||
List.walk data initialState (\state, T k v -> transform state k v)
|
List.walk data initialState (\state, (k, v) -> transform state k v)
|
||||||
|
|
||||||
## Same as [Dict.walk], except you can stop walking early.
|
## Same as [Dict.walk], except you can stop walking early.
|
||||||
##
|
##
|
||||||
@ -270,7 +270,7 @@ walk = \@Dict { data }, initialState, transform ->
|
|||||||
## ```
|
## ```
|
||||||
walkUntil : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state | k implements Hash & Eq
|
walkUntil : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state | k implements Hash & Eq
|
||||||
walkUntil = \@Dict { data }, initialState, transform ->
|
walkUntil = \@Dict { data }, initialState, transform ->
|
||||||
List.walkUntil data initialState (\state, T k v -> transform state k v)
|
List.walkUntil data initialState (\state, (k, v) -> transform state k v)
|
||||||
|
|
||||||
## Get the value for a given key. If there is a value for the specified key it
|
## Get the value for a given key. If there is a value for the specified key it
|
||||||
## will return [Ok value], otherwise return [Err KeyNotFound].
|
## will return [Ok value], otherwise return [Err KeyNotFound].
|
||||||
@ -296,7 +296,7 @@ get = \@Dict { metadata, dataIndices, data }, key ->
|
|||||||
when findIndexHelper metadata dataIndices data h2Key key probe 0 is
|
when findIndexHelper metadata dataIndices data h2Key key probe 0 is
|
||||||
Ok index ->
|
Ok index ->
|
||||||
dataIndex = listGetUnsafe dataIndices index
|
dataIndex = listGetUnsafe dataIndices index
|
||||||
(T _ v) = listGetUnsafe data dataIndex
|
(_, v) = listGetUnsafe data dataIndex
|
||||||
|
|
||||||
Ok v
|
Ok v
|
||||||
|
|
||||||
@ -353,7 +353,7 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
|
|||||||
@Dict {
|
@Dict {
|
||||||
metadata,
|
metadata,
|
||||||
dataIndices,
|
dataIndices,
|
||||||
data: List.set data dataIndex (T key value),
|
data: List.set data dataIndex (key, value),
|
||||||
size,
|
size,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,9 +447,9 @@ update = \dict, key, alter ->
|
|||||||
## |> Dict.insert 3 "Three"
|
## |> Dict.insert 3 "Three"
|
||||||
## |> Dict.insert 4 "Four"
|
## |> Dict.insert 4 "Four"
|
||||||
## |> Dict.toList
|
## |> Dict.toList
|
||||||
## |> Bool.isEq [T 1 "One", T 2 "Two", T 3 "Three", T 4 "Four"]
|
## |> Bool.isEq [(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")]
|
||||||
## ```
|
## ```
|
||||||
toList : Dict k v -> List (T k v) | k implements Hash & Eq
|
toList : Dict k v -> List (k, v) | k implements Hash & Eq
|
||||||
toList = \@Dict { data } ->
|
toList = \@Dict { data } ->
|
||||||
data
|
data
|
||||||
|
|
||||||
@ -466,7 +466,7 @@ toList = \@Dict { data } ->
|
|||||||
## ```
|
## ```
|
||||||
keys : Dict k v -> List k | k implements Hash & Eq
|
keys : Dict k v -> List k | k implements Hash & Eq
|
||||||
keys = \@Dict { data } ->
|
keys = \@Dict { data } ->
|
||||||
List.map data (\T k _ -> k)
|
List.map data (\(k, _) -> k)
|
||||||
|
|
||||||
## Returns the values of a dictionary as a [List].
|
## Returns the values of a dictionary as a [List].
|
||||||
## This requires allocating a temporary [List], prefer using [Dict.toList] or [Dict.walk] instead.
|
## This requires allocating a temporary [List], prefer using [Dict.toList] or [Dict.walk] instead.
|
||||||
@ -481,7 +481,7 @@ keys = \@Dict { data } ->
|
|||||||
## ```
|
## ```
|
||||||
values : Dict k v -> List v | k implements Hash & Eq
|
values : Dict k v -> List v | k implements Hash & Eq
|
||||||
values = \@Dict { data } ->
|
values = \@Dict { data } ->
|
||||||
List.map data (\T _ v -> v)
|
List.map data (\(_, v) -> v)
|
||||||
|
|
||||||
## Combine two dictionaries by keeping the [union](https://en.wikipedia.org/wiki/Union_(set_theory))
|
## Combine two dictionaries by keeping the [union](https://en.wikipedia.org/wiki/Union_(set_theory))
|
||||||
## of all the key-value pairs. This means that all the key-value pairs in
|
## of all the key-value pairs. This means that all the key-value pairs in
|
||||||
@ -567,7 +567,7 @@ removeAll = \xs, ys ->
|
|||||||
|
|
||||||
swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v | k implements Hash & Eq
|
swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v | k implements Hash & Eq
|
||||||
swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex ->
|
swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex ->
|
||||||
(T key _) = listGetUnsafe data lastIndex
|
(key, _) = listGetUnsafe data lastIndex
|
||||||
hashKey =
|
hashKey =
|
||||||
createLowLevelHasher PseudoRandSeed
|
createLowLevelHasher PseudoRandSeed
|
||||||
|> Hash.hash key
|
|> Hash.hash key
|
||||||
@ -603,7 +603,7 @@ insertNotFoundHelper = \@Dict { metadata, dataIndices, data, size }, key, value,
|
|||||||
probe = newProbe h1Key (div8 (List.len metadata))
|
probe = newProbe h1Key (div8 (List.len metadata))
|
||||||
index = nextEmptyOrDeletedHelper metadata probe 0
|
index = nextEmptyOrDeletedHelper metadata probe 0
|
||||||
dataIndex = List.len data
|
dataIndex = List.len data
|
||||||
nextData = List.append data (T key value)
|
nextData = List.append data (key, value)
|
||||||
|
|
||||||
@Dict {
|
@Dict {
|
||||||
metadata: List.set metadata index h2Key,
|
metadata: List.set metadata index h2Key,
|
||||||
@ -629,7 +629,7 @@ nextEmptyOrDeletedHelper = \metadata, probe, offset ->
|
|||||||
|
|
||||||
# TODO: investigate if this needs to be split into more specific helper functions.
|
# TODO: investigate if this needs to be split into more specific helper functions.
|
||||||
# There is a chance that returning specific sub-info like the value would be faster.
|
# There is a chance that returning specific sub-info like the value would be faster.
|
||||||
findIndexHelper : List I8, List Nat, List (T k v), I8, k, Probe, Nat -> Result Nat [NotFound] | k implements Hash & Eq
|
findIndexHelper : List I8, List Nat, List (k, v), I8, k, Probe, Nat -> Result Nat [NotFound] | k implements Hash & Eq
|
||||||
findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset ->
|
findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset ->
|
||||||
# For finding a value, we must search past all deleted element tombstones.
|
# For finding a value, we must search past all deleted element tombstones.
|
||||||
index = Num.addWrap (mul8 probe.slotIndex) offset
|
index = Num.addWrap (mul8 probe.slotIndex) offset
|
||||||
@ -642,7 +642,7 @@ findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset ->
|
|||||||
else if md == h2Key then
|
else if md == h2Key then
|
||||||
# Potentially matching slot, check if the key is a match.
|
# Potentially matching slot, check if the key is a match.
|
||||||
dataIndex = listGetUnsafe dataIndices index
|
dataIndex = listGetUnsafe dataIndices index
|
||||||
(T k _) = listGetUnsafe data dataIndex
|
(k, _) = listGetUnsafe data dataIndex
|
||||||
|
|
||||||
if k == key then
|
if k == key then
|
||||||
# We have a match, return its index.
|
# We have a match, return its index.
|
||||||
@ -687,7 +687,7 @@ rehash = \@Dict { metadata, dataIndices, data, size } ->
|
|||||||
|
|
||||||
rehashHelper newDict metadata dataIndices data 0
|
rehashHelper newDict metadata dataIndices data 0
|
||||||
|
|
||||||
rehashHelper : Dict k v, List I8, List Nat, List (T k v), Nat -> Dict k v | k implements Hash & Eq
|
rehashHelper : Dict k v, List I8, List Nat, List (k, v), Nat -> Dict k v | k implements Hash & implements
|
||||||
rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
|
rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
|
||||||
when List.get oldMetadata index is
|
when List.get oldMetadata index is
|
||||||
Ok md ->
|
Ok md ->
|
||||||
@ -695,7 +695,7 @@ rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
|
|||||||
if md >= 0 then
|
if md >= 0 then
|
||||||
# We have an actual element here
|
# We have an actual element here
|
||||||
dataIndex = listGetUnsafe oldDataIndices index
|
dataIndex = listGetUnsafe oldDataIndices index
|
||||||
(T k _) = listGetUnsafe oldData dataIndex
|
(k, _) = listGetUnsafe oldData dataIndex
|
||||||
|
|
||||||
insertForRehash dict k dataIndex
|
insertForRehash dict k dataIndex
|
||||||
else
|
else
|
||||||
@ -731,8 +731,6 @@ emptySlot = -128
|
|||||||
deletedSlot : I8
|
deletedSlot : I8
|
||||||
deletedSlot = -2
|
deletedSlot = -2
|
||||||
|
|
||||||
T k v : [T k v]
|
|
||||||
|
|
||||||
# Capacity must be a power of 2.
|
# Capacity must be a power of 2.
|
||||||
# We still will use slots of 8 even though this version has no true slots.
|
# We still will use slots of 8 even though this version has no true slots.
|
||||||
# We just move an element at a time.
|
# We just move an element at a time.
|
||||||
@ -869,7 +867,7 @@ expect
|
|||||||
|
|
||||||
expect
|
expect
|
||||||
dict =
|
dict =
|
||||||
fromList [T 1u8 1u8, T 2 2, T 3 3]
|
fromList [(1u8, 1u8), (2, 2), (3, 3)]
|
||||||
|> remove 1
|
|> remove 1
|
||||||
|> remove 3
|
|> remove 3
|
||||||
|
|
||||||
@ -877,7 +875,7 @@ expect
|
|||||||
|
|
||||||
expect
|
expect
|
||||||
list =
|
list =
|
||||||
fromList [T 1u8 1u8, T 2u8 2u8, T 3 3]
|
fromList [(1u8, 1u8), (2u8, 2u8), (3, 3)]
|
||||||
|> remove 1
|
|> remove 1
|
||||||
|> insert 0 0
|
|> insert 0 0
|
||||||
|> remove 3
|
|> remove 3
|
||||||
|
@ -989,19 +989,11 @@ pow : Frac a, Frac a -> Frac a
|
|||||||
## This process is known as [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring).
|
## This process is known as [exponentiation by squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring).
|
||||||
##
|
##
|
||||||
## For a [Frac] alternative to this function, which supports negative exponents,
|
## For a [Frac] alternative to this function, which supports negative exponents,
|
||||||
## see #Num.exp.
|
## see #Num.pow.
|
||||||
## ```
|
|
||||||
## Num.exp 5 0
|
|
||||||
##
|
##
|
||||||
## Num.exp 5 1
|
## ## Warning
|
||||||
##
|
##
|
||||||
## Num.exp 5 2
|
## It is very easy for this function to produce an answer
|
||||||
##
|
|
||||||
## Num.exp 5 6
|
|
||||||
## ```
|
|
||||||
## ## Performance Details
|
|
||||||
##
|
|
||||||
## Be careful! It is very easy for this function to produce an answer
|
|
||||||
## so large it causes an overflow.
|
## so large it causes an overflow.
|
||||||
powInt : Int a, Int a -> Int a
|
powInt : Int a, Int a -> Int a
|
||||||
|
|
||||||
|
@ -267,9 +267,15 @@ pub const NUM_IS_INFINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is
|
|||||||
pub const NUM_IS_FINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is_finite");
|
pub const NUM_IS_FINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is_finite");
|
||||||
pub const NUM_LOG: IntrinsicName = float_intrinsic!("roc_builtins.num.log");
|
pub const NUM_LOG: IntrinsicName = float_intrinsic!("roc_builtins.num.log");
|
||||||
pub const NUM_POW: IntrinsicName = float_intrinsic!("roc_builtins.num.pow");
|
pub const NUM_POW: IntrinsicName = float_intrinsic!("roc_builtins.num.pow");
|
||||||
|
pub const NUM_FABS: IntrinsicName = float_intrinsic!("roc_builtins.num.fabs");
|
||||||
|
pub const NUM_SQRT: IntrinsicName = float_intrinsic!("roc_builtins.num.sqrt");
|
||||||
|
|
||||||
pub const NUM_POW_INT: IntrinsicName = int_intrinsic!("roc_builtins.num.pow_int");
|
pub const NUM_POW_INT: IntrinsicName = int_intrinsic!("roc_builtins.num.pow_int");
|
||||||
pub const NUM_DIV_CEIL: IntrinsicName = int_intrinsic!("roc_builtins.num.div_ceil");
|
pub const NUM_DIV_CEIL: IntrinsicName = int_intrinsic!("roc_builtins.num.div_ceil");
|
||||||
|
pub const NUM_CEILING_F32: IntrinsicName = int_intrinsic!("roc_builtins.num.ceiling_f32");
|
||||||
|
pub const NUM_CEILING_F64: IntrinsicName = int_intrinsic!("roc_builtins.num.ceiling_f64");
|
||||||
|
pub const NUM_FLOOR_F32: IntrinsicName = int_intrinsic!("roc_builtins.num.floor_f32");
|
||||||
|
pub const NUM_FLOOR_F64: IntrinsicName = int_intrinsic!("roc_builtins.num.floor_f64");
|
||||||
pub const NUM_ROUND_F32: IntrinsicName = int_intrinsic!("roc_builtins.num.round_f32");
|
pub const NUM_ROUND_F32: IntrinsicName = int_intrinsic!("roc_builtins.num.round_f32");
|
||||||
pub const NUM_ROUND_F64: IntrinsicName = int_intrinsic!("roc_builtins.num.round_f64");
|
pub const NUM_ROUND_F64: IntrinsicName = int_intrinsic!("roc_builtins.num.round_f64");
|
||||||
|
|
||||||
|
@ -458,6 +458,11 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg, AArch64Assembler> for AArch64C
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
|
#[inline(always)]
|
||||||
|
fn base_pointer() -> AArch64GeneralReg {
|
||||||
|
AArch64GeneralReg::FP
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, src: AArch64GeneralReg) {
|
fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, src: AArch64GeneralReg) {
|
||||||
cmp_reg64_imm12(buf, src, 0);
|
cmp_reg64_imm12(buf, src, 0);
|
||||||
@ -1065,7 +1070,7 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn neq_reg64_reg64_reg64(
|
fn neq_reg_reg_reg(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
_register_width: RegisterWidth,
|
_register_width: RegisterWidth,
|
||||||
dst: AArch64GeneralReg,
|
dst: AArch64GeneralReg,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
single_register_floats, single_register_int_builtins, single_register_integers, Backend, Env,
|
pointer_layouts, single_register_floats, single_register_int_builtins,
|
||||||
Relocation,
|
single_register_integers, Backend, Env, Relocation,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::{CollectIn, Vec};
|
use bumpalo::collections::{CollectIn, Vec};
|
||||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||||
@ -150,6 +150,8 @@ pub enum CompareOperation {
|
|||||||
/// Generally, I prefer explicit sources, as opposed to dst being one of the sources. Ex: `x = x + y` would be `add x, x, y` instead of `add x, y`.
|
/// Generally, I prefer explicit sources, as opposed to dst being one of the sources. Ex: `x = x + y` would be `add x, x, y` instead of `add x, y`.
|
||||||
/// dst should always come before sources.
|
/// dst should always come before sources.
|
||||||
pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
||||||
|
fn base_pointer() -> GeneralReg;
|
||||||
|
|
||||||
fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||||
fn abs_freg64_freg64(
|
fn abs_freg64_freg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
@ -517,7 +519,7 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
|||||||
Self::eq_reg_reg_reg(buf, RegisterWidth::W64, dst, src1, src2)
|
Self::eq_reg_reg_reg(buf, RegisterWidth::W64, dst, src1, src2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn neq_reg64_reg64_reg64(
|
fn neq_reg_reg_reg(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
register_width: RegisterWidth,
|
register_width: RegisterWidth,
|
||||||
dst: GeneralReg,
|
dst: GeneralReg,
|
||||||
@ -904,17 +906,22 @@ impl<
|
|||||||
ASM::mov_base32_reg64(&mut self.buf, offset, CC::GENERAL_RETURN_REGS[0]);
|
ASM::mov_base32_reg64(&mut self.buf, offset, CC::GENERAL_RETURN_REGS[0]);
|
||||||
ASM::mov_base32_reg64(&mut self.buf, offset + 8, CC::GENERAL_RETURN_REGS[1]);
|
ASM::mov_base32_reg64(&mut self.buf, offset + 8, CC::GENERAL_RETURN_REGS[1]);
|
||||||
}
|
}
|
||||||
|
pointer_layouts!() => {
|
||||||
other => {
|
|
||||||
//
|
|
||||||
match other {
|
|
||||||
LayoutRepr::Boxed(_) => {
|
|
||||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||||
ASM::mov_reg64_reg64(&mut self.buf, dst_reg, CC::GENERAL_RETURN_REGS[0]);
|
ASM::mov_reg64_reg64(&mut self.buf, dst_reg, CC::GENERAL_RETURN_REGS[0]);
|
||||||
}
|
}
|
||||||
LayoutRepr::LambdaSet(lambda_set) => {
|
LayoutRepr::LambdaSet(lambda_set) => {
|
||||||
self.move_return_value(dst, &lambda_set.runtime_representation())
|
self.move_return_value(dst, &lambda_set.runtime_representation())
|
||||||
}
|
}
|
||||||
|
LayoutRepr::Union(UnionLayout::NonRecursive(_)) => {
|
||||||
|
CC::load_returned_complex_symbol(
|
||||||
|
&mut self.buf,
|
||||||
|
&mut self.storage_manager,
|
||||||
|
self.layout_interner,
|
||||||
|
dst,
|
||||||
|
ret_layout,
|
||||||
|
);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
CC::load_returned_complex_symbol(
|
CC::load_returned_complex_symbol(
|
||||||
&mut self.buf,
|
&mut self.buf,
|
||||||
@ -926,8 +933,6 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_switch(
|
fn build_switch(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -1558,7 +1563,7 @@ impl<
|
|||||||
let src2_reg = self
|
let src2_reg = self
|
||||||
.storage_manager
|
.storage_manager
|
||||||
.load_to_general_reg(&mut self.buf, src2);
|
.load_to_general_reg(&mut self.buf, src2);
|
||||||
ASM::neq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, src1_reg, src2_reg);
|
ASM::neq_reg_reg_reg(&mut self.buf, width, dst_reg, src1_reg, src2_reg);
|
||||||
}
|
}
|
||||||
LayoutRepr::STR => {
|
LayoutRepr::STR => {
|
||||||
self.build_fn_call(
|
self.build_fn_call(
|
||||||
@ -1576,7 +1581,7 @@ impl<
|
|||||||
|
|
||||||
let width = RegisterWidth::W8; // we're comparing booleans
|
let width = RegisterWidth::W8; // we're comparing booleans
|
||||||
let dst_reg = self.storage_manager.load_to_general_reg(&mut self.buf, dst);
|
let dst_reg = self.storage_manager.load_to_general_reg(&mut self.buf, dst);
|
||||||
ASM::neq_reg64_reg64_reg64(&mut self.buf, width, dst_reg, dst_reg, tmp_reg);
|
ASM::neq_reg_reg_reg(&mut self.buf, width, dst_reg, dst_reg, tmp_reg);
|
||||||
}
|
}
|
||||||
x => todo!("NumNeq: layout, {:?}", x),
|
x => todo!("NumNeq: layout, {:?}", x),
|
||||||
}
|
}
|
||||||
@ -1714,25 +1719,13 @@ impl<
|
|||||||
ASM::xor_reg64_reg64_reg64(buf, dst_reg, dst_reg, dst_reg); // zero out dst reg
|
ASM::xor_reg64_reg64_reg64(buf, dst_reg, dst_reg, dst_reg); // zero out dst reg
|
||||||
ASM::mov_reg32_freg32(buf, dst_reg, src_reg);
|
ASM::mov_reg32_freg32(buf, dst_reg, src_reg);
|
||||||
ASM::and_reg64_reg64_reg64(buf, dst_reg, dst_reg, mask_reg);
|
ASM::and_reg64_reg64_reg64(buf, dst_reg, dst_reg, mask_reg);
|
||||||
ASM::neq_reg64_reg64_reg64(
|
ASM::neq_reg_reg_reg(buf, RegisterWidth::W32, dst_reg, dst_reg, mask_reg);
|
||||||
buf,
|
|
||||||
RegisterWidth::W32,
|
|
||||||
dst_reg,
|
|
||||||
dst_reg,
|
|
||||||
mask_reg,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Layout::F64 => {
|
Layout::F64 => {
|
||||||
ASM::mov_reg64_imm64(buf, mask_reg, 0x7ff0_0000_0000_0000);
|
ASM::mov_reg64_imm64(buf, mask_reg, 0x7ff0_0000_0000_0000);
|
||||||
ASM::mov_reg64_freg64(buf, dst_reg, src_reg);
|
ASM::mov_reg64_freg64(buf, dst_reg, src_reg);
|
||||||
ASM::and_reg64_reg64_reg64(buf, dst_reg, dst_reg, mask_reg);
|
ASM::and_reg64_reg64_reg64(buf, dst_reg, dst_reg, mask_reg);
|
||||||
ASM::neq_reg64_reg64_reg64(
|
ASM::neq_reg_reg_reg(buf, RegisterWidth::W64, dst_reg, dst_reg, mask_reg);
|
||||||
buf,
|
|
||||||
RegisterWidth::W64,
|
|
||||||
dst_reg,
|
|
||||||
dst_reg,
|
|
||||||
mask_reg,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -2213,6 +2206,7 @@ impl<
|
|||||||
storage_manager,
|
storage_manager,
|
||||||
self.layout_interner,
|
self.layout_interner,
|
||||||
element_ptr,
|
element_ptr,
|
||||||
|
0,
|
||||||
*ret_layout,
|
*ret_layout,
|
||||||
*dst,
|
*dst,
|
||||||
);
|
);
|
||||||
@ -2600,7 +2594,7 @@ impl<
|
|||||||
union_layout: &UnionLayout<'a>,
|
union_layout: &UnionLayout<'a>,
|
||||||
) {
|
) {
|
||||||
match union_layout {
|
match union_layout {
|
||||||
UnionLayout::NonRecursive(tag_layouts) | UnionLayout::Recursive(tag_layouts) => {
|
UnionLayout::NonRecursive(tag_layouts) => {
|
||||||
self.storage_manager.load_field_at_index(
|
self.storage_manager.load_field_at_index(
|
||||||
self.layout_interner,
|
self.layout_interner,
|
||||||
sym,
|
sym,
|
||||||
@ -2609,6 +2603,77 @@ impl<
|
|||||||
tag_layouts[tag_id as usize],
|
tag_layouts[tag_id as usize],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
UnionLayout::NullableUnwrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_fields,
|
||||||
|
} => {
|
||||||
|
debug_assert_ne!(tag_id, *nullable_id as TagIdIntType);
|
||||||
|
|
||||||
|
let element_layout = other_fields[index as usize];
|
||||||
|
|
||||||
|
let ptr_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, structure);
|
||||||
|
|
||||||
|
let mut offset = 0;
|
||||||
|
for field in &other_fields[..index as usize] {
|
||||||
|
offset += self.layout_interner.stack_size(*field);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::ptr_read(
|
||||||
|
&mut self.buf,
|
||||||
|
&mut self.storage_manager,
|
||||||
|
self.layout_interner,
|
||||||
|
ptr_reg,
|
||||||
|
offset as i32,
|
||||||
|
element_layout,
|
||||||
|
*sym,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnionLayout::NullableWrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_tags,
|
||||||
|
} => {
|
||||||
|
debug_assert_ne!(tag_id, *nullable_id as TagIdIntType);
|
||||||
|
|
||||||
|
let other_fields = if tag_id < *nullable_id {
|
||||||
|
other_tags[tag_id as usize]
|
||||||
|
} else {
|
||||||
|
other_tags[tag_id as usize - 1]
|
||||||
|
};
|
||||||
|
|
||||||
|
let element_layout = other_fields[index as usize];
|
||||||
|
|
||||||
|
let ptr_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, structure);
|
||||||
|
|
||||||
|
let mask_symbol = self.debug_symbol("tag_id_mask");
|
||||||
|
let mask_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.claim_general_reg(&mut self.buf, &mask_symbol);
|
||||||
|
ASM::mov_reg64_imm64(&mut self.buf, mask_reg, (!0b111) as _);
|
||||||
|
|
||||||
|
// mask out the tag id bits
|
||||||
|
ASM::and_reg64_reg64_reg64(&mut self.buf, ptr_reg, ptr_reg, mask_reg);
|
||||||
|
|
||||||
|
let mut offset = 0;
|
||||||
|
for field in &other_fields[..index as usize] {
|
||||||
|
offset += self.layout_interner.stack_size(*field);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::ptr_read(
|
||||||
|
&mut self.buf,
|
||||||
|
&mut self.storage_manager,
|
||||||
|
self.layout_interner,
|
||||||
|
ptr_reg,
|
||||||
|
offset as i32,
|
||||||
|
element_layout,
|
||||||
|
*sym,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let union_in_layout = self
|
let union_in_layout = self
|
||||||
.layout_interner
|
.layout_interner
|
||||||
@ -2653,11 +2718,17 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// box is just a pointer on the stack
|
// box is just a pointer on the stack
|
||||||
let base_offset = self.storage_manager.claim_stack_area(&sym, 8);
|
let base_offset = self.storage_manager.claim_pointer_stack_area(sym);
|
||||||
ASM::mov_base32_reg64(&mut self.buf, base_offset, ptr_reg);
|
ASM::mov_base32_reg64(&mut self.buf, base_offset, ptr_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_box(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>) {
|
fn expr_box(
|
||||||
|
&mut self,
|
||||||
|
sym: Symbol,
|
||||||
|
value: Symbol,
|
||||||
|
element_layout: InLayout<'a>,
|
||||||
|
reuse: Option<Symbol>,
|
||||||
|
) {
|
||||||
let element_width_symbol = Symbol::DEV_TMP;
|
let element_width_symbol = Symbol::DEV_TMP;
|
||||||
self.load_layout_stack_size(element_layout, element_width_symbol);
|
self.load_layout_stack_size(element_layout, element_width_symbol);
|
||||||
|
|
||||||
@ -2665,18 +2736,27 @@ impl<
|
|||||||
let element_alignment_symbol = Symbol::DEV_TMP2;
|
let element_alignment_symbol = Symbol::DEV_TMP2;
|
||||||
self.load_layout_alignment(Layout::U32, element_alignment_symbol);
|
self.load_layout_alignment(Layout::U32, element_alignment_symbol);
|
||||||
|
|
||||||
|
let allocation = self.debug_symbol("allocation");
|
||||||
|
|
||||||
|
match reuse {
|
||||||
|
None => {
|
||||||
self.allocate_with_refcount(
|
self.allocate_with_refcount(
|
||||||
Symbol::DEV_TMP3,
|
allocation,
|
||||||
element_width_symbol,
|
element_width_symbol,
|
||||||
element_alignment_symbol,
|
element_alignment_symbol,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
Some(reuse) => {
|
||||||
|
self.allocate_with_refcount_if_null(allocation, reuse, element_layout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.free_symbol(&element_width_symbol);
|
self.free_symbol(&element_width_symbol);
|
||||||
self.free_symbol(&element_alignment_symbol);
|
self.free_symbol(&element_alignment_symbol);
|
||||||
|
|
||||||
self.build_ptr_write(sym, Symbol::DEV_TMP3, value, element_layout);
|
self.build_ptr_write(sym, allocation, value, element_layout);
|
||||||
|
|
||||||
self.free_symbol(&Symbol::DEV_TMP3);
|
self.free_symbol(&allocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_unbox(&mut self, dst: Symbol, ptr: Symbol, element_layout: InLayout<'a>) {
|
fn expr_unbox(&mut self, dst: Symbol, ptr: Symbol, element_layout: InLayout<'a>) {
|
||||||
@ -2684,25 +2764,136 @@ impl<
|
|||||||
.storage_manager
|
.storage_manager
|
||||||
.load_to_general_reg(&mut self.buf, &ptr);
|
.load_to_general_reg(&mut self.buf, &ptr);
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
Self::ptr_read(
|
Self::ptr_read(
|
||||||
&mut self.buf,
|
&mut self.buf,
|
||||||
&mut self.storage_manager,
|
&mut self.storage_manager,
|
||||||
self.layout_interner,
|
self.layout_interner,
|
||||||
ptr_reg,
|
ptr_reg,
|
||||||
|
offset,
|
||||||
element_layout,
|
element_layout,
|
||||||
dst,
|
dst,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>) {
|
fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>) {
|
||||||
self.storage_manager.load_union_tag_id(
|
let layout_interner: &mut STLayoutInterner<'a> = self.layout_interner;
|
||||||
self.layout_interner,
|
let _buf: &mut Vec<'a, u8> = &mut self.buf;
|
||||||
|
match union_layout {
|
||||||
|
UnionLayout::NonRecursive(tags) => {
|
||||||
|
self.storage_manager.load_union_tag_id_nonrecursive(
|
||||||
|
layout_interner,
|
||||||
&mut self.buf,
|
&mut self.buf,
|
||||||
sym,
|
sym,
|
||||||
structure,
|
structure,
|
||||||
union_layout,
|
tags,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
UnionLayout::NullableUnwrapped { nullable_id, .. } => {
|
||||||
|
// simple is_null check on the pointer
|
||||||
|
let tmp = Symbol::DEV_TMP5;
|
||||||
|
let reg = self.storage_manager.claim_general_reg(&mut self.buf, &tmp);
|
||||||
|
ASM::mov_reg64_imm64(&mut self.buf, reg, 0);
|
||||||
|
|
||||||
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, sym);
|
||||||
|
let src1_reg = reg;
|
||||||
|
let src2_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, structure);
|
||||||
|
|
||||||
|
match *nullable_id {
|
||||||
|
true => {
|
||||||
|
ASM::eq_reg_reg_reg(
|
||||||
|
&mut self.buf,
|
||||||
|
RegisterWidth::W64,
|
||||||
|
dst_reg,
|
||||||
|
src1_reg,
|
||||||
|
src2_reg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
ASM::neq_reg_reg_reg(
|
||||||
|
&mut self.buf,
|
||||||
|
RegisterWidth::W64,
|
||||||
|
dst_reg,
|
||||||
|
src1_reg,
|
||||||
|
src2_reg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.free_symbol(&tmp);
|
||||||
|
}
|
||||||
|
UnionLayout::NullableWrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_tags,
|
||||||
|
} => {
|
||||||
|
let number_of_tags = other_tags.len() + 1;
|
||||||
|
|
||||||
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, sym);
|
||||||
|
|
||||||
|
// build a table to index into with the value that we find
|
||||||
|
let nullable_id = *nullable_id as usize;
|
||||||
|
let it = std::iter::once(nullable_id)
|
||||||
|
.chain(0..nullable_id)
|
||||||
|
.chain(nullable_id + 1..number_of_tags);
|
||||||
|
|
||||||
|
let table = self.debug_symbol("tag_id_table");
|
||||||
|
let table_offset = self
|
||||||
|
.storage_manager
|
||||||
|
.claim_stack_area(&table, (number_of_tags * 2) as _);
|
||||||
|
|
||||||
|
let mut offset = table_offset;
|
||||||
|
for i in it {
|
||||||
|
ASM::mov_reg64_imm64(&mut self.buf, dst_reg, i as i64);
|
||||||
|
ASM::mov_base32_reg16(&mut self.buf, offset, dst_reg);
|
||||||
|
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.free_symbol(&table);
|
||||||
|
|
||||||
|
// mask the 3 lowest bits
|
||||||
|
let tmp = Symbol::DEV_TMP5;
|
||||||
|
let reg = self.storage_manager.claim_general_reg(&mut self.buf, &tmp);
|
||||||
|
ASM::mov_reg64_imm64(&mut self.buf, reg, 0b111);
|
||||||
|
|
||||||
|
let src1_reg = reg;
|
||||||
|
let src2_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, structure);
|
||||||
|
|
||||||
|
ASM::and_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||||
|
|
||||||
|
// we're indexing into an array of u16, so double this index
|
||||||
|
// also the stack grows down, so negate the index
|
||||||
|
ASM::mov_reg64_imm64(&mut self.buf, reg, 2);
|
||||||
|
ASM::umul_reg64_reg64_reg64(
|
||||||
|
&mut self.buf,
|
||||||
|
&mut self.storage_manager,
|
||||||
|
dst_reg,
|
||||||
|
dst_reg,
|
||||||
|
reg,
|
||||||
|
);
|
||||||
|
|
||||||
|
// index into the table
|
||||||
|
let base_pointer = ASM::base_pointer();
|
||||||
|
ASM::add_reg64_reg64_reg64(&mut self.buf, dst_reg, dst_reg, base_pointer);
|
||||||
|
|
||||||
|
// load the 16-bit value at the pointer
|
||||||
|
ASM::mov_reg16_mem16_offset32(&mut self.buf, dst_reg, dst_reg, table_offset);
|
||||||
|
|
||||||
|
// keep only the lowest 16 bits
|
||||||
|
ASM::mov_reg64_imm64(&mut self.buf, reg, 0xFFFF);
|
||||||
|
ASM::and_reg64_reg64_reg64(&mut self.buf, dst_reg, dst_reg, reg);
|
||||||
|
|
||||||
|
self.free_symbol(&tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
x => todo!("getting tag id of union with layout ({:?})", x),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn tag(
|
fn tag(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -2710,15 +2901,144 @@ impl<
|
|||||||
fields: &'a [Symbol],
|
fields: &'a [Symbol],
|
||||||
union_layout: &UnionLayout<'a>,
|
union_layout: &UnionLayout<'a>,
|
||||||
tag_id: TagIdIntType,
|
tag_id: TagIdIntType,
|
||||||
|
reuse: Option<Symbol>,
|
||||||
) {
|
) {
|
||||||
self.storage_manager.create_union(
|
let target_info = self.storage_manager.target_info;
|
||||||
|
|
||||||
|
let layout_interner: &mut STLayoutInterner<'a> = self.layout_interner;
|
||||||
|
let buf: &mut Vec<'a, u8> = &mut self.buf;
|
||||||
|
|
||||||
|
let (data_size, data_alignment) =
|
||||||
|
union_layout.data_size_and_alignment(layout_interner, target_info);
|
||||||
|
|
||||||
|
match union_layout {
|
||||||
|
UnionLayout::NonRecursive(field_layouts) => {
|
||||||
|
let id_offset = data_size - data_alignment;
|
||||||
|
let base_offset = self.storage_manager.claim_stack_area(sym, data_size);
|
||||||
|
let mut current_offset = base_offset;
|
||||||
|
|
||||||
|
let it = fields.iter().zip(field_layouts[tag_id as usize].iter());
|
||||||
|
for (field, field_layout) in it {
|
||||||
|
self.storage_manager.copy_symbol_to_stack_offset(
|
||||||
|
layout_interner,
|
||||||
|
buf,
|
||||||
|
current_offset,
|
||||||
|
field,
|
||||||
|
field_layout,
|
||||||
|
);
|
||||||
|
let field_size = layout_interner.stack_size(*field_layout);
|
||||||
|
current_offset += field_size as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the tag id in the right place
|
||||||
|
self.storage_manager
|
||||||
|
.with_tmp_general_reg(buf, |_symbol_storage, buf, reg| {
|
||||||
|
ASM::mov_reg64_imm64(buf, reg, tag_id as i64);
|
||||||
|
|
||||||
|
let total_id_offset = base_offset as u32 + id_offset;
|
||||||
|
debug_assert!(total_id_offset % data_alignment == 0);
|
||||||
|
|
||||||
|
// pick the right instruction based on the alignment of the tag id
|
||||||
|
if field_layouts.len() <= u8::MAX as _ {
|
||||||
|
ASM::mov_base32_reg8(buf, total_id_offset as i32, reg);
|
||||||
|
} else {
|
||||||
|
ASM::mov_base32_reg16(buf, total_id_offset as i32, reg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
UnionLayout::NullableUnwrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_fields,
|
||||||
|
} => {
|
||||||
|
if tag_id == *nullable_id as TagIdIntType {
|
||||||
|
// it's just a null pointer
|
||||||
|
self.load_literal_i64(sym, 0);
|
||||||
|
} else {
|
||||||
|
// construct the payload as a struct on the stack
|
||||||
|
let temp_sym = Symbol::DEV_TMP5;
|
||||||
|
let layout = self
|
||||||
|
.layout_interner
|
||||||
|
.insert_no_semantic(LayoutRepr::Struct(other_fields));
|
||||||
|
|
||||||
|
self.load_literal_symbols(fields);
|
||||||
|
self.storage_manager.create_struct(
|
||||||
self.layout_interner,
|
self.layout_interner,
|
||||||
&mut self.buf,
|
&mut self.buf,
|
||||||
sym,
|
&temp_sym,
|
||||||
union_layout,
|
&layout,
|
||||||
fields,
|
fields,
|
||||||
tag_id,
|
);
|
||||||
)
|
|
||||||
|
// now effectively box this struct
|
||||||
|
self.expr_box(*sym, temp_sym, layout, reuse);
|
||||||
|
|
||||||
|
self.free_symbol(&temp_sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UnionLayout::NullableWrapped {
|
||||||
|
nullable_id,
|
||||||
|
other_tags,
|
||||||
|
} => {
|
||||||
|
let nullable_id = *nullable_id;
|
||||||
|
|
||||||
|
if tag_id == nullable_id as TagIdIntType {
|
||||||
|
// it's just a null pointer
|
||||||
|
self.load_literal_i64(sym, 0);
|
||||||
|
} else {
|
||||||
|
let other_fields = if tag_id < nullable_id {
|
||||||
|
other_tags[tag_id as usize]
|
||||||
|
} else {
|
||||||
|
other_tags[tag_id as usize - 1]
|
||||||
|
};
|
||||||
|
|
||||||
|
// construct the payload as a struct on the stack
|
||||||
|
let temp_sym = Symbol::DEV_TMP5;
|
||||||
|
let layout = self
|
||||||
|
.layout_interner
|
||||||
|
.insert_no_semantic(LayoutRepr::Struct(other_fields));
|
||||||
|
|
||||||
|
self.load_literal_symbols(fields);
|
||||||
|
self.storage_manager.create_struct(
|
||||||
|
self.layout_interner,
|
||||||
|
&mut self.buf,
|
||||||
|
&temp_sym,
|
||||||
|
&layout,
|
||||||
|
fields,
|
||||||
|
);
|
||||||
|
|
||||||
|
// now effectively box this struct
|
||||||
|
let untagged_pointer_symbol = self.debug_symbol("untagged_pointer");
|
||||||
|
self.expr_box(untagged_pointer_symbol, temp_sym, layout, reuse);
|
||||||
|
|
||||||
|
self.free_symbol(&temp_sym);
|
||||||
|
|
||||||
|
let tag_id_symbol = self.debug_symbol("tag_id");
|
||||||
|
|
||||||
|
// index zero is taken up by the nullable tag, so any tags before it in the
|
||||||
|
// ordering need to be incremented by one
|
||||||
|
let pointer_tag = if tag_id < nullable_id {
|
||||||
|
tag_id + 1
|
||||||
|
} else {
|
||||||
|
tag_id
|
||||||
|
};
|
||||||
|
|
||||||
|
// finally, we need to tag the pointer
|
||||||
|
debug_assert!(tag_id < 8);
|
||||||
|
self.load_literal_i64(&tag_id_symbol, pointer_tag as _);
|
||||||
|
|
||||||
|
self.build_int_bitwise_or(
|
||||||
|
sym,
|
||||||
|
&untagged_pointer_symbol,
|
||||||
|
&tag_id_symbol,
|
||||||
|
IntWidth::U64,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.free_symbol(&untagged_pointer_symbol);
|
||||||
|
self.free_symbol(&tag_id_symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x => todo!("creating unions with layout: {:?}", x),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_literal(&mut self, sym: &Symbol, layout: &InLayout<'a>, lit: &Literal<'a>) {
|
fn load_literal(&mut self, sym: &Symbol, layout: &InLayout<'a>, lit: &Literal<'a>) {
|
||||||
@ -2860,7 +3180,7 @@ impl<
|
|||||||
if self.storage_manager.is_stored_primitive(sym) {
|
if self.storage_manager.is_stored_primitive(sym) {
|
||||||
// Just load it to the correct type of reg as a stand alone value.
|
// Just load it to the correct type of reg as a stand alone value.
|
||||||
match repr {
|
match repr {
|
||||||
single_register_integers!() => {
|
single_register_integers!() | pointer_layouts!() => {
|
||||||
self.storage_manager.load_to_specified_general_reg(
|
self.storage_manager.load_to_specified_general_reg(
|
||||||
&mut self.buf,
|
&mut self.buf,
|
||||||
sym,
|
sym,
|
||||||
@ -2874,22 +3194,14 @@ impl<
|
|||||||
CC::FLOAT_RETURN_REGS[0],
|
CC::FLOAT_RETURN_REGS[0],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
other => match other {
|
|
||||||
LayoutRepr::Boxed(_) => {
|
|
||||||
// treat like a 64-bit integer
|
|
||||||
self.storage_manager.load_to_specified_general_reg(
|
|
||||||
&mut self.buf,
|
|
||||||
sym,
|
|
||||||
CC::GENERAL_RETURN_REGS[0],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
LayoutRepr::LambdaSet(lambda_set) => {
|
LayoutRepr::LambdaSet(lambda_set) => {
|
||||||
self.return_symbol(sym, &lambda_set.runtime_representation())
|
self.return_symbol(sym, &lambda_set.runtime_representation())
|
||||||
}
|
}
|
||||||
_ => {
|
LayoutRepr::Union(UnionLayout::NonRecursive(_))
|
||||||
|
| LayoutRepr::Builtin(_)
|
||||||
|
| LayoutRepr::Struct(_) => {
|
||||||
internal_error!("All primitive values should fit in a single register");
|
internal_error!("All primitive values should fit in a single register");
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CC::return_complex_symbol(
|
CC::return_complex_symbol(
|
||||||
@ -3297,22 +3609,78 @@ impl<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn allocate_with_refcount_if_null(&mut self, dst: Symbol, src: Symbol, layout: InLayout) {
|
||||||
|
let src_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, &src);
|
||||||
|
|
||||||
|
// dummy mov that is supposed to move src into dst, and is filled in later because don't know yet which
|
||||||
|
// register the other branch will pick for dst. We must pass two different registers here
|
||||||
|
// otherwise the whole instruction is skipped!
|
||||||
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, &dst);
|
||||||
|
let mov_start_index = self.buf.len();
|
||||||
|
ASM::mov_reg64_reg64(&mut self.buf, dst_reg, src_reg);
|
||||||
|
|
||||||
|
// jump to where the pointer is valid, because it is already valid if non-zero
|
||||||
|
let jmp_start_index = self.buf.len();
|
||||||
|
let jmp_end_index = ASM::jne_reg64_imm64_imm32(&mut self.buf, src_reg, 0x0, 0);
|
||||||
|
|
||||||
|
self.free_symbol(&dst);
|
||||||
|
|
||||||
|
// so, the pointer is NULL, allocate
|
||||||
|
|
||||||
|
let data_bytes = self.debug_symbol("data_bytes");
|
||||||
|
self.load_layout_stack_size(layout, data_bytes);
|
||||||
|
|
||||||
|
let element_alignment = self.debug_symbol("element_alignment");
|
||||||
|
self.load_layout_alignment(layout, element_alignment);
|
||||||
|
|
||||||
|
self.allocate_with_refcount(dst, data_bytes, element_alignment);
|
||||||
|
|
||||||
|
self.free_symbol(&data_bytes);
|
||||||
|
self.free_symbol(&element_alignment);
|
||||||
|
|
||||||
|
let mut tmp = bumpalo::vec![in self.env.arena];
|
||||||
|
|
||||||
|
// update the jump
|
||||||
|
let destination_index = self.buf.len();
|
||||||
|
ASM::jne_reg64_imm64_imm32(
|
||||||
|
&mut tmp,
|
||||||
|
src_reg,
|
||||||
|
0x0,
|
||||||
|
(destination_index - jmp_end_index) as i32,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.buf[jmp_start_index..][..tmp.len()].copy_from_slice(tmp.as_slice());
|
||||||
|
|
||||||
|
// figure out what register was actually used
|
||||||
|
let dst_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, &dst);
|
||||||
|
|
||||||
|
tmp.clear();
|
||||||
|
ASM::mov_reg64_reg64(&mut tmp, dst_reg, src_reg);
|
||||||
|
|
||||||
|
self.buf[mov_start_index..][..tmp.len()].copy_from_slice(tmp.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
fn unbox_str_or_list(
|
fn unbox_str_or_list(
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
storage_manager: &mut StorageManager<'a, 'r, GeneralReg, FloatReg, ASM, CC>,
|
storage_manager: &mut StorageManager<'a, 'r, GeneralReg, FloatReg, ASM, CC>,
|
||||||
dst: Symbol,
|
dst: Symbol,
|
||||||
ptr_reg: GeneralReg,
|
ptr_reg: GeneralReg,
|
||||||
tmp_reg: GeneralReg,
|
tmp_reg: GeneralReg,
|
||||||
|
offset: i32,
|
||||||
) {
|
) {
|
||||||
let base_offset = storage_manager.claim_stack_area(&dst, 24);
|
let base_offset = storage_manager.claim_stack_area(&dst, 24);
|
||||||
|
|
||||||
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, 0);
|
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, offset);
|
||||||
ASM::mov_base32_reg64(buf, base_offset, tmp_reg);
|
ASM::mov_base32_reg64(buf, base_offset, tmp_reg);
|
||||||
|
|
||||||
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, 8);
|
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, offset + 8);
|
||||||
ASM::mov_base32_reg64(buf, base_offset + 8, tmp_reg);
|
ASM::mov_base32_reg64(buf, base_offset + 8, tmp_reg);
|
||||||
|
|
||||||
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, 16);
|
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, offset + 16);
|
||||||
ASM::mov_base32_reg64(buf, base_offset + 16, tmp_reg);
|
ASM::mov_base32_reg64(buf, base_offset + 16, tmp_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3323,6 +3691,7 @@ impl<
|
|||||||
stack_size: u32,
|
stack_size: u32,
|
||||||
ptr_reg: GeneralReg,
|
ptr_reg: GeneralReg,
|
||||||
tmp_reg: GeneralReg,
|
tmp_reg: GeneralReg,
|
||||||
|
read_offset: i32,
|
||||||
) {
|
) {
|
||||||
let mut copied = 0;
|
let mut copied = 0;
|
||||||
let size = stack_size as i32;
|
let size = stack_size as i32;
|
||||||
@ -3336,7 +3705,7 @@ impl<
|
|||||||
|
|
||||||
if size - copied >= 8 {
|
if size - copied >= 8 {
|
||||||
for _ in (0..(size - copied)).step_by(8) {
|
for _ in (0..(size - copied)).step_by(8) {
|
||||||
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, copied);
|
ASM::mov_reg64_mem64_offset32(buf, tmp_reg, ptr_reg, read_offset + copied);
|
||||||
ASM::mov_base32_reg64(buf, base_offset + copied, tmp_reg);
|
ASM::mov_base32_reg64(buf, base_offset + copied, tmp_reg);
|
||||||
|
|
||||||
copied += 8;
|
copied += 8;
|
||||||
@ -3345,7 +3714,7 @@ impl<
|
|||||||
|
|
||||||
if size - copied >= 4 {
|
if size - copied >= 4 {
|
||||||
for _ in (0..(size - copied)).step_by(4) {
|
for _ in (0..(size - copied)).step_by(4) {
|
||||||
ASM::mov_reg32_mem32_offset32(buf, tmp_reg, ptr_reg, copied);
|
ASM::mov_reg32_mem32_offset32(buf, tmp_reg, ptr_reg, read_offset + copied);
|
||||||
ASM::mov_base32_reg32(buf, base_offset + copied, tmp_reg);
|
ASM::mov_base32_reg32(buf, base_offset + copied, tmp_reg);
|
||||||
|
|
||||||
copied += 4;
|
copied += 4;
|
||||||
@ -3354,7 +3723,7 @@ impl<
|
|||||||
|
|
||||||
if size - copied >= 2 {
|
if size - copied >= 2 {
|
||||||
for _ in (0..(size - copied)).step_by(2) {
|
for _ in (0..(size - copied)).step_by(2) {
|
||||||
ASM::mov_reg16_mem16_offset32(buf, tmp_reg, ptr_reg, copied);
|
ASM::mov_reg16_mem16_offset32(buf, tmp_reg, ptr_reg, read_offset + copied);
|
||||||
ASM::mov_base32_reg16(buf, base_offset + copied, tmp_reg);
|
ASM::mov_base32_reg16(buf, base_offset + copied, tmp_reg);
|
||||||
|
|
||||||
copied += 2;
|
copied += 2;
|
||||||
@ -3363,7 +3732,7 @@ impl<
|
|||||||
|
|
||||||
if size - copied >= 1 {
|
if size - copied >= 1 {
|
||||||
for _ in (0..(size - copied)).step_by(1) {
|
for _ in (0..(size - copied)).step_by(1) {
|
||||||
ASM::mov_reg8_mem8_offset32(buf, tmp_reg, ptr_reg, copied);
|
ASM::mov_reg8_mem8_offset32(buf, tmp_reg, ptr_reg, read_offset + copied);
|
||||||
ASM::mov_base32_reg8(buf, base_offset + copied, tmp_reg);
|
ASM::mov_base32_reg8(buf, base_offset + copied, tmp_reg);
|
||||||
|
|
||||||
copied += 1;
|
copied += 1;
|
||||||
@ -3376,6 +3745,7 @@ impl<
|
|||||||
storage_manager: &mut StorageManager<'a, 'r, GeneralReg, FloatReg, ASM, CC>,
|
storage_manager: &mut StorageManager<'a, 'r, GeneralReg, FloatReg, ASM, CC>,
|
||||||
layout_interner: &STLayoutInterner<'a>,
|
layout_interner: &STLayoutInterner<'a>,
|
||||||
ptr_reg: GeneralReg,
|
ptr_reg: GeneralReg,
|
||||||
|
offset: i32,
|
||||||
element_in_layout: InLayout<'a>,
|
element_in_layout: InLayout<'a>,
|
||||||
dst: Symbol,
|
dst: Symbol,
|
||||||
) {
|
) {
|
||||||
@ -3388,48 +3758,55 @@ impl<
|
|||||||
}
|
}
|
||||||
IntWidth::I64 | IntWidth::U64 => {
|
IntWidth::I64 | IntWidth::U64 => {
|
||||||
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
||||||
ASM::mov_reg64_mem64_offset32(buf, dst_reg, ptr_reg, 0);
|
ASM::mov_reg64_mem64_offset32(buf, dst_reg, ptr_reg, offset);
|
||||||
}
|
}
|
||||||
IntWidth::I32 | IntWidth::U32 => {
|
IntWidth::I32 | IntWidth::U32 => {
|
||||||
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
||||||
ASM::mov_reg32_mem32_offset32(buf, dst_reg, ptr_reg, 0);
|
ASM::mov_reg32_mem32_offset32(buf, dst_reg, ptr_reg, offset);
|
||||||
}
|
}
|
||||||
IntWidth::I16 | IntWidth::U16 => {
|
IntWidth::I16 | IntWidth::U16 => {
|
||||||
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
||||||
ASM::mov_reg16_mem16_offset32(buf, dst_reg, ptr_reg, 0);
|
ASM::mov_reg16_mem16_offset32(buf, dst_reg, ptr_reg, offset);
|
||||||
}
|
}
|
||||||
IntWidth::I8 | IntWidth::U8 => {
|
IntWidth::I8 | IntWidth::U8 => {
|
||||||
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
||||||
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, 0);
|
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, offset);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Builtin::Float(FloatWidth::F64) => {
|
Builtin::Float(FloatWidth::F64) => {
|
||||||
let dst_reg = storage_manager.claim_float_reg(buf, &dst);
|
let dst_reg = storage_manager.claim_float_reg(buf, &dst);
|
||||||
ASM::mov_freg64_mem64_offset32(buf, dst_reg, ptr_reg, 0);
|
ASM::mov_freg64_mem64_offset32(buf, dst_reg, ptr_reg, offset);
|
||||||
}
|
}
|
||||||
Builtin::Float(FloatWidth::F32) => {
|
Builtin::Float(FloatWidth::F32) => {
|
||||||
let dst_reg = storage_manager.claim_float_reg(buf, &dst);
|
let dst_reg = storage_manager.claim_float_reg(buf, &dst);
|
||||||
ASM::mov_freg32_mem32_offset32(buf, dst_reg, ptr_reg, 0);
|
ASM::mov_freg32_mem32_offset32(buf, dst_reg, ptr_reg, offset);
|
||||||
}
|
}
|
||||||
Builtin::Bool => {
|
Builtin::Bool => {
|
||||||
// the same as an 8-bit integer
|
// the same as an 8-bit integer
|
||||||
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
||||||
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, 0);
|
ASM::mov_reg8_mem8_offset32(buf, dst_reg, ptr_reg, offset);
|
||||||
}
|
}
|
||||||
Builtin::Decimal => {
|
Builtin::Decimal => {
|
||||||
// same as 128-bit integer
|
// same as 128-bit integer
|
||||||
}
|
}
|
||||||
Builtin::Str | Builtin::List(_) => {
|
Builtin::Str | Builtin::List(_) => {
|
||||||
storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| {
|
storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| {
|
||||||
Self::unbox_str_or_list(buf, storage_manager, dst, ptr_reg, tmp_reg);
|
Self::unbox_str_or_list(
|
||||||
|
buf,
|
||||||
|
storage_manager,
|
||||||
|
dst,
|
||||||
|
ptr_reg,
|
||||||
|
tmp_reg,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
LayoutRepr::Boxed(_) => {
|
pointer_layouts!() => {
|
||||||
// the same as 64-bit integer (for 64-bit targets)
|
// the same as 64-bit integer (for 64-bit targets)
|
||||||
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
let dst_reg = storage_manager.claim_general_reg(buf, &dst);
|
||||||
ASM::mov_reg64_mem64_offset32(buf, dst_reg, ptr_reg, 0);
|
ASM::mov_reg64_mem64_offset32(buf, dst_reg, ptr_reg, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutRepr::Struct { .. } => {
|
LayoutRepr::Struct { .. } => {
|
||||||
@ -3437,7 +3814,15 @@ impl<
|
|||||||
let stack_size = layout_interner.stack_size(element_in_layout);
|
let stack_size = layout_interner.stack_size(element_in_layout);
|
||||||
|
|
||||||
storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| {
|
storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| {
|
||||||
Self::unbox_to_stack(buf, storage_manager, dst, stack_size, ptr_reg, tmp_reg);
|
Self::unbox_to_stack(
|
||||||
|
buf,
|
||||||
|
storage_manager,
|
||||||
|
dst,
|
||||||
|
stack_size,
|
||||||
|
ptr_reg,
|
||||||
|
tmp_reg,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3446,7 +3831,15 @@ impl<
|
|||||||
let stack_size = layout_interner.stack_size(element_in_layout);
|
let stack_size = layout_interner.stack_size(element_in_layout);
|
||||||
|
|
||||||
storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| {
|
storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp_reg| {
|
||||||
Self::unbox_to_stack(buf, storage_manager, dst, stack_size, ptr_reg, tmp_reg);
|
Self::unbox_to_stack(
|
||||||
|
buf,
|
||||||
|
storage_manager,
|
||||||
|
dst,
|
||||||
|
stack_size,
|
||||||
|
ptr_reg,
|
||||||
|
tmp_reg,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3456,12 +3849,11 @@ impl<
|
|||||||
storage_manager,
|
storage_manager,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
ptr_reg,
|
ptr_reg,
|
||||||
|
offset,
|
||||||
lambda_set.runtime_representation(),
|
lambda_set.runtime_representation(),
|
||||||
dst,
|
dst,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => todo!("unboxing of {:?}", layout_interner.dbg(element_in_layout)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3497,7 +3889,7 @@ impl<
|
|||||||
let sym_reg = storage_manager.load_to_float_reg(buf, &value);
|
let sym_reg = storage_manager.load_to_float_reg(buf, &value);
|
||||||
ASM::movesd_mem64_offset32_freg64(buf, ptr_reg, element_offset, sym_reg);
|
ASM::movesd_mem64_offset32_freg64(buf, ptr_reg, element_offset, sym_reg);
|
||||||
}
|
}
|
||||||
LayoutRepr::Boxed(_) => {
|
pointer_layouts!() => {
|
||||||
let sym_reg = storage_manager.load_to_general_reg(buf, &value);
|
let sym_reg = storage_manager.load_to_general_reg(buf, &value);
|
||||||
ASM::mov_mem64_offset32_reg64(buf, ptr_reg, element_offset, sym_reg);
|
ASM::mov_mem64_offset32_reg64(buf, ptr_reg, element_offset, sym_reg);
|
||||||
}
|
}
|
||||||
@ -3605,7 +3997,7 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the alignment bytes of `layout` into the given `symbol`
|
/// Loads the alignment bytes of `layout` into the given `symbol`
|
||||||
fn load_layout_alignment(&mut self, layout: InLayout<'a>, symbol: Symbol) {
|
fn load_layout_alignment(&mut self, layout: InLayout<'_>, symbol: Symbol) {
|
||||||
let u32_layout = Layout::U32;
|
let u32_layout = Layout::U32;
|
||||||
let alignment = self.layout_interner.alignment_bytes(layout);
|
let alignment = self.layout_interner.alignment_bytes(layout);
|
||||||
let alignment_literal = Literal::Int((alignment as i128).to_ne_bytes());
|
let alignment_literal = Literal::Int((alignment as i128).to_ne_bytes());
|
||||||
@ -3614,7 +4006,7 @@ impl<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the stack size of `layout` into the given `symbol`
|
/// Loads the stack size of `layout` into the given `symbol`
|
||||||
fn load_layout_stack_size(&mut self, layout: InLayout<'a>, symbol: Symbol) {
|
fn load_layout_stack_size(&mut self, layout: InLayout<'_>, symbol: Symbol) {
|
||||||
let u64_layout = Layout::U64;
|
let u64_layout = Layout::U64;
|
||||||
let width = self.layout_interner.stack_size(layout);
|
let width = self.layout_interner.stack_size(layout);
|
||||||
let width_literal = Literal::Int((width as i128).to_ne_bytes());
|
let width_literal = Literal::Int((width as i128).to_ne_bytes());
|
||||||
@ -3671,3 +4063,17 @@ macro_rules! single_register_layouts {
|
|||||||
single_register_integers!() | single_register_floats!()
|
single_register_integers!() | single_register_floats!()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! pointer_layouts {
|
||||||
|
() => {
|
||||||
|
LayoutRepr::Boxed(_)
|
||||||
|
| LayoutRepr::RecursivePointer(_)
|
||||||
|
| LayoutRepr::Union(
|
||||||
|
UnionLayout::Recursive(_)
|
||||||
|
| UnionLayout::NonNullableUnwrapped(_)
|
||||||
|
| UnionLayout::NullableWrapped { .. }
|
||||||
|
| UnionLayout::NullableUnwrapped { .. },
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
generic64::{Assembler, CallConv, RegTrait},
|
generic64::{Assembler, CallConv, RegTrait},
|
||||||
sign_extended_int_builtins, single_register_floats, single_register_int_builtins,
|
pointer_layouts, sign_extended_int_builtins, single_register_floats,
|
||||||
single_register_integers, single_register_layouts, Env,
|
single_register_int_builtins, single_register_integers, single_register_layouts, Env,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
@ -11,8 +11,7 @@ use roc_module::symbol::Symbol;
|
|||||||
use roc_mono::{
|
use roc_mono::{
|
||||||
ir::{JoinPointId, Param},
|
ir::{JoinPointId, Param},
|
||||||
layout::{
|
layout::{
|
||||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, TagIdIntType,
|
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
||||||
UnionLayout,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
@ -91,7 +90,7 @@ pub struct StorageManager<
|
|||||||
phantom_cc: PhantomData<CC>,
|
phantom_cc: PhantomData<CC>,
|
||||||
phantom_asm: PhantomData<ASM>,
|
phantom_asm: PhantomData<ASM>,
|
||||||
pub(crate) env: &'r Env<'a>,
|
pub(crate) env: &'r Env<'a>,
|
||||||
target_info: TargetInfo,
|
pub(crate) target_info: TargetInfo,
|
||||||
// Data about where each symbol is stored.
|
// Data about where each symbol is stored.
|
||||||
symbol_storage_map: MutMap<Symbol, Storage<GeneralReg, FloatReg>>,
|
symbol_storage_map: MutMap<Symbol, Storage<GeneralReg, FloatReg>>,
|
||||||
|
|
||||||
@ -598,20 +597,21 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_union_tag_id(
|
pub fn load_union_tag_id_nonrecursive(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout_interner: &mut STLayoutInterner<'a>,
|
layout_interner: &mut STLayoutInterner<'a>,
|
||||||
_buf: &mut Vec<'a, u8>,
|
_buf: &mut Vec<'a, u8>,
|
||||||
sym: &Symbol,
|
sym: &Symbol,
|
||||||
structure: &Symbol,
|
structure: &Symbol,
|
||||||
union_layout: &UnionLayout<'a>,
|
tags: &[&[InLayout]],
|
||||||
) {
|
) {
|
||||||
|
let union_layout = UnionLayout::NonRecursive(tags);
|
||||||
|
|
||||||
// This must be removed and reinserted for ownership and mutability reasons.
|
// This must be removed and reinserted for ownership and mutability reasons.
|
||||||
let owned_data = self.remove_allocation_for_sym(structure);
|
let owned_data = self.remove_allocation_for_sym(structure);
|
||||||
self.allocation_map
|
self.allocation_map
|
||||||
.insert(*structure, Rc::clone(&owned_data));
|
.insert(*structure, Rc::clone(&owned_data));
|
||||||
match union_layout {
|
|
||||||
UnionLayout::NonRecursive(_) => {
|
|
||||||
let (union_offset, _) = self.stack_offset_and_size(structure);
|
let (union_offset, _) = self.stack_offset_and_size(structure);
|
||||||
|
|
||||||
let (data_size, data_alignment) =
|
let (data_size, data_alignment) =
|
||||||
@ -630,9 +630,6 @@ impl<
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
x => todo!("getting tag id of union with layout ({:?})", x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loads the dst to be the later 64 bits of a list (its length).
|
// Loads the dst to be the later 64 bits of a list (its length).
|
||||||
pub fn list_len(&mut self, _buf: &mut Vec<'a, u8>, dst: &Symbol, list: &Symbol) {
|
pub fn list_len(&mut self, _buf: &mut Vec<'a, u8>, dst: &Symbol, list: &Symbol) {
|
||||||
@ -700,56 +697,6 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a union on the stack, moving the data in fields into the union and tagging it.
|
|
||||||
pub fn create_union(
|
|
||||||
&mut self,
|
|
||||||
layout_interner: &mut STLayoutInterner<'a>,
|
|
||||||
buf: &mut Vec<'a, u8>,
|
|
||||||
sym: &Symbol,
|
|
||||||
union_layout: &UnionLayout<'a>,
|
|
||||||
fields: &'a [Symbol],
|
|
||||||
tag_id: TagIdIntType,
|
|
||||||
) {
|
|
||||||
match union_layout {
|
|
||||||
UnionLayout::NonRecursive(field_layouts) => {
|
|
||||||
let (data_size, data_alignment) =
|
|
||||||
union_layout.data_size_and_alignment(layout_interner, self.target_info);
|
|
||||||
let id_offset = data_size - data_alignment;
|
|
||||||
let base_offset = self.claim_stack_area(sym, data_size);
|
|
||||||
let mut current_offset = base_offset;
|
|
||||||
|
|
||||||
let it = fields.iter().zip(field_layouts[tag_id as usize].iter());
|
|
||||||
for (field, field_layout) in it {
|
|
||||||
self.copy_symbol_to_stack_offset(
|
|
||||||
layout_interner,
|
|
||||||
buf,
|
|
||||||
current_offset,
|
|
||||||
field,
|
|
||||||
field_layout,
|
|
||||||
);
|
|
||||||
let field_size = layout_interner.stack_size(*field_layout);
|
|
||||||
current_offset += field_size as i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
// put the tag id in the right place
|
|
||||||
self.with_tmp_general_reg(buf, |_symbol_storage, buf, reg| {
|
|
||||||
ASM::mov_reg64_imm64(buf, reg, tag_id as i64);
|
|
||||||
|
|
||||||
let total_id_offset = base_offset as u32 + id_offset;
|
|
||||||
debug_assert!(total_id_offset % data_alignment == 0);
|
|
||||||
|
|
||||||
// pick the right instruction based on the alignment of the tag id
|
|
||||||
if field_layouts.len() <= u8::MAX as _ {
|
|
||||||
ASM::mov_base32_reg8(buf, total_id_offset as i32, reg);
|
|
||||||
} else {
|
|
||||||
ASM::mov_base32_reg16(buf, total_id_offset as i32, reg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
x => todo!("creating unions with layout: {:?}", x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copies a complex symbol on the stack to the arg pointer.
|
/// Copies a complex symbol on the stack to the arg pointer.
|
||||||
pub fn copy_symbol_to_arg_pointer(
|
pub fn copy_symbol_to_arg_pointer(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -845,12 +792,6 @@ impl<
|
|||||||
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
|
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
LayoutRepr::Boxed(_) => {
|
|
||||||
// like a 64-bit integer
|
|
||||||
debug_assert_eq!(to_offset % 8, 0);
|
|
||||||
let reg = self.load_to_general_reg(buf, sym);
|
|
||||||
ASM::mov_base32_reg64(buf, to_offset, reg);
|
|
||||||
}
|
|
||||||
LayoutRepr::LambdaSet(lambda_set) => {
|
LayoutRepr::LambdaSet(lambda_set) => {
|
||||||
// like its runtime representation
|
// like its runtime representation
|
||||||
self.copy_symbol_to_stack_offset(
|
self.copy_symbol_to_stack_offset(
|
||||||
@ -861,14 +802,18 @@ impl<
|
|||||||
&lambda_set.runtime_representation(),
|
&lambda_set.runtime_representation(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ if layout_interner.stack_size(*layout) == 0 => {}
|
|
||||||
LayoutRepr::Struct { .. } | LayoutRepr::Union(UnionLayout::NonRecursive(_)) => {
|
LayoutRepr::Struct { .. } | LayoutRepr::Union(UnionLayout::NonRecursive(_)) => {
|
||||||
let (from_offset, size) = self.stack_offset_and_size(sym);
|
let (from_offset, size) = self.stack_offset_and_size(sym);
|
||||||
debug_assert_eq!(size, layout_interner.stack_size(*layout));
|
debug_assert_eq!(size, layout_interner.stack_size(*layout));
|
||||||
|
|
||||||
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
|
self.copy_to_stack_offset(buf, size, from_offset, to_offset)
|
||||||
}
|
}
|
||||||
x => todo!("copying data to the stack with layout, {:?}", x),
|
LayoutRepr::RecursivePointer(_) | LayoutRepr::Boxed(_) | LayoutRepr::Union(_) => {
|
||||||
|
// like a 64-bit integer
|
||||||
|
debug_assert_eq!(to_offset % 8, 0);
|
||||||
|
let reg = self.load_to_general_reg(buf, sym);
|
||||||
|
ASM::mov_base32_reg64(buf, to_offset, reg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1160,7 +1105,7 @@ impl<
|
|||||||
layout: InLayout<'a>,
|
layout: InLayout<'a>,
|
||||||
) {
|
) {
|
||||||
match layout_interner.get(layout).repr {
|
match layout_interner.get(layout).repr {
|
||||||
single_register_layouts!() => {
|
single_register_layouts!() | pointer_layouts!() => {
|
||||||
let base_offset = self.claim_stack_size(8);
|
let base_offset = self.claim_stack_size(8);
|
||||||
self.symbol_storage_map.insert(
|
self.symbol_storage_map.insert(
|
||||||
symbol,
|
symbol,
|
||||||
@ -1172,14 +1117,12 @@ impl<
|
|||||||
self.allocation_map
|
self.allocation_map
|
||||||
.insert(symbol, Rc::new((base_offset, 8)));
|
.insert(symbol, Rc::new((base_offset, 8)));
|
||||||
}
|
}
|
||||||
_ => {
|
LayoutRepr::LambdaSet(lambda_set) => self.joinpoint_argument_stack_storage(
|
||||||
if let LayoutRepr::LambdaSet(lambda_set) = layout_interner.get(layout).repr {
|
|
||||||
self.joinpoint_argument_stack_storage(
|
|
||||||
layout_interner,
|
layout_interner,
|
||||||
symbol,
|
symbol,
|
||||||
lambda_set.runtime_representation(),
|
lambda_set.runtime_representation(),
|
||||||
)
|
),
|
||||||
} else {
|
_ => {
|
||||||
let stack_size = layout_interner.stack_size(layout);
|
let stack_size = layout_interner.stack_size(layout);
|
||||||
if stack_size == 0 {
|
if stack_size == 0 {
|
||||||
self.no_data(&symbol);
|
self.no_data(&symbol);
|
||||||
@ -1189,7 +1132,6 @@ impl<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Setups a join point.
|
/// Setups a join point.
|
||||||
/// To do this, each of the join pionts params are given a storage location.
|
/// To do this, each of the join pionts params are given a storage location.
|
||||||
@ -1228,7 +1170,7 @@ impl<
|
|||||||
base_offset: i32,
|
base_offset: i32,
|
||||||
) {
|
) {
|
||||||
match layout_interner.get(layout).repr {
|
match layout_interner.get(layout).repr {
|
||||||
single_register_integers!() => {
|
single_register_integers!() | pointer_layouts!() => {
|
||||||
let reg = self.load_to_general_reg(buf, &symbol);
|
let reg = self.load_to_general_reg(buf, &symbol);
|
||||||
ASM::mov_base32_reg64(buf, base_offset, reg);
|
ASM::mov_base32_reg64(buf, base_offset, reg);
|
||||||
}
|
}
|
||||||
@ -1236,7 +1178,6 @@ impl<
|
|||||||
let reg = self.load_to_float_reg(buf, &symbol);
|
let reg = self.load_to_float_reg(buf, &symbol);
|
||||||
ASM::mov_base32_freg64(buf, base_offset, reg);
|
ASM::mov_base32_freg64(buf, base_offset, reg);
|
||||||
}
|
}
|
||||||
_ => match layout_interner.get(layout).repr {
|
|
||||||
LayoutRepr::LambdaSet(lambda_set) => {
|
LayoutRepr::LambdaSet(lambda_set) => {
|
||||||
self.jump_argument_stack_storage(
|
self.jump_argument_stack_storage(
|
||||||
layout_interner,
|
layout_interner,
|
||||||
@ -1246,17 +1187,12 @@ impl<
|
|||||||
base_offset,
|
base_offset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
LayoutRepr::Boxed(_) => {
|
|
||||||
let reg = self.load_to_general_reg(buf, &symbol);
|
|
||||||
ASM::mov_base32_reg64(buf, base_offset, reg);
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
internal_error!(
|
internal_error!(
|
||||||
r"cannot load non-primitive layout ({:?}) to primitive stack location",
|
r"cannot load non-primitive layout ({:?}) to primitive stack location",
|
||||||
layout_interner.dbg(layout)
|
layout_interner.dbg(layout)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1341,6 +1277,22 @@ impl<
|
|||||||
base_offset
|
base_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn claim_pointer_stack_area(&mut self, sym: Symbol) -> i32 {
|
||||||
|
let size = 8;
|
||||||
|
|
||||||
|
let base_offset = self.claim_stack_size(size);
|
||||||
|
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
sym,
|
||||||
|
Stack(Primitive {
|
||||||
|
base_offset,
|
||||||
|
reg: None,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
base_offset
|
||||||
|
}
|
||||||
|
|
||||||
/// claim_stack_size claims `amount` bytes from the stack alignind to 8.
|
/// claim_stack_size claims `amount` bytes from the stack alignind to 8.
|
||||||
/// This may be free space in the stack or result in increasing the stack size.
|
/// This may be free space in the stack or result in increasing the stack size.
|
||||||
/// It returns base pointer relative offset of the new data.
|
/// It returns base pointer relative offset of the new data.
|
||||||
@ -1541,12 +1493,10 @@ impl<
|
|||||||
fn is_primitive(layout_interner: &mut STLayoutInterner<'_>, layout: InLayout<'_>) -> bool {
|
fn is_primitive(layout_interner: &mut STLayoutInterner<'_>, layout: InLayout<'_>) -> bool {
|
||||||
match layout_interner.get(layout).repr {
|
match layout_interner.get(layout).repr {
|
||||||
single_register_layouts!() => true,
|
single_register_layouts!() => true,
|
||||||
_ => match layout_interner.get(layout).repr {
|
pointer_layouts!() => true,
|
||||||
LayoutRepr::Boxed(_) => true,
|
|
||||||
LayoutRepr::LambdaSet(lambda_set) => {
|
LayoutRepr::LambdaSet(lambda_set) => {
|
||||||
is_primitive(layout_interner, lambda_set.runtime_representation())
|
is_primitive(layout_interner, lambda_set.runtime_representation())
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait};
|
use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait};
|
||||||
use crate::{
|
use crate::{
|
||||||
single_register_floats, single_register_int_builtins, single_register_integers,
|
pointer_layouts, single_register_floats, single_register_int_builtins,
|
||||||
single_register_layouts, Relocation,
|
single_register_integers, single_register_layouts, Relocation,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
@ -461,6 +461,7 @@ impl X64_64SystemVStoreArgs {
|
|||||||
) {
|
) {
|
||||||
match layout_interner.get(in_layout).repr {
|
match layout_interner.get(in_layout).repr {
|
||||||
single_register_integers!() => self.store_arg_general(buf, storage_manager, sym),
|
single_register_integers!() => self.store_arg_general(buf, storage_manager, sym),
|
||||||
|
pointer_layouts!() => self.store_arg_general(buf, storage_manager, sym),
|
||||||
single_register_floats!() => self.store_arg_float(buf, storage_manager, sym),
|
single_register_floats!() => self.store_arg_float(buf, storage_manager, sym),
|
||||||
LayoutRepr::I128 | LayoutRepr::U128 => {
|
LayoutRepr::I128 | LayoutRepr::U128 => {
|
||||||
let (offset, _) = storage_manager.stack_offset_and_size(&sym);
|
let (offset, _) = storage_manager.stack_offset_and_size(&sym);
|
||||||
@ -507,13 +508,6 @@ impl X64_64SystemVStoreArgs {
|
|||||||
}
|
}
|
||||||
self.tmp_stack_offset += size as i32;
|
self.tmp_stack_offset += size as i32;
|
||||||
}
|
}
|
||||||
other => {
|
|
||||||
// look at the layout in more detail
|
|
||||||
match other {
|
|
||||||
LayoutRepr::Boxed(_) => {
|
|
||||||
// treat boxed like a 64-bit integer
|
|
||||||
self.store_arg_general(buf, storage_manager, sym)
|
|
||||||
}
|
|
||||||
LayoutRepr::LambdaSet(lambda_set) => self.store_arg(
|
LayoutRepr::LambdaSet(lambda_set) => self.store_arg(
|
||||||
buf,
|
buf,
|
||||||
storage_manager,
|
storage_manager,
|
||||||
@ -594,8 +588,6 @@ impl X64_64SystemVStoreArgs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_arg_general<'a>(
|
fn store_arg_general<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -664,6 +656,7 @@ impl X64_64SystemVLoadArgs {
|
|||||||
let stack_size = layout_interner.stack_size(in_layout);
|
let stack_size = layout_interner.stack_size(in_layout);
|
||||||
match layout_interner.get(in_layout).repr {
|
match layout_interner.get(in_layout).repr {
|
||||||
single_register_integers!() => self.load_arg_general(storage_manager, sym),
|
single_register_integers!() => self.load_arg_general(storage_manager, sym),
|
||||||
|
pointer_layouts!() => self.load_arg_general(storage_manager, sym),
|
||||||
single_register_floats!() => self.load_arg_float(storage_manager, sym),
|
single_register_floats!() => self.load_arg_float(storage_manager, sym),
|
||||||
_ if stack_size == 0 => {
|
_ if stack_size == 0 => {
|
||||||
storage_manager.no_data(&sym);
|
storage_manager.no_data(&sym);
|
||||||
@ -673,11 +666,6 @@ impl X64_64SystemVLoadArgs {
|
|||||||
storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size);
|
storage_manager.complex_stack_arg(&sym, self.argument_offset, stack_size);
|
||||||
self.argument_offset += stack_size as i32;
|
self.argument_offset += stack_size as i32;
|
||||||
}
|
}
|
||||||
other => match other {
|
|
||||||
LayoutRepr::Boxed(_) => {
|
|
||||||
// boxed layouts are pointers, which we treat as 64-bit integers
|
|
||||||
self.load_arg_general(storage_manager, sym)
|
|
||||||
}
|
|
||||||
LayoutRepr::LambdaSet(lambda_set) => self.load_arg(
|
LayoutRepr::LambdaSet(lambda_set) => self.load_arg(
|
||||||
storage_manager,
|
storage_manager,
|
||||||
layout_interner,
|
layout_interner,
|
||||||
@ -704,7 +692,6 @@ impl X64_64SystemVLoadArgs {
|
|||||||
layout_interner.dbg(in_layout)
|
layout_interner.dbg(in_layout)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1152,6 +1139,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
||||||
|
#[inline(always)]
|
||||||
|
fn base_pointer() -> X86_64GeneralReg {
|
||||||
|
X86_64GeneralReg::RBP
|
||||||
|
}
|
||||||
|
|
||||||
// These functions should map to the raw assembly functions below.
|
// These functions should map to the raw assembly functions below.
|
||||||
// In some cases, that means you can just directly call one of the direct assembly functions.
|
// In some cases, that means you can just directly call one of the direct assembly functions.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -1756,7 +1748,7 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn neq_reg64_reg64_reg64(
|
fn neq_reg_reg_reg(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
register_width: RegisterWidth,
|
register_width: RegisterWidth,
|
||||||
dst: X86_64GeneralReg,
|
dst: X86_64GeneralReg,
|
||||||
|
@ -798,7 +798,7 @@ trait Backend<'a> {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
self.load_literal_symbols(arguments);
|
self.load_literal_symbols(arguments);
|
||||||
self.tag(sym, arguments, tag_layout, *tag_id);
|
self.tag(sym, arguments, tag_layout, *tag_id, None);
|
||||||
}
|
}
|
||||||
Expr::ExprBox { symbol: value } => {
|
Expr::ExprBox { symbol: value } => {
|
||||||
let element_layout = match self.interner().get(*layout).repr {
|
let element_layout = match self.interner().get(*layout).repr {
|
||||||
@ -807,7 +807,7 @@ trait Backend<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.load_literal_symbols([*value].as_slice());
|
self.load_literal_symbols([*value].as_slice());
|
||||||
self.expr_box(*sym, *value, element_layout)
|
self.expr_box(*sym, *value, element_layout, None)
|
||||||
}
|
}
|
||||||
Expr::ExprUnbox { symbol: ptr } => {
|
Expr::ExprUnbox { symbol: ptr } => {
|
||||||
let element_layout = *layout;
|
let element_layout = *layout;
|
||||||
@ -815,7 +815,60 @@ trait Backend<'a> {
|
|||||||
self.load_literal_symbols([*ptr].as_slice());
|
self.load_literal_symbols([*ptr].as_slice());
|
||||||
self.expr_unbox(*sym, *ptr, element_layout)
|
self.expr_unbox(*sym, *ptr, element_layout)
|
||||||
}
|
}
|
||||||
x => todo!("the expression, {:?}", x),
|
Expr::NullPointer => {
|
||||||
|
self.load_literal_i64(sym, 0);
|
||||||
|
}
|
||||||
|
Expr::Reuse {
|
||||||
|
tag_layout,
|
||||||
|
tag_id,
|
||||||
|
symbol: reused,
|
||||||
|
arguments,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
self.load_literal_symbols(arguments);
|
||||||
|
self.tag(sym, arguments, tag_layout, *tag_id, Some(*reused));
|
||||||
|
}
|
||||||
|
Expr::Reset { symbol, .. } => {
|
||||||
|
let layout = *self.layout_map().get(symbol).unwrap();
|
||||||
|
|
||||||
|
// Expand the Refcounting statement into more detailed IR with a function call
|
||||||
|
// If this layout requires a new RC proc, we get enough info to create a linker symbol
|
||||||
|
// for it. Here we don't create linker symbols at this time, but in Wasm backend, we do.
|
||||||
|
let (new_expr, new_specializations) = {
|
||||||
|
let (module_id, layout_interner, interns, rc_proc_gen, _) =
|
||||||
|
self.module_interns_helpers_mut();
|
||||||
|
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||||
|
|
||||||
|
rc_proc_gen.call_reset_refcount(ident_ids, layout_interner, layout, *symbol)
|
||||||
|
};
|
||||||
|
|
||||||
|
for spec in new_specializations.into_iter() {
|
||||||
|
self.helper_proc_symbols_mut().push(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.build_expr(sym, &new_expr, &Layout::BOOL)
|
||||||
|
}
|
||||||
|
Expr::ResetRef { symbol, .. } => {
|
||||||
|
let layout = *self.layout_map().get(symbol).unwrap();
|
||||||
|
|
||||||
|
// Expand the Refcounting statement into more detailed IR with a function call
|
||||||
|
// If this layout requires a new RC proc, we get enough info to create a linker symbol
|
||||||
|
// for it. Here we don't create linker symbols at this time, but in Wasm backend, we do.
|
||||||
|
let (new_expr, new_specializations) = {
|
||||||
|
let (module_id, layout_interner, interns, rc_proc_gen, _) =
|
||||||
|
self.module_interns_helpers_mut();
|
||||||
|
let ident_ids = interns.all_ident_ids.get_mut(&module_id).unwrap();
|
||||||
|
|
||||||
|
rc_proc_gen.call_resetref_refcount(ident_ids, layout_interner, layout, *symbol)
|
||||||
|
};
|
||||||
|
|
||||||
|
for spec in new_specializations.into_iter() {
|
||||||
|
self.helper_proc_symbols_mut().push(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.build_expr(sym, &new_expr, &Layout::BOOL)
|
||||||
|
}
|
||||||
|
Expr::RuntimeErrorFunction(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1068,13 +1121,12 @@ trait Backend<'a> {
|
|||||||
}
|
}
|
||||||
LowLevel::Eq => {
|
LowLevel::Eq => {
|
||||||
debug_assert_eq!(2, args.len(), "Eq: expected to have exactly two argument");
|
debug_assert_eq!(2, args.len(), "Eq: expected to have exactly two argument");
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
arg_layouts[0], arg_layouts[1],
|
self.interner().eq_repr(arg_layouts[0], arg_layouts[1],),
|
||||||
"Eq: expected all arguments of to have the same layout"
|
"Eq: expected all arguments of to have the same layout"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"Eq: expected to have return layout of type Bool"
|
"Eq: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_eq(sym, &args[0], &args[1], &arg_layouts[0])
|
self.build_eq(sym, &args[0], &args[1], &arg_layouts[0])
|
||||||
@ -1085,22 +1137,20 @@ trait Backend<'a> {
|
|||||||
args.len(),
|
args.len(),
|
||||||
"NotEq: expected to have exactly two argument"
|
"NotEq: expected to have exactly two argument"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
arg_layouts[0], arg_layouts[1],
|
self.interner().eq_repr(arg_layouts[0], arg_layouts[1],),
|
||||||
"NotEq: expected all arguments of to have the same layout"
|
"NotEq: expected all arguments of to have the same layout"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NotEq: expected to have return layout of type Bool"
|
"NotEq: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_neq(sym, &args[0], &args[1], &arg_layouts[0])
|
self.build_neq(sym, &args[0], &args[1], &arg_layouts[0])
|
||||||
}
|
}
|
||||||
LowLevel::Not => {
|
LowLevel::Not => {
|
||||||
debug_assert_eq!(1, args.len(), "Not: expected to have exactly one argument");
|
debug_assert_eq!(1, args.len(), "Not: expected to have exactly one argument");
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"Not: expected to have return layout of type Bool"
|
"Not: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_not(sym, &args[0], &arg_layouts[0])
|
self.build_not(sym, &args[0], &arg_layouts[0])
|
||||||
@ -1111,13 +1161,12 @@ trait Backend<'a> {
|
|||||||
args.len(),
|
args.len(),
|
||||||
"NumLt: expected to have exactly two argument"
|
"NumLt: expected to have exactly two argument"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
arg_layouts[0], arg_layouts[1],
|
self.interner().eq_repr(arg_layouts[0], arg_layouts[1],),
|
||||||
"NumLt: expected all arguments of to have the same layout"
|
"NumLt: expected all arguments of to have the same layout"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NumLt: expected to have return layout of type Bool"
|
"NumLt: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_num_lt(sym, &args[0], &args[1], &arg_layouts[0])
|
self.build_num_lt(sym, &args[0], &args[1], &arg_layouts[0])
|
||||||
@ -1128,13 +1177,12 @@ trait Backend<'a> {
|
|||||||
args.len(),
|
args.len(),
|
||||||
"NumGt: expected to have exactly two argument"
|
"NumGt: expected to have exactly two argument"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
arg_layouts[0], arg_layouts[1],
|
self.interner().eq_repr(arg_layouts[0], arg_layouts[1],),
|
||||||
"NumGt: expected all arguments of to have the same layout"
|
"NumGt: expected all arguments of to have the same layout"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NumGt: expected to have return layout of type Bool"
|
"NumGt: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_num_gt(sym, &args[0], &args[1], &arg_layouts[0])
|
self.build_num_gt(sym, &args[0], &args[1], &arg_layouts[0])
|
||||||
@ -1159,9 +1207,8 @@ trait Backend<'a> {
|
|||||||
"NumIsNan: expected to have exactly one argument"
|
"NumIsNan: expected to have exactly one argument"
|
||||||
);
|
);
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NumIsNan: expected to have return layout of type Bool"
|
"NumIsNan: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_num_is_nan(sym, &args[0], &arg_layouts[0])
|
self.build_num_is_nan(sym, &args[0], &arg_layouts[0])
|
||||||
@ -1173,9 +1220,8 @@ trait Backend<'a> {
|
|||||||
"NumIsInfinite: expected to have exactly one argument"
|
"NumIsInfinite: expected to have exactly one argument"
|
||||||
);
|
);
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NumIsInfinite: expected to have return layout of type Bool"
|
"NumIsInfinite: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_num_is_infinite(sym, &args[0], &arg_layouts[0])
|
self.build_num_is_infinite(sym, &args[0], &arg_layouts[0])
|
||||||
@ -1187,9 +1233,8 @@ trait Backend<'a> {
|
|||||||
"NumIsFinite: expected to have exactly one argument"
|
"NumIsFinite: expected to have exactly one argument"
|
||||||
);
|
);
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NumIsFinite: expected to have return layout of type Bool"
|
"NumIsFinite: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_num_is_finite(sym, &args[0], &arg_layouts[0])
|
self.build_num_is_finite(sym, &args[0], &arg_layouts[0])
|
||||||
@ -1204,9 +1249,8 @@ trait Backend<'a> {
|
|||||||
arg_layouts[0], arg_layouts[1],
|
arg_layouts[0], arg_layouts[1],
|
||||||
"NumLte: expected all arguments of to have the same layout"
|
"NumLte: expected all arguments of to have the same layout"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NumLte: expected to have return layout of type Bool"
|
"NumLte: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_num_lte(sym, &args[0], &args[1], &arg_layouts[0])
|
self.build_num_lte(sym, &args[0], &args[1], &arg_layouts[0])
|
||||||
@ -1221,9 +1265,8 @@ trait Backend<'a> {
|
|||||||
arg_layouts[0], arg_layouts[1],
|
arg_layouts[0], arg_layouts[1],
|
||||||
"NumGte: expected all arguments of to have the same layout"
|
"NumGte: expected all arguments of to have the same layout"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NumGte: expected to have return layout of type Bool"
|
"NumGte: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
self.build_num_gte(sym, &args[0], &args[1], &arg_layouts[0])
|
self.build_num_gte(sym, &args[0], &args[1], &arg_layouts[0])
|
||||||
@ -1761,9 +1804,8 @@ trait Backend<'a> {
|
|||||||
args.len(),
|
args.len(),
|
||||||
"NumIsZero: expected to have exactly one argument"
|
"NumIsZero: expected to have exactly one argument"
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert!(
|
||||||
Layout::BOOL,
|
self.interner().eq_repr(Layout::BOOL, *ret_layout,),
|
||||||
*ret_layout,
|
|
||||||
"NumIsZero: expected to have return layout of type Bool"
|
"NumIsZero: expected to have return layout of type Bool"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2216,13 +2258,20 @@ trait Backend<'a> {
|
|||||||
args: &'a [Symbol],
|
args: &'a [Symbol],
|
||||||
tag_layout: &UnionLayout<'a>,
|
tag_layout: &UnionLayout<'a>,
|
||||||
tag_id: TagIdIntType,
|
tag_id: TagIdIntType,
|
||||||
|
reuse: Option<Symbol>,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// load a value from a pointer
|
/// load a value from a pointer
|
||||||
fn expr_unbox(&mut self, sym: Symbol, ptr: Symbol, element_layout: InLayout<'a>);
|
fn expr_unbox(&mut self, sym: Symbol, ptr: Symbol, element_layout: InLayout<'a>);
|
||||||
|
|
||||||
/// store a refcounted value on the heap
|
/// store a refcounted value on the heap
|
||||||
fn expr_box(&mut self, sym: Symbol, value: Symbol, element_layout: InLayout<'a>);
|
fn expr_box(
|
||||||
|
&mut self,
|
||||||
|
sym: Symbol,
|
||||||
|
value: Symbol,
|
||||||
|
element_layout: InLayout<'a>,
|
||||||
|
reuse: Option<Symbol>,
|
||||||
|
);
|
||||||
|
|
||||||
/// return_symbol moves a symbol to the correct return location for the backend and adds a jump to the end of the function.
|
/// return_symbol moves a symbol to the correct return location for the backend and adds a jump to the end of the function.
|
||||||
fn return_symbol(&mut self, sym: &Symbol, layout: &InLayout<'a>);
|
fn return_symbol(&mut self, sym: &Symbol, layout: &InLayout<'a>);
|
||||||
|
@ -7,11 +7,12 @@ use inkwell::{
|
|||||||
};
|
};
|
||||||
use roc_builtins::{
|
use roc_builtins::{
|
||||||
bitcode::{FloatWidth, IntWidth, IntrinsicName},
|
bitcode::{FloatWidth, IntWidth, IntrinsicName},
|
||||||
float_intrinsic, llvm_int_intrinsic,
|
llvm_int_intrinsic,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::build::{add_func, FunctionSpec};
|
use super::build::{add_func, FunctionSpec};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
fn add_float_intrinsic<'ctx, F>(
|
fn add_float_intrinsic<'ctx, F>(
|
||||||
ctx: &'ctx Context,
|
ctx: &'ctx Context,
|
||||||
module: &Module<'ctx>,
|
module: &Module<'ctx>,
|
||||||
@ -111,18 +112,6 @@ pub(crate) fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
|||||||
i8_ptr_type.fn_type(&[], false),
|
i8_ptr_type.fn_type(&[], false),
|
||||||
);
|
);
|
||||||
|
|
||||||
add_float_intrinsic(ctx, module, &LLVM_LOG, |t| t.fn_type(&[t.into()], false));
|
|
||||||
add_float_intrinsic(ctx, module, &LLVM_POW, |t| {
|
|
||||||
t.fn_type(&[t.into(), t.into()], false)
|
|
||||||
});
|
|
||||||
add_float_intrinsic(ctx, module, &LLVM_FABS, |t| t.fn_type(&[t.into()], false));
|
|
||||||
add_float_intrinsic(ctx, module, &LLVM_SIN, |t| t.fn_type(&[t.into()], false));
|
|
||||||
add_float_intrinsic(ctx, module, &LLVM_COS, |t| t.fn_type(&[t.into()], false));
|
|
||||||
add_float_intrinsic(ctx, module, &LLVM_CEILING, |t| {
|
|
||||||
t.fn_type(&[t.into()], false)
|
|
||||||
});
|
|
||||||
add_float_intrinsic(ctx, module, &LLVM_FLOOR, |t| t.fn_type(&[t.into()], false));
|
|
||||||
|
|
||||||
add_int_intrinsic(ctx, module, &LLVM_ADD_WITH_OVERFLOW, |t| {
|
add_int_intrinsic(ctx, module, &LLVM_ADD_WITH_OVERFLOW, |t| {
|
||||||
let fields = [t.into(), i1_type.into()];
|
let fields = [t.into(), i1_type.into()];
|
||||||
ctx.struct_type(&fields, false)
|
ctx.struct_type(&fields, false)
|
||||||
@ -150,17 +139,6 @@ pub(crate) fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LLVM_POW: IntrinsicName = float_intrinsic!("llvm.pow");
|
|
||||||
pub const LLVM_FABS: IntrinsicName = float_intrinsic!("llvm.fabs");
|
|
||||||
pub static LLVM_SQRT: IntrinsicName = float_intrinsic!("llvm.sqrt");
|
|
||||||
pub static LLVM_LOG: IntrinsicName = float_intrinsic!("llvm.log");
|
|
||||||
|
|
||||||
pub static LLVM_SIN: IntrinsicName = float_intrinsic!("llvm.sin");
|
|
||||||
pub static LLVM_COS: IntrinsicName = float_intrinsic!("llvm.cos");
|
|
||||||
pub static LLVM_CEILING: IntrinsicName = float_intrinsic!("llvm.ceil");
|
|
||||||
pub static LLVM_FLOOR: IntrinsicName = float_intrinsic!("llvm.floor");
|
|
||||||
pub static LLVM_ROUND: IntrinsicName = float_intrinsic!("llvm.round");
|
|
||||||
|
|
||||||
pub static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
pub static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
||||||
pub static LLVM_MEMSET_I32: &str = "llvm.memset.p0i8.i32";
|
pub static LLVM_MEMSET_I32: &str = "llvm.memset.p0i8.i32";
|
||||||
|
|
||||||
|
@ -41,9 +41,13 @@ use crate::llvm::{
|
|||||||
self, basic_type_from_layout, zig_num_parse_result_type, zig_to_int_checked_result_type,
|
self, basic_type_from_layout, zig_num_parse_result_type, zig_to_int_checked_result_type,
|
||||||
},
|
},
|
||||||
intrinsics::{
|
intrinsics::{
|
||||||
LLVM_ADD_SATURATED, LLVM_ADD_WITH_OVERFLOW, LLVM_CEILING, LLVM_COS, LLVM_FABS, LLVM_FLOOR,
|
// These instrinsics do not generate calls to libc and are safe to keep.
|
||||||
LLVM_LOG, LLVM_MUL_WITH_OVERFLOW, LLVM_POW, LLVM_ROUND, LLVM_SIN, LLVM_SQRT,
|
// If we find that any of them generate calls to libc on some platforms, we need to define them as zig bitcode.
|
||||||
LLVM_SUB_SATURATED, LLVM_SUB_WITH_OVERFLOW,
|
LLVM_ADD_SATURATED,
|
||||||
|
LLVM_ADD_WITH_OVERFLOW,
|
||||||
|
LLVM_MUL_WITH_OVERFLOW,
|
||||||
|
LLVM_SUB_SATURATED,
|
||||||
|
LLVM_SUB_WITH_OVERFLOW,
|
||||||
},
|
},
|
||||||
refcounting::PointerToRefcount,
|
refcounting::PointerToRefcount,
|
||||||
};
|
};
|
||||||
@ -1704,7 +1708,11 @@ fn build_float_binop<'ctx>(
|
|||||||
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
|
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),
|
||||||
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
NumLte => bd.build_float_compare(OLE, lhs, rhs, "float_lte").into(),
|
||||||
NumDivFrac => bd.build_float_div(lhs, rhs, "div_float").into(),
|
NumDivFrac => bd.build_float_div(lhs, rhs, "div_float").into(),
|
||||||
NumPow => env.call_intrinsic(&LLVM_POW[float_width], &[lhs.into(), rhs.into()]),
|
NumPow => call_bitcode_fn(
|
||||||
|
env,
|
||||||
|
&[lhs.into(), rhs.into()],
|
||||||
|
&bitcode::NUM_POW[float_width],
|
||||||
|
),
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Unrecognized int binary operation: {:?}", op);
|
unreachable!("Unrecognized int binary operation: {:?}", op);
|
||||||
}
|
}
|
||||||
@ -2316,9 +2324,9 @@ fn build_float_unary_op<'a, 'ctx>(
|
|||||||
// TODO: Handle different sized floats
|
// TODO: Handle different sized floats
|
||||||
match op {
|
match op {
|
||||||
NumNeg => bd.build_float_neg(arg, "negate_float").into(),
|
NumNeg => bd.build_float_neg(arg, "negate_float").into(),
|
||||||
NumAbs => env.call_intrinsic(&LLVM_FABS[float_width], &[arg.into()]),
|
NumAbs => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_FABS[float_width]),
|
||||||
NumSqrtUnchecked => env.call_intrinsic(&LLVM_SQRT[float_width], &[arg.into()]),
|
NumSqrtUnchecked => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_SQRT[float_width]),
|
||||||
NumLogUnchecked => env.call_intrinsic(&LLVM_LOG[float_width], &[arg.into()]),
|
NumLogUnchecked => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_LOG[float_width]),
|
||||||
NumToFrac => {
|
NumToFrac => {
|
||||||
let return_width = match layout_interner.get(layout).repr {
|
let return_width = match layout_interner.get(layout).repr {
|
||||||
LayoutRepr::Builtin(Builtin::Float(return_width)) => return_width,
|
LayoutRepr::Builtin(Builtin::Float(return_width)) => return_width,
|
||||||
@ -2342,64 +2350,46 @@ fn build_float_unary_op<'a, 'ctx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumCeiling => {
|
NumCeiling => {
|
||||||
let (return_signed, return_type) = match layout_interner.get(layout).repr {
|
let int_width = match layout_interner.get(layout).repr {
|
||||||
LayoutRepr::Builtin(Builtin::Int(int_width)) => (
|
LayoutRepr::Builtin(Builtin::Int(int_width)) => int_width,
|
||||||
int_width.is_signed(),
|
|
||||||
convert::int_type_from_int_width(env, int_width),
|
|
||||||
),
|
|
||||||
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
|
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
|
||||||
};
|
};
|
||||||
let opcode = if return_signed {
|
match float_width {
|
||||||
InstructionOpcode::FPToSI
|
FloatWidth::F32 => {
|
||||||
} else {
|
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_CEILING_F32[int_width])
|
||||||
InstructionOpcode::FPToUI
|
}
|
||||||
};
|
FloatWidth::F64 => {
|
||||||
env.builder.build_cast(
|
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_CEILING_F64[int_width])
|
||||||
opcode,
|
}
|
||||||
env.call_intrinsic(&LLVM_CEILING[float_width], &[arg.into()]),
|
}
|
||||||
return_type,
|
|
||||||
"num_ceiling",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
NumFloor => {
|
NumFloor => {
|
||||||
let (return_signed, return_type) = match layout_interner.get(layout).repr {
|
let int_width = match layout_interner.get(layout).repr {
|
||||||
LayoutRepr::Builtin(Builtin::Int(int_width)) => (
|
LayoutRepr::Builtin(Builtin::Int(int_width)) => int_width,
|
||||||
int_width.is_signed(),
|
_ => internal_error!("Floor return layout is not int: {:?}", layout),
|
||||||
convert::int_type_from_int_width(env, int_width),
|
|
||||||
),
|
|
||||||
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
|
|
||||||
};
|
};
|
||||||
let opcode = if return_signed {
|
match float_width {
|
||||||
InstructionOpcode::FPToSI
|
FloatWidth::F32 => {
|
||||||
} else {
|
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_FLOOR_F32[int_width])
|
||||||
InstructionOpcode::FPToUI
|
}
|
||||||
};
|
FloatWidth::F64 => {
|
||||||
env.builder.build_cast(
|
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_FLOOR_F64[int_width])
|
||||||
opcode,
|
}
|
||||||
env.call_intrinsic(&LLVM_FLOOR[float_width], &[arg.into()]),
|
}
|
||||||
return_type,
|
|
||||||
"num_floor",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
NumRound => {
|
NumRound => {
|
||||||
let (return_signed, return_type) = match layout_interner.get(layout).repr {
|
let int_width = match layout_interner.get(layout).repr {
|
||||||
LayoutRepr::Builtin(Builtin::Int(int_width)) => (
|
LayoutRepr::Builtin(Builtin::Int(int_width)) => int_width,
|
||||||
int_width.is_signed(),
|
_ => internal_error!("Round return layout is not int: {:?}", layout),
|
||||||
convert::int_type_from_int_width(env, int_width),
|
|
||||||
),
|
|
||||||
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
|
|
||||||
};
|
};
|
||||||
let opcode = if return_signed {
|
match float_width {
|
||||||
InstructionOpcode::FPToSI
|
FloatWidth::F32 => {
|
||||||
} else {
|
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ROUND_F32[int_width])
|
||||||
InstructionOpcode::FPToUI
|
}
|
||||||
};
|
FloatWidth::F64 => {
|
||||||
env.builder.build_cast(
|
call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ROUND_F64[int_width])
|
||||||
opcode,
|
}
|
||||||
env.call_intrinsic(&LLVM_ROUND[float_width], &[arg.into()]),
|
}
|
||||||
return_type,
|
|
||||||
"num_round",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
NumIsNan => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_NAN[float_width]),
|
NumIsNan => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_NAN[float_width]),
|
||||||
NumIsInfinite => {
|
NumIsInfinite => {
|
||||||
@ -2408,8 +2398,8 @@ fn build_float_unary_op<'a, 'ctx>(
|
|||||||
NumIsFinite => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_FINITE[float_width]),
|
NumIsFinite => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_FINITE[float_width]),
|
||||||
|
|
||||||
// trigonometry
|
// trigonometry
|
||||||
NumSin => env.call_intrinsic(&LLVM_SIN[float_width], &[arg.into()]),
|
NumSin => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_SIN[float_width]),
|
||||||
NumCos => env.call_intrinsic(&LLVM_COS[float_width], &[arg.into()]),
|
NumCos => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_COS[float_width]),
|
||||||
|
|
||||||
NumAtan => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ATAN[float_width]),
|
NumAtan => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ATAN[float_width]),
|
||||||
NumAcos => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ACOS[float_width]),
|
NumAcos => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ACOS[float_width]),
|
||||||
|
@ -32,7 +32,7 @@ use roc_module::symbol::{
|
|||||||
};
|
};
|
||||||
use roc_mono::ir::{
|
use roc_mono::ir::{
|
||||||
CapturedSymbols, ExternalSpecializations, GlueLayouts, LambdaSetId, PartialProc, Proc,
|
CapturedSymbols, ExternalSpecializations, GlueLayouts, LambdaSetId, PartialProc, Proc,
|
||||||
ProcLayout, Procs, ProcsBase, UpdateModeIds,
|
ProcLayout, Procs, ProcsBase, UpdateModeIds, UsageTrackingMap,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::LayoutInterner;
|
use roc_mono::layout::LayoutInterner;
|
||||||
use roc_mono::layout::{
|
use roc_mono::layout::{
|
||||||
@ -3115,7 +3115,6 @@ fn update<'a>(
|
|||||||
&mut layout_interner,
|
&mut layout_interner,
|
||||||
module_id,
|
module_id,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
state.target_info,
|
|
||||||
&mut state.procedures,
|
&mut state.procedures,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -5783,6 +5782,7 @@ fn make_specializations<'a>(
|
|||||||
abilities: AbilitiesView::World(&world_abilities),
|
abilities: AbilitiesView::World(&world_abilities),
|
||||||
exposed_by_module,
|
exposed_by_module,
|
||||||
derived_module: &derived_module,
|
derived_module: &derived_module,
|
||||||
|
struct_indexing: UsageTrackingMap::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut procs = Procs::new_in(arena);
|
let mut procs = Procs::new_in(arena);
|
||||||
@ -5883,6 +5883,7 @@ fn build_pending_specializations<'a>(
|
|||||||
abilities: AbilitiesView::Module(&abilities_store),
|
abilities: AbilitiesView::Module(&abilities_store),
|
||||||
exposed_by_module,
|
exposed_by_module,
|
||||||
derived_module: &derived_module,
|
derived_module: &derived_module,
|
||||||
|
struct_indexing: UsageTrackingMap::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let layout_cache_snapshot = layout_cache.snapshot();
|
let layout_cache_snapshot = layout_cache.snapshot();
|
||||||
@ -6364,6 +6365,7 @@ fn load_derived_partial_procs<'a>(
|
|||||||
abilities: AbilitiesView::World(world_abilities),
|
abilities: AbilitiesView::World(world_abilities),
|
||||||
exposed_by_module,
|
exposed_by_module,
|
||||||
derived_module,
|
derived_module,
|
||||||
|
struct_indexing: UsageTrackingMap::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let partial_proc = match derived_expr {
|
let partial_proc = match derived_expr {
|
||||||
|
@ -15,11 +15,10 @@ use bumpalo::collections::CollectIn;
|
|||||||
|
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
use roc_target::TargetInfo;
|
|
||||||
|
|
||||||
use crate::ir::{
|
use crate::ir::{
|
||||||
BranchInfo, Call, CallType, Expr, JoinPointId, Literal, ModifyRc, Proc, ProcLayout, Stmt,
|
BranchInfo, Call, CallType, Expr, JoinPointId, ListLiteralElement, Literal, ModifyRc, Proc,
|
||||||
UpdateModeId,
|
ProcLayout, Stmt, UpdateModeId,
|
||||||
};
|
};
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
Builtin, InLayout, Layout, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout,
|
||||||
@ -27,7 +26,7 @@ use crate::layout::{
|
|||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
|
||||||
use roc_collections::{MutMap, MutSet};
|
use roc_collections::MutMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Try to find increments of symbols followed by decrements of the symbol they were indexed out of (their parent).
|
Try to find increments of symbols followed by decrements of the symbol they were indexed out of (their parent).
|
||||||
@ -38,12 +37,10 @@ pub fn specialize_drops<'a, 'i>(
|
|||||||
layout_interner: &'i mut STLayoutInterner<'a>,
|
layout_interner: &'i mut STLayoutInterner<'a>,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
ident_ids: &'i mut IdentIds,
|
ident_ids: &'i mut IdentIds,
|
||||||
target_info: TargetInfo,
|
|
||||||
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
procs: &mut MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
) {
|
) {
|
||||||
for ((_symbol, proc_layout), proc) in procs.iter_mut() {
|
for ((_symbol, proc_layout), proc) in procs.iter_mut() {
|
||||||
let mut environment =
|
let mut environment = DropSpecializationEnvironment::new(arena, home, proc_layout.result);
|
||||||
DropSpecializationEnvironment::new(arena, home, proc_layout.result, target_info);
|
|
||||||
specialize_drops_proc(arena, layout_interner, ident_ids, &mut environment, proc);
|
specialize_drops_proc(arena, layout_interner, ident_ids, &mut environment, proc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +101,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
_ => unreachable!("List get should have two arguments"),
|
_ => unreachable!("List get should have two arguments"),
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.add_list_child(*structure, *binding, index);
|
environment.add_list_child_symbol(*structure, *binding, index);
|
||||||
|
|
||||||
alloc_let_with_continuation!(environment)
|
alloc_let_with_continuation!(environment)
|
||||||
}
|
}
|
||||||
@ -114,9 +111,18 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
RC::NoRc => alloc_let_with_continuation!(environment),
|
RC::NoRc => alloc_let_with_continuation!(environment),
|
||||||
// We probably should not pass the increments to the continuation.
|
// We probably should not pass the increments to the continuation.
|
||||||
RC::Rc | RC::Uknown => {
|
RC::Rc | RC::Uknown => {
|
||||||
let mut new_environment = environment.clone_without_incremented();
|
let incremented_symbols = environment.incremented_symbols.drain();
|
||||||
|
|
||||||
alloc_let_with_continuation!(&mut new_environment)
|
let new_stmt = alloc_let_with_continuation!(environment);
|
||||||
|
|
||||||
|
// The new_environment might have inserted increments that were set to 0 before. We need to add th
|
||||||
|
for (symbol, increment) in incremented_symbols.map.into_iter() {
|
||||||
|
environment
|
||||||
|
.incremented_symbols
|
||||||
|
.insert_count(symbol, increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_stmt
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
@ -127,16 +133,62 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
// the parent might be deallocated before the function can use it.
|
// the parent might be deallocated before the function can use it.
|
||||||
// Thus forget everything about any increments.
|
// Thus forget everything about any increments.
|
||||||
|
|
||||||
let mut new_environment = environment.clone_without_incremented();
|
let incremented_symbols = environment.incremented_symbols.drain();
|
||||||
|
|
||||||
alloc_let_with_continuation!(&mut new_environment)
|
let new_stmt = alloc_let_with_continuation!(environment);
|
||||||
|
|
||||||
|
// The new_environment might have inserted increments that were set to 0 before. We need to add th
|
||||||
|
for (symbol, increment) in incremented_symbols.map.into_iter() {
|
||||||
|
environment
|
||||||
|
.incremented_symbols
|
||||||
|
.insert_count(symbol, increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_stmt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Tag { tag_id, .. } => {
|
Expr::Tag {
|
||||||
|
tag_id,
|
||||||
|
arguments: children,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
environment.symbol_tag.insert(*binding, *tag_id);
|
environment.symbol_tag.insert(*binding, *tag_id);
|
||||||
|
|
||||||
|
for (index, child) in children.iter().enumerate() {
|
||||||
|
environment.add_union_child(*binding, *child, *tag_id, index as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_let_with_continuation!(environment)
|
||||||
|
}
|
||||||
|
Expr::Struct(children) => {
|
||||||
|
for (index, child) in children.iter().enumerate() {
|
||||||
|
environment.add_struct_child(*binding, *child, index as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_let_with_continuation!(environment)
|
||||||
|
}
|
||||||
|
Expr::ExprBox { symbol: child } => {
|
||||||
|
environment.add_box_child(*binding, *child);
|
||||||
|
|
||||||
|
alloc_let_with_continuation!(environment)
|
||||||
|
}
|
||||||
|
Expr::Array {
|
||||||
|
elems: children, ..
|
||||||
|
} => {
|
||||||
|
for (index, child) in
|
||||||
|
children
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, child)| match child {
|
||||||
|
ListLiteralElement::Literal(_) => None,
|
||||||
|
ListLiteralElement::Symbol(s) => Some((index, s)),
|
||||||
|
})
|
||||||
|
{
|
||||||
|
environment.add_list_child(*binding, *child, index as u64);
|
||||||
|
}
|
||||||
|
|
||||||
alloc_let_with_continuation!(environment)
|
alloc_let_with_continuation!(environment)
|
||||||
}
|
}
|
||||||
Expr::StructAtIndex {
|
Expr::StructAtIndex {
|
||||||
@ -188,13 +240,10 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
}
|
}
|
||||||
alloc_let_with_continuation!(environment)
|
alloc_let_with_continuation!(environment)
|
||||||
}
|
}
|
||||||
Expr::Struct(_)
|
Expr::RuntimeErrorFunction(_)
|
||||||
| Expr::RuntimeErrorFunction(_)
|
|
||||||
| Expr::ExprBox { .. }
|
|
||||||
| Expr::NullPointer
|
| Expr::NullPointer
|
||||||
| Expr::GetTagId { .. }
|
| Expr::GetTagId { .. }
|
||||||
| Expr::EmptyArray
|
| Expr::EmptyArray => {
|
||||||
| Expr::Array { .. } => {
|
|
||||||
// Does nothing relevant to drop specialization. So we can just continue.
|
// Does nothing relevant to drop specialization. So we can just continue.
|
||||||
alloc_let_with_continuation!(environment)
|
alloc_let_with_continuation!(environment)
|
||||||
}
|
}
|
||||||
@ -283,10 +332,12 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Find the lowest symbol count for each symbol in each branch, and update the environment to match.
|
// Find the lowest symbol count for each symbol in each branch, and update the environment to match.
|
||||||
for (symbol, count) in environment.incremented_symbols.iter_mut() {
|
for (symbol, count) in environment.incremented_symbols.map.iter_mut() {
|
||||||
let consumed = branch_envs
|
let consumed = branch_envs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|branch_env| branch_env.incremented_symbols.get(symbol).unwrap_or(&0))
|
.map(|branch_env| {
|
||||||
|
branch_env.incremented_symbols.map.get(symbol).unwrap_or(&0)
|
||||||
|
})
|
||||||
.min()
|
.min()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -300,10 +351,14 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
let symbol_differences =
|
let symbol_differences =
|
||||||
environment
|
environment
|
||||||
.incremented_symbols
|
.incremented_symbols
|
||||||
|
.map
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(symbol, count)| {
|
.filter_map(|(symbol, count)| {
|
||||||
let branch_count =
|
let branch_count = $branch_env
|
||||||
$branch_env.incremented_symbols.get(symbol).unwrap_or(&0);
|
.incremented_symbols
|
||||||
|
.map
|
||||||
|
.get(symbol)
|
||||||
|
.unwrap_or(&0);
|
||||||
|
|
||||||
match branch_count - count {
|
match branch_count - count {
|
||||||
0 => None,
|
0 => None,
|
||||||
@ -338,11 +393,6 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
(info.clone(), new_branch)
|
(info.clone(), new_branch)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove all 0 counts as cleanup.
|
|
||||||
environment
|
|
||||||
.incremented_symbols
|
|
||||||
.retain(|_, count| *count > 0);
|
|
||||||
|
|
||||||
arena.alloc(Stmt::Switch {
|
arena.alloc(Stmt::Switch {
|
||||||
cond_symbol: *cond_symbol,
|
cond_symbol: *cond_symbol,
|
||||||
cond_layout: *cond_layout,
|
cond_layout: *cond_layout,
|
||||||
@ -354,10 +404,12 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
Stmt::Ret(symbol) => arena.alloc(Stmt::Ret(*symbol)),
|
Stmt::Ret(symbol) => arena.alloc(Stmt::Ret(*symbol)),
|
||||||
Stmt::Refcounting(rc, continuation) => match rc {
|
Stmt::Refcounting(rc, continuation) => match rc {
|
||||||
ModifyRc::Inc(symbol, count) => {
|
ModifyRc::Inc(symbol, count) => {
|
||||||
let any = environment.any_incremented(symbol);
|
let inc_before = environment.incremented_symbols.contains(symbol);
|
||||||
|
|
||||||
// Add a symbol for every increment performed.
|
// Add a symbol for every increment performed.
|
||||||
environment.add_incremented(*symbol, *count);
|
environment
|
||||||
|
.incremented_symbols
|
||||||
|
.insert_count(*symbol, *count);
|
||||||
|
|
||||||
let new_continuation = specialize_drops_stmt(
|
let new_continuation = specialize_drops_stmt(
|
||||||
arena,
|
arena,
|
||||||
@ -367,12 +419,17 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
continuation,
|
continuation,
|
||||||
);
|
);
|
||||||
|
|
||||||
if any {
|
if inc_before {
|
||||||
// There were increments before this one, best to let the first one do the increments.
|
// There were increments before this one, best to let the first one do the increments.
|
||||||
// Or there are no increments left, so we can just continue.
|
// Or there are no increments left, so we can just continue.
|
||||||
new_continuation
|
new_continuation
|
||||||
} else {
|
} else {
|
||||||
match environment.get_incremented(symbol) {
|
match environment
|
||||||
|
.incremented_symbols
|
||||||
|
.map
|
||||||
|
.remove(symbol)
|
||||||
|
.unwrap_or(0)
|
||||||
|
{
|
||||||
// This is the first increment, but all increments are consumed. So don't insert any.
|
// This is the first increment, but all increments are consumed. So don't insert any.
|
||||||
0 => new_continuation,
|
0 => new_continuation,
|
||||||
// We still need to do some increments.
|
// We still need to do some increments.
|
||||||
@ -393,7 +450,7 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
// dec a
|
// dec a
|
||||||
// dec b
|
// dec b
|
||||||
|
|
||||||
if environment.pop_incremented(symbol) {
|
if environment.incremented_symbols.pop(symbol) {
|
||||||
// This decremented symbol was incremented before, so we can remove it.
|
// This decremented symbol was incremented before, so we can remove it.
|
||||||
specialize_drops_stmt(
|
specialize_drops_stmt(
|
||||||
arena,
|
arena,
|
||||||
@ -411,10 +468,10 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
// As a might get dropped as a result of the decrement of b.
|
// As a might get dropped as a result of the decrement of b.
|
||||||
let mut incremented_children = {
|
let mut incremented_children = {
|
||||||
let mut todo_children = bumpalo::vec![in arena; *symbol];
|
let mut todo_children = bumpalo::vec![in arena; *symbol];
|
||||||
let mut incremented_children = MutSet::default();
|
let mut incremented_children = CountingMap::new();
|
||||||
|
|
||||||
while let Some(child) = todo_children.pop() {
|
while let Some(child) = todo_children.pop() {
|
||||||
if environment.pop_incremented(&child) {
|
if environment.incremented_symbols.pop(&child) {
|
||||||
incremented_children.insert(child);
|
incremented_children.insert(child);
|
||||||
} else {
|
} else {
|
||||||
todo_children.extend(environment.get_children(&child));
|
todo_children.extend(environment.get_children(&child));
|
||||||
@ -485,8 +542,10 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add back the increments for the children to the environment.
|
// Add back the increments for the children to the environment.
|
||||||
for child_symbol in incremented_children.iter() {
|
for (child_symbol, symbol_count) in incremented_children.map.into_iter() {
|
||||||
environment.add_incremented(*child_symbol, 1)
|
environment
|
||||||
|
.incremented_symbols
|
||||||
|
.insert_count(child_symbol, symbol_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
updated_stmt
|
updated_stmt
|
||||||
@ -565,7 +624,8 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||||||
body,
|
body,
|
||||||
remainder,
|
remainder,
|
||||||
} => {
|
} => {
|
||||||
let mut new_environment = environment.clone_without_incremented();
|
let mut new_environment = environment.clone();
|
||||||
|
new_environment.incremented_symbols.clear();
|
||||||
|
|
||||||
for param in parameters.iter() {
|
for param in parameters.iter() {
|
||||||
new_environment.add_symbol_layout(param.symbol, param.layout);
|
new_environment.add_symbol_layout(param.symbol, param.layout);
|
||||||
@ -604,7 +664,7 @@ fn specialize_struct<'a, 'i>(
|
|||||||
environment: &mut DropSpecializationEnvironment<'a>,
|
environment: &mut DropSpecializationEnvironment<'a>,
|
||||||
symbol: &Symbol,
|
symbol: &Symbol,
|
||||||
struct_layout: &'a [InLayout],
|
struct_layout: &'a [InLayout],
|
||||||
incremented_children: &mut MutSet<Child>,
|
incremented_children: &mut CountingMap<Child>,
|
||||||
continuation: &'a Stmt<'a>,
|
continuation: &'a Stmt<'a>,
|
||||||
) -> &'a Stmt<'a> {
|
) -> &'a Stmt<'a> {
|
||||||
match environment.struct_children.get(symbol) {
|
match environment.struct_children.get(symbol) {
|
||||||
@ -620,7 +680,7 @@ fn specialize_struct<'a, 'i>(
|
|||||||
|
|
||||||
for (index, _layout) in struct_layout.iter().enumerate() {
|
for (index, _layout) in struct_layout.iter().enumerate() {
|
||||||
for (child, _i) in children_clone.iter().filter(|(_, i)| *i == index as u64) {
|
for (child, _i) in children_clone.iter().filter(|(_, i)| *i == index as u64) {
|
||||||
let removed = incremented_children.remove(child);
|
let removed = incremented_children.pop(child);
|
||||||
index_symbols.insert(index, (*child, removed));
|
index_symbols.insert(index, (*child, removed));
|
||||||
|
|
||||||
if removed {
|
if removed {
|
||||||
@ -693,7 +753,7 @@ fn specialize_union<'a, 'i>(
|
|||||||
environment: &mut DropSpecializationEnvironment<'a>,
|
environment: &mut DropSpecializationEnvironment<'a>,
|
||||||
symbol: &Symbol,
|
symbol: &Symbol,
|
||||||
union_layout: UnionLayout<'a>,
|
union_layout: UnionLayout<'a>,
|
||||||
incremented_children: &mut MutSet<Child>,
|
incremented_children: &mut CountingMap<Child>,
|
||||||
continuation: &'a Stmt<'a>,
|
continuation: &'a Stmt<'a>,
|
||||||
) -> &'a Stmt<'a> {
|
) -> &'a Stmt<'a> {
|
||||||
let current_tag = environment.symbol_tag.get(symbol).copied();
|
let current_tag = environment.symbol_tag.get(symbol).copied();
|
||||||
@ -736,7 +796,7 @@ fn specialize_union<'a, 'i>(
|
|||||||
{
|
{
|
||||||
debug_assert_eq!(tag, *t);
|
debug_assert_eq!(tag, *t);
|
||||||
|
|
||||||
let removed = incremented_children.remove(child);
|
let removed = incremented_children.pop(child);
|
||||||
index_symbols.insert(index, (*child, removed));
|
index_symbols.insert(index, (*child, removed));
|
||||||
|
|
||||||
if removed {
|
if removed {
|
||||||
@ -898,14 +958,14 @@ fn specialize_boxed<'a, 'i>(
|
|||||||
layout_interner: &'i mut STLayoutInterner<'a>,
|
layout_interner: &'i mut STLayoutInterner<'a>,
|
||||||
ident_ids: &'i mut IdentIds,
|
ident_ids: &'i mut IdentIds,
|
||||||
environment: &mut DropSpecializationEnvironment<'a>,
|
environment: &mut DropSpecializationEnvironment<'a>,
|
||||||
incremented_children: &mut MutSet<Child>,
|
incremented_children: &mut CountingMap<Child>,
|
||||||
symbol: &Symbol,
|
symbol: &Symbol,
|
||||||
continuation: &'a Stmt<'a>,
|
continuation: &'a Stmt<'a>,
|
||||||
) -> &'a Stmt<'a> {
|
) -> &'a Stmt<'a> {
|
||||||
let removed = match incremented_children.iter().next() {
|
let removed = match incremented_children.map.iter().next() {
|
||||||
Some(s) => {
|
Some((s, _)) => {
|
||||||
let s = *s;
|
let s = *s;
|
||||||
incremented_children.remove(&s);
|
incremented_children.pop(&s);
|
||||||
Some(s)
|
Some(s)
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
@ -924,23 +984,20 @@ fn specialize_boxed<'a, 'i>(
|
|||||||
*symbol,
|
*symbol,
|
||||||
// If the symbol is unique:
|
// If the symbol is unique:
|
||||||
// - free the box
|
// - free the box
|
||||||
|_, _, _| {
|
|_, _, continuation| {
|
||||||
arena.alloc(Stmt::Refcounting(
|
arena.alloc(Stmt::Refcounting(
|
||||||
// TODO can be replaced by free if ever added to the IR.
|
// TODO can be replaced by free if ever added to the IR.
|
||||||
ModifyRc::DecRef(*symbol),
|
ModifyRc::DecRef(*symbol),
|
||||||
new_continuation,
|
continuation,
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
// If the symbol is not unique:
|
// If the symbol is not unique:
|
||||||
// - increment the child
|
// - increment the child
|
||||||
// - decref the box
|
// - decref the box
|
||||||
|_, _, _| {
|
|_, _, continuation| {
|
||||||
arena.alloc(Stmt::Refcounting(
|
arena.alloc(Stmt::Refcounting(
|
||||||
ModifyRc::Inc(s, 1),
|
ModifyRc::Inc(s, 1),
|
||||||
arena.alloc(Stmt::Refcounting(
|
arena.alloc(Stmt::Refcounting(ModifyRc::DecRef(*symbol), continuation)),
|
||||||
ModifyRc::DecRef(*symbol),
|
|
||||||
new_continuation,
|
|
||||||
)),
|
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
new_continuation,
|
new_continuation,
|
||||||
@ -958,7 +1015,7 @@ fn specialize_list<'a, 'i>(
|
|||||||
layout_interner: &'i mut STLayoutInterner<'a>,
|
layout_interner: &'i mut STLayoutInterner<'a>,
|
||||||
ident_ids: &'i mut IdentIds,
|
ident_ids: &'i mut IdentIds,
|
||||||
environment: &mut DropSpecializationEnvironment<'a>,
|
environment: &mut DropSpecializationEnvironment<'a>,
|
||||||
incremented_children: &mut MutSet<Child>,
|
incremented_children: &mut CountingMap<Child>,
|
||||||
symbol: &Symbol,
|
symbol: &Symbol,
|
||||||
item_layout: InLayout,
|
item_layout: InLayout,
|
||||||
continuation: &'a Stmt<'a>,
|
continuation: &'a Stmt<'a>,
|
||||||
@ -993,7 +1050,7 @@ fn specialize_list<'a, 'i>(
|
|||||||
for (child, i) in children_clone.iter().filter(|(_child, i)| *i == index) {
|
for (child, i) in children_clone.iter().filter(|(_child, i)| *i == index) {
|
||||||
debug_assert!(length > *i);
|
debug_assert!(length > *i);
|
||||||
|
|
||||||
let removed = incremented_children.remove(child);
|
let removed = incremented_children.pop(child);
|
||||||
index_symbols.insert(index, (*child, removed));
|
index_symbols.insert(index, (*child, removed));
|
||||||
|
|
||||||
if removed {
|
if removed {
|
||||||
@ -1214,7 +1271,6 @@ struct DropSpecializationEnvironment<'a> {
|
|||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
layout: InLayout<'a>,
|
layout: InLayout<'a>,
|
||||||
target_info: TargetInfo,
|
|
||||||
|
|
||||||
symbol_layouts: MutMap<Symbol, InLayout<'a>>,
|
symbol_layouts: MutMap<Symbol, InLayout<'a>>,
|
||||||
|
|
||||||
@ -1231,7 +1287,7 @@ struct DropSpecializationEnvironment<'a> {
|
|||||||
list_children: MutMap<Parent, Vec<'a, (Child, Index)>>,
|
list_children: MutMap<Parent, Vec<'a, (Child, Index)>>,
|
||||||
|
|
||||||
// Keeps track of all incremented symbols.
|
// Keeps track of all incremented symbols.
|
||||||
incremented_symbols: MutMap<Symbol, u64>,
|
incremented_symbols: CountingMap<Symbol>,
|
||||||
|
|
||||||
// Map containing the current known tag of a layout.
|
// Map containing the current known tag of a layout.
|
||||||
symbol_tag: MutMap<Symbol, Tag>,
|
symbol_tag: MutMap<Symbol, Tag>,
|
||||||
@ -1244,42 +1300,23 @@ struct DropSpecializationEnvironment<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DropSpecializationEnvironment<'a> {
|
impl<'a> DropSpecializationEnvironment<'a> {
|
||||||
fn new(arena: &'a Bump, home: ModuleId, layout: InLayout<'a>, target_info: TargetInfo) -> Self {
|
fn new(arena: &'a Bump, home: ModuleId, layout: InLayout<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
arena,
|
arena,
|
||||||
home,
|
home,
|
||||||
layout,
|
layout,
|
||||||
target_info,
|
|
||||||
symbol_layouts: MutMap::default(),
|
symbol_layouts: MutMap::default(),
|
||||||
struct_children: MutMap::default(),
|
struct_children: MutMap::default(),
|
||||||
union_children: MutMap::default(),
|
union_children: MutMap::default(),
|
||||||
box_children: MutMap::default(),
|
box_children: MutMap::default(),
|
||||||
list_children: MutMap::default(),
|
list_children: MutMap::default(),
|
||||||
incremented_symbols: MutMap::default(),
|
incremented_symbols: CountingMap::new(),
|
||||||
symbol_tag: MutMap::default(),
|
symbol_tag: MutMap::default(),
|
||||||
symbol_index: MutMap::default(),
|
symbol_index: MutMap::default(),
|
||||||
list_length: MutMap::default(),
|
list_length: MutMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_without_incremented(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
arena: self.arena,
|
|
||||||
home: self.home,
|
|
||||||
layout: self.layout,
|
|
||||||
target_info: self.target_info,
|
|
||||||
symbol_layouts: self.symbol_layouts.clone(),
|
|
||||||
struct_children: self.struct_children.clone(),
|
|
||||||
union_children: self.union_children.clone(),
|
|
||||||
box_children: self.box_children.clone(),
|
|
||||||
list_children: self.list_children.clone(),
|
|
||||||
incremented_symbols: MutMap::default(),
|
|
||||||
symbol_tag: self.symbol_tag.clone(),
|
|
||||||
symbol_index: self.symbol_index.clone(),
|
|
||||||
list_length: self.list_length.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_symbol<'i>(&self, ident_ids: &'i mut IdentIds, debug_name: &str) -> Symbol {
|
fn create_symbol<'i>(&self, ident_ids: &'i mut IdentIds, debug_name: &str) -> Symbol {
|
||||||
let ident_id = ident_ids.add_str(debug_name);
|
let ident_id = ident_ids.add_str(debug_name);
|
||||||
Symbol::new(self.home, ident_id)
|
Symbol::new(self.home, ident_id)
|
||||||
@ -1316,12 +1353,16 @@ impl<'a> DropSpecializationEnvironment<'a> {
|
|||||||
.push(child);
|
.push(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_list_child(&mut self, parent: Parent, child: Child, index: &Symbol) {
|
fn add_list_child(&mut self, parent: Parent, child: Child, index: u64) {
|
||||||
if let Some(index) = self.symbol_index.get(index) {
|
|
||||||
self.list_children
|
self.list_children
|
||||||
.entry(parent)
|
.entry(parent)
|
||||||
.or_insert_with(|| Vec::new_in(self.arena))
|
.or_insert_with(|| Vec::new_in(self.arena))
|
||||||
.push((child, *index));
|
.push((child, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_list_child_symbol(&mut self, parent: Parent, child: Child, index: &Symbol) {
|
||||||
|
if let Some(index) = self.symbol_index.get(index) {
|
||||||
|
self.add_list_child(parent, child, *index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1346,39 +1387,6 @@ impl<'a> DropSpecializationEnvironment<'a> {
|
|||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Add a symbol for every increment performed.
|
|
||||||
*/
|
|
||||||
fn add_incremented(&mut self, symbol: Symbol, count: u64) {
|
|
||||||
self.incremented_symbols
|
|
||||||
.entry(symbol)
|
|
||||||
.and_modify(|c| *c += count)
|
|
||||||
.or_insert(count);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn any_incremented(&self, symbol: &Symbol) -> bool {
|
|
||||||
self.incremented_symbols.contains_key(symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Return the amount of times a symbol still has to be incremented.
|
|
||||||
Accounting for later consumtion and removal of the increment.
|
|
||||||
*/
|
|
||||||
fn get_incremented(&mut self, symbol: &Symbol) -> u64 {
|
|
||||||
self.incremented_symbols.remove(symbol).unwrap_or(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_incremented(&mut self, symbol: &Symbol) -> bool {
|
|
||||||
match self.incremented_symbols.get_mut(symbol) {
|
|
||||||
Some(0) => false,
|
|
||||||
Some(c) => {
|
|
||||||
*c -= 1;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
None => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1490,3 +1498,59 @@ fn low_level_no_rc(lowlevel: &LowLevel) -> RC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Map that contains a count for each key.
|
||||||
|
/// Keys with a count of 0 are kept around, so that it can be seen that they were once present.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct CountingMap<K> {
|
||||||
|
map: MutMap<K, u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K> CountingMap<K>
|
||||||
|
where
|
||||||
|
K: Eq + std::hash::Hash + Clone,
|
||||||
|
{
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
map: MutMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, key: K) {
|
||||||
|
self.insert_count(key, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_count(&mut self, key: K, count: u64) {
|
||||||
|
self.map
|
||||||
|
.entry(key)
|
||||||
|
.and_modify(|c| *c += count)
|
||||||
|
.or_insert(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop(&mut self, key: &K) -> bool {
|
||||||
|
match self.map.get_mut(key) {
|
||||||
|
Some(0) => false,
|
||||||
|
Some(c) => {
|
||||||
|
*c -= 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, symbol: &K) -> bool {
|
||||||
|
self.map.contains_key(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain(&mut self) -> Self {
|
||||||
|
let res = self.clone();
|
||||||
|
for (_, v) in self.map.iter_mut() {
|
||||||
|
*v = 0;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&mut self) {
|
||||||
|
self.map.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1386,6 +1386,7 @@ pub struct Env<'a, 'i> {
|
|||||||
pub abilities: AbilitiesView<'i>,
|
pub abilities: AbilitiesView<'i>,
|
||||||
pub exposed_by_module: &'i ExposedByModule,
|
pub exposed_by_module: &'i ExposedByModule,
|
||||||
pub derived_module: &'i SharedDerivedModule,
|
pub derived_module: &'i SharedDerivedModule,
|
||||||
|
pub struct_indexing: UsageTrackingMap<(Symbol, u64), Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'i> Env<'a, 'i> {
|
impl<'a, 'i> Env<'a, 'i> {
|
||||||
@ -4225,6 +4226,7 @@ pub fn with_hole<'a>(
|
|||||||
// If this symbol is a raw value, find the real name we gave to its specialized usage.
|
// If this symbol is a raw value, find the real name we gave to its specialized usage.
|
||||||
if let ReuseSymbol::Value(_symbol) = can_reuse_symbol(
|
if let ReuseSymbol::Value(_symbol) = can_reuse_symbol(
|
||||||
env,
|
env,
|
||||||
|
layout_cache,
|
||||||
procs,
|
procs,
|
||||||
&roc_can::expr::Expr::Var(symbol, variable),
|
&roc_can::expr::Expr::Var(symbol, variable),
|
||||||
variable,
|
variable,
|
||||||
@ -4324,7 +4326,7 @@ pub fn with_hole<'a>(
|
|||||||
OpaqueRef { argument, .. } => {
|
OpaqueRef { argument, .. } => {
|
||||||
let (arg_var, loc_arg_expr) = *argument;
|
let (arg_var, loc_arg_expr) = *argument;
|
||||||
|
|
||||||
match can_reuse_symbol(env, procs, &loc_arg_expr.value, arg_var) {
|
match can_reuse_symbol(env, layout_cache, procs, &loc_arg_expr.value, arg_var) {
|
||||||
// Opaques decay to their argument.
|
// Opaques decay to their argument.
|
||||||
ReuseSymbol::Value(symbol) => {
|
ReuseSymbol::Value(symbol) => {
|
||||||
let real_name = procs.get_or_insert_symbol_specialization(
|
let real_name = procs.get_or_insert_symbol_specialization(
|
||||||
@ -4909,20 +4911,19 @@ pub fn with_hole<'a>(
|
|||||||
RecordUpdate {
|
RecordUpdate {
|
||||||
record_var,
|
record_var,
|
||||||
symbol: structure,
|
symbol: structure,
|
||||||
updates,
|
ref updates,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
use FieldType::*;
|
use FieldType::*;
|
||||||
|
|
||||||
enum FieldType<'a> {
|
enum FieldType<'a> {
|
||||||
CopyExisting(u64),
|
CopyExisting,
|
||||||
UpdateExisting(&'a roc_can::expr::Field),
|
UpdateExisting(&'a roc_can::expr::Field),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strategy: turn a record update into the creation of a new record.
|
// Strategy: turn a record update into the creation of a new record.
|
||||||
// This has the benefit that we don't need to do anything special for reference
|
// This has the benefit that we don't need to do anything special for reference
|
||||||
// counting
|
// counting
|
||||||
|
|
||||||
let sorted_fields_result = {
|
let sorted_fields_result = {
|
||||||
let mut layout_env = layout::Env::from_components(
|
let mut layout_env = layout::Env::from_components(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
@ -4938,43 +4939,56 @@ pub fn with_hole<'a>(
|
|||||||
Err(_) => return runtime_error(env, "Can't update record with improper layout"),
|
Err(_) => return runtime_error(env, "Can't update record with improper layout"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
let single_field_struct = sorted_fields.len() == 1;
|
||||||
|
|
||||||
let mut symbols = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
// The struct indexing generated by the current context
|
||||||
|
let mut current_struct_indexing = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||||
|
// The symbols that are used to create the new struct
|
||||||
|
let mut new_struct_symbols = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||||
|
// Information about the fields that are being updated
|
||||||
let mut fields = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
let mut fields = Vec::with_capacity_in(sorted_fields.len(), env.arena);
|
||||||
|
let mut index = 0;
|
||||||
|
for (label, _, opt_field_layout) in sorted_fields.iter() {
|
||||||
|
let record_index = (structure, index);
|
||||||
|
|
||||||
let mut current = 0;
|
|
||||||
for (label, _, opt_field_layout) in sorted_fields.into_iter() {
|
|
||||||
match opt_field_layout {
|
match opt_field_layout {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug_assert!(!updates.contains_key(&label));
|
debug_assert!(!updates.contains_key(label));
|
||||||
// this was an optional field, and now does not exist!
|
// this was an optional field, and now does not exist!
|
||||||
// do not increment `current`!
|
// do not increment `index`!
|
||||||
}
|
}
|
||||||
Ok(field_layout) => {
|
Ok(_field_layout) => {
|
||||||
field_layouts.push(field_layout);
|
current_struct_indexing.push(record_index);
|
||||||
|
|
||||||
if let Some(field) = updates.get(&label) {
|
// The struct with a single field is optimized in such a way that replacing later indexing will cause an incorrect IR.
|
||||||
let field_symbol = possible_reuse_symbol_or_specialize(
|
// Thus, only insert these struct_indices if there is more than one field in the struct.
|
||||||
|
if !single_field_struct {
|
||||||
|
let original_struct_symbol = env.unique_symbol();
|
||||||
|
env.struct_indexing
|
||||||
|
.insert(record_index, original_struct_symbol);
|
||||||
|
}
|
||||||
|
if let Some(field) = updates.get(label) {
|
||||||
|
let new_struct_symbol = possible_reuse_symbol_or_specialize(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
&field.loc_expr.value,
|
&field.loc_expr.value,
|
||||||
field.var,
|
field.var,
|
||||||
);
|
);
|
||||||
|
new_struct_symbols.push(new_struct_symbol);
|
||||||
fields.push(UpdateExisting(field));
|
fields.push(UpdateExisting(field));
|
||||||
symbols.push(field_symbol);
|
|
||||||
} else {
|
} else {
|
||||||
fields.push(CopyExisting(current));
|
new_struct_symbols
|
||||||
symbols.push(env.unique_symbol());
|
.push(*env.struct_indexing.get(record_index).unwrap());
|
||||||
|
fields.push(CopyExisting);
|
||||||
}
|
}
|
||||||
|
|
||||||
current += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let symbols = symbols.into_bump_slice();
|
|
||||||
|
let new_struct_symbols = new_struct_symbols.into_bump_slice();
|
||||||
|
|
||||||
let record_layout = layout_cache
|
let record_layout = layout_cache
|
||||||
.from_var(env.arena, record_var, env.subs)
|
.from_var(env.arena, record_var, env.subs)
|
||||||
@ -4985,8 +4999,8 @@ pub fn with_hole<'a>(
|
|||||||
_ => arena.alloc([record_layout]),
|
_ => arena.alloc([record_layout]),
|
||||||
};
|
};
|
||||||
|
|
||||||
if symbols.len() == 1 {
|
if single_field_struct {
|
||||||
// TODO we can probably special-case this more, skippiing the generation of
|
// TODO we can probably special-case this more, skipping the generation of
|
||||||
// UpdateExisting
|
// UpdateExisting
|
||||||
let mut stmt = hole.clone();
|
let mut stmt = hole.clone();
|
||||||
|
|
||||||
@ -4994,7 +5008,7 @@ pub fn with_hole<'a>(
|
|||||||
|
|
||||||
match what_to_do {
|
match what_to_do {
|
||||||
UpdateExisting(field) => {
|
UpdateExisting(field) => {
|
||||||
substitute_in_exprs(env.arena, &mut stmt, assigned, symbols[0]);
|
substitute_in_exprs(env.arena, &mut stmt, assigned, new_struct_symbols[0]);
|
||||||
|
|
||||||
stmt = assign_to_symbol(
|
stmt = assign_to_symbol(
|
||||||
env,
|
env,
|
||||||
@ -5002,11 +5016,11 @@ pub fn with_hole<'a>(
|
|||||||
layout_cache,
|
layout_cache,
|
||||||
field.var,
|
field.var,
|
||||||
*field.loc_expr.clone(),
|
*field.loc_expr.clone(),
|
||||||
symbols[0],
|
new_struct_symbols[0],
|
||||||
stmt,
|
stmt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
CopyExisting(_) => {
|
CopyExisting => {
|
||||||
unreachable!(
|
unreachable!(
|
||||||
r"when a record has just one field and is updated, it must update that one field"
|
r"when a record has just one field and is updated, it must update that one field"
|
||||||
);
|
);
|
||||||
@ -5015,12 +5029,10 @@ pub fn with_hole<'a>(
|
|||||||
|
|
||||||
stmt
|
stmt
|
||||||
} else {
|
} else {
|
||||||
let expr = Expr::Struct(symbols);
|
let expr = Expr::Struct(new_struct_symbols);
|
||||||
let mut stmt = Stmt::Let(assigned, expr, record_layout, hole);
|
let mut stmt = Stmt::Let(assigned, expr, record_layout, hole);
|
||||||
|
|
||||||
let it = field_layouts.iter().zip(symbols.iter()).zip(fields);
|
for (new_struct_symbol, what_to_do) in new_struct_symbols.iter().zip(fields) {
|
||||||
|
|
||||||
for ((field_layout, symbol), what_to_do) in it {
|
|
||||||
match what_to_do {
|
match what_to_do {
|
||||||
UpdateExisting(field) => {
|
UpdateExisting(field) => {
|
||||||
stmt = assign_to_symbol(
|
stmt = assign_to_symbol(
|
||||||
@ -5029,11 +5041,17 @@ pub fn with_hole<'a>(
|
|||||||
layout_cache,
|
layout_cache,
|
||||||
field.var,
|
field.var,
|
||||||
*field.loc_expr.clone(),
|
*field.loc_expr.clone(),
|
||||||
*symbol,
|
*new_struct_symbol,
|
||||||
stmt,
|
stmt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
CopyExisting(index) => {
|
CopyExisting => {
|
||||||
|
// When a field is copied, the indexing symbol is already placed in new_struct_symbols
|
||||||
|
// Thus, we don't need additional logic here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let structure_needs_specialization =
|
let structure_needs_specialization =
|
||||||
procs.ability_member_aliases.get(structure).is_some()
|
procs.ability_member_aliases.get(structure).is_some()
|
||||||
|| procs.is_module_thunk(structure)
|
|| procs.is_module_thunk(structure)
|
||||||
@ -5041,20 +5059,23 @@ pub fn with_hole<'a>(
|
|||||||
|
|
||||||
let specialized_structure_sym = if structure_needs_specialization {
|
let specialized_structure_sym = if structure_needs_specialization {
|
||||||
// We need to specialize the record now; create a new one for it.
|
// We need to specialize the record now; create a new one for it.
|
||||||
// TODO: reuse this symbol for all updates
|
|
||||||
env.unique_symbol()
|
env.unique_symbol()
|
||||||
} else {
|
} else {
|
||||||
// The record is already good.
|
// The record is already good.
|
||||||
structure
|
structure
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for record_index in current_struct_indexing.into_iter().rev() {
|
||||||
|
if let Some(symbol) = env.struct_indexing.get_used(&record_index) {
|
||||||
|
let layout = field_layouts[record_index.1 as usize];
|
||||||
let access_expr = Expr::StructAtIndex {
|
let access_expr = Expr::StructAtIndex {
|
||||||
structure: specialized_structure_sym,
|
structure: specialized_structure_sym,
|
||||||
index,
|
index: record_index.1,
|
||||||
field_layouts,
|
field_layouts,
|
||||||
};
|
};
|
||||||
stmt =
|
stmt = Stmt::Let(symbol, access_expr, layout, arena.alloc(stmt));
|
||||||
Stmt::Let(*symbol, access_expr, *field_layout, arena.alloc(stmt));
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if structure_needs_specialization {
|
if structure_needs_specialization {
|
||||||
stmt = specialize_symbol(
|
stmt = specialize_symbol(
|
||||||
@ -5067,9 +5088,7 @@ pub fn with_hole<'a>(
|
|||||||
structure,
|
structure,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5227,7 +5246,7 @@ pub fn with_hole<'a>(
|
|||||||
// re-use that symbol, and don't define its value again
|
// re-use that symbol, and don't define its value again
|
||||||
let mut result;
|
let mut result;
|
||||||
use ReuseSymbol::*;
|
use ReuseSymbol::*;
|
||||||
match can_reuse_symbol(env, procs, &loc_expr.value, fn_var) {
|
match can_reuse_symbol(env, layout_cache, procs, &loc_expr.value, fn_var) {
|
||||||
LocalFunction(_) => {
|
LocalFunction(_) => {
|
||||||
unreachable!("if this was known to be a function, we would not be here")
|
unreachable!("if this was known to be a function, we would not be here")
|
||||||
}
|
}
|
||||||
@ -5685,14 +5704,19 @@ fn compile_struct_like<'a, L, UnusedLayout>(
|
|||||||
// TODO how should function pointers be handled here?
|
// TODO how should function pointers be handled here?
|
||||||
use ReuseSymbol::*;
|
use ReuseSymbol::*;
|
||||||
match take_elem_expr(index) {
|
match take_elem_expr(index) {
|
||||||
Some((var, loc_expr)) => match can_reuse_symbol(env, procs, &loc_expr.value, var) {
|
Some((var, loc_expr)) => {
|
||||||
|
match can_reuse_symbol(env, layout_cache, procs, &loc_expr.value, var) {
|
||||||
Imported(symbol) | LocalFunction(symbol) | UnspecializedExpr(symbol) => {
|
Imported(symbol) | LocalFunction(symbol) | UnspecializedExpr(symbol) => {
|
||||||
elem_symbols.push(symbol);
|
elem_symbols.push(symbol);
|
||||||
can_elems.push(Field::FunctionOrUnspecialized(symbol, variable));
|
can_elems.push(Field::FunctionOrUnspecialized(symbol, variable));
|
||||||
}
|
}
|
||||||
Value(symbol) => {
|
Value(symbol) => {
|
||||||
let reusable =
|
let reusable = procs.get_or_insert_symbol_specialization(
|
||||||
procs.get_or_insert_symbol_specialization(env, layout_cache, symbol, var);
|
env,
|
||||||
|
layout_cache,
|
||||||
|
symbol,
|
||||||
|
var,
|
||||||
|
);
|
||||||
elem_symbols.push(reusable);
|
elem_symbols.push(reusable);
|
||||||
can_elems.push(Field::ValueSymbol);
|
can_elems.push(Field::ValueSymbol);
|
||||||
}
|
}
|
||||||
@ -5700,7 +5724,8 @@ fn compile_struct_like<'a, L, UnusedLayout>(
|
|||||||
elem_symbols.push(env.unique_symbol());
|
elem_symbols.push(env.unique_symbol());
|
||||||
can_elems.push(Field::Field(var, *loc_expr));
|
can_elems.push(Field::Field(var, *loc_expr));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
// this field was optional, but not given
|
// this field was optional, but not given
|
||||||
continue;
|
continue;
|
||||||
@ -6816,7 +6841,7 @@ pub fn from_can<'a>(
|
|||||||
store_specialized_expectation_lookups(env, [variable], &[spec_var]);
|
store_specialized_expectation_lookups(env, [variable], &[spec_var]);
|
||||||
|
|
||||||
let symbol_is_reused = matches!(
|
let symbol_is_reused = matches!(
|
||||||
can_reuse_symbol(env, procs, &loc_condition.value, variable),
|
can_reuse_symbol(env, layout_cache, procs, &loc_condition.value, variable),
|
||||||
ReuseSymbol::Value(_)
|
ReuseSymbol::Value(_)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -7615,7 +7640,8 @@ enum ReuseSymbol {
|
|||||||
|
|
||||||
fn can_reuse_symbol<'a>(
|
fn can_reuse_symbol<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &Procs<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
expr: &roc_can::expr::Expr,
|
expr: &roc_can::expr::Expr,
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
) -> ReuseSymbol {
|
) -> ReuseSymbol {
|
||||||
@ -7627,6 +7653,52 @@ fn can_reuse_symbol<'a>(
|
|||||||
late_resolve_ability_specialization(env, *member, *specialization_id, expr_var)
|
late_resolve_ability_specialization(env, *member, *specialization_id, expr_var)
|
||||||
}
|
}
|
||||||
Var(symbol, _) => *symbol,
|
Var(symbol, _) => *symbol,
|
||||||
|
RecordAccess {
|
||||||
|
record_var,
|
||||||
|
field,
|
||||||
|
loc_expr,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let sorted_fields_result = {
|
||||||
|
let mut layout_env = layout::Env::from_components(
|
||||||
|
layout_cache,
|
||||||
|
env.subs,
|
||||||
|
env.arena,
|
||||||
|
env.target_info,
|
||||||
|
);
|
||||||
|
layout::sort_record_fields(&mut layout_env, *record_var)
|
||||||
|
};
|
||||||
|
|
||||||
|
let sorted_fields = match sorted_fields_result {
|
||||||
|
Ok(fields) => fields,
|
||||||
|
Err(_) => unreachable!("Can't access record with improper layout"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let index = sorted_fields
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(current, (label, _, _))| (label == *field).then_some(current));
|
||||||
|
|
||||||
|
let struct_index = index.expect("field not in its own type");
|
||||||
|
|
||||||
|
let struct_symbol = possible_reuse_symbol_or_specialize(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
&loc_expr.value,
|
||||||
|
*record_var,
|
||||||
|
);
|
||||||
|
|
||||||
|
match env
|
||||||
|
.struct_indexing
|
||||||
|
.get((struct_symbol, struct_index as u64))
|
||||||
|
{
|
||||||
|
Some(symbol) => *symbol,
|
||||||
|
None => {
|
||||||
|
return NotASymbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => return NotASymbol,
|
_ => return NotASymbol,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -7660,7 +7732,7 @@ fn possible_reuse_symbol_or_specialize<'a>(
|
|||||||
expr: &roc_can::expr::Expr,
|
expr: &roc_can::expr::Expr,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
) -> Symbol {
|
) -> Symbol {
|
||||||
match can_reuse_symbol(env, procs, expr, var) {
|
match can_reuse_symbol(env, layout_cache, procs, expr, var) {
|
||||||
ReuseSymbol::Value(symbol) => {
|
ReuseSymbol::Value(symbol) => {
|
||||||
procs.get_or_insert_symbol_specialization(env, layout_cache, symbol, var)
|
procs.get_or_insert_symbol_specialization(env, layout_cache, symbol, var)
|
||||||
}
|
}
|
||||||
@ -7999,7 +8071,7 @@ fn assign_to_symbol<'a>(
|
|||||||
result: Stmt<'a>,
|
result: Stmt<'a>,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
use ReuseSymbol::*;
|
use ReuseSymbol::*;
|
||||||
match can_reuse_symbol(env, procs, &loc_arg.value, arg_var) {
|
match can_reuse_symbol(env, layout_cache, procs, &loc_arg.value, arg_var) {
|
||||||
Imported(original) | LocalFunction(original) | UnspecializedExpr(original) => {
|
Imported(original) | LocalFunction(original) | UnspecializedExpr(original) => {
|
||||||
// for functions we must make sure they are specialized correctly
|
// for functions we must make sure they are specialized correctly
|
||||||
specialize_symbol(
|
specialize_symbol(
|
||||||
@ -9983,3 +10055,42 @@ where
|
|||||||
|
|
||||||
answer
|
answer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Usage {
|
||||||
|
Used,
|
||||||
|
Unused,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UsageTrackingMap<K, V> {
|
||||||
|
map: MutMap<K, (V, Usage)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> Default for UsageTrackingMap<K, V> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
map: MutMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K, V> UsageTrackingMap<K, V>
|
||||||
|
where
|
||||||
|
K: std::cmp::Eq + std::hash::Hash,
|
||||||
|
{
|
||||||
|
pub fn insert(&mut self, key: K, value: V) {
|
||||||
|
self.map.insert(key, (value, Usage::Unused));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&mut self, key: K) -> Option<&V> {
|
||||||
|
let (value, usage) = self.map.get_mut(&key)?;
|
||||||
|
*usage = Usage::Used;
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_used(&mut self, key: &K) -> Option<V> {
|
||||||
|
self.map.remove(key).and_then(|(value, usage)| match usage {
|
||||||
|
Usage::Used => Some(value),
|
||||||
|
Usage::Unused => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1842,7 +1842,7 @@ fn float_add_checked_fail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen_dev"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn float_add_overflow() {
|
fn float_add_overflow() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
"1.7976931348623157e308 + 1.7976931348623157e308",
|
"1.7976931348623157e308 + 1.7976931348623157e308",
|
||||||
|
@ -597,7 +597,7 @@ fn top_level_destructure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_len_0() {
|
fn linked_list_len_0() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -625,7 +625,7 @@ fn linked_list_len_0() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_len_twice_0() {
|
fn linked_list_len_twice_0() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -653,7 +653,7 @@ fn linked_list_len_twice_0() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_len_1() {
|
fn linked_list_len_1() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -681,7 +681,7 @@ fn linked_list_len_1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_len_twice_1() {
|
fn linked_list_len_twice_1() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -709,7 +709,7 @@ fn linked_list_len_twice_1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_len_3() {
|
fn linked_list_len_3() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -738,7 +738,7 @@ fn linked_list_len_3() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_sum_num_a() {
|
fn linked_list_sum_num_a() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -767,7 +767,7 @@ fn linked_list_sum_num_a() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_sum_int() {
|
fn linked_list_sum_int() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -795,7 +795,7 @@ fn linked_list_sum_int() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_map() {
|
fn linked_list_map() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -1244,7 +1244,7 @@ fn return_wrapped_closure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_is_singleton() {
|
fn linked_list_is_singleton() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -1279,7 +1279,7 @@ fn linked_list_is_singleton() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_is_empty_1() {
|
fn linked_list_is_empty_1() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -1314,7 +1314,7 @@ fn linked_list_is_empty_1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_is_empty_2() {
|
fn linked_list_is_empty_2() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -1346,7 +1346,7 @@ fn linked_list_is_empty_2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_singleton() {
|
fn linked_list_singleton() {
|
||||||
// verifies only that valid llvm is produced
|
// verifies only that valid llvm is produced
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
@ -1712,7 +1712,7 @@ fn nested_pattern_match_two_ways() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_guarded_double_pattern_match() {
|
fn linked_list_guarded_double_pattern_match() {
|
||||||
// the important part here is that the first case (with the nested Cons) does not match
|
// the important part here is that the first case (with the nested Cons) does not match
|
||||||
// TODO this also has undefined behavior
|
// TODO this also has undefined behavior
|
||||||
@ -1744,7 +1744,7 @@ fn linked_list_guarded_double_pattern_match() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn linked_list_double_pattern_match() {
|
fn linked_list_double_pattern_match() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -2063,7 +2063,7 @@ fn non_unary_union_with_lambda_set_with_imported_toplevels_issue_4733() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn nullable_wrapped_with_non_nullable_singleton_tags() {
|
fn nullable_wrapped_with_non_nullable_singleton_tags() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -2094,7 +2094,7 @@ fn nullable_wrapped_with_non_nullable_singleton_tags() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn nullable_wrapped_with_nullable_not_last_index() {
|
fn nullable_wrapped_with_nullable_not_last_index() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
@ -2102,9 +2102,9 @@ fn nullable_wrapped_with_nullable_not_last_index() {
|
|||||||
app "test" provides [main] to "./platform"
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
Parser : [
|
Parser : [
|
||||||
OneOrMore Parser,
|
|
||||||
Keyword Str,
|
|
||||||
CharLiteral,
|
CharLiteral,
|
||||||
|
Keyword Str,
|
||||||
|
OneOrMore Parser,
|
||||||
]
|
]
|
||||||
|
|
||||||
toIdParser : Parser -> Str
|
toIdParser : Parser -> Str
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
procedure Dict.1 (Dict.547):
|
procedure Dict.1 (Dict.537):
|
||||||
let Dict.556 : List {[], []} = Array [];
|
let Dict.546 : List {[], []} = Array [];
|
||||||
let Dict.563 : U64 = 0i64;
|
let Dict.553 : U64 = 0i64;
|
||||||
let Dict.564 : U64 = 8i64;
|
let Dict.554 : U64 = 8i64;
|
||||||
let Dict.557 : List U64 = CallByName List.11 Dict.563 Dict.564;
|
let Dict.547 : List U64 = CallByName List.11 Dict.553 Dict.554;
|
||||||
let Dict.560 : I8 = CallByName Dict.37;
|
let Dict.550 : I8 = CallByName Dict.36;
|
||||||
let Dict.561 : U64 = 8i64;
|
let Dict.551 : U64 = 8i64;
|
||||||
let Dict.558 : List I8 = CallByName List.11 Dict.560 Dict.561;
|
let Dict.548 : List I8 = CallByName List.11 Dict.550 Dict.551;
|
||||||
let Dict.559 : U64 = 0i64;
|
let Dict.549 : U64 = 0i64;
|
||||||
let Dict.555 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.556, Dict.557, Dict.558, Dict.559};
|
let Dict.545 : {List {[], []}, List U64, List I8, U64} = Struct {Dict.546, Dict.547, Dict.548, Dict.549};
|
||||||
ret Dict.555;
|
ret Dict.545;
|
||||||
|
|
||||||
procedure Dict.37 ():
|
procedure Dict.36 ():
|
||||||
let Dict.562 : I8 = -128i64;
|
let Dict.552 : I8 = -128i64;
|
||||||
ret Dict.562;
|
ret Dict.552;
|
||||||
|
|
||||||
procedure Dict.4 (Dict.553):
|
procedure Dict.4 (Dict.543):
|
||||||
let Dict.99 : U64 = StructAtIndex 3 Dict.553;
|
let Dict.97 : U64 = StructAtIndex 3 Dict.543;
|
||||||
let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.553;
|
let #Derived_gen.2 : List {[], []} = StructAtIndex 0 Dict.543;
|
||||||
dec #Derived_gen.2;
|
dec #Derived_gen.2;
|
||||||
let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.553;
|
let #Derived_gen.1 : List U64 = StructAtIndex 1 Dict.543;
|
||||||
dec #Derived_gen.1;
|
dec #Derived_gen.1;
|
||||||
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.553;
|
let #Derived_gen.0 : List I8 = StructAtIndex 2 Dict.543;
|
||||||
dec #Derived_gen.0;
|
dec #Derived_gen.0;
|
||||||
ret Dict.99;
|
ret Dict.97;
|
||||||
|
|
||||||
procedure List.11 (List.115, List.116):
|
procedure List.11 (List.115, List.116):
|
||||||
let List.495 : List I8 = CallByName List.68 List.116;
|
let List.495 : List I8 = CallByName List.68 List.116;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
procedure Test.0 ():
|
||||||
|
let Test.2 : Str = "value";
|
||||||
|
let Test.3 : {Str, Str} = Struct {Test.2, Test.2};
|
||||||
|
dec Test.2;
|
||||||
|
let Test.4 : Str = "result";
|
||||||
|
ret Test.4;
|
@ -697,8 +697,8 @@ procedure Json.25 (Json.183):
|
|||||||
let Json.1906 : List U8 = CallByName List.8 Json.1907 Json.1908;
|
let Json.1906 : List U8 = CallByName List.8 Json.1907 Json.1908;
|
||||||
ret Json.1906;
|
ret Json.1906;
|
||||||
else
|
else
|
||||||
let Json.1948 : U64 = StructAtIndex 0 Json.186;
|
|
||||||
inc Json.184;
|
inc Json.184;
|
||||||
|
let Json.1948 : U64 = StructAtIndex 0 Json.186;
|
||||||
let Json.1947 : {List U8, List U8} = CallByName List.52 Json.184 Json.1948;
|
let Json.1947 : {List U8, List U8} = CallByName List.52 Json.184 Json.1948;
|
||||||
let Json.210 : List U8 = StructAtIndex 0 Json.1947;
|
let Json.210 : List U8 = StructAtIndex 0 Json.1947;
|
||||||
let Json.212 : List U8 = StructAtIndex 1 Json.1947;
|
let Json.212 : List U8 = StructAtIndex 1 Json.1947;
|
||||||
|
@ -623,8 +623,8 @@ procedure Json.25 (Json.183):
|
|||||||
let Json.1532 : List U8 = CallByName List.8 Json.1533 Json.1534;
|
let Json.1532 : List U8 = CallByName List.8 Json.1533 Json.1534;
|
||||||
ret Json.1532;
|
ret Json.1532;
|
||||||
else
|
else
|
||||||
let Json.1574 : U64 = StructAtIndex 0 Json.186;
|
|
||||||
inc Json.184;
|
inc Json.184;
|
||||||
|
let Json.1574 : U64 = StructAtIndex 0 Json.186;
|
||||||
let Json.1573 : {List U8, List U8} = CallByName List.52 Json.184 Json.1574;
|
let Json.1573 : {List U8, List U8} = CallByName List.52 Json.184 Json.1574;
|
||||||
let Json.210 : List U8 = StructAtIndex 0 Json.1573;
|
let Json.210 : List U8 = StructAtIndex 0 Json.1573;
|
||||||
let Json.212 : List U8 = StructAtIndex 1 Json.1573;
|
let Json.212 : List U8 = StructAtIndex 1 Json.1573;
|
||||||
|
@ -630,8 +630,8 @@ procedure Json.25 (Json.183):
|
|||||||
let Json.1532 : List U8 = CallByName List.8 Json.1533 Json.1534;
|
let Json.1532 : List U8 = CallByName List.8 Json.1533 Json.1534;
|
||||||
ret Json.1532;
|
ret Json.1532;
|
||||||
else
|
else
|
||||||
let Json.1574 : U64 = StructAtIndex 0 Json.186;
|
|
||||||
inc Json.184;
|
inc Json.184;
|
||||||
|
let Json.1574 : U64 = StructAtIndex 0 Json.186;
|
||||||
let Json.1573 : {List U8, List U8} = CallByName List.52 Json.184 Json.1574;
|
let Json.1573 : {List U8, List U8} = CallByName List.52 Json.184 Json.1574;
|
||||||
let Json.210 : List U8 = StructAtIndex 0 Json.1573;
|
let Json.210 : List U8 = StructAtIndex 0 Json.1573;
|
||||||
let Json.212 : List U8 = StructAtIndex 1 Json.1573;
|
let Json.212 : List U8 = StructAtIndex 1 Json.1573;
|
||||||
|
@ -118,8 +118,8 @@ procedure Json.25 (Json.183):
|
|||||||
let Json.1179 : List U8 = CallByName List.8 Json.1180 Json.1181;
|
let Json.1179 : List U8 = CallByName List.8 Json.1180 Json.1181;
|
||||||
ret Json.1179;
|
ret Json.1179;
|
||||||
else
|
else
|
||||||
let Json.1221 : U64 = StructAtIndex 0 Json.186;
|
|
||||||
inc Json.184;
|
inc Json.184;
|
||||||
|
let Json.1221 : U64 = StructAtIndex 0 Json.186;
|
||||||
let Json.1220 : {List U8, List U8} = CallByName List.52 Json.184 Json.1221;
|
let Json.1220 : {List U8, List U8} = CallByName List.52 Json.184 Json.1221;
|
||||||
let Json.210 : List U8 = StructAtIndex 0 Json.1220;
|
let Json.210 : List U8 = StructAtIndex 0 Json.1220;
|
||||||
let Json.212 : List U8 = StructAtIndex 1 Json.1220;
|
let Json.212 : List U8 = StructAtIndex 1 Json.1220;
|
||||||
|
@ -147,8 +147,8 @@ procedure Json.25 (Json.183):
|
|||||||
let Json.1220 : List U8 = CallByName List.8 Json.1221 Json.1222;
|
let Json.1220 : List U8 = CallByName List.8 Json.1221 Json.1222;
|
||||||
ret Json.1220;
|
ret Json.1220;
|
||||||
else
|
else
|
||||||
let Json.1262 : U64 = StructAtIndex 0 Json.186;
|
|
||||||
inc Json.184;
|
inc Json.184;
|
||||||
|
let Json.1262 : U64 = StructAtIndex 0 Json.186;
|
||||||
let Json.1261 : {List U8, List U8} = CallByName List.52 Json.184 Json.1262;
|
let Json.1261 : {List U8, List U8} = CallByName List.52 Json.184 Json.1262;
|
||||||
let Json.210 : List U8 = StructAtIndex 0 Json.1261;
|
let Json.210 : List U8 = StructAtIndex 0 Json.1261;
|
||||||
let Json.212 : List U8 = StructAtIndex 1 Json.1261;
|
let Json.212 : List U8 = StructAtIndex 1 Json.1261;
|
||||||
|
@ -150,8 +150,8 @@ procedure Json.25 (Json.183):
|
|||||||
let Json.1220 : List U8 = CallByName List.8 Json.1221 Json.1222;
|
let Json.1220 : List U8 = CallByName List.8 Json.1221 Json.1222;
|
||||||
ret Json.1220;
|
ret Json.1220;
|
||||||
else
|
else
|
||||||
let Json.1262 : U64 = StructAtIndex 0 Json.186;
|
|
||||||
inc Json.184;
|
inc Json.184;
|
||||||
|
let Json.1262 : U64 = StructAtIndex 0 Json.186;
|
||||||
let Json.1261 : {List U8, List U8} = CallByName List.52 Json.184 Json.1262;
|
let Json.1261 : {List U8, List U8} = CallByName List.52 Json.184 Json.1262;
|
||||||
let Json.210 : List U8 = StructAtIndex 0 Json.1261;
|
let Json.210 : List U8 = StructAtIndex 0 Json.1261;
|
||||||
let Json.212 : List U8 = StructAtIndex 1 Json.1261;
|
let Json.212 : List U8 = StructAtIndex 1 Json.1261;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
procedure Test.1 ():
|
procedure Test.1 ():
|
||||||
let Test.7 : U8 = 1i64;
|
let Test.10 : U8 = 1i64;
|
||||||
let Test.8 : U8 = 2i64;
|
let Test.11 : U8 = 2i64;
|
||||||
let Test.6 : {U8, U8} = Struct {Test.7, Test.8};
|
let Test.9 : {U8, U8} = Struct {Test.10, Test.11};
|
||||||
ret Test.6;
|
ret Test.9;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.9 : {U8, U8} = CallByName Test.1;
|
let Test.13 : {U8, U8} = CallByName Test.1;
|
||||||
let Test.3 : U8 = StructAtIndex 0 Test.9;
|
let Test.4 : U8 = StructAtIndex 0 Test.13;
|
||||||
let Test.5 : {U8, U8} = CallByName Test.1;
|
let Test.8 : {U8, U8} = CallByName Test.1;
|
||||||
let Test.4 : U8 = StructAtIndex 1 Test.5;
|
let Test.6 : U8 = StructAtIndex 1 Test.8;
|
||||||
let Test.2 : List U8 = Array [Test.3, Test.4];
|
let Test.2 : List U8 = Array [Test.4, Test.6];
|
||||||
ret Test.2;
|
ret Test.2;
|
||||||
|
@ -179,8 +179,8 @@ procedure Json.60 (Json.540):
|
|||||||
let Json.1336 : U8 = GetTagId Json.1327;
|
let Json.1336 : U8 = GetTagId Json.1327;
|
||||||
let Json.1337 : Int1 = lowlevel Eq Json.1335 Json.1336;
|
let Json.1337 : Int1 = lowlevel Eq Json.1335 Json.1336;
|
||||||
if Json.1337 then
|
if Json.1337 then
|
||||||
let Json.542 : U64 = UnionAtIndex (Id 2) (Index 0) Json.1327;
|
|
||||||
inc Json.540;
|
inc Json.540;
|
||||||
|
let Json.542 : U64 = UnionAtIndex (Id 2) (Index 0) Json.1327;
|
||||||
let Json.1329 : List U8 = CallByName List.29 Json.540 Json.542;
|
let Json.1329 : List U8 = CallByName List.29 Json.540 Json.542;
|
||||||
let Json.1332 : U64 = 0i64;
|
let Json.1332 : U64 = 0i64;
|
||||||
let Json.1331 : {U64, U64} = Struct {Json.542, Json.1332};
|
let Json.1331 : {U64, U64} = Struct {Json.542, Json.1332};
|
||||||
@ -532,18 +532,15 @@ procedure Json.68 ():
|
|||||||
procedure Json.69 (Json.1467):
|
procedure Json.69 (Json.1467):
|
||||||
joinpoint Json.1197 Json.1165:
|
joinpoint Json.1197 Json.1165:
|
||||||
let Json.599 : List U8 = StructAtIndex 0 Json.1165;
|
let Json.599 : List U8 = StructAtIndex 0 Json.1165;
|
||||||
inc Json.599;
|
inc 4 Json.599;
|
||||||
let Json.600 : List U8 = StructAtIndex 1 Json.1165;
|
let Json.600 : List U8 = StructAtIndex 1 Json.1165;
|
||||||
let Json.1315 : U64 = 0i64;
|
let Json.1315 : U64 = 0i64;
|
||||||
let Json.601 : [C {}, C U8] = CallByName List.2 Json.599 Json.1315;
|
let Json.601 : [C {}, C U8] = CallByName List.2 Json.599 Json.1315;
|
||||||
let Json.1314 : U64 = 1i64;
|
let Json.1314 : U64 = 1i64;
|
||||||
inc Json.599;
|
|
||||||
let Json.602 : [C {}, C U8] = CallByName List.2 Json.599 Json.1314;
|
let Json.602 : [C {}, C U8] = CallByName List.2 Json.599 Json.1314;
|
||||||
let Json.1313 : U64 = 2i64;
|
let Json.1313 : U64 = 2i64;
|
||||||
inc Json.599;
|
|
||||||
let Json.603 : List U8 = CallByName List.29 Json.599 Json.1313;
|
let Json.603 : List U8 = CallByName List.29 Json.599 Json.1313;
|
||||||
let Json.1312 : U64 = 6i64;
|
let Json.1312 : U64 = 6i64;
|
||||||
inc Json.599;
|
|
||||||
let Json.604 : List U8 = CallByName List.29 Json.599 Json.1312;
|
let Json.604 : List U8 = CallByName List.29 Json.599 Json.1312;
|
||||||
let Json.1198 : {[C {}, C U8], [C {}, C U8]} = Struct {Json.601, Json.602};
|
let Json.1198 : {[C {}, C U8], [C {}, C U8]} = Struct {Json.601, Json.602};
|
||||||
joinpoint Json.1277:
|
joinpoint Json.1277:
|
||||||
@ -832,7 +829,7 @@ procedure Test.3 ():
|
|||||||
let Test.7 : Str = "Roc";
|
let Test.7 : Str = "Roc";
|
||||||
let Test.6 : [C [C List U8, C ], C Str] = TagId(1) Test.7;
|
let Test.6 : [C [C List U8, C ], C Str] = TagId(1) Test.7;
|
||||||
let Test.5 : Int1 = CallByName Bool.11 Test.1 Test.6;
|
let Test.5 : Int1 = CallByName Bool.11 Test.1 Test.6;
|
||||||
dec Test.6;
|
dec Test.7;
|
||||||
expect Test.5;
|
expect Test.5;
|
||||||
dec Test.0;
|
dec Test.0;
|
||||||
dec Test.1;
|
dec Test.1;
|
||||||
|
@ -153,8 +153,8 @@ procedure Json.60 (Json.540):
|
|||||||
let Json.1336 : U8 = GetTagId Json.1327;
|
let Json.1336 : U8 = GetTagId Json.1327;
|
||||||
let Json.1337 : Int1 = lowlevel Eq Json.1335 Json.1336;
|
let Json.1337 : Int1 = lowlevel Eq Json.1335 Json.1336;
|
||||||
if Json.1337 then
|
if Json.1337 then
|
||||||
let Json.542 : U64 = UnionAtIndex (Id 2) (Index 0) Json.1327;
|
|
||||||
inc Json.540;
|
inc Json.540;
|
||||||
|
let Json.542 : U64 = UnionAtIndex (Id 2) (Index 0) Json.1327;
|
||||||
let Json.1329 : List U8 = CallByName List.29 Json.540 Json.542;
|
let Json.1329 : List U8 = CallByName List.29 Json.540 Json.542;
|
||||||
let Json.1332 : U64 = 0i64;
|
let Json.1332 : U64 = 0i64;
|
||||||
let Json.1331 : {U64, U64} = Struct {Json.542, Json.1332};
|
let Json.1331 : {U64, U64} = Struct {Json.542, Json.1332};
|
||||||
@ -506,18 +506,15 @@ procedure Json.68 ():
|
|||||||
procedure Json.69 (Json.1467):
|
procedure Json.69 (Json.1467):
|
||||||
joinpoint Json.1197 Json.1165:
|
joinpoint Json.1197 Json.1165:
|
||||||
let Json.599 : List U8 = StructAtIndex 0 Json.1165;
|
let Json.599 : List U8 = StructAtIndex 0 Json.1165;
|
||||||
inc Json.599;
|
inc 4 Json.599;
|
||||||
let Json.600 : List U8 = StructAtIndex 1 Json.1165;
|
let Json.600 : List U8 = StructAtIndex 1 Json.1165;
|
||||||
let Json.1315 : U64 = 0i64;
|
let Json.1315 : U64 = 0i64;
|
||||||
let Json.601 : [C {}, C U8] = CallByName List.2 Json.599 Json.1315;
|
let Json.601 : [C {}, C U8] = CallByName List.2 Json.599 Json.1315;
|
||||||
let Json.1314 : U64 = 1i64;
|
let Json.1314 : U64 = 1i64;
|
||||||
inc Json.599;
|
|
||||||
let Json.602 : [C {}, C U8] = CallByName List.2 Json.599 Json.1314;
|
let Json.602 : [C {}, C U8] = CallByName List.2 Json.599 Json.1314;
|
||||||
let Json.1313 : U64 = 2i64;
|
let Json.1313 : U64 = 2i64;
|
||||||
inc Json.599;
|
|
||||||
let Json.603 : List U8 = CallByName List.29 Json.599 Json.1313;
|
let Json.603 : List U8 = CallByName List.29 Json.599 Json.1313;
|
||||||
let Json.1312 : U64 = 6i64;
|
let Json.1312 : U64 = 6i64;
|
||||||
inc Json.599;
|
|
||||||
let Json.604 : List U8 = CallByName List.29 Json.599 Json.1312;
|
let Json.604 : List U8 = CallByName List.29 Json.599 Json.1312;
|
||||||
let Json.1198 : {[C {}, C U8], [C {}, C U8]} = Struct {Json.601, Json.602};
|
let Json.1198 : {[C {}, C U8], [C {}, C U8]} = Struct {Json.601, Json.602};
|
||||||
joinpoint Json.1277:
|
joinpoint Json.1277:
|
||||||
@ -865,7 +862,7 @@ procedure Test.12 ():
|
|||||||
let Test.16 : {List U8, I64} = Struct {Test.17, Test.18};
|
let Test.16 : {List U8, I64} = Struct {Test.17, Test.18};
|
||||||
let Test.15 : [C Str, C {List U8, I64}] = TagId(1) Test.16;
|
let Test.15 : [C Str, C {List U8, I64}] = TagId(1) Test.16;
|
||||||
let Test.14 : Int1 = CallByName Bool.11 Test.10 Test.15;
|
let Test.14 : Int1 = CallByName Bool.11 Test.10 Test.15;
|
||||||
dec Test.15;
|
dec Test.16;
|
||||||
expect Test.14;
|
expect Test.14;
|
||||||
dec Test.10;
|
dec Test.10;
|
||||||
let Test.13 : {} = Struct {};
|
let Test.13 : {} = Struct {};
|
||||||
|
@ -5,7 +5,8 @@ procedure Test.0 ():
|
|||||||
let Test.9 : U64 = 1i64;
|
let Test.9 : U64 = 1i64;
|
||||||
let Test.10 : Int1 = lowlevel Eq Test.8 Test.9;
|
let Test.10 : Int1 = lowlevel Eq Test.8 Test.9;
|
||||||
if Test.10 then
|
if Test.10 then
|
||||||
dec Test.1;
|
dec Test.11;
|
||||||
|
decref Test.1;
|
||||||
let Test.3 : Str = "B";
|
let Test.3 : Str = "B";
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
else
|
else
|
||||||
|
@ -5,7 +5,7 @@ procedure Test.0 ():
|
|||||||
let Test.2 : [<rnu><null>, C *self] = TagId(0) Test.13;
|
let Test.2 : [<rnu><null>, C *self] = TagId(0) Test.13;
|
||||||
let Test.10 : U8 = 1i64;
|
let Test.10 : U8 = 1i64;
|
||||||
let Test.11 : U8 = GetTagId Test.2;
|
let Test.11 : U8 = GetTagId Test.2;
|
||||||
dec Test.2;
|
joinpoint #Derived_gen.0:
|
||||||
let Test.12 : Int1 = lowlevel Eq Test.10 Test.11;
|
let Test.12 : Int1 = lowlevel Eq Test.10 Test.11;
|
||||||
if Test.12 then
|
if Test.12 then
|
||||||
let Test.8 : I64 = 0i64;
|
let Test.8 : I64 = 0i64;
|
||||||
@ -13,3 +13,12 @@ procedure Test.0 ():
|
|||||||
else
|
else
|
||||||
let Test.9 : I64 = 1i64;
|
let Test.9 : I64 = 1i64;
|
||||||
ret Test.9;
|
ret Test.9;
|
||||||
|
in
|
||||||
|
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.2;
|
||||||
|
if #Derived_gen.1 then
|
||||||
|
dec Test.13;
|
||||||
|
decref Test.2;
|
||||||
|
jump #Derived_gen.0;
|
||||||
|
else
|
||||||
|
decref Test.2;
|
||||||
|
jump #Derived_gen.0;
|
||||||
|
@ -30,6 +30,15 @@ procedure Test.0 ():
|
|||||||
let Test.16 : Str = "";
|
let Test.16 : Str = "";
|
||||||
let Test.15 : [<r>C List *self, C Str] = TagId(1) Test.16;
|
let Test.15 : [<r>C List *self, C Str] = TagId(1) Test.16;
|
||||||
let Test.13 : Int1 = CallByName Bool.11 Test.14 Test.15;
|
let Test.13 : Int1 = CallByName Bool.11 Test.14 Test.15;
|
||||||
dec Test.15;
|
joinpoint #Derived_gen.0:
|
||||||
dec Test.14;
|
dec Test.14;
|
||||||
ret Test.13;
|
ret Test.13;
|
||||||
|
in
|
||||||
|
let #Derived_gen.1 : Int1 = lowlevel RefCountIsUnique Test.15;
|
||||||
|
if #Derived_gen.1 then
|
||||||
|
dec Test.16;
|
||||||
|
decref Test.15;
|
||||||
|
jump #Derived_gen.0;
|
||||||
|
else
|
||||||
|
decref Test.15;
|
||||||
|
jump #Derived_gen.0;
|
||||||
|
@ -45,10 +45,9 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||||||
|
|
||||||
procedure Test.1 (Test.2):
|
procedure Test.1 (Test.2):
|
||||||
let Test.28 : U64 = 0i64;
|
let Test.28 : U64 = 0i64;
|
||||||
inc Test.2;
|
inc 2 Test.2;
|
||||||
let Test.26 : [C {}, C I64] = CallByName List.2 Test.2 Test.28;
|
let Test.26 : [C {}, C I64] = CallByName List.2 Test.2 Test.28;
|
||||||
let Test.27 : U64 = 0i64;
|
let Test.27 : U64 = 0i64;
|
||||||
inc Test.2;
|
|
||||||
let Test.25 : [C {}, C I64] = CallByName List.2 Test.2 Test.27;
|
let Test.25 : [C {}, C I64] = CallByName List.2 Test.2 Test.27;
|
||||||
let Test.8 : {[C {}, C I64], [C {}, C I64]} = Struct {Test.25, Test.26};
|
let Test.8 : {[C {}, C I64], [C {}, C I64]} = Struct {Test.25, Test.26};
|
||||||
joinpoint Test.22:
|
joinpoint Test.22:
|
||||||
|
47
crates/compiler/test_mono/generated/record_update.txt
Normal file
47
crates/compiler/test_mono/generated/record_update.txt
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
procedure List.3 (List.104, List.105, List.106):
|
||||||
|
let List.503 : {List U64, U64} = CallByName List.64 List.104 List.105 List.106;
|
||||||
|
let List.502 : List U64 = StructAtIndex 0 List.503;
|
||||||
|
ret List.502;
|
||||||
|
|
||||||
|
procedure List.6 (#Attr.2):
|
||||||
|
let List.501 : U64 = lowlevel ListLen #Attr.2;
|
||||||
|
ret List.501;
|
||||||
|
|
||||||
|
procedure List.64 (List.101, List.102, List.103):
|
||||||
|
let List.500 : U64 = CallByName List.6 List.101;
|
||||||
|
let List.497 : Int1 = CallByName Num.22 List.102 List.500;
|
||||||
|
if List.497 then
|
||||||
|
let List.498 : {List U64, U64} = CallByName List.67 List.101 List.102 List.103;
|
||||||
|
ret List.498;
|
||||||
|
else
|
||||||
|
let List.496 : {List U64, U64} = Struct {List.101, List.103};
|
||||||
|
ret List.496;
|
||||||
|
|
||||||
|
procedure List.67 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
|
let List.499 : {List U64, U64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4;
|
||||||
|
ret List.499;
|
||||||
|
|
||||||
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
|
let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
|
ret Num.281;
|
||||||
|
|
||||||
|
procedure Test.1 (Test.2):
|
||||||
|
let Test.6 : List U64 = StructAtIndex 0 Test.2;
|
||||||
|
let Test.8 : List U64 = StructAtIndex 1 Test.2;
|
||||||
|
let Test.10 : List U64 = StructAtIndex 2 Test.2;
|
||||||
|
let Test.13 : U64 = 8i64;
|
||||||
|
let Test.14 : U64 = 8i64;
|
||||||
|
let Test.9 : List U64 = CallByName List.3 Test.8 Test.13 Test.14;
|
||||||
|
let Test.11 : U64 = 7i64;
|
||||||
|
let Test.12 : U64 = 7i64;
|
||||||
|
let Test.7 : List U64 = CallByName List.3 Test.6 Test.11 Test.12;
|
||||||
|
let Test.5 : {List U64, List U64, List U64} = Struct {Test.7, Test.9, Test.10};
|
||||||
|
ret Test.5;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.15 : List U64 = Array [];
|
||||||
|
let Test.16 : List U64 = Array [];
|
||||||
|
let Test.17 : List U64 = Array [];
|
||||||
|
let Test.4 : {List U64, List U64, List U64} = Struct {Test.15, Test.16, Test.17};
|
||||||
|
let Test.3 : {List U64, List U64, List U64} = CallByName Test.1 Test.4;
|
||||||
|
ret Test.3;
|
@ -44,9 +44,8 @@ procedure Num.22 (#Attr.2, #Attr.3):
|
|||||||
ret Num.283;
|
ret Num.283;
|
||||||
|
|
||||||
procedure Test.1 (Test.2, Test.3, Test.4):
|
procedure Test.1 (Test.2, Test.3, Test.4):
|
||||||
inc Test.4;
|
inc 2 Test.4;
|
||||||
let Test.29 : [C {}, C I64] = CallByName List.2 Test.4 Test.3;
|
let Test.29 : [C {}, C I64] = CallByName List.2 Test.4 Test.3;
|
||||||
inc Test.4;
|
|
||||||
let Test.28 : [C {}, C I64] = CallByName List.2 Test.4 Test.2;
|
let Test.28 : [C {}, C I64] = CallByName List.2 Test.4 Test.2;
|
||||||
let Test.13 : {[C {}, C I64], [C {}, C I64]} = Struct {Test.28, Test.29};
|
let Test.13 : {[C {}, C I64], [C {}, C I64]} = Struct {Test.28, Test.29};
|
||||||
joinpoint Test.25:
|
joinpoint Test.25:
|
||||||
|
@ -136,8 +136,8 @@ procedure Json.25 (Json.183):
|
|||||||
let Json.1223 : List U8 = CallByName List.8 Json.1224 Json.1225;
|
let Json.1223 : List U8 = CallByName List.8 Json.1224 Json.1225;
|
||||||
ret Json.1223;
|
ret Json.1223;
|
||||||
else
|
else
|
||||||
let Json.1265 : U64 = StructAtIndex 0 Json.186;
|
|
||||||
inc Json.184;
|
inc Json.184;
|
||||||
|
let Json.1265 : U64 = StructAtIndex 0 Json.186;
|
||||||
let Json.1264 : {List U8, List U8} = CallByName List.52 Json.184 Json.1265;
|
let Json.1264 : {List U8, List U8} = CallByName List.52 Json.184 Json.1265;
|
||||||
let Json.210 : List U8 = StructAtIndex 0 Json.1264;
|
let Json.210 : List U8 = StructAtIndex 0 Json.1264;
|
||||||
let Json.212 : List U8 = StructAtIndex 1 Json.1264;
|
let Json.212 : List U8 = StructAtIndex 1 Json.1264;
|
||||||
|
@ -3050,3 +3050,31 @@ fn specialize_after_match() {
|
|||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn drop_specialize_after_struct() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
Tuple a b : { left : a, right : b }
|
||||||
|
|
||||||
|
main =
|
||||||
|
v = "value"
|
||||||
|
t = { left: v, right: v }
|
||||||
|
"result"
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn record_update() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
main = f {a: [], b: [], c:[]}
|
||||||
|
f : {a: List Nat, b: List Nat, c: List Nat} -> {a: List Nat, b: List Nat, c: List Nat}
|
||||||
|
f = \record -> {record & a: List.set record.a 7 7, b: List.set record.b 8 8}
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -23,10 +23,10 @@ procedure Dep.0 ():
|
|||||||
ret Dep.1;
|
ret Dep.1;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.3 : Str = "http://www.example.com";
|
let Test.5 : {Str, Str} = CallByName Dep.0;
|
||||||
let Test.4 : {Str, Str} = CallByName Dep.0;
|
let Test.2 : Str = StructAtIndex 0 Test.5;
|
||||||
let Test.2 : Str = StructAtIndex 0 Test.4;
|
let #Derived_gen.0 : Str = StructAtIndex 1 Test.5;
|
||||||
let #Derived_gen.0 : Str = StructAtIndex 1 Test.4;
|
|
||||||
dec #Derived_gen.0;
|
dec #Derived_gen.0;
|
||||||
let Test.1 : {Str, Str} = Struct {Test.2, Test.3};
|
let Test.4 : Str = "http://www.example.com";
|
||||||
|
let Test.1 : {Str, Str} = Struct {Test.2, Test.4};
|
||||||
ret Test.1;
|
ret Test.1;
|
||||||
|
@ -24,7 +24,7 @@ macro_rules! internal_error {
|
|||||||
macro_rules! user_error {
|
macro_rules! user_error {
|
||||||
($($arg:tt)*) => ({
|
($($arg:tt)*) => ({
|
||||||
eprintln!("We ran into an issue while compiling your code.");
|
eprintln!("We ran into an issue while compiling your code.");
|
||||||
eprintln!("Sadly, we don't havs a pretty error message for this case yet.");
|
eprintln!("Sadly, we don't have a pretty error message for this case yet.");
|
||||||
eprintln!("If you can't figure out the problem from the context below, please reach out at: https://roc.zulipchat.com/");
|
eprintln!("If you can't figure out the problem from the context below, please reach out at: https://roc.zulipchat.com/");
|
||||||
eprintln!($($arg)*);
|
eprintln!($($arg)*);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
|
@ -1471,14 +1471,6 @@ fn surgery_elf_help(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Explicitly ignore some symbols that are currently always linked.
|
|
||||||
const ALWAYS_LINKED: &[&str] = &["__divti3", "__udivti3"];
|
|
||||||
|
|
||||||
match app_obj.symbol_by_index(index) {
|
|
||||||
Ok(sym) if ALWAYS_LINKED.contains(&sym.name().unwrap_or_default()) => {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
internal_error!(
|
internal_error!(
|
||||||
"Undefined Symbol in relocation, {:+x?}: {:+x?}",
|
"Undefined Symbol in relocation, {:+x?}: {:+x?}",
|
||||||
rel,
|
rel,
|
||||||
@ -1486,8 +1478,6 @@ fn surgery_elf_help(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
internal_error!("Relocation target not yet support: {:+x?}", rel);
|
internal_error!("Relocation target not yet support: {:+x?}", rel);
|
||||||
|
@ -434,7 +434,19 @@ pub(crate) fn surgery_pe(executable_path: &Path, metadata_path: &Path, roc_app_b
|
|||||||
relocation,
|
relocation,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if *address == 0 && !name.starts_with("roc") {
|
let is_ingested_compiler_rt = [
|
||||||
|
"__muloti4",
|
||||||
|
"__divti3",
|
||||||
|
"__udivti3",
|
||||||
|
"__modti3",
|
||||||
|
"__umodti3",
|
||||||
|
"__fixdfti",
|
||||||
|
"__fixsfti",
|
||||||
|
"__fixunsdfti",
|
||||||
|
"__fixunssfti",
|
||||||
|
]
|
||||||
|
.contains(&name.as_str());
|
||||||
|
if *address == 0 && !name.starts_with("roc") && !is_ingested_compiler_rt {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"I don't know the address of the {} function! this may cause segfaults",
|
"I don't know the address of the {} function! this may cause segfaults",
|
||||||
name
|
name
|
||||||
|
@ -13,6 +13,7 @@ use crate::tarball::Compression;
|
|||||||
// let's try to avoid doing that.
|
// let's try to avoid doing that.
|
||||||
const BROTLI_BUFFER_BYTES: usize = 8 * 1_000_000; // MB
|
const BROTLI_BUFFER_BYTES: usize = 8 * 1_000_000; // MB
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct PackageMetadata<'a> {
|
pub struct PackageMetadata<'a> {
|
||||||
/// The BLAKE3 hash of the tarball's contents. Also the .tar filename on disk.
|
/// The BLAKE3 hash of the tarball's contents. Also the .tar filename on disk.
|
||||||
pub content_hash: &'a str,
|
pub content_hash: &'a str,
|
||||||
@ -29,13 +30,29 @@ pub struct PackageMetadata<'a> {
|
|||||||
/// - .tar.br
|
/// - .tar.br
|
||||||
const VALID_EXTENSION_SUFFIXES: [&str; 2] = [".gz", ".br"];
|
const VALID_EXTENSION_SUFFIXES: [&str; 2] = [".gz", ".br"];
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// Since the TLD (top level domain) `.zip` is now available, there is a new attack
|
||||||
|
/// vector where malicous URLs can be used to confuse the reader.
|
||||||
|
/// Example of a URL which would take you to example.zip:
|
||||||
|
/// https://github.com∕kubernetes∕kubernetes∕archive∕refs∕tags∕@example.zip
|
||||||
|
/// roc employs a checksum mechanism to prevent tampering with packages.
|
||||||
|
/// Nevertheless we should avoid such issues earlier.
|
||||||
|
/// You can read more here: https://medium.com/@bobbyrsec/the-dangers-of-googles-zip-tld-5e1e675e59a5
|
||||||
|
const MISLEADING_CHARACTERS_IN_URL: [char; 5] = [
|
||||||
|
'@', // @ - For now we avoid usage of the @, to avoid the "tld zip" attack vector
|
||||||
|
'\u{2044}', // U+2044 == ⁄ Fraction Slash
|
||||||
|
'\u{2215}', // U+2215 == ∕ Division Slash
|
||||||
|
'\u{FF0F}', // U+2215 == / Fullwidth Solidus
|
||||||
|
'\u{29F8}', // U+29F8 == ⧸ Big Solidus
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum UrlProblem {
|
pub enum UrlProblem {
|
||||||
InvalidExtensionSuffix(String),
|
InvalidExtensionSuffix(String),
|
||||||
MissingTarExt,
|
MissingTarExt,
|
||||||
InvalidFragment(String),
|
InvalidFragment(String),
|
||||||
MissingHash,
|
MissingHash,
|
||||||
MissingHttps,
|
MissingHttps,
|
||||||
|
MisleadingCharacter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a str> for PackageMetadata<'a> {
|
impl<'a> TryFrom<&'a str> for PackageMetadata<'a> {
|
||||||
@ -56,6 +73,14 @@ impl<'a> PackageMetadata<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Next, check if there are misleading characters in the URL
|
||||||
|
if url
|
||||||
|
.chars()
|
||||||
|
.any(|ch| MISLEADING_CHARACTERS_IN_URL.contains(&ch))
|
||||||
|
{
|
||||||
|
return Err(UrlProblem::MisleadingCharacter);
|
||||||
|
}
|
||||||
|
|
||||||
// Next, get the (optional) URL fragment, which must be a .roc filename
|
// Next, get the (optional) URL fragment, which must be a .roc filename
|
||||||
let (without_fragment, fragment) = match without_protocol.rsplit_once('#') {
|
let (without_fragment, fragment) = match without_protocol.rsplit_once('#') {
|
||||||
Some((before_fragment, fragment)) => {
|
Some((before_fragment, fragment)) => {
|
||||||
@ -102,6 +127,105 @@ impl<'a> PackageMetadata<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_problem_missing_https() {
|
||||||
|
let expected = Err(UrlProblem::MissingHttps);
|
||||||
|
assert_eq!(PackageMetadata::try_from("http://example.com"), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_problem_misleading_characters() {
|
||||||
|
let expected = Err(UrlProblem::MisleadingCharacter);
|
||||||
|
|
||||||
|
for misleading_character_example in [
|
||||||
|
"https://user:password@example.com/",
|
||||||
|
//"https://example.com⁄path",
|
||||||
|
"https://example.com\u{2044}path",
|
||||||
|
//"https://example.com∕path",
|
||||||
|
"https://example.com\u{2215}path",
|
||||||
|
//"https://example.com/path",
|
||||||
|
"https://example.com\u{ff0f}path",
|
||||||
|
//"https://example.com⧸path",
|
||||||
|
"https://example.com\u{29f8}path",
|
||||||
|
] {
|
||||||
|
assert_eq!(
|
||||||
|
PackageMetadata::try_from(misleading_character_example),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_problem_invalid_fragment_not_a_roc_file() {
|
||||||
|
let expected = Err(UrlProblem::InvalidFragment("filename.sh".to_string()));
|
||||||
|
assert_eq!(
|
||||||
|
PackageMetadata::try_from("https://example.com/#filename.sh"),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_problem_invalid_fragment_empty_roc_filename() {
|
||||||
|
let expected = Err(UrlProblem::InvalidFragment(".roc".to_string()));
|
||||||
|
assert_eq!(
|
||||||
|
PackageMetadata::try_from("https://example.com/#.roc"),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_problem_not_a_tar_url() {
|
||||||
|
let expected = Err(UrlProblem::MissingTarExt);
|
||||||
|
assert_eq!(
|
||||||
|
PackageMetadata::try_from("https://example.com/filename.zip"),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_problem_invalid_tar_suffix() {
|
||||||
|
let expected = Err(UrlProblem::InvalidExtensionSuffix(".zip".to_string()));
|
||||||
|
assert_eq!(
|
||||||
|
PackageMetadata::try_from("https://example.com/filename.tar.zip"),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_problem_missing_hash() {
|
||||||
|
let expected = Err(UrlProblem::MissingHash);
|
||||||
|
assert_eq!(
|
||||||
|
PackageMetadata::try_from("https://example.com/.tar.gz"),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_without_fragment() {
|
||||||
|
let expected = Ok(PackageMetadata {
|
||||||
|
cache_subdir: "example.com/path",
|
||||||
|
content_hash: "hash",
|
||||||
|
root_module_filename: None,
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
PackageMetadata::try_from("https://example.com/path/hash.tar.gz"),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_with_fragment() {
|
||||||
|
let expected = Ok(PackageMetadata {
|
||||||
|
cache_subdir: "example.com/path",
|
||||||
|
content_hash: "hash",
|
||||||
|
root_module_filename: Some("filename.roc"),
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
PackageMetadata::try_from("https://example.com/path/hash.tar.gz#filename.roc"),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Problem {
|
pub enum Problem {
|
||||||
UnsupportedEncoding(String),
|
UnsupportedEncoding(String),
|
||||||
|
@ -656,49 +656,11 @@ nextNodeId = \rendered ->
|
|||||||
eqRenderedTree : RenderedTree state, RenderedTree state -> Bool
|
eqRenderedTree : RenderedTree state, RenderedTree state -> Bool
|
||||||
eqRenderedTree = \a, b ->
|
eqRenderedTree = \a, b ->
|
||||||
(a.root == b.root)
|
(a.root == b.root)
|
||||||
&& eqRenderedNodes a.nodes b.nodes
|
&& (a.nodes == b.nodes)
|
||||||
&& (List.len a.handlers == List.len b.handlers)
|
&& (List.len a.handlers == List.len b.handlers)
|
||||||
&& (a.deletedNodeCache == b.deletedNodeCache)
|
&& (a.deletedNodeCache == b.deletedNodeCache)
|
||||||
&& (a.deletedHandlerCache == b.deletedHandlerCache)
|
&& (a.deletedHandlerCache == b.deletedHandlerCache)
|
||||||
|
|
||||||
eqRenderedNodes : List (Result RenderedNode [DeletedNode]), List (Result RenderedNode [DeletedNode]) -> Bool
|
|
||||||
eqRenderedNodes = \a, b ->
|
|
||||||
List.map2 a b Tuple
|
|
||||||
|> List.all
|
|
||||||
(\t ->
|
|
||||||
when t is
|
|
||||||
Tuple (Ok x) (Ok y) -> eqRenderedNode x y
|
|
||||||
Tuple (Err x) (Err y) -> x == y
|
|
||||||
_ -> Bool.false)
|
|
||||||
|
|
||||||
eqRenderedNode : RenderedNode, RenderedNode -> Bool
|
|
||||||
eqRenderedNode = \a, b ->
|
|
||||||
when { a, b } is
|
|
||||||
{ a: RenderedNone, b: RenderedNone } ->
|
|
||||||
Bool.true
|
|
||||||
|
|
||||||
{ a: RenderedText aStr, b: RenderedText bStr } ->
|
|
||||||
aStr == bStr
|
|
||||||
|
|
||||||
{ a: RenderedElement aName aAttrs aChildren, b: RenderedElement bName bAttrs bChildren } ->
|
|
||||||
(aName == bName)
|
|
||||||
&& (aChildren == bChildren) # good enough for testing!
|
|
||||||
&& eqRenderedAttrs aAttrs bAttrs
|
|
||||||
|
|
||||||
_ -> Bool.false
|
|
||||||
|
|
||||||
eqRenderedAttrs : RenderedAttributes, RenderedAttributes -> Bool
|
|
||||||
eqRenderedAttrs = \a, b ->
|
|
||||||
eqAttrDict a.eventListeners b.eventListeners
|
|
||||||
&& eqAttrDict a.htmlAttrs b.htmlAttrs
|
|
||||||
&& eqAttrDict a.domProps b.domProps
|
|
||||||
&& eqAttrDict a.styles b.styles
|
|
||||||
|
|
||||||
eqAttrDict : Dict Str v, Dict Str v -> Bool | v implements Eq
|
|
||||||
eqAttrDict = \a, b ->
|
|
||||||
Dict.keys a
|
|
||||||
|> List.all \k -> Dict.get a k == Dict.get b k
|
|
||||||
|
|
||||||
# indexNodes
|
# indexNodes
|
||||||
expect
|
expect
|
||||||
html : Html {}
|
html : Html {}
|
||||||
@ -713,12 +675,12 @@ expect
|
|||||||
expected = {
|
expected = {
|
||||||
nodes: [
|
nodes: [
|
||||||
RenderedText "Roc",
|
RenderedText "Roc",
|
||||||
RenderedElement "a" { emptyRenderedAttrs & htmlAttrs: Dict.fromList [T "href" "https://www.roc-lang.org/"] } [0],
|
RenderedElement "a" { emptyRenderedAttrs & htmlAttrs: Dict.fromList [("href", "https://www.roc-lang.org/")] } [0],
|
||||||
],
|
],
|
||||||
siblingIds: [1],
|
siblingIds: [1],
|
||||||
}
|
}
|
||||||
|
|
||||||
(List.map2 actual.nodes expected.nodes eqRenderedNode |> List.walk Bool.true Bool.and)
|
(actual.nodes == expected.nodes)
|
||||||
&& (actual.siblingIds == expected.siblingIds)
|
&& (actual.siblingIds == expected.siblingIds)
|
||||||
|
|
||||||
# diff
|
# diff
|
||||||
@ -822,7 +784,7 @@ expect
|
|||||||
Ok (RenderedText "The app"),
|
Ok (RenderedText "The app"),
|
||||||
Ok (RenderedElement "h1" emptyRenderedAttrs [0]),
|
Ok (RenderedElement "h1" emptyRenderedAttrs [0]),
|
||||||
Ok (RenderedText "The answer is 42"),
|
Ok (RenderedText "The answer is 42"),
|
||||||
Ok (RenderedElement "div" { emptyRenderedAttrs & eventListeners: Dict.fromList [T "click" { accessors: [], handlerId: 0 }] } [2]),
|
Ok (RenderedElement "div" { emptyRenderedAttrs & eventListeners: Dict.fromList [("click", { accessors: [], handlerId: 0 })] } [2]),
|
||||||
Ok (RenderedElement "body" emptyRenderedAttrs [1, 3]),
|
Ok (RenderedElement "body" emptyRenderedAttrs [1, 3]),
|
||||||
],
|
],
|
||||||
deletedNodeCache: [],
|
deletedNodeCache: [],
|
||||||
|
Loading…
Reference in New Issue
Block a user