Compare commits

...

332 Commits

Author SHA1 Message Date
David Mak 847615fc2f core: Implement numpy.matmul for 2D-2D ndarrays 2024-04-23 10:27:37 +08:00
David Mak 5dfcc63978 core/classes: Take reference of indexes 2024-04-16 17:20:24 +08:00
David Mak 025b3cd02f core/stmt: Remove gen_if_chained*
Turns out it is really difficult to get lifetimes and closures right, so
let's just provide the most rudimentary if-else codegen and we can nest
them if necessary.
2024-04-16 17:16:50 +08:00
David Mak e0f440040c core/expr: Implement negative indices for ndarray 2024-04-15 12:49:42 +08:00
David Mak f0715e2b6d core/stmt: Add gen_if* functions
For generating if-constructs in IR.
2024-04-15 12:20:34 +08:00
David Mak e7fca67786 core/stmt: Do not generate jumps if bb is already terminated
Future-proofs gen_*_callback functions in case other codegen functions
will delegate to it in the future.
2024-04-15 12:20:34 +08:00
David Mak 52c731c312 core: Implement Not/UAdd/USub for booleans
Not sure if this is deliberate or an oversight, but we implement it
anyway for consistency with other Python implementations.
2024-04-12 18:29:58 +08:00
David Mak 00d1b9be9b core: Fix __inv__ for i8-based boolean operands 2024-04-12 15:35:54 +08:00
David Mak 8404d4c4dc meta: Update dependencies 2024-04-12 15:29:09 +08:00
David Mak e614dd4257 core/type_inferencer: Fix location of unary/compare expressions
Codegen uses this location information to determine the CallId, and if
a function call is the operand of a unary expression or left-hand
operand of a compare expression, codegen will use the type of the
operator expression rather than the actual operand type.
2024-04-05 15:42:10 +08:00
David Mak 937a8b9698 core/magic_methods: Fix type of unary ops with primitive types 2024-04-05 13:23:08 +08:00
David Mak 876ad6c59c core/type_inferencer: Include location info if inferencer fails 2024-04-05 13:22:35 +08:00
David Mak a920fe0501 core: Implement elementwise comparison operators 2024-04-03 00:07:33 +08:00
David Mak 727a1886b3 core: Implement elementwise unary operators 2024-04-03 00:07:33 +08:00
David Mak 6af13a8261 core: Implement elementwise binary operators
Including immediate variants of these operators.
2024-04-03 00:07:33 +08:00
David Mak 3540d0ab29 core/magic_methods: Add typeof_*op
Used to determine the expected type of the binary operator with
primitive operands.
2024-04-03 00:07:33 +08:00
David Mak 3a6c53d760 core/toplevel/numpy: Split ndarray type var utilities 2024-04-03 00:07:33 +08:00
David Mak 87bc34f7ec core: Implement calculations for broadcasting ndarrays 2024-04-03 00:07:31 +08:00
David Mak f50a5f0345 core/type_inferencer: Allow both int32 and isize when indexing ndarray 2024-04-02 16:49:12 +08:00
David Mak a77fd213e0 core/magic_methods: Allow unknown return types
These types can be later inferred by the type inferencer.
2024-04-02 16:49:12 +08:00
David Mak 8f1497df83 core/helper: Add PrimitiveDefinitionIds::iter 2024-04-02 16:49:12 +08:00
David Mak 5ca2dbeec8 core/typedef: Add Type::obj_id to replace get_obj_id 2024-04-02 16:49:10 +08:00
David Mak 9a98cde595 core: Extract codegen portion of gen_*op_expr
This allows *ops to be generated internally using LLVM values as
input. Required in a future change.
2024-04-01 16:48:25 +08:00
David Mak 5ba8601b39 core: Remove ArrayValue variants of functions
These will be lowered and optimized away later anyways, and we have
ArrayLikeAccessor now.
2024-04-01 16:48:25 +08:00
David Mak 26a01b14d5 core: Use more typed slices in APIs 2024-04-01 16:48:25 +08:00
David Mak d5f4817134 core/builtins: Fix len() on ndarrays 2024-04-01 16:48:24 +08:00
David Mak 789bfb5a26 core: Fix index-based operations not returning i32 2024-04-01 16:46:45 +08:00
David Mak 4bb0e60981 core: Apply clippy suggestions 2024-04-01 16:46:41 +08:00
Sebastien Bourdeauducq 623fcf85af msys2: update 2024-03-25 14:45:36 +08:00
David Mak 13f06f3e29 core: Refactor VarMap to IndexMap
This is the only Map I can find that preserves insertion order while
also deduplicating elements by key.
2024-03-22 15:51:23 +08:00
David Mak f0da9c0283 core: Add ArrayLikeValue
For exposing LLVM values that can be accessed like an array.
2024-03-22 15:51:06 +08:00
David Mak 2c4bf3ce59 core: Allow unsized CodeGenerator to be passed to some codegen functions
Enables codegen_callback to call these codegen functions as well.
2024-03-22 15:07:28 +08:00
David Mak e980f19c93 core: Simplify typed value assertions 2024-03-22 15:07:28 +08:00
David Mak cfbc37c1ed core: Add gen_for_callback_incrementing
Simplifies generation of monotonically increasing for loops.
2024-03-22 15:07:28 +08:00
David Mak 50264e8750 core: Add missing unchecked accessors for NDArrayDimsProxy 2024-03-22 15:07:28 +08:00
David Mak 1b77e62901 core: Split numpy into codegen and toplevel 2024-03-22 15:07:28 +08:00
David Mak fd44ee6887 core: Apply clippy suggestions 2024-03-22 15:07:23 +08:00
David Mak c8866b1534 core/classes: Rename get_* functions to remove prefix
As suggested by Rust API Guidelines.
2024-03-21 15:46:10 +08:00
David Mak 84a888758a core: Rename unsafe functions to unchecked
This is this intended name of the functions.
2024-03-21 15:46:10 +08:00
David Mak 9d550725b7 meta: Update cargo dependencies 2024-03-21 15:45:26 +08:00
David Mak 2edc1de0b6 standalone: Update ndarray.py to output all elements in ndarrays 2024-03-07 14:59:13 +08:00
David Mak c3b122acfc core: Implement `ndarray.copy` 2024-03-07 14:59:13 +08:00
David Mak a94927a11d core: Update __builtin_assume expressions
No dimension size should be 0.
2024-03-07 14:59:13 +08:00
David Mak ebf86cd134 core: Use size_t for accessing array elements 2024-03-07 14:59:13 +08:00
David Mak cccd8f2d00 core: Fix ndarray_eye not preserving signness of offset 2024-03-07 14:59:13 +08:00
David Mak 3292aed099 core: Fix ndarray subscript operator returning the wrong object
Should be returning the newly created object instead of the original
ndarray...
2024-03-07 14:59:13 +08:00
David Mak 96b7f29679 core: Implement `ndarray.fill` 2024-03-07 14:59:13 +08:00
David Mak 3d2abf73c8 core: Replace ndarray_init_dims IRRT impl with IR impl
Implementation of that function in IR allows for more flexibility in
terms of different integer type widths.
2024-03-07 14:59:13 +08:00
David Mak f682e9bf7a core: Match IRRT compile flavor with build profile 2024-03-07 14:59:02 +08:00
David Mak b26cb2b360 core: Express member func def IDs as offsets from class def ID 2024-03-06 12:24:39 +08:00
David Mak 2317516cf6 core: Use tvars from ndarray for class definition 2024-03-04 23:58:02 +08:00
David Mak 77de24ef74 core: Use BTreeMap for type variable mapping
There have been multiple instances where I had the need to iterate over
type variables, only to discover that the traversal order is arbitrary.

This commit fixes that by adding SortedMapping, which utilizes BTreeMap
internally to guarantee a traversal order. All instances of VarMap are
now refactored to use this to ensure that type variables are iterated in
 the order of its variable ID, which should be monotonically incremented
 by the unifier.
2024-03-04 23:56:04 +08:00
David Mak 234a6bde2a core: Use TObj for NDArray 2024-03-01 15:41:55 +08:00
David Mak c3db6297d9 core: Add primitive definition-id list
So that we have a single ground truth for the definition IDs of
primitive types.
2024-03-01 11:29:10 +08:00
David Mak 82fdb02d13 core: Extract LLVM intrinsic functions to their functions 2024-02-23 15:41:06 +08:00
David Mak 4efdd17513 core: Add missing From implementations for LLVM wrapper classes 2024-02-23 15:41:06 +08:00
David Mak 49de81ef1e core: Apply clippy suggestions 2024-02-23 15:41:06 +08:00
David Mak 8492503af2 core: Update cargo dependencies 2024-02-23 15:41:04 +08:00
Sebastien Bourdeauducq e1dbe2526a flake: switch to nixpkgs unstable for newer rustc 2024-02-20 15:46:51 +08:00
Sebastien Bourdeauducq f37de381ce update dependencies 2024-02-20 13:33:20 +08:00
Sebastien Bourdeauducq 4452c8986a update ARTIQ version used for PGO profiling 2024-02-20 13:29:00 +08:00
David Mak 22e831cb76 core: Add test for indexing into ndarray 2024-02-19 17:13:10 +08:00
David Mak cc538d221a core: Implement codegen for indexing into ndarray 2024-02-19 17:13:09 +08:00
David Mak 0d5c53e60c core: Implement type inference for indexing into ndarray 2024-02-19 17:13:09 +08:00
David Mak 976a9512c1 core: Add const variants to NDArray element getters 2024-02-19 16:56:21 +08:00
David Mak 1eacaf9afa core: Fix IRRT argument order to ndarray_flatten_index 2024-02-19 16:37:13 +08:00
David Mak 8c7e44098a core: Fix IRRT implementation of ndarray_flatten_index 2024-02-19 16:37:13 +08:00
David Mak 282a3e1911 core: Fix typo in error message 2024-02-14 16:26:13 +08:00
David Mak 5cecb2bb74 core: Fix Literal use in variable type annotation 2024-02-06 18:16:14 +08:00
David Mak 1963c30744 core: Use Display output for locations 2024-02-06 18:11:51 +08:00
David Mak 27011f385b core: Add location to non-primitive value return error 2024-02-02 12:49:21 +08:00
David Mak d6302b6ec8 core: Allow tuple of primitives to be returned 2024-02-02 12:48:52 +08:00
David Mak fef4b2a5ce standalone: Disable tests requiring return of non-primitive values 2024-01-29 12:49:50 +08:00
David Mak b3736c3e99 core: Disallow returning of non-primitive values
Non-primitive values are represented by an `alloca`-ed value in the
function body, and when the pointer is returned from the function, the
`alloca`-ed object is deallocated on the stack.

Related to #54.
2024-01-29 12:49:24 +08:00
Sebastien Bourdeauducq e328e44c9a update MSYS2 2024-01-26 15:55:45 +08:00
Sebastien Bourdeauducq 9e4e90f8a0 update dependencies 2024-01-26 15:52:48 +08:00
David Mak 8470915809 core: Add NDArrayValue and helper functions 2024-01-25 15:51:39 +08:00
David Mak 148900302e core: Add RangeValue and helper functions 2024-01-25 15:51:39 +08:00
David Mak 5ee08b585f core: Add ListValue and helper functions 2024-01-25 15:51:39 +08:00
David Mak f1581299fc core: Minor changes to IRRT
Add missing documentation, remove redundant lifetime variables, and fix
typos.
2024-01-25 15:50:53 +08:00
David Mak af95ba5012 standalone: Add debug flag to run_demo.sh
Allows running demos using the debug build instead of the (default)
release build.
2024-01-25 15:50:53 +08:00
David Mak 9c9756be33 standalone: Use size_t in demo.c 2024-01-25 15:50:53 +08:00
David Mak 2a922c7480 artiq: Fix source module of NDArray
Should be `numpy.typing` instead of `numpy`.
2024-01-17 10:40:08 +08:00
David Mak e3e2c36ef4 core: Mark TNDArray and TLiteral as unimplemented in tests 2024-01-17 09:58:14 +08:00
David Mak 4f9a0110c4 meta: Update insta snapshots 2024-01-17 09:49:50 +08:00
David Mak 12c0eed0a3 core: Fix compilation of tests 2024-01-17 09:49:49 +08:00
David Mak c679474f5c standalone: Fix redefinition of ndarray consumer functions 2024-01-17 09:38:13 +08:00
Sébastien Bourdeauducq ab3fa05996 demo: use portable format strings 2024-01-10 18:35:35 +08:00
David Mak 140f8f8a08 core: Implement most ndarray-creation functions 2023-12-22 16:29:55 +08:00
David Mak 27fcf8926e core: Implement ndarray constructor and numpy.empty 2023-12-22 16:29:54 +08:00
David Mak afa7d9b100 core: Implement helper for creation of generic ndarray 2023-12-21 15:39:49 +08:00
David Mak c395472094 core: Initial infrastructure for ndarray 2023-12-21 15:39:46 +08:00
David Mak 03870f222d core: Extract special method handling in type inferencer
To prepare for more special handling with methods.
2023-12-21 15:38:26 +08:00
David Mak e435b25756 core: Allow implicit promotions of integral literals
It should not matter, since it is the value of the literal that matters
with respect to the const generic variable.
2023-12-21 15:21:08 +08:00
David Mak bd792904f9 core: Add size_t to primitive store
Used for ndims in ndarray.
2023-12-21 15:20:31 +08:00
David Mak 1c3a823670 core: Do not discard value names for IRRT 2023-12-20 15:16:02 +08:00
David Mak f01d833d48 standalone: Add missing parenthesis 2023-12-20 15:15:47 +08:00
David Mak 9d64e606f4 core: Reject multiple literal bounds
This is currently broken due to how we handle function calls in the
unifier.
2023-12-18 10:04:25 +08:00
David Mak 6dccb343bb Revert "core: Do not keep unification result for function arguments"
This reverts commit f09f3c27a5.
2023-12-18 10:01:23 +08:00
Sebastien Bourdeauducq d47534e2ad interpret_demo: add typing.Literal 2023-12-18 08:50:49 +08:00
David Mak 8886964776 core: Remove redundant argument in type annotation parsing 2023-12-16 18:40:48 +08:00
David Mak f09f3c27a5 core: Do not keep unification result for function arguments
For some reason, when unifying a function call parameter with an
argument, subsequent calls to the same function will only accept the
type of the substituted argument.

This affect snippets like:

```
def make1() -> C[Literal[1]]:
    return ...

def make2() -> C[Literal[2]]:
    return ...

def consume(instance: C[Literal[1, 2]]):
    pass

consume(make1())
consume(make2())
```

The last statement will result in a compiler error, as the parameter of
consume is replaced with C[Literal[1]].

We fix this by getting a snapshot before performing unification, and
restoring the snapshot after unification succeeds.
2023-12-16 18:40:48 +08:00
David Mak 0bbc9ce6f5 core: Deduplicate values in `Literal`
Matches the behavior with `typing.Literal`.
2023-12-16 18:40:48 +08:00
David Mak 457d3b6cd7 core: Refactor generic constants to `Literal`
Better matches the syntax of `typing.Literal`.
2023-12-16 18:40:48 +08:00
David Mak 5f692debd8 core: Add PrimitiveStore into Unifier
This will be used during unification between a const generic variable
and a `Literal`.
2023-12-16 18:40:48 +08:00
David Mak c7735d935b standalone: Output id of undefined identifier 2023-12-16 18:40:48 +08:00
David Mak b47ac1b89b core: Minor formatting cleanup 2023-12-15 17:46:44 +08:00
David Mak a19f1065e3 meta: Refactor to use more let-else bindings 2023-12-12 16:31:14 +08:00
Sebastien Bourdeauducq 5bf05c6a69 update ARTIQ version used for PGO profiling 2023-12-12 15:57:48 +08:00
David Mak 32746c37be core: Refactor to return errors by HashSet 2023-12-12 15:41:59 +08:00
David Mak 1d6291b9ba ast: Add Ord implementation to Location 2023-12-12 15:41:59 +08:00
David Mak 16655959f2 meta: Update cargo dependencies 2023-12-12 15:41:59 +08:00
David Mak beee3e1f7e artiq: Pass artiq builtins to NAC3 constructor 2023-12-12 11:28:03 +08:00
David Mak d4c109b6ef core: Add missing generic constant concrete type 2023-12-12 11:28:01 +08:00
David Mak 5ffd06dd61 core: Remove debugging statement 2023-12-12 11:23:51 +08:00
David Mak 95d0c3c93c artiq: Rename const_generic_dummy to const_generic_marker 2023-12-12 11:23:51 +08:00
David Mak bd3d67f3d6 artiq: Apply clippy pedantic changes 2023-12-11 15:16:23 +08:00
David Mak ddfb532b80 standalone: Apply clippy pedantic changes 2023-12-11 15:16:23 +08:00
David Mak 02933753ca core: Apply clippy pedantic changes 2023-12-11 15:16:23 +08:00
David Mak a1f244834f meta: Bringup some documentation 2023-12-11 15:16:23 +08:00
David Mak d304afd333 meta: Apply clippy suggested changes 2023-12-11 15:16:23 +08:00
David Mak ef04696b02 meta: Lift return out of conditional statement 2023-12-11 15:16:23 +08:00
David Mak 4dc5dbb856 meta: Replace equality assertion with assert_eq
Emits a more useful assertion message.
2023-12-11 15:16:23 +08:00
David Mak fd9f66b8d9 meta: Remove redundant casts and brackets 2023-12-11 15:16:23 +08:00
David Mak 5182453bd9 meta: Remove redundant path prefixes 2023-12-11 15:16:23 +08:00
Sebastien Bourdeauducq 68556da5fd update ARTIQ version used for PGO profiling 2023-12-11 09:37:03 +08:00
David Mak 983f080ea7 artiq: Implement handling for const generic variables 2023-12-08 18:02:14 +08:00
David Mak 031e660f18 core: Initial implementation for const generics 2023-12-08 18:02:11 +08:00
David Mak b6dfcfcc38 core: Move some SymbolValue functions to symbol_resolver.rs 2023-12-08 18:00:51 +08:00
David Mak c93ad152d7 core: Codegen for ellipsis expression as NotImplemented
A lot of refactoring was performed, specifically with relaxing
expression codegen to return Option in case where ellipsis are used
within a subexpression.
2023-12-08 18:00:51 +08:00
David Mak 68b97347b1 core: Infer builtins name list using builtin declaration list 2023-12-08 17:29:34 +08:00
David Mak 875d534de4 ast: Use `{filename}:{row}:{col}` for location output 2023-12-08 15:48:54 +08:00
Sebastien Bourdeauducq adadf56e2b nac3standalone: generate PIC 2023-12-04 19:09:50 +08:00
Sebastien Bourdeauducq 9f610745b7 cargo: update dependencies 2023-12-04 18:51:06 +08:00
Sebastien Bourdeauducq 98199768e3 demo: fix 64-bit format strings 2023-12-04 18:51:06 +08:00
Sebastien Bourdeauducq bfa9ceaae3 switch to new nixpkgs release 2023-12-03 10:31:05 +08:00
Sebastien Bourdeauducq 120f8da5c7 fix compilation warnings 2023-11-26 09:09:24 +08:00
Sebastien Bourdeauducq cee62aa6c5 pin down LLVM used for IRRT 2023-11-25 20:15:29 +08:00
Sebastien Bourdeauducq fcda360ad6 flake: update dependencies 2023-11-24 18:11:25 +08:00
Sebastien Bourdeauducq 87c20ada48 windows: switch to CLANG64 MSYS2
For compatibility with MSVC (Anaconda and others).
2023-11-24 18:10:00 +08:00
Sebastien Bourdeauducq 38e968cff6 gitignore: fix msys2 path 2023-11-24 17:18:17 +08:00
David Mak 5c5620692f core: Add np_{round,floor,ceil}
These functions are NumPy variants of round/floor/ceil, which returns
floats instead of ints.
2023-11-23 13:45:07 +08:00
David Mak 0af1e37e99 core: Prefix all NumPy/SciPy functions with np_/sp_spec 2023-11-23 13:35:23 +08:00
David Mak 854e33ed48 meta: Update cargo dependencies 2023-11-23 13:31:24 +08:00
Sebastien Bourdeauducq f020d61cbb update ARTIQ version used for PGO profiling 2023-11-11 11:10:58 +08:00
David Mak 10538b5296 core: Update insta snapshots 2023-11-09 13:00:27 +08:00
David Mak d322c91697 core: Change bitshift operators to accept int32/uint32 for RHS operand 2023-11-09 12:16:20 +08:00
David Mak 3231eb0d78 core: Add compile-time error and runtime assertion for negative shifts 2023-11-09 12:16:20 +08:00
Sebastien Bourdeauducq 1ca4de99b9 update ARTIQ version used for PGO profiling 2023-11-08 17:29:29 +08:00
Sebastien Bourdeauducq bf4b1aae47 update dependencies 2023-11-08 17:23:49 +08:00
David Mak 08a5050f9a core: Implement non-trivial builtin functions using IRRT 2023-11-06 12:57:23 +08:00
David Mak c2ab6b58ff artiq: Implement `with legacy_parallel` block 2023-11-04 13:42:44 +08:00
David Mak 0a84f7ac31 Add CodeGenerator::gen_block and refactor to use it 2023-11-04 13:42:44 +08:00
David Mak fd787ca3f5 core: Remove trunc
The behavior of trunc is already implemented by casts and is therefore
redundant.
2023-11-04 13:35:53 +08:00
David Mak 4dbe07a0c0 core: Revert breaking changes to round-family functions
These functions should return ints as the math.* functions do instead of
following the convention of numpy.* functions.
2023-11-04 13:35:53 +08:00
David Mak 2e055e8ab1 core: Replace rint implementation with LLVM intrinsic 2023-11-04 13:35:53 +08:00
David Mak 9d737743c1 standalone: Add regression test for numeric primitive operations 2023-11-03 16:24:26 +08:00
David Mak c6b9aefe00 core: Fix int32-to-uint64 conversion
This conversion should be sign-extended.
2023-11-03 16:24:26 +08:00
David Mak 8ad09748d0 core: Fix conversion from float to unsigned types
These conversions also need to wraparound.
2023-11-03 16:24:26 +08:00
David Mak 7a5a2db842 core: Fix handling of float-to-int32 casts
Out-of-bound conversions should be wrapped around.
2023-11-03 16:24:26 +08:00
David Mak 447eb9c387 standalone: Fix output format string for output_uint* 2023-11-03 16:24:26 +08:00
David Mak 92d6f0a5d3 core: Implement bitwise not for unsigned ints and fix implementation 2023-11-03 16:24:26 +08:00
David Mak 7e4dab15ae standalone: Add math tests for non-number arguments 2023-11-01 18:03:29 +08:00
David Mak ff1fed112c core: Rework gamma/gammaln to match SciPy behavior
Matches behavior for infinities and NaNs.
2023-11-01 18:03:29 +08:00
David Mak 36a6a7b8cd core: Replace TopLevelDef comments with documentation 2023-11-01 18:03:29 +08:00
David Mak 2b635a0b97 core: Implement numpy and scipy functions 2023-11-01 18:03:29 +08:00
David Mak 60ad100fbb core: Implement and expose {isinf,isnan} 2023-11-01 18:03:29 +08:00
David Mak 316f0824d8 flake: Add scipy 2023-11-01 18:03:29 +08:00
David Mak 7cf7634985 core: Add create_fn_by_* functions
Used for abstracting the creation of function from different sources.
2023-11-01 18:03:29 +08:00
David Mak 068f0d9faf core: Do not cast floor/ceil result to int
NumPy explicitly states that the return type of the floor/ceil is float.
2023-11-01 18:03:29 +08:00
David Mak 95810d4229 core: Remove {ceil64,floor64,round,round64}
These are not present in NumPy or Artiq.
2023-11-01 18:03:29 +08:00
David Mak 630897b779 standalone: Do not output sign if float is NaN
Matches behavior in Python.
2023-11-01 18:03:29 +08:00
Sebastien Bourdeauducq e546535df0 flake: update nixpkgs 2023-11-01 15:53:47 +08:00
David Mak 352f70b885 artiq: Update host exception list to match possibly thrown types 2023-11-01 13:28:48 +08:00
David Mak e95586f61e core: Fix IR generation of `for` loop containing break/continue
Fix cases where the body BB would have two terminators because of a
preceding continue/break statement already emitting a terminator.
2023-11-01 13:21:27 +08:00
David Mak bb27e3d400 standalone: Fix indentation of demo.c 2023-11-01 13:20:26 +08:00
David Mak bb5147521f standalone: Fix indentation of test files 2023-11-01 13:20:26 +08:00
David Mak 9518d3fe14 artiq: Fix timeline not resetting upon exiting sequential block 2023-10-30 14:04:53 +08:00
David Mak cbd333ab10 artiq: Extract `parallel` block timeline utilities 2023-10-30 14:04:53 +08:00
David Mak 65d6104d00 artiq: Improve IR value naming and add documentation 2023-10-30 14:04:53 +08:00
David Mak 8373a6cb0f artiq: Use gen_block when generating "with sequential" 2023-10-30 14:04:53 +08:00
David Mak f75ae78677 cargo: Update dependencies 2023-10-30 14:04:53 +08:00
Sebastien Bourdeauducq ea2ab0ef7c update nixpkgs, python 3.11 2023-10-25 21:09:22 +08:00
David Mak e49b760e34 ld: Support multiple CFIs with different encoding in .eh_frame
We now parse each CFI to read its encoding as opposed to assuming that
all CFIs within the same EH_Frame uses the same encoding. FDEs are now
iterated in a per-CFI manner.
2023-10-20 18:15:03 +08:00
David Mak aa92778363 ld: Fix remapping of FDEs with multiple CFIs 2023-10-20 18:14:27 +08:00
David Mak e1487ed335 cargo: Update dependencies 2023-10-20 18:11:45 +08:00
David Mak 73500c9081 core: Remove lazy_static from dependencies 2023-10-16 15:55:10 +08:00
David Mak 9ca34c714e flake: Enable thread-safe mode for LLVM
This is required as we use the LLVM APIs from multiple threads.
2023-10-16 15:55:10 +08:00
David Mak 7fc2a30c14 Force single-threaded compilation if LLVM is not thread-safe 2023-10-16 15:55:10 +08:00
David Mak 950f431483 standalone: Update help text for `--emit-llvm` 2023-10-16 15:52:51 +08:00
David Mak a50c690428 standalone: Fix run_demo script
- Link main and module*.bc together if using multiple threads
- Fix temporary files not being deleted
2023-10-16 15:52:48 +08:00
David Mak 48eb64403f standalone: Treat -T0 as using all available threads 2023-10-13 14:57:16 +08:00
David Mak 2c44b58bb8 standalone: Require use of `-T` for specifying thread count 2023-10-13 14:36:34 +08:00
David Mak 50230e61f3 core: Simplify loop condition check for list comprehension 2023-10-06 12:24:03 +08:00
David Mak 0205161e35 core: Simplify list creation for comprehension 2023-10-06 12:22:38 +08:00
David Mak a2fce49b26 core: Allocate exceptions at the beginning of function
Only one instance of exception is necessary, as exceptions will always
be initialized before being thrown.
2023-10-06 12:13:20 +08:00
David Mak 60a503a791 core: Allocate more stack variables at the beginning of function
All allocas for temporary objects are now placed in the beginning of the
function. Allocas for on-temporary objects are not modified because
these variables may appear in a loop and thus must be uniquely
allocated by different allocas.
2023-10-06 11:42:47 +08:00
David Mak 0c49b30a90 core: Restore debug info before function call is invoked
Previously, the IR which sets up the call to the target function will
have its debug location pointing at the last argument of the function
call instead of the function call itself.
2023-10-06 11:35:23 +08:00
David Mak c7de22287e core: Fix restoration of stack address
All allocas for temporary objects are now placed in the beginning of the
function. Allocas for on-temporary objects are not modified because
these variables may appear in a loop and thus must be uniquely
represented.
2023-10-06 11:34:23 +08:00
David Mak 1a54aaa1c0 core: Restore debug location when generating allocas
Debug location is lost when moving the builder cursor.
2023-10-06 11:11:50 +08:00
David Mak c5629d4eb5 standalone: Remove redundant const in demo library 2023-10-06 10:32:58 +08:00
David Mak a79286113e standalone: Add output_bool in demo library 2023-10-06 10:19:22 +08:00
Sebastien Bourdeauducq 901e921e00 windows: fix build 2023-10-05 18:02:53 +08:00
Sebastien Bourdeauducq 45a323e969 windows: update msys2 packages 2023-10-05 17:52:29 +08:00
Sebastien Bourdeauducq 11759a722f flake: fix pgo build 2023-10-05 17:38:36 +08:00
David Mak 480a4bc0ad core: Implement comparison operators for unsigned types 2023-10-05 17:13:10 +08:00
Sebastien Bourdeauducq a1d3093196 flake: update dependencies 2023-10-05 17:05:57 +08:00
Sebastien Bourdeauducq 85c5f2c044 cargo: update dependencies 2023-10-05 17:03:35 +08:00
David Mak f34c6053d6 standalone: Add flags to control demo output options 2023-10-04 18:11:44 +08:00
David Mak e8a5f0dfef standalone: Fix parsing NAC3 args in check_demo.sh 2023-10-04 18:03:28 +08:00
David Mak 7140901261 standalone: Fix missing libraries when linking
Fixes `undefined reference to 'pow'` for pow.py using -O0.
2023-10-04 18:03:28 +08:00
David Mak 2a775d822e core: Demote dead code into a stdout warning 2023-10-04 18:03:25 +08:00
David Mak 1659c3e724 standalone: Remove temporary logfiles after execution 2023-09-30 09:31:18 +08:00
David Mak f53cb804ec standalone: Add execution of test cases via lli 2023-09-30 09:31:18 +08:00
David Mak 279376a373 standalone: Emit IRRT IR 2023-09-30 09:31:18 +08:00
David Mak b6afd1bfda standalone: Split check_demos into check_demo
Allows individual tests to be executed.
2023-09-30 09:31:18 +08:00
David Mak be3e8f50a2 standalone: Refactor demo library to C
Needed for use by lli.
2023-09-30 09:31:18 +08:00
David Mak 059d3da58b standalone: Add float64 output tests 2023-09-30 09:31:18 +08:00
David Mak 9b28f23d8c flake: Add clang alongside clang-unwrapped 2023-09-30 09:31:18 +08:00
Sebastien Bourdeauducq 119f4d63e9 cargo: update dependencies 2023-09-29 14:46:22 +08:00
Sebastien Bourdeauducq 458fa12788 flake: update dependencies 2023-09-29 14:07:47 +08:00
David Mak 48c6498d1f core: Fix restoration of loop target in try statement
old_loop_target is only assigned if ctx.loop_target is overwritten,
meaning that if ctx.loop_target is never overwritten, ctx.loop_target
will always be overwritten to None.

We fix this by only restoring from old_loop_target if we previously
assigned to old_loop_target.
2023-09-28 20:00:02 +08:00
David Mak 2a38d5160e meta: Respect opt flags when performing whole-module optimization 2023-09-28 19:58:54 +08:00
David Mak b39831b388 standalone: Update demos
- Add `output_str` for printing a string
- Add demo_test.py to test interop
2023-09-28 19:58:53 +08:00
David Mak cb39f61e79 core: Fix passing structure arguments to extern functions
All parameters with a structure type in extern functions are marked as
`byref` instead of `byval`, as most ABIs require the first several
arguments to be passed in registers before spilling into the stack.

`byval` breaks this contract by explicitly requiring all arguments to be
 passed in the stack, breaking interop with libraries written in other
 languages.
2023-09-28 15:02:35 +08:00
David Mak 176f250bdb core: Fix missing conversion to i1 for IfExp 2023-09-28 10:06:40 +08:00
David Mak acdb1de6fe meta: Improve documentation for various modified classes 2023-09-25 15:42:07 +08:00
David Mak 31dcd2dde9 core: Use i8 for boolean variable allocation
In LLVM, i1 represents a 1-byte integer with a single valid bit; The
rest of the 7 upper bits are undefined. This causes problems when
using these variables in memory operations (e.g. memcpy/memmove as
needed by List slicing and assignment).

We fix this by treating all local boolean variables as i8 so that they
are well-defined for memory operations. Function ABIs will continue to
use i1, as memory operations cannot be directly performed on function
arguments or return types, instead they are always converted back into
local boolean variables (which are i8s anyways).

Fixes #315.
2023-09-25 15:42:07 +08:00
David Mak fc93fc2f0e core: Move bitcode verification error message into panic message 2023-09-22 17:16:29 +08:00
David Mak dd42022633 core: Minor refactor allocate_list 2023-09-22 17:16:29 +08:00
David Mak 6dfc43c8b0 core: Add name to build_gep_and_load 2023-09-22 17:16:29 +08:00
David Mak ab2360d7a0 core: Remove emit_llvm from CodeGenLLVMOptions
We instead output an LLVM bitcode file when the option is specified on
the command-line.
2023-09-22 17:16:29 +08:00
David Mak ee1ee4ab3b core: Replace deprecated _ExtInt with _BitInt 2023-09-22 17:16:29 +08:00
David Mak 3e430b9b40 core: Fix missing changes for codegen tests
Apparently the changes were dropped after rebasing.
2023-09-22 17:16:21 +08:00
David Mak 9e57498958 meta: Update dependencies 2023-09-21 09:38:38 +08:00
David Mak 769fd01df8 meta: Allow specifying compiler arguments for check_demos 2023-09-18 11:35:20 +08:00
David Mak 411837cacd artiq: Specify target CPU when creating LLVM target options
We can try to optimize for the host and Cortex-A9 chips; The RISC-V
ISAs do not target specific chips, so we will fallback to using the
generic CPU.
2023-09-18 11:35:20 +08:00
David Mak f59d45805f standalone: Add command line flags for target properties
For testing codegen for different platforms on the host system.
2023-09-18 11:35:20 +08:00
David Mak 048fcb0a69 core: Switch to LLVM New Pass Manager 2023-09-18 11:35:15 +08:00
David Mak 676d07657a core: Add target field to CodeGenLLVMOptions
For specifying the target machine options when optimizing and linking.

This field is currently unused but will be required in a future
commit.
2023-09-18 09:46:24 +08:00
David Mak 2482a1ef9b core: Add CodeGenTargetMachineOptions
Needed in a future commit.
2023-09-18 09:41:49 +08:00
David Mak eb63f2ad48 meta: Update to Rust Edition 2021 2023-09-15 10:25:50 +08:00
Sebastien Bourdeauducq ff27e22ee6 flake: switch back to nixpkgs unstable
Too many issues with python-updates branch for now.
2023-09-13 19:15:47 +08:00
Sebastien Bourdeauducq d672ef094b msys2: update packages, Python 3.11 2023-09-13 09:50:33 +08:00
Sebastien Bourdeauducq d25921230e switch to Python 3.11 2023-09-13 09:44:08 +08:00
Sebastien Bourdeauducq 66f07b5bf4 flake: switch to nixos-unstable 2023-09-12 18:14:39 +08:00
David Mak 008d50995c meta: Update run_demo.sh
- Allow more than one argument to nac3standalone executable
2023-09-12 16:20:50 +08:00
David Mak 474f9050ce standalone: Expose flags in command-line 2023-09-12 16:20:49 +08:00
David Mak 3993a5cf3f core: Add LLVM options to WorkerRegistry 2023-09-12 10:57:05 +08:00
David Mak 39724de598 core: Add CodeGenLLVMOptions
For specifying LLVM options during code generation.
2023-09-12 10:57:04 +08:00
David Mak e4940247f3 standalone: Implement command-line parser using clap
In preparation for adding more command-line options.
2023-09-12 10:08:34 +08:00
David Mak 4481d48709 core: Use C-style for loop logic for iterables
Index increment is now performed at the end of the loop body.
2023-09-06 20:09:38 +08:00
David Mak b4983526bd core: Remove redundant for.cond BB for iterable loops
Simplifies logic for creating basic blocks.
2023-09-06 20:09:37 +08:00
David Mak b4a9616648 core: Add assertion for when `range` has step of 0
Aligns with the behavior in Python.
2023-09-06 20:09:36 +08:00
David Mak e0de82993f core: Preserve value of variable shadowed by for loop
Previously, the final value of the target expression would be one after
the last element of the loop, which does not match Python's behavior.

This commit fixes this problem while also preserving the last assigned
value of the loop beyond the loop, matching Python's behavior.
2023-09-06 20:09:36 +08:00
David Mak 6805253515 core: Use AST var name for IR name
Aids debugging IR.
2023-09-06 20:09:36 +08:00
David Mak 19915bac79 core: Prepend statement type to basic block label names
Aids debugging IR.
2023-09-06 20:09:36 +08:00
David Mak 17b4686260 standalone: Adapt loop example to output loop variable 2023-09-06 18:56:45 +08:00
David Mak 6de0884dc1 core: Use anonymous name for variables if unspecified
The current default prefix is only derived from the instruction type,
which is not helpful during the comprehension of the IR. Changing to
anonymous names (e.g. %1) helps understand that the variable is only
needed as part of a larger (possibly named) expression.
2023-09-06 14:02:15 +08:00
David Mak f1b0e05b3d core: Rename IR variables
Because it is unclear which variables are expressions and
subexpressions, all variables which are previously anonymous are named
using (1) the control flow statement if available, (2) the possible name
of the variable as inferred from the variable name in Rust, and (3) the
"addr" prefix to indicate that the values are pointers. These three
strings are joint together using '.', forming "for.i.addr" for instance.
2023-09-06 14:02:15 +08:00
David Mak ff23968544 core: Add name parameter to gen_{var_alloc,store_target}
This allows variables in the IR to be assigned a custom name as opposed
to names with a default prefix.
2023-09-06 11:00:02 +08:00
Sebastien Bourdeauducq 049908044a flake: update dependencies 2023-09-04 11:00:15 +08:00
David Mak d37287a33d Cargo: Update dependencies 2023-09-04 10:43:57 +08:00
Sebastien Bourdeauducq 283bd7c69a cargo: update dependencies 2023-07-14 10:57:21 +08:00
Sebastien Bourdeauducq 3d73f5c129 flake: update dependencies 2023-07-10 13:46:00 +08:00
Sebastien Bourdeauducq d824c5d8b5 flake: cleanup dev shells 2023-05-30 16:28:46 +08:00
Sebastien Bourdeauducq b8d637f5c4 cargo: update dependencies 2023-05-27 18:56:21 +08:00
Sebastien Bourdeauducq 3af287d1c4 flake: nixpkgs 23.05 2023-05-27 18:14:55 +08:00
Sebastien Bourdeauducq 5b53be0311 update dependencies 2023-04-30 17:11:47 +08:00
Sebastien Bourdeauducq aead36f0fd update dependencies 2023-03-08 15:19:09 +08:00
Sebastien Bourdeauducq c269444c0b msys2: update packages 2023-01-14 16:09:21 +08:00
Sebastien Bourdeauducq 52cec3c12f msys2: nix store doesn't like tildes 2023-01-14 16:09:00 +08:00
Sebastien Bourdeauducq 2927f2a1d0 msys2: adapt to recent pacman 2023-01-14 16:08:39 +08:00
Sebastien Bourdeauducq c1c45373a6 update dependencies 2023-01-12 19:31:03 +08:00
Sebastien Bourdeauducq 946ea155b8 flake: switch to NixOS release 2022-11-30 11:37:48 +08:00
Sebastien Bourdeauducq 085c6ee738 update dependencies 2022-11-18 16:15:46 +08:00
Sebastien Bourdeauducq cfa67c418a update MSYS2 URL 2022-11-03 19:00:44 +08:00
Sebastien Bourdeauducq 813bfa92a7 windows: fix nac3artiq module installation path 2022-08-05 22:42:32 +08:00
Sebastien Bourdeauducq fff4b65169 windows: parallel LLVM build 2022-08-05 18:24:00 +08:00
Sebastien Bourdeauducq c891fffd75 windows: update msys2, python 3.10 2022-08-05 17:27:07 +08:00
Sebastien Bourdeauducq 12acd35e15 switch to nixpkgs master, python 3.10 2022-08-05 17:24:49 +08:00
Sebastien Bourdeauducq f66ca02b2d update Rust dependencies 2022-08-05 16:58:57 +08:00
z78078 b514f91441 nac3artiq: inherit kernel constructors
Closes #139
Co-authored-by: z78078 <cc@m-labs.hk>
Co-committed-by: z78078 <cc@m-labs.hk>
2022-07-28 19:18:36 +08:00
z78078 8f95b79257 nac3artiq: throw error message when constructor use rpc decorator (#306)
Co-authored-by: z78078 <cc@m-labs.hk>
Co-committed-by: z78078 <cc@m-labs.hk>
2022-07-11 15:55:55 +08:00
z78078 ebd25af38b nac3standalone: allow classes without explicit init (#221)
Reviewed-on: M-Labs/nac3#304
Co-authored-by: z78078 <cc@m-labs.hk>
Co-committed-by: z78078 <cc@m-labs.hk>
2022-07-07 10:36:25 +08:00
z78078 96b3a3bf5c work around random segmentation fault (#275)
Reviewed-on: M-Labs/nac3#302
Co-authored-by: z78078 <cc@m-labs.hk>
Co-committed-by: z78078 <cc@m-labs.hk>
2022-07-04 18:06:36 +08:00
ychenfo a18d095245 nac3core: codegen fix call parameter type error 2022-07-04 14:39:33 +08:00
Sebastien Bourdeauducq b242463548 update dependencies 2022-07-02 19:04:19 +08:00
Sebastien Bourdeauducq 8e6e4d6715 README: call for Nix 2.8 (older versions have flake bugs) 2022-06-06 18:14:21 +08:00
Sebastien Bourdeauducq 73c2aefe4b README: mention nac3ld 2022-06-06 18:13:21 +08:00
Sebastien Bourdeauducq 892597cda4 update dependencies 2022-06-06 17:54:23 +08:00
Sebastien Bourdeauducq 33321c5e9c README,nix: remove lld 2022-06-06 17:50:32 +08:00
occheung 50ed04b787 nac3ld: replace unsafe code 2022-06-06 14:41:14 +08:00
occheung 7cb9be0f81 nac3artiq: refactor compile methods
Avoids writing relocatable object to a file when linking with nac3ld.
2022-06-06 14:41:10 +08:00
occheung ac560ba985 nac3artiq: switch ld.lld to nac3ld for non-host target 2022-06-06 14:40:13 +08:00
occheung a96371145d add nac3ld 2022-06-06 14:40:13 +08:00
ychenfo 8addf2b55e nac3standalone: add more tests 2022-06-01 17:58:16 +08:00
ychenfo 5d5e9a5e02 nac3core: fix codegen error of inheritance 2022-06-01 17:58:16 +08:00
Sebastien Bourdeauducq 4c39dd240f update all dependencies 2022-05-31 23:09:51 +08:00
occheung 48fc5ceb8e nac3artiq: demote global value to private
... except typeinfo & now symbols.
typeinfo will be read by the runtime linker; now is for now-pinning.
2022-05-30 22:46:41 +08:00
ychenfo c4ab2855e5 nac3core: pretty print codegen panic error 2022-05-30 04:09:21 +08:00
ychenfo ffac37dc48 nac3core: fix exception type in primitive store 2022-05-29 19:14:00 +08:00
ychenfo 76473152e8 nac3core: fix llvm.expect intrinsic name
this might be one of the causes for the random segfault bug
2022-05-27 04:23:49 +08:00
Sebastien Bourdeauducq b04631e935 update dependencies, switch to nixpkgs 22.05 2022-05-24 11:10:29 +08:00
ychenfo 09820e5aed nac3artiq: return err instead of panic for host object attribute error 2022-05-18 02:54:42 +08:00
Sebastien Bourdeauducq 0ec2ed4d91 update dependencies 2022-05-17 12:05:12 +08:00
ychenfo 2cb725b7ac nac3artiq: correct global name for const object 2022-05-16 02:50:42 +08:00
Sebastien Bourdeauducq b9259b1907 update nixpkgs and LLVM 2022-05-14 16:33:03 +08:00
ychenfo 096f4b03c0 nac3core: fix assignment 2022-05-14 02:30:08 +08:00
ychenfo a022005183 nac3core: fix broken tests 2022-05-11 03:53:53 +08:00
ychenfo 325ba0a408 nac3core: add debug info 2022-05-11 03:53:53 +08:00
ychenfo ae6434696c nac3artiq: rename the filename of modinit
rename from __nac3_synthesized_modinit__ to <nac3_synthesized_modinit> to be more idomatic python
2022-05-11 03:52:16 +08:00
Sebastien Bourdeauducq 3f327113b2 update dependencies, use upstream inkwell 2022-04-27 15:41:46 +08:00
Sebastien Bourdeauducq 27d509d70e nac3artiq: get_const_obj should no longer make a pointer. Closes #272 2022-04-27 15:28:58 +08:00
Sebastien Bourdeauducq a321b13bec fix typos 2022-04-27 11:08:10 +08:00
ychenfo 48cb485b89 nac3core: show outer type info in type error messages
Reviewed-on: M-Labs/nac3#274
Co-authored-by: ychenfo <yc@m-labs.hk>
Co-committed-by: ychenfo <yc@m-labs.hk>
2022-04-22 15:31:55 +08:00
Sebastien Bourdeauducq 837aaa95f1 flake: contain sipyco to nac3artiq-profile 2022-04-19 10:34:55 +08:00
Sebastien Bourdeauducq a19e9c0bec flake: provide llvm-as for IRRT
clang already depends on llvmPackages_13.llvm, so, unlike the statically-linked tools
from llvm-nac3, this does not make the bloat even worse.
2022-04-19 10:23:41 +08:00
Sebastien Bourdeauducq 5dbe1d3d7d llvm: restore llvm-config 2022-04-19 10:23:12 +08:00
Sebastien Bourdeauducq e9bca3c822 llvm: set LLVM_BUILD_TOOLS=OFF 2022-04-19 00:30:11 +08:00
Sebastien Bourdeauducq 42d1aad507 flake: add PGO build to Hydra 2022-04-18 23:58:43 +08:00
Sebastien Bourdeauducq 2777a6e05f flake: use nac3devices example for PGO 2022-04-18 23:57:57 +08:00
Sebastien Bourdeauducq 05be5e93c4 flake: update nixpkgs 2022-04-18 18:48:05 +08:00
Sebastien Bourdeauducq 85f21060e4 update to LLVM 14 2022-04-18 18:47:20 +08:00
Sebastien Bourdeauducq a308d24caa nac3standalone: cleanup 2022-04-18 16:02:48 +08:00
Sebastien Bourdeauducq 1eac111d4c cleanup 2022-04-18 15:55:37 +08:00
ychenfo 44199781dc nac3standalone: add tests for operators 2022-04-18 15:31:56 +08:00
ychenfo 711c3d3303 nac3core: support custom operators 2022-04-18 15:31:56 +08:00
sb10q 0975264482 README: center icon 2022-04-18 15:11:32 +08:00
Sebastien Bourdeauducq 087aded3a3 add icon
Icon is copyright Evgeny Filatov and not covered by any free software license.
2022-04-18 15:07:53 +08:00
ychenfo f14b32be67 nac3artiq: type check host int bound instead of panic when codegen 2022-04-16 03:01:37 +08:00
David Nadlinger 879c66cccf flake.nix: Fix outdated nixConfig keys
The old syntax seems to be silently ignored on (at least)
Nix 2.7.0.
2022-04-13 21:21:18 +01:00
96 changed files with 22936 additions and 6690 deletions

1
.clippy.toml Normal file
View File

@ -0,0 +1 @@
doc-valid-idents = ["NumPy", ".."]

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
__pycache__
/target
windows/msys2
nix/windows/msys2

1078
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
[workspace]
members = [
"nac3ld",
"nac3ast",
"nac3parser",
"nac3core",
@ -7,6 +8,7 @@ members = [
"nac3artiq",
"runkernel",
]
resolver = "2"
[profile.release]
debug = true

View File

@ -1,5 +1,10 @@
# NAC3
<div align="center">
![icon](https://git.m-labs.hk/M-Labs/nac3/raw/branch/master/nac3.svg)
</div>
# NAC3
NAC3 is a major, backward-incompatible rewrite of the compiler for the [ARTIQ](https://m-labs.hk/artiq) physics experiment control and data acquisition system. It features greatly improved compilation speeds, a much better type system, and more predictable and transparent operation.
NAC3 has a modular design and its applicability reaches beyond ARTIQ. The ``nac3core`` module does not contain anything specific to ARTIQ, and can be used in any project that requires compiling Python to machine code.
@ -8,7 +13,7 @@ NAC3 has a modular design and its applicability reaches beyond ARTIQ. The ``nac3
## Packaging
NAC3 is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
NAC3 is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.8+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
## Try NAC3
@ -18,27 +23,19 @@ After setting up Nix as above, use ``nix shell git+https://github.com/m-labs/art
### Windows
Install [MSYS2](https://www.msys2.org/), and open "MSYS2 MinGW x64". Edit ``/etc/pacman.conf`` to add:
Install [MSYS2](https://www.msys2.org/), and open "MSYS2 CLANG64". Edit ``/etc/pacman.conf`` to add:
```
[artiq]
SigLevel = Optional TrustAll
Server = https://lab.m-labs.hk/msys2
Server = https://msys2.m-labs.hk/artiq-nac3
```
Then run the following commands:
```
pacman -Syu
pacman -S mingw-w64-x86_64-artiq
pacman -S mingw-w64-clang-x86_64-artiq
```
Install ``lld-msys2`` manually:
```
wget https://nixbld.m-labs.hk/build/115527/download/1/ld.lld.exe
mv ld.lld.exe C:/msys64/mingw64/bin
```
Note: This build of NAC3 cannot be used with Anaconda Python nor the python.org binaries for Windows. Those Python versions are compiled with Visual Studio (MSVC) and their ABI is incompatible with the GNU ABI used in this build. We have no plans to support Visual Studio nor the MSVC ABI. If you need a MSVC build, please install the requisite bloated spyware from Microsoft and compile NAC3 yourself.
## For developers
This repository contains:
@ -46,6 +43,7 @@ This repository contains:
- ``nac3parser``: Python parser (based on RustPython).
- ``nac3core``: Core compiler library, containing type-checking and code generation.
- ``nac3standalone``: Standalone compiler tool (core language only).
- ``nac3ld``: Minimalist RISC-V and ARM linker.
- ``nac3artiq``: Integration with ARTIQ and implementation of ARTIQ-specific extensions to the core language.
- ``runkernel``: Simple program that runs compiled ARTIQ kernels on the host and displays RTIO operations. Useful for testing without hardware.

View File

@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1649619156,
"narHash": "sha256-p0q4zpuKMwrzGF+5ZU7Thnpac5TinhDI9jr2mBxhV4w=",
"lastModified": 1708296515,
"narHash": "sha256-FyF489fYNAUy7b6dkYV6rGPyzp+4tThhr80KNAaF/yY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e7d63bd0d50df412f5a1d8acfa3caae75522e347",
"rev": "b98a4e1746acceb92c509bc496ef3d0e5ad8d4aa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-21.11",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}

View File

@ -1,7 +1,7 @@
{
description = "The third-generation ARTIQ compiler";
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-21.11;
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable;
outputs = { self, nixpkgs }:
let
@ -9,15 +9,24 @@
in rec {
packages.x86_64-linux = rec {
llvm-nac3 = pkgs.callPackage ./nix/llvm {};
llvm-tools-irrt = pkgs.runCommandNoCC "llvm-tools-irrt" {}
''
mkdir -p $out/bin
ln -s ${pkgs.llvmPackages_14.clang-unwrapped}/bin/clang $out/bin/clang-irrt
ln -s ${pkgs.llvmPackages_14.llvm.out}/bin/llvm-as $out/bin/llvm-as-irrt
'';
nac3artiq = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage {
pkgs.rustPlatform.buildRustPackage rec {
name = "nac3artiq";
outputs = [ "out" "runkernel" "standalone" ];
src = self;
cargoLock = { lockFile = ./Cargo.lock; };
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_13.clang-unwrapped llvm-nac3 ];
cargoLock = {
lockFile = ./Cargo.lock;
};
passthru.cargoLock = cargoLock;
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_14.clang llvm-tools-irrt pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
buildInputs = [ pkgs.python3 llvm-nac3 ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ])) ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ps.scipy ])) ];
checkPhase =
''
echo "Checking nac3standalone demos..."
@ -49,21 +58,21 @@
# LLVM PGO support
llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_13.stdenv;
stdenv = pkgs.llvmPackages_14.stdenv;
extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ];
};
nac3artiq-instrumented = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage {
name = "nac3artiq-instrumented";
src = self;
cargoLock = { lockFile = ./Cargo.lock; };
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_13.clang-unwrapped llvm-nac3-instrumented ];
inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt llvm-nac3-instrumented ];
buildInputs = [ pkgs.python3 llvm-nac3-instrumented ];
cargoBuildFlags = [ "--package" "nac3artiq" "--features" "init-llvm-profile" ];
doCheck = false;
configurePhase =
''
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_13.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_14.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
'';
installPhase =
''
@ -75,11 +84,35 @@
);
nac3artiq-profile = pkgs.stdenvNoCC.mkDerivation {
name = "nac3artiq-profile";
src = self;
buildInputs = [ (python3-mimalloc.withPackages(ps: [ ps.numpy nac3artiq-instrumented ])) pkgs.lld_13 pkgs.llvmPackages_13.libllvm ];
srcs = [
(pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "sipyco";
rev = "939f84f9b5eef7efbf7423c735d1834783b6140e";
sha256 = "sha256-15Nun4EY35j+6SPZkjzZtyH/ncxLS60KuGJjFh5kSTc=";
})
(pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "artiq";
rev = "923ca3377d42c815f979983134ec549dc39d3ca0";
sha256 = "sha256-oJoEeNEeNFSUyh6jXG8Tzp6qHVikeHS0CzfE+mODPgw=";
})
];
buildInputs = [
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ps.jsonschema ps.lmdb nac3artiq-instrumented ]))
pkgs.llvmPackages_14.llvm.out
];
phases = [ "buildPhase" "installPhase" ];
# TODO: get more representative code.
buildPhase = "python $src/nac3artiq/demo/demo.py";
buildPhase =
''
srcs=($srcs)
sipyco=''${srcs[0]}
artiq=''${srcs[1]}
export PYTHONPATH=$sipyco:$artiq
python -m artiq.frontend.artiq_ddb_template $artiq/artiq/examples/nac3devices/nac3devices.json > device_db.py
cp $artiq/artiq/examples/nac3devices/nac3devices.py .
python -m artiq.frontend.artiq_compile nac3devices.py
'';
installPhase =
''
mkdir $out
@ -87,15 +120,15 @@
'';
};
llvm-nac3-pgo = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_13.stdenv;
stdenv = pkgs.llvmPackages_14.stdenv;
extraCmakeFlags = [ "-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata" ];
};
nac3artiq-pgo = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage {
name = "nac3artiq-pgo";
src = self;
cargoLock = { lockFile = ./Cargo.lock; };
nativeBuildInputs = [ pkgs.python3 pkgs.llvmPackages_13.clang-unwrapped llvm-nac3-pgo ];
inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt llvm-nac3-pgo ];
buildInputs = [ pkgs.python3 llvm-nac3-pgo ];
cargoBuildFlags = [ "--package" "nac3artiq" ];
cargoTestFlags = [ "--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq" ];
@ -111,17 +144,18 @@
packages.x86_64-w64-mingw32 = import ./nix/windows { inherit pkgs; };
devShell.x86_64-linux = pkgs.mkShell {
devShells.x86_64-linux.default = pkgs.mkShell {
name = "nac3-dev-shell";
buildInputs = with pkgs; [
# build dependencies
packages.x86_64-linux.llvm-nac3
llvmPackages_13.clang-unwrapped # IRRT
llvmPackages_14.clang # demo
packages.x86_64-linux.llvm-tools-irrt
cargo
rustc
# runtime dependencies
lld_13
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ]))
lld_14 # for running kernels on the host
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ]))
# development tools
cargo-insta
clippy
@ -139,16 +173,15 @@
};
hydraJobs = {
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq;
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq nac3artiq-pgo;
llvm-nac3-msys2 = packages.x86_64-w64-mingw32.llvm-nac3;
nac3artiq-msys2 = packages.x86_64-w64-mingw32.nac3artiq;
nac3artiq-msys2-pkg = packages.x86_64-w64-mingw32.nac3artiq-pkg;
lld-msys2 = packages.x86_64-w64-mingw32.lld;
};
};
nixConfig = {
binaryCachePublicKeys = ["nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc="];
binaryCaches = ["https://nixbld.m-labs.hk" "https://cache.nixos.org"];
extra-trusted-public-keys = "nixbld.m-labs.hk-1:5aSRVA5b320xbNvu30tqxVPXpld73bhtOeH6uAjRyHc=";
extra-substituters = "https://nixbld.m-labs.hk";
};
}

56
nac3.svg Normal file
View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
id="a"
width="128"
height="128"
viewBox="0 0 95.99999 95.99999"
version="1.1"
sodipodi:docname="nac3.svg"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs11" />
<sodipodi:namedview
id="namedview9"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
width="128px"
inkscape:zoom="5.9448568"
inkscape:cx="60.472441"
inkscape:cy="60.556547"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="0"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="a" />
<rect
x="40.072601"
y="-26.776209"
width="55.668747"
height="55.668747"
transform="matrix(0.71803815,0.69600374,-0.71803815,0.69600374,0,0)"
style="fill:#be211e;stroke:#000000;stroke-width:4.37375px;stroke-linecap:round;stroke-linejoin:round"
id="rect2" />
<line
x1="38.00692"
y1="63.457153"
x2="57.993061"
y2="63.457153"
style="fill:none;stroke:#000000;stroke-width:4.37269px;stroke-linecap:round;stroke-linejoin:round"
id="line4" />
<path
d="m 48.007301,57.843329 c -1.943097,0 -3.877522,-0.41727 -5.686157,-1.246007 -3.218257,-1.474616 -5.650382,-4.075418 -6.849639,-7.323671 -2.065624,-5.588921 -1.192751,-10.226647 2.575258,-13.827 0.611554,-0.584909 1.518048,-0.773041 2.323689,-0.488206 0.80673,0.286405 1.369495,0.998486 1.447563,1.827234 0.237469,2.549302 2.439719,5.917376 4.28414,6.55273 0.396859,0.13506 0.820953,-0.05859 1.097084,-0.35222 0.339254,-0.360754 0.451065,-0.961893 -1.013597,-3.191372 -2.089851,-3.181137 -4.638728,-8.754903 -0.262407,-15.069853 0.494457,-0.713491 1.384673,-1.068907 2.256469,-0.909156 0.871795,0.161332 1.583757,0.806404 1.752251,1.651189 0.716448,3.591862 2.962357,6.151755 5.199306,8.023138 1.935503,1.61861 4.344688,3.867387 5.435687,7.096643 2.283183,6.758017 -1.202511,14.114988 -8.060822,16.494025 -1.467083,0.509226 -2.98513,0.762536 -4.498836,0.762536 z M 39.358865,40.002192 c -0.304711,0.696206 -0.541636,2.080524 -0.56865,2.237454 -0.330316,1.918771 0.168305,3.803963 0.846157,5.539951 0.856828,2.19436 2.437543,3.942467 4.583411,4.925713 2.143691,0.981675 4.554131,1.097816 6.789992,0.322666 4.571485,-1.586549 6.977584,-6.532238 5.363036,-11.02597 v -5.27e-4 C 55.455481,39.447968 54.023463,38.162043 52.221335,36.65432 50.876945,35.529534 49.409662,33.987726 48.417983,32.135555 48.01343,31.37996 47.79547,30.34303 47.76669,29.413263 c -0.187481,0.669514 -0.212441,2.325923 -0.150396,2.93691 0.179209,1.764456 1.333476,3.644546 2.340611,5.171243 1.311568,1.988179 2.72058,6.037272 0.459681,8.367985 -1.54192,1.58953 -4.038511,2.052034 -5.839973,1.38492 -2.398314,-0.888147 -3.942744,-2.690627 -4.941118,-4.768029 -0.121194,-0.25217 -0.532464,-1.174187 -0.276619,-2.5041 z"
id="path6"
style="stroke-width:1.09317" />
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -2,23 +2,25 @@
name = "nac3artiq"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2018"
edition = "2021"
[lib]
name = "nac3artiq"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.14", features = ["extension-module"] }
parking_lot = "0.11"
tempfile = "3"
itertools = "0.12"
pyo3 = { version = "0.20", features = ["extension-module"] }
parking_lot = "0.12"
tempfile = "3.10"
nac3parser = { path = "../nac3parser" }
nac3core = { path = "../nac3core" }
nac3ld = { path = "../nac3ld" }
[dependencies.inkwell]
version = "0.1.0-beta.4"
version = "0.4"
default-features = false
features = ["llvm13-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
[features]
init-llvm-profile = []

View File

@ -18,6 +18,13 @@ class EmbeddingMap:
"SPIError",
"0:ZeroDivisionError",
"0:IndexError",
"0:ValueError",
"0:RuntimeError",
"0:AssertionError",
"0:KeyError",
"0:NotImplementedError",
"0:OverflowError",
"0:IOError",
"0:UnwrapNoneError"])
def preallocate_runtime_exception_names(self, names):

View File

@ -10,7 +10,7 @@ from embedding_map import EmbeddingMap
__all__ = [
"Kernel", "KernelInvariant", "virtual",
"Kernel", "KernelInvariant", "virtual", "ConstGeneric",
"Option", "Some", "none", "UnwrapNoneError",
"round64", "floor64", "ceil64",
"extern", "kernel", "portable", "nac3",
@ -67,6 +67,12 @@ def Some(v: T) -> Option[T]:
none = Option(None)
class _ConstGenericMarker:
pass
def ConstGeneric(name, constraint):
return TypeVar(name, _ConstGenericMarker, constraint)
def round64(x):
return round(x)
@ -80,7 +86,13 @@ def ceil64(x):
import device_db
core_arguments = device_db.device_db["core"]["arguments"]
compiler = nac3artiq.NAC3(core_arguments["target"])
artiq_builtins = {
"none": none,
"virtual": virtual,
"_ConstGenericMarker": _ConstGenericMarker,
"Option": Option,
}
compiler = nac3artiq.NAC3(core_arguments["target"], artiq_builtins)
allow_registration = True
# Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side.
registered_functions = set()

View File

@ -1,18 +1,23 @@
use nac3core::{
codegen::{
expr::gen_call,
llvm_intrinsics::{call_int_smax, call_stackrestore, call_stacksave},
stmt::{gen_block, gen_with},
CodeGenContext, CodeGenerator,
},
symbol_resolver::ValueEnum,
toplevel::{DefinitionId, GenCall},
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum}
toplevel::{DefinitionId, GenCall, helper::PRIMITIVE_DEF_IDS},
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum, VarMap}
};
use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef};
use inkwell::{
context::Context, module::Linkage, types::IntType, values::BasicValueEnum, AddressSpace,
context::Context,
module::Linkage,
types::IntType,
values::BasicValueEnum,
AddressSpace,
};
use pyo3::{PyObject, PyResult, Python, types::{PyDict, PyList}};
@ -26,13 +31,45 @@ use std::{
sync::Arc,
};
/// The parallelism mode within a block.
#[derive(Copy, Clone, Eq, PartialEq)]
enum ParallelMode {
/// No parallelism is currently registered for this context.
None,
/// Legacy (or shallow) parallelism. Default before NAC3.
///
/// Each statement within the `with` block is treated as statements to be executed in parallel.
Legacy,
/// Deep parallelism. Default since NAC3.
///
/// Each function call within the `with` block (except those within a nested `sequential` block)
/// are treated to be executed in parallel.
Deep
}
pub struct ArtiqCodeGenerator<'a> {
name: String,
/// The size of a `size_t` variable in bits.
size_t: u32,
/// Monotonic counter for naming `start`/`stop` variables used by `with parallel` blocks.
name_counter: u32,
/// Variable for tracking the start of a `with parallel` block.
start: Option<Expr<Option<Type>>>,
/// Variable for tracking the end of a `with parallel` block.
end: Option<Expr<Option<Type>>>,
timeline: &'a (dyn TimeFns + Sync),
/// The [ParallelMode] of the current parallel context.
///
/// The current parallel context refers to the nearest `with parallel` or `with legacy_parallel`
/// statement, which is used to determine when and how the timeline should be updated.
parallel_mode: ParallelMode,
}
impl<'a> ArtiqCodeGenerator<'a> {
@ -42,7 +79,75 @@ impl<'a> ArtiqCodeGenerator<'a> {
timeline: &'a (dyn TimeFns + Sync),
) -> ArtiqCodeGenerator<'a> {
assert!(size_t == 32 || size_t == 64);
ArtiqCodeGenerator { name, size_t, name_counter: 0, start: None, end: None, timeline }
ArtiqCodeGenerator {
name,
size_t,
name_counter: 0,
start: None,
end: None,
timeline,
parallel_mode: ParallelMode::None,
}
}
/// If the generator is currently in a direct-`parallel` block context, emits IR that resets the
/// position of the timeline to the initial timeline position before entering the `parallel`
/// block.
///
/// Direct-`parallel` block context refers to when the generator is generating statements whose
/// closest parent `with` statement is a `with parallel` block.
fn timeline_reset_start(
&mut self,
ctx: &mut CodeGenContext<'_, '_>
) -> Result<(), String> {
if let Some(start) = self.start.clone() {
let start_val = self.gen_expr(ctx, &start)?
.unwrap()
.to_basic_value_enum(ctx, self, start.custom.unwrap())?;
self.timeline.emit_at_mu(ctx, start_val);
}
Ok(())
}
/// If the generator is currently in a `parallel` block context, emits IR that updates the
/// maximum end position of the `parallel` block as specified by the timeline `end` value.
///
/// In general the `end` parameter should be set to `self.end` for updating the maximum end
/// position for the current `parallel` block. Other values can be passed in to update the
/// maximum end position for other `parallel` blocks.
///
/// `parallel`-block context refers to when the generator is generating statements within a
/// (possibly indirect) `parallel` block.
///
/// * `store_name` - The LLVM value name for the pointer to `end`. `.addr` will be appended to
/// the end of the provided value name.
fn timeline_update_end_max(
&mut self,
ctx: &mut CodeGenContext<'_, '_>,
end: Option<Expr<Option<Type>>>,
store_name: Option<&str>,
) -> Result<(), String> {
if let Some(end) = end {
let old_end = self.gen_expr(ctx, &end)?
.unwrap()
.to_basic_value_enum(ctx, self, end.custom.unwrap())?;
let now = self.timeline.emit_now_mu(ctx);
let max = call_int_smax(
ctx,
old_end.into_int_value(),
now.into_int_value(),
Some("smax")
);
let end_store = self.gen_store_target(
ctx,
&end,
store_name.map(|name| format!("{name}.addr")).as_deref())?
.unwrap();
ctx.builder.build_store(end_store, max).unwrap();
}
Ok(())
}
}
@ -59,183 +164,201 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
}
}
fn gen_call<'ctx, 'a>(
fn gen_block<'ctx, 'a, 'c, I: Iterator<Item=&'c Stmt<Option<Type>>>>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
stmts: I
) -> Result<(), String> where Self: Sized {
// Legacy parallel emits timeline end-update/timeline-reset after each top-level statement
// in the parallel block
if self.parallel_mode == ParallelMode::Legacy {
for stmt in stmts {
self.gen_stmt(ctx, stmt)?;
if ctx.is_terminated() {
break;
}
self.timeline_update_end_max(ctx, self.end.clone(), Some("end"))?;
self.timeline_reset_start(ctx)?;
}
Ok(())
} else {
gen_block(self, ctx, stmts)
}
}
fn gen_call<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, '_>,
obj: Option<(Type, ValueEnum<'ctx>)>,
fun: (&FunSignature, DefinitionId),
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let result = gen_call(self, ctx, obj, fun, params)?;
if let Some(end) = self.end.clone() {
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self, end.custom.unwrap())?;
let now = self.timeline.emit_now_mu(ctx);
let smax = ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
ctx.module.add_function(
"llvm.smax.i64",
i64.fn_type(&[i64.into(), i64.into()], false),
None,
)
});
let max = ctx
.builder
.build_call(smax, &[old_end.into(), now.into()], "smax")
.try_as_basic_value()
.left()
.unwrap();
let end_store = self.gen_store_target(ctx, &end)?;
ctx.builder.build_store(end_store, max);
}
if let Some(start) = self.start.clone() {
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self, start.custom.unwrap())?;
self.timeline.emit_at_mu(ctx, start_val);
// Deep parallel emits timeline end-update/timeline-reset after each function call
if self.parallel_mode == ParallelMode::Deep {
self.timeline_update_end_max(ctx, self.end.clone(), Some("end"))?;
self.timeline_reset_start(ctx)?;
}
Ok(result)
}
fn gen_with<'ctx, 'a>(
fn gen_with(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'_, '_>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String> {
if let StmtKind::With { items, body, .. } = &stmt.node {
if items.len() == 1 && items[0].optional_vars.is_none() {
let item = &items[0];
// Behavior of parallel and sequential:
// Each function call (indirectly, can be inside a sequential block) within a parallel
// block will update the end variable to the maximum now_mu in the block.
// Each function call directly inside a parallel block will reset the timeline after
// execution. A parallel block within a sequential block (or not within any block) will
// set the timeline to the max now_mu within the block (and the outer max now_mu will also
// be updated).
//
// Implementation: We track the start and end separately.
// - If there is a start variable, it indicates that we are directly inside a
// parallel block and we have to reset the timeline after every function call.
// - If there is a end variable, it indicates that we are (indirectly) inside a
// parallel block, and we should update the max end value.
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
if id == &"parallel".into() {
let old_start = self.start.take();
let old_end = self.end.take();
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
} else {
self.timeline.emit_now_mu(ctx)
};
// Emulate variable allocation, as we need to use the CodeGenContext
// HashMap to store our variable due to lifetime limitation
// Note: we should be able to store variables directly if generic
// associative type is used by limiting the lifetime of CodeGenerator to
// the LLVM Context.
// The name is guaranteed to be unique as users cannot use this as variable
// name.
self.start = old_start.clone().map_or_else(
|| {
let start = format!("with-{}-start", self.name_counter).into();
let start_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: start, ctx: name_ctx.clone() },
custom: Some(ctx.primitives.int64),
};
let start = self.gen_store_target(ctx, &start_expr)?;
ctx.builder.build_store(start, now);
Ok(Some(start_expr)) as Result<_, String>
},
|v| Ok(Some(v)),
)?;
let end = format!("with-{}-end", self.name_counter).into();
let end_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: end, ctx: name_ctx.clone() },
custom: Some(ctx.primitives.int64),
};
let end = self.gen_store_target(ctx, &end_expr)?;
ctx.builder.build_store(end, now);
self.end = Some(end_expr);
self.name_counter += 1;
gen_block(self, ctx, body.iter())?;
let current = ctx.builder.get_insert_block().unwrap();
// if the current block is terminated, move before the terminator
// we want to set the timeline before reaching the terminator
// TODO: This may be unsound if there are multiple exit paths in the
// block... e.g.
// if ...:
// return
// Perhaps we can fix this by using actual with block?
let reset_position = if let Some(terminator) = current.get_terminator() {
ctx.builder.position_before(&terminator);
true
} else {
false
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val = self
.gen_expr(ctx, &end_expr)?
.unwrap()
.to_basic_value_enum(ctx, self, end_expr.custom.unwrap())?;
let StmtKind::With { items, body, .. } = &stmt.node else {
unreachable!()
};
// inside a sequential block
if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val);
}
// inside a parallel block, should update the outer max now_mu
if let Some(old_end) = &old_end {
let outer_end_val = self
.gen_expr(ctx, old_end)?
.unwrap()
.to_basic_value_enum(ctx, self, old_end.custom.unwrap())?;
let smax =
ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
let i64 = ctx.ctx.i64_type();
ctx.module.add_function(
"llvm.smax.i64",
i64.fn_type(&[i64.into(), i64.into()], false),
None,
)
});
let max = ctx
.builder
.build_call(smax, &[end_val.into(), outer_end_val.into()], "smax")
.try_as_basic_value()
.left()
if items.len() == 1 && items[0].optional_vars.is_none() {
let item = &items[0];
// Behavior of parallel and sequential:
// Each function call (indirectly, can be inside a sequential block) within a parallel
// block will update the end variable to the maximum now_mu in the block.
// Each function call directly inside a parallel block will reset the timeline after
// execution. A parallel block within a sequential block (or not within any block) will
// set the timeline to the max now_mu within the block (and the outer max now_mu will also
// be updated).
//
// Implementation: We track the start and end separately.
// - If there is a start variable, it indicates that we are directly inside a
// parallel block and we have to reset the timeline after every function call.
// - If there is a end variable, it indicates that we are (indirectly) inside a
// parallel block, and we should update the max end value.
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
if id == &"parallel".into() || id == &"legacy_parallel".into() {
let old_start = self.start.take();
let old_end = self.end.take();
let old_parallel_mode = self.parallel_mode;
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?
.unwrap()
.to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
} else {
self.timeline.emit_now_mu(ctx)
};
// Emulate variable allocation, as we need to use the CodeGenContext
// HashMap to store our variable due to lifetime limitation
// Note: we should be able to store variables directly if generic
// associative type is used by limiting the lifetime of CodeGenerator to
// the LLVM Context.
// The name is guaranteed to be unique as users cannot use this as variable
// name.
self.start = old_start.clone().map_or_else(
|| {
let start = format!("with-{}-start", self.name_counter).into();
let start_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: start, ctx: name_ctx.clone() },
custom: Some(ctx.primitives.int64),
};
let start = self
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
.unwrap();
let outer_end = self.gen_store_target(ctx, old_end)?;
ctx.builder.build_store(outer_end, max);
}
self.start = old_start;
self.end = old_end;
if reset_position {
ctx.builder.position_at_end(current);
}
return Ok(());
} else if id == &"sequential".into() {
let start = self.start.take();
for stmt in body.iter() {
self.gen_stmt(ctx, stmt)?;
if ctx.is_terminated() {
break;
}
}
self.start = start;
return Ok(());
ctx.builder.build_store(start, now).unwrap();
Ok(Some(start_expr)) as Result<_, String>
},
|v| Ok(Some(v)),
)?;
let end = format!("with-{}-end", self.name_counter).into();
let end_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: end, ctx: name_ctx.clone() },
custom: Some(ctx.primitives.int64),
};
let end = self
.gen_store_target(ctx, &end_expr, Some("end.addr"))?
.unwrap();
ctx.builder.build_store(end, now).unwrap();
self.end = Some(end_expr);
self.name_counter += 1;
self.parallel_mode = match id.to_string().as_str() {
"parallel" => ParallelMode::Deep,
"legacy_parallel" => ParallelMode::Legacy,
_ => unreachable!(),
};
self.gen_block(ctx, body.iter())?;
let current = ctx.builder.get_insert_block().unwrap();
// if the current block is terminated, move before the terminator
// we want to set the timeline before reaching the terminator
// TODO: This may be unsound if there are multiple exit paths in the
// block... e.g.
// if ...:
// return
// Perhaps we can fix this by using actual with block?
let reset_position = if let Some(terminator) = current.get_terminator() {
ctx.builder.position_before(&terminator);
true
} else {
false
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val = self
.gen_expr(ctx, &end_expr)?
.unwrap()
.to_basic_value_enum(ctx, self, end_expr.custom.unwrap())?;
// inside a sequential block
if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val);
}
// inside a parallel block, should update the outer max now_mu
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
self.parallel_mode = old_parallel_mode;
self.end = old_end;
self.start = old_start;
if reset_position {
ctx.builder.position_at_end(current);
}
return Ok(());
} else if id == &"sequential".into() {
// For deep parallel, temporarily take away start to avoid function calls in
// the block from resetting the timeline.
// This does not affect legacy parallel, as the timeline will be reset after
// this block finishes execution.
let start = self.start.take();
self.gen_block(ctx, body.iter())?;
self.start = start;
// Reset the timeline when we are exiting the sequential block
// Legacy parallel does not need this, since it will be reset after codegen
// for this statement is completed
if self.parallel_mode == ParallelMode::Deep {
self.timeline_reset_start(ctx)?;
}
return Ok(());
}
}
// not parallel/sequential
gen_with(self, ctx, stmt)
} else {
unreachable!()
}
// not parallel/sequential
gen_with(self, ctx, stmt)
}
}
fn gen_rpc_tag<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
fn gen_rpc_tag(
ctx: &mut CodeGenContext<'_, '_>,
ty: Type,
buffer: &mut Vec<u8>,
) -> Result<(), String> {
@ -280,14 +403,14 @@ fn gen_rpc_tag<'ctx, 'a>(
Ok(())
}
fn rpc_codegen_callback_fn<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
fn rpc_codegen_callback_fn<'ctx>(
ctx: &mut CodeGenContext<'ctx, '_>,
obj: Option<(Type, ValueEnum<'ctx>)>,
fun: (&FunSignature, DefinitionId),
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
generator: &mut dyn CodeGenerator,
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::Generic);
let ptr_type = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
let size_type = generator.get_size_type(ctx.ctx);
let int8 = ctx.ctx.i8_type();
let int32 = ctx.ctx.i32_type();
@ -299,7 +422,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
if obj.is_some() {
tag.push(b'O');
}
for arg in fun.0.args.iter() {
for arg in &fun.0.args {
gen_rpc_tag(ctx, arg.ty, &mut tag)?;
}
tag.push(b':');
@ -335,34 +458,25 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
})
.as_pointer_value();
let arg_length = args.len() + if obj.is_some() { 1 } else { 0 };
let arg_length = args.len() + usize::from(obj.is_some());
let stacksave = ctx.module.get_function("llvm.stacksave").unwrap_or_else(|| {
ctx.module.add_function("llvm.stacksave", ptr_type.fn_type(&[], false), None)
});
let stackrestore = ctx.module.get_function("llvm.stackrestore").unwrap_or_else(|| {
ctx.module.add_function(
"llvm.stackrestore",
ctx.ctx.void_type().fn_type(&[ptr_type.into()], false),
None,
let stackptr = call_stacksave(ctx, Some("rpc.stack"));
let args_ptr = ctx.builder
.build_array_alloca(
ptr_type,
ctx.ctx.i32_type().const_int(arg_length as u64, false),
"argptr",
)
});
let stackptr = ctx.builder.build_call(stacksave, &[], "rpc.stack");
let args_ptr = ctx.builder.build_array_alloca(
ptr_type,
ctx.ctx.i32_type().const_int(arg_length as u64, false),
"argptr",
);
.unwrap();
// -- rpc args handling
let mut keys = fun.0.args.clone();
let mut mapping = HashMap::new();
for (key, value) in args.into_iter() {
for (key, value) in args {
mapping.insert(key.unwrap_or_else(|| keys.remove(0).name), value);
}
// default value handling
for k in keys.into_iter() {
for k in keys {
mapping.insert(
k.name,
ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into()
@ -385,17 +499,17 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
}
for (i, arg) in real_params.iter().enumerate() {
let arg_slot = ctx.builder.build_alloca(arg.get_type(), &format!("rpc.arg{}", i));
ctx.builder.build_store(arg_slot, *arg);
let arg_slot = ctx.builder.build_bitcast(arg_slot, ptr_type, "rpc.arg");
let arg_slot = generator.gen_var_alloc(ctx, arg.get_type(), Some(&format!("rpc.arg{i}"))).unwrap();
ctx.builder.build_store(arg_slot, *arg).unwrap();
let arg_slot = ctx.builder.build_bitcast(arg_slot, ptr_type, "rpc.arg").unwrap();
let arg_ptr = unsafe {
ctx.builder.build_gep(
args_ptr,
&[int32.const_int(i as u64, false)],
&format!("rpc.arg{}", i),
&format!("rpc.arg{i}"),
)
};
ctx.builder.build_store(arg_ptr, arg_slot);
}.unwrap();
ctx.builder.build_store(arg_ptr, arg_slot).unwrap();
}
// call
@ -405,26 +519,24 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
ctx.ctx.void_type().fn_type(
&[
int32.into(),
tag_ptr_type.ptr_type(AddressSpace::Generic).into(),
ptr_type.ptr_type(AddressSpace::Generic).into(),
tag_ptr_type.ptr_type(AddressSpace::default()).into(),
ptr_type.ptr_type(AddressSpace::default()).into(),
],
false,
),
None,
)
});
ctx.builder.build_call(
rpc_send,
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
"rpc.send",
);
ctx.builder
.build_call(
rpc_send,
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
"rpc.send",
)
.unwrap();
// reclaim stack space used by arguments
ctx.builder.build_call(
stackrestore,
&[stackptr.try_as_basic_value().unwrap_left().into()],
"rpc.stackrestore",
);
call_stackrestore(ctx, stackptr);
// -- receive value:
// T result = {
@ -450,62 +562,60 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
let alloc_bb = ctx.ctx.append_basic_block(current_function, "rpc.continue");
let tail_bb = ctx.ctx.append_basic_block(current_function, "rpc.tail");
let ret_ty = ctx.get_llvm_type(generator, fun.0.ret);
let ret_ty = ctx.get_llvm_abi_type(generator, fun.0.ret);
let need_load = !ret_ty.is_pointer_type();
let slot = ctx.builder.build_alloca(ret_ty, "rpc.ret.slot");
let slotgen = ctx.builder.build_bitcast(slot, ptr_type, "rpc.ret.ptr");
ctx.builder.build_unconditional_branch(head_bb);
let slot = ctx.builder.build_alloca(ret_ty, "rpc.ret.slot").unwrap();
let slotgen = ctx.builder.build_bitcast(slot, ptr_type, "rpc.ret.ptr").unwrap();
ctx.builder.build_unconditional_branch(head_bb).unwrap();
ctx.builder.position_at_end(head_bb);
let phi = ctx.builder.build_phi(ptr_type, "rpc.ptr");
let phi = ctx.builder.build_phi(ptr_type, "rpc.ptr").unwrap();
phi.add_incoming(&[(&slotgen, prehead_bb)]);
let alloc_size = ctx
.build_call_or_invoke(rpc_recv, &[phi.as_basic_value()], "rpc.size.next")
.unwrap()
.into_int_value();
let is_done = ctx.builder.build_int_compare(
inkwell::IntPredicate::EQ,
int32.const_zero(),
alloc_size,
"rpc.done",
);
let is_done = ctx.builder
.build_int_compare(
inkwell::IntPredicate::EQ,
int32.const_zero(),
alloc_size,
"rpc.done",
)
.unwrap();
ctx.builder.build_conditional_branch(is_done, tail_bb, alloc_bb);
ctx.builder.build_conditional_branch(is_done, tail_bb, alloc_bb).unwrap();
ctx.builder.position_at_end(alloc_bb);
let alloc_ptr = ctx.builder.build_array_alloca(ptr_type, alloc_size, "rpc.alloc");
let alloc_ptr = ctx.builder.build_bitcast(alloc_ptr, ptr_type, "rpc.alloc.ptr");
let alloc_ptr = ctx.builder.build_array_alloca(ptr_type, alloc_size, "rpc.alloc").unwrap();
let alloc_ptr = ctx.builder.build_bitcast(alloc_ptr, ptr_type, "rpc.alloc.ptr").unwrap();
phi.add_incoming(&[(&alloc_ptr, alloc_bb)]);
ctx.builder.build_unconditional_branch(head_bb);
ctx.builder.build_unconditional_branch(head_bb).unwrap();
ctx.builder.position_at_end(tail_bb);
let result = ctx.builder.build_load(slot, "rpc.result");
let result = ctx.builder.build_load(slot, "rpc.result").unwrap();
if need_load {
ctx.builder.build_call(
stackrestore,
&[stackptr.try_as_basic_value().unwrap_left().into()],
"rpc.stackrestore",
);
call_stackrestore(ctx, stackptr);
}
Ok(Some(result))
}
pub fn attributes_writeback<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
pub fn attributes_writeback(
ctx: &mut CodeGenContext<'_, '_>,
generator: &mut dyn CodeGenerator,
inner_resolver: &InnerResolver,
host_attributes: PyObject,
host_attributes: &PyObject,
) -> Result<(), String> {
Python::with_gil(|py| -> PyResult<Result<(), String>> {
let host_attributes = host_attributes.cast_as::<PyList>(py)?;
let host_attributes: &PyList = host_attributes.downcast(py)?;
let top_levels = ctx.top_level.definitions.read();
let globals = inner_resolver.global_value_ids.read();
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let mut values = Vec::new();
let mut scratch_buffer = Vec::new();
for (_, val) in globals.iter() {
for val in (*globals).values() {
let val = val.as_ref(py);
let ty = inner_resolver.get_obj_type(py, val, &mut ctx.unifier, &top_levels, &ctx.primitives)?;
if let Err(ty) = ty {
@ -514,13 +624,13 @@ pub fn attributes_writeback<'ctx, 'a>(
let ty = ty.unwrap();
match &*ctx.unifier.get_ty(ty) {
TypeEnum::TObj { fields, obj_id, .. }
if *obj_id != ctx.primitives.option.get_obj_id(&ctx.unifier) =>
if *obj_id != ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
{
// we only care about primitive attributes
// for non-primitive attributes, they should be in another global
let mut attributes = Vec::new();
let obj = inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap();
for (name, (field_ty, is_mutable)) in fields.iter() {
for (name, (field_ty, is_mutable)) in fields {
if !is_mutable {
continue
}
@ -529,7 +639,7 @@ pub fn attributes_writeback<'ctx, 'a>(
let index = ctx.get_attr_index(ty, *name);
values.push((*field_ty, ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)])));
&[zero, int32.const_int(index as u64, false)], None)));
}
}
if !attributes.is_empty() {
@ -557,10 +667,10 @@ pub fn attributes_writeback<'ctx, 'a>(
default_value: None
}).collect(),
ret: ctx.primitives.none,
vars: Default::default()
vars: VarMap::default()
};
let args: Vec<_> = values.into_iter().map(|(_, val)| (None, ValueEnum::Dynamic(val))).collect();
if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, DefinitionId(0)), args, generator) {
if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, PRIMITIVE_DEF_IDS.int32), args, generator) {
return Ok(Err(e));
}
Ok(Ok(()))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,18 @@
use inkwell::{values::BasicValueEnum, AddressSpace, AtomicOrdering};
use inkwell::{values::{BasicValueEnum, CallSiteValue}, AddressSpace, AtomicOrdering};
use itertools::Either;
use nac3core::codegen::CodeGenContext;
/// Functions for manipulating the timeline.
pub trait TimeFns {
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx>;
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>);
fn emit_delay_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, dt: BasicValueEnum<'ctx>);
/// Emits LLVM IR for `now_mu`.
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx>;
/// Emits LLVM IR for `at_mu`.
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>);
/// Emits LLVM IR for `delay_mu`.
fn emit_delay_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, dt: BasicValueEnum<'ctx>);
}
pub struct NowPinningTimeFns64 {}
@ -12,82 +20,79 @@ pub struct NowPinningTimeFns64 {}
// For FPGA design reasons, on VexRiscv with 64-bit data bus, the "now" CSR is split into two 32-bit
// values that are each padded to 64-bits.
impl TimeFns for NowPinningTimeFns64 {
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx> {
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
let i64_type = ctx.ctx.i64_type();
let i32_type = ctx.ctx.i32_type();
let now = ctx
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr =
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::Generic), "now_hiptr");
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_gep")
};
if let (BasicValueEnum::IntValue(now_hi), BasicValueEnum::IntValue(now_lo)) = (
ctx.builder.build_load(now_hiptr, "now_hi"),
ctx.builder.build_load(now_loptr, "now_lo"),
) {
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "now_zext_hi");
let shifted_hi = ctx.builder.build_left_shift(
zext_hi,
i64_type.const_int(32, false),
"now_shifted_zext_hi",
);
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "now_zext_lo");
ctx.builder.build_or(shifted_hi, zext_lo, "now_or").into()
} else {
unreachable!();
}
} else {
unreachable!();
}
let now_hiptr = ctx.builder
.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr")
.map(BasicValueEnum::into_pointer_value)
.unwrap();
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
}.unwrap();
let now_hi = ctx.builder.build_load(now_hiptr, "now.hi")
.map(BasicValueEnum::into_int_value)
.unwrap();
let now_lo = ctx.builder.build_load(now_loptr, "now.lo")
.map(BasicValueEnum::into_int_value)
.unwrap();
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "").unwrap();
let shifted_hi = ctx.builder
.build_left_shift(zext_hi, i64_type.const_int(32, false), "")
.unwrap();
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "").unwrap();
ctx.builder.build_or(shifted_hi, zext_lo, "now_mu").map(Into::into).unwrap()
}
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
let i32_type = ctx.ctx.i32_type();
let i64_type = ctx.ctx.i64_type();
let i64_32 = i64_type.const_int(32, false);
if let BasicValueEnum::IntValue(time) = t {
let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
let time = t.into_int_value();
let time_hi = ctx.builder
.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "time.hi").unwrap(),
i32_type,
"now_trunc",
);
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
let now = ctx
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr = ctx.builder.build_bitcast(
now,
i32_type.ptr_type(AddressSpace::Generic),
"now_bitcast",
);
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_gep")
};
ctx.builder
.build_store(now_hiptr, time_hi)
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
ctx.builder
.build_store(now_loptr, time_lo)
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
} else {
unreachable!();
}
} else {
unreachable!();
}
"",
)
.unwrap();
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo").unwrap();
let now = ctx
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr = ctx.builder
.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr")
.map(BasicValueEnum::into_pointer_value)
.unwrap();
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
}.unwrap();
ctx.builder
.build_store(now_hiptr, time_hi)
.unwrap()
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
ctx.builder
.build_store(now_loptr, time_lo)
.unwrap()
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
}
fn emit_delay_mu<'ctx, 'a>(
fn emit_delay_mu<'ctx>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
dt: BasicValueEnum<'ctx>,
) {
let i64_type = ctx.ctx.i64_type();
@ -96,57 +101,55 @@ impl TimeFns for NowPinningTimeFns64 {
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr =
ctx.builder.build_bitcast(now, i32_type.ptr_type(AddressSpace::Generic), "now_hiptr");
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now_loptr")
};
if let (
BasicValueEnum::IntValue(now_hi),
BasicValueEnum::IntValue(now_lo),
BasicValueEnum::IntValue(dt),
) = (
ctx.builder.build_load(now_hiptr, "now_hi"),
ctx.builder.build_load(now_loptr, "now_lo"),
dt,
) {
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "now_zext_hi");
let shifted_hi = ctx.builder.build_left_shift(
zext_hi,
let now_hiptr = ctx.builder
.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr")
.map(BasicValueEnum::into_pointer_value)
.unwrap();
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
}.unwrap();
let now_hi = ctx.builder.build_load(now_hiptr, "now.hi")
.map(BasicValueEnum::into_int_value)
.unwrap();
let now_lo = ctx.builder.build_load(now_loptr, "now.lo")
.map(BasicValueEnum::into_int_value)
.unwrap();
let dt = dt.into_int_value();
let zext_hi = ctx.builder.build_int_z_extend(now_hi, i64_type, "").unwrap();
let shifted_hi = ctx.builder
.build_left_shift(zext_hi, i64_type.const_int(32, false), "")
.unwrap();
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "").unwrap();
let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now").unwrap();
let time = ctx.builder.build_int_add(now_val, dt, "time").unwrap();
let time_hi = ctx.builder
.build_int_truncate(
ctx.builder.build_right_shift(
time,
i64_type.const_int(32, false),
"now_shifted_zext_hi",
);
let zext_lo = ctx.builder.build_int_z_extend(now_lo, i64_type, "now_zext_lo");
let now_val = ctx.builder.build_or(shifted_hi, zext_lo, "now_or");
false,
"",
).unwrap(),
i32_type,
"time.hi",
)
.unwrap();
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo").unwrap();
let time = ctx.builder.build_int_add(now_val, dt, "now_add");
let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(
time,
i64_type.const_int(32, false),
false,
"now_lshr",
),
i32_type,
"now_trunc",
);
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
ctx.builder
.build_store(now_hiptr, time_hi)
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
ctx.builder
.build_store(now_loptr, time_lo)
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
} else {
unreachable!();
}
} else {
unreachable!();
};
ctx.builder
.build_store(now_hiptr, time_hi)
.unwrap()
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
ctx.builder
.build_store(now_loptr, time_lo)
.unwrap()
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
}
}
@ -155,66 +158,66 @@ pub static NOW_PINNING_TIME_FNS_64: NowPinningTimeFns64 = NowPinningTimeFns64 {}
pub struct NowPinningTimeFns {}
impl TimeFns for NowPinningTimeFns {
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx> {
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
let i64_type = ctx.ctx.i64_type();
let now = ctx
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now");
if let BasicValueEnum::IntValue(now_raw) = now_raw {
let i64_32 = i64_type.const_int(32, false);
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now_shl");
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now_lshr");
ctx.builder.build_or(now_lo, now_hi, "now_or").into()
} else {
unreachable!();
}
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now")
.map(BasicValueEnum::into_int_value)
.unwrap();
let i64_32 = i64_type.const_int(32, false);
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo").unwrap();
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi").unwrap();
ctx.builder.build_or(now_lo, now_hi, "now_mu")
.map(Into::into)
.unwrap()
}
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
let i32_type = ctx.ctx.i32_type();
let i64_type = ctx.ctx.i64_type();
let i64_32 = i64_type.const_int(32, false);
if let BasicValueEnum::IntValue(time) = t {
let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
let time = t.into_int_value();
let time_hi = ctx.builder
.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "").unwrap(),
i32_type,
"now_trunc",
);
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
let now = ctx
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr = ctx.builder.build_bitcast(
now,
i32_type.ptr_type(AddressSpace::Generic),
"now_bitcast",
);
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now_gep")
};
ctx.builder
.build_store(now_hiptr, time_hi)
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
ctx.builder
.build_store(now_loptr, time_lo)
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
} else {
unreachable!();
}
} else {
unreachable!();
}
"time.hi",
)
.unwrap();
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc").unwrap();
let now = ctx
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_hiptr = ctx.builder
.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr")
.map(BasicValueEnum::into_pointer_value)
.unwrap();
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
}.unwrap();
ctx.builder
.build_store(now_hiptr, time_hi)
.unwrap()
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
ctx.builder
.build_store(now_loptr, time_lo)
.unwrap()
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
}
fn emit_delay_mu<'ctx, 'a>(
fn emit_delay_mu<'ctx>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
dt: BasicValueEnum<'ctx>,
) {
let i32_type = ctx.ctx.i32_type();
@ -224,41 +227,43 @@ impl TimeFns for NowPinningTimeFns {
.module
.get_global("now")
.unwrap_or_else(|| ctx.module.add_global(i64_type, None, "now"));
let now_raw = ctx.builder.build_load(now.as_pointer_value(), "now");
if let (BasicValueEnum::IntValue(now_raw), BasicValueEnum::IntValue(dt)) = (now_raw, dt) {
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now_shl");
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now_lshr");
let now_val = ctx.builder.build_or(now_lo, now_hi, "now_or");
let time = ctx.builder.build_int_add(now_val, dt, "now_add");
let time_hi = ctx.builder.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "now_lshr"),
let now_raw = ctx.builder
.build_load(now.as_pointer_value(), "")
.map(BasicValueEnum::into_int_value)
.unwrap();
let dt = dt.into_int_value();
let now_lo = ctx.builder.build_left_shift(now_raw, i64_32, "now.lo").unwrap();
let now_hi = ctx.builder.build_right_shift(now_raw, i64_32, false, "now.hi").unwrap();
let now_val = ctx.builder.build_or(now_lo, now_hi, "now_val").unwrap();
let time = ctx.builder.build_int_add(now_val, dt, "time").unwrap();
let time_hi = ctx.builder
.build_int_truncate(
ctx.builder.build_right_shift(time, i64_32, false, "time.hi").unwrap(),
i32_type,
"now_trunc",
);
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "now_trunc");
let now_hiptr = ctx.builder.build_bitcast(
now,
i32_type.ptr_type(AddressSpace::Generic),
"now_bitcast",
);
if let BasicValueEnum::PointerValue(now_hiptr) = now_hiptr {
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now_gep")
};
ctx.builder
.build_store(now_hiptr, time_hi)
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
ctx.builder
.build_store(now_loptr, time_lo)
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
} else {
unreachable!();
}
} else {
unreachable!();
}
)
.unwrap();
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo").unwrap();
let now_hiptr = ctx.builder
.build_bitcast(now, i32_type.ptr_type(AddressSpace::default()), "now.hi.addr")
.map(BasicValueEnum::into_pointer_value)
.unwrap();
let now_loptr = unsafe {
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
}.unwrap();
ctx.builder
.build_store(now_hiptr, time_hi)
.unwrap()
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
ctx.builder
.build_store(now_loptr, time_lo)
.unwrap()
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
.unwrap();
}
}
@ -267,14 +272,17 @@ pub static NOW_PINNING_TIME_FNS: NowPinningTimeFns = NowPinningTimeFns {};
pub struct ExternTimeFns {}
impl TimeFns for ExternTimeFns {
fn emit_now_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>) -> BasicValueEnum<'ctx> {
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
let now_mu = ctx.module.get_function("now_mu").unwrap_or_else(|| {
ctx.module.add_function("now_mu", ctx.ctx.i64_type().fn_type(&[], false), None)
});
ctx.builder.build_call(now_mu, &[], "now_mu").try_as_basic_value().left().unwrap()
ctx.builder.build_call(now_mu, &[], "now_mu")
.map(CallSiteValue::try_as_basic_value)
.map(Either::unwrap_left)
.unwrap()
}
fn emit_at_mu<'ctx, 'a>(&self, ctx: &mut CodeGenContext<'ctx, 'a>, t: BasicValueEnum<'ctx>) {
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
let at_mu = ctx.module.get_function("at_mu").unwrap_or_else(|| {
ctx.module.add_function(
"at_mu",
@ -282,12 +290,12 @@ impl TimeFns for ExternTimeFns {
None,
)
});
ctx.builder.build_call(at_mu, &[t.into()], "at_mu");
ctx.builder.build_call(at_mu, &[t.into()], "at_mu").unwrap();
}
fn emit_delay_mu<'ctx, 'a>(
fn emit_delay_mu<'ctx>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
dt: BasicValueEnum<'ctx>,
) {
let delay_mu = ctx.module.get_function("delay_mu").unwrap_or_else(|| {
@ -297,7 +305,7 @@ impl TimeFns for ExternTimeFns {
None,
)
});
ctx.builder.build_call(delay_mu, &[dt.into()], "delay_mu");
ctx.builder.build_call(delay_mu, &[dt.into()], "delay_mu").unwrap();
}
}

View File

@ -2,7 +2,7 @@
name = "nac3ast"
version = "0.1.0"
authors = ["RustPython Team", "M-Labs"]
edition = "2018"
edition = "2021"
[features]
default = ["constant-optimization", "fold"]
@ -10,7 +10,7 @@ constant-optimization = ["fold"]
fold = []
[dependencies]
lazy_static = "1.4.0"
parking_lot = "0.11.1"
string-interner = "0.13.0"
fxhash = "0.2.1"
lazy_static = "1.4"
parking_lot = "0.12"
string-interner = "0.15"
fxhash = "0.2"

View File

@ -5,10 +5,10 @@ pub use crate::constant::*;
use std::{fmt, collections::HashMap, cell::RefCell};
use parking_lot::{Mutex, MutexGuard};
use string_interner::{DefaultBackend, DefaultSymbol, StringInterner, symbol::SymbolU32};
use string_interner::{DefaultBackend, StringInterner, symbol::SymbolU32};
use fxhash::FxBuildHasher;
pub type Interner = StringInterner<DefaultSymbol, DefaultBackend<DefaultSymbol>, FxBuildHasher>;
pub type Interner = StringInterner<DefaultBackend, FxBuildHasher>;
lazy_static! {
static ref INTERNER: Mutex<Interner> = Mutex::new(StringInterner::with_hasher(FxBuildHasher::default()));
}

View File

@ -1,8 +1,9 @@
//! Datatypes to support source location information.
use std::cmp::Ordering;
use crate::ast_gen::StrRef;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FileName(pub StrRef);
impl Default for FileName {
fn default() -> Self {
@ -17,7 +18,7 @@ impl From<String> for FileName {
}
/// A location somewhere in the sourcecode.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Location {
pub row: usize,
pub column: usize,
@ -26,7 +27,29 @@ pub struct Location {
impl fmt::Display for Location {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: line {} column {}", self.file.0, self.row, self.column)
write!(f, "{}:{}:{}", self.file.0, self.row, self.column)
}
}
impl Ord for Location {
fn cmp(&self, other: &Self) -> Ordering {
let file_cmp = self.file.0.to_string().cmp(&other.file.0.to_string());
if file_cmp != Ordering::Equal {
return file_cmp
}
let row_cmp = self.row.cmp(&other.row);
if row_cmp != Ordering::Equal {
return row_cmp
}
self.column.cmp(&other.column)
}
}
impl PartialOrd for Location {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

View File

@ -2,24 +2,25 @@
name = "nac3core"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2018"
edition = "2021"
[dependencies]
itertools = "0.10.1"
crossbeam = "0.8.1"
parking_lot = "0.11.1"
rayon = "1.5.1"
itertools = "0.12"
crossbeam = "0.8"
indexmap = "2.2"
parking_lot = "0.12"
rayon = "1.8"
nac3parser = { path = "../nac3parser" }
[dependencies.inkwell]
version = "0.1.0-beta.4"
version = "0.4"
default-features = false
features = ["llvm13-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
[dev-dependencies]
test-case = "1.2.0"
indoc = "1.0"
indoc = "2.0"
insta = "=1.11.0"
[build-dependencies]
regex = "1"
regex = "1.10"

View File

@ -9,19 +9,20 @@ use std::{
fn main() {
const FILE: &str = "src/codegen/irrt/irrt.c";
println!("cargo:rerun-if-changed={}", FILE);
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir);
/*
* HACK: Sadly, clang doesn't let us emit generic LLVM bitcode.
* Compiling for WASM32 and filtering the output with regex is the closest we can get.
*/
const FLAG: &[&str] = &[
let flags: &[&str] = &[
"--target=wasm32",
FILE,
"-O3",
"-fno-discard-value-names",
match env::var("PROFILE").as_deref() {
Ok("debug") => "-O0",
Ok("release") => "-O3",
flavor => panic!("Unknown or missing build flavor {flavor:?}"),
},
"-emit-llvm",
"-S",
"-Wall",
@ -29,8 +30,13 @@ fn main() {
"-o",
"-",
];
let output = Command::new("clang")
.args(FLAG)
println!("cargo:rerun-if-changed={FILE}");
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir);
let output = Command::new("clang-irrt")
.args(flags)
.output()
.map(|o| {
assert!(o.status.success(), "{}", std::str::from_utf8(&o.stderr).unwrap());
@ -42,9 +48,9 @@ fn main() {
let output = std::str::from_utf8(&output.stdout).unwrap().replace("\r\n", "\n");
let mut filtered_output = String::with_capacity(output.len());
let regex_filter = regex::Regex::new(r"(?ms:^define.*?\}$)|(?m:^declare.*?$)").unwrap();
let regex_filter = Regex::new(r"(?ms:^define.*?\}$)|(?m:^declare.*?$)").unwrap();
for f in regex_filter.captures_iter(&output) {
assert!(f.len() == 1);
assert_eq!(f.len(), 1);
filtered_output.push_str(&f[0]);
filtered_output.push('\n');
}
@ -61,12 +67,12 @@ fn main() {
file.write_all(filtered_output.as_bytes()).unwrap();
}
let mut llvm_as = Command::new("llvm-as")
let mut llvm_as = Command::new("llvm-as-irrt")
.stdin(Stdio::piped())
.arg("-o")
.arg(out_path.join("irrt.bc"))
.spawn()
.unwrap();
llvm_as.stdin.as_mut().unwrap().write_all(filtered_output.as_bytes()).unwrap();
assert!(llvm_as.wait().unwrap().success())
assert!(llvm_as.wait().unwrap().success());
}

File diff suppressed because it is too large Load Diff

View File

@ -3,12 +3,13 @@ use crate::{
toplevel::DefinitionId,
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap},
},
};
use nac3parser::ast::StrRef;
use std::collections::HashMap;
use indexmap::IndexMap;
pub struct ConcreteTypeStore {
store: Vec<ConcreteTypeEnum>,
@ -50,7 +51,7 @@ pub enum ConcreteTypeEnum {
TObj {
obj_id: DefinitionId,
fields: HashMap<StrRef, (ConcreteType, bool)>,
params: HashMap<u32, ConcreteType>,
params: IndexMap<u32, ConcreteType>,
},
TVirtual {
ty: ConcreteType,
@ -60,9 +61,13 @@ pub enum ConcreteTypeEnum {
ret: ConcreteType,
vars: HashMap<u32, ConcreteType>,
},
TLiteral {
values: Vec<SymbolValue>,
},
}
impl ConcreteTypeStore {
#[must_use]
pub fn new() -> ConcreteTypeStore {
ConcreteTypeStore {
store: vec![
@ -80,6 +85,7 @@ impl ConcreteTypeStore {
}
}
#[must_use]
pub fn get(&self, cty: ConcreteType) -> &ConcreteTypeEnum {
&self.store[cty.0]
}
@ -194,9 +200,12 @@ impl ConcreteTypeStore {
ty: self.from_unifier_type(unifier, primitives, *ty, cache),
},
TypeEnum::TFunc(signature) => {
self.from_signature(unifier, primitives, &*signature, cache)
self.from_signature(unifier, primitives, signature, cache)
}
_ => unreachable!(),
TypeEnum::TLiteral { values, .. } => ConcreteTypeEnum::TLiteral {
values: values.clone(),
},
_ => unreachable!("{:?}", ty_enum.get_type_name()),
};
let index = if let Some(ConcreteType(index)) = cache.get(&ty).unwrap() {
self.store[*index] = result;
@ -266,7 +275,7 @@ impl ConcreteTypeStore {
params: params
.iter()
.map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache)))
.collect::<HashMap<_, _>>(),
.collect::<VarMap>(),
},
ConcreteTypeEnum::TFunc { args, ret, vars } => TypeEnum::TFunc(FunSignature {
args: args
@ -281,8 +290,12 @@ impl ConcreteTypeStore {
vars: vars
.iter()
.map(|(id, cty)| (*id, self.to_unifier_type(unifier, primitives, *cty, cache)))
.collect::<HashMap<_, _>>(),
.collect::<VarMap>(),
}),
ConcreteTypeEnum::TLiteral { values, .. } => TypeEnum::TLiteral {
values: values.clone(),
loc: None,
}
};
let result = unifier.add_ty(result);
if let Some(ty) = cache.get(&cty).unwrap() {

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
use crate::{
codegen::{expr::*, stmt::*, CodeGenContext},
codegen::{classes::ArraySliceValue, expr::*, stmt::*, bool_to_i1, bool_to_i8, CodeGenContext},
symbol_resolver::ValueEnum,
toplevel::{DefinitionId, TopLevelDef},
typecheck::typedef::{FunSignature, Type},
@ -7,7 +7,7 @@ use crate::{
use inkwell::{
context::Context,
types::{BasicTypeEnum, IntType},
values::{BasicValueEnum, PointerValue},
values::{BasicValueEnum, IntValue, PointerValue},
};
use nac3parser::ast::{Expr, Stmt, StrRef};
@ -22,9 +22,9 @@ pub trait CodeGenerator {
/// - fun: Function signature and definition ID.
/// - params: Function parameters. Note that this does not include the object even if the
/// function is a class method.
fn gen_call<'ctx, 'a>(
fn gen_call<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
obj: Option<(Type, ValueEnum<'ctx>)>,
fun: (&FunSignature, DefinitionId),
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
@ -39,9 +39,9 @@ pub trait CodeGenerator {
/// - signature: Function signature of the constructor.
/// - def: Class definition for the constructor class.
/// - params: Function parameters.
fn gen_constructor<'ctx, 'a>(
fn gen_constructor<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
signature: &FunSignature,
def: &TopLevelDef,
params: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
@ -59,20 +59,20 @@ pub trait CodeGenerator {
/// function is a class method.
/// Note that this function should check if the function is generated in another thread (due to
/// possible race condition), see the default implementation for an example.
fn gen_func_instance<'ctx, 'a>(
fn gen_func_instance<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
obj: Option<(Type, ValueEnum<'ctx>)>,
fun: (&FunSignature, &mut TopLevelDef, String),
id: usize,
) -> Result<String, String> {
gen_func_instance(ctx, obj, fun, id)
gen_func_instance(ctx, &obj, fun, id)
}
/// Generate the code for an expression.
fn gen_expr<'ctx, 'a>(
fn gen_expr<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
expr: &Expr<Option<Type>>,
) -> Result<Option<ValueEnum<'ctx>>, String>
where
@ -83,30 +83,44 @@ pub trait CodeGenerator {
/// Allocate memory for a variable and return a pointer pointing to it.
/// The default implementation places the allocations at the start of the function.
fn gen_var_alloc<'ctx, 'a>(
fn gen_var_alloc<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
ty: BasicTypeEnum<'ctx>,
name: Option<&str>,
) -> Result<PointerValue<'ctx>, String> {
gen_var(ctx, ty)
gen_var(ctx, ty, name)
}
/// Allocate memory for a variable and return a pointer pointing to it.
/// The default implementation places the allocations at the start of the function.
fn gen_array_var_alloc<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, '_>,
ty: BasicTypeEnum<'ctx>,
size: IntValue<'ctx>,
name: Option<&'ctx str>,
) -> Result<ArraySliceValue<'ctx>, String> {
gen_array_var(ctx, ty, size, name)
}
/// Return a pointer pointing to the target of the expression.
fn gen_store_target<'ctx, 'a>(
fn gen_store_target<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
pattern: &Expr<Option<Type>>,
) -> Result<PointerValue<'ctx>, String>
name: Option<&str>,
) -> Result<Option<PointerValue<'ctx>>, String>
where
Self: Sized,
{
gen_store_target(self, ctx, pattern)
gen_store_target(self, ctx, pattern, name)
}
/// Generate code for an assignment expression.
fn gen_assign<'ctx, 'a>(
fn gen_assign<'ctx>(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
target: &Expr<Option<Type>>,
value: ValueEnum<'ctx>,
) -> Result<(), String>
@ -118,9 +132,9 @@ pub trait CodeGenerator {
/// Generate code for a while expression.
/// Return true if the while loop must early return
fn gen_while<'ctx, 'a>(
fn gen_while(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'_, '_>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String>
where
@ -131,9 +145,9 @@ pub trait CodeGenerator {
/// Generate code for a while expression.
/// Return true if the while loop must early return
fn gen_for<'ctx, 'a>(
fn gen_for(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'_, '_>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String>
where
@ -144,9 +158,9 @@ pub trait CodeGenerator {
/// Generate code for an if expression.
/// Return true if the statement must early return
fn gen_if<'ctx, 'a>(
fn gen_if(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'_, '_>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String>
where
@ -155,9 +169,9 @@ pub trait CodeGenerator {
gen_if(self, ctx, stmt)
}
fn gen_with<'ctx, 'a>(
fn gen_with(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'_, '_>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String>
where
@ -167,10 +181,11 @@ pub trait CodeGenerator {
}
/// Generate code for a statement
///
/// Return true if the statement must early return
fn gen_stmt<'ctx, 'a>(
fn gen_stmt(
&mut self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'_, '_>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String>
where
@ -178,6 +193,36 @@ pub trait CodeGenerator {
{
gen_stmt(self, ctx, stmt)
}
/// Generates code for a block statement.
fn gen_block<'a, I: Iterator<Item = &'a Stmt<Option<Type>>>>(
&mut self,
ctx: &mut CodeGenContext<'_, '_>,
stmts: I,
) -> Result<(), String>
where
Self: Sized,
{
gen_block(self, ctx, stmts)
}
/// See [`bool_to_i1`].
fn bool_to_i1<'ctx>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>
) -> IntValue<'ctx> {
bool_to_i1(&ctx.builder, bool_value)
}
/// See [`bool_to_i8`].
fn bool_to_i8<'ctx>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>
) -> IntValue<'ctx> {
bool_to_i8(&ctx.builder, ctx.ctx, bool_value)
}
}
pub struct DefaultCodeGenerator {
@ -186,17 +231,21 @@ pub struct DefaultCodeGenerator {
}
impl DefaultCodeGenerator {
#[must_use]
pub fn new(name: String, size_t: u32) -> DefaultCodeGenerator {
assert!(size_t == 32 || size_t == 64);
assert!(matches!(size_t, 32 | 64));
DefaultCodeGenerator { name, size_t }
}
}
impl CodeGenerator for DefaultCodeGenerator {
/// Returns the name for this [`CodeGenerator`].
fn get_name(&self) -> &str {
&self.name
}
/// Returns an LLVM integer type representing `size_t`.
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx> {
// it should be unsigned, but we don't really need unsigned and this could save us from
// having to do a bit cast...

View File

@ -1,13 +1,15 @@
typedef _ExtInt(8) int8_t;
typedef unsigned _ExtInt(8) uint8_t;
typedef _ExtInt(32) int32_t;
typedef unsigned _ExtInt(32) uint32_t;
typedef _ExtInt(64) int64_t;
typedef unsigned _ExtInt(64) uint64_t;
typedef _BitInt(8) int8_t;
typedef unsigned _BitInt(8) uint8_t;
typedef _BitInt(32) int32_t;
typedef unsigned _BitInt(32) uint32_t;
typedef _BitInt(64) int64_t;
typedef unsigned _BitInt(64) uint64_t;
# define MAX(a, b) (a > b ? a : b)
# define MIN(a, b) (a > b ? b : a)
# define NULL ((void *) 0)
// adapted from GNU Scientific Library: https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
// need to make sure `exp >= 0` before calling this function
#define DEF_INT_EXP(T) T __nac3_int_exp_##T( \
@ -137,4 +139,243 @@ int32_t __nac3_list_slice_assign_var_size(
return dest_arr_len - (dest_end - dest_ind) - 1;
}
return dest_arr_len;
}
}
int32_t __nac3_isinf(double x) {
return __builtin_isinf(x);
}
int32_t __nac3_isnan(double x) {
return __builtin_isnan(x);
}
double tgamma(double arg);
double __nac3_gamma(double z) {
// Handling for denormals
// | x | Python gamma(x) | C tgamma(x) |
// --- | ----------------- | --------------- | ----------- |
// (1) | nan | nan | nan |
// (2) | -inf | -inf | inf |
// (3) | inf | inf | inf |
// (4) | 0.0 | inf | inf |
// (5) | {-1.0, -2.0, ...} | inf | nan |
// (1)-(3)
if (__builtin_isinf(z) || __builtin_isnan(z)) {
return z;
}
double v = tgamma(z);
// (4)-(5)
return __builtin_isinf(v) || __builtin_isnan(v) ? __builtin_inf() : v;
}
double lgamma(double arg);
double __nac3_gammaln(double x) {
// libm's handling of value overflows differs from scipy:
// - scipy: gammaln(-inf) -> -inf
// - libm : lgamma(-inf) -> inf
if (__builtin_isinf(x)) {
return x;
}
return lgamma(x);
}
double j0(double x);
double __nac3_j0(double x) {
// libm's handling of value overflows differs from scipy:
// - scipy: j0(inf) -> nan
// - libm : j0(inf) -> 0.0
if (__builtin_isinf(x)) {
return __builtin_nan("");
}
return j0(x);
}
uint32_t __nac3_ndarray_calc_size(
const uint64_t *list_data,
uint32_t list_len
) {
uint32_t num_elems = 1;
for (uint32_t i = 0; i < list_len; ++i) {
uint64_t val = list_data[i];
__builtin_assume(val > 0);
num_elems *= val;
}
return num_elems;
}
uint64_t __nac3_ndarray_calc_size64(
const uint64_t *list_data,
uint64_t list_len
) {
uint64_t num_elems = 1;
for (uint64_t i = 0; i < list_len; ++i) {
uint64_t val = list_data[i];
__builtin_assume(val > 0);
num_elems *= val;
}
return num_elems;
}
void __nac3_ndarray_calc_nd_indices(
uint32_t index,
const uint32_t* dims,
uint32_t num_dims,
uint32_t* idxs
) {
uint32_t stride = 1;
for (uint32_t dim = 0; dim < num_dims; dim++) {
uint32_t i = num_dims - dim - 1;
__builtin_assume(dims[i] > 0);
idxs[i] = (index / stride) % dims[i];
stride *= dims[i];
}
}
void __nac3_ndarray_calc_nd_indices64(
uint64_t index,
const uint64_t* dims,
uint64_t num_dims,
uint32_t* idxs
) {
uint64_t stride = 1;
for (uint64_t dim = 0; dim < num_dims; dim++) {
uint64_t i = num_dims - dim - 1;
__builtin_assume(dims[i] > 0);
idxs[i] = (uint32_t) ((index / stride) % dims[i]);
stride *= dims[i];
}
}
uint32_t __nac3_ndarray_flatten_index(
const uint32_t* dims,
uint32_t num_dims,
const uint32_t* indices,
uint32_t num_indices
) {
uint32_t idx = 0;
uint32_t stride = 1;
for (uint32_t i = 0; i < num_dims; ++i) {
uint32_t ri = num_dims - i - 1;
if (ri < num_indices) {
idx += (stride * indices[ri]);
}
__builtin_assume(dims[i] > 0);
stride *= dims[ri];
}
return idx;
}
uint64_t __nac3_ndarray_flatten_index64(
const uint64_t* dims,
uint64_t num_dims,
const uint32_t* indices,
uint64_t num_indices
) {
uint64_t idx = 0;
uint64_t stride = 1;
for (uint64_t i = 0; i < num_dims; ++i) {
uint64_t ri = num_dims - i - 1;
if (ri < num_indices) {
idx += (stride * indices[ri]);
}
__builtin_assume(dims[i] > 0);
stride *= dims[ri];
}
return idx;
}
void __nac3_ndarray_calc_broadcast(
const uint32_t *lhs_dims,
uint32_t lhs_ndims,
const uint32_t *rhs_dims,
uint32_t rhs_ndims,
uint32_t *out_dims
) {
uint32_t max_ndims = lhs_ndims > rhs_ndims ? lhs_ndims : rhs_ndims;
for (uint32_t i = 0; i < max_ndims; ++i) {
uint32_t *lhs_dim_sz = i < lhs_ndims ? &lhs_dims[lhs_ndims - i - 1] : NULL;
uint32_t *rhs_dim_sz = i < rhs_ndims ? &rhs_dims[rhs_ndims - i - 1] : NULL;
uint32_t *out_dim = &out_dims[max_ndims - i - 1];
if (lhs_dim_sz == NULL) {
*out_dim = *rhs_dim_sz;
} else if (rhs_dim_sz == NULL) {
*out_dim = *lhs_dim_sz;
} else if (*lhs_dim_sz == 1) {
*out_dim = *rhs_dim_sz;
} else if (*rhs_dim_sz == 1) {
*out_dim = *lhs_dim_sz;
} else if (*lhs_dim_sz == *rhs_dim_sz) {
*out_dim = *lhs_dim_sz;
} else {
__builtin_unreachable();
}
}
}
void __nac3_ndarray_calc_broadcast64(
const uint64_t *lhs_dims,
uint64_t lhs_ndims,
const uint64_t *rhs_dims,
uint64_t rhs_ndims,
uint64_t *out_dims
) {
uint64_t max_ndims = lhs_ndims > rhs_ndims ? lhs_ndims : rhs_ndims;
for (uint64_t i = 0; i < max_ndims; ++i) {
uint64_t *lhs_dim_sz = i < lhs_ndims ? &lhs_dims[lhs_ndims - i - 1] : NULL;
uint64_t *rhs_dim_sz = i < rhs_ndims ? &rhs_dims[rhs_ndims - i - 1] : NULL;
uint64_t *out_dim = &out_dims[max_ndims - i - 1];
if (lhs_dim_sz == NULL) {
*out_dim = *rhs_dim_sz;
} else if (rhs_dim_sz == NULL) {
*out_dim = *lhs_dim_sz;
} else if (*lhs_dim_sz == 1) {
*out_dim = *rhs_dim_sz;
} else if (*rhs_dim_sz == 1) {
*out_dim = *lhs_dim_sz;
} else if (*lhs_dim_sz == *rhs_dim_sz) {
*out_dim = *lhs_dim_sz;
} else {
__builtin_unreachable();
}
}
}
void __nac3_ndarray_calc_broadcast_idx(
const uint32_t *src_dims,
uint32_t src_ndims,
const uint32_t *in_idx,
uint32_t *out_idx
) {
for (uint32_t i = 0; i < src_ndims; ++i) {
uint32_t src_i = src_ndims - i - 1;
out_idx[src_i] = src_dims[src_i] == 1 ? 0 : in_idx[src_i];
}
}
void __nac3_ndarray_calc_broadcast_idx64(
const uint64_t *src_dims,
uint64_t src_ndims,
const uint32_t *in_idx,
uint32_t *out_idx
) {
for (uint64_t i = 0; i < src_ndims; ++i) {
uint64_t src_i = src_ndims - i - 1;
out_idx[src_i] = src_dims[src_i] == 1 ? 0 : (uint32_t) in_idx[src_i];
}
}

View File

@ -1,17 +1,34 @@
use crate::typecheck::typedef::Type;
use super::{CodeGenContext, CodeGenerator};
use super::{
classes::{
ArrayLikeIndexer,
ArrayLikeValue,
ArraySliceValue,
ListValue,
NDArrayValue,
TypedArrayLikeAdapter,
UntypedArrayLikeAccessor,
},
CodeGenContext,
CodeGenerator,
llvm_intrinsics,
};
use inkwell::{
attributes::{Attribute, AttributeLoc},
context::Context,
memory_buffer::MemoryBuffer,
module::Module,
types::BasicTypeEnum,
values::{IntValue, PointerValue},
types::{BasicTypeEnum, IntType},
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue},
AddressSpace, IntPredicate,
};
use itertools::Either;
use nac3parser::ast::Expr;
use crate::codegen::classes::TypedArrayLikeAccessor;
use crate::codegen::stmt::gen_for_callback_incrementing;
#[must_use]
pub fn load_irrt(ctx: &Context) -> Module {
let bitcode_buf = MemoryBuffer::create_from_memory_range(
include_bytes!(concat!(env!("OUT_DIR"), "/irrt.bc")),
@ -33,9 +50,9 @@ pub fn load_irrt(ctx: &Context) -> Module {
// repeated squaring method adapted from GNU Scientific Library:
// https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
pub fn integer_power<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>,
pub fn integer_power<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
base: IntValue<'ctx>,
exp: IntValue<'ctx>,
signed: bool,
@ -58,7 +75,7 @@ pub fn integer_power<'ctx, 'a>(
exp,
exp.get_type().const_zero(),
"assert_int_pow_ge_0",
);
).unwrap();
ctx.make_assert(
generator,
ge_zero,
@ -69,14 +86,15 @@ pub fn integer_power<'ctx, 'a>(
);
ctx.builder
.build_call(pow_fun, &[base.into(), exp.into()], "call_int_pow")
.try_as_basic_value()
.unwrap_left()
.into_int_value()
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
pub fn calculate_len_for_slice_range<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>,
pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
start: IntValue<'ctx>,
end: IntValue<'ctx>,
step: IntValue<'ctx>,
@ -94,7 +112,7 @@ pub fn calculate_len_for_slice_range<'ctx, 'a>(
step,
step.get_type().const_zero(),
"range_step_ne",
);
).unwrap();
ctx.make_assert(
generator,
not_zero,
@ -105,10 +123,10 @@ pub fn calculate_len_for_slice_range<'ctx, 'a>(
);
ctx.builder
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
.try_as_basic_value()
.left()
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
.into_int_value()
}
/// NOTE: the output value of the end index of this function should be compared ***inclusively***,
@ -151,47 +169,55 @@ pub fn calculate_len_for_slice_range<'ctx, 'a>(
/// ,step
/// )
/// ```
pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
pub fn handle_slice_indices<'ctx, G: CodeGenerator>(
start: &Option<Box<Expr<Option<Type>>>>,
end: &Option<Box<Expr<Option<Type>>>>,
step: &Option<Box<Expr<Option<Type>>>>,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
generator: &mut G,
list: PointerValue<'ctx>,
) -> Result<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), String> {
list: ListValue<'ctx>,
) -> Result<Option<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>)>, String> {
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let one = int32.const_int(1, false);
let length = ctx.build_gep_and_load(list, &[zero, one]).into_int_value();
let length = ctx.builder.build_int_truncate_or_bit_cast(length, int32, "leni32");
Ok(match (start, end, step) {
let length = list.load_size(ctx, Some("length"));
let length = ctx.builder.build_int_truncate_or_bit_cast(length, int32, "leni32").unwrap();
Ok(Some(match (start, end, step) {
(s, e, None) => (
s.as_ref().map_or_else(
|| Ok(int32.const_zero()),
|s| handle_slice_index_bound(s, ctx, generator, length),
)?,
if let Some(s) = s.as_ref() {
match handle_slice_index_bound(s, ctx, generator, length)? {
Some(v) => v,
None => return Ok(None),
}
} else {
int32.const_zero()
},
{
let e = e.as_ref().map_or_else(
|| Ok(length),
|e| handle_slice_index_bound(e, ctx, generator, length),
)?;
ctx.builder.build_int_sub(e, one, "final_end")
let e = if let Some(s) = e.as_ref() {
match handle_slice_index_bound(s, ctx, generator, length)? {
Some(v) => v,
None => return Ok(None),
}
} else {
length
};
ctx.builder.build_int_sub(e, one, "final_end").unwrap()
},
one,
),
(s, e, Some(step)) => {
let step = generator
.gen_expr(ctx, step)?
.unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.int32)?
.into_int_value();
let step = if let Some(v) = generator.gen_expr(ctx, step)? {
v.to_basic_value_enum(ctx, generator, ctx.primitives.int32)?.into_int_value()
} else {
return Ok(None)
};
// assert step != 0, throw exception if not
let not_zero = ctx.builder.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
);
).unwrap();
ctx.make_assert(
generator,
not_zero,
@ -200,12 +226,14 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
[None, None, None],
ctx.current_loc,
);
let len_id = ctx.builder.build_int_sub(length, one, "lenmin1");
let neg = ctx.builder.build_int_compare(IntPredicate::SLT, step, zero, "step_is_neg");
let len_id = ctx.builder.build_int_sub(length, one, "lenmin1").unwrap();
let neg = ctx.builder.build_int_compare(IntPredicate::SLT, step, zero, "step_is_neg").unwrap();
(
match s {
Some(s) => {
let s = handle_slice_index_bound(s, ctx, generator, length)?;
let Some(s) = handle_slice_index_bound(s, ctx, generator, length)? else {
return Ok(None)
};
ctx.builder
.build_select(
ctx.builder.build_and(
@ -214,46 +242,54 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
s,
length,
"s_eq_len",
),
).unwrap(),
neg,
"should_minus_one",
),
ctx.builder.build_int_sub(s, one, "s_min"),
).unwrap(),
ctx.builder.build_int_sub(s, one, "s_min").unwrap(),
s,
"final_start",
)
.into_int_value()
.map(BasicValueEnum::into_int_value)
.unwrap()
}
None => ctx.builder.build_select(neg, len_id, zero, "stt").into_int_value(),
None => ctx.builder.build_select(neg, len_id, zero, "stt")
.map(BasicValueEnum::into_int_value)
.unwrap(),
},
match e {
Some(e) => {
let e = handle_slice_index_bound(e, ctx, generator, length)?;
let Some(e) = handle_slice_index_bound(e, ctx, generator, length)? else {
return Ok(None)
};
ctx.builder
.build_select(
neg,
ctx.builder.build_int_add(e, one, "end_add_one"),
ctx.builder.build_int_sub(e, one, "end_sub_one"),
ctx.builder.build_int_add(e, one, "end_add_one").unwrap(),
ctx.builder.build_int_sub(e, one, "end_sub_one").unwrap(),
"final_end",
)
.into_int_value()
.map(BasicValueEnum::into_int_value)
.unwrap()
}
None => ctx.builder.build_select(neg, zero, len_id, "end").into_int_value(),
None => ctx.builder.build_select(neg, zero, len_id, "end")
.map(BasicValueEnum::into_int_value)
.unwrap(),
},
step,
)
}
})
}))
}
/// this function allows index out of range, since python
/// allows index out of range in slice (`a = [1,2,3]; a[1:10] == [2,3]`).
pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>(
i: &Expr<Option<Type>>,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
generator: &mut G,
length: IntValue<'ctx>,
) -> Result<IntValue<'ctx>, String> {
) -> Result<Option<IntValue<'ctx>>, String> {
const SYMBOL: &str = "__nac3_slice_index_bound";
let func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
let i32_t = ctx.ctx.i32_type();
@ -261,30 +297,34 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
ctx.module.add_function(SYMBOL, fn_t, None)
});
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator, i.custom.unwrap())?;
Ok(ctx
let i = if let Some(v) = generator.gen_expr(ctx, i)? {
v.to_basic_value_enum(ctx, generator, i.custom.unwrap())?
} else {
return Ok(None)
};
Ok(Some(ctx
.builder
.build_call(func, &[i.into(), length.into()], "bounded_ind")
.try_as_basic_value()
.left()
.unwrap()
.into_int_value())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()))
}
/// This function handles 'end' **inclusively**.
/// Order of tuples assign_idx and value_idx is ('start', 'end', 'step').
/// Order of tuples `assign_idx` and `value_idx` is ('start', 'end', 'step').
/// Negative index should be handled before entering this function
pub fn list_slice_assignment<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>,
pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
ty: BasicTypeEnum<'ctx>,
dest_arr: PointerValue<'ctx>,
dest_arr: ListValue<'ctx>,
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
src_arr: PointerValue<'ctx>,
src_arr: ListValue<'ctx>,
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
) {
let size_ty = generator.get_size_type(ctx.ctx);
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::Generic);
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
let int32 = ctx.ctx.i32_type();
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr);
let slice_assign_fun = {
@ -309,22 +349,22 @@ pub fn list_slice_assignment<'ctx, 'a>(
let zero = int32.const_zero();
let one = int32.const_int(1, false);
let dest_arr_ptr = ctx.build_gep_and_load(dest_arr, &[zero, zero]);
let dest_arr_ptr = dest_arr.data().base_ptr(ctx, generator);
let dest_arr_ptr = ctx.builder.build_pointer_cast(
dest_arr_ptr.into_pointer_value(),
dest_arr_ptr,
elem_ptr_type,
"dest_arr_ptr_cast",
);
let dest_len = ctx.build_gep_and_load(dest_arr, &[zero, one]).into_int_value();
let dest_len = ctx.builder.build_int_truncate_or_bit_cast(dest_len, int32, "srclen32");
let src_arr_ptr = ctx.build_gep_and_load(src_arr, &[zero, zero]);
).unwrap();
let dest_len = dest_arr.load_size(ctx, Some("dest.len"));
let dest_len = ctx.builder.build_int_truncate_or_bit_cast(dest_len, int32, "srclen32").unwrap();
let src_arr_ptr = src_arr.data().base_ptr(ctx, generator);
let src_arr_ptr = ctx.builder.build_pointer_cast(
src_arr_ptr.into_pointer_value(),
src_arr_ptr,
elem_ptr_type,
"src_arr_ptr_cast",
);
let src_len = ctx.build_gep_and_load(src_arr, &[zero, one]).into_int_value();
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32");
).unwrap();
let src_len = src_arr.load_size(ctx, Some("src.len"));
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32").unwrap();
// index in bound and positive should be done
// assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
@ -332,29 +372,31 @@ pub fn list_slice_assignment<'ctx, 'a>(
let src_end = ctx.builder
.build_select(
ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
IntPredicate::SLT,
src_idx.2,
zero,
"is_neg",
),
ctx.builder.build_int_sub(src_idx.1, one, "e_min_one"),
ctx.builder.build_int_add(src_idx.1, one, "e_add_one"),
).unwrap(),
ctx.builder.build_int_sub(src_idx.1, one, "e_min_one").unwrap(),
ctx.builder.build_int_add(src_idx.1, one, "e_add_one").unwrap(),
"final_e",
)
.into_int_value();
.map(BasicValueEnum::into_int_value)
.unwrap();
let dest_end = ctx.builder
.build_select(
ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
IntPredicate::SLT,
dest_idx.2,
zero,
"is_neg",
),
ctx.builder.build_int_sub(dest_idx.1, one, "e_min_one"),
ctx.builder.build_int_add(dest_idx.1, one, "e_add_one"),
).unwrap(),
ctx.builder.build_int_sub(dest_idx.1, one, "e_min_one").unwrap(),
ctx.builder.build_int_add(dest_idx.1, one, "e_add_one").unwrap(),
"final_e",
)
.into_int_value();
.map(BasicValueEnum::into_int_value)
.unwrap();
let src_slice_len =
calculate_len_for_slice_range(generator, ctx, src_idx.0, src_end, src_idx.2);
let dest_slice_len =
@ -364,21 +406,21 @@ pub fn list_slice_assignment<'ctx, 'a>(
src_slice_len,
dest_slice_len,
"slice_src_eq_dest",
);
).unwrap();
let src_slt_dest = ctx.builder.build_int_compare(
IntPredicate::SLT,
src_slice_len,
dest_slice_len,
"slice_src_slt_dest",
);
).unwrap();
let dest_step_eq_one = ctx.builder.build_int_compare(
IntPredicate::EQ,
dest_idx.2,
dest_idx.2.get_type().const_int(1, false),
"slice_dest_step_eq_one",
);
let cond_1 = ctx.builder.build_and(dest_step_eq_one, src_slt_dest, "slice_cond_1");
let cond = ctx.builder.build_or(src_eq_dest, cond_1, "slice_cond");
).unwrap();
let cond_1 = ctx.builder.build_and(dest_step_eq_one, src_slt_dest, "slice_cond_1").unwrap();
let cond = ctx.builder.build_or(src_eq_dest, cond_1, "slice_cond").unwrap();
ctx.make_assert(
generator,
cond,
@ -408,27 +450,532 @@ pub fn list_slice_assignment<'ctx, 'a>(
BasicTypeEnum::StructType(t) => t.size_of().unwrap(),
_ => unreachable!(),
};
ctx.builder.build_int_truncate_or_bit_cast(s, int32, "size")
ctx.builder.build_int_truncate_or_bit_cast(s, int32, "size").unwrap()
}
.into(),
];
ctx.builder
.build_call(slice_assign_fun, args.as_slice(), "slice_assign")
.try_as_basic_value()
.unwrap_left()
.into_int_value()
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
};
// update length
let need_update =
ctx.builder.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update");
let need_update = ctx.builder
.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update")
.unwrap();
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let update_bb = ctx.ctx.append_basic_block(current, "update");
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb);
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap();
ctx.builder.position_at_end(update_bb);
let dest_len_ptr = unsafe { ctx.builder.build_gep(dest_arr, &[zero, one], "dest_len_ptr") };
let new_len = ctx.builder.build_int_z_extend_or_bit_cast(new_len, size_ty, "new_len");
ctx.builder.build_store(dest_len_ptr, new_len);
ctx.builder.build_unconditional_branch(cont_bb);
let new_len = ctx.builder
.build_int_z_extend_or_bit_cast(new_len, size_ty, "new_len")
.unwrap();
dest_arr.store_size(ctx, generator, new_len);
ctx.builder.build_unconditional_branch(cont_bb).unwrap();
ctx.builder.position_at_end(cont_bb);
}
/// Generates a call to `isinf` in IR. Returns an `i1` representing the result.
pub fn call_isinf<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
) -> IntValue<'ctx> {
let intrinsic_fn = ctx.module.get_function("__nac3_isinf").unwrap_or_else(|| {
let fn_type = ctx.ctx.i32_type().fn_type(&[ctx.ctx.f64_type().into()], false);
ctx.module.add_function("__nac3_isinf", fn_type, None)
});
let ret = ctx.builder
.build_call(intrinsic_fn, &[v.into()], "isinf")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap();
generator.bool_to_i1(ctx, ret)
}
/// Generates a call to `isnan` in IR. Returns an `i1` representing the result.
pub fn call_isnan<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
) -> IntValue<'ctx> {
let intrinsic_fn = ctx.module.get_function("__nac3_isnan").unwrap_or_else(|| {
let fn_type = ctx.ctx.i32_type().fn_type(&[ctx.ctx.f64_type().into()], false);
ctx.module.add_function("__nac3_isnan", fn_type, None)
});
let ret = ctx.builder
.build_call(intrinsic_fn, &[v.into()], "isnan")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap();
generator.bool_to_i1(ctx, ret)
}
/// Generates a call to `gamma` in IR. Returns an `f64` representing the result.
pub fn call_gamma<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
) -> FloatValue<'ctx> {
let llvm_f64 = ctx.ctx.f64_type();
let intrinsic_fn = ctx.module.get_function("__nac3_gamma").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_gamma", fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[v.into()], "gamma")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Generates a call to `gammaln` in IR. Returns an `f64` representing the result.
pub fn call_gammaln<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
) -> FloatValue<'ctx> {
let llvm_f64 = ctx.ctx.f64_type();
let intrinsic_fn = ctx.module.get_function("__nac3_gammaln").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_gammaln", fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[v.into()], "gammaln")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Generates a call to `j0` in IR. Returns an `f64` representing the result.
pub fn call_j0<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
v: FloatValue<'ctx>,
) -> FloatValue<'ctx> {
let llvm_f64 = ctx.ctx.f64_type();
let intrinsic_fn = ctx.module.get_function("__nac3_j0").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_j0", fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[v.into()], "j0")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Generates a call to `__nac3_ndarray_calc_size`. Returns an [`IntValue`] representing the
/// calculated total size.
///
/// * `num_dims` - An [`IntValue`] containing the number of dimensions.
/// * `dims` - A [`PointerValue`] to an array containing the size of each dimension.
pub fn call_ndarray_calc_size<'ctx, G, Dims>(
generator: &G,
ctx: &CodeGenContext<'ctx, '_>,
dims: &Dims,
) -> IntValue<'ctx>
where
G: CodeGenerator + ?Sized,
Dims: ArrayLikeIndexer<'ctx>, {
let llvm_i64 = ctx.ctx.i64_type();
let llvm_usize = generator.get_size_type(ctx.ctx);
let llvm_pi64 = llvm_i64.ptr_type(AddressSpace::default());
let ndarray_calc_size_fn_name = match llvm_usize.get_bit_width() {
32 => "__nac3_ndarray_calc_size",
64 => "__nac3_ndarray_calc_size64",
bw => unreachable!("Unsupported size type bit width: {}", bw)
};
let ndarray_calc_size_fn_t = llvm_usize.fn_type(
&[
llvm_pi64.into(),
llvm_usize.into(),
],
false,
);
let ndarray_calc_size_fn = ctx.module.get_function(ndarray_calc_size_fn_name)
.unwrap_or_else(|| {
ctx.module.add_function(ndarray_calc_size_fn_name, ndarray_calc_size_fn_t, None)
});
ctx.builder
.build_call(
ndarray_calc_size_fn,
&[
dims.base_ptr(ctx, generator).into(),
dims.size(ctx, generator).into(),
],
"",
)
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Generates a call to `__nac3_ndarray_calc_nd_indices`. Returns a [`TypeArrayLikeAdpater`]
/// containing `i32` indices of the flattened index.
///
/// * `index` - The index to compute the multidimensional index for.
/// * `ndarray` - LLVM pointer to the `NDArray`. This value must be the LLVM representation of an
/// `NDArray`.
pub fn call_ndarray_calc_nd_indices<'ctx, G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &mut CodeGenContext<'ctx, '_>,
index: IntValue<'ctx>,
ndarray: NDArrayValue<'ctx>,
) -> TypedArrayLikeAdapter<'ctx, IntValue<'ctx>> {
let llvm_void = ctx.ctx.void_type();
let llvm_i32 = ctx.ctx.i32_type();
let llvm_usize = generator.get_size_type(ctx.ctx);
let llvm_pi32 = llvm_i32.ptr_type(AddressSpace::default());
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
let ndarray_calc_nd_indices_fn_name = match llvm_usize.get_bit_width() {
32 => "__nac3_ndarray_calc_nd_indices",
64 => "__nac3_ndarray_calc_nd_indices64",
bw => unreachable!("Unsupported size type bit width: {}", bw)
};
let ndarray_calc_nd_indices_fn = ctx.module.get_function(ndarray_calc_nd_indices_fn_name).unwrap_or_else(|| {
let fn_type = llvm_void.fn_type(
&[
llvm_usize.into(),
llvm_pusize.into(),
llvm_usize.into(),
llvm_pi32.into(),
],
false,
);
ctx.module.add_function(ndarray_calc_nd_indices_fn_name, fn_type, None)
});
let ndarray_num_dims = ndarray.load_ndims(ctx);
let ndarray_dims = ndarray.dim_sizes();
let indices = ctx.builder.build_array_alloca(
llvm_i32,
ndarray_num_dims,
"",
).unwrap();
ctx.builder
.build_call(
ndarray_calc_nd_indices_fn,
&[
index.into(),
ndarray_dims.base_ptr(ctx, generator).into(),
ndarray_num_dims.into(),
indices.into(),
],
"",
)
.unwrap();
TypedArrayLikeAdapter::from(
ArraySliceValue::from_ptr_val(indices, ndarray_num_dims, None),
Box::new(|_, v| v.into_int_value()),
Box::new(|_, v| v.into()),
)
}
fn call_ndarray_flatten_index_impl<'ctx, G, Indices>(
generator: &G,
ctx: &CodeGenContext<'ctx, '_>,
ndarray: NDArrayValue<'ctx>,
indices: &Indices,
) -> IntValue<'ctx>
where
G: CodeGenerator + ?Sized,
Indices: ArrayLikeIndexer<'ctx>, {
let llvm_i32 = ctx.ctx.i32_type();
let llvm_usize = generator.get_size_type(ctx.ctx);
let llvm_pi32 = llvm_i32.ptr_type(AddressSpace::default());
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
debug_assert_eq!(
IntType::try_from(indices.element_type(ctx, generator))
.map(IntType::get_bit_width)
.unwrap_or_default(),
llvm_i32.get_bit_width(),
"Expected i32 value for argument `indices` to `call_ndarray_flatten_index_impl`"
);
debug_assert_eq!(
indices.size(ctx, generator).get_type().get_bit_width(),
llvm_usize.get_bit_width(),
"Expected usize integer value for argument `indices_size` to `call_ndarray_flatten_index_impl`"
);
let ndarray_flatten_index_fn_name = match llvm_usize.get_bit_width() {
32 => "__nac3_ndarray_flatten_index",
64 => "__nac3_ndarray_flatten_index64",
bw => unreachable!("Unsupported size type bit width: {}", bw)
};
let ndarray_flatten_index_fn = ctx.module.get_function(ndarray_flatten_index_fn_name).unwrap_or_else(|| {
let fn_type = llvm_usize.fn_type(
&[
llvm_pusize.into(),
llvm_usize.into(),
llvm_pi32.into(),
llvm_usize.into(),
],
false,
);
ctx.module.add_function(ndarray_flatten_index_fn_name, fn_type, None)
});
let ndarray_num_dims = ndarray.load_ndims(ctx);
let ndarray_dims = ndarray.dim_sizes();
let index = ctx.builder
.build_call(
ndarray_flatten_index_fn,
&[
ndarray_dims.base_ptr(ctx, generator).into(),
ndarray_num_dims.into(),
indices.base_ptr(ctx, generator).into(),
indices.size(ctx, generator).into(),
],
"",
)
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap();
index
}
/// Generates a call to `__nac3_ndarray_flatten_index`. Returns the flattened index for the
/// multidimensional index.
///
/// * `ndarray` - LLVM pointer to the `NDArray`. This value must be the LLVM representation of an
/// `NDArray`.
/// * `indices` - The multidimensional index to compute the flattened index for.
pub fn call_ndarray_flatten_index<'ctx, G, Index>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
ndarray: NDArrayValue<'ctx>,
indices: &Index,
) -> IntValue<'ctx>
where
G: CodeGenerator + ?Sized,
Index: ArrayLikeIndexer<'ctx>, {
call_ndarray_flatten_index_impl(
generator,
ctx,
ndarray,
indices,
)
}
/// Generates a call to `__nac3_ndarray_calc_broadcast`. Returns a tuple containing the number of
/// dimension and size of each dimension of the resultant `ndarray`.
pub fn call_ndarray_calc_broadcast<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
lhs: NDArrayValue<'ctx>,
rhs: NDArrayValue<'ctx>,
) -> TypedArrayLikeAdapter<'ctx, IntValue<'ctx>> {
let llvm_usize = generator.get_size_type(ctx.ctx);
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
let ndarray_calc_broadcast_fn_name = match llvm_usize.get_bit_width() {
32 => "__nac3_ndarray_calc_broadcast",
64 => "__nac3_ndarray_calc_broadcast64",
bw => unreachable!("Unsupported size type bit width: {}", bw)
};
let ndarray_calc_broadcast_fn = ctx.module.get_function(ndarray_calc_broadcast_fn_name).unwrap_or_else(|| {
let fn_type = llvm_usize.fn_type(
&[
llvm_pusize.into(),
llvm_usize.into(),
llvm_pusize.into(),
llvm_usize.into(),
llvm_pusize.into(),
],
false,
);
ctx.module.add_function(ndarray_calc_broadcast_fn_name, fn_type, None)
});
let lhs_ndims = lhs.load_ndims(ctx);
let rhs_ndims = rhs.load_ndims(ctx);
let min_ndims = llvm_intrinsics::call_int_umin(ctx, lhs_ndims, rhs_ndims, None);
gen_for_callback_incrementing(
generator,
ctx,
llvm_usize.const_zero(),
(min_ndims, false),
|generator, ctx, idx| {
let idx = ctx.builder.build_int_sub(min_ndims, idx, "").unwrap();
let (lhs_dim_sz, rhs_dim_sz) = unsafe {
(
lhs.dim_sizes().get_typed_unchecked(ctx, generator, &idx, None),
rhs.dim_sizes().get_typed_unchecked(ctx, generator, &idx, None),
)
};
let llvm_usize_const_one = llvm_usize.const_int(1, false);
let lhs_eqz = ctx.builder.build_int_compare(
IntPredicate::EQ,
lhs_dim_sz,
llvm_usize_const_one,
"",
).unwrap();
let rhs_eqz = ctx.builder.build_int_compare(
IntPredicate::EQ,
rhs_dim_sz,
llvm_usize_const_one,
"",
).unwrap();
let lhs_or_rhs_eqz = ctx.builder.build_or(
lhs_eqz,
rhs_eqz,
""
).unwrap();
let lhs_eq_rhs = ctx.builder.build_int_compare(
IntPredicate::EQ,
lhs_dim_sz,
rhs_dim_sz,
""
).unwrap();
let is_compatible = ctx.builder.build_or(
lhs_or_rhs_eqz,
lhs_eq_rhs,
""
).unwrap();
ctx.make_assert(
generator,
is_compatible,
"0:ValueError",
"operands could not be broadcast together",
[None, None, None],
ctx.current_loc,
);
Ok(())
},
llvm_usize.const_int(1, false),
).unwrap();
let max_ndims = llvm_intrinsics::call_int_umax(ctx, lhs_ndims, rhs_ndims, None);
let lhs_dims = lhs.dim_sizes().base_ptr(ctx, generator);
let lhs_ndims = lhs.load_ndims(ctx);
let rhs_dims = rhs.dim_sizes().base_ptr(ctx, generator);
let rhs_ndims = rhs.load_ndims(ctx);
let out_dims = ctx.builder.build_array_alloca(llvm_usize, max_ndims, "").unwrap();
let out_dims = ArraySliceValue::from_ptr_val(out_dims, max_ndims, None);
ctx.builder
.build_call(
ndarray_calc_broadcast_fn,
&[
lhs_dims.into(),
lhs_ndims.into(),
rhs_dims.into(),
rhs_ndims.into(),
out_dims.base_ptr(ctx, generator).into(),
],
"",
)
.unwrap();
TypedArrayLikeAdapter::from(
out_dims,
Box::new(|_, v| v.into_int_value()),
Box::new(|_, v| v.into()),
)
}
/// Generates a call to `__nac3_ndarray_calc_broadcast_idx`. Returns an [`ArrayAllocaValue`]
/// containing the indices used for accessing `array` corresponding to the index of the broadcasted
/// array `broadcast_idx`.
pub fn call_ndarray_calc_broadcast_index<'ctx, G: CodeGenerator + ?Sized, BroadcastIdx: UntypedArrayLikeAccessor<'ctx>>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
array: NDArrayValue<'ctx>,
broadcast_idx: &BroadcastIdx,
) -> TypedArrayLikeAdapter<'ctx, IntValue<'ctx>> {
let llvm_i32 = ctx.ctx.i32_type();
let llvm_usize = generator.get_size_type(ctx.ctx);
let llvm_pi32 = llvm_i32.ptr_type(AddressSpace::default());
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
let ndarray_calc_broadcast_fn_name = match llvm_usize.get_bit_width() {
32 => "__nac3_ndarray_calc_broadcast_idx",
64 => "__nac3_ndarray_calc_broadcast_idx64",
bw => unreachable!("Unsupported size type bit width: {}", bw)
};
let ndarray_calc_broadcast_fn = ctx.module.get_function(ndarray_calc_broadcast_fn_name).unwrap_or_else(|| {
let fn_type = llvm_usize.fn_type(
&[
llvm_pusize.into(),
llvm_usize.into(),
llvm_pi32.into(),
llvm_pi32.into(),
],
false,
);
ctx.module.add_function(ndarray_calc_broadcast_fn_name, fn_type, None)
});
let broadcast_size = broadcast_idx.size(ctx, generator);
let out_idx = ctx.builder.build_array_alloca(llvm_i32, broadcast_size, "").unwrap();
let array_dims = array.dim_sizes().base_ptr(ctx, generator);
let array_ndims = array.load_ndims(ctx);
let broadcast_idx_ptr = unsafe {
broadcast_idx.ptr_offset_unchecked(
ctx,
generator,
&llvm_usize.const_zero(),
None
)
};
ctx.builder
.build_call(
ndarray_calc_broadcast_fn,
&[
array_dims.into(),
array_ndims.into(),
broadcast_idx_ptr.into(),
out_idx.into(),
],
"",
)
.unwrap();
TypedArrayLikeAdapter::from(
ArraySliceValue::from_ptr_val(out_idx, broadcast_size, None),
Box::new(|_, v| v.into_int_value()),
Box::new(|_, v| v.into()),
)
}

View File

@ -0,0 +1,562 @@
use inkwell::AddressSpace;
use inkwell::context::Context;
use inkwell::types::AnyTypeEnum::IntType;
use inkwell::types::FloatType;
use inkwell::values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue, PointerValue};
use itertools::Either;
use crate::codegen::CodeGenContext;
/// Returns the string representation for the floating-point type `ft` when used in intrinsic
/// functions.
fn get_float_intrinsic_repr(ctx: &Context, ft: FloatType) -> &'static str {
// Standard LLVM floating-point types
if ft == ctx.f16_type() {
return "f16"
}
if ft == ctx.f32_type() {
return "f32"
}
if ft == ctx.f64_type() {
return "f64"
}
if ft == ctx.f128_type() {
return "f128"
}
// Non-standard floating-point types
if ft == ctx.x86_f80_type() {
return "f80"
}
if ft == ctx.ppc_f128_type() {
return "ppcf128"
}
unreachable!()
}
/// Invokes the [`llvm.stacksave`](https://llvm.org/docs/LangRef.html#llvm-stacksave-intrinsic)
/// intrinsic.
pub fn call_stacksave<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
name: Option<&str>,
) -> PointerValue<'ctx> {
const FN_NAME: &str = "llvm.stacksave";
let intrinsic_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
let llvm_i8 = ctx.ctx.i8_type();
let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default());
let fn_type = llvm_p0i8.fn_type(&[], false);
ctx.module.add_function(FN_NAME, fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_pointer_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the
/// [`llvm.stackrestore`](https://llvm.org/docs/LangRef.html#llvm-stackrestore-intrinsic) intrinsic.
pub fn call_stackrestore<'ctx>(ctx: &CodeGenContext<'ctx, '_>, ptr: PointerValue<'ctx>) {
const FN_NAME: &str = "llvm.stackrestore";
let intrinsic_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
let llvm_void = ctx.ctx.void_type();
let llvm_i8 = ctx.ctx.i8_type();
let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default());
let fn_type = llvm_void.fn_type(&[llvm_p0i8.into()], false);
ctx.module.add_function(FN_NAME, fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[ptr.into()], "")
.unwrap();
}
/// Invokes the [`llvm.abs`](https://llvm.org/docs/LangRef.html#llvm-abs-intrinsic) intrinsic.
///
/// * `src` - The value for which the absolute value is to be returned.
/// * `is_int_min_poison` - Whether `poison` is to be returned if `src` is `INT_MIN`.
pub fn call_int_abs<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
src: IntValue<'ctx>,
is_int_min_poison: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(is_int_min_poison.get_type().get_bit_width(), 1);
debug_assert!(is_int_min_poison.is_const());
let llvm_src_t = src.get_type();
let fn_name = format!("llvm.abs.i{}", llvm_src_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let llvm_i1 = ctx.ctx.bool_type();
let fn_type = llvm_src_t.fn_type(&[llvm_src_t.into(), llvm_i1.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[src.into(), is_int_min_poison.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.smax`](https://llvm.org/docs/LangRef.html#llvm-smax-intrinsic) intrinsic.
pub fn call_int_smax<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
a: IntValue<'ctx>,
b: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(a.get_type().get_bit_width(), b.get_type().get_bit_width());
let llvm_int_t = a.get_type();
let fn_name = format!("llvm.smax.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[a.into(), b.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.smin`](https://llvm.org/docs/LangRef.html#llvm-smin-intrinsic) intrinsic.
pub fn call_int_smin<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
a: IntValue<'ctx>,
b: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(a.get_type().get_bit_width(), b.get_type().get_bit_width());
let llvm_int_t = a.get_type();
let fn_name = format!("llvm.smin.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[a.into(), b.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.umax`](https://llvm.org/docs/LangRef.html#llvm-umax-intrinsic) intrinsic.
pub fn call_int_umax<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
a: IntValue<'ctx>,
b: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(a.get_type().get_bit_width(), b.get_type().get_bit_width());
let llvm_int_t = a.get_type();
let fn_name = format!("llvm.umax.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[a.into(), b.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.umin`](https://llvm.org/docs/LangRef.html#llvm-umin-intrinsic) intrinsic.
pub fn call_int_umin<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
a: IntValue<'ctx>,
b: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(a.get_type().get_bit_width(), b.get_type().get_bit_width());
let llvm_int_t = a.get_type();
let fn_name = format!("llvm.umin.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[a.into(), b.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.memcpy`](https://llvm.org/docs/LangRef.html#llvm-memcpy-intrinsic) intrinsic.
///
/// * `dest` - The pointer to the destination. Must be a pointer to an integer type.
/// * `src` - The pointer to the source. Must be a pointer to an integer type.
/// * `len` - The number of bytes to copy.
/// * `is_volatile` - Whether the `memcpy` operation should be `volatile`.
pub fn call_memcpy<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
dest: PointerValue<'ctx>,
src: PointerValue<'ctx>,
len: IntValue<'ctx>,
is_volatile: IntValue<'ctx>,
) {
debug_assert!(dest.get_type().get_element_type().is_int_type());
debug_assert!(src.get_type().get_element_type().is_int_type());
debug_assert_eq!(
dest.get_type().get_element_type().into_int_type().get_bit_width(),
src.get_type().get_element_type().into_int_type().get_bit_width(),
);
debug_assert!(matches!(len.get_type().get_bit_width(), 32 | 64));
debug_assert_eq!(is_volatile.get_type().get_bit_width(), 1);
let llvm_dest_t = dest.get_type();
let llvm_src_t = src.get_type();
let llvm_len_t = len.get_type();
let fn_name = format!(
"llvm.memcpy.p0i{}.p0i{}.i{}",
llvm_dest_t.get_element_type().into_int_type().get_bit_width(),
llvm_src_t.get_element_type().into_int_type().get_bit_width(),
llvm_len_t.get_bit_width(),
);
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let llvm_void = ctx.ctx.void_type();
let fn_type = llvm_void.fn_type(
&[
llvm_dest_t.into(),
llvm_src_t.into(),
llvm_len_t.into(),
is_volatile.get_type().into(),
],
false,
);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[dest.into(), src.into(), len.into(), is_volatile.into()], "")
.unwrap();
}
/// Invokes the `llvm.memcpy` intrinsic.
///
/// Unlike [`call_memcpy`], this function accepts any type of pointer value. If `dest` or `src` is
/// not a pointer to an integer, the pointer(s) will be cast to `i8*` before invoking `memcpy`.
pub fn call_memcpy_generic<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
dest: PointerValue<'ctx>,
src: PointerValue<'ctx>,
len: IntValue<'ctx>,
is_volatile: IntValue<'ctx>,
) {
let llvm_i8 = ctx.ctx.i8_type();
let llvm_p0i8 = llvm_i8.ptr_type(AddressSpace::default());
let dest_elem_t = dest.get_type().get_element_type();
let src_elem_t = src.get_type().get_element_type();
let dest = if matches!(dest_elem_t, IntType(t) if t.get_bit_width() == 8) {
dest
} else {
ctx.builder
.build_bitcast(dest, llvm_p0i8, "")
.map(BasicValueEnum::into_pointer_value)
.unwrap()
};
let src = if matches!(src_elem_t, IntType(t) if t.get_bit_width() == 8) {
src
} else {
ctx.builder
.build_bitcast(src, llvm_p0i8, "")
.map(BasicValueEnum::into_pointer_value)
.unwrap()
};
call_memcpy(ctx, dest, src, len, is_volatile);
}
/// Invokes the [`llvm.powi`](https://llvm.org/docs/LangRef.html#llvm-powi-intrinsic) intrinsic.
pub fn call_float_powi<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
power: IntValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_val_t = val.get_type();
let llvm_power_t = power.get_type();
let fn_name = format!(
"llvm.powi.{}.i{}",
get_float_intrinsic_repr(ctx.ctx, llvm_val_t),
llvm_power_t.get_bit_width(),
);
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_val_t.fn_type(&[llvm_val_t.into(), llvm_power_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into(), power.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.pow`](https://llvm.org/docs/LangRef.html#llvm-pow-intrinsic) intrinsic.
pub fn call_float_pow<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
power: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
debug_assert_eq!(val.get_type(), power.get_type());
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.pow.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into(), llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into(), power.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.fabs`](https://llvm.org/docs/LangRef.html#llvm-fabs-intrinsic) intrinsic.
pub fn call_float_fabs<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
src: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_src_t = src.get_type();
let fn_name = format!("llvm.fabs.{}", get_float_intrinsic_repr(ctx.ctx, llvm_src_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_src_t.fn_type(&[llvm_src_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[src.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.minnum`](https://llvm.org/docs/LangRef.html#llvm-minnum-intrinsic) intrinsic.
pub fn call_float_minnum<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val1: FloatValue<'ctx>,
val2: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
debug_assert_eq!(val1.get_type(), val2.get_type());
let llvm_float_t = val1.get_type();
let fn_name = format!("llvm.minnum.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into(), llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val1.into(), val2.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.maxnum`](https://llvm.org/docs/LangRef.html#llvm-maxnum-intrinsic) intrinsic.
pub fn call_float_maxnum<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val1: FloatValue<'ctx>,
val2: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
debug_assert_eq!(val1.get_type(), val2.get_type());
let llvm_float_t = val1.get_type();
let fn_name = format!("llvm.maxnum.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into(), llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val1.into(), val2.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.floor`](https://llvm.org/docs/LangRef.html#llvm-floor-intrinsic) intrinsic.
pub fn call_float_floor<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.floor.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.ceil`](https://llvm.org/docs/LangRef.html#llvm-ceil-intrinsic) intrinsic.
pub fn call_float_ceil<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.ceil.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.round`](https://llvm.org/docs/LangRef.html#llvm-round-intrinsic) intrinsic.
pub fn call_float_round<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.round.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the
/// [`llvm.roundeven`](https://llvm.org/docs/LangRef.html#llvm-roundeven-intrinsic) intrinsic.
pub fn call_float_roundeven<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: FloatValue<'ctx>,
name: Option<&str>,
) -> FloatValue<'ctx> {
let llvm_float_t = val.get_type();
let fn_name = format!("llvm.roundeven.{}", get_float_intrinsic_repr(ctx.ctx, llvm_float_t));
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_float_t.fn_type(&[llvm_float_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Invokes the [`llvm.expect`](https://llvm.org/docs/LangRef.html#llvm-expect-intrinsic) intrinsic.
pub fn call_expect<'ctx>(
ctx: &CodeGenContext<'ctx, '_>,
val: IntValue<'ctx>,
expected_val: IntValue<'ctx>,
name: Option<&str>,
) -> IntValue<'ctx> {
debug_assert_eq!(val.get_type().get_bit_width(), expected_val.get_type().get_bit_width());
let llvm_int_t = val.get_type();
let fn_name = format!("llvm.expect.i{}", llvm_int_t.get_bit_width());
let intrinsic_fn = ctx.module.get_function(fn_name.as_str()).unwrap_or_else(|| {
let fn_type = llvm_int_t.fn_type(&[llvm_int_t.into(), llvm_int_t.into()], false);
ctx.module.add_function(fn_name.as_str(), fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[val.into(), expected_val.into()], name.unwrap_or_default())
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value))
.map(Either::unwrap_left)
.unwrap()
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,23 @@
use crate::{
codegen::{
concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenTask, DefaultCodeGenerator,
WithCall, WorkerRegistry,
concrete_type::ConcreteTypeStore, CodeGenContext, CodeGenLLVMOptions,
CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
},
symbol_resolver::{SymbolResolver, ValueEnum},
toplevel::{
composer::TopLevelComposer, DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
composer::{ComposerConfig, TopLevelComposer}, DefinitionId, FunInstance, TopLevelContext,
TopLevelDef,
},
typecheck::{
type_inferencer::{FunctionData, Inferencer, PrimitiveStore},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap},
},
};
use indoc::indoc;
use inkwell::{
targets::{InitializationConfig, Target},
OptimizationLevel
};
use nac3parser::{
ast::{fold::Fold, StrRef},
parser::parse_program,
@ -59,12 +64,14 @@ impl SymbolResolver for Resolver {
unimplemented!()
}
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
self.id_to_def
.read()
.get(&id)
.cloned()
.ok_or_else(|| format!("cannot find symbol `{}`", id))
.ok_or_else(|| HashSet::from([
format!("cannot find symbol `{}`", id),
]))
}
fn get_string_id(&self, _: &str) -> i32 {
@ -85,7 +92,7 @@ fn test_primitives() {
"};
let statements = parse_program(source, Default::default()).unwrap();
let composer: TopLevelComposer = Default::default();
let composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 32).0;
let mut unifier = composer.unifier.clone();
let primitives = composer.primitives_ty;
let top_level = Arc::new(composer.make_top_level_context());
@ -104,7 +111,7 @@ fn test_primitives() {
FuncArg { name: "b".into(), ty: primitives.int32, default_value: None },
],
ret: primitives.int32,
vars: HashMap::new(),
vars: VarMap::new(),
};
let mut store = ConcreteTypeStore::new();
@ -180,28 +187,51 @@ fn test_primitives() {
let expected = indoc! {"
; ModuleID = 'test'
source_filename = \"test\"
define i32 @testing(i32 %0, i32 %1) {
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 !dbg !4 {
init:
%add = add i32 %0, %1
%cmp = icmp eq i32 %add, 1
br i1 %cmp, label %then, label %else
then: ; preds = %init
br label %cont
else: ; preds = %init
br label %cont
cont: ; preds = %else, %then
%if_exp_result.0 = phi i32 [ %0, %then ], [ 0, %else ]
ret i32 %if_exp_result.0
%add = add i32 %1, %0, !dbg !9
%cmp = icmp eq i32 %add, 1, !dbg !10
%. = select i1 %cmp, i32 %0, i32 0, !dbg !11
ret i32 %., !dbg !12
}
"}
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: \"unknown\", directory: \"\")
!4 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !5, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !8)
!5 = !DISubroutineType(flags: DIFlagPublic, types: !6)
!6 = !{!7}
!7 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
!8 = !{}
!9 = !DILocation(line: 1, column: 9, scope: !4)
!10 = !DILocation(line: 2, column: 15, scope: !4)
!11 = !DILocation(line: 0, scope: !4)
!12 = !DILocation(line: 3, column: 8, scope: !4)
"}
.trim();
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
})));
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, f);
Target::initialize_all(&InitializationConfig::default());
let llvm_options = CodeGenLLVMOptions {
opt_level: OptimizationLevel::Default,
target: CodeGenTargetMachineOptions::from_host_triple(),
};
let (registry, handles) = WorkerRegistry::create_workers(
threads,
top_level,
&llvm_options,
&f
);
registry.add_task(task);
registry.wait_tasks_complete(handles);
}
@ -219,7 +249,7 @@ fn test_simple_call() {
"};
let statements_2 = parse_program(source_2, Default::default()).unwrap();
let composer: TopLevelComposer = Default::default();
let composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 32).0;
let mut unifier = composer.unifier.clone();
let primitives = composer.primitives_ty;
let top_level = Arc::new(composer.make_top_level_context());
@ -228,7 +258,7 @@ fn test_simple_call() {
let signature = FunSignature {
args: vec![FuncArg { name: "a".into(), ty: primitives.int32, default_value: None }],
ret: primitives.int32,
vars: HashMap::new(),
vars: VarMap::new(),
};
let fun_ty = unifier.add_ty(TypeEnum::TFunc(signature.clone()));
let mut store = ConcreteTypeStore::new();
@ -342,23 +372,56 @@ fn test_simple_call() {
; ModuleID = 'test'
source_filename = \"test\"
define i32 @testing(i32 %0) {
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @testing(i32 %0) local_unnamed_addr #0 !dbg !5 {
init:
%call = call i32 @foo.0(i32 %0)
%mul = mul i32 %call, 2
ret i32 %mul
%add.i = shl i32 %0, 1, !dbg !10
%mul = add i32 %add.i, 2, !dbg !10
ret i32 %mul, !dbg !10
}
define i32 @foo.0(i32 %0) {
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @foo.0(i32 %0) local_unnamed_addr #0 !dbg !11 {
init:
%add = add i32 %0, 1
ret i32 %add
%add = add i32 %0, 1, !dbg !12
ret i32 %add, !dbg !12
}
"}
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2, !4}
!0 = !{i32 2, !\"Debug Info Version\", i32 3}
!1 = !{i32 2, !\"Dwarf Version\", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!3 = !DIFile(filename: \"unknown\", directory: \"\")
!4 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!5 = distinct !DISubprogram(name: \"testing\", linkageName: \"testing\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !9)
!6 = !DISubroutineType(flags: DIFlagPublic, types: !7)
!7 = !{!8}
!8 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
!9 = !{}
!10 = !DILocation(line: 2, column: 12, scope: !5)
!11 = distinct !DISubprogram(name: \"foo.0\", linkageName: \"foo.0\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9)
!12 = !DILocation(line: 1, column: 12, scope: !11)
"}
.trim();
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
})));
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, f);
Target::initialize_all(&InitializationConfig::default());
let llvm_options = CodeGenLLVMOptions {
opt_level: OptimizationLevel::Default,
target: CodeGenTargetMachineOptions::from_host_triple(),
};
let (registry, handles) = WorkerRegistry::create_workers(
threads,
top_level,
&llvm_options,
&f
);
registry.add_task(task);
registry.wait_tasks_complete(handles);
}

View File

@ -1,22 +1,19 @@
use std::fmt::Debug;
use std::sync::Arc;
use std::{collections::HashMap, fmt::Display};
use std::{collections::HashMap, collections::HashSet, fmt::Display};
use std::rc::Rc;
use crate::typecheck::typedef::TypeEnum;
use crate::{
codegen::CodeGenContext,
toplevel::{DefinitionId, TopLevelDef},
};
use crate::{
codegen::CodeGenerator,
codegen::{CodeGenContext, CodeGenerator},
toplevel::{DefinitionId, TopLevelDef, type_annotation::TypeAnnotation},
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, Unifier},
typedef::{Type, TypeEnum, Unifier, VarMap},
},
};
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
use itertools::{chain, izip};
use nac3parser::ast::{Expr, Location, StrRef};
use itertools::{chain, Itertools, izip};
use nac3parser::ast::{Constant, Expr, Location, StrRef};
use parking_lot::RwLock;
#[derive(Clone, PartialEq, Debug)]
@ -33,15 +30,190 @@ pub enum SymbolValue {
OptionNone,
}
impl SymbolValue {
/// Creates a [`SymbolValue`] from a [`Constant`].
///
/// * `constant` - The constant to create the value from.
/// * `expected_ty` - The expected type of the [`SymbolValue`].
pub fn from_constant(
constant: &Constant,
expected_ty: Type,
primitives: &PrimitiveStore,
unifier: &mut Unifier
) -> Result<Self, String> {
match constant {
Constant::None => {
if unifier.unioned(expected_ty, primitives.option) {
Ok(SymbolValue::OptionNone)
} else {
Err(format!("Expected {expected_ty:?}, but got Option"))
}
}
Constant::Bool(b) => {
if unifier.unioned(expected_ty, primitives.bool) {
Ok(SymbolValue::Bool(*b))
} else {
Err(format!("Expected {expected_ty:?}, but got bool"))
}
}
Constant::Str(s) => {
if unifier.unioned(expected_ty, primitives.str) {
Ok(SymbolValue::Str(s.to_string()))
} else {
Err(format!("Expected {expected_ty:?}, but got str"))
}
},
Constant::Int(i) => {
if unifier.unioned(expected_ty, primitives.int32) {
i32::try_from(*i)
.map(SymbolValue::I32)
.map_err(|e| e.to_string())
} else if unifier.unioned(expected_ty, primitives.int64) {
i64::try_from(*i)
.map(SymbolValue::I64)
.map_err(|e| e.to_string())
} else if unifier.unioned(expected_ty, primitives.uint32) {
u32::try_from(*i)
.map(SymbolValue::U32)
.map_err(|e| e.to_string())
} else if unifier.unioned(expected_ty, primitives.uint64) {
u64::try_from(*i)
.map(SymbolValue::U64)
.map_err(|e| e.to_string())
} else {
Err(format!("Expected {}, but got int", unifier.stringify(expected_ty)))
}
}
Constant::Tuple(t) => {
let expected_ty = unifier.get_ty(expected_ty);
let TypeEnum::TTuple { ty } = expected_ty.as_ref() else {
return Err(format!("Expected {:?}, but got Tuple", expected_ty.get_type_name()))
};
assert_eq!(ty.len(), t.len());
let elems = t
.iter()
.zip(ty)
.map(|(constant, ty)| Self::from_constant(constant, *ty, primitives, unifier))
.collect::<Result<Vec<SymbolValue>, _>>()?;
Ok(SymbolValue::Tuple(elems))
}
Constant::Float(f) => {
if unifier.unioned(expected_ty, primitives.float) {
Ok(SymbolValue::Double(*f))
} else {
Err(format!("Expected {expected_ty:?}, but got float"))
}
},
_ => Err(format!("Unsupported value type {constant:?}")),
}
}
/// Creates a [`SymbolValue`] from a [`Constant`], with its type being inferred from the constant value.
///
/// * `constant` - The constant to create the value from.
pub fn from_constant_inferred(
constant: &Constant,
) -> Result<Self, String> {
match constant {
Constant::None => Ok(SymbolValue::OptionNone),
Constant::Bool(b) => Ok(SymbolValue::Bool(*b)),
Constant::Str(s) => Ok(SymbolValue::Str(s.to_string())),
Constant::Int(i) => {
let i = *i;
if i >= 0 {
i32::try_from(i).map(SymbolValue::I32)
.or_else(|_| i64::try_from(i).map(SymbolValue::I64))
.map_err(|_| format!("Literal cannot be expressed as any integral type: {i}"))
} else {
u32::try_from(i).map(SymbolValue::U32)
.or_else(|_| u64::try_from(i).map(SymbolValue::U64))
.map_err(|_| format!("Literal cannot be expressed as any integral type: {i}"))
}
}
Constant::Tuple(t) => {
let elems = t
.iter()
.map(Self::from_constant_inferred)
.collect::<Result<Vec<SymbolValue>, _>>()?;
Ok(SymbolValue::Tuple(elems))
}
Constant::Float(f) => Ok(SymbolValue::Double(*f)),
_ => Err(format!("Unsupported value type {constant:?}")),
}
}
/// Returns the [`Type`] representing the data type of this value.
pub fn get_type(&self, primitives: &PrimitiveStore, unifier: &mut Unifier) -> Type {
match self {
SymbolValue::I32(_) => primitives.int32,
SymbolValue::I64(_) => primitives.int64,
SymbolValue::U32(_) => primitives.uint32,
SymbolValue::U64(_) => primitives.uint64,
SymbolValue::Str(_) => primitives.str,
SymbolValue::Double(_) => primitives.float,
SymbolValue::Bool(_) => primitives.bool,
SymbolValue::Tuple(vs) => {
let vs_tys = vs
.iter()
.map(|v| v.get_type(primitives, unifier))
.collect::<Vec<_>>();
unifier.add_ty(TypeEnum::TTuple {
ty: vs_tys,
})
}
SymbolValue::OptionSome(_) | SymbolValue::OptionNone => primitives.option,
}
}
/// Returns the [`TypeAnnotation`] representing the data type of this value.
pub fn get_type_annotation(&self, primitives: &PrimitiveStore, unifier: &mut Unifier) -> TypeAnnotation {
match self {
SymbolValue::Bool(..)
| SymbolValue::Double(..)
| SymbolValue::I32(..)
| SymbolValue::I64(..)
| SymbolValue::U32(..)
| SymbolValue::U64(..)
| SymbolValue::Str(..) => TypeAnnotation::Primitive(self.get_type(primitives, unifier)),
SymbolValue::Tuple(vs) => {
let vs_tys = vs
.iter()
.map(|v| v.get_type_annotation(primitives, unifier))
.collect::<Vec<_>>();
TypeAnnotation::Tuple(vs_tys)
}
SymbolValue::OptionNone => TypeAnnotation::CustomClass {
id: primitives.option.obj_id(unifier).unwrap(),
params: Vec::default(),
},
SymbolValue::OptionSome(v) => {
let ty = v.get_type_annotation(primitives, unifier);
TypeAnnotation::CustomClass {
id: primitives.option.obj_id(unifier).unwrap(),
params: vec![ty],
}
}
}
}
/// Returns the [`TypeEnum`] representing the data type of this value.
pub fn get_type_enum(&self, primitives: &PrimitiveStore, unifier: &mut Unifier) -> Rc<TypeEnum> {
let ty = self.get_type(primitives, unifier);
unifier.get_ty(ty)
}
}
impl Display for SymbolValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SymbolValue::I32(i) => write!(f, "{}", i),
SymbolValue::I64(i) => write!(f, "int64({})", i),
SymbolValue::U32(i) => write!(f, "uint32({})", i),
SymbolValue::U64(i) => write!(f, "uint64({})", i),
SymbolValue::Str(s) => write!(f, "\"{}\"", s),
SymbolValue::Double(d) => write!(f, "{}", d),
SymbolValue::I32(i) => write!(f, "{i}"),
SymbolValue::I64(i) => write!(f, "int64({i})"),
SymbolValue::U32(i) => write!(f, "uint32({i})"),
SymbolValue::U64(i) => write!(f, "uint64({i})"),
SymbolValue::Str(s) => write!(f, "\"{s}\""),
SymbolValue::Double(d) => write!(f, "{d}"),
SymbolValue::Bool(b) => {
if *b {
write!(f, "True")
@ -50,42 +222,82 @@ impl Display for SymbolValue {
}
}
SymbolValue::Tuple(t) => {
write!(f, "({})", t.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", "))
write!(f, "({})", t.iter().map(|v| format!("{v}")).collect::<Vec<_>>().join(", "))
}
SymbolValue::OptionSome(v) => write!(f, "Some({})", v),
SymbolValue::OptionSome(v) => write!(f, "Some({v})"),
SymbolValue::OptionNone => write!(f, "none"),
}
}
}
impl TryFrom<SymbolValue> for u64 {
type Error = ();
/// Tries to convert a [`SymbolValue`] into a [`u64`], returning [`Err`] if the value is not
/// numeric or if the value cannot be converted into a `u64` without overflow.
fn try_from(value: SymbolValue) -> Result<Self, Self::Error> {
match value {
SymbolValue::I32(v) => u64::try_from(v).map_err(|_| ()),
SymbolValue::I64(v) => u64::try_from(v).map_err(|_| ()),
SymbolValue::U32(v) => Ok(v as u64),
SymbolValue::U64(v) => Ok(v),
_ => Err(()),
}
}
}
impl TryFrom<SymbolValue> for i128 {
type Error = ();
/// Tries to convert a [`SymbolValue`] into a [`i128`], returning [`Err`] if the value is not
/// numeric.
fn try_from(value: SymbolValue) -> Result<Self, Self::Error> {
match value {
SymbolValue::I32(v) => Ok(v as i128),
SymbolValue::I64(v) => Ok(v as i128),
SymbolValue::U32(v) => Ok(v as i128),
SymbolValue::U64(v) => Ok(v as i128),
_ => Err(()),
}
}
}
pub trait StaticValue {
/// Returns a unique identifier for this value.
fn get_unique_identifier(&self) -> u64;
fn get_const_obj<'ctx, 'a>(
/// Returns the constant object represented by this unique identifier.
fn get_const_obj<'ctx>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
generator: &mut dyn CodeGenerator,
) -> BasicValueEnum<'ctx>;
fn to_basic_value_enum<'ctx, 'a>(
/// Converts this value to a LLVM [`BasicValueEnum`].
fn to_basic_value_enum<'ctx>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
generator: &mut dyn CodeGenerator,
expected_ty: Type,
) -> Result<BasicValueEnum<'ctx>, String>;
fn get_field<'ctx, 'a>(
/// Returns a field within this value.
fn get_field<'ctx>(
&self,
name: StrRef,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
) -> Option<ValueEnum<'ctx>>;
/// Returns a single element of this tuple.
fn get_tuple_element<'ctx>(&self, index: u32) -> Option<ValueEnum<'ctx>>;
}
#[derive(Clone)]
pub enum ValueEnum<'ctx> {
/// [ValueEnum] representing a static value.
Static(Arc<dyn StaticValue + Send + Sync>),
/// [ValueEnum] representing a dynamic value.
Dynamic(BasicValueEnum<'ctx>),
}
@ -120,6 +332,8 @@ impl<'ctx> From<StructValue<'ctx>> for ValueEnum<'ctx> {
}
impl<'ctx> ValueEnum<'ctx> {
/// Converts this [`ValueEnum`] to a [`BasicValueEnum`].
pub fn to_basic_value_enum<'a>(
self,
ctx: &mut CodeGenContext<'ctx, 'a>,
@ -134,7 +348,7 @@ impl<'ctx> ValueEnum<'ctx> {
}
pub trait SymbolResolver {
// get type of type variable identifier or top-level function type
/// Get type of type variable identifier or top-level function type,
fn get_symbol_type(
&self,
unifier: &mut Unifier,
@ -143,16 +357,16 @@ pub trait SymbolResolver {
str: StrRef,
) -> Result<Type, String>;
// get the top-level definition of identifiers
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, String>;
/// Get the top-level definition of identifiers.
fn get_identifier_def(&self, str: StrRef) -> Result<DefinitionId, HashSet<String>>;
fn get_symbol_value<'ctx, 'a>(
fn get_symbol_value<'ctx>(
&self,
str: StrRef,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
) -> Option<ValueEnum<'ctx>>;
fn get_default_param_value(&self, expr: &nac3parser::ast::Expr) -> Option<SymbolValue>;
fn get_default_param_value(&self, expr: &Expr) -> Option<SymbolValue>;
fn get_string_id(&self, s: &str) -> i32;
fn get_exception_id(&self, tyid: usize) -> usize;
@ -167,7 +381,7 @@ pub trait SymbolResolver {
}
thread_local! {
static IDENTIFIER_ID: [StrRef; 11] = [
static IDENTIFIER_ID: [StrRef; 12] = [
"int32".into(),
"int64".into(),
"float".into(),
@ -179,17 +393,18 @@ thread_local! {
"Exception".into(),
"uint32".into(),
"uint64".into(),
"Literal".into(),
];
}
// convert type annotation into type
/// Converts a type annotation into a [Type].
pub fn parse_type_annotation<T>(
resolver: &dyn SymbolResolver,
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
expr: &Expr<T>,
) -> Result<Type, String> {
) -> Result<Type, HashSet<String>> {
use nac3parser::ast::ExprKind::*;
let ids = IDENTIFIER_ID.with(|ids| *ids);
let int32_id = ids[0];
@ -203,6 +418,7 @@ pub fn parse_type_annotation<T>(
let exn_id = ids[8];
let uint32_id = ids[9];
let uint64_id = ids[10];
let literal_id = ids[11];
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
if *id == int32_id {
@ -223,39 +439,44 @@ pub fn parse_type_annotation<T>(
Ok(primitives.exception)
} else {
let obj_id = resolver.get_identifier_def(*id);
match obj_id {
Ok(obj_id) => {
let def = top_level_defs[obj_id.0].read();
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
if !type_vars.is_empty() {
return Err(format!(
if let Ok(obj_id) = obj_id {
let def = top_level_defs[obj_id.0].read();
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
if !type_vars.is_empty() {
return Err(HashSet::from([
format!(
"Unexpected number of type parameters: expected {} but got 0",
type_vars.len()
));
}
let fields = chain(
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
methods.iter().map(|(k, v, _)| (*k, (*v, false))),
)
),
]))
}
let fields = chain(
fields.iter().map(|(k, v, m)| (*k, (*v, *m))),
methods.iter().map(|(k, v, _)| (*k, (*v, false))),
)
.collect();
Ok(unifier.add_ty(TypeEnum::TObj {
obj_id,
fields,
params: Default::default(),
}))
} else {
Err(format!("Cannot use function name as type at {}", loc))
}
Ok(unifier.add_ty(TypeEnum::TObj {
obj_id,
fields,
params: VarMap::default(),
}))
} else {
Err(HashSet::from([
format!("Cannot use function name as type at {loc}"),
]))
}
Err(_) => {
let ty = resolver
.get_symbol_type(unifier, top_level_defs, primitives, *id)
.map_err(|e| format!("Unknown type annotation at {}: {}", loc, e))?;
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
Ok(ty)
} else {
Err(format!("Unknown type annotation {} at {}", id, loc))
}
} else {
let ty = resolver
.get_symbol_type(unifier, top_level_defs, primitives, *id)
.map_err(|e| HashSet::from([
format!("Unknown type annotation at {loc}: {e}"),
]))?;
if let TypeEnum::TVar { .. } = &*unifier.get_ty(ty) {
Ok(ty)
} else {
Err(HashSet::from([
format!("Unknown type annotation {id} at {loc}"),
]))
}
}
}
@ -278,8 +499,31 @@ pub fn parse_type_annotation<T>(
.collect::<Result<Vec<_>, _>>()?;
Ok(unifier.add_ty(TypeEnum::TTuple { ty }))
} else {
Err("Expected multiple elements for tuple".into())
Err(HashSet::from([
"Expected multiple elements for tuple".into()
]))
}
} else if *id == literal_id {
let mut parse_literal = |elt: &Expr<T>| {
let ty = parse_type_annotation(resolver, top_level_defs, unifier, primitives, elt)?;
let ty_enum = &*unifier.get_ty_immutable(ty);
match ty_enum {
TypeEnum::TLiteral { values, .. } => Ok(values.clone()),
_ => Err(HashSet::from([
format!("Expected literal in type argument for Literal at {}", elt.location),
]))
}
};
let values = if let Tuple { elts, .. } = &slice.node {
elts.iter()
.map(&mut parse_literal)
.collect::<Result<Vec<_>, _>>()?
} else {
vec![parse_literal(slice)?]
}.into_iter().flatten().collect_vec();
Ok(unifier.get_fresh_literal(values, Some(slice.location)))
} else {
let types = if let Tuple { elts, .. } = &slice.node {
elts.iter()
@ -295,13 +539,15 @@ pub fn parse_type_annotation<T>(
let def = top_level_defs[obj_id.0].read();
if let TopLevelDef::Class { fields, methods, type_vars, .. } = &*def {
if types.len() != type_vars.len() {
return Err(format!(
"Unexpected number of type parameters: expected {} but got {}",
type_vars.len(),
types.len()
));
return Err(HashSet::from([
format!(
"Unexpected number of type parameters: expected {} but got {}",
type_vars.len(),
types.len()
),
]))
}
let mut subst = HashMap::new();
let mut subst = VarMap::new();
for (var, ty) in izip!(type_vars.iter(), types.iter()) {
let id = if let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*var) {
*id
@ -323,7 +569,9 @@ pub fn parse_type_annotation<T>(
}));
Ok(unifier.add_ty(TypeEnum::TObj { obj_id, fields, params: subst }))
} else {
Err("Cannot use function name as type".into())
Err(HashSet::from([
"Cannot use function name as type".into(),
]))
}
}
};
@ -334,10 +582,17 @@ pub fn parse_type_annotation<T>(
if let Name { id, .. } = &value.node {
subscript_name_handle(id, slice, unifier)
} else {
Err(format!("unsupported type expression at {}", expr.location))
Err(HashSet::from([
format!("unsupported type expression at {}", expr.location),
]))
}
}
_ => Err(format!("unsupported type expression at {}", expr.location)),
Constant { value, .. } => SymbolValue::from_constant_inferred(value)
.map(|v| unifier.get_fresh_literal(vec![v], Some(expr.location)))
.map_err(|err| HashSet::from([err])),
_ => Err(HashSet::from([
format!("unsupported type expression at {}", expr.location),
])),
}
}
@ -348,7 +603,7 @@ impl dyn SymbolResolver + Send + Sync {
unifier: &mut Unifier,
primitives: &PrimitiveStore,
expr: &Expr<T>,
) -> Result<Type, String> {
) -> Result<Type, HashSet<String>> {
parse_type_annotation(self, top_level_defs, unifier, primitives, expr)
}
@ -361,13 +616,13 @@ impl dyn SymbolResolver + Send + Sync {
unifier.internal_stringify(
ty,
&mut |id| {
if let TopLevelDef::Class { name, .. } = &*top_level_defs[id].read() {
name.to_string()
} else {
let TopLevelDef::Class { name, .. } = &*top_level_defs[id].read() else {
unreachable!("expected class definition")
}
};
name.to_string()
},
&mut |id| format!("typevar{}", id),
&mut |id| format!("typevar{id}"),
&mut None,
)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,79 @@
use std::convert::TryInto;
use crate::symbol_resolver::SymbolValue;
use crate::toplevel::numpy::subst_ndarray_tvars;
use crate::typecheck::typedef::{Mapping, VarMap};
use nac3parser::ast::{Constant, Location};
use super::*;
/// Structure storing [`DefinitionId`] for primitive types.
#[derive(Clone, Copy)]
pub struct PrimitiveDefinitionIds {
pub int32: DefinitionId,
pub int64: DefinitionId,
pub uint32: DefinitionId,
pub uint64: DefinitionId,
pub float: DefinitionId,
pub bool: DefinitionId,
pub none: DefinitionId,
pub range: DefinitionId,
pub str: DefinitionId,
pub exception: DefinitionId,
pub option: DefinitionId,
pub ndarray: DefinitionId,
}
impl PrimitiveDefinitionIds {
/// Returns all [`DefinitionId`] of primitives as a [`Vec`].
///
/// There are no guarantees on ordering of the IDs.
#[must_use]
fn as_vec(&self) -> Vec<DefinitionId> {
vec![
self.int32,
self.int64,
self.uint32,
self.uint64,
self.float,
self.bool,
self.none,
self.range,
self.str,
self.exception,
self.option,
self.ndarray,
]
}
/// Returns an iterator over all [`DefinitionId`]s of this instance in indeterminate order.
pub fn iter(&self) -> impl Iterator<Item=DefinitionId> {
self.as_vec().into_iter()
}
/// Returns the primitive with the largest [`DefinitionId`].
#[must_use]
pub fn max_id(&self) -> DefinitionId {
self.iter().max().unwrap()
}
}
/// The [definition IDs][DefinitionId] for primitive types.
pub const PRIMITIVE_DEF_IDS: PrimitiveDefinitionIds = PrimitiveDefinitionIds {
int32: DefinitionId(0),
int64: DefinitionId(1),
uint32: DefinitionId(8),
uint64: DefinitionId(9),
float: DefinitionId(2),
bool: DefinitionId(3),
none: DefinitionId(4),
range: DefinitionId(5),
str: DefinitionId(6),
exception: DefinitionId(7),
option: DefinitionId(10),
ndarray: DefinitionId(14),
};
impl TopLevelDef {
pub fn to_string(&self, unifier: &mut Unifier) -> String {
match self {
@ -43,48 +112,49 @@ impl TopLevelDef {
}
impl TopLevelComposer {
pub fn make_primitives() -> (PrimitiveStore, Unifier) {
#[must_use]
pub fn make_primitives(size_t: u32) -> (PrimitiveStore, Unifier) {
let mut unifier = Unifier::new();
let int32 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(0),
obj_id: PRIMITIVE_DEF_IDS.int32,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let int64 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(1),
obj_id: PRIMITIVE_DEF_IDS.int64,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let float = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(2),
obj_id: PRIMITIVE_DEF_IDS.float,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let bool = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(3),
obj_id: PRIMITIVE_DEF_IDS.bool,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let none = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(4),
obj_id: PRIMITIVE_DEF_IDS.none,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let range = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(5),
obj_id: PRIMITIVE_DEF_IDS.range,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let str = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(6),
obj_id: PRIMITIVE_DEF_IDS.str,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let exception = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(7),
obj_id: PRIMITIVE_DEF_IDS.exception,
fields: vec![
("__name__".into(), (int32, true)),
("__file__".into(), (int32, true)),
("__file__".into(), (str, true)),
("__line__".into(), (int32, true)),
("__col__".into(), (int32, true)),
("__func__".into(), (str, true)),
@ -95,32 +165,32 @@ impl TopLevelComposer {
]
.into_iter()
.collect::<HashMap<_, _>>(),
params: HashMap::new(),
params: VarMap::new(),
});
let uint32 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(8),
obj_id: PRIMITIVE_DEF_IDS.uint32,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let uint64 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(9),
obj_id: PRIMITIVE_DEF_IDS.uint64,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let option_type_var = unifier.get_fresh_var(Some("option_type_var".into()), None);
let is_some_type_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![],
ret: bool,
vars: HashMap::from([(option_type_var.1, option_type_var.0)]),
vars: VarMap::from([(option_type_var.1, option_type_var.0)]),
}));
let unwrap_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![],
ret: option_type_var.0,
vars: HashMap::from([(option_type_var.1, option_type_var.0)]),
vars: VarMap::from([(option_type_var.1, option_type_var.0)]),
}));
let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
obj_id: PRIMITIVE_DEF_IDS.option,
fields: vec![
("is_some".into(), (is_some_type_fun_ty, true)),
("is_none".into(), (is_some_type_fun_ty, true)),
@ -128,30 +198,134 @@ impl TopLevelComposer {
]
.into_iter()
.collect::<HashMap<_, _>>(),
params: HashMap::from([(option_type_var.1, option_type_var.0)]),
params: VarMap::from([(option_type_var.1, option_type_var.0)]),
});
let size_t_ty = match size_t {
32 => uint32,
64 => uint64,
_ => unreachable!(),
};
let ndarray_dtype_tvar = unifier.get_fresh_var(Some("ndarray_dtype".into()), None);
let ndarray_ndims_tvar = unifier.get_fresh_const_generic_var(size_t_ty, Some("ndarray_ndims".into()), None);
let ndarray_copy_fun_ret_ty = unifier.get_fresh_var(None, None);
let ndarray_copy_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![],
ret: ndarray_copy_fun_ret_ty.0,
vars: VarMap::from([
(ndarray_dtype_tvar.1, ndarray_dtype_tvar.0),
(ndarray_ndims_tvar.1, ndarray_ndims_tvar.0),
]),
}));
let ndarray_fill_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg {
name: "value".into(),
ty: ndarray_dtype_tvar.0,
default_value: None,
},
],
ret: none,
vars: VarMap::from([
(ndarray_dtype_tvar.1, ndarray_dtype_tvar.0),
(ndarray_ndims_tvar.1, ndarray_ndims_tvar.0),
]),
}));
let ndarray_binop_fun_other_ty = unifier.get_fresh_var(None, None);
let ndarray_binop_fun_ret_ty = unifier.get_fresh_var(None, None);
let ndarray_binop_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg {
name: "other".into(),
ty: ndarray_binop_fun_other_ty.0,
default_value: None,
},
],
ret: ndarray_binop_fun_ret_ty.0,
vars: VarMap::from([
(ndarray_dtype_tvar.1, ndarray_dtype_tvar.0),
(ndarray_ndims_tvar.1, ndarray_ndims_tvar.0),
]),
}));
let ndarray_truediv_fun_other_ty = unifier.get_fresh_var(None, None);
let ndarray_truediv_fun_ret_ty = unifier.get_fresh_var(None, None);
let ndarray_truediv_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg {
name: "other".into(),
ty: ndarray_truediv_fun_other_ty.0,
default_value: None,
},
],
ret: ndarray_truediv_fun_ret_ty.0,
vars: VarMap::from([
(ndarray_dtype_tvar.1, ndarray_dtype_tvar.0),
(ndarray_ndims_tvar.1, ndarray_ndims_tvar.0),
]),
}));
let ndarray = unifier.add_ty(TypeEnum::TObj {
obj_id: PRIMITIVE_DEF_IDS.ndarray,
fields: Mapping::from([
("copy".into(), (ndarray_copy_fun_ty, true)),
("fill".into(), (ndarray_fill_fun_ty, true)),
("__add__".into(), (ndarray_binop_fun_ty, true)),
("__sub__".into(), (ndarray_binop_fun_ty, true)),
("__mul__".into(), (ndarray_binop_fun_ty, true)),
("__truediv__".into(), (ndarray_truediv_fun_ty, true)),
("__floordiv__".into(), (ndarray_binop_fun_ty, true)),
("__mod__".into(), (ndarray_binop_fun_ty, true)),
("__pow__".into(), (ndarray_binop_fun_ty, true)),
("__iadd__".into(), (ndarray_binop_fun_ty, true)),
("__isub__".into(), (ndarray_binop_fun_ty, true)),
("__imul__".into(), (ndarray_binop_fun_ty, true)),
("__itruediv__".into(), (ndarray_truediv_fun_ty, true)),
("__ifloordiv__".into(), (ndarray_binop_fun_ty, true)),
("__imod__".into(), (ndarray_binop_fun_ty, true)),
("__ipow__".into(), (ndarray_binop_fun_ty, true)),
]),
params: VarMap::from([
(ndarray_dtype_tvar.1, ndarray_dtype_tvar.0),
(ndarray_ndims_tvar.1, ndarray_ndims_tvar.0),
]),
});
let ndarray_usized_ndims_tvar = unifier.get_fresh_const_generic_var(size_t_ty, Some("ndarray_ndims".into()), None);
let ndarray_unsized = subst_ndarray_tvars(&mut unifier, ndarray, Some(ndarray_usized_ndims_tvar.0), None);
unifier.unify(ndarray_copy_fun_ret_ty.0, ndarray).unwrap();
unifier.unify(ndarray_binop_fun_other_ty.0, ndarray_unsized).unwrap();
unifier.unify(ndarray_binop_fun_ret_ty.0, ndarray).unwrap();
let ndarray_float = subst_ndarray_tvars(&mut unifier, ndarray, Some(float), None);
unifier.unify(ndarray_truediv_fun_other_ty.0, ndarray).unwrap();
unifier.unify(ndarray_truediv_fun_ret_ty.0, ndarray_float).unwrap();
let primitives = PrimitiveStore {
int32,
int64,
uint32,
uint64,
float,
bool,
none,
range,
str,
exception,
uint32,
uint64,
option,
ndarray,
size_t,
};
unifier.put_primitive_store(&primitives);
crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier);
(primitives, unifier)
}
/// already include the definition_id of itself inside the ancestors vector
/// when first registering, the type_vars, fields, methods, ancestors are invalid
/// already include the `definition_id` of itself inside the ancestors vector
/// when first registering, the `type_vars`, fields, methods, ancestors are invalid
#[must_use]
pub fn make_top_level_class_def(
index: usize,
obj_id: DefinitionId,
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
name: StrRef,
constructor: Option<Type>,
@ -159,11 +333,11 @@ impl TopLevelComposer {
) -> TopLevelDef {
TopLevelDef::Class {
name,
object_id: DefinitionId(index),
type_vars: Default::default(),
fields: Default::default(),
methods: Default::default(),
ancestors: Default::default(),
object_id: obj_id,
type_vars: Vec::default(),
fields: Vec::default(),
methods: Vec::default(),
ancestors: Vec::default(),
constructor,
resolver,
loc,
@ -171,6 +345,7 @@ impl TopLevelComposer {
}
/// when first registering, the type is a invalid value
#[must_use]
pub fn make_top_level_function_def(
name: String,
simple_name: StrRef,
@ -182,15 +357,16 @@ impl TopLevelComposer {
name,
simple_name,
signature: ty,
var_id: Default::default(),
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
var_id: Vec::default(),
instance_to_symbol: HashMap::default(),
instance_to_stmt: HashMap::default(),
resolver,
codegen_callback: None,
loc,
}
}
#[must_use]
pub fn make_class_method_name(mut class_name: String, method_name: &str) -> String {
class_name.push('.');
class_name.push_str(method_name);
@ -200,13 +376,13 @@ impl TopLevelComposer {
pub fn get_class_method_def_info(
class_methods_def: &[(StrRef, Type, DefinitionId)],
method_name: StrRef,
) -> Result<(Type, DefinitionId), String> {
) -> Result<(Type, DefinitionId), HashSet<String>> {
for (name, ty, def_id) in class_methods_def {
if name == &method_name {
return Ok((*ty, *def_id));
}
}
Err(format!("no method {} in the current class", method_name))
Err(HashSet::from([format!("no method {method_name} in the current class")]))
}
/// get all base class def id of a class, excluding itself. \
@ -217,7 +393,7 @@ impl TopLevelComposer {
pub fn get_all_ancestors_helper(
child: &TypeAnnotation,
temp_def_list: &[Arc<RwLock<TopLevelDef>>],
) -> Result<Vec<TypeAnnotation>, String> {
) -> Result<Vec<TypeAnnotation>, HashSet<String>> {
let mut result: Vec<TypeAnnotation> = Vec::new();
let mut parent = Self::get_parent(child, temp_def_list);
while let Some(p) = parent {
@ -229,16 +405,16 @@ impl TopLevelComposer {
};
// check cycle
let no_cycle = result.iter().all(|x| {
if let TypeAnnotation::CustomClass { id, .. } = x {
id.0 != p_id.0
} else {
let TypeAnnotation::CustomClass { id, .. } = x else {
unreachable!("must be class kind annotation")
}
};
id.0 != p_id.0
});
if no_cycle {
result.push(p);
} else {
return Err("cyclic inheritance detected".into());
return Err(HashSet::from(["cyclic inheritance detected".into()]));
}
}
Ok(result)
@ -256,23 +432,25 @@ impl TopLevelComposer {
};
let child_def = temp_def_list.get(child_id.0).unwrap();
let child_def = child_def.read();
if let TopLevelDef::Class { ancestors, .. } = &*child_def {
if !ancestors.is_empty() {
Some(ancestors[0].clone())
} else {
None
}
} else {
let TopLevelDef::Class { ancestors, .. } = &*child_def else {
unreachable!("child must be top level class def")
};
if ancestors.is_empty() {
None
} else {
Some(ancestors[0].clone())
}
}
/// get the var_id of a given TVar type
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<u32, String> {
/// get the `var_id` of a given `TVar` type
pub fn get_var_id(var_ty: Type, unifier: &mut Unifier) -> Result<u32, HashSet<String>> {
if let TypeEnum::TVar { id, .. } = unifier.get_ty(var_ty).as_ref() {
Ok(*id)
} else {
Err("not type var".to_string())
Err(HashSet::from([
"not type var".to_string(),
]))
}
}
@ -286,39 +464,38 @@ impl TopLevelComposer {
let this = this.as_ref();
let other = unifier.get_ty(other);
let other = other.as_ref();
if let (
let (
TypeEnum::TFunc(FunSignature { args: this_args, ret: this_ret, .. }),
TypeEnum::TFunc(FunSignature { args: other_args, ret: other_ret, .. }),
) = (this, other)
{
// check args
let args_ok = this_args
.iter()
.map(|FuncArg { name, ty, .. }| (name, type_var_to_concrete_def.get(ty).unwrap()))
.zip(other_args.iter().map(|FuncArg { name, ty, .. }| {
(name, type_var_to_concrete_def.get(ty).unwrap())
}))
.all(|(this, other)| {
if this.0 == &"self".into() && this.0 == other.0 {
true
} else {
this.0 == other.0
&& check_overload_type_annotation_compatible(this.1, other.1, unifier)
}
});
// check rets
let ret_ok = check_overload_type_annotation_compatible(
type_var_to_concrete_def.get(this_ret).unwrap(),
type_var_to_concrete_def.get(other_ret).unwrap(),
unifier,
);
// return
args_ok && ret_ok
} else {
) = (this, other) else {
unreachable!("this function must be called with function type")
}
};
// check args
let args_ok = this_args
.iter()
.map(|FuncArg { name, ty, .. }| (name, type_var_to_concrete_def.get(ty).unwrap()))
.zip(other_args.iter().map(|FuncArg { name, ty, .. }| {
(name, type_var_to_concrete_def.get(ty).unwrap())
}))
.all(|(this, other)| {
if this.0 == &"self".into() && this.0 == other.0 {
true
} else {
this.0 == other.0
&& check_overload_type_annotation_compatible(this.1, other.1, unifier)
}
});
// check rets
let ret_ok = check_overload_type_annotation_compatible(
type_var_to_concrete_def.get(this_ret).unwrap(),
type_var_to_concrete_def.get(other_ret).unwrap(),
unifier,
);
// return
args_ok && ret_ok
}
pub fn check_overload_field_type(
@ -334,7 +511,7 @@ impl TopLevelComposer {
)
}
pub fn get_all_assigned_field(stmts: &[ast::Stmt<()>]) -> Result<HashSet<StrRef>, String> {
pub fn get_all_assigned_field(stmts: &[Stmt<()>]) -> Result<HashSet<StrRef>, HashSet<String>> {
let mut result = HashSet::new();
for s in stmts {
match &s.node {
@ -351,10 +528,12 @@ impl TopLevelComposer {
}
} =>
{
return Err(format!(
"redundant type annotation for class fields at {}",
s.location
))
return Err(HashSet::from([
format!(
"redundant type annotation for class fields at {}",
s.location
),
]))
}
ast::StmtKind::Assign { targets, .. } => {
for t in targets {
@ -376,14 +555,14 @@ impl TopLevelComposer {
ast::StmtKind::If { body, orelse, .. } => {
let inited_for_sure = Self::get_all_assigned_field(body.as_slice())?
.intersection(&Self::get_all_assigned_field(orelse.as_slice())?)
.cloned()
.copied()
.collect::<HashSet<_>>();
result.extend(inited_for_sure);
}
ast::StmtKind::Try { body, orelse, finalbody, .. } => {
let inited_for_sure = Self::get_all_assigned_field(body.as_slice())?
.intersection(&Self::get_all_assigned_field(orelse.as_slice())?)
.cloned()
.copied()
.collect::<HashSet<_>>();
result.extend(inited_for_sure);
result.extend(Self::get_all_assigned_field(finalbody.as_slice())?);
@ -391,9 +570,9 @@ impl TopLevelComposer {
ast::StmtKind::With { body, .. } => {
result.extend(Self::get_all_assigned_field(body.as_slice())?);
}
ast::StmtKind::Pass { .. } => {}
ast::StmtKind::Assert { .. } => {}
ast::StmtKind::Expr { .. } => {}
ast::StmtKind::Pass { .. }
| ast::StmtKind::Assert { .. }
| ast::StmtKind::Expr { .. } => {}
_ => {
unimplemented!()
@ -406,7 +585,7 @@ impl TopLevelComposer {
pub fn parse_parameter_default_value(
default: &ast::Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
) -> Result<SymbolValue, String> {
) -> Result<SymbolValue, HashSet<String>> {
parse_parameter_default_value(default, resolver)
}
@ -416,40 +595,6 @@ impl TopLevelComposer {
primitive: &PrimitiveStore,
unifier: &mut Unifier,
) -> Result<(), String> {
fn type_default_param(
val: &SymbolValue,
primitive: &PrimitiveStore,
unifier: &mut Unifier,
) -> TypeAnnotation {
match val {
SymbolValue::Bool(..) => TypeAnnotation::Primitive(primitive.bool),
SymbolValue::Double(..) => TypeAnnotation::Primitive(primitive.float),
SymbolValue::I32(..) => TypeAnnotation::Primitive(primitive.int32),
SymbolValue::I64(..) => TypeAnnotation::Primitive(primitive.int64),
SymbolValue::U32(..) => TypeAnnotation::Primitive(primitive.uint32),
SymbolValue::U64(..) => TypeAnnotation::Primitive(primitive.uint64),
SymbolValue::Str(..) => TypeAnnotation::Primitive(primitive.str),
SymbolValue::Tuple(vs) => {
let vs_tys = vs
.iter()
.map(|v| type_default_param(v, primitive, unifier))
.collect::<Vec<_>>();
TypeAnnotation::Tuple(vs_tys)
}
SymbolValue::OptionNone => TypeAnnotation::CustomClass {
id: primitive.option.get_obj_id(unifier),
params: Default::default(),
},
SymbolValue::OptionSome(v) => {
let ty = type_default_param(v, primitive, unifier);
TypeAnnotation::CustomClass {
id: primitive.option.get_obj_id(unifier),
params: vec![ty],
}
}
}
}
fn is_compatible(
found: &TypeAnnotation,
expect: &TypeAnnotation,
@ -465,7 +610,7 @@ impl TopLevelComposer {
TypeAnnotation::CustomClass { id: e_id, params: e_param },
) => {
*f_id == *e_id
&& *f_id == primitive.option.get_obj_id(unifier)
&& *f_id == primitive.option.obj_id(unifier).unwrap()
&& (f_param.is_empty()
|| (f_param.len() == 1
&& e_param.len() == 1
@ -481,15 +626,15 @@ impl TopLevelComposer {
}
}
let found = type_default_param(val, primitive, unifier);
if !is_compatible(&found, ty, unifier, primitive) {
let found = val.get_type_annotation(primitive, unifier);
if is_compatible(&found, ty, unifier, primitive) {
Ok(())
} else {
Err(format!(
"incompatible default parameter type, expect {}, found {}",
ty.stringify(unifier),
found.stringify(unifier),
))
} else {
Ok(())
}
}
}
@ -497,14 +642,14 @@ impl TopLevelComposer {
pub fn parse_parameter_default_value(
default: &ast::Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
) -> Result<SymbolValue, String> {
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, String> {
) -> Result<SymbolValue, HashSet<String>> {
fn handle_constant(val: &Constant, loc: &Location) -> Result<SymbolValue, HashSet<String>> {
match val {
Constant::Int(v) => {
if let Ok(v) = (*v).try_into() {
Ok(SymbolValue::I32(v))
} else {
Err(format!("integer value out of range at {}", loc))
Err(HashSet::from([format!("integer value out of range at {loc}")]))
}
}
Constant::Float(v) => Ok(SymbolValue::Double(*v)),
@ -512,10 +657,11 @@ pub fn parse_parameter_default_value(
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
)),
Constant::None => Err(format!(
"`None` is not supported, use `none` for option type instead ({})",
loc
)),
Constant::None => Err(HashSet::from([
format!(
"`None` is not supported, use `none` for option type instead ({loc})"
),
])),
_ => unimplemented!("this constant is not supported at {}", loc),
}
}
@ -528,37 +674,51 @@ pub fn parse_parameter_default_value(
let v: Result<i64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::I64(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
_ => Err(HashSet::from([
format!("default param value out of range at {}", default.location)
])),
}
}
_ => Err(format!("only allow constant integer here at {}", default.location))
_ => Err(HashSet::from([
format!("only allow constant integer here at {}", default.location),
]))
}
ast::ExprKind::Name { id, .. } if *id == "uint32".into() => match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<u32, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U32(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
_ => Err(HashSet::from([
format!("default param value out of range at {}", default.location),
])),
}
}
_ => Err(format!("only allow constant integer here at {}", default.location))
_ => Err(HashSet::from([
format!("only allow constant integer here at {}", default.location),
]))
}
ast::ExprKind::Name { id, .. } if *id == "uint64".into() => match &args[0].node {
ast::ExprKind::Constant { value: Constant::Int(v), .. } => {
let v: Result<u64, _> = (*v).try_into();
match v {
Ok(v) => Ok(SymbolValue::U64(v)),
_ => Err(format!("default param value out of range at {}", default.location)),
_ => Err(HashSet::from([
format!("default param value out of range at {}", default.location),
])),
}
}
_ => Err(format!("only allow constant integer here at {}", default.location))
_ => Err(HashSet::from([
format!("only allow constant integer here at {}", default.location),
]))
}
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(
SymbolValue::OptionSome(
Box::new(parse_parameter_default_value(&args[0], resolver)?)
)
),
_ => Err(format!("unsupported default parameter at {}", default.location)),
_ => Err(HashSet::from([
format!("unsupported default parameter at {}", default.location),
])),
}
}
ast::ExprKind::Tuple { elts, .. } => Ok(SymbolValue::Tuple(elts
@ -569,17 +729,21 @@ pub fn parse_parameter_default_value(
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
ast::ExprKind::Name { id, .. } => {
resolver.get_default_param_value(default).ok_or_else(
|| format!(
"`{}` cannot be used as a default parameter at {} \
(not primitive type, option or tuple / not defined?)",
id,
default.location
)
|| HashSet::from([
format!(
"`{}` cannot be used as a default parameter at {} \
(not primitive type, option or tuple / not defined?)",
id,
default.location
),
])
)
}
_ => Err(format!(
"unsupported default parameter (not primitive type, option or tuple) at {}",
default.location
))
_ => Err(HashSet::from([
format!(
"unsupported default parameter (not primitive type, option or tuple) at {}",
default.location
),
]))
}
}

View File

@ -3,13 +3,12 @@ use std::{
collections::{HashMap, HashSet},
fmt::Debug,
iter::FromIterator,
ops::{Deref, DerefMut},
sync::Arc,
};
use super::codegen::CodeGenContext;
use super::typecheck::type_inferencer::PrimitiveStore;
use super::typecheck::typedef::{FunSignature, FuncArg, SharedUnifier, Type, TypeEnum, Unifier};
use super::typecheck::typedef::{FunSignature, FuncArg, SharedUnifier, Type, TypeEnum, Unifier, VarMap};
use crate::{
codegen::CodeGenerator,
symbol_resolver::{SymbolResolver, ValueEnum},
@ -26,6 +25,7 @@ pub struct DefinitionId(pub usize);
pub mod builtins;
pub mod composer;
pub mod helper;
pub mod numpy;
pub mod type_annotation;
use composer::*;
use type_annotation::*;
@ -49,13 +49,14 @@ pub struct GenCall {
}
impl GenCall {
#[must_use]
pub fn new(fp: GenCallCallback) -> GenCall {
GenCall { fp }
}
pub fn run<'ctx, 'a>(
pub fn run<'ctx>(
&self,
ctx: &mut CodeGenContext<'ctx, 'a>,
ctx: &mut CodeGenContext<'ctx, '_>,
obj: Option<(Type, ValueEnum<'ctx>)>,
fun: (&FunSignature, DefinitionId),
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
@ -75,58 +76,62 @@ impl Debug for GenCall {
pub struct FunInstance {
pub body: Arc<Vec<Stmt<Option<Type>>>>,
pub calls: Arc<HashMap<CodeLocation, CallId>>,
pub subst: HashMap<u32, Type>,
pub subst: VarMap,
pub unifier_id: usize,
}
#[derive(Debug, Clone)]
pub enum TopLevelDef {
Class {
// name for error messages and symbols
/// Name for error messages and symbols.
name: StrRef,
// object ID used for TypeEnum
/// Object ID used for [TypeEnum].
object_id: DefinitionId,
/// type variables bounded to the class.
type_vars: Vec<Type>,
// class fields
// name, type, is mutable
/// Class fields.
///
/// Name and type is mutable.
fields: Vec<(StrRef, Type, bool)>,
// class methods, pointing to the corresponding function definition.
/// Class methods, pointing to the corresponding function definition.
methods: Vec<(StrRef, Type, DefinitionId)>,
// ancestor classes, including itself.
/// Ancestor classes, including itself.
ancestors: Vec<TypeAnnotation>,
// symbol resolver of the module defined the class, none if it is built-in type
/// Symbol resolver of the module defined the class; [None] if it is built-in type.
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
// constructor type
/// Constructor type.
constructor: Option<Type>,
// definition location
/// Definition location.
loc: Option<Location>,
},
Function {
// prefix for symbol, should be unique globally
/// Prefix for symbol, should be unique globally.
name: String,
// simple name, the same as in method/function definition
/// Simple name, the same as in method/function definition.
simple_name: StrRef,
// function signature.
/// Function signature.
signature: Type,
// instantiated type variable IDs
/// Instantiated type variable IDs.
var_id: Vec<u32>,
/// Function instance to symbol mapping
/// Key: string representation of type variable values, sorted by variable ID in ascending
///
/// * Key: String representation of type variable values, sorted by variable ID in ascending
/// order, including type variables associated with the class.
/// Value: function symbol name.
/// * Value: Function symbol name.
instance_to_symbol: HashMap<String, String>,
/// Function instances to annotated AST mapping
/// Key: string representation of type variable values, sorted by variable ID in ascending
///
/// * Key: String representation of type variable values, sorted by variable ID in ascending
/// order, including type variables associated with the class. Excluding rigid type
/// variables.
/// rigid type variables that would be substituted when the function is instantiated.
///
/// Rigid type variables that would be substituted when the function is instantiated.
instance_to_stmt: HashMap<String, FunInstance>,
// symbol resolver of the module defined the class
/// Symbol resolver of the module defined the class.
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
// custom codegen callback
/// Custom code generation callback.
codegen_callback: Option<Arc<GenCall>>,
// definition location
/// Definition location.
loc: Option<Location>,
},
}

View File

@ -0,0 +1,103 @@
use itertools::Itertools;
use crate::{
toplevel::helper::PRIMITIVE_DEF_IDS,
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, TypeEnum, Unifier, VarMap},
},
};
/// Creates a `ndarray` [`Type`] with the given type arguments.
///
/// * `dtype` - The element type of the `ndarray`, or [`None`] if the type variable is not
/// specialized.
/// * `ndims` - The number of dimensions of the `ndarray`, or [`None`] if the type variable is not
/// specialized.
pub fn make_ndarray_ty(
unifier: &mut Unifier,
primitives: &PrimitiveStore,
dtype: Option<Type>,
ndims: Option<Type>,
) -> Type {
subst_ndarray_tvars(unifier, primitives.ndarray, dtype, ndims)
}
/// Substitutes type variables in `ndarray`.
///
/// * `dtype` - The element type of the `ndarray`, or [`None`] if the type variable is not
/// specialized.
/// * `ndims` - The number of dimensions of the `ndarray`, or [`None`] if the type variable is not
/// specialized.
pub fn subst_ndarray_tvars(
unifier: &mut Unifier,
ndarray: Type,
dtype: Option<Type>,
ndims: Option<Type>,
) -> Type {
let TypeEnum::TObj { obj_id, params, .. } = &*unifier.get_ty_immutable(ndarray) else {
panic!("Expected `ndarray` to be TObj, but got {}", unifier.stringify(ndarray))
};
debug_assert_eq!(*obj_id, PRIMITIVE_DEF_IDS.ndarray);
if dtype.is_none() && ndims.is_none() {
return ndarray
}
let tvar_ids = params.iter()
.map(|(obj_id, _)| *obj_id)
.collect_vec();
debug_assert_eq!(tvar_ids.len(), 2);
let mut tvar_subst = VarMap::new();
if let Some(dtype) = dtype {
tvar_subst.insert(tvar_ids[0], dtype);
}
if let Some(ndims) = ndims {
tvar_subst.insert(tvar_ids[1], ndims);
}
unifier.subst(ndarray, &tvar_subst).unwrap_or(ndarray)
}
fn unpack_ndarray_tvars(
unifier: &mut Unifier,
ndarray: Type,
) -> Vec<(u32, Type)> {
let TypeEnum::TObj { obj_id, params, .. } = &*unifier.get_ty_immutable(ndarray) else {
panic!("Expected `ndarray` to be TObj, but got {}", unifier.stringify(ndarray))
};
debug_assert_eq!(*obj_id, PRIMITIVE_DEF_IDS.ndarray);
debug_assert_eq!(params.len(), 2);
params.iter()
.sorted_by_key(|(obj_id, _)| *obj_id)
.map(|(var_id, ty)| (*var_id, *ty))
.collect_vec()
}
/// Unpacks the type variable IDs of `ndarray` into a tuple. The elements of the tuple corresponds
/// to `dtype` (the element type) and `ndims` (the number of dimensions) of the `ndarray`
/// respectively.
pub fn unpack_ndarray_var_ids(
unifier: &mut Unifier,
ndarray: Type,
) -> (u32, u32) {
unpack_ndarray_tvars(unifier, ndarray)
.into_iter()
.map(|v| v.0)
.collect_tuple()
.unwrap()
}
/// Unpacks the type variables of `ndarray` into a tuple. The elements of the tuple corresponds to
/// `dtype` (the element type) and `ndims` (the number of dimensions) of the `ndarray` respectively.
pub fn unpack_ndarray_var_tys(
unifier: &mut Unifier,
ndarray: Type,
) -> (Type, Type) {
unpack_ndarray_tvars(unifier, ndarray)
.into_iter()
.map(|v| v.1)
.collect_tuple()
.unwrap()
}

View File

@ -1,13 +1,11 @@
---
source: nac3core/src/toplevel/test.rs
assertion_line: 549
expression: res_vec
---
[
"Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [18]\n}\n",
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [156]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",

View File

@ -1,15 +1,13 @@
---
source: nac3core/src/toplevel/test.rs
assertion_line: 549
expression: res_vec
---
[
"Class {\nname: \"A\",\nancestors: [\"A[T]\"],\nfields: [\"a\", \"b\", \"c\"],\nmethods: [(\"__init__\", \"fn[[t:T], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"T\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[t:T], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.foo\",\nsig: \"fn[[c:C], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B[typevar7]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"typevar7\"]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B[typevar145]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"typevar145\"]\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
"Class {\nname: \"C\",\nancestors: [\"C\", \"B[bool]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\", \"e\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: []\n}\n",

View File

@ -1,15 +1,13 @@
---
source: nac3core/src/toplevel/test.rs
assertion_line: 549
expression: res_vec
---
[
"Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n",
"Class {\nname: \"A\",\nancestors: [\"A[T, V]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v:V], none]\"), (\"fun\", \"fn[[a:T], V]\")],\ntype_vars: [\"T\", \"V\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [20]\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [25]\n}\n",
"Function {\nname: \"gfun\",\nsig: \"fn[[a:A[int32, list[float]]], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [158]\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [163]\n}\n",
"Function {\nname: \"gfun\",\nsig: \"fn[[a:A[list[float], int32]], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
]

View File

@ -1,15 +1,13 @@
---
source: nac3core/src/toplevel/test.rs
assertion_line: 549
expression: res_vec
---
[
"Class {\nname: \"A\",\nancestors: [\"A[typevar6, typevar7]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"typevar6\", \"typevar7\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[bool, float], b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[bool, float]], A[bool, int32]]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\", \"A[int64, bool]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\")],\ntype_vars: []\n}\n",
"Class {\nname: \"A\",\nancestors: [\"A[typevar144, typevar145]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[float, bool], b:B], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\")],\ntype_vars: [\"typevar144\", \"typevar145\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[float, bool], b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[float, bool]], A[bool, int32]]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\", \"A[int64, bool]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[float, bool]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[list[B], int32]], tuple[A[virtual[A[B, int32]], bool], B]]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:B], B]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.bar\",\nsig: \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.bar\",\nsig: \"fn[[a:A[list[B], int32]], tuple[A[virtual[A[B, int32]], bool], B]]\",\nvar_id: []\n}\n",
]

View File

@ -1,19 +1,17 @@
---
source: nac3core/src/toplevel/test.rs
assertion_line: 549
expression: res_vec
---
[
"Class {\nname: \"A\",\nancestors: [\"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [26]\n}\n",
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [164]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [34]\n}\n",
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [172]\n}\n",
]

View File

@ -36,7 +36,7 @@ struct Resolver(Arc<ResolverInternal>);
impl SymbolResolver for Resolver {
fn get_default_param_value(
&self,
_: &nac3parser::ast::Expr,
_: &ast::Expr,
) -> Option<crate::symbol_resolver::SymbolValue> {
unimplemented!()
}
@ -64,8 +64,9 @@ impl SymbolResolver for Resolver {
unimplemented!()
}
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
self.0.id_to_def.lock().get(&id).cloned()
.ok_or_else(|| HashSet::from(["Unknown identifier".to_string()]))
}
fn get_string_id(&self, _: &str) -> i32 {
@ -105,21 +106,37 @@ impl SymbolResolver for Resolver {
def __init__(self):
self.c: int32 = 4
self.a: bool = True
"}
"},
];
"register"
)]
fn test_simple_register(source: Vec<&str>) {
let mut composer: TopLevelComposer = Default::default();
let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0;
for s in source {
let ast = parse_program(s, Default::default()).unwrap();
let ast = ast[0].clone();
composer.register_top_level(ast, None, "".into()).unwrap();
composer.register_top_level(ast, None, "".into(), false).unwrap();
}
}
#[test_case(
indoc! {"
class A:
def foo(self):
pass
a = A()
"};
"register"
)]
fn test_simple_register_without_constructor(source: &str) {
let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0;
let ast = parse_program(source, Default::default()).unwrap();
let ast = ast[0].clone();
composer.register_top_level(ast, None, "".into(), true).unwrap();
}
#[test_case(
vec![
indoc! {"
@ -148,7 +165,7 @@ fn test_simple_register(source: Vec<&str>) {
"function compose"
)]
fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&str>) {
let mut composer: TopLevelComposer = Default::default();
let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0;
let internal_resolver = Arc::new(ResolverInternal {
id_to_def: Default::default(),
@ -163,7 +180,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
let ast = ast[0].clone();
let (id, def_id, ty) =
composer.register_top_level(ast, Some(resolver.clone()), "".into()).unwrap();
composer.register_top_level(ast, Some(resolver.clone()), "".into(), false).unwrap();
internal_resolver.add_id_def(id, def_id);
if let Some(ty) = ty {
internal_resolver.add_id_type(id, ty);
@ -345,7 +362,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
pass
"}
],
vec!["application of type vars to generic class is not currently supported (at unknown: line 4 column 24)"];
vec!["application of type vars to generic class is not currently supported (at unknown:4:24)"];
"err no type var in generic app"
)]
#[test_case(
@ -401,7 +418,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
def __init__():
pass
"}],
vec!["__init__ method must have a `self` parameter (at unknown: line 2 column 5)"];
vec!["__init__ method must have a `self` parameter (at unknown:2:5)"];
"err no self_1"
)]
#[test_case(
@ -423,7 +440,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
"}
],
vec!["a class definition can only have at most one base class declaration and one generic declaration (at unknown: line 1 column 24)"];
vec!["a class definition can only have at most one base class declaration and one generic declaration (at unknown:1:24)"];
"err multiple inheritance"
)]
#[test_case(
@ -491,12 +508,12 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
pass
"}
],
vec!["duplicate definition of class `A` (at unknown: line 1 column 1)"];
vec!["duplicate definition of class `A` (at unknown:1:1)"];
"class same name"
)]
fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
let print = false;
let mut composer: TopLevelComposer = Default::default();
let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0;
let internal_resolver = make_internal_resolver_with_tvar(
vec![
@ -515,7 +532,7 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
let ast = ast[0].clone();
let (id, def_id, ty) = {
match composer.register_top_level(ast, Some(resolver.clone()), "".into()) {
match composer.register_top_level(ast, Some(resolver.clone()), "".into(), false) {
Ok(x) => x,
Err(msg) => {
if print {
@ -535,9 +552,9 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
if let Err(msg) = composer.start_analysis(false) {
if print {
println!("{}", msg);
println!("{}", msg.iter().sorted().join("\n----------\n"));
} else {
assert_eq!(res[0], msg);
assert_eq!(res[0], msg.iter().next().unwrap());
}
} else {
// skip 5 to skip primitives
@ -673,7 +690,7 @@ fn test_analyze(source: Vec<&str>, res: Vec<&str>) {
)]
fn test_inference(source: Vec<&str>, res: Vec<&str>) {
let print = true;
let mut composer: TopLevelComposer = Default::default();
let mut composer = TopLevelComposer::new(Vec::new(), ComposerConfig::default(), 64).0;
let internal_resolver = make_internal_resolver_with_tvar(
vec![
@ -699,7 +716,7 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
let ast = ast[0].clone();
let (id, def_id, ty) = {
match composer.register_top_level(ast, Some(resolver.clone()), "".into()) {
match composer.register_top_level(ast, Some(resolver.clone()), "".into(), false) {
Ok(x) => x,
Err(msg) => {
if print {
@ -719,9 +736,9 @@ fn test_inference(source: Vec<&str>, res: Vec<&str>) {
if let Err(msg) = composer.start_analysis(true) {
if print {
println!("{}", msg);
println!("{}", msg.iter().sorted().join("\n----------\n"));
} else {
assert_eq!(res[0], msg);
assert_eq!(res[0], msg.iter().next().unwrap());
}
} else {
// skip 5 to skip primitives

View File

@ -1,4 +1,8 @@
use crate::symbol_resolver::SymbolValue;
use crate::toplevel::helper::PRIMITIVE_DEF_IDS;
use crate::typecheck::typedef::VarMap;
use super::*;
use nac3parser::ast::Constant;
#[derive(Clone, Debug)]
pub enum TypeAnnotation {
@ -12,6 +16,8 @@ pub enum TypeAnnotation {
// can only be CustomClassKind
Virtual(Box<TypeAnnotation>),
TypeVar(Type),
/// A `Literal` allowing a subset of literals.
Literal(Vec<Constant>),
List(Box<TypeAnnotation>),
Tuple(Vec<TypeAnnotation>),
}
@ -22,17 +28,16 @@ impl TypeAnnotation {
match self {
Primitive(ty) | TypeVar(ty) => unifier.stringify(*ty),
CustomClass { id, params } => {
let class_name = match unifier.top_level {
Some(ref top) => {
if let TopLevelDef::Class { name, .. } =
&*top.definitions.read()[id.0].read()
{
(*name).into()
} else {
unreachable!()
}
let class_name = if let Some(ref top) = unifier.top_level {
if let TopLevelDef::Class { name, .. } =
&*top.definitions.read()[id.0].read()
{
(*name).into()
} else {
unreachable!()
}
None => format!("class_def_{}", id.0),
} else {
format!("class_def_{}", id.0)
};
format!(
"{}{}",
@ -40,13 +45,14 @@ impl TypeAnnotation {
{
let param_list = params.iter().map(|p| p.stringify(unifier)).collect_vec().join(", ");
if param_list.is_empty() {
"".into()
String::new()
} else {
format!("[{}]", param_list)
format!("[{param_list}]")
}
}
)
}
Literal(values) => format!("Literal({})", values.iter().map(|v| format!("{v:?}")).join(", ")),
Virtual(ty) => format!("virtual[{}]", ty.stringify(unifier)),
List(ty) => format!("list[{}]", ty.stringify(unifier)),
Tuple(types) => {
@ -56,6 +62,12 @@ impl TypeAnnotation {
}
}
/// Parses an AST expression `expr` into a [`TypeAnnotation`].
///
/// * `locked` - A [`HashMap`] containing the IDs of known definitions, mapped to a [`Vec`] of all
/// generic variables associated with the definition.
/// * `type_var` - The type variable associated with the type argument currently being parsed. Pass
/// [`None`] when this function is invoked externally.
pub fn parse_ast_to_type_annotation_kinds<T>(
resolver: &(dyn SymbolResolver + Send + Sync),
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
@ -64,7 +76,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
expr: &ast::Expr<T>,
// the key stores the type_var of this topleveldef::class, we only need this field here
locked: HashMap<DefinitionId, Vec<Type>>,
) -> Result<TypeAnnotation, String> {
) -> Result<TypeAnnotation, HashSet<String>> {
let name_handle = |id: &StrRef,
unifier: &mut Unifier,
locked: HashMap<DefinitionId, Vec<Type>>| {
@ -83,7 +95,7 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
} else if id == &"str".into() {
Ok(TypeAnnotation::Primitive(primitives.str))
} else if id == &"Exception".into() {
Ok(TypeAnnotation::CustomClass { id: DefinitionId(7), params: Default::default() })
Ok(TypeAnnotation::CustomClass { id: PRIMITIVE_DEF_IDS.exception, params: Vec::default() })
} else if let Ok(obj_id) = resolver.get_identifier_def(*id) {
let type_vars = {
let def_read = top_level_defs[obj_id.0].try_read();
@ -91,10 +103,12 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
type_vars.clone()
} else {
return Err(format!(
"function cannot be used as a type (at {})",
expr.location
));
return Err(HashSet::from([
format!(
"function cannot be used as a type (at {})",
expr.location
),
]))
}
} else {
locked.get(&obj_id).unwrap().clone()
@ -102,11 +116,13 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
};
// check param number here
if !type_vars.is_empty() {
return Err(format!(
"expect {} type variable parameter but got 0 (at {})",
type_vars.len(),
expr.location,
));
return Err(HashSet::from([
format!(
"expect {} type variable parameter but got 0 (at {})",
type_vars.len(),
expr.location,
),
]))
}
Ok(TypeAnnotation::CustomClass { id: obj_id, params: vec![] })
} else if let Ok(ty) = resolver.get_symbol_type(unifier, top_level_defs, primitives, *id) {
@ -115,10 +131,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
unifier.unify(var, ty).unwrap();
Ok(TypeAnnotation::TypeVar(ty))
} else {
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location))
Err(HashSet::from([
format!("`{}` is not a valid type annotation (at {})", id, expr.location),
]))
}
} else {
Err(format!("`{}` is not a valid type annotation (at {})", id, expr.location))
Err(HashSet::from([
format!("`{}` is not a valid type annotation (at {})", id, expr.location),
]))
}
};
@ -127,19 +147,21 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
slice: &ast::Expr<T>,
unifier: &mut Unifier,
mut locked: HashMap<DefinitionId, Vec<Type>>| {
if vec!["virtual".into(), "Generic".into(), "list".into(), "tuple".into()].contains(id)
if ["virtual".into(), "Generic".into(), "list".into(), "tuple".into(), "Option".into()].contains(id)
{
return Err(format!("keywords cannot be class name (at {})", expr.location));
return Err(HashSet::from([
format!("keywords cannot be class name (at {})", expr.location),
]))
}
let obj_id = resolver.get_identifier_def(*id)?;
let type_vars = {
let def_read = top_level_defs[obj_id.0].try_read();
if let Some(def_read) = def_read {
if let TopLevelDef::Class { type_vars, .. } = &*def_read {
type_vars.clone()
} else {
let TopLevelDef::Class { type_vars, .. } = &*def_read else {
unreachable!("must be class here")
}
};
type_vars.clone()
} else {
locked.get(&obj_id).unwrap().clone()
}
@ -152,12 +174,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
vec![slice]
};
if type_vars.len() != params_ast.len() {
return Err(format!(
"expect {} type parameters but got {} (at {})",
type_vars.len(),
params_ast.len(),
params_ast[0].location,
));
return Err(HashSet::from([
format!(
"expect {} type parameters but got {} (at {})",
type_vars.len(),
params_ast.len(),
params_ast[0].location,
),
]))
}
let result = params_ast
.iter()
@ -181,15 +205,17 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
if no_type_var {
result
} else {
return Err(format!(
"application of type vars to generic class \
is not currently supported (at {})",
params_ast[0].location
));
return Err(HashSet::from([
format!(
"application of type vars to generic class is not currently supported (at {})",
params_ast[0].location
),
]))
}
};
Ok(TypeAnnotation::CustomClass { id: obj_id, params: param_type_infos })
};
match &expr.node {
ast::ExprKind::Name { id, .. } => name_handle(id, unifier, locked),
// virtual
@ -281,16 +307,70 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
Ok(TypeAnnotation::Tuple(type_annotations))
}
// Literal
ast::ExprKind::Subscript { value, slice, .. }
if {
matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"Literal".into())
} => {
let tup_elts = {
if let ast::ExprKind::Tuple { elts, .. } = &slice.node {
elts.as_slice()
} else {
std::slice::from_ref(slice.as_ref())
}
};
let type_annotations = tup_elts
.iter()
.map(|e| {
match &e.node {
ast::ExprKind::Constant { value, .. } => Ok(
TypeAnnotation::Literal(vec![value.clone()]),
),
_ => parse_ast_to_type_annotation_kinds(
resolver,
top_level_defs,
unifier,
primitives,
e,
locked.clone(),
),
}
})
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flat_map(|type_ann| match type_ann {
TypeAnnotation::Literal(values) => values,
_ => unreachable!(),
})
.collect_vec();
if type_annotations.len() == 1 {
Ok(TypeAnnotation::Literal(type_annotations))
} else {
Err(HashSet::from([
format!("multiple literal bounds are currently unsupported (at {})", value.location)
]))
}
}
// custom class
ast::ExprKind::Subscript { value, slice, .. } => {
if let ast::ExprKind::Name { id, .. } = &value.node {
class_name_handle(id, slice, unifier, locked)
} else {
Err(format!("unsupported expression type for class name (at {})", value.location))
Err(HashSet::from([
format!("unsupported expression type for class name (at {})", value.location)
]))
}
}
_ => Err(format!("unsupported expression for type annotation (at {})", expr.location)),
ast::ExprKind::Constant { value, .. } => {
Ok(TypeAnnotation::Literal(vec![value.clone()]))
}
_ => Err(HashSet::from([
format!("unsupported expression for type annotation (at {})", expr.location),
])),
}
}
@ -300,107 +380,145 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
pub fn get_type_from_type_annotation_kinds(
top_level_defs: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
ann: &TypeAnnotation,
subst_list: &mut Option<Vec<Type>>
) -> Result<Type, String> {
) -> Result<Type, HashSet<String>> {
match ann {
TypeAnnotation::CustomClass { id: obj_id, params } => {
let def_read = top_level_defs[obj_id.0].read();
let class_def: &TopLevelDef = def_read.deref();
if let TopLevelDef::Class { fields, methods, type_vars, .. } = class_def {
if type_vars.len() != params.len() {
Err(format!(
let class_def: &TopLevelDef = &def_read;
let TopLevelDef::Class { fields, methods, type_vars, .. } = class_def else {
unreachable!("should be class def here")
};
if type_vars.len() != params.len() {
return Err(HashSet::from([
format!(
"unexpected number of type parameters: expected {} but got {}",
type_vars.len(),
params.len()
))
} else {
let param_ty = params
.iter()
.map(|x| {
get_type_from_type_annotation_kinds(
top_level_defs,
unifier,
primitives,
x,
subst_list
)
})
.collect::<Result<Vec<_>, _>>()?;
),
]))
}
let subst = {
// check for compatible range
// TODO: if allow type var to be applied(now this disallowed in the parse_to_type_annotation), need more check
let mut result: HashMap<u32, Type> = HashMap::new();
for (tvar, p) in type_vars.iter().zip(param_ty) {
if let TypeEnum::TVar { id, range, fields: None, name, loc } =
unifier.get_ty(*tvar).as_ref()
{
let ok: bool = {
// create a temp type var and unify to check compatibility
p == *tvar || {
let temp = unifier.get_fresh_var_with_range(
range.as_slice(),
*name,
*loc,
);
unifier.unify(temp.0, p).is_ok()
}
};
if ok {
result.insert(*id, p);
} else {
return Err(format!(
let param_ty = params
.iter()
.map(|x| {
get_type_from_type_annotation_kinds(
top_level_defs,
unifier,
x,
subst_list
)
})
.collect::<Result<Vec<_>, _>>()?;
let subst = {
// check for compatible range
// TODO: if allow type var to be applied(now this disallowed in the parse_to_type_annotation), need more check
let mut result = VarMap::new();
for (tvar, p) in type_vars.iter().zip(param_ty) {
match unifier.get_ty(*tvar).as_ref() {
TypeEnum::TVar { id, range, fields: None, name, loc, is_const_generic: false } => {
let ok: bool = {
// create a temp type var and unify to check compatibility
p == *tvar || {
let temp = unifier.get_fresh_var_with_range(
range.as_slice(),
*name,
*loc,
);
unifier.unify(temp.0, p).is_ok()
}
};
if ok {
result.insert(*id, p);
} else {
return Err(HashSet::from([
format!(
"cannot apply type {} to type variable with id {:?}",
unifier.internal_stringify(
p,
&mut |id| format!("class{}", id),
&mut |id| format!("typevar{}", id),
&mut |id| format!("class{id}"),
&mut |id| format!("typevar{id}"),
&mut None
),
*id
));
}
} else {
unreachable!("must be generic type var")
)
]))
}
}
result
};
let mut tobj_fields = methods
.iter()
.map(|(name, ty, _)| {
let subst_ty = unifier.subst(*ty, &subst).unwrap_or(*ty);
// methods are immutable
(*name, (subst_ty, false))
})
.collect::<HashMap<_, _>>();
tobj_fields.extend(fields.iter().map(|(name, ty, mutability)| {
let subst_ty = unifier.subst(*ty, &subst).unwrap_or(*ty);
(*name, (subst_ty, *mutability))
}));
let need_subst = !subst.is_empty();
let ty = unifier.add_ty(TypeEnum::TObj {
obj_id: *obj_id,
fields: tobj_fields,
params: subst,
});
if need_subst {
subst_list.as_mut().map(|wl| wl.push(ty));
TypeEnum::TVar { id, range, name, loc, is_const_generic: true, .. } => {
let ty = range[0];
let ok: bool = {
// create a temp type var and unify to check compatibility
p == *tvar || {
let temp = unifier.get_fresh_const_generic_var(
ty,
*name,
*loc,
);
unifier.unify(temp.0, p).is_ok()
}
};
if ok {
result.insert(*id, p);
} else {
return Err(HashSet::from([
format!(
"cannot apply type {} to type variable {}",
unifier.stringify(p),
name.unwrap_or_else(|| format!("typevar{id}").into()),
),
]))
}
}
_ => unreachable!("must be generic type var"),
}
Ok(ty)
}
} else {
unreachable!("should be class def here")
result
};
let mut tobj_fields = methods
.iter()
.map(|(name, ty, _)| {
let subst_ty = unifier.subst(*ty, &subst).unwrap_or(*ty);
// methods are immutable
(*name, (subst_ty, false))
})
.collect::<HashMap<_, _>>();
tobj_fields.extend(fields.iter().map(|(name, ty, mutability)| {
let subst_ty = unifier.subst(*ty, &subst).unwrap_or(*ty);
(*name, (subst_ty, *mutability))
}));
let need_subst = !subst.is_empty();
let ty = unifier.add_ty(TypeEnum::TObj {
obj_id: *obj_id,
fields: tobj_fields,
params: subst,
});
if need_subst {
if let Some(wl) = subst_list.as_mut() {
wl.push(ty);
}
}
Ok(ty)
}
TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) => Ok(*ty),
TypeAnnotation::Literal(values) => {
let values = values.iter()
.map(SymbolValue::from_constant_inferred)
.collect::<Result<Vec<_>, _>>()
.map_err(|err| HashSet::from([err]))?;
let var = unifier.get_fresh_literal(values, None);
Ok(var)
}
TypeAnnotation::Virtual(ty) => {
let ty = get_type_from_type_annotation_kinds(
top_level_defs,
unifier,
primitives,
ty.as_ref(),
subst_list
)?;
@ -410,7 +528,6 @@ pub fn get_type_from_type_annotation_kinds(
let ty = get_type_from_type_annotation_kinds(
top_level_defs,
unifier,
primitives,
ty.as_ref(),
subst_list
)?;
@ -420,7 +537,7 @@ pub fn get_type_from_type_annotation_kinds(
let tys = tys
.iter()
.map(|x| {
get_type_from_type_annotation_kinds(top_level_defs, unifier, primitives, x, subst_list)
get_type_from_type_annotation_kinds(top_level_defs, unifier, x, subst_list)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(unifier.add_ty(TypeEnum::TTuple { ty: tys }))
@ -437,9 +554,10 @@ pub fn get_type_from_type_annotation_kinds(
/// considered to be type variables associated with the class \
/// \
/// But note that here we do not make a duplication of `T`, `V`, we directly
/// use them as they are in the TopLevelDef::Class since those in the
/// TopLevelDef::Class.type_vars will be substitute later when seeing applications/instantiations
/// use them as they are in the [`TopLevelDef::Class`] since those in the
/// `TopLevelDef::Class.type_vars` will be substitute later when seeing applications/instantiations
/// the Type of their fields and methods will also be subst when application/instantiation
#[must_use]
pub fn make_self_type_annotation(type_vars: &[Type], object_id: DefinitionId) -> TypeAnnotation {
TypeAnnotation::CustomClass {
id: object_id,
@ -450,27 +568,25 @@ pub fn make_self_type_annotation(type_vars: &[Type], object_id: DefinitionId) ->
/// get all the occurences of type vars contained in a type annotation
/// e.g. `A[int, B[T], V, virtual[C[G]]]` => [T, V, G]
/// this function will not make a duplicate of type var
#[must_use]
pub fn get_type_var_contained_in_type_annotation(ann: &TypeAnnotation) -> Vec<TypeAnnotation> {
let mut result: Vec<TypeAnnotation> = Vec::new();
match ann {
TypeAnnotation::TypeVar(..) => result.push(ann.clone()),
TypeAnnotation::Virtual(ann) => {
result.extend(get_type_var_contained_in_type_annotation(ann.as_ref()))
TypeAnnotation::Virtual(ann) | TypeAnnotation::List(ann) => {
result.extend(get_type_var_contained_in_type_annotation(ann.as_ref()));
}
TypeAnnotation::CustomClass { params, .. } => {
for p in params {
result.extend(get_type_var_contained_in_type_annotation(p));
}
}
TypeAnnotation::List(ann) => {
result.extend(get_type_var_contained_in_type_annotation(ann.as_ref()))
}
TypeAnnotation::Tuple(anns) => {
for a in anns {
result.extend(get_type_var_contained_in_type_annotation(a));
}
}
TypeAnnotation::Primitive(..) => {}
TypeAnnotation::Primitive(..) | TypeAnnotation::Literal { .. } => {}
}
result
}
@ -485,18 +601,17 @@ pub fn check_overload_type_annotation_compatible(
(TypeAnnotation::Primitive(a), TypeAnnotation::Primitive(b)) => a == b,
(TypeAnnotation::TypeVar(a), TypeAnnotation::TypeVar(b)) => {
let a = unifier.get_ty(*a);
let a = a.deref();
let a = &*a;
let b = unifier.get_ty(*b);
let b = b.deref();
if let (
let b = &*b;
let (
TypeEnum::TVar { id: a, fields: None, .. },
TypeEnum::TVar { id: b, fields: None, .. },
) = (a, b)
{
a == b
} else {
) = (a, b) else {
unreachable!("must be type var")
}
};
a == b
}
(TypeAnnotation::Virtual(a), TypeAnnotation::Virtual(b))
| (TypeAnnotation::List(a), TypeAnnotation::List(b)) => {

View File

@ -2,13 +2,15 @@ use crate::typecheck::typedef::TypeEnum;
use super::type_inferencer::Inferencer;
use super::typedef::Type;
use nac3parser::ast::{self, Expr, ExprKind, Stmt, StmtKind, StrRef};
use nac3parser::ast::{self, Constant, Expr, ExprKind, Operator::{LShift, RShift}, Stmt, StmtKind, StrRef};
use std::{collections::HashSet, iter::once};
impl<'a> Inferencer<'a> {
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), String> {
fn should_have_value(&mut self, expr: &Expr<Option<Type>>) -> Result<(), HashSet<String>> {
if matches!(expr.custom, Some(ty) if self.unifier.unioned(ty, self.primitives.none)) {
Err(format!("Error at {}: cannot have value none", expr.location))
Err(HashSet::from([
format!("Error at {}: cannot have value none", expr.location),
]))
} else {
Ok(())
}
@ -18,10 +20,11 @@ impl<'a> Inferencer<'a> {
&mut self,
pattern: &Expr<Option<Type>>,
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<(), String> {
) -> Result<(), HashSet<String>> {
match &pattern.node {
ast::ExprKind::Name { id, .. } if id == &"none".into() =>
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
ExprKind::Name { id, .. } if id == &"none".into() => Err(HashSet::from([
format!("cannot assign to a `none` (at {})", pattern.location),
])),
ExprKind::Name { id, .. } => {
if !defined_identifiers.contains(id) {
defined_identifiers.insert(*id);
@ -30,7 +33,7 @@ impl<'a> Inferencer<'a> {
Ok(())
}
ExprKind::Tuple { elts, .. } => {
for elt in elts.iter() {
for elt in elts {
self.check_pattern(elt, defined_identifiers)?;
self.should_have_value(elt)?;
}
@ -41,15 +44,19 @@ impl<'a> Inferencer<'a> {
self.should_have_value(value)?;
self.check_expr(slice, defined_identifiers)?;
if let TypeEnum::TTuple { .. } = &*self.unifier.get_ty(value.custom.unwrap()) {
return Err(format!(
"Error at {}: cannot assign to tuple element",
value.location
));
return Err(HashSet::from([
format!(
"Error at {}: cannot assign to tuple element",
value.location
),
]))
}
Ok(())
}
ExprKind::Constant { .. } => {
Err(format!("cannot assign to a constant (at {})", pattern.location))
Err(HashSet::from([
format!("cannot assign to a constant (at {})", pattern.location),
]))
}
_ => self.check_expr(pattern, defined_identifiers),
}
@ -59,15 +66,17 @@ impl<'a> Inferencer<'a> {
&mut self,
expr: &Expr<Option<Type>>,
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<(), String> {
) -> Result<(), HashSet<String>> {
// there are some cases where the custom field is None
if let Some(ty) = &expr.custom {
if !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
return Err(format!(
"expected concrete type at {} but got {}",
expr.location,
self.unifier.get_ty(*ty).get_type_name()
));
if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) && !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
return Err(HashSet::from([
format!(
"expected concrete type at {} but got {}",
expr.location,
self.unifier.get_ty(*ty).get_type_name()
)
]))
}
}
match &expr.node {
@ -87,10 +96,12 @@ impl<'a> Inferencer<'a> {
self.defined_identifiers.insert(*id);
}
Err(e) => {
return Err(format!(
"type error at identifier `{}` ({}) at {}",
id, e, expr.location
));
return Err(HashSet::from([
format!(
"type error at identifier `{}` ({}) at {}",
id, e, expr.location
)
]))
}
}
}
@ -98,7 +109,7 @@ impl<'a> Inferencer<'a> {
ExprKind::List { elts, .. }
| ExprKind::Tuple { elts, .. }
| ExprKind::BoolOp { values: elts, .. } => {
for elt in elts.iter() {
for elt in elts {
self.check_expr(elt, defined_identifiers)?;
self.should_have_value(elt)?;
}
@ -107,11 +118,29 @@ impl<'a> Inferencer<'a> {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
}
ExprKind::BinOp { left, right, .. } => {
ExprKind::BinOp { left, op, right } => {
self.check_expr(left, defined_identifiers)?;
self.check_expr(right, defined_identifiers)?;
self.should_have_value(left)?;
self.should_have_value(right)?;
// Check whether a bitwise shift has a negative RHS constant value
if *op == LShift || *op == RShift {
if let ExprKind::Constant { value, .. } = &right.node {
let Constant::Int(rhs_val) = value else {
unreachable!()
};
if *rhs_val < 0 {
return Err(HashSet::from([
format!(
"shift count is negative at {}",
right.location
),
]))
}
}
}
}
ExprKind::UnaryOp { operand, .. } => {
self.check_expr(operand, defined_identifiers)?;
@ -141,7 +170,7 @@ impl<'a> Inferencer<'a> {
}
ExprKind::Lambda { args, body } => {
let mut defined_identifiers = defined_identifiers.clone();
for arg in args.args.iter() {
for arg in &args.args {
// TODO: should we check the types here?
if !defined_identifiers.contains(&arg.node.arg) {
defined_identifiers.insert(arg.node.arg);
@ -179,24 +208,45 @@ impl<'a> Inferencer<'a> {
Ok(())
}
/// Check that the return value is a non-`alloca` type, effectively only allowing primitive types.
///
/// This is a workaround preventing the caller from using a variable `alloca`-ed in the body, which
/// is freed when the function returns.
fn check_return_value_ty(&mut self, ret_ty: Type) -> bool {
match &*self.unifier.get_ty_immutable(ret_ty) {
TypeEnum::TObj { .. } => {
[
self.primitives.int32,
self.primitives.int64,
self.primitives.uint32,
self.primitives.uint64,
self.primitives.float,
self.primitives.bool,
].iter().any(|allowed_ty| self.unifier.unioned(ret_ty, *allowed_ty))
}
TypeEnum::TTuple { ty } => ty.iter().all(|t| self.check_return_value_ty(*t)),
_ => false,
}
}
// check statements for proper identifier def-use and return on all paths
fn check_stmt(
&mut self,
stmt: &Stmt<Option<Type>>,
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<bool, String> {
) -> Result<bool, HashSet<String>> {
match &stmt.node {
StmtKind::For { target, iter, body, orelse, .. } => {
self.check_expr(iter, defined_identifiers)?;
self.should_have_value(iter)?;
let mut local_defined_identifiers = defined_identifiers.clone();
for stmt in orelse.iter() {
for stmt in orelse {
self.check_stmt(stmt, &mut local_defined_identifiers)?;
}
let mut local_defined_identifiers = defined_identifiers.clone();
self.check_pattern(target, &mut local_defined_identifiers)?;
self.should_have_value(target)?;
for stmt in body.iter() {
for stmt in body {
self.check_stmt(stmt, &mut local_defined_identifiers)?;
}
Ok(false)
@ -209,7 +259,7 @@ impl<'a> Inferencer<'a> {
let body_returned = self.check_block(body, &mut body_identifiers)?;
let orelse_returned = self.check_block(orelse, &mut orelse_identifiers)?;
for ident in body_identifiers.iter() {
for ident in &body_identifiers {
if !defined_identifiers.contains(ident) && orelse_identifiers.contains(ident) {
defined_identifiers.insert(*ident);
}
@ -226,7 +276,7 @@ impl<'a> Inferencer<'a> {
}
StmtKind::With { items, body, .. } => {
let mut new_defined_identifiers = defined_identifiers.clone();
for item in items.iter() {
for item in items {
self.check_expr(&item.context_expr, defined_identifiers)?;
if let Some(var) = item.optional_vars.as_ref() {
self.check_pattern(var, &mut new_defined_identifiers)?;
@ -238,7 +288,7 @@ impl<'a> Inferencer<'a> {
StmtKind::Try { body, handlers, orelse, finalbody, .. } => {
self.check_block(body, &mut defined_identifiers.clone())?;
self.check_block(orelse, &mut defined_identifiers.clone())?;
for handler in handlers.iter() {
for handler in handlers {
let mut defined_identifiers = defined_identifiers.clone();
let ast::ExcepthandlerKind::ExceptHandler { name, body, .. } = &handler.node;
if let Some(name) = name {
@ -273,6 +323,27 @@ impl<'a> Inferencer<'a> {
if let Some(value) = value {
self.check_expr(value, defined_identifiers)?;
self.should_have_value(value)?;
// Check that the return value is a non-`alloca` type, effectively only allowing primitive types.
// This is a workaround preventing the caller from using a variable `alloca`-ed in the body, which
// is freed when the function returns.
if let Some(ret_ty) = value.custom {
// Explicitly allow ellipsis as a return value, as the type of the ellipsis is contextually
// inferred and just generates an unconditional assertion
if matches!(value.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) {
return Ok(true)
}
if !self.check_return_value_ty(ret_ty) {
return Err(HashSet::from([
format!(
"return value of type {} must be a primitive or a tuple of primitives at {}",
self.unifier.stringify(ret_ty),
value.location,
),
]))
}
}
}
Ok(true)
}
@ -291,11 +362,11 @@ impl<'a> Inferencer<'a> {
&mut self,
block: &[Stmt<Option<Type>>],
defined_identifiers: &mut HashSet<StrRef>,
) -> Result<bool, String> {
) -> Result<bool, HashSet<String>> {
let mut ret = false;
for stmt in block {
if ret {
return Err(format!("dead code at {:?}", stmt.location));
eprintln!("warning: dead code at {}\n", stmt.location);
}
if self.check_stmt(stmt, defined_identifiers)? {
ret = true;

View File

@ -1,12 +1,18 @@
use std::cmp::max;
use crate::symbol_resolver::SymbolValue;
use crate::toplevel::helper::PRIMITIVE_DEF_IDS;
use crate::toplevel::numpy::{make_ndarray_ty, unpack_ndarray_var_tys};
use crate::typecheck::{
type_inferencer::*,
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier},
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap},
};
use nac3parser::ast::{self, StrRef};
use nac3parser::ast::StrRef;
use nac3parser::ast::{Cmpop, Operator, Unaryop};
use std::collections::HashMap;
use std::rc::Rc;
use itertools::Itertools;
#[must_use]
pub fn binop_name(op: &Operator) -> &'static str {
match op {
Operator::Add => "__add__",
@ -25,6 +31,7 @@ pub fn binop_name(op: &Operator) -> &'static str {
}
}
#[must_use]
pub fn binop_assign_name(op: &Operator) -> &'static str {
match op {
Operator::Add => "__iadd__",
@ -43,6 +50,7 @@ pub fn binop_assign_name(op: &Operator) -> &'static str {
}
}
#[must_use]
pub fn unaryop_name(op: &Unaryop) -> &'static str {
match op {
Unaryop::UAdd => "__pos__",
@ -52,6 +60,7 @@ pub fn unaryop_name(op: &Unaryop) -> &'static str {
}
}
#[must_use]
pub fn comparison_name(op: &Cmpop) -> Option<&'static str> {
match op {
Cmpop::Lt => Some("__lt__"),
@ -83,11 +92,11 @@ where
pub fn impl_binop(
unifier: &mut Unifier,
store: &PrimitiveStore,
_store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Type,
ops: &[ast::Operator],
ret_ty: Option<Type>,
ops: &[Operator],
) {
with_fields(unifier, ty, |unifier, fields| {
let (other_ty, other_var_id) = if other_ty.len() == 1 {
@ -96,11 +105,15 @@ pub fn impl_binop(
let (ty, var_id) = unifier.get_fresh_var_with_range(other_ty, Some("N".into()), None);
(ty, Some(var_id))
};
let function_vars = if let Some(var_id) = other_var_id {
vec![(var_id, other_ty)].into_iter().collect::<HashMap<_, _>>()
vec![(var_id, other_ty)].into_iter().collect::<VarMap>()
} else {
HashMap::new()
VarMap::new()
};
let ret_ty = ret_ty.unwrap_or_else(|| unifier.get_fresh_var(None, None).0);
for op in ops {
fields.insert(binop_name(op).into(), {
(
@ -120,7 +133,7 @@ pub fn impl_binop(
fields.insert(binop_assign_name(op).into(), {
(
unifier.add_ty(TypeEnum::TFunc(FunSignature {
ret: store.none,
ret: ret_ty,
vars: function_vars.clone(),
args: vec![FuncArg {
ty: other_ty,
@ -135,15 +148,17 @@ pub fn impl_binop(
});
}
pub fn impl_unaryop(unifier: &mut Unifier, ty: Type, ret_ty: Type, ops: &[ast::Unaryop]) {
pub fn impl_unaryop(unifier: &mut Unifier, ty: Type, ret_ty: Option<Type>, ops: &[Unaryop]) {
with_fields(unifier, ty, |unifier, fields| {
let ret_ty = ret_ty.unwrap_or_else(|| unifier.get_fresh_var(None, None).0);
for op in ops {
fields.insert(
unaryop_name(op).into(),
(
unifier.add_ty(TypeEnum::TFunc(FunSignature {
ret: ret_ty,
vars: HashMap::new(),
vars: VarMap::new(),
args: vec![],
})),
false,
@ -155,19 +170,35 @@ pub fn impl_unaryop(unifier: &mut Unifier, ty: Type, ret_ty: Type, ops: &[ast::U
pub fn impl_cmpop(
unifier: &mut Unifier,
store: &PrimitiveStore,
_store: &PrimitiveStore,
ty: Type,
other_ty: Type,
ops: &[ast::Cmpop],
other_ty: &[Type],
ops: &[Cmpop],
ret_ty: Option<Type>,
) {
with_fields(unifier, ty, |unifier, fields| {
let (other_ty, other_var_id) = if other_ty.len() == 1 {
(other_ty[0], None)
} else {
let (ty, var_id) = unifier.get_fresh_var_with_range(other_ty, Some("N".into()), None);
(ty, Some(var_id))
};
let function_vars = if let Some(var_id) = other_var_id {
vec![(var_id, other_ty)].into_iter().collect::<VarMap>()
} else {
VarMap::new()
};
let ret_ty = ret_ty.unwrap_or_else(|| unifier.get_fresh_var(None, None).0);
for op in ops {
fields.insert(
comparison_name(op).unwrap().into(),
(
unifier.add_ty(TypeEnum::TFunc(FunSignature {
ret: store.bool,
vars: HashMap::new(),
ret: ret_ty,
vars: function_vars.clone(),
args: vec![FuncArg {
ty: other_ty,
default_value: None,
@ -181,13 +212,13 @@ pub fn impl_cmpop(
});
}
/// Add, Sub, Mult
/// `Add`, `Sub`, `Mult`
pub fn impl_basic_arithmetic(
unifier: &mut Unifier,
store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Type,
ret_ty: Option<Type>,
) {
impl_binop(
unifier,
@ -195,94 +226,368 @@ pub fn impl_basic_arithmetic(
ty,
other_ty,
ret_ty,
&[ast::Operator::Add, ast::Operator::Sub, ast::Operator::Mult],
)
&[Operator::Add, Operator::Sub, Operator::Mult],
);
}
/// Pow
/// `Pow`
pub fn impl_pow(
unifier: &mut Unifier,
store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Type,
ret_ty: Option<Type>,
) {
impl_binop(unifier, store, ty, other_ty, ret_ty, &[ast::Operator::Pow])
impl_binop(unifier, store, ty, other_ty, ret_ty, &[Operator::Pow]);
}
/// BitOr, BitXor, BitAnd
/// `BitOr`, `BitXor`, `BitAnd`
pub fn impl_bitwise_arithmetic(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
impl_binop(
unifier,
store,
ty,
&[ty],
ty,
&[ast::Operator::BitAnd, ast::Operator::BitOr, ast::Operator::BitXor],
)
Some(ty),
&[Operator::BitAnd, Operator::BitOr, Operator::BitXor],
);
}
/// LShift, RShift
/// `LShift`, `RShift`
pub fn impl_bitwise_shift(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
impl_binop(unifier, store, ty, &[ty], ty, &[ast::Operator::LShift, ast::Operator::RShift])
impl_binop(unifier, store, ty, &[store.int32, store.uint32], Some(ty), &[Operator::LShift, Operator::RShift]);
}
/// Div
pub fn impl_div(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type, other_ty: &[Type]) {
impl_binop(unifier, store, ty, other_ty, store.float, &[ast::Operator::Div])
/// `Div`
pub fn impl_div(
unifier: &mut Unifier,
store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Option<Type>,
) {
impl_binop(unifier, store, ty, other_ty, ret_ty, &[Operator::Div]);
}
/// FloorDiv
/// `FloorDiv`
pub fn impl_floordiv(
unifier: &mut Unifier,
store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Type,
ret_ty: Option<Type>,
) {
impl_binop(unifier, store, ty, other_ty, ret_ty, &[ast::Operator::FloorDiv])
impl_binop(unifier, store, ty, other_ty, ret_ty, &[Operator::FloorDiv]);
}
/// Mod
/// `Mod`
pub fn impl_mod(
unifier: &mut Unifier,
store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Type,
ret_ty: Option<Type>,
) {
impl_binop(unifier, store, ty, other_ty, ret_ty, &[ast::Operator::Mod])
impl_binop(unifier, store, ty, other_ty, ret_ty, &[Operator::Mod]);
}
/// UAdd, USub
pub fn impl_sign(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type) {
impl_unaryop(unifier, ty, ty, &[ast::Unaryop::UAdd, ast::Unaryop::USub])
/// [Operator::MatMult]
pub fn impl_matmul(
unifier: &mut Unifier,
store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Option<Type>,
) {
impl_binop(unifier, store, ty, other_ty, ret_ty, &[Operator::MatMult])
}
/// Invert
pub fn impl_invert(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type) {
impl_unaryop(unifier, ty, ty, &[ast::Unaryop::Invert])
/// `UAdd`, `USub`
pub fn impl_sign(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type, ret_ty: Option<Type>) {
impl_unaryop(unifier, ty, ret_ty, &[Unaryop::UAdd, Unaryop::USub]);
}
/// Not
pub fn impl_not(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
impl_unaryop(unifier, ty, store.bool, &[ast::Unaryop::Not])
/// `Invert`
pub fn impl_invert(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type, ret_ty: Option<Type>) {
impl_unaryop(unifier, ty, ret_ty, &[Unaryop::Invert]);
}
/// Lt, LtE, Gt, GtE
pub fn impl_comparison(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type, other_ty: Type) {
/// `Not`
pub fn impl_not(unifier: &mut Unifier, _store: &PrimitiveStore, ty: Type, ret_ty: Option<Type>) {
impl_unaryop(unifier, ty, ret_ty, &[Unaryop::Not]);
}
/// `Lt`, `LtE`, `Gt`, `GtE`
pub fn impl_comparison(
unifier: &mut Unifier,
store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Option<Type>,
) {
impl_cmpop(
unifier,
store,
ty,
other_ty,
&[ast::Cmpop::Lt, ast::Cmpop::Gt, ast::Cmpop::LtE, ast::Cmpop::GtE],
)
&[Cmpop::Lt, Cmpop::Gt, Cmpop::LtE, Cmpop::GtE],
ret_ty,
);
}
/// Eq, NotEq
pub fn impl_eq(unifier: &mut Unifier, store: &PrimitiveStore, ty: Type) {
impl_cmpop(unifier, store, ty, ty, &[ast::Cmpop::Eq, ast::Cmpop::NotEq])
/// `Eq`, `NotEq`
pub fn impl_eq(
unifier: &mut Unifier,
store: &PrimitiveStore,
ty: Type,
other_ty: &[Type],
ret_ty: Option<Type>,
) {
impl_cmpop(unifier, store, ty, other_ty, &[Cmpop::Eq, Cmpop::NotEq], ret_ty);
}
/// Returns the expected return type of binary operations with at least one `ndarray` operand.
pub fn typeof_ndarray_broadcast(
unifier: &mut Unifier,
primitives: &PrimitiveStore,
left: Type,
right: Type,
) -> Result<Type, String> {
let is_left_ndarray = left.obj_id(unifier).is_some_and(|id| id == PRIMITIVE_DEF_IDS.ndarray);
let is_right_ndarray = right.obj_id(unifier).is_some_and(|id| id == PRIMITIVE_DEF_IDS.ndarray);
assert!(is_left_ndarray || is_right_ndarray);
if is_left_ndarray && is_right_ndarray {
// Perform broadcasting on two ndarray operands.
let (left_ty_dtype, left_ty_ndims) = unpack_ndarray_var_tys(unifier, left);
let (right_ty_dtype, right_ty_ndims) = unpack_ndarray_var_tys(unifier, right);
assert!(unifier.unioned(left_ty_dtype, right_ty_dtype));
let left_ty_ndims = match &*unifier.get_ty_immutable(left_ty_ndims) {
TypeEnum::TLiteral { values, .. } => values.clone(),
_ => unreachable!(),
};
let right_ty_ndims = match &*unifier.get_ty_immutable(right_ty_ndims) {
TypeEnum::TLiteral { values, .. } => values.clone(),
_ => unreachable!(),
};
let res_ndims = left_ty_ndims.into_iter()
.cartesian_product(right_ty_ndims)
.map(|(left, right)| {
let left_val = u64::try_from(left).unwrap();
let right_val = u64::try_from(right).unwrap();
max(left_val, right_val)
})
.unique()
.map(SymbolValue::U64)
.collect_vec();
let res_ndims = unifier.get_fresh_literal(res_ndims, None);
Ok(make_ndarray_ty(unifier, primitives, Some(left_ty_dtype), Some(res_ndims)))
} else {
let (ndarray_ty, scalar_ty) = if is_left_ndarray {
(left, right)
} else {
(right, left)
};
let (ndarray_ty_dtype, _) = unpack_ndarray_var_tys(unifier, ndarray_ty);
if unifier.unioned(ndarray_ty_dtype, scalar_ty) {
Ok(ndarray_ty)
} else {
let (expected_ty, actual_ty) = if is_left_ndarray {
(ndarray_ty_dtype, scalar_ty)
} else {
(scalar_ty, ndarray_ty_dtype)
};
Err(format!(
"Expected right-hand side operand to be {}, got {}",
unifier.stringify(expected_ty),
unifier.stringify(actual_ty),
))
}
}
}
/// Returns the return type given a binary operator and its primitive operands.
pub fn typeof_binop(
unifier: &mut Unifier,
primitives: &PrimitiveStore,
op: &Operator,
lhs: Type,
rhs: Type,
) -> Result<Option<Type>, String> {
let is_left_ndarray = lhs.obj_id(unifier).is_some_and(|id| id == PRIMITIVE_DEF_IDS.ndarray);
let is_right_ndarray = rhs.obj_id(unifier).is_some_and(|id| id == PRIMITIVE_DEF_IDS.ndarray);
Ok(Some(match op {
Operator::Add
| Operator::Sub
| Operator::Mult
| Operator::Mod
| Operator::FloorDiv => {
if is_left_ndarray || is_right_ndarray {
typeof_ndarray_broadcast(unifier, primitives, lhs, rhs)?
} else if unifier.unioned(lhs, rhs) {
lhs
} else {
return Ok(None)
}
}
Operator::MatMult => {
let (_, lhs_ndims) = unpack_ndarray_var_tys(unifier, lhs);
let lhs_ndims = match &*unifier.get_ty_immutable(lhs_ndims) {
TypeEnum::TLiteral { values, .. } => {
assert_eq!(values.len(), 1);
u64::try_from(values[0].clone()).unwrap()
}
_ => unreachable!(),
};
let (_, rhs_ndims) = unpack_ndarray_var_tys(unifier, rhs);
let rhs_ndims = match &*unifier.get_ty_immutable(rhs_ndims) {
TypeEnum::TLiteral { values, .. } => {
assert_eq!(values.len(), 1);
u64::try_from(values[0].clone()).unwrap()
}
_ => unreachable!(),
};
match (lhs_ndims, rhs_ndims) {
(2, 2) => typeof_ndarray_broadcast(unifier, primitives, lhs, rhs)?,
(lhs, rhs) if lhs == 0 || rhs == 0 => {
return Err(format!(
"Input operand {} does not have enough dimensions (has {lhs}, requires {rhs})",
(rhs == 0) as u8
))
}
(lhs, rhs) => {
return Err(format!("ndarray.__matmul__ on {lhs}D and {rhs}D operands not supported"))
}
}
}
Operator::Div => {
if is_left_ndarray || is_right_ndarray {
typeof_ndarray_broadcast(unifier, primitives, lhs, rhs)?
} else if unifier.unioned(lhs, rhs) {
primitives.float
} else {
return Ok(None)
}
}
Operator::Pow => {
if is_left_ndarray || is_right_ndarray {
typeof_ndarray_broadcast(unifier, primitives, lhs, rhs)?
} else if [primitives.int32, primitives.int64, primitives.uint32, primitives.uint64, primitives.float].into_iter().any(|ty| unifier.unioned(lhs, ty)) {
lhs
} else {
return Ok(None)
}
}
Operator::LShift
| Operator::RShift => lhs,
Operator::BitOr
| Operator::BitXor
| Operator::BitAnd => {
if unifier.unioned(lhs, rhs) {
lhs
} else {
return Ok(None)
}
}
}))
}
pub fn typeof_unaryop(
unifier: &mut Unifier,
primitives: &PrimitiveStore,
op: &Unaryop,
operand: Type,
) -> Result<Option<Type>, String> {
let operand_obj_id = operand.obj_id(unifier);
if *op == Unaryop::Not && operand_obj_id.is_some_and(|id| id == primitives.ndarray.obj_id(unifier).unwrap()) {
return Err("The truth value of an array with more than one element is ambiguous".to_string())
}
Ok(match *op {
Unaryop::Not => {
match operand_obj_id {
Some(v) if v == PRIMITIVE_DEF_IDS.ndarray => Some(operand),
Some(_) => Some(primitives.bool),
_ => None
}
}
Unaryop::Invert => {
if operand_obj_id.is_some_and(|id| id == PRIMITIVE_DEF_IDS.bool) {
Some(primitives.int32)
} else if operand_obj_id.is_some_and(|id| PRIMITIVE_DEF_IDS.iter().any(|prim_id| id == prim_id)) {
Some(operand)
} else {
None
}
}
Unaryop::UAdd
| Unaryop::USub => {
if operand_obj_id.is_some_and(|id| id == PRIMITIVE_DEF_IDS.ndarray) {
let (dtype, _) = unpack_ndarray_var_tys(unifier, operand);
if dtype.obj_id(unifier).is_some_and(|id| id == PRIMITIVE_DEF_IDS.bool) {
return Err(if *op == Unaryop::UAdd {
"The ufunc 'positive' cannot be applied to ndarray[bool, N]".to_string()
} else {
"The numpy boolean negative, the `-` operator, is not supported, use the `~` operator function instead.".to_string()
})
}
Some(operand)
} else if operand_obj_id.is_some_and(|id| id == PRIMITIVE_DEF_IDS.bool) {
Some(primitives.int32)
} else if operand_obj_id.is_some_and(|id| PRIMITIVE_DEF_IDS.iter().any(|prim_id| id == prim_id)) {
Some(operand)
} else {
None
}
}
})
}
/// Returns the return type given a comparison operator and its primitive operands.
pub fn typeof_cmpop(
unifier: &mut Unifier,
primitives: &PrimitiveStore,
_op: &Cmpop,
lhs: Type,
rhs: Type,
) -> Result<Option<Type>, String> {
let is_left_ndarray = lhs
.obj_id(unifier)
.is_some_and(|id| id == PRIMITIVE_DEF_IDS.ndarray);
let is_right_ndarray = rhs
.obj_id(unifier)
.is_some_and(|id| id == PRIMITIVE_DEF_IDS.ndarray);
Ok(Some(if is_left_ndarray || is_right_ndarray {
let brd = typeof_ndarray_broadcast(unifier, primitives, lhs, rhs)?;
let (_, ndims) = unpack_ndarray_var_tys(unifier, brd);
make_ndarray_ty(unifier, primitives, Some(primitives.bool), Some(ndims))
} else if unifier.unioned(lhs, rhs) {
primitives.bool
} else {
return Ok(None)
}))
}
pub fn set_primitives_magic_methods(store: &PrimitiveStore, unifier: &mut Unifier) {
@ -293,38 +598,63 @@ pub fn set_primitives_magic_methods(store: &PrimitiveStore, unifier: &mut Unifie
bool: bool_t,
uint32: uint32_t,
uint64: uint64_t,
ndarray: ndarray_t,
..
} = *store;
let size_t = store.usize();
/* int ======== */
for t in [int32_t, int64_t, uint32_t, uint64_t] {
impl_basic_arithmetic(unifier, store, t, &[t], t);
impl_pow(unifier, store, t, &[t], t);
let ndarray_int_t = make_ndarray_ty(unifier, store, Some(t), None);
impl_basic_arithmetic(unifier, store, t, &[t, ndarray_int_t], None);
impl_pow(unifier, store, t, &[t, ndarray_int_t], None);
impl_bitwise_arithmetic(unifier, store, t);
impl_bitwise_shift(unifier, store, t);
impl_div(unifier, store, t, &[t]);
impl_floordiv(unifier, store, t, &[t], t);
impl_mod(unifier, store, t, &[t], t);
impl_invert(unifier, store, t);
impl_not(unifier, store, t);
impl_comparison(unifier, store, t, t);
impl_eq(unifier, store, t);
impl_div(unifier, store, t, &[t, ndarray_int_t], None);
impl_floordiv(unifier, store, t, &[t, ndarray_int_t], None);
impl_mod(unifier, store, t, &[t, ndarray_int_t], None);
impl_invert(unifier, store, t, Some(t));
impl_not(unifier, store, t, Some(bool_t));
impl_comparison(unifier, store, t, &[t, ndarray_int_t], None);
impl_eq(unifier, store, t, &[t, ndarray_int_t], None);
}
for t in [int32_t, int64_t] {
impl_sign(unifier, store, t);
impl_sign(unifier, store, t, Some(t));
}
/* float ======== */
impl_basic_arithmetic(unifier, store, float_t, &[float_t], float_t);
impl_pow(unifier, store, float_t, &[int32_t, float_t], float_t);
impl_div(unifier, store, float_t, &[float_t]);
impl_floordiv(unifier, store, float_t, &[float_t], float_t);
impl_mod(unifier, store, float_t, &[float_t], float_t);
impl_sign(unifier, store, float_t);
impl_not(unifier, store, float_t);
impl_comparison(unifier, store, float_t, float_t);
impl_eq(unifier, store, float_t);
let ndarray_float_t = make_ndarray_ty(unifier, store, Some(float_t), None);
let ndarray_int32_t = make_ndarray_ty(unifier, store, Some(int32_t), None);
impl_basic_arithmetic(unifier, store, float_t, &[float_t, ndarray_float_t], None);
impl_pow(unifier, store, float_t, &[int32_t, float_t, ndarray_int32_t, ndarray_float_t], None);
impl_div(unifier, store, float_t, &[float_t, ndarray_float_t], None);
impl_floordiv(unifier, store, float_t, &[float_t, ndarray_float_t], None);
impl_mod(unifier, store, float_t, &[float_t, ndarray_float_t], None);
impl_sign(unifier, store, float_t, Some(float_t));
impl_not(unifier, store, float_t, Some(bool_t));
impl_comparison(unifier, store, float_t, &[float_t, ndarray_float_t], None);
impl_eq(unifier, store, float_t, &[float_t, ndarray_float_t], None);
/* bool ======== */
impl_not(unifier, store, bool_t);
impl_eq(unifier, store, bool_t);
let ndarray_bool_t = make_ndarray_ty(unifier, store, Some(bool_t), None);
impl_invert(unifier, store, bool_t, Some(int32_t));
impl_not(unifier, store, bool_t, Some(bool_t));
impl_sign(unifier, store, bool_t, Some(int32_t));
impl_eq(unifier, store, bool_t, &[bool_t, ndarray_bool_t], None);
/* ndarray ===== */
let ndarray_usized_ndims_tvar = unifier.get_fresh_const_generic_var(size_t, Some("ndarray_ndims".into()), None);
let ndarray_unsized_t = make_ndarray_ty(unifier, store, None, Some(ndarray_usized_ndims_tvar.0));
let (ndarray_dtype_t, _) = unpack_ndarray_var_tys(unifier, ndarray_t);
let (ndarray_unsized_dtype_t, _) = unpack_ndarray_var_tys(unifier, ndarray_unsized_t);
impl_basic_arithmetic(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
impl_pow(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
impl_div(unifier, store, ndarray_t, &[ndarray_t, ndarray_dtype_t], None);
impl_floordiv(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
impl_mod(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
impl_matmul(unifier, store, ndarray_t, &[ndarray_t], Some(ndarray_t));
impl_sign(unifier, store, ndarray_t, Some(ndarray_t));
impl_invert(unifier, store, ndarray_t, Some(ndarray_t));
impl_eq(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
impl_comparison(unifier, store, ndarray_t, &[ndarray_unsized_t, ndarray_unsized_dtype_t], None);
}

View File

@ -43,15 +43,18 @@ pub struct TypeError {
}
impl TypeError {
#[must_use]
pub fn new(kind: TypeErrorKind, loc: Option<Location>) -> TypeError {
TypeError { kind, loc }
}
#[must_use]
pub fn at(mut self, loc: Option<Location>) -> TypeError {
self.loc = self.loc.or(loc);
self
}
#[must_use]
pub fn to_display(self, unifier: &Unifier) -> DisplayTypeError {
DisplayTypeError { err: self, unifier }
}
@ -64,8 +67,8 @@ pub struct DisplayTypeError<'a> {
fn loc_to_str(loc: Option<Location>) -> String {
match loc {
Some(loc) => format!("(in {})", loc),
None => "".to_string(),
Some(loc) => format!("(in {loc})"),
None => String::new(),
}
}
@ -75,21 +78,20 @@ impl<'a> Display for DisplayTypeError<'a> {
let mut notes = Some(HashMap::new());
match &self.err.kind {
TooManyArguments { expected, got } => {
write!(f, "Too many arguments. Expected {} but got {}", expected, got)
write!(f, "Too many arguments. Expected {expected} but got {got}")
}
MissingArgs(args) => {
write!(f, "Missing arguments: {}", args)
write!(f, "Missing arguments: {args}")
}
UnknownArgName(name) => {
write!(f, "Unknown argument name: {}", name)
write!(f, "Unknown argument name: {name}")
}
IncorrectArgType { name, expected, got } => {
let expected = self.unifier.stringify_with_notes(*expected, &mut notes);
let got = self.unifier.stringify_with_notes(*got, &mut notes);
write!(
f,
"Incorrect argument type for {}. Expected {}, but got {}",
name, expected, got
"Incorrect argument type for {name}. Expected {expected}, but got {got}"
)
}
FieldUnificationError { field, types, loc } => {
@ -126,7 +128,7 @@ impl<'a> Display for DisplayTypeError<'a> {
);
if let Some(loc) = loc {
result?;
write!(f, " (in {})", loc)?;
write!(f, " (in {loc})")?;
return Ok(());
}
result
@ -136,12 +138,12 @@ impl<'a> Display for DisplayTypeError<'a> {
{
let t1 = self.unifier.stringify_with_notes(*t1, &mut notes);
let t2 = self.unifier.stringify_with_notes(*t2, &mut notes);
write!(f, "Tuple length mismatch: got {} and {}", t1, t2)
write!(f, "Tuple length mismatch: got {t1} and {t2}")
}
_ => {
let t1 = self.unifier.stringify_with_notes(*t1, &mut notes);
let t2 = self.unifier.stringify_with_notes(*t2, &mut notes);
write!(f, "Incompatible types: {} and {}", t1, t2)
write!(f, "Incompatible types: {t1} and {t2}")
}
}
}
@ -150,18 +152,17 @@ impl<'a> Display for DisplayTypeError<'a> {
write!(f, "Cannot assign to an element of a tuple")
} else {
let t = self.unifier.stringify_with_notes(*t, &mut notes);
write!(f, "Cannot assign to field {} of {}, which is immutable", name, t)
write!(f, "Cannot assign to field {name} of {t}, which is immutable")
}
}
NoSuchField(name, t) => {
let t = self.unifier.stringify_with_notes(*t, &mut notes);
write!(f, "`{}::{}` field/method does not exist", t, name)
write!(f, "`{t}::{name}` field/method does not exist")
}
TupleIndexOutOfBounds { index, len } => {
write!(
f,
"Tuple index out of bounds. Got {} but tuple has only {} elements",
index, len
"Tuple index out of bounds. Got {index} but tuple has only {len} elements"
)
}
RequiresTypeAnn => {
@ -172,13 +173,13 @@ impl<'a> Display for DisplayTypeError<'a> {
}
}?;
if let Some(loc) = self.err.loc {
write!(f, " at {}", loc)?;
write!(f, " at {loc}")?;
}
let notes = notes.unwrap();
if !notes.is_empty() {
write!(f, "\n\nNotes:")?;
for line in notes.values() {
write!(f, "\n {}", line)?;
write!(f, "\n {line}")?;
}
}
Ok(())

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,10 @@ use super::*;
use crate::{
codegen::CodeGenContext,
symbol_resolver::ValueEnum,
toplevel::{DefinitionId, TopLevelDef},
toplevel::{DefinitionId, helper::PRIMITIVE_DEF_IDS, TopLevelDef},
};
use indoc::indoc;
use itertools::zip;
use std::iter::zip;
use nac3parser::parser::parse_program;
use parking_lot::RwLock;
use test_case::test_case;
@ -20,7 +20,7 @@ struct Resolver {
impl SymbolResolver for Resolver {
fn get_default_param_value(
&self,
_: &nac3parser::ast::Expr,
_: &ast::Expr,
) -> Option<crate::symbol_resolver::SymbolValue> {
unimplemented!()
}
@ -43,8 +43,9 @@ impl SymbolResolver for Resolver {
unimplemented!()
}
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.id_to_def.get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
self.id_to_def.get(&id).cloned()
.ok_or_else(|| HashSet::from(["Unknown identifier".to_string()]))
}
fn get_string_id(&self, _: &str) -> i32 {
@ -62,7 +63,7 @@ struct TestEnvironment {
pub primitives: PrimitiveStore,
pub id_to_name: HashMap<usize, StrRef>,
pub identifier_mapping: HashMap<StrRef, Type>,
pub virtual_checks: Vec<(Type, Type, nac3parser::ast::Location)>,
pub virtual_checks: Vec<(Type, Type, Location)>,
pub calls: HashMap<CodeLocation, CallId>,
pub top_level: TopLevelContext,
}
@ -72,67 +73,77 @@ impl TestEnvironment {
let mut unifier = Unifier::new();
let int32 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(0),
obj_id: PRIMITIVE_DEF_IDS.int32,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
with_fields(&mut unifier, int32, |unifier, fields| {
let add_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "other".into(), ty: int32, default_value: None }],
ret: int32,
vars: HashMap::new(),
vars: VarMap::new(),
}));
fields.insert("__add__".into(), (add_ty, false));
});
let int64 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(1),
obj_id: PRIMITIVE_DEF_IDS.int64,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let float = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(2),
obj_id: PRIMITIVE_DEF_IDS.float,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let bool = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(3),
obj_id: PRIMITIVE_DEF_IDS.bool,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let none = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(4),
obj_id: PRIMITIVE_DEF_IDS.none,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let range = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(5),
obj_id: PRIMITIVE_DEF_IDS.range,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let str = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(6),
obj_id: PRIMITIVE_DEF_IDS.str,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let exception = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(7),
obj_id: PRIMITIVE_DEF_IDS.exception,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let uint32 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(8),
obj_id: PRIMITIVE_DEF_IDS.uint32,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let uint64 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(9),
obj_id: PRIMITIVE_DEF_IDS.uint64,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
obj_id: PRIMITIVE_DEF_IDS.option,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let ndarray_dtype_tvar = unifier.get_fresh_var(Some("ndarray_dtype".into()), None);
let ndarray_ndims_tvar = unifier.get_fresh_const_generic_var(uint64, Some("ndarray_ndims".into()), None);
let ndarray = unifier.add_ty(TypeEnum::TObj {
obj_id: PRIMITIVE_DEF_IDS.ndarray,
fields: HashMap::new(),
params: VarMap::from([
(ndarray_dtype_tvar.1, ndarray_dtype_tvar.0),
(ndarray_ndims_tvar.1, ndarray_ndims_tvar.0),
]),
});
let primitives = PrimitiveStore {
int32,
@ -146,7 +157,10 @@ impl TestEnvironment {
uint32,
uint64,
option,
ndarray,
size_t: 64,
};
unifier.put_primitive_store(&primitives);
set_primitives_magic_methods(&primitives, &mut unifier);
let id_to_name = [
@ -197,67 +211,72 @@ impl TestEnvironment {
let mut identifier_mapping = HashMap::new();
let mut top_level_defs: Vec<Arc<RwLock<TopLevelDef>>> = Vec::new();
let int32 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(0),
obj_id: PRIMITIVE_DEF_IDS.int32,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
with_fields(&mut unifier, int32, |unifier, fields| {
let add_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "other".into(), ty: int32, default_value: None }],
ret: int32,
vars: HashMap::new(),
vars: VarMap::new(),
}));
fields.insert("__add__".into(), (add_ty, false));
});
let int64 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(1),
obj_id: PRIMITIVE_DEF_IDS.int64,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let float = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(2),
obj_id: PRIMITIVE_DEF_IDS.float,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let bool = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(3),
obj_id: PRIMITIVE_DEF_IDS.bool,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let none = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(4),
obj_id: PRIMITIVE_DEF_IDS.none,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let range = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(5),
obj_id: PRIMITIVE_DEF_IDS.range,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let str = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(6),
obj_id: PRIMITIVE_DEF_IDS.str,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let exception = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(7),
obj_id: PRIMITIVE_DEF_IDS.exception,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let uint32 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(8),
obj_id: PRIMITIVE_DEF_IDS.uint32,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let uint64 = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(9),
obj_id: PRIMITIVE_DEF_IDS.uint64,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
obj_id: PRIMITIVE_DEF_IDS.option,
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
});
let ndarray = unifier.add_ty(TypeEnum::TObj {
obj_id: PRIMITIVE_DEF_IDS.ndarray,
fields: HashMap::new(),
params: VarMap::new(),
});
identifier_mapping.insert("None".into(), none);
for (i, name) in ["int32", "int64", "float", "bool", "none", "range", "str", "Exception"]
@ -293,14 +312,18 @@ impl TestEnvironment {
uint32,
uint64,
option,
ndarray,
size_t: 64,
};
unifier.put_primitive_store(&primitives);
let (v0, id) = unifier.get_dummy_var();
let foo_ty = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(defs + 1),
fields: [("a".into(), (v0, true))].iter().cloned().collect::<HashMap<_, _>>(),
params: [(id, v0)].iter().cloned().collect::<HashMap<_, _>>(),
params: [(id, v0)].iter().cloned().collect::<VarMap>(),
});
top_level_defs.push(
RwLock::new(TopLevelDef::Class {

View File

@ -1,10 +1,12 @@
use itertools::{zip, Itertools};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Display;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::{borrow::Cow, collections::HashSet};
use std::iter::zip;
use indexmap::IndexMap;
use itertools::Itertools;
use nac3parser::ast::{Location, StrRef};
@ -12,6 +14,7 @@ use super::type_error::{TypeError, TypeErrorKind};
use super::unification_table::{UnificationKey, UnificationTable};
use crate::symbol_resolver::SymbolValue;
use crate::toplevel::{DefinitionId, TopLevelContext, TopLevelDef};
use crate::typecheck::type_inferencer::PrimitiveStore;
#[cfg(test)]
mod test;
@ -23,7 +26,10 @@ pub type Type = UnificationKey;
pub struct CallId(pub(super) usize);
pub type Mapping<K, V = Type> = HashMap<K, V>;
type VarMap = Mapping<u32>;
pub type IndexMapping<K, V = Type> = IndexMap<K, V>;
/// The mapping between type variable ID and [unifier type][`Type`].
pub type VarMap = IndexMapping<u32>;
#[derive(Clone)]
pub struct Call {
@ -55,13 +61,14 @@ pub enum RecordKey {
}
impl Type {
// a wrapper function for cleaner code so that we don't need to
// write this long pattern matching just to get the field `obj_id`
pub fn get_obj_id(self, unifier: &Unifier) -> DefinitionId {
if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty_immutable(self).as_ref() {
*obj_id
/// Wrapper function for cleaner code so that we don't need to write this long pattern matching
/// just to get the field `obj_id`.
#[must_use]
pub fn obj_id(self, unifier: &Unifier) -> Option<DefinitionId> {
if let TypeEnum::TObj { obj_id, .. } = &*unifier.get_ty_immutable(self) {
Some(*obj_id)
} else {
unreachable!("expect a object type")
None
}
}
}
@ -96,8 +103,8 @@ impl From<i32> for RecordKey {
impl Display for RecordKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RecordKey::Str(s) => write!(f, "{}", s),
RecordKey::Int(i) => write!(f, "{}", i),
RecordKey::Str(s) => write!(f, "{s}"),
RecordKey::Int(i) => write!(f, "{i}"),
}
}
}
@ -110,11 +117,13 @@ pub struct RecordField {
}
impl RecordField {
#[must_use]
pub fn new(ty: Type, mutable: bool, loc: Option<Location>) -> RecordField {
RecordField { ty, mutable, loc }
}
}
/// Category of variable and value types.
#[derive(Clone)]
pub enum TypeEnum {
TRigidVar {
@ -122,6 +131,8 @@ pub enum TypeEnum {
name: Option<StrRef>,
loc: Option<Location>,
},
/// A type variable.
TVar {
id: u32,
// empty indicates this is not a struct/tuple/list
@ -130,30 +141,61 @@ pub enum TypeEnum {
range: Vec<Type>,
name: Option<StrRef>,
loc: Option<Location>,
/// Whether this type variable refers to a const-generic variable.
is_const_generic: bool,
},
/// A literal generic type matching `typing.Literal`.
TLiteral {
/// The value of the constant.
values: Vec<SymbolValue>,
loc: Option<Location>,
},
/// A tuple type.
TTuple {
/// The types of elements present in this tuple.
ty: Vec<Type>,
},
/// A list type.
TList {
/// The type of elements present in this list.
ty: Type,
},
/// An object type.
TObj {
/// The [DefintionId] of this object type.
obj_id: DefinitionId,
/// The fields present in this object type.
///
/// The key of the [Mapping] is the identifier of the field, while the value is a tuple
/// containing the [Type] of the field, and a `bool` indicating whether the field is a
/// variable (as opposed to a function).
fields: Mapping<StrRef, (Type, bool)>,
/// Mapping between the ID of type variables and the [Type] representing the type variables
/// of this object type.
params: VarMap,
},
TVirtual {
ty: Type,
},
TCall(Vec<CallId>),
/// A function type.
TFunc(FunSignature),
}
impl TypeEnum {
#[must_use]
pub fn get_type_name(&self) -> &'static str {
match self {
TypeEnum::TRigidVar { .. } => "TRigidVar",
TypeEnum::TVar { .. } => "TVar",
TypeEnum::TLiteral { .. } => "TConstant",
TypeEnum::TTuple { .. } => "TTuple",
TypeEnum::TList { .. } => "TList",
TypeEnum::TObj { .. } => "TObj",
@ -173,7 +215,8 @@ pub struct Unifier {
pub(crate) calls: Vec<Rc<Call>>,
var_id: u32,
unify_cache: HashSet<(Type, Type)>,
snapshot: Option<(usize, u32)>
snapshot: Option<(usize, u32)>,
primitive_store: Option<PrimitiveStore>,
}
impl Default for Unifier {
@ -184,6 +227,7 @@ impl Default for Unifier {
impl Unifier {
/// Get an empty unifier
#[must_use]
pub fn new() -> Unifier {
Unifier {
unification_table: UnificationTable::new(),
@ -192,9 +236,27 @@ impl Unifier {
unify_cache: HashSet::new(),
top_level: None,
snapshot: None,
primitive_store: None,
}
}
/// Sets the [`PrimitiveStore`] instance within this `Unifier`.
///
/// This function can only be invoked once. Any subsequent invocations will result in an
/// assertion error.
pub fn put_primitive_store(&mut self, primitives: &PrimitiveStore) {
assert!(self.primitive_store.is_none());
self.primitive_store.replace(*primitives);
}
/// Returns the [`UnificationTable`] associated with this `Unifier`.
///
/// # Safety
///
/// The use of this function is discouraged under most circumstances. Only use this function if
/// in-place manipulation of type variables and/or type fields is necessary, otherwise prefer to
/// [add a new type][`Unifier::add_ty`] and [unify the type][`Unifier::unify`] with an existing
/// type.
pub unsafe fn get_unification_table(&mut self) -> &mut UnificationTable<Rc<TypeEnum>> {
&mut self.unification_table
}
@ -213,9 +275,11 @@ impl Unifier {
top_level: None,
unify_cache: HashSet::new(),
snapshot: None,
primitive_store: None,
}
}
#[must_use]
pub fn get_shared_unifier(&self) -> SharedUnifier {
Arc::new(Mutex::new((
self.unification_table.get_send(),
@ -225,7 +289,7 @@ impl Unifier {
}
/// Register a type to the unifier.
/// Returns a key in the unification_table.
/// Returns a key in the `unification_table`.
pub fn add_ty(&mut self, a: TypeEnum) -> Type {
self.unification_table.new_key(Rc::new(a))
}
@ -239,6 +303,7 @@ impl Unifier {
fields: Some(fields),
name: None,
loc: None,
is_const_generic: false,
})
}
@ -257,6 +322,7 @@ impl Unifier {
}
}
#[must_use]
pub fn get_call_signature_immutable(&self, id: CallId) -> Option<FunSignature> {
let fun = self.calls.get(id.0).unwrap().fun.borrow().unwrap();
if let TypeEnum::TFunc(sign) = &*self.get_ty_immutable(fun) {
@ -270,11 +336,12 @@ impl Unifier {
self.unification_table.get_representative(ty)
}
/// Get the TypeEnum of a type.
/// Get the `TypeEnum` of a type.
pub fn get_ty(&mut self, a: Type) -> Rc<TypeEnum> {
self.unification_table.probe_value(a).clone()
}
#[must_use]
pub fn get_ty_immutable(&self, a: Type) -> Rc<TypeEnum> {
self.unification_table.probe_value_immutable(a).clone()
}
@ -293,11 +360,16 @@ impl Unifier {
self.get_fresh_var_with_range(&[], None, None)
}
/// Returns a fresh [type variable][TypeEnum::TVar] with no associated range.
///
/// This type variable can be instantiated by any type.
pub fn get_fresh_var(&mut self, name: Option<StrRef>, loc: Option<Location>) -> (Type, u32) {
self.get_fresh_var_with_range(&[], name, loc)
}
/// Get a fresh type variable.
/// Returns a fresh [type variable][TypeEnum::TVar] with the range specified by `range`.
///
/// This type variable can be instantiated by any type present in `range`.
pub fn get_fresh_var_with_range(
&mut self,
range: &[Type],
@ -307,7 +379,32 @@ impl Unifier {
let id = self.var_id + 1;
self.var_id += 1;
let range = range.to_vec();
(self.add_ty(TypeEnum::TVar { id, range, fields: None, name, loc }), id)
(self.add_ty(TypeEnum::TVar { id, range, fields: None, name, loc, is_const_generic: false }), id)
}
/// Returns a fresh type representing a constant generic variable with the given underlying type `ty`.
pub fn get_fresh_const_generic_var(
&mut self,
ty: Type,
name: Option<StrRef>,
loc: Option<Location>,
) -> (Type, u32) {
let id = self.var_id + 1;
self.var_id += 1;
(self.add_ty(TypeEnum::TVar { id, range: vec![ty], fields: None, name, loc, is_const_generic: true }), id)
}
/// Returns a fresh type representing a [literal][TypeEnum::TConstant] with the given `values`.
pub fn get_fresh_literal(
&mut self,
values: Vec<SymbolValue>,
loc: Option<Location>,
) -> Type {
let ty_enum = TypeEnum::TLiteral {
values: values.into_iter().dedup().collect(),
loc
};
self.add_ty(ty_enum)
}
/// Unification would not unify rigid variables with other types, but we want to do this for
@ -326,8 +423,7 @@ impl Unifier {
Some(
range
.iter()
.map(|ty| self.get_instantiations(*ty).unwrap_or_else(|| vec![*ty]))
.flatten()
.flat_map(|ty| self.get_instantiations(*ty).unwrap_or_else(|| vec![*ty]))
.collect_vec(),
)
}
@ -368,7 +464,7 @@ impl Unifier {
.map(|params| {
self.subst(
ty,
&zip(keys.iter().cloned(), params.iter().cloned()).collect(),
&zip(keys.iter().copied(), params.iter().copied()).collect(),
)
.unwrap_or(ty)
})
@ -383,18 +479,21 @@ impl Unifier {
pub fn is_concrete(&mut self, a: Type, allowed_typevars: &[Type]) -> bool {
use TypeEnum::*;
match &*self.get_ty(a) {
TRigidVar { .. } => true,
TRigidVar { .. }
| TLiteral { .. }
// functions are instantiated for each call sites, so the function type can contain
// type variables.
| TFunc { .. } => true,
TVar { .. } => allowed_typevars.iter().any(|b| self.unification_table.unioned(a, *b)),
TCall { .. } => false,
TList { ty } => self.is_concrete(*ty, allowed_typevars),
TList { ty }
| TVirtual { ty } => self.is_concrete(*ty, allowed_typevars),
TTuple { ty } => ty.iter().all(|ty| self.is_concrete(*ty, allowed_typevars)),
TObj { params: vars, .. } => {
vars.values().all(|ty| self.is_concrete(*ty, allowed_typevars))
}
// functions are instantiated for each call sites, so the function type can contain
// type variables.
TFunc { .. } => true,
TVirtual { ty } => self.is_concrete(*ty, allowed_typevars),
}
}
@ -424,15 +523,12 @@ impl Unifier {
}
let Call { posargs, kwargs, ret, fun, loc } = call;
let instantiated = self.instantiate_fun(b, &*signature);
let instantiated = self.instantiate_fun(b, signature);
let r = self.get_ty(instantiated);
let r = r.as_ref();
let signature;
if let TypeEnum::TFunc(s) = &*r {
signature = s;
} else {
unreachable!();
}
let TypeEnum::TFunc(signature) = r else {
unreachable!()
};
// we check to make sure that all required arguments (those without default
// arguments) are provided, and do not provide the same argument twice.
let mut required = required.to_vec();
@ -455,7 +551,7 @@ impl Unifier {
TypeError::new(TypeErrorKind::IncorrectArgType { name, expected, got: *t }, *loc)
})?;
}
for (k, t) in kwargs.iter() {
for (k, t) in kwargs {
if let Some(i) = required.iter().position(|v| v == k) {
required.remove(i);
}
@ -531,8 +627,8 @@ impl Unifier {
};
match (&*ty_a, &*ty_b) {
(
TVar { fields: fields1, id, name: name1, loc: loc1, .. },
TVar { fields: fields2, id: id2, name: name2, loc: loc2, .. },
TVar { fields: fields1, id, name: name1, loc: loc1, is_const_generic: false, .. },
TVar { fields: fields2, id: id2, name: name2, loc: loc2, is_const_generic: false, .. },
) => {
let new_fields = match (fields1, fields2) {
(None, None) => None,
@ -542,7 +638,7 @@ impl Unifier {
}
(Some(fields1), Some(fields2)) => {
let mut new_fields: Mapping<_, _> = fields2.clone();
for (key, val1) in fields1.iter() {
for (key, val1) in fields1 {
if let Some(val2) = fields2.get(key) {
self.unify_impl(val1.ty, val2.ty, false).map_err(|_| {
TypeError::new(
@ -571,9 +667,9 @@ impl Unifier {
};
let intersection = self
.get_intersection(a, b)
.map_err(|_| TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None))?
.map_err(|()| TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None))?
.unwrap();
let range = if let TypeEnum::TVar { range, .. } = &*self.get_ty(intersection) {
let range = if let TVar { range, .. } = &*self.get_ty(intersection) {
range.clone()
} else {
unreachable!()
@ -581,16 +677,17 @@ impl Unifier {
self.unification_table.unify(a, b);
self.unification_table.set_value(
a,
Rc::new(TypeEnum::TVar {
Rc::new(TVar {
id: name1.map_or(*id2, |_| *id),
fields: new_fields,
range,
name: name1.or(*name2),
loc: loc1.or(*loc2),
is_const_generic: false,
}),
);
}
(TVar { fields: None, range, .. }, _) => {
(TVar { fields: None, range, is_const_generic: false, .. }, _) => {
// We check for the range of the type variable to see if unification is allowed.
// Note that although b may be compatible with a, we may have to constrain type
// variables in b to make sure that instantiations of b would always be compatible
@ -607,9 +704,9 @@ impl Unifier {
self.unify_impl(x, b, false)?;
self.set_a_to_b(a, x);
}
(TVar { fields: Some(fields), range, .. }, TTuple { ty }) => {
let len = ty.len() as i32;
for (k, v) in fields.iter() {
(TVar { fields: Some(fields), range, is_const_generic: false, .. }, TTuple { ty }) => {
let len = i32::try_from(ty.len()).unwrap();
for (k, v) in fields {
match *k {
RecordKey::Int(i) => {
if v.mutable {
@ -637,11 +734,11 @@ impl Unifier {
self.unify_impl(x, b, false)?;
self.set_a_to_b(a, x);
}
(TVar { fields: Some(fields), range, .. }, TList { ty }) => {
for (k, v) in fields.iter() {
(TVar { fields: Some(fields), range, is_const_generic: false, .. }, TList { ty }) => {
for (k, v) in fields {
match *k {
RecordKey::Int(_) => {
self.unify_impl(v.ty, *ty, false).map_err(|e| e.at(v.loc))?
self.unify_impl(v.ty, *ty, false).map_err(|e| e.at(v.loc))?;
}
RecordKey::Str(_) => {
return Err(TypeError::new(TypeErrorKind::NoSuchField(*k, b), v.loc))
@ -652,21 +749,100 @@ impl Unifier {
self.unify_impl(x, b, false)?;
self.set_a_to_b(a, x);
}
(TVar { id: id1, range: ty1, is_const_generic: true, .. }, TVar { id: id2, range: ty2, .. }) => {
let ty1 = ty1[0];
let ty2 = ty2[0];
if id1 != id2 {
self.unify_impl(ty1, ty2, false)?;
}
self.set_a_to_b(a, b);
}
(TVar { range: tys, is_const_generic: true, .. }, TLiteral { values, .. }) => {
assert_eq!(tys.len(), 1);
assert_eq!(values.len(), 1);
let primitives = &self.primitive_store
.expect("Expected PrimitiveStore to be present");
let ty = tys[0];
let value= &values[0];
let value_ty = value.get_type(primitives, self);
// If the types don't match, try to implicitly promote integers
if !self.unioned(ty, value_ty) {
let Ok(num_val) = i128::try_from(value.clone()) else {
return Self::incompatible_types(a, b)
};
let can_convert = if self.unioned(ty, primitives.int32) {
i32::try_from(num_val).is_ok()
} else if self.unioned(ty, primitives.int64) {
i64::try_from(num_val).is_ok()
} else if self.unioned(ty, primitives.uint32) {
u32::try_from(num_val).is_ok()
} else if self.unioned(ty, primitives.uint64) {
u64::try_from(num_val).is_ok()
} else {
false
};
if !can_convert {
return Self::incompatible_types(a, b)
}
}
self.set_a_to_b(a, b);
}
(TLiteral { values: val1, .. }, TLiteral { values: val2, .. }) => {
for (v1, v2) in zip(val1, val2) {
if v1 != v2 {
let symbol_value_to_int = |value: &SymbolValue| -> Option<i128> {
match value {
SymbolValue::I32(v) => Some(*v as i128),
SymbolValue::I64(v) => Some(*v as i128),
SymbolValue::U32(v) => Some(*v as i128),
SymbolValue::U64(v) => Some(*v as i128),
_ => None,
}
};
// Try performing integer promotion on literals
let v1i = symbol_value_to_int(v1);
let v2i = symbol_value_to_int(v2);
if v1i != v2i {
return Self::incompatible_types(a, b)
}
}
}
self.set_a_to_b(a, b);
}
(TTuple { ty: ty1 }, TTuple { ty: ty2 }) => {
if ty1.len() != ty2.len() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
}
for (x, y) in ty1.iter().zip(ty2.iter()) {
self.unify_impl(*x, *y, false)?;
if self.unify_impl(*x, *y, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
}
}
self.set_a_to_b(a, b);
}
(TList { ty: ty1 }, TList { ty: ty2 }) => {
self.unify_impl(*ty1, *ty2, false)?;
if self.unify_impl(*ty1, *ty2, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
}
self.set_a_to_b(a, b);
}
(TVar { fields: Some(map), range, .. }, TObj { fields, .. }) => {
for (k, field) in map.iter() {
for (k, field) in map {
match *k {
RecordKey::Str(s) => {
let (ty, mutable) = fields.get(&s).copied().ok_or_else(|| {
@ -698,7 +874,7 @@ impl Unifier {
(TVar { fields: Some(map), range, .. }, TVirtual { ty }) => {
let ty = self.get_ty(*ty);
if let TObj { fields, .. } = ty.as_ref() {
for (k, field) in map.iter() {
for (k, field) in map {
match *k {
RecordKey::Str(s) => {
let (ty, _) = fields.get(&s).copied().ok_or_else(|| {
@ -740,21 +916,32 @@ impl Unifier {
TObj { obj_id: id2, params: params2, .. },
) => {
if id1 != id2 {
self.incompatible_types(a, b)?;
Self::incompatible_types(a, b)?;
}
for (x, y) in zip(params1.values(), params2.values()) {
self.unify_impl(*x, *y, false)?;
// Sort the type arguments by its UnificationKey first, since `HashMap::iter` visits
// all K-V pairs "in arbitrary order"
let (tv1, tv2) = (
params1.iter().map(|(_, v)| v).collect_vec(),
params2.iter().map(|(_, v)| v).collect_vec(),
);
for (x, y) in zip(tv1, tv2) {
if self.unify_impl(*x, *y, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
};
}
self.set_a_to_b(a, b);
}
(TVirtual { ty: ty1 }, TVirtual { ty: ty2 }) => {
self.unify_impl(*ty1, *ty2, false)?;
if self.unify_impl(*ty1, *ty2, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
};
self.set_a_to_b(a, b);
}
(TCall(calls1), TCall(calls2)) => {
// we do not unify individual calls, instead we defer until the unification wtih a
// function definition.
let calls = calls1.iter().chain(calls2.iter()).cloned().collect();
let calls = calls1.iter().chain(calls2.iter()).copied().collect();
self.set_a_to_b(a, b);
self.unification_table.set_value(b, Rc::new(TCall(calls)));
}
@ -767,7 +954,7 @@ impl Unifier {
.rev()
.collect();
// we unify every calls to the function signature.
for c in calls.iter() {
for c in calls {
let call = self.calls[c.0].clone();
self.unify_call(&call, b, signature, &required)?;
}
@ -784,9 +971,13 @@ impl Unifier {
if x.name != y.name || x.default_value != y.default_value {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
}
self.unify_impl(x.ty, y.ty, false)?;
if self.unify_impl(x.ty, y.ty, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
};
}
self.unify_impl(sign1.ret, sign2.ret, false)?;
if self.unify_impl(sign1.ret, sign2.ret, false).is_err() {
return Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None));
};
self.set_a_to_b(a, b);
}
(TVar { fields: Some(fields), .. }, _) => {
@ -795,10 +986,10 @@ impl Unifier {
}
_ => {
if swapped {
return self.incompatible_types(a, b);
} else {
self.unify_impl(b, a, true)?;
return Self::incompatible_types(a, b);
}
self.unify_impl(b, a, true)?;
}
}
Ok(())
@ -818,19 +1009,18 @@ impl Unifier {
ty,
&mut |id| {
top_level.as_ref().map_or_else(
|| format!("{}", id),
|| format!("{id}"),
|top_level| {
if let TopLevelDef::Class { name, .. } =
&*top_level.definitions.read()[id].read()
{
name.to_string()
} else {
let top_level_def = &top_level.definitions.read()[id];
let TopLevelDef::Class { name, .. } = &*top_level_def.read() else {
unreachable!("expected class definition")
}
};
name.to_string()
},
)
},
&mut |id| format!("typevar{}", id),
&mut |id| format!("typevar{id}"),
notes,
)
}
@ -873,7 +1063,7 @@ impl Unifier {
if !range.is_empty() && notes.is_some() && !notes.as_ref().unwrap().contains_key(id)
{
// just in case if there is any cyclic dependency
notes.as_mut().unwrap().insert(*id, "".into());
notes.as_mut().unwrap().insert(*id, String::new());
let body = format!(
"{} ∈ {{{}}}",
n,
@ -887,6 +1077,9 @@ impl Unifier {
};
n
}
TypeEnum::TLiteral { values, .. } => {
format!("const({})", values.iter().map(|v| format!("{v:?}")).join(", "))
}
TypeEnum::TTuple { ty } => {
let mut fields =
ty.iter().map(|v| self.internal_stringify(*v, obj_to_name, var_to_name, notes));
@ -903,15 +1096,13 @@ impl Unifier {
}
TypeEnum::TObj { obj_id, params, .. } => {
let name = obj_to_name(obj_id.0);
if !params.is_empty() {
let params = params
if params.is_empty() {
name
} else {
let mut params = params
.iter()
.map(|(_, v)| self.internal_stringify(*v, obj_to_name, var_to_name, notes));
// sort to preserve order
let mut params = params.sorted();
format!("{}[{}]", name, params.join(", "))
} else {
name
}
}
TypeEnum::TCall { .. } => "call".to_owned(),
@ -937,20 +1128,20 @@ impl Unifier {
})
.join(", ");
let ret = self.internal_stringify(signature.ret, obj_to_name, var_to_name, notes);
format!("fn[[{}], {}]", params, ret)
format!("fn[[{params}], {ret}]")
}
}
}
/// Unifies `a` and `b` together, and set the value to the value of `b`.
fn set_a_to_b(&mut self, a: Type, b: Type) {
// unify a and b together, and set the value to b's value.
let table = &mut self.unification_table;
let ty_b = table.probe_value(b).clone();
table.unify(a, b);
table.set_value(a, ty_b)
table.set_value(a, ty_b);
}
fn incompatible_types(&mut self, a: Type, b: Type) -> Result<(), TypeError> {
fn incompatible_types(a: Type, b: Type) -> Result<(), TypeError> {
Err(TypeError::new(TypeErrorKind::IncompatibleTypes(a, b), None))
}
@ -960,7 +1151,7 @@ impl Unifier {
fn instantiate_fun(&mut self, ty: Type, fun: &FunSignature) -> Type {
let mut instantiated = true;
let mut vars = Vec::new();
for (k, v) in fun.vars.iter() {
for (k, v) in &fun.vars {
if let TypeEnum::TVar { id, name, loc, range, .. } =
self.unification_table.probe_value(*v).as_ref()
{
@ -1014,8 +1205,8 @@ impl Unifier {
// variables, i.e. things like TRecord, TCall should not occur, and we
// should be safe to not implement the substitution for those variants.
match &*ty {
TypeEnum::TRigidVar { .. } => None,
TypeEnum::TVar { id, .. } => mapping.get(id).cloned(),
TypeEnum::TRigidVar { .. } | TypeEnum::TLiteral { .. } => None,
TypeEnum::TVar { id, .. } => mapping.get(id).copied(),
TypeEnum::TTuple { ty } => {
let mut new_ty = Cow::from(ty);
for (i, t) in ty.iter().enumerate() {
@ -1077,14 +1268,14 @@ impl Unifier {
}
if new_params.is_some() || new_ret.is_some() || matches!(new_args, Cow::Owned(..)) {
let params = new_params.unwrap_or_else(|| params.clone());
let ret = new_ret.unwrap_or_else(|| *ret);
let ret = new_ret.unwrap_or(*ret);
let args = new_args.into_owned();
Some(self.add_ty(TypeEnum::TFunc(FunSignature { args, ret, vars: params })))
} else {
None
}
}
_ => {
TypeEnum::TCall(_) => {
unreachable!("{} not expected", ty.get_type_name())
}
}
@ -1092,15 +1283,15 @@ impl Unifier {
fn subst_map<K>(
&mut self,
map: &Mapping<K>,
map: &IndexMapping<K>,
mapping: &VarMap,
cache: &mut HashMap<Type, Option<Type>>,
) -> Option<Mapping<K>>
where
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
) -> Option<IndexMapping<K>>
where
K: std::hash::Hash + Eq + Clone,
{
let mut map2 = None;
for (k, v) in map.iter() {
for (k, v) in map {
if let Some(v1) = self.subst_impl(*v, mapping, cache) {
if map2.is_none() {
map2 = Some(map.clone());
@ -1118,10 +1309,10 @@ impl Unifier {
cache: &mut HashMap<Type, Option<Type>>,
) -> Option<Mapping<K, (Type, bool)>>
where
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
K: std::hash::Hash + Eq + Clone,
{
let mut map2 = None;
for (k, (v, mutability)) in map.iter() {
for (k, (v, mutability)) in map {
if let Some(v1) = self.subst_impl(*v, mapping, cache) {
if map2.is_none() {
map2 = Some(map.clone());
@ -1166,6 +1357,7 @@ impl Unifier {
range,
name: name2.or(*name),
loc: loc2.or(*loc),
is_const_generic: false,
};
Ok(Some(self.unification_table.new_key(ty.into())))
}
@ -1176,7 +1368,7 @@ impl Unifier {
if range.is_empty() {
Ok(Some(a))
} else {
for v in range.iter() {
for v in range {
let result = self.get_intersection(a, *v);
if let Ok(result) = result {
return Ok(result.or(Some(a)));
@ -1192,7 +1384,7 @@ impl Unifier {
.try_collect()?;
if ty.iter().any(Option::is_some) {
Ok(Some(self.add_ty(TTuple {
ty: zip(ty.into_iter(), ty1.iter()).map(|(a, b)| a.unwrap_or(*b)).collect(),
ty: zip(ty, ty1.iter()).map(|(a, b)| a.unwrap_or(*b)).collect(),
})))
} else {
Ok(None)
@ -1218,7 +1410,7 @@ impl Unifier {
if range.is_empty() {
return Ok(None);
}
for t in range.iter() {
for t in range {
let result = self.get_intersection(*t, b);
if let Ok(result) = result {
return Ok(result);

View File

@ -40,14 +40,14 @@ impl Unifier {
TypeEnum::TObj { obj_id: id1, params: params1, .. },
TypeEnum::TObj { obj_id: id2, params: params2, .. },
) => id1 == id2 && self.map_eq(params1, params2),
// TCall and TFunc are not yet implemented
// TLiteral, TCall and TFunc are not yet implemented
_ => false,
}
}
fn map_eq<K>(&mut self, map1: &Mapping<K>, map2: &Mapping<K>) -> bool
where
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
fn map_eq<K>(&mut self, map1: &IndexMapping<K>, map2: &IndexMapping<K>) -> bool
where
K: std::hash::Hash + Eq + Clone
{
if map1.len() != map2.len() {
return false;
@ -62,7 +62,7 @@ impl Unifier {
fn map_eq2<K>(&mut self, map1: &Mapping<K, RecordField>, map2: &Mapping<K, RecordField>) -> bool
where
K: std::hash::Hash + std::cmp::Eq + std::clone::Clone,
K: std::hash::Hash + Eq + Clone,
{
if map1.len() != map2.len() {
return false;
@ -91,7 +91,7 @@ impl TestEnvironment {
unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(0),
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
}),
);
type_mapping.insert(
@ -99,7 +99,7 @@ impl TestEnvironment {
unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(1),
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
}),
);
type_mapping.insert(
@ -107,7 +107,7 @@ impl TestEnvironment {
unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(2),
fields: HashMap::new(),
params: HashMap::new(),
params: VarMap::new(),
}),
);
let (v0, id) = unifier.get_dummy_var();
@ -116,7 +116,7 @@ impl TestEnvironment {
unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(3),
fields: [("a".into(), (v0, true))].iter().cloned().collect::<HashMap<_, _>>(),
params: [(id, v0)].iter().cloned().collect::<HashMap<_, _>>(),
params: [(id, v0)].iter().cloned().collect::<VarMap>(),
}),
);
@ -139,7 +139,7 @@ impl TestEnvironment {
match &typ[..end] {
"tuple" => {
let mut s = &typ[end..];
assert!(&s[0..1] == "[");
assert_eq!(&s[0..1], "[");
let mut ty = Vec::new();
while &s[0..1] != "]" {
let result = self.internal_parse(&s[1..], mapping);
@ -149,14 +149,14 @@ impl TestEnvironment {
(self.unifier.add_ty(TypeEnum::TTuple { ty }), &s[1..])
}
"list" => {
assert!(&typ[end..end + 1] == "[");
assert_eq!(&typ[end..end + 1], "[");
let (ty, s) = self.internal_parse(&typ[end + 1..], mapping);
assert!(&s[0..1] == "]");
assert_eq!(&s[0..1], "]");
(self.unifier.add_ty(TypeEnum::TList { ty }), &s[1..])
}
"Record" => {
let mut s = &typ[end..];
assert!(&s[0..1] == "[");
assert_eq!(&s[0..1], "[");
let mut fields = HashMap::new();
while &s[0..1] != "]" {
let eq = s.find('=').unwrap();
@ -176,7 +176,7 @@ impl TestEnvironment {
let te = self.unifier.get_ty(ty);
if let TypeEnum::TObj { params, .. } = &*te.as_ref() {
if !params.is_empty() {
assert!(&s[0..1] == "[");
assert_eq!(&s[0..1], "[");
let mut p = Vec::new();
while &s[0..1] != "]" {
let result = self.internal_parse(&s[1..], mapping);
@ -286,7 +286,7 @@ fn test_unify(
("v1", "tuple[int]"),
("v2", "tuple[float]"),
],
(("v1", "v2"), "Incompatible types: 0 and 1")
(("v1", "v2"), "Incompatible types: tuple[0] and tuple[1]")
; "tuple parameter mismatch"
)]
#[test_case(2,
@ -339,23 +339,21 @@ fn test_recursive_subst() {
let int = *env.type_mapping.get("int").unwrap();
let foo_id = *env.type_mapping.get("Foo").unwrap();
let foo_ty = env.unifier.get_ty(foo_id);
let mapping: HashMap<_, _>;
with_fields(&mut env.unifier, foo_id, |_unifier, fields| {
fields.insert("rec".into(), (foo_id, true));
});
if let TypeEnum::TObj { params, .. } = &*foo_ty {
mapping = params.iter().map(|(id, _)| (*id, int)).collect();
} else {
let TypeEnum::TObj { params, .. } = &*foo_ty else {
unreachable!()
}
};
let mapping = params.iter().map(|(id, _)| (*id, int)).collect();
let instantiated = env.unifier.subst(foo_id, &mapping).unwrap();
let instantiated_ty = env.unifier.get_ty(instantiated);
if let TypeEnum::TObj { fields, .. } = &*instantiated_ty {
assert!(env.unifier.unioned(fields.get(&"a".into()).unwrap().0, int));
assert!(env.unifier.unioned(fields.get(&"rec".into()).unwrap().0, instantiated));
} else {
let TypeEnum::TObj { fields, .. } = &*instantiated_ty else {
unreachable!()
}
};
assert!(env.unifier.unioned(fields.get(&"a".into()).unwrap().0, int));
assert!(env.unifier.unioned(fields.get(&"rec".into()).unwrap().0, instantiated));
}
#[test]
@ -365,7 +363,7 @@ fn test_virtual() {
let fun = env.unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![],
ret: int,
vars: HashMap::new(),
vars: VarMap::new(),
}));
let bar = env.unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(5),
@ -373,7 +371,7 @@ fn test_virtual() {
.iter()
.cloned()
.collect::<HashMap<StrRef, _>>(),
params: HashMap::new(),
params: VarMap::new(),
});
let v0 = env.unifier.get_dummy_var().0;
let v1 = env.unifier.get_dummy_var().0;
@ -478,7 +476,8 @@ fn test_typevar_range() {
let int_list = env.unifier.add_ty(TypeEnum::TList { ty: int });
assert_eq!(
env.unify(a_list, int_list),
Err("Expected any one of these types: 1, but got 0".into())
Err("Incompatible types: list[typevar22] and list[0]\
\n\nNotes:\n typevar22 {1}".into())
);
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
@ -506,7 +505,7 @@ fn test_rigid_var() {
assert_eq!(env.unify(a, b), Err("Incompatible types: typevar3 and typevar2".to_string()));
env.unifier.unify(list_a, list_x).unwrap();
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: 0 and typevar2".to_string()));
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: list[typevar2] and list[0]".to_string()));
env.unifier.replace_rigid_var(a, int);
env.unifier.unify(list_x, list_int).unwrap();

8
nac3ld/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "nac3ld"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2021"
[dependencies]
byteorder = { version = "1.5", default-features = false }

523
nac3ld/src/dwarf.rs Normal file
View File

@ -0,0 +1,523 @@
#![allow(non_camel_case_types, non_upper_case_globals)]
use std::mem;
use byteorder::{ByteOrder, LittleEndian};
pub const DW_EH_PE_omit: u8 = 0xFF;
pub const DW_EH_PE_absptr: u8 = 0x00;
pub const DW_EH_PE_uleb128: u8 = 0x01;
pub const DW_EH_PE_udata2: u8 = 0x02;
pub const DW_EH_PE_udata4: u8 = 0x03;
pub const DW_EH_PE_udata8: u8 = 0x04;
pub const DW_EH_PE_sleb128: u8 = 0x09;
pub const DW_EH_PE_sdata2: u8 = 0x0A;
pub const DW_EH_PE_sdata4: u8 = 0x0B;
pub const DW_EH_PE_sdata8: u8 = 0x0C;
pub const DW_EH_PE_pcrel: u8 = 0x10;
pub const DW_EH_PE_textrel: u8 = 0x20;
pub const DW_EH_PE_datarel: u8 = 0x30;
pub const DW_EH_PE_funcrel: u8 = 0x40;
pub const DW_EH_PE_aligned: u8 = 0x50;
pub const DW_EH_PE_indirect: u8 = 0x80;
pub struct DwarfReader<'a> {
pub slice: &'a [u8],
pub virt_addr: u32,
base_slice: &'a [u8],
base_virt_addr: u32,
}
impl<'a> DwarfReader<'a> {
pub fn new(slice: &[u8], virt_addr: u32) -> DwarfReader {
DwarfReader { slice, virt_addr, base_slice: slice, base_virt_addr: virt_addr }
}
/// Creates a new instance from another instance of [DwarfReader], optionally removing any
/// offsets previously applied to the other instance.
pub fn from_reader(other: &DwarfReader<'a>, reset_offset: bool) -> DwarfReader<'a> {
if reset_offset {
DwarfReader::new(other.base_slice, other.base_virt_addr)
} else {
DwarfReader::new(other.slice, other.virt_addr)
}
}
pub fn offset(&mut self, offset: u32) {
self.slice = &self.slice[offset as usize..];
self.virt_addr = self.virt_addr.wrapping_add(offset);
}
/// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable Length Data" of the
/// [DWARF-4 Manual](https://dwarfstd.org/doc/DWARF4.pdf).
pub fn read_uleb128(&mut self) -> u64 {
let mut shift: usize = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read_u8();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
result
}
pub fn read_sleb128(&mut self) -> i64 {
let mut shift: u32 = 0;
let mut result: u64 = 0;
let mut byte: u8;
loop {
byte = self.read_u8();
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte & 0x80 == 0 {
break;
}
}
// sign-extend
if shift < u64::BITS && (byte & 0x40) != 0 {
result |= (!0u64) << shift;
}
result as i64
}
pub fn read_u8(&mut self) -> u8 {
let val = self.slice[0];
self.slice = &self.slice[1..];
val
}
}
macro_rules! impl_read_fn {
( $($type: ty, $byteorder_fn: ident);* ) => {
impl<'a> DwarfReader<'a> {
$(
pub fn $byteorder_fn(&mut self) -> $type {
let val = LittleEndian::$byteorder_fn(self.slice);
self.slice = &self.slice[mem::size_of::<$type>()..];
val
}
)*
}
}
}
impl_read_fn!(
u16, read_u16;
u32, read_u32;
u64, read_u64;
i16, read_i16;
i32, read_i32;
i64, read_i64
);
pub struct DwarfWriter<'a> {
pub slice: &'a mut [u8],
pub offset: usize,
}
impl<'a> DwarfWriter<'a> {
pub fn new(slice: &mut [u8]) -> DwarfWriter {
DwarfWriter { slice, offset: 0 }
}
pub fn write_u8(&mut self, data: u8) {
self.slice[self.offset] = data;
self.offset += 1;
}
pub fn write_u32(&mut self, data: u32) {
LittleEndian::write_u32(&mut self.slice[self.offset..], data);
self.offset += 4;
}
}
fn read_encoded_pointer(reader: &mut DwarfReader, encoding: u8) -> Result<usize, ()> {
if encoding == DW_EH_PE_omit {
return Err(());
}
// DW_EH_PE_aligned implies it's an absolute pointer value
// However, we are linking library for 32-bits architecture
// The size of variable should be 4 bytes instead
if encoding == DW_EH_PE_aligned {
let shifted_virt_addr = round_up(reader.virt_addr as usize, mem::size_of::<u32>())?;
let addr_inc = shifted_virt_addr - reader.virt_addr as usize;
reader.slice = &reader.slice[addr_inc..];
reader.virt_addr = shifted_virt_addr as u32;
return Ok(reader.read_u32() as usize);
}
match encoding & 0x0F {
DW_EH_PE_absptr => Ok(reader.read_u32() as usize),
DW_EH_PE_uleb128 => Ok(reader.read_uleb128() as usize),
DW_EH_PE_udata2 => Ok(reader.read_u16() as usize),
DW_EH_PE_udata4 => Ok(reader.read_u32() as usize),
DW_EH_PE_udata8 => Ok(reader.read_u64() as usize),
DW_EH_PE_sleb128 => Ok(reader.read_sleb128() as usize),
DW_EH_PE_sdata2 => Ok(reader.read_i16() as usize),
DW_EH_PE_sdata4 => Ok(reader.read_i32() as usize),
DW_EH_PE_sdata8 => Ok(reader.read_i64() as usize),
_ => Err(()),
}
}
fn read_encoded_pointer_with_pc(
reader: &mut DwarfReader,
encoding: u8,
) -> Result<usize, ()> {
let entry_virt_addr = reader.virt_addr;
let mut result = read_encoded_pointer(reader, encoding)?;
// DW_EH_PE_aligned implies it's an absolute pointer value
if encoding == DW_EH_PE_aligned {
return Ok(result);
}
result = match encoding & 0x70 {
DW_EH_PE_pcrel => result.wrapping_add(entry_virt_addr as usize),
// .eh_frame normally would not have these kinds of relocations
// These would not be supported by a dedicated linker relocation schemes for RISC-V
DW_EH_PE_textrel | DW_EH_PE_datarel | DW_EH_PE_funcrel | DW_EH_PE_aligned => {
unimplemented!()
}
// Other values should be impossible
_ => unreachable!(),
};
if encoding & DW_EH_PE_indirect != 0 {
// There should not be a need for indirect addressing, as assembly code from
// the dynamic library should not be freely moved relative to the EH frame.
unreachable!()
}
Ok(result)
}
#[inline]
fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
if align.is_power_of_two() {
Ok((unrounded + align - 1) & !(align - 1))
} else {
Err(())
}
}
/// Minimalistic structure to store everything needed for parsing FDEs to synthesize `.eh_frame_hdr`
/// section.
///
/// Refer to [The Linux Standard Base Core Specification, Generic Part](https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html)
/// for more information.
pub struct EH_Frame<'a> {
reader: DwarfReader<'a>,
}
impl<'a> EH_Frame<'a> {
/// Creates an [EH_Frame] using the bytes in the `.eh_frame` section and its address in the ELF
/// file.
pub fn new(eh_frame_slice: &[u8], eh_frame_addr: u32) -> Result<EH_Frame, ()> {
Ok(EH_Frame { reader: DwarfReader::new(eh_frame_slice, eh_frame_addr) })
}
/// Returns an [Iterator] over all Call Frame Information (CFI) records.
pub fn cfi_records(&self) -> CFI_Records<'a> {
let reader = DwarfReader::from_reader(&self.reader, true);
let len = reader.slice.len();
CFI_Records {
reader,
available: len,
}
}
}
/// A single Call Frame Information (CFI) record.
///
/// From the [specification](https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html):
///
/// > Each CFI record contains a Common Information Entry (CIE) record followed by 1 or more Frame
/// Description Entry (FDE) records.
pub struct CFI_Record<'a> {
// It refers to the augmentation data that corresponds to 'R' in the augmentation string
fde_pointer_encoding: u8,
fde_reader: DwarfReader<'a>,
}
impl<'a> CFI_Record<'a> {
pub fn from_reader(cie_reader: &mut DwarfReader<'a>) -> Result<CFI_Record<'a>, ()> {
let length = cie_reader.read_u32();
let fde_reader = match length {
// eh_frame with 0 lengths means the CIE is terminated
0 => panic!("Cannot create an EH_Frame from a termination CIE"),
// length == u32::MAX means that the length is only representable with 64 bits,
// which does not make sense in a system with 32-bit address.
0xFFFFFFFF => unimplemented!(),
_ => {
let mut fde_reader = DwarfReader::from_reader(cie_reader, false);
fde_reader.offset(length);
fde_reader
}
};
// Routine check on the .eh_frame well-formness, in terms of CIE ID & Version args.
let cie_ptr = cie_reader.read_u32();
assert_eq!(cie_ptr, 0);
assert_eq!(cie_reader.read_u8(), 1);
// Parse augmentation string
// The first character must be 'z', there is no way to proceed otherwise
assert_eq!(cie_reader.read_u8(), b'z');
// Establish a pointer that skips ahead of the string
// Skip code/data alignment factors & return address register along the way as well
// We only tackle the case where 'z' and 'R' are part of the augmentation string, otherwise
// we cannot get the addresses to make .eh_frame_hdr
let mut aug_data_reader = DwarfReader::from_reader(cie_reader, false);
let mut aug_str_len = 0;
loop {
if aug_data_reader.read_u8() == b'\0' {
break;
}
aug_str_len += 1;
}
if aug_str_len == 0 {
unimplemented!();
}
aug_data_reader.read_uleb128(); // Code alignment factor
aug_data_reader.read_sleb128(); // Data alignment factor
aug_data_reader.read_uleb128(); // Return address register
aug_data_reader.read_uleb128(); // Augmentation data length
let mut fde_pointer_encoding = DW_EH_PE_omit;
for _ in 0..aug_str_len {
match cie_reader.read_u8() {
b'L' => {
aug_data_reader.read_u8();
}
b'P' => {
let encoding = aug_data_reader.read_u8();
read_encoded_pointer(&mut aug_data_reader, encoding)?;
}
b'R' => {
fde_pointer_encoding = aug_data_reader.read_u8();
}
// Other characters are not supported
_ => unimplemented!(),
}
}
assert_ne!(fde_pointer_encoding, DW_EH_PE_omit);
Ok(CFI_Record {
fde_pointer_encoding,
fde_reader,
})
}
/// Returns a [DwarfReader] initialized to the first Frame Description Entry (FDE) of this CFI
/// record.
pub fn get_fde_reader(&self) -> DwarfReader<'a> {
DwarfReader::from_reader(&self.fde_reader, true)
}
/// Returns an [Iterator] over all Frame Description Entries (FDEs).
pub fn fde_records(&self) -> FDE_Records<'a> {
let reader = self.get_fde_reader();
let len = reader.slice.len();
FDE_Records {
pointer_encoding: self.fde_pointer_encoding,
reader,
available: len,
}
}
}
/// [Iterator] over Call Frame Information (CFI) records in an
/// [Exception Handling (EH) frame][EH_Frame].
pub struct CFI_Records<'a> {
reader: DwarfReader<'a>,
available: usize,
}
impl<'a> Iterator for CFI_Records<'a> {
type Item = CFI_Record<'a>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.available == 0 {
return None;
}
let mut this_reader = DwarfReader::from_reader(&self.reader, false);
// Remove the length of the header and the content from the counter
let length = self.reader.read_u32();
let length = match length {
// eh_frame with 0-length means the CIE is terminated
0 => return None,
0xFFFFFFFF => unimplemented!("CIE entries larger than 4 bytes not supported"),
other => other,
} as usize;
// Remove the length of the header and the content from the counter
self.available -= length + mem::size_of::<u32>();
let mut next_reader = DwarfReader::from_reader(&self.reader, false);
next_reader.offset(length as u32);
let cie_ptr = self.reader.read_u32();
self.reader = next_reader;
// Skip this record if it is a FDE
if cie_ptr == 0 {
// Rewind back to the start of the CFI Record
return Some(CFI_Record::from_reader(&mut this_reader).ok().unwrap())
}
}
}
}
/// [Iterator] over Frame Description Entries (FDEs) in an
/// [Exception Handling (EH) frame][EH_Frame].
pub struct FDE_Records<'a> {
pointer_encoding: u8,
reader: DwarfReader<'a>,
available: usize,
}
impl<'a> Iterator for FDE_Records<'a> {
type Item = (u32, u32);
fn next(&mut self) -> Option<Self::Item> {
// Parse each FDE to obtain the starting address that the FDE applies to
// Send the FDE offset and the mentioned address to a callback that write up the
// .eh_frame_hdr section
if self.available == 0 {
return None;
}
// Remove the length of the header and the content from the counter
let length = match self.reader.read_u32() {
// eh_frame with 0-length means the CIE is terminated
0 => return None,
0xFFFFFFFF => unimplemented!("CIE entries larger than 4 bytes not supported"),
other => other,
} as usize;
// Remove the length of the header and the content from the counter
self.available -= length + mem::size_of::<u32>();
let mut next_fde_reader = DwarfReader::from_reader(&self.reader, false);
next_fde_reader.offset(length as u32);
let cie_ptr = self.reader.read_u32();
let next_val = if cie_ptr != 0 {
let pc_begin = read_encoded_pointer_with_pc(&mut self.reader, self.pointer_encoding)
.expect("Failed to read PC Begin");
Some((pc_begin as u32, self.reader.virt_addr))
} else {
None
};
self.reader = next_fde_reader;
next_val
}
}
pub struct EH_Frame_Hdr<'a> {
fde_writer: DwarfWriter<'a>,
eh_frame_hdr_addr: u32,
fdes: Vec<(u32, u32)>,
}
impl<'a> EH_Frame_Hdr<'a> {
/// Create a [EH_Frame_Hdr] object, and write out the fixed fields of `.eh_frame_hdr` to memory.
///
/// Load address is not known at this point.
pub fn new(
eh_frame_hdr_slice: &mut [u8],
eh_frame_hdr_addr: u32,
eh_frame_addr: u32,
) -> EH_Frame_Hdr {
let mut writer = DwarfWriter::new(eh_frame_hdr_slice);
writer.write_u8(1); // version
writer.write_u8(0x1B); // eh_frame_ptr_enc - PC-relative 4-byte signed value
writer.write_u8(0x03); // fde_count_enc - 4-byte unsigned value
writer.write_u8(0x3B); // table_enc - .eh_frame_hdr section-relative 4-byte signed value
let eh_frame_offset = eh_frame_addr
.wrapping_sub(eh_frame_hdr_addr + writer.offset as u32 + ((mem::size_of::<u8>() as u32) * 4));
writer.write_u32(eh_frame_offset); // eh_frame_ptr
writer.write_u32(0); // `fde_count`, will be written in finalize_fde
EH_Frame_Hdr { fde_writer: writer, eh_frame_hdr_addr, fdes: Vec::new() }
}
/// The offset of the `fde_count` value relative to the start of the `.eh_frame_hdr` section in
/// bytes.
fn fde_count_offset() -> usize {
8
}
pub fn add_fde(&mut self, init_loc: u32, addr: u32) {
self.fdes.push((
init_loc.wrapping_sub(self.eh_frame_hdr_addr),
addr.wrapping_sub(self.eh_frame_hdr_addr),
));
}
pub fn finalize_fde(mut self) {
self.fdes
.sort_by(|(left_init_loc, _), (right_init_loc, _)| left_init_loc.cmp(right_init_loc));
for (init_loc, addr) in &self.fdes {
self.fde_writer.write_u32(*init_loc);
self.fde_writer.write_u32(*addr);
}
LittleEndian::write_u32(&mut self.fde_writer.slice[Self::fde_count_offset()..], self.fdes.len() as u32);
}
pub fn size_from_eh_frame(eh_frame: &[u8]) -> usize {
// The virtual address of the EH frame does not matter in this case
// Calculation of size does not involve modifying any headers
let mut reader = DwarfReader::new(eh_frame, 0);
let mut fde_count = 0;
while !reader.slice.is_empty() {
// The original length field should be able to hold the entire value.
// The device memory space is limited to 32-bits addresses anyway.
let entry_length = reader.read_u32();
if entry_length == 0 || entry_length == 0xFFFFFFFF {
unimplemented!()
}
// This slot stores the CIE ID (for CIE)/CIE Pointer (for FDE).
// This value must be non-zero for FDEs.
let cie_ptr = reader.read_u32();
if cie_ptr != 0 {
fde_count += 1;
}
reader.offset(entry_length - mem::size_of::<u32>() as u32)
}
12 + fde_count * 8
}
}

2893
nac3ld/src/elf.rs Normal file

File diff suppressed because it is too large Load Diff

1485
nac3ld/src/lib.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,20 +5,20 @@ description = "Parser for python code."
authors = [ "RustPython Team", "M-Labs" ]
build = "build.rs"
license = "MIT"
edition = "2018"
edition = "2021"
[build-dependencies]
lalrpop = "0.19.6"
lalrpop = "0.20"
[dependencies]
nac3ast = { path = "../nac3ast" }
lalrpop-util = "0.19.6"
log = "0.4.1"
lalrpop-util = "0.20"
log = "0.4"
unic-emoji-char = "0.9"
unic-ucd-ident = "0.9"
unicode_names2 = "0.4"
phf = { version = "0.9", features = ["macros"] }
ahash = "0.7.2"
unicode_names2 = "1.2"
phf = { version = "0.11", features = ["macros"] }
ahash = "0.8"
[dev-dependencies]
insta = "=1.11.0"

View File

@ -170,7 +170,7 @@ impl From<LalrpopError<Location, Tok, LexicalError>> for ParseError {
location: token.0,
}
}
LalrpopError::UnrecognizedEOF { location, .. } => ParseError {
LalrpopError::UnrecognizedEof { location, .. } => ParseError {
error: ParseErrorType::Eof,
location,
},

View File

@ -486,8 +486,8 @@ where
}
}
match p {
0xD800..=0xDFFF => Ok(std::char::REPLACEMENT_CHARACTER),
_ => std::char::from_u32(p).ok_or(unicode_error),
0xD800..=0xDFFF => Ok(char::REPLACEMENT_CHARACTER),
_ => char::from_u32(p).ok_or(unicode_error),
}
}

View File

@ -2,14 +2,18 @@
name = "nac3standalone"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2018"
edition = "2021"
[dependencies]
parking_lot = "0.11.1"
parking_lot = "0.12"
nac3parser = { path = "../nac3parser" }
nac3core = { path = "../nac3core" }
[dependencies.clap]
version = "4.5"
features = ["derive"]
[dependencies.inkwell]
version = "0.1.0-beta.4"
version = "0.4"
default-features = false
features = ["llvm13-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
features = ["llvm14-0", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]

View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -e
if [ -z "$1" ]; then
echo "Requires at least one argument"
exit 1
fi
declare -a nac3args
while [ $# -gt 1 ]; do
nac3args+=("$1")
shift
done
demo="$1"
echo -n "Checking $demo... "
./interpret_demo.py "$demo" > interpreted.log
./run_demo.sh --out run.log "${nac3args[@]}" "$demo"
./run_demo.sh --lli --out run_lli.log "${nac3args[@]}" "$demo"
diff -Nau interpreted.log run.log
diff -Nau interpreted.log run_lli.log
echo "ok"
rm -f interpreted.log run.log run_lli.log

View File

@ -4,12 +4,8 @@ set -e
count=0
for demo in src/*.py; do
echo -n "checking $demo... "
./interpret_demo.py $demo > interpreted.log
./run_demo.sh $demo > run.log
diff -Nau interpreted.log run.log
echo "ok"
let "count+=1"
./check_demo.sh "$@" "$demo"
((count += 1))
done
echo "Ran $count demo checks - PASSED"

109
nac3standalone/demo/demo.c Normal file
View File

@ -0,0 +1,109 @@
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define usize size_t
double dbl_nan(void) {
return NAN;
}
double dbl_inf(void) {
return INFINITY;
}
void output_bool(bool x) {
puts(x ? "True" : "False");
}
void output_int32(int32_t x) {
printf("%"PRId32"\n", x);
}
void output_int64(int64_t x) {
printf("%"PRId64"\n", x);
}
void output_uint32(uint32_t x) {
printf("%"PRIu32"\n", x);
}
void output_uint64(uint64_t x) {
printf("%"PRIu64"\n", x);
}
void output_float64(double x) {
if (isnan(x)) {
puts("nan");
} else {
printf("%f\n", x);
}
}
void output_asciiart(int32_t x) {
static const char *chars = " .,-:;i+hHM$*#@ ";
if (x < 0) {
putchar('\n');
} else {
putchar(chars[x]);
}
}
struct cslice {
void *data;
usize len;
};
void output_int32_list(struct cslice *slice) {
const int32_t *data = (int32_t *) slice->data;
putchar('[');
for (usize i = 0; i < slice->len; ++i) {
if (i == slice->len - 1) {
printf("%d", data[i]);
} else {
printf("%d, ", data[i]);
}
}
putchar(']');
putchar('\n');
}
void output_str(struct cslice *slice) {
const char *data = (const char *) slice->data;
for (usize i = 0; i < slice->len; ++i) {
putchar(data[i]);
}
putchar('\n');
}
uint64_t dbg_stack_address(__attribute__((unused)) struct cslice *slice) {
int i;
void *ptr = (void *) &i;
return (uintptr_t) ptr;
}
uint32_t __nac3_personality(uint32_t state, uint32_t exception_object, uint32_t context) {
printf("__nac3_personality(state: %u, exception_object: %u, context: %u)\n", state, exception_object, context);
exit(101);
__builtin_unreachable();
}
uint32_t __nac3_raise(uint32_t state, uint32_t exception_object, uint32_t context) {
printf("__nac3_raise(state: %u, exception_object: %u, context: %u)\n", state, exception_object, context);
exit(101);
__builtin_unreachable();
}
void __nac3_end_catch(void) {}
extern int32_t run(void);
int main(void) {
run();
}

View File

@ -1,90 +0,0 @@
mod cslice {
// copied from https://github.com/dherman/cslice
use std::marker::PhantomData;
use std::slice;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct CSlice<'a, T> {
base: *const T,
len: usize,
marker: PhantomData<&'a ()>,
}
impl<'a, T> AsRef<[T]> for CSlice<'a, T> {
fn as_ref(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.base, self.len) }
}
}
}
#[no_mangle]
pub extern "C" fn output_int32(x: i32) {
println!("{}", x);
}
#[no_mangle]
pub extern "C" fn output_int64(x: i64) {
println!("{}", x);
}
#[no_mangle]
pub extern "C" fn output_uint32(x: u32) {
println!("{}", x);
}
#[no_mangle]
pub extern "C" fn output_uint64(x: u64) {
println!("{}", x);
}
#[no_mangle]
pub extern "C" fn output_float64(x: f64) {
// debug output to preserve the digits after the decimal points
// to match python `print` function
println!("{:?}", x);
}
#[no_mangle]
pub extern "C" fn output_asciiart(x: i32) {
let chars = " .,-:;i+hHM$*#@ ";
if x < 0 {
println!("");
} else {
print!("{}", chars.chars().nth(x as usize).unwrap());
}
}
#[no_mangle]
pub extern "C" fn output_int32_list(x: &cslice::CSlice<i32>) {
print!("[");
let mut it = x.as_ref().iter().peekable();
while let Some(e) = it.next() {
if it.peek().is_none() {
print!("{}", e);
} else {
print!("{}, ", e);
}
}
println!("]");
}
#[no_mangle]
pub extern "C" fn __nac3_personality(_state: u32, _exception_object: u32, _context: u32) -> u32 {
unimplemented!();
}
#[no_mangle]
pub extern "C" fn __nac3_raise(_state: u32, _exception_object: u32, _context: u32) -> u32 {
unimplemented!();
}
extern "C" {
fn run() -> i32;
}
fn main() {
unsafe {
run();
}
}

View File

@ -3,10 +3,14 @@
import sys
import importlib.util
import importlib.machinery
import math
import numpy as np
import numpy.typing as npt
import pathlib
from numpy import int32, int64, uint32, uint64
from typing import TypeVar, Generic
from scipy import special
from typing import TypeVar, Generic, Literal, Union
T = TypeVar('T')
class Option(Generic[T]):
@ -41,26 +45,66 @@ def Some(v: T) -> Option[T]:
none = Option(None)
class _ConstGenericMarker:
pass
def ConstGeneric(name, constraint):
return TypeVar(name, _ConstGenericMarker, constraint)
N = TypeVar("N", bound=np.uint64)
class _NDArrayDummy(Generic[T, N]):
pass
# https://stackoverflow.com/questions/67803260/how-to-create-a-type-alias-with-a-throw-away-generic
NDArray = Union[npt.NDArray[T], _NDArrayDummy[T, N]]
def round_away_zero(x):
if x >= 0.0:
return math.floor(x + 0.5)
else:
return math.ceil(x - 0.5)
def patch(module):
def dbl_nan():
return np.nan
def dbl_inf():
return np.inf
def output_asciiart(x):
if x < 0:
sys.stdout.write("\n")
else:
sys.stdout.write(" .,-:;i+hHM$*#@ "[x])
def output_float(x):
print("%f" % x)
def dbg_stack_address(_):
return 0
def extern(fun):
name = fun.__name__
if name == "output_asciiart":
if name == "dbl_nan":
return dbl_nan
elif name == "dbl_inf":
return dbl_inf
elif name == "output_asciiart":
return output_asciiart
elif name == "output_float64":
return output_float
elif name in {
"output_bool",
"output_int32",
"output_int64",
"output_int32_list",
"output_uint32",
"output_uint64",
"output_float64"
"output_str",
}:
return print
elif name == "dbg_stack_address":
return dbg_stack_address
else:
raise NotImplementedError
@ -69,12 +113,85 @@ def patch(module):
module.uint32 = uint32
module.uint64 = uint64
module.TypeVar = TypeVar
module.ConstGeneric = ConstGeneric
module.Generic = Generic
module.Literal = Literal
module.extern = extern
module.Option = Option
module.Some = Some
module.none = none
# Builtin Math functions
module.round = round_away_zero
module.round64 = round_away_zero
module.np_round = np.round
module.floor = math.floor
module.floor64 = math.floor
module.np_floor = np.floor
module.ceil = math.ceil
module.ceil64 = math.ceil
module.np_ceil = np.ceil
# NumPy ndarray functions
module.ndarray = NDArray
module.np_ndarray = np.ndarray
module.np_empty = np.empty
module.np_zeros = np.zeros
module.np_ones = np.ones
module.np_full = np.full
module.np_eye = np.eye
module.np_identity = np.identity
# NumPy Math functions
module.np_isnan = np.isnan
module.np_isinf = np.isinf
module.np_sin = np.sin
module.np_cos = np.cos
module.np_exp = np.exp
module.np_exp2 = np.exp2
module.np_log = np.log
module.np_log10 = np.log10
module.np_log2 = np.log2
module.np_fabs = np.fabs
module.np_trunc = np.trunc
module.np_sqrt = np.sqrt
module.np_rint = np.rint
module.np_tan = np.tan
module.np_arcsin = np.arcsin
module.np_arccos = np.arccos
module.np_arctan = np.arctan
module.np_sinh = np.sinh
module.np_cosh = np.cosh
module.np_tanh = np.tanh
module.np_arcsinh = np.arcsinh
module.np_arccosh = np.arccosh
module.np_arctanh = np.arctanh
module.np_expm1 = np.expm1
module.np_cbrt = np.cbrt
module.np_arctan2 = np.arctan2
module.np_copysign = np.copysign
module.np_fmax = np.fmax
module.np_fmin = np.fmin
module.np_ldexp = np.ldexp
module.np_hypot = np.hypot
module.np_nextafter = np.nextafter
# SciPy Math Functions
module.sp_spec_erf = special.erf
module.sp_spec_erfc = special.erfc
module.sp_spec_gamma = special.gamma
module.sp_spec_gammaln = special.gammaln
module.sp_spec_j0 = special.j0
module.sp_spec_j1 = special.j1
# NumPy NDArray Functions
module.np_ndarray = np.ndarray
module.np_empty = np.empty
module.np_zeros = np.zeros
module.np_ones = np.ones
module.np_full = np.full
module.np_eye = np.eye
module.np_identity = np.identity
def file_import(filename, prefix="file_import_"):
filename = pathlib.Path(filename)

View File

@ -7,14 +7,59 @@ if [ -z "$1" ]; then
exit 1
fi
if [ -e ../../target/release/nac3standalone ]; then
declare -a nac3args
while [ $# -ge 1 ]; do
case "$1" in
--out)
shift
outfile="$1"
;;
--lli)
use_lli=1
;;
--debug)
debug=1
;;
*)
nac3args+=("$1")
;;
esac
shift
done
if [ -n "$debug" ] && [ -e ../../target/debug/nac3standalone ]; then
nac3standalone=../../target/debug/nac3standalone
elif [ -e ../../target/release/nac3standalone ]; then
nac3standalone=../../target/release/nac3standalone
else
# used by Nix builds
nac3standalone=../../target/x86_64-unknown-linux-gnu/release/nac3standalone
fi
rm -f *.o
$nac3standalone $1
rustc -o demo demo.rs -Crelocation-model=static -Clink-arg=./module.o
./demo
rm -f ./*.o ./*.bc demo
if [ -z "$use_lli" ]; then
$nac3standalone "${nac3args[@]}"
clang -c -std=gnu11 -Wall -Wextra -O3 -o demo.o demo.c
clang -lm -o demo module.o demo.o
if [ -z "$outfile" ]; then
./demo
else
./demo > "$outfile"
fi
else
$nac3standalone --emit-llvm "${nac3args[@]}"
clang -c -std=gnu11 -Wall -Wextra -O3 -emit-llvm -o demo.bc demo.c
shopt -s nullglob
llvm-link -o nac3out.bc module*.bc main.bc
shopt -u nullglob
if [ -z "$outfile" ]; then
lli --extra-module demo.bc --extra-module irrt.bc nac3out.bc
else
lli --extra-module demo.bc --extra-module irrt.bc nac3out.bc > "$outfile"
fi
fi

View File

@ -0,0 +1,30 @@
# Different cases for using boolean variables in boolean contexts.
# Tests whether all boolean variables (expressed as i8s) are lowered into i1s before used in branching instruction (`br`)
def bfunc(b: bool) -> bool:
return not b
def run() -> int32:
b1 = True
b2 = False
if b1:
pass
if not b2:
pass
while b2:
pass
l = [i for i in range(10) if b2]
b_and = True and False
b_or = True or False
b_and = b1 and b2
b_or = b1 or b2
bfunc(b1)
return 0

View File

@ -23,8 +23,8 @@ class A:
def get_a(self) -> int32:
return self.a
def get_b(self) -> B:
return self.b
# def get_b(self) -> B:
# return self.b
def run() -> int32:

View File

@ -0,0 +1,50 @@
A = ConstGeneric("A", int32)
B = ConstGeneric("B", uint32)
T = TypeVar("T")
class ConstGenericClass(Generic[A]):
def __init__(self):
pass
class ConstGeneric2Class(Generic[A, B]):
def __init__(self):
pass
class HybridGenericClass2(Generic[A, T]):
pass
class HybridGenericClass3(Generic[T, A, B]):
pass
def make_generic_2() -> ConstGenericClass[Literal[2]]:
return ...
def make_generic2_1_2() -> ConstGeneric2Class[Literal[1], Literal[2]]:
return ...
def make_hybrid_class_2_int32() -> HybridGenericClass2[Literal[2], int32]:
return ...
def make_hybrid_class_i32_0_1() -> HybridGenericClass3[int32, Literal[0], Literal[1]]:
return ...
def consume_generic_2(instance: ConstGenericClass[Literal[2]]):
pass
def consume_generic2_1_2(instance: ConstGeneric2Class[Literal[1], Literal[2]]):
pass
def consume_hybrid_class_2_i32(instance: HybridGenericClass2[Literal[2], int32]):
pass
def consume_hybrid_class_i32_0_1(instance: HybridGenericClass3[int32, Literal[0], Literal[1]]):
pass
def f():
consume_generic_2(make_generic_2())
consume_generic2_1_2(make_generic2_1_2())
consume_hybrid_class_2_i32(make_hybrid_class_2_int32())
consume_hybrid_class_i32_0_1(make_hybrid_class_i32_0_1())
def run() -> int32:
return 0

View File

@ -0,0 +1,8 @@
def f():
return
return
def run() -> int32:
f()
return 0

View File

@ -0,0 +1,83 @@
@extern
def output_bool(x: bool):
...
@extern
def output_int32(x: int32):
...
@extern
def output_int64(x: int64):
...
@extern
def output_uint32(x: uint32):
...
@extern
def output_uint64(x: uint64):
...
@extern
def output_float64(x: float):
...
@extern
def output_int32_list(x: list[int32]):
...
@extern
def output_asciiart(x: int32):
...
@extern
def output_str(x: str):
...
def test_output_bool():
output_bool(True)
output_bool(False)
def test_output_int32():
output_int32(-128)
def test_output_int64():
output_int64(int64(-256))
def test_output_uint32():
output_uint32(uint32(128))
def test_output_uint64():
output_uint64(uint64(256))
def test_output_float64():
output_float64(0.0)
output_float64(1.0)
output_float64(-1.0)
output_float64(128.0)
output_float64(-128.0)
output_float64(16.25)
output_float64(-16.25)
def test_output_asciiart():
for i in range(17):
output_asciiart(i)
output_asciiart(0)
def test_output_int32_list():
output_int32_list([0, 1, 3, 5, 10])
def test_output_str_family():
output_str("hello world")
def run() -> int32:
test_output_bool()
test_output_int32()
test_output_int64()
test_output_uint32()
test_output_uint64()
test_output_float64()
test_output_asciiart()
test_output_int32_list()
test_output_str_family()
return 0

View File

@ -0,0 +1,32 @@
from __future__ import annotations
@extern
def output_int32(x: int32):
...
class A:
a: int32
def __init__(self, a: int32):
self.a = a
def f1(self):
self.f2()
def f2(self):
output_int32(self.a)
class B(A):
b: int32
def __init__(self, b: int32):
self.a = b + 1
self.b = b
def run() -> int32:
aaa = A(5)
bbb = B(2)
aaa.f1()
bbb.f1()
return 0

View File

@ -0,0 +1,17 @@
@extern
def output_int32(x: int32):
...
@extern
def output_int32_list(x: list[int32]):
...
def run() -> int32:
bl = [True, False]
bl1 = bl[:]
bl1[1:] = [True]
output_int32_list([int32(b) for b in bl1])
output_int32_list([int32(b) for b in bl1])
return 0

View File

@ -1,9 +1,12 @@
# For Loop using an increasing range() expression as its iterable
@extern
def output_int32(x: int32):
...
def run() -> int32:
for _ in range(10):
output_int32(_)
_ = 0
i = 0
for i in range(10):
output_int32(i)
output_int32(i)
return 0

View File

@ -0,0 +1,21 @@
@extern
def output_int32(x: int32):
...
def run() -> int32:
for i in range(4):
output_int32(i)
if i < 2:
continue
else:
break
n = [0, 1, 2, 3]
for i in n:
output_int32(i)
if i < 2:
continue
else:
break
return 0

View File

@ -0,0 +1,12 @@
# For Loop using a decreasing range() expression as its iterable
@extern
def output_int32(x: int32):
...
def run() -> int32:
i = 0
for i in range(10, 0, -1):
output_int32(i)
output_int32(i)
return 0

View File

@ -0,0 +1,17 @@
# For Loop using a list as its iterable
@extern
def output_int32(x: int32):
...
def run() -> int32:
l = [0, 1, 2, 3, 4]
# i: int32 # declaration-without-initializer not yet supported
i = 0 # i must be declared before the loop; this is not necessary in Python
for i in l:
output_int32(i)
i = 0
output_int32(i)
output_int32(i)
return 0

View File

@ -0,0 +1,14 @@
# For Loop using an range() expression as its iterable, additionally reassigning the target on each iteration
@extern
def output_int32(x: int32):
...
def run() -> int32:
i = 0
for i in range(10):
output_int32(i)
i = 0
output_int32(i)
output_int32(i)
return 0

View File

@ -0,0 +1,33 @@
# Break within try statement within a loop
# Taken from https://book.pythontips.com/en/latest/for_-_else.html
@extern
def output_int32(x: int32):
...
@extern
def output_float64(x: float):
...
@extern
def output_str(x: str):
...
def run() -> int32:
for n in range(2, 10):
for x in range(2, n):
try:
if n % x == 0:
output_int32(n)
output_str(" equals ")
output_int32(x)
output_str(" * ")
output_float64(n / x)
except: # Assume this is intended to catch x == 0
break
else:
# loop fell through without finding a factor
output_int32(n)
output_str(" is a prime number")
return 0

View File

@ -0,0 +1,274 @@
@extern
def output_bool(x: bool):
...
@extern
def output_int32(x: int32):
...
@extern
def output_int64(x: int64):
...
@extern
def output_float64(x: float):
...
@extern
def dbl_nan() -> float:
...
@extern
def dbl_inf() -> float:
...
def dbl_pi() -> float:
return 3.1415926535897932384626433
def dbl_e() -> float:
return 2.71828182845904523536028747135266249775724709369995
def test_round():
for x in [-1.5, -0.5, 0.5, 1.5]:
output_int32(round(x))
def test_round64():
for x in [-1.5, -0.5, 0.5, 1.5]:
output_int64(round64(x))
def test_np_round():
for x in [-1.5, -0.5, 0.5, 1.5, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_round(x))
def test_np_isnan():
for x in [dbl_nan(), 0.0, dbl_inf()]:
output_bool(np_isnan(x))
def test_np_isinf():
for x in [dbl_inf(), -dbl_inf(), 0.0, dbl_nan()]:
output_bool(np_isinf(x))
def test_np_sin():
pi = dbl_pi()
for x in [-pi, -pi / 2.0, -pi / 4.0, 0.0, pi / 4.0, pi / 2.0, pi, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_sin(x))
def test_np_cos():
pi = dbl_pi()
for x in [-pi, -pi / 2.0, -pi / 4.0, 0.0, pi / 4.0, pi / 2.0, pi, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_cos(x))
def test_np_exp():
for x in [0.0, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_exp(x))
def test_np_exp2():
for x in [0.0, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_exp2(x))
def test_np_log():
e = dbl_e()
for x in [1.0, e, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_log(x))
def test_np_log10():
for x in [1.0, 10.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_log10(x))
def test_np_log2():
for x in [1.0, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_log2(x))
def test_np_fabs():
for x in [-1.0, 0.0, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_fabs(x))
def test_floor():
for x in [-1.5, -0.5, 0.5, 1.5]:
output_int32(floor(x))
def test_floor64():
for x in [-1.5, -0.5, 0.5, 1.5]:
output_int64(floor64(x))
def test_np_floor():
for x in [-1.5, -0.5, 0.5, 1.5, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_floor(x))
def test_ceil():
for x in [-1.5, -0.5, 0.5, 1.5]:
output_int32(ceil(x))
def test_ceil64():
for x in [-1.5, -0.5, 0.5, 1.5]:
output_int64(ceil64(x))
def test_np_ceil():
for x in [-1.5, -0.5, 0.5, 1.5, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_ceil(x))
def test_np_sqrt():
for x in [1.0, 2.0, 4.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_sqrt(x))
def test_np_rint():
for x in [-1.5, -0.5, 0.5, 1.5, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_rint(x))
def test_np_tan():
pi = dbl_pi()
for x in [-pi, -pi / 2.0, -pi / 4.0, 0.0, pi / 4.0, pi / 2.0, pi, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_tan(x))
def test_np_arcsin():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_arcsin(x))
def test_np_arccos():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_arccos(x))
def test_np_arctan():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_arctan(x))
def test_np_sinh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_sinh(x))
def test_np_cosh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_cosh(x))
def test_np_tanh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_tanh(x))
def test_np_arcsinh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_arcsinh(x))
def test_np_arccosh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_arccosh(x))
def test_np_arctanh():
for x in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_arctanh(x))
def test_np_expm1():
for x in [0.0, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_expm1(x))
def test_np_cbrt():
for x in [1.0, 8.0, 27.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_expm1(x))
def test_sp_spec_erf():
for x in [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(sp_spec_erf(x))
def test_sp_spec_erfc():
for x in [-3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(sp_spec_erfc(x))
def test_sp_spec_gamma():
for x in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(sp_spec_gamma(x))
def test_sp_spec_gammaln():
for x in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(sp_spec_gammaln(x))
def test_sp_spec_j0():
for x in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(sp_spec_j0(x))
def test_sp_spec_j1():
for x in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]:
output_float64(sp_spec_j1(x))
def test_np_arctan2():
for x1 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_arctan2(x1, x2))
def test_np_copysign():
for x1 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_copysign(x1, x2))
def test_np_fmax():
for x1 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_fmax(x1, x2))
def test_np_fmin():
for x1 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-1.0, -0.5, 0.0, 0.5, 1.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_fmin(x1, x2))
def test_np_ldexp():
for x1 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-2, -1, 0, 1, 2]:
output_float64(np_ldexp(x1, x2))
def test_np_hypot():
for x1 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_hypot(x1, x2))
def test_np_nextafter():
for x1 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
for x2 in [-2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, dbl_inf(), -dbl_inf(), dbl_nan()]:
output_float64(np_nextafter(x1, x2))
def run() -> int32:
test_round()
test_round64()
test_np_round()
test_np_isnan()
test_np_isinf()
test_np_sin()
test_np_cos()
test_np_exp()
test_np_exp2()
test_np_log()
test_np_log10()
test_np_log2()
test_np_fabs()
test_floor()
test_floor64()
test_np_floor()
test_ceil()
test_ceil64()
test_np_ceil()
test_np_sqrt()
test_np_rint()
test_np_tan()
test_np_arcsin()
test_np_arccos()
test_np_arctan()
test_np_sinh()
test_np_cosh()
test_np_tanh()
test_np_arcsinh()
test_np_arccosh()
test_np_arctanh()
test_np_expm1()
test_np_cbrt()
test_sp_spec_erf()
test_sp_spec_erfc()
test_sp_spec_gamma()
test_sp_spec_gammaln()
test_sp_spec_j0()
test_sp_spec_j1()
test_np_arctan2()
test_np_copysign()
test_np_fmax()
test_np_fmin()
test_np_ldexp()
test_np_hypot()
test_np_nextafter()
return 0

View File

@ -0,0 +1,742 @@
@extern
def output_bool(x: bool):
...
@extern
def output_int32(x: int32):
...
@extern
def output_float64(x: float):
...
def output_ndarray_bool_2(n: ndarray[bool, Literal[2]]):
for r in range(len(n)):
for c in range(len(n[r])):
output_bool(n[r][c])
def output_ndarray_int32_1(n: ndarray[int32, Literal[1]]):
for i in range(len(n)):
output_int32(n[i])
def output_ndarray_int32_2(n: ndarray[int32, Literal[2]]):
for r in range(len(n)):
for c in range(len(n[r])):
output_int32(n[r][c])
def output_ndarray_float_1(n: ndarray[float, Literal[1]]):
for i in range(len(n)):
output_float64(n[i])
def output_ndarray_float_2(n: ndarray[float, Literal[2]]):
for r in range(len(n)):
for c in range(len(n[r])):
output_float64(n[r][c])
def consume_ndarray_1(n: ndarray[float, Literal[1]]):
pass
def test_ndarray_ctor():
n: ndarray[float, Literal[1]] = np_ndarray([1])
consume_ndarray_1(n)
def test_ndarray_empty():
n: ndarray[float, 1] = np_empty([1])
consume_ndarray_1(n)
def test_ndarray_zeros():
n: ndarray[float, 1] = np_zeros([1])
output_ndarray_float_1(n)
def test_ndarray_ones():
n: ndarray[float, 1] = np_ones([1])
output_ndarray_float_1(n)
def test_ndarray_full():
n_float: ndarray[float, 1] = np_full([1], 2.0)
output_ndarray_float_1(n_float)
n_i32: ndarray[int32, 1] = np_full([1], 2)
output_ndarray_int32_1(n_i32)
def test_ndarray_eye():
n: ndarray[float, 2] = np_eye(2)
output_ndarray_float_2(n)
def test_ndarray_identity():
n: ndarray[float, 2] = np_identity(2)
output_ndarray_float_2(n)
def test_ndarray_fill():
n: ndarray[float, 2] = np_empty([2, 2])
n.fill(1.0)
output_ndarray_float_2(n)
def test_ndarray_copy():
x: ndarray[float, 2] = np_identity(2)
y = x.copy()
x.fill(0.0)
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_neg_idx():
x = np_identity(2)
for i in range(-1, -3, -1):
for j in range(-1, -3, -1):
output_float64(x[i][j])
def test_ndarray_add():
x = np_identity(2)
y = x + np_ones([2, 2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_add_broadcast():
x = np_identity(2)
# y: ndarray[float, 2] = x + np_ones([2])
y = x + np_ones([2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_add_broadcast_lhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = 1.0 + x
y = 1.0 + x
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_add_broadcast_rhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = x + 1.0
y = x + 1.0
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_iadd():
x = np_identity(2)
x += np_ones([2, 2])
output_ndarray_float_2(x)
def test_ndarray_iadd_broadcast():
x = np_identity(2)
x += np_ones([2])
output_ndarray_float_2(x)
def test_ndarray_iadd_broadcast_scalar():
x = np_identity(2)
x += 1.0
output_ndarray_float_2(x)
def test_ndarray_sub():
x = np_ones([2, 2])
y = x - np_identity(2)
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_sub_broadcast():
x = np_identity(2)
# y: ndarray[float, 2] = x - np_ones([2])
y = x - np_ones([2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_sub_broadcast_lhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = 1.0 - x
y = 1.0 - x
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_sub_broadcast_rhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = x - 1
y = x - 1.0
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_isub():
x = np_ones([2, 2])
x -= np_identity(2)
output_ndarray_float_2(x)
def test_ndarray_isub_broadcast():
x = np_identity(2)
x -= np_ones([2])
output_ndarray_float_2(x)
def test_ndarray_isub_broadcast_scalar():
x = np_identity(2)
x -= 1.0
output_ndarray_float_2(x)
def test_ndarray_mul():
x = np_ones([2, 2])
y = x * np_identity(2)
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_mul_broadcast():
x = np_identity(2)
# y: ndarray[float, 2] = x * np_ones([2])
y = x * np_ones([2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_mul_broadcast_lhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = 2.0 * x
y = 2.0 * x
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_mul_broadcast_rhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = x * 2.0
y = x * 2.0
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_imul():
x = np_ones([2, 2])
x *= np_identity(2)
output_ndarray_float_2(x)
def test_ndarray_imul_broadcast():
x = np_identity(2)
x *= np_ones([2])
output_ndarray_float_2(x)
def test_ndarray_imul_broadcast_scalar():
x = np_identity(2)
x *= 2.0
output_ndarray_float_2(x)
def test_ndarray_truediv():
x = np_identity(2)
y = x / np_ones([2, 2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_truediv_broadcast():
x = np_identity(2)
# y: ndarray[float, 2] = x / np_ones([2])
y = x / np_ones([2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_truediv_broadcast_lhs_scalar():
x = np_ones([2, 2])
# y: ndarray[float, 2] = 2.0 / x
y = 2.0 / x
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_truediv_broadcast_rhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = x / 2.0
y = x / 2.0
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_itruediv():
x = np_identity(2)
x /= np_ones([2, 2])
output_ndarray_float_2(x)
def test_ndarray_itruediv_broadcast():
x = np_identity(2)
x /= np_ones([2])
output_ndarray_float_2(x)
def test_ndarray_itruediv_broadcast_scalar():
x = np_identity(2)
x /= 2.0
output_ndarray_float_2(x)
def test_ndarray_floordiv():
x = np_identity(2)
y = x // np_ones([2, 2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_floordiv_broadcast():
x = np_identity(2)
# y: ndarray[float, 2] = x // np_ones([2])
y = x // np_ones([2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_floordiv_broadcast_lhs_scalar():
x = np_ones([2, 2])
# y: ndarray[float, 2] = 2.0 // x
y = 2.0 // x
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_floordiv_broadcast_rhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = x // 2.0
y = x // 2.0
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_ifloordiv():
x = np_identity(2)
x //= np_ones([2, 2])
output_ndarray_float_2(x)
def test_ndarray_ifloordiv_broadcast():
x = np_identity(2)
x //= np_ones([2])
output_ndarray_float_2(x)
def test_ndarray_ifloordiv_broadcast_scalar():
x = np_identity(2)
x //= 2.0
output_ndarray_float_2(x)
def test_ndarray_mod():
x = np_identity(2)
y = x % np_full([2, 2], 2.0)
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_mod_broadcast():
x = np_identity(2)
# y: ndarray[float, 2] = x % np_ones([2])
y = x % np_full([2], 2.0)
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_mod_broadcast_lhs_scalar():
x = np_ones([2, 2])
# y: ndarray[float, 2] = 2.0 % x
y = 2.0 % x
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_mod_broadcast_rhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = x % 2.0
y = x % 2.0
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_imod():
x = np_identity(2)
x %= np_full([2, 2], 2.0)
output_ndarray_float_2(x)
def test_ndarray_imod_broadcast():
x = np_identity(2)
x %= np_full([2], 2.0)
output_ndarray_float_2(x)
def test_ndarray_imod_broadcast_scalar():
x = np_identity(2)
x %= 2.0
output_ndarray_float_2(x)
def test_ndarray_pow():
x = np_identity(2)
y = x ** np_full([2, 2], 2.0)
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_pow_broadcast():
x = np_identity(2)
# y: ndarray[float, 2] = x ** np_full([2], 2.0)
y = x ** np_full([2], 2.0)
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_pow_broadcast_lhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = 2.0 ** x
y = 2.0 ** x
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_pow_broadcast_rhs_scalar():
x = np_identity(2)
# y: ndarray[float, 2] = x % 2.0
y = x ** 2.0
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_ipow():
x = np_identity(2)
x **= np_full([2, 2], 2.0)
output_ndarray_float_2(x)
def test_ndarray_ipow_broadcast():
x = np_identity(2)
x **= np_full([2], 2.0)
output_ndarray_float_2(x)
def test_ndarray_ipow_broadcast_scalar():
x = np_identity(2)
x **= 2.0
output_ndarray_float_2(x)
def test_ndarray_matmul():
x = np_identity(2)
y = x @ np_ones([2, 2])
output_ndarray_float_2(x)
output_ndarray_float_2(y)
def test_ndarray_imatmul():
x = np_identity(2)
x @= np_ones([2, 2])
output_ndarray_float_2(x)
def test_ndarray_pos():
x_int32 = np_full([2, 2], -2)
y_int32 = +x_int32
output_ndarray_int32_2(x_int32)
output_ndarray_int32_2(y_int32)
x_float = np_full([2, 2], -2.0)
y_float = +x_float
output_ndarray_float_2(x_float)
output_ndarray_float_2(y_float)
def test_ndarray_neg():
x_int32 = np_full([2, 2], -2)
y_int32 = -x_int32
output_ndarray_int32_2(x_int32)
output_ndarray_int32_2(y_int32)
x_float = np_full([2, 2], 2.0)
y_float = -x_float
output_ndarray_float_2(x_float)
output_ndarray_float_2(y_float)
def test_ndarray_inv():
x_int32 = np_full([2, 2], -2)
y_int32 = ~x_int32
output_ndarray_int32_2(x_int32)
output_ndarray_int32_2(y_int32)
x_bool = np_full([2, 2], True)
y_bool = ~x_bool
output_ndarray_bool_2(x_bool)
output_ndarray_bool_2(y_bool)
def test_ndarray_eq():
x = np_identity(2)
y = x == np_full([2, 2], 0.0)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_eq_broadcast():
x = np_identity(2)
y = x == np_full([2], 0.0)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_eq_broadcast_lhs_scalar():
x = np_identity(2)
y = 0.0 == x
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_eq_broadcast_rhs_scalar():
x = np_identity(2)
y = x == 0.0
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_ne():
x = np_identity(2)
y = x != np_full([2, 2], 0.0)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_ne_broadcast():
x = np_identity(2)
y = x != np_full([2], 0.0)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_ne_broadcast_lhs_scalar():
x = np_identity(2)
y = 0.0 != x
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_ne_broadcast_rhs_scalar():
x = np_identity(2)
y = x != 0.0
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_lt():
x = np_identity(2)
y = x < np_full([2, 2], 1.0)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_lt_broadcast():
x = np_identity(2)
y = x < np_full([2], 1.0)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_lt_broadcast_lhs_scalar():
x = np_identity(2)
y = 1.0 < x
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_lt_broadcast_rhs_scalar():
x = np_identity(2)
y = x < 1.0
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_le():
x = np_identity(2)
y = x <= np_full([2, 2], 0.5)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_le_broadcast():
x = np_identity(2)
y = x <= np_full([2], 0.5)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_le_broadcast_lhs_scalar():
x = np_identity(2)
y = 0.5 <= x
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_le_broadcast_rhs_scalar():
x = np_identity(2)
y = x <= 0.5
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_gt():
x = np_identity(2)
y = x > np_full([2, 2], 0.0)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_gt_broadcast():
x = np_identity(2)
y = x > np_full([2], 0.0)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_gt_broadcast_lhs_scalar():
x = np_identity(2)
y = 0.0 > x
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_gt_broadcast_rhs_scalar():
x = np_identity(2)
y = x > 0.0
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_ge():
x = np_identity(2)
y = x >= np_full([2, 2], 0.5)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_ge_broadcast():
x = np_identity(2)
y = x >= np_full([2], 0.5)
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_ge_broadcast_lhs_scalar():
x = np_identity(2)
y = 0.5 >= x
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def test_ndarray_ge_broadcast_rhs_scalar():
x = np_identity(2)
y = x >= 0.5
output_ndarray_float_2(x)
output_ndarray_bool_2(y)
def run() -> int32:
test_ndarray_ctor()
test_ndarray_empty()
test_ndarray_zeros()
test_ndarray_ones()
test_ndarray_full()
test_ndarray_eye()
test_ndarray_identity()
test_ndarray_fill()
test_ndarray_copy()
test_ndarray_neg_idx()
test_ndarray_add()
test_ndarray_add_broadcast()
test_ndarray_add_broadcast_lhs_scalar()
test_ndarray_add_broadcast_rhs_scalar()
test_ndarray_iadd()
test_ndarray_iadd_broadcast()
test_ndarray_iadd_broadcast_scalar()
test_ndarray_sub()
test_ndarray_sub_broadcast()
test_ndarray_sub_broadcast_lhs_scalar()
test_ndarray_sub_broadcast_rhs_scalar()
test_ndarray_isub()
test_ndarray_isub_broadcast()
test_ndarray_isub_broadcast_scalar()
test_ndarray_mul()
test_ndarray_mul_broadcast()
test_ndarray_mul_broadcast_lhs_scalar()
test_ndarray_mul_broadcast_rhs_scalar()
test_ndarray_imul()
test_ndarray_imul_broadcast()
test_ndarray_imul_broadcast_scalar()
test_ndarray_truediv()
test_ndarray_truediv_broadcast()
test_ndarray_truediv_broadcast_lhs_scalar()
test_ndarray_truediv_broadcast_rhs_scalar()
test_ndarray_itruediv()
test_ndarray_itruediv_broadcast()
test_ndarray_itruediv_broadcast_scalar()
test_ndarray_floordiv()
test_ndarray_floordiv_broadcast()
test_ndarray_floordiv_broadcast_lhs_scalar()
test_ndarray_floordiv_broadcast_rhs_scalar()
test_ndarray_ifloordiv()
test_ndarray_ifloordiv_broadcast()
test_ndarray_ifloordiv_broadcast_scalar()
test_ndarray_mod()
test_ndarray_mod_broadcast()
test_ndarray_mod_broadcast_lhs_scalar()
test_ndarray_mod_broadcast_rhs_scalar()
test_ndarray_imod()
test_ndarray_imod_broadcast()
test_ndarray_imod_broadcast_scalar()
test_ndarray_pow()
test_ndarray_pow_broadcast()
test_ndarray_pow_broadcast_lhs_scalar()
test_ndarray_pow_broadcast_rhs_scalar()
test_ndarray_ipow()
test_ndarray_ipow_broadcast()
test_ndarray_ipow_broadcast_scalar()
test_ndarray_matmul()
test_ndarray_imatmul()
test_ndarray_pos()
test_ndarray_neg()
test_ndarray_inv()
test_ndarray_eq()
test_ndarray_eq_broadcast()
test_ndarray_eq_broadcast_lhs_scalar()
test_ndarray_eq_broadcast_rhs_scalar()
test_ndarray_ne()
test_ndarray_ne_broadcast()
test_ndarray_ne_broadcast_lhs_scalar()
test_ndarray_ne_broadcast_rhs_scalar()
test_ndarray_lt()
test_ndarray_lt_broadcast()
test_ndarray_lt_broadcast_lhs_scalar()
test_ndarray_lt_broadcast_rhs_scalar()
test_ndarray_lt()
test_ndarray_le_broadcast()
test_ndarray_le_broadcast_lhs_scalar()
test_ndarray_le_broadcast_rhs_scalar()
test_ndarray_gt()
test_ndarray_gt_broadcast()
test_ndarray_gt_broadcast_lhs_scalar()
test_ndarray_gt_broadcast_rhs_scalar()
test_ndarray_gt()
test_ndarray_ge_broadcast()
test_ndarray_ge_broadcast_lhs_scalar()
test_ndarray_ge_broadcast_rhs_scalar()
return 0

View File

@ -0,0 +1,184 @@
@extern
def output_bool(x: bool):
...
@extern
def output_int32(x: int32):
...
@extern
def output_int64(x: int64):
...
@extern
def output_uint32(x: uint32):
...
@extern
def output_uint64(x: uint64):
...
@extern
def output_float64(x: float):
...
def u32_min() -> uint32:
return uint32(0)
def u32_max() -> uint32:
return ~uint32(0)
def i32_min() -> int32:
return int32(1 << 31)
def i32_max() -> int32:
return int32(~(1 << 31))
def u64_min() -> uint64:
return uint64(0)
def u64_max() -> uint64:
return ~uint64(0)
def i64_min() -> int64:
return int64(1) << 63
def i64_max() -> int64:
return ~(int64(1) << 63)
def test_u32_bnot():
output_uint32(~uint32(0))
def test_u64_bnot():
output_uint64(~uint64(0))
def test_conv_from_i32():
for x in [
i32_min(),
i32_min() + 1,
-1,
0,
1,
i32_max() - 1,
i32_max()
]:
output_int64(int64(x))
output_uint32(uint32(x))
output_uint64(uint64(x))
output_float64(float(x))
def test_conv_from_u32():
for x in [
u32_min(),
u32_min() + uint32(1),
u32_max() - uint32(1),
u32_max()
]:
output_uint64(uint64(x))
output_int32(int32(x))
output_int64(int64(x))
output_float64(float(x))
def test_conv_from_i64():
for x in [
i64_min(),
i64_min() + int64(1),
int64(-1),
int64(0),
int64(1),
i64_max() - int64(1),
i64_max()
]:
output_int32(int32(x))
output_uint64(uint64(x))
output_uint32(uint32(x))
output_float64(float(x))
def test_conv_from_u64():
for x in [
u64_min(),
u64_min() + uint64(1),
u64_max() - uint64(1),
u64_max()
]:
output_uint32(uint32(x))
output_int64(int64(x))
output_int32(int32(x))
output_float64(float(x))
def test_f64toi32():
for x in [
float(i32_min()) - 1.0,
float(i32_min()),
float(i32_min()) + 1.0,
-1.5,
-0.5,
0.5,
1.5,
float(i32_max()) - 1.0,
float(i32_max()),
float(i32_max()) + 1.0
]:
output_int32(int32(x))
def test_f64toi64():
for x in [
float(i64_min()),
float(i64_min()) + 1.0,
-1.5,
-0.5,
0.5,
1.5,
# 2^53 is the highest integral power-of-two of which uint64 and float have a one-to-one correspondence
float(uint64(2) ** uint64(52)) - 1.0,
float(uint64(2) ** uint64(52)),
float(uint64(2) ** uint64(52)) + 1.0,
]:
output_int64(int64(x))
def test_f64tou32():
for x in [
-1.5,
float(u32_min()) - 1.0,
-0.5,
float(u32_min()),
0.5,
float(u32_min()) + 1.0,
1.5,
float(u32_max()) - 1.0,
float(u32_max()),
float(u32_max()) + 1.0
]:
output_uint32(uint32(x))
def test_f64tou64():
for x in [
-1.5,
float(u64_min()) - 1.0,
-0.5,
float(u64_min()),
0.5,
float(u64_min()) + 1.0,
1.5,
# 2^53 is the highest integral power-of-two of which uint64 and float have a one-to-one correspondence
float(uint64(2) ** uint64(52)) - 1.0,
float(uint64(2) ** uint64(52)),
float(uint64(2) ** uint64(52)) + 1.0,
]:
output_uint64(uint64(x))
def run() -> int32:
test_u32_bnot()
test_u64_bnot()
test_conv_from_i32()
test_conv_from_u32()
test_conv_from_i64()
test_conv_from_u64()
test_f64toi32()
test_f64toi64()
test_f64tou32()
test_f64tou64()
return 0

View File

@ -0,0 +1,281 @@
from __future__ import annotations
@extern
def output_bool(x: bool):
...
@extern
def output_int32(x: int32):
...
@extern
def output_uint32(x: uint32):
...
@extern
def output_int64(x: int64):
...
@extern
def output_uint64(x: uint64):
...
@extern
def output_float64(x: float):
...
def run() -> int32:
test_bool()
test_int32()
test_uint32()
test_int64()
test_uint64()
# test_A()
# test_B()
return 0
def test_bool():
t = True
f = False
output_bool(not t)
output_bool(not f)
output_int32(~t)
output_int32(~f)
output_int32(+t)
output_int32(+f)
output_int32(-t)
output_int32(-f)
def test_int32():
a = 17
b = 3
output_int32(a + b)
output_int32(a - b)
output_int32(a * b)
output_int32(a // b)
output_int32(a % b)
output_int32(a | b)
output_int32(a ^ b)
output_int32(a & b)
output_int32(a << b)
output_int32(a << uint32(b))
output_int32(a >> b)
output_int32(a >> uint32(b))
output_float64(a / b)
a += b
output_int32(a)
a -= b
output_int32(a)
a *= b
output_int32(a)
a //= b
output_int32(a)
a %= b
output_int32(a)
a |= b
output_int32(a)
a ^= b
output_int32(a)
a &= b
output_int32(a)
a <<= b
output_int32(a)
a >>= b
output_int32(a)
# fail because (a / b) is float
# a /= b
def test_uint32():
a = uint32(17)
b = uint32(3)
output_uint32(a + b)
output_uint32(a - b)
output_uint32(a * b)
output_uint32(a // b)
output_uint32(a % b)
output_uint32(a | b)
output_uint32(a ^ b)
output_uint32(a & b)
output_uint32(a << b)
output_uint32(a << int32(b))
output_uint32(a >> b)
output_uint32(a >> int32(b))
output_float64(a / b)
a += b
output_uint32(a)
a -= b
output_uint32(a)
a *= b
output_uint32(a)
a //= b
output_uint32(a)
a %= b
output_uint32(a)
a |= b
output_uint32(a)
a ^= b
output_uint32(a)
a &= b
output_uint32(a)
a <<= b
output_uint32(a)
a >>= b
output_uint32(a)
def test_int64():
a = int64(17)
b = int64(3)
output_int64(a + b)
output_int64(a - b)
output_int64(a * b)
output_int64(a // b)
output_int64(a % b)
output_int64(a | b)
output_int64(a ^ b)
output_int64(a & b)
output_int64(a << int32(b))
output_int64(a << uint32(b))
output_int64(a >> int32(b))
output_int64(a >> uint32(b))
output_float64(a / b)
a += b
output_int64(a)
a -= b
output_int64(a)
a *= b
output_int64(a)
a //= b
output_int64(a)
a %= b
output_int64(a)
a |= b
output_int64(a)
a ^= b
output_int64(a)
a &= b
output_int64(a)
a <<= int32(b)
output_int64(a)
a >>= int32(b)
output_int64(a)
def test_uint64():
a = uint64(17)
b = uint64(3)
output_uint64(a + b)
output_uint64(a - b)
output_uint64(a * b)
output_uint64(a // b)
output_uint64(a % b)
output_uint64(a | b)
output_uint64(a ^ b)
output_uint64(a & b)
output_uint64(a << uint32(b))
output_uint64(a >> uint32(b))
output_float64(a / b)
a += b
output_uint64(a)
a -= b
output_uint64(a)
a *= b
output_uint64(a)
a //= b
output_uint64(a)
a %= b
output_uint64(a)
a |= b
output_uint64(a)
a ^= b
output_uint64(a)
a &= b
output_uint64(a)
a <<= uint32(b)
output_uint64(a)
a >>= uint32(b)
output_uint64(a)
# FIXME Fix returning objects of non-primitive types; Currently this is disabled in the function checker
# class A:
# a: int32
# def __init__(self, a: int32):
# self.a = a
#
# def __add__(self, other: A) -> A:
# output_int32(self.a + other.a)
# return A(self.a + other.a)
#
# def __sub__(self, other: A) -> A:
# output_int32(self.a - other.a)
# return A(self.a - other.a)
#
# def test_A():
# a = A(17)
# b = A(3)
#
# c = a + b
# # fail due to alloca in __add__ function
# # output_int32(c.a)
#
# a += b
# # fail due to alloca in __add__ function
# # output_int32(a.a)
#
# a = A(17)
# b = A(3)
# d = a - b
# # fail due to alloca in __add__ function
# # output_int32(c.a)
#
# a -= b
# # fail due to alloca in __add__ function
# # output_int32(a.a)
#
# a = A(17)
# b = A(3)
# a.__add__(b)
# a.__sub__(b)
#
#
# class B:
# a: int32
# def __init__(self, a: int32):
# self.a = a
#
# def __add__(self, other: B) -> B:
# output_int32(self.a + other.a)
# return B(self.a + other.a)
#
# def __sub__(self, other: B) -> B:
# output_int32(self.a - other.a)
# return B(self.a - other.a)
#
# def __iadd__(self, other: B) -> B:
# output_int32(self.a + other.a + 24)
# return B(self.a + other.a + 24)
#
# def __isub__(self, other: B) -> B:
# output_int32(self.a - other.a - 24)
# return B(self.a - other.a - 24)
#
# def test_B():
# a = B(17)
# b = B(3)
#
# c = a + b
# # fail due to alloca in __add__ function
# # output_int32(c.a)
#
# a += b
# # fail due to alloca in __add__ function
# # output_int32(a.a)
#
# a = B(17)
# b = B(3)
# d = a - b
# # fail due to alloca in __add__ function
# # output_int32(c.a)
#
# a -= b
# # fail due to alloca in __add__ function
# # output_int32(a.a)
#
# a = B(17)
# b = B(3)
# a.__add__(b)
# a.__sub__(b)

View File

@ -0,0 +1,36 @@
from __future__ import annotations
@extern
def output_int32(a: int32):
...
class A:
d: int32
a: list[B]
def __init__(self, b: list[B]):
self.d = 123
self.a = b
def f(self):
output_int32(self.d)
class B:
a: A
def __init__(self, a: A):
self.a = a
def ff(self):
self.a.f()
class Demo:
a: A
def __init__(self, a: A):
self.a = a
def run() -> int32:
aa = A([])
bb = B(aa)
aa.a = [bb]
d = Demo(aa)
d.a.a[0].ff()
return 0

View File

@ -0,0 +1,15 @@
@extern
def output_bool(x: bool):
...
@extern
def dbg_stack_address(x: str) -> uint64:
...
def run() -> int32:
a = dbg_stack_address("a")
b = dbg_stack_address("b")
output_bool(a == b)
return 0

View File

@ -0,0 +1,26 @@
def run() -> int32:
# Numeric Primitives
b: bool = False
i32: int32 = 0
i64: int64 = int64(0)
u32: uint32 = uint32(0)
u64: uint64 = uint64(0)
f64: float = 0.0
# String
s: str = ""
# List
l_i32: list[int32] = []
l_f64: list[float] = []
l_str: list[str] = []
# Option
o_some: Option[int32] = Some(0)
o_none: Option[int32] = none
# Tuple
t_i32_i32: tuple[int32, int32] = (0, 0)
t_i32_f64: tuple[int32, float] = (0, 0.0)
return 0

View File

@ -34,5 +34,9 @@ def run() -> int32:
insta = A()
inst = C(insta)
inst.foo()
insta2 = B()
inst2 = C(insta2)
inst2.foo()
return 0

View File

@ -10,6 +10,7 @@ use nac3core::{
use nac3parser::ast::{self, StrRef};
use parking_lot::{Mutex, RwLock};
use std::{collections::HashMap, sync::Arc};
use std::collections::HashSet;
pub struct ResolverInternal {
pub id_to_type: Mutex<HashMap<StrRef, Type>>,
@ -50,19 +51,22 @@ impl SymbolResolver for Resolver {
_: &PrimitiveStore,
str: StrRef,
) -> Result<Type, String> {
self.0.id_to_type.lock().get(&str).cloned().ok_or(format!("cannot get type of {}", str))
self.0.id_to_type.lock().get(&str).copied().ok_or(format!("cannot get type of {str}"))
}
fn get_symbol_value<'ctx, 'a>(
fn get_symbol_value<'ctx>(
&self,
_: StrRef,
_: &mut CodeGenContext<'ctx, 'a>,
_: &mut CodeGenContext<'ctx, '_>,
) -> Option<ValueEnum<'ctx>> {
unimplemented!()
}
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Undefined identifier".to_string())
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
self.0.id_to_def.lock().get(&id).copied()
.ok_or_else(|| HashSet::from([
format!("Undefined identifier `{id}`"),
]))
}
fn get_string_id(&self, s: &str) -> i32 {
@ -70,7 +74,8 @@ impl SymbolResolver for Resolver {
if let Some(id) = str_store.get(s) {
*id
} else {
let id = str_store.len() as i32;
let id = i32::try_from(str_store.len())
.expect("Symbol resolver string store size exceeds max capacity (i32::MAX)");
str_store.insert(s.to_string(), id);
id
}

View File

@ -1,220 +1,353 @@
use clap::Parser;
use inkwell::{
memory_buffer::MemoryBuffer,
passes::{PassManager, PassManagerBuilder},
passes::PassBuilderOptions,
support::is_multithreaded,
targets::*,
OptimizationLevel,
};
use parking_lot::{Mutex, RwLock};
use std::{borrow::Borrow, collections::HashMap, env, fs, path::Path, sync::Arc};
use std::{collections::HashMap, fs, path::Path, sync::Arc};
use std::collections::HashSet;
use nac3core::{
codegen::{
concrete_type::ConcreteTypeStore, irrt::load_irrt, CodeGenTask, DefaultCodeGenerator,
WithCall, WorkerRegistry,
concrete_type::ConcreteTypeStore, irrt::load_irrt, CodeGenLLVMOptions,
CodeGenTargetMachineOptions, CodeGenTask, DefaultCodeGenerator, WithCall, WorkerRegistry,
},
symbol_resolver::SymbolResolver,
toplevel::{
composer::TopLevelComposer, helper::parse_parameter_default_value, type_annotation::*,
composer::{ComposerConfig, TopLevelComposer},
helper::parse_parameter_default_value,
type_annotation::*,
TopLevelDef,
},
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{FunSignature, Type, Unifier},
typedef::{FunSignature, Type, Unifier, VarMap},
},
};
use nac3parser::{
ast::{Expr, ExprKind, StmtKind},
ast::{Constant, Expr, ExprKind, StmtKind, StrRef},
parser,
};
mod basic_symbol_resolver;
use basic_symbol_resolver::*;
/// Command-line argument parser definition.
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct CommandLineArgs {
/// The name of the input file.
file_name: String,
/// The number of threads allocated to processing the source file. If 0 is passed to this
/// parameter, all available threads will be used for compilation.
#[arg(short = 'T', default_value_t = 1)]
threads: u32,
/// The level to optimize the LLVM IR.
#[arg(short = 'O', default_value_t = 2, value_parser = clap::value_parser!(u32).range(0..=3))]
opt_level: u32,
/// Whether to emit LLVM IR at the end of every module.
///
/// If multithreaded compilation is also enabled, each thread will emit its own module.
#[arg(long, default_value_t = false)]
emit_llvm: bool,
/// The target triple to compile for.
#[arg(long)]
triple: Option<String>,
/// The target CPU to compile for.
#[arg(long)]
mcpu: Option<String>,
/// Additional target features to enable/disable, specified using the `+`/`-` prefixes.
#[arg(long)]
target_features: Option<String>,
}
fn handle_typevar_definition(
var: &Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> Result<Type, HashSet<String>> {
let ExprKind::Call { func, args, .. } = &var.node else {
return Err(HashSet::from([
format!(
"expression {var:?} cannot be handled as a generic parameter in global scope"
),
]))
};
match &func.node {
ExprKind::Name { id, .. } if id == &"TypeVar".into() => {
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
return Err(HashSet::from([
format!("Expected string constant for first parameter of `TypeVar`, got {:?}", &args[0].node),
]))
};
let generic_name: StrRef = ty_name.to_string().into();
let constraints = args
.iter()
.skip(1)
.map(|x| -> Result<Type, HashSet<String>> {
let ty = parse_ast_to_type_annotation_kinds(
resolver,
def_list,
unifier,
primitives,
x,
HashMap::default(),
)?;
get_type_from_type_annotation_kinds(
def_list, unifier, &ty, &mut None
)
})
.collect::<Result<Vec<_>, _>>()?;
let loc = func.location;
if constraints.len() == 1 {
return Err(HashSet::from([
format!("A single constraint is not allowed (at {loc})"),
]))
}
Ok(unifier.get_fresh_var_with_range(&constraints, Some(generic_name), Some(loc)).0)
}
ExprKind::Name { id, .. } if id == &"ConstGeneric".into() => {
if args.len() != 2 {
return Err(HashSet::from([
format!("Expected 2 arguments for `ConstGeneric`, got {}", args.len()),
]))
}
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
return Err(HashSet::from([
format!(
"Expected string constant for first parameter of `ConstGeneric`, got {:?}",
&args[0].node
),
]))
};
let generic_name: StrRef = ty_name.to_string().into();
let ty = parse_ast_to_type_annotation_kinds(
resolver,
def_list,
unifier,
primitives,
&args[1],
HashMap::default(),
)?;
let constraint = get_type_from_type_annotation_kinds(
def_list, unifier, &ty, &mut None
)?;
let loc = func.location;
Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).0)
}
_ => Err(HashSet::from([
format!(
"expression {var:?} cannot be handled as a generic parameter in global scope"
),
]))
}
}
fn handle_assignment_pattern(
targets: &[Expr],
value: &Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
internal_resolver: &ResolverInternal,
def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> Result<(), String> {
if targets.len() == 1 {
match &targets[0].node {
ExprKind::Name { id, .. } => {
if let Ok(var) = handle_typevar_definition(
value,
resolver,
def_list,
unifier,
primitives,
) {
internal_resolver.add_id_type(*id, var);
Ok(())
} else if let Ok(val) =
parse_parameter_default_value(value, resolver)
{
internal_resolver.add_module_global(*id, val);
Ok(())
} else {
Err(format!("fails to evaluate this expression `{:?}` as a constant or generic parameter at {}",
targets[0].node,
targets[0].location,
))
}
}
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
handle_assignment_pattern(
elts,
value,
resolver,
internal_resolver,
def_list,
unifier,
primitives,
)?;
Ok(())
}
_ => Err(format!(
"assignment to {:?} is not supported at {}",
targets[0], targets[0].location
)),
}
} else {
match &value.node {
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
if elts.len() == targets.len() {
for (tar, val) in targets.iter().zip(elts) {
handle_assignment_pattern(
std::slice::from_ref(tar),
val,
resolver,
internal_resolver,
def_list,
unifier,
primitives,
)?;
}
Ok(())
} else {
Err(format!(
"number of elements to unpack does not match (expect {}, found {}) at {}",
targets.len(),
elts.len(),
value.location
))
}
}
_ => Err(format!(
"unpack of this expression is not supported at {}",
value.location
)),
}
}
}
fn main() {
let file_name = env::args().nth(1).unwrap();
let threads: u32 = env::args().nth(2).map(|s| str::parse(&s).unwrap()).unwrap_or(1);
const SIZE_T: u32 = usize::BITS;
let cli = CommandLineArgs::parse();
let CommandLineArgs {
file_name,
threads,
opt_level,
emit_llvm,
triple,
mcpu,
target_features,
} = cli;
Target::initialize_all(&InitializationConfig::default());
let host_target_machine = CodeGenTargetMachineOptions::from_host();
let triple = triple.unwrap_or(host_target_machine.triple.clone());
let mcpu = mcpu
.map(|arg| if arg == "native" { host_target_machine.cpu.clone() } else { arg })
.unwrap_or_default();
let target_features = target_features.unwrap_or_default();
let threads = if is_multithreaded() {
if threads == 0 {
std::thread::available_parallelism()
.map(|threads| threads.get() as u32)
.unwrap_or(1u32)
} else {
threads
}
} else {
if threads != 1 {
println!("Warning: Number of threads specified in command-line but multithreading is disabled in LLVM at build time! Defaulting to single-threaded compilation");
}
1
};
let opt_level = match opt_level {
0 => OptimizationLevel::None,
1 => OptimizationLevel::Less,
2 => OptimizationLevel::Default,
// The default behavior for -O<n> where n>3 defaults to O3 for both Clang and GCC
_ => OptimizationLevel::Aggressive,
};
let program = match fs::read_to_string(file_name.clone()) {
Ok(program) => program,
Err(err) => {
println!("Cannot open input file: {}", err);
println!("Cannot open input file: {err}");
return;
}
};
let primitive: PrimitiveStore = TopLevelComposer::make_primitives().0;
let primitive: PrimitiveStore = TopLevelComposer::make_primitives(SIZE_T).0;
let (mut composer, builtins_def, builtins_ty) =
TopLevelComposer::new(vec![], Default::default());
TopLevelComposer::new(vec![], ComposerConfig::default(), SIZE_T);
let internal_resolver: Arc<ResolverInternal> = ResolverInternal {
id_to_type: builtins_ty.into(),
id_to_def: builtins_def.into(),
class_names: Default::default(),
module_globals: Default::default(),
str_store: Default::default(),
}
.into();
class_names: Mutex::default(),
module_globals: Mutex::default(),
str_store: Mutex::default(),
}.into();
let resolver =
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
let parser_result = parser::parse_program(&program, file_name.into()).unwrap();
for stmt in parser_result.into_iter() {
if let StmtKind::Assign { targets, value, .. } = &stmt.node {
fn handle_typevar_definition(
var: &Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> Result<Type, String> {
if let ExprKind::Call { func, args, .. } = &var.node {
if matches!(&func.node, ExprKind::Name { id, .. } if id == &"TypeVar".into()) {
let constraints = args
.iter()
.skip(1)
.map(|x| -> Result<Type, String> {
let ty = parse_ast_to_type_annotation_kinds(
resolver,
def_list,
unifier,
primitives,
x,
Default::default(),
)?;
get_type_from_type_annotation_kinds(
def_list, unifier, primitives, &ty, &mut None
)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(unifier.get_fresh_var_with_range(&constraints, None, None).0)
} else {
Err(format!(
"expression {:?} cannot be handled as a TypeVar in global scope",
var
))
}
} else {
Err(format!(
"expression {:?} cannot be handled as a TypeVar in global scope",
var
))
for stmt in parser_result {
match &stmt.node {
StmtKind::Assign { targets, value, .. } => {
let def_list = composer.extract_def_list();
let unifier = &mut composer.unifier;
let primitives = &composer.primitives_ty;
if let Err(err) = handle_assignment_pattern(
targets,
value,
resolver.as_ref(),
internal_resolver.as_ref(),
&def_list,
unifier,
primitives,
) {
eprintln!("{err}");
return;
}
},
// allow (and ignore) "from __future__ import annotations"
StmtKind::ImportFrom { module, names, .. }
if module == &Some("__future__".into()) && names.len() == 1 && names[0].name == "annotations".into() => (),
_ => {
let (name, def_id, ty) =
composer.register_top_level(stmt, Some(resolver.clone()), "__main__", true).unwrap();
internal_resolver.add_id_def(name, def_id);
if let Some(ty) = ty {
internal_resolver.add_id_type(name, ty);
}
}
fn handle_assignment_pattern(
targets: &[Expr],
value: &Expr,
resolver: &(dyn SymbolResolver + Send + Sync),
internal_resolver: &ResolverInternal,
def_list: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> Result<(), String> {
if targets.len() == 1 {
match &targets[0].node {
ExprKind::Name { id, .. } => {
if let Ok(var) = handle_typevar_definition(
value.borrow(),
resolver,
def_list,
unifier,
primitives,
) {
internal_resolver.add_id_type(*id, var);
Ok(())
} else if let Ok(val) =
parse_parameter_default_value(value.borrow(), resolver)
{
internal_resolver.add_module_global(*id, val);
Ok(())
} else {
Err(format!("fails to evaluate this expression `{:?}` as a constant or TypeVar at {}",
targets[0].node,
targets[0].location,
))
}
}
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
handle_assignment_pattern(
elts,
value,
resolver,
internal_resolver,
def_list,
unifier,
primitives,
)?;
Ok(())
}
_ => Err(format!(
"assignment to {:?} is not supported at {}",
targets[0], targets[0].location
)),
}
} else {
match &value.node {
ExprKind::List { elts, .. } | ExprKind::Tuple { elts, .. } => {
if elts.len() != targets.len() {
Err(format!(
"number of elements to unpack does not match (expect {}, found {}) at {}",
targets.len(),
elts.len(),
value.location
))
} else {
for (tar, val) in targets.iter().zip(elts) {
handle_assignment_pattern(
std::slice::from_ref(tar),
val,
resolver,
internal_resolver,
def_list,
unifier,
primitives,
)?;
}
Ok(())
}
}
_ => Err(format!(
"unpack of this expression is not supported at {}",
value.location
)),
}
}
}
let def_list = composer.extract_def_list();
let unifier = &mut composer.unifier;
let primitives = &composer.primitives_ty;
if let Err(err) = handle_assignment_pattern(
targets,
value,
resolver.as_ref(),
internal_resolver.as_ref(),
&def_list,
unifier,
primitives,
) {
eprintln!("{}", err);
return;
}
continue;
}
let (name, def_id, ty) =
composer.register_top_level(stmt, Some(resolver.clone()), "__main__".into()).unwrap();
internal_resolver.add_id_def(name, def_id);
if let Some(ty) = ty {
internal_resolver.add_id_type(name, ty);
}
}
let signature = FunSignature { args: vec![], ret: primitive.int32, vars: HashMap::new() };
let signature = FunSignature { args: vec![], ret: primitive.int32, vars: VarMap::new() };
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature = store.from_signature(&mut composer.unifier, &primitive, &signature, &mut cache);
@ -231,16 +364,26 @@ fn main() {
.unwrap_or_else(|_| panic!("cannot find run() entry point"))
.0]
.write();
if let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } = &mut *instance {
instance_to_symbol.insert("".to_string(), "run".to_string());
instance_to_stmt[""].clone()
} else {
let TopLevelDef::Function { instance_to_stmt, instance_to_symbol, .. } = &mut *instance else {
unreachable!()
}
};
instance_to_symbol.insert(String::new(), "run".to_string());
instance_to_stmt[""].clone()
};
let llvm_options = CodeGenLLVMOptions {
opt_level,
target: CodeGenTargetMachineOptions {
triple,
cpu: mcpu,
features: target_features,
reloc_mode: RelocMode::PIC,
..host_target_machine
},
};
let task = CodeGenTask {
subst: Default::default(),
subst: Vec::default(),
symbol_name: "run".to_string(),
body: instance.body,
signature,
@ -251,7 +394,7 @@ fn main() {
id: 0,
};
let membuffers: Arc<Mutex<Vec<Vec<u8>>>> = Default::default();
let membuffers: Arc<Mutex<Vec<Vec<u8>>>> = Arc::default();
let membuffer = membuffers.clone();
let f = Arc::new(WithCall::new(Box::new(move |module| {
@ -260,9 +403,9 @@ fn main() {
membuffer.lock().push(buffer);
})));
let threads = (0..threads)
.map(|i| Box::new(DefaultCodeGenerator::new(format!("module{}", i), 64)))
.map(|i| Box::new(DefaultCodeGenerator::new(format!("module{i}"), SIZE_T)))
.collect();
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, f);
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level, &llvm_options, &f);
registry.add_task(task);
registry.wait_tasks_complete(handles);
@ -271,14 +414,27 @@ fn main() {
let main = context
.create_module_from_ir(MemoryBuffer::create_from_memory_range(&buffers[0], "main"))
.unwrap();
for buffer in buffers.iter().skip(1) {
if emit_llvm {
main.write_bitcode_to_path(Path::new("main.bc"));
}
for (idx, buffer) in buffers.iter().skip(1).enumerate() {
let other = context
.create_module_from_ir(MemoryBuffer::create_from_memory_range(buffer, "main"))
.unwrap();
if emit_llvm {
other.write_bitcode_to_path(Path::new(&format!("module{idx}.bc")));
}
main.link_in_module(other).unwrap();
}
main.link_in_module(load_irrt(&context)).unwrap();
let irrt = load_irrt(&context);
if emit_llvm {
irrt.write_bitcode_to_path(Path::new("irrt.bc"));
}
main.link_in_module(irrt).unwrap();
let mut function_iter = main.get_first_function();
while let Some(func) = function_iter {
@ -288,25 +444,18 @@ fn main() {
function_iter = func.get_next_function();
}
let builder = PassManagerBuilder::create();
builder.set_optimization_level(OptimizationLevel::Aggressive);
let passes = PassManager::create(());
builder.set_inliner_with_threshold(255);
builder.populate_module_pass_manager(&passes);
passes.run_on(&main);
let triple = TargetMachine::get_default_triple();
let target = Target::from_triple(&triple).expect("couldn't create target from target triple");
let target_machine = target
.create_target_machine(
&triple,
"",
"",
OptimizationLevel::Default,
RelocMode::Default,
CodeModel::Default,
)
let target_machine = llvm_options.target
.create_target_machine(llvm_options.opt_level)
.expect("couldn't create target machine");
let pass_options = PassBuilderOptions::create();
pass_options.set_merge_functions(true);
let passes = format!("default<O{}>", opt_level as u32);
let result = main.run_passes(passes.as_str(), &target_machine, pass_options);
if let Err(err) = result {
panic!("Failed to run optimization for module `main`: {}", err.to_string());
}
target_machine
.write_to_file(&main, FileType::Object, Path::new("module.o"))
.expect("couldn't write module to file");

View File

@ -8,7 +8,6 @@
, ncurses
, zlib
, which
, llvmPackages_13
, debugVersion ? false
, enableManpages ? false
, enableSharedLibraries ? false
@ -18,7 +17,7 @@
let
inherit (lib) optional optionals optionalString;
release_version = "13.0.1";
release_version = "14.0.6";
candidate = ""; # empty or "rcN"
dash-candidate = lib.optionalString (candidate != "") "-${candidate}";
version = "${release_version}${dash-candidate}"; # differentiating these (variables) is important for RCs
@ -35,7 +34,7 @@ in stdenv.mkDerivation (rec {
pname = "llvm";
inherit version;
src = fetch pname "sha256-7GuA2Cw4SsrS3BkpA6bPLNuv+4ibhL+5janXHmMPyDQ=";
src = fetch pname "sha256-BQki7KrKV4H99mMeqSvHFRg/IC+dLxUUcibwI0FPYZo=";
unpackPhase = ''
unpackFile $src
@ -50,19 +49,12 @@ in stdenv.mkDerivation (rec {
buildInputs = [ ];
propagatedBuildInputs = optionals (stdenv.buildPlatform == stdenv.hostPlatform) [ ncurses ]
++ [ zlib ];
propagatedBuildInputs = [ ncurses zlib ];
checkInputs = [ which ];
patches = [
./gnu-install-dirs.patch
# Fix random compiler crashes: https://bugs.llvm.org/show_bug.cgi?id=50611
(fetchpatch {
url = "https://raw.githubusercontent.com/archlinux/svntogit-packages/4764a4f8c920912a2bfd8b0eea57273acfe0d8a8/trunk/no-strict-aliasing-DwarfCompileUnit.patch";
sha256 = "18l6mrvm2vmwm77ckcnbjvh6ybvn72rhrb799d4qzwac4x2ifl7g";
stripLen = 1;
})
./llvm-future-riscv-abi.diff
];
@ -120,7 +112,9 @@ in stdenv.mkDerivation (rec {
"-DLLVM_HOST_TRIPLE=${stdenv.hostPlatform.config}"
"-DLLVM_DEFAULT_TARGET_TRIPLE=${stdenv.hostPlatform.config}"
"-DLLVM_ENABLE_UNWIND_TABLES=OFF"
"-DLLVM_ENABLE_THREADS=OFF"
"-DLLVM_ENABLE_THREADS=ON"
"-DLLVM_INCLUDE_BENCHMARKS=OFF"
"-DLLVM_BUILD_TOOLS=OFF"
"-DLLVM_TARGETS_TO_BUILD=X86;ARM;RISCV"
] ++ optionals enableSharedLibraries [
"-DLLVM_LINK_LLVM_DYLIB=ON"
@ -137,7 +131,6 @@ in stdenv.mkDerivation (rec {
"-DCAN_TARGET_i386=false"
] ++ optionals (stdenv.hostPlatform != stdenv.buildPlatform) [
"-DCMAKE_CROSSCOMPILING=True"
"-DLLVM_TABLEGEN=${llvmPackages_13.tools.llvm}/bin/llvm-tblgen"
(
let
nativeCC = pkgsBuildBuild.targetPackages.stdenv.cc;
@ -154,6 +147,7 @@ in stdenv.mkDerivation (rec {
] ++ extraCmakeFlags;
postBuild = ''
make llvm-config
rm -fR $out
'';
@ -162,6 +156,7 @@ in stdenv.mkDerivation (rec {
'';
postInstall = ''
cp bin/llvm-config $out/bin
mkdir -p $python/share
mv $out/share/opt-viewer $python/share/opt-viewer
moveToOutput "bin/llvm-config*" "$dev"

View File

@ -1,23 +1,10 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 135036f509d2..265c36f8211b 100644
index fec956091cd5..5a766f5c5d7c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -270,15 +270,21 @@ if (CMAKE_BUILD_TYPE AND
message(FATAL_ERROR "Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
endif()
+include(GNUInstallDirs)
+
set(LLVM_LIBDIR_SUFFIX "" CACHE STRING "Define suffix of library directory name (32/64)" )
-set(LLVM_TOOLS_INSTALL_DIR "bin" CACHE STRING "Path for binary subdirectory (defaults to 'bin')")
+set(LLVM_TOOLS_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING
+ "Path for binary subdirectory (defaults to 'bin')")
mark_as_advanced(LLVM_TOOLS_INSTALL_DIR)
set(LLVM_UTILS_INSTALL_DIR "${LLVM_TOOLS_INSTALL_DIR}" CACHE STRING
"Path to install LLVM utilities (enabled by LLVM_INSTALL_UTILS=ON) (defaults to LLVM_TOOLS_INSTALL_DIR)")
mark_as_advanced(LLVM_UTILS_INSTALL_DIR)
@@ -303,6 +303,9 @@ set(LLVM_EXAMPLES_INSTALL_DIR "examples" CACHE STRING
"Path for examples subdirectory (enabled by LLVM_BUILD_EXAMPLES=ON) (defaults to 'examples')")
mark_as_advanced(LLVM_EXAMPLES_INSTALL_DIR)
+set(LLVM_INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}/cmake/llvm" CACHE STRING
+ "Path for CMake subdirectory (defaults to lib/cmake/llvm)" )
@ -25,70 +12,22 @@ index 135036f509d2..265c36f8211b 100644
# They are used as destination of target generators.
set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin)
set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
@@ -581,9 +587,9 @@ option (LLVM_ENABLE_SPHINX "Use Sphinx to generate llvm documentation." OFF)
option (LLVM_ENABLE_OCAMLDOC "Build OCaml bindings documentation." ON)
option (LLVM_ENABLE_BINDINGS "Build bindings." ON)
-set(LLVM_INSTALL_DOXYGEN_HTML_DIR "share/doc/llvm/doxygen-html"
+set(LLVM_INSTALL_DOXYGEN_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/doxygen-html"
CACHE STRING "Doxygen-generated HTML documentation install directory")
-set(LLVM_INSTALL_OCAMLDOC_HTML_DIR "share/doc/llvm/ocaml-html"
+set(LLVM_INSTALL_OCAMLDOC_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/ocaml-html"
CACHE STRING "OCamldoc-generated HTML documentation install directory")
option (LLVM_BUILD_EXTERNAL_COMPILER_RT
@@ -1048,7 +1054,7 @@ endif()
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
install(DIRECTORY include/llvm include/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "*.def"
@@ -1059,7 +1065,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
)
install(DIRECTORY ${LLVM_INCLUDE_DIR}/llvm ${LLVM_INCLUDE_DIR}/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "*.def"
@@ -1073,13 +1079,13 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if (LLVM_INSTALL_MODULEMAPS)
install(DIRECTORY include/llvm include/llvm-c
- DESTINATION include
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
COMPONENT llvm-headers
FILES_MATCHING
PATTERN "module.modulemap"
)
install(FILES include/llvm/module.install.modulemap
- DESTINATION include/llvm
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm
COMPONENT llvm-headers
RENAME "module.extern.modulemap"
)
diff --git a/cmake/modules/AddLLVM.cmake b/cmake/modules/AddLLVM.cmake
index 9c2b85374307..5531ceeb2eeb 100644
index fed1fec7d72e..4baed19b9e98 100644
--- a/cmake/modules/AddLLVM.cmake
+++ b/cmake/modules/AddLLVM.cmake
@@ -818,9 +818,9 @@ macro(add_llvm_library name)
@@ -838,8 +838,8 @@ macro(add_llvm_library name)
get_target_export_arg(${name} LLVM export_to_llvmexports ${umbrella})
install(TARGETS ${name}
${export_to_llvmexports}
- LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
- RUNTIME DESTINATION bin COMPONENT ${name})
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT ${name}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${name})
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" COMPONENT ${name}
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" COMPONENT ${name}
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT ${name})
if (NOT LLVM_ENABLE_IDE)
add_llvm_install_targets(install-${name}
@@ -1036,7 +1036,7 @@ function(process_llvm_pass_plugins)
@@ -1056,7 +1056,7 @@ function(process_llvm_pass_plugins)
"set(LLVM_STATIC_EXTENSIONS ${LLVM_STATIC_EXTENSIONS})")
install(FILES
${llvm_cmake_builddir}/LLVMConfigExtensions.cmake
@ -97,16 +36,7 @@ index 9c2b85374307..5531ceeb2eeb 100644
COMPONENT cmake-exports)
set(ExtensionDef "${LLVM_BINARY_DIR}/include/llvm/Support/Extension.def")
@@ -1250,7 +1250,7 @@ macro(add_llvm_example name)
endif()
add_llvm_executable(${name} ${ARGN})
if( LLVM_BUILD_EXAMPLES )
- install(TARGETS ${name} RUNTIME DESTINATION examples)
+ install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples)
endif()
set_target_properties(${name} PROPERTIES FOLDER "Examples")
endmacro(add_llvm_example name)
@@ -1868,7 +1868,7 @@ function(llvm_install_library_symlink name dest type)
@@ -1902,7 +1902,7 @@ function(llvm_install_library_symlink name dest type)
set(full_name ${CMAKE_${type}_LIBRARY_PREFIX}${name}${CMAKE_${type}_LIBRARY_SUFFIX})
set(full_dest ${CMAKE_${type}_LIBRARY_PREFIX}${dest}${CMAKE_${type}_LIBRARY_SUFFIX})
@ -115,7 +45,7 @@ index 9c2b85374307..5531ceeb2eeb 100644
if(WIN32 AND "${type}" STREQUAL "SHARED")
set(output_dir bin)
endif()
@@ -1879,7 +1879,7 @@ function(llvm_install_library_symlink name dest type)
@@ -1913,7 +1913,7 @@ function(llvm_install_library_symlink name dest type)
endfunction()
@ -124,7 +54,7 @@ index 9c2b85374307..5531ceeb2eeb 100644
cmake_parse_arguments(ARG "ALWAYS_GENERATE" "COMPONENT" "" ${ARGN})
foreach(path ${CMAKE_MODULE_PATH})
if(EXISTS ${path}/LLVMInstallSymlink.cmake)
@@ -1902,7 +1902,7 @@ function(llvm_install_symlink name dest)
@@ -1936,7 +1936,7 @@ function(llvm_install_symlink name dest)
set(full_dest ${dest}${CMAKE_EXECUTABLE_SUFFIX})
install(SCRIPT ${INSTALL_SYMLINK}
@ -133,7 +63,7 @@ index 9c2b85374307..5531ceeb2eeb 100644
COMPONENT ${component})
if (NOT LLVM_ENABLE_IDE AND NOT ARG_ALWAYS_GENERATE)
@@ -1985,7 +1985,8 @@ function(add_llvm_tool_symlink link_name target)
@@ -2019,7 +2019,8 @@ function(add_llvm_tool_symlink link_name target)
endif()
if ((TOOL_IS_TOOLCHAIN OR NOT LLVM_INSTALL_TOOLCHAIN_ONLY) AND LLVM_BUILD_TOOLS)
@ -143,7 +73,7 @@ index 9c2b85374307..5531ceeb2eeb 100644
endif()
endif()
endfunction()
@@ -2114,9 +2115,9 @@ function(llvm_setup_rpath name)
@@ -2148,9 +2149,9 @@ function(llvm_setup_rpath name)
# Since BUILD_SHARED_LIBS is only recommended for use by developers,
# hardcode the rpath to build/install lib dir first in this mode.
# FIXME: update this when there is better solution.
@ -156,10 +86,10 @@ index 9c2b85374307..5531ceeb2eeb 100644
set_property(TARGET ${name} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,-z,origin ")
diff --git a/cmake/modules/AddOCaml.cmake b/cmake/modules/AddOCaml.cmake
index 554046b20edf..4d1ad980641e 100644
index 891c9e6d618c..8d963f3b0069 100644
--- a/cmake/modules/AddOCaml.cmake
+++ b/cmake/modules/AddOCaml.cmake
@@ -144,9 +144,9 @@ function(add_ocaml_library name)
@@ -147,9 +147,9 @@ function(add_ocaml_library name)
endforeach()
if( APPLE )
@ -171,86 +101,55 @@ index 554046b20edf..4d1ad980641e 100644
endif()
list(APPEND ocaml_flags "-ldopt" "-Wl,-rpath,${ocaml_rpath}")
diff --git a/cmake/modules/AddSphinxTarget.cmake b/cmake/modules/AddSphinxTarget.cmake
index e80c3b5c1cac..482f6d715ef5 100644
--- a/cmake/modules/AddSphinxTarget.cmake
+++ b/cmake/modules/AddSphinxTarget.cmake
@@ -90,7 +90,7 @@ function (add_sphinx_target builder project)
endif()
elseif (builder STREQUAL html)
string(TOUPPER "${project}" project_upper)
- set(${project_upper}_INSTALL_SPHINX_HTML_DIR "share/doc/${project}/html"
+ set(${project_upper}_INSTALL_SPHINX_HTML_DIR "${CMAKE_INSTALL_DOCDIR}/${project}/html"
CACHE STRING "HTML documentation install directory for ${project}")
# '/.' indicates: copy the contents of the directory directly into
diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt
index 51b6a4fdc284..4adc2acfc074 100644
index cea0c1df0a14..eedcd9450312 100644
--- a/cmake/modules/CMakeLists.txt
+++ b/cmake/modules/CMakeLists.txt
@@ -1,6 +1,6 @@
@@ -2,7 +2,7 @@ include(ExtendPath)
include(LLVMDistributionSupport)
include(FindPrefixFromConfig)
-set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm)
+set(LLVM_INSTALL_PACKAGE_DIR ${LLVM_INSTALL_CMAKE_DIR} CACHE STRING "Path for CMake subdirectory (defaults to 'cmake/llvm')")
set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}")
# First for users who use an installed LLVM, create the LLVMExports.cmake file.
@@ -109,13 +109,13 @@ foreach(p ${_count})
set(LLVM_CONFIG_CODE "${LLVM_CONFIG_CODE}
get_filename_component(LLVM_INSTALL_PREFIX \"\${LLVM_INSTALL_PREFIX}\" PATH)")
endforeach(p)
-set(LLVM_CONFIG_INCLUDE_DIRS "\${LLVM_INSTALL_PREFIX}/include")
+set(LLVM_CONFIG_INCLUDE_DIRS "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}")
set(LLVM_CONFIG_INCLUDE_DIR "${LLVM_CONFIG_INCLUDE_DIRS}")
set(LLVM_CONFIG_MAIN_INCLUDE_DIR "${LLVM_CONFIG_INCLUDE_DIRS}")
-set(LLVM_CONFIG_LIBRARY_DIRS "\${LLVM_INSTALL_PREFIX}/lib\${LLVM_LIBDIR_SUFFIX}")
+set(LLVM_CONFIG_LIBRARY_DIRS "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}\${LLVM_LIBDIR_SUFFIX}")
set(LLVM_CONFIG_CMAKE_DIR "\${LLVM_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}")
set(LLVM_CONFIG_BINARY_DIR "\${LLVM_INSTALL_PREFIX}")
-set(LLVM_CONFIG_TOOLS_BINARY_DIR "\${LLVM_INSTALL_PREFIX}/bin")
+set(LLVM_CONFIG_TOOLS_BINARY_DIR "\${LLVM_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}")
@@ -122,7 +122,7 @@ set(LLVM_CONFIG_INCLUDE_DIRS
)
list(REMOVE_DUPLICATES LLVM_CONFIG_INCLUDE_DIRS)
# Generate a default location for lit
if (LLVM_INSTALL_UTILS AND LLVM_BUILD_UTILS)
-extend_path(LLVM_CONFIG_LIBRARY_DIR "\${LLVM_INSTALL_PREFIX}" "lib\${LLVM_LIBDIR_SUFFIX}")
+extend_path(LLVM_CONFIG_LIBRARY_DIR "\${LLVM_INSTALL_PREFIX}" "${CMAKE_INSTALL_LIBDIR}\${LLVM_LIBDIR_SUFFIX}")
set(LLVM_CONFIG_LIBRARY_DIRS
"${LLVM_CONFIG_LIBRARY_DIR}"
# FIXME: Should there be other entries here?
diff --git a/cmake/modules/LLVMInstallSymlink.cmake b/cmake/modules/LLVMInstallSymlink.cmake
index 3e6a2c9a2648..52e14d955c60 100644
index b5c35f706cb7..9261ab797de6 100644
--- a/cmake/modules/LLVMInstallSymlink.cmake
+++ b/cmake/modules/LLVMInstallSymlink.cmake
@@ -4,7 +4,7 @@
@@ -6,7 +6,7 @@ include(GNUInstallDirs)
function(install_symlink name target outdir)
set(DESTDIR $ENV{DESTDIR})
- set(bindir "${DESTDIR}${CMAKE_INSTALL_PREFIX}/${outdir}/")
- set(bindir "${DESTDIR}${CMAKE_INSTALL_PREFIX}/${outdir}")
+ set(bindir "${DESTDIR}${outdir}/")
message(STATUS "Creating ${name}")
diff --git a/docs/CMake.rst b/docs/CMake.rst
index f1ac2c7d4934..c6e1469b5e13 100644
index 044ec8a4d39d..504d0eac3ade 100644
--- a/docs/CMake.rst
+++ b/docs/CMake.rst
@@ -202,7 +202,7 @@ CMake manual, or execute ``cmake --help-variable VARIABLE_NAME``.
@@ -224,7 +224,7 @@ description is in `LLVM-related variables`_ below.
**LLVM_LIBDIR_SUFFIX**:STRING
Extra suffix to append to the directory where libraries are to be
installed. On a 64-bit architecture, one could use ``-DLLVM_LIBDIR_SUFFIX=64``
- to install libraries to ``/usr/lib64``.
+ to install libraries to ``/usr/lib64``. See also ``CMAKE_INSTALL_LIBDIR``.
Rarely-used CMake variables
---------------------------
@@ -551,8 +551,8 @@ LLVM-specific variables
**LLVM_INSTALL_DOXYGEN_HTML_DIR**:STRING
The path to install Doxygen-generated HTML documentation to. This path can
- either be absolute or relative to the CMAKE_INSTALL_PREFIX. Defaults to
- `share/doc/llvm/doxygen-html`.
+ either be absolute or relative to the ``CMAKE_INSTALL_PREFIX``. Defaults to
+ `${CMAKE_INSTALL_DOCDIR}/${project}/doxygen-html`.
**LLVM_LINK_LLVM_DYLIB**:BOOL
If enabled, tools will be linked with the libLLVM shared library. Defaults
@@ -792,9 +792,11 @@ the ``cmake`` command or by setting it directly in ``ccmake`` or ``cmake-gui``).
**LLVM_PARALLEL_{COMPILE,LINK}_JOBS**:STRING
Building the llvm toolchain can use a lot of resources, particularly
@@ -910,9 +910,11 @@ the ``cmake`` command or by setting it directly in ``ccmake`` or ``cmake-gui``).
This file is available in two different locations.
@ -265,18 +164,6 @@ index f1ac2c7d4934..c6e1469b5e13 100644
* ``<LLVM_BUILD_ROOT>/lib/cmake/llvm/LLVMConfig.cmake`` where
``<LLVM_BUILD_ROOT>`` is the root of the LLVM build tree. **Note: this is only
diff --git a/examples/Bye/CMakeLists.txt b/examples/Bye/CMakeLists.txt
index bb96edb4b4bf..678c22fb43c8 100644
--- a/examples/Bye/CMakeLists.txt
+++ b/examples/Bye/CMakeLists.txt
@@ -14,6 +14,6 @@ if (NOT WIN32)
BUILDTREE_ONLY
)
- install(TARGETS ${name} RUNTIME DESTINATION examples)
+ install(TARGETS ${name} RUNTIME DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples)
set_target_properties(${name} PROPERTIES FOLDER "Examples")
endif()
diff --git a/include/llvm/CMakeLists.txt b/include/llvm/CMakeLists.txt
index b46319f24fc8..2feabd1954e4 100644
--- a/include/llvm/CMakeLists.txt
@ -289,93 +176,45 @@ index b46319f24fc8..2feabd1954e4 100644
+ configure_file(module.modulemap.build ${LLVM_INCLUDE_DIR}/module.modulemap COPYONLY)
endif (NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in
index ebe5b73a5c65..70c497be12f5 100644
index abbb8a450da6..70c497be12f5 100644
--- a/tools/llvm-config/BuildVariables.inc.in
+++ b/tools/llvm-config/BuildVariables.inc.in
@@ -23,6 +23,10 @@
@@ -23,7 +23,10 @@
#define LLVM_CXXFLAGS "@LLVM_CXXFLAGS@"
#define LLVM_BUILDMODE "@LLVM_BUILDMODE@"
#define LLVM_LIBDIR_SUFFIX "@LLVM_LIBDIR_SUFFIX@"
+#define LLVM_INSTALL_BINDIR "@CMAKE_INSTALL_BINDIR@"
+#define LLVM_INSTALL_LIBDIR "@CMAKE_INSTALL_LIBDIR@"
+#define LLVM_INSTALL_INCLUDEDIR "@CMAKE_INSTALL_INCLUDEDIR@"
#define LLVM_INSTALL_INCLUDEDIR "@CMAKE_INSTALL_INCLUDEDIR@"
+#define LLVM_INSTALL_CMAKEDIR "@LLVM_INSTALL_CMAKE_DIR@"
#define LLVM_TARGETS_BUILT "@LLVM_TARGETS_BUILT@"
#define LLVM_SYSTEM_LIBS "@LLVM_SYSTEM_LIBS@"
#define LLVM_BUILD_SYSTEM "@LLVM_BUILD_SYSTEM@"
diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp
index 1a2f04552d13..44fa7d3eec6b 100644
index 8ed88f33ead4..5e7184bab90d 100644
--- a/tools/llvm-config/llvm-config.cpp
+++ b/tools/llvm-config/llvm-config.cpp
@@ -357,12 +357,26 @@ int main(int argc, char **argv) {
("-I" + ActiveIncludeDir + " " + "-I" + ActiveObjRoot + "/include");
} else {
ActivePrefix = CurrentExecPrefix;
- ActiveIncludeDir = ActivePrefix + "/include";
- SmallString<256> path(StringRef(LLVM_TOOLS_INSTALL_DIR));
- sys::fs::make_absolute(ActivePrefix, path);
- ActiveBinDir = std::string(path.str());
@@ -363,12 +363,20 @@ int main(int argc, char **argv) {
ActiveIncludeDir = std::string(Path.str());
}
{
- SmallString<256> Path(LLVM_TOOLS_INSTALL_DIR);
+ SmallString<256> Path(LLVM_INSTALL_BINDIR);
sys::fs::make_absolute(ActivePrefix, Path);
ActiveBinDir = std::string(Path.str());
}
- ActiveLibDir = ActivePrefix + "/lib" + LLVM_LIBDIR_SUFFIX;
- ActiveCMakeDir = ActiveLibDir + "/cmake/llvm";
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_INCLUDEDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveIncludeDir = std::string(path.str());
+ SmallString<256> Path(LLVM_INSTALL_LIBDIR LLVM_LIBDIR_SUFFIX);
+ sys::fs::make_absolute(ActivePrefix, Path);
+ ActiveLibDir = std::string(Path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_BINDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveBinDir = std::string(path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_LIBDIR LLVM_LIBDIR_SUFFIX));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveLibDir = std::string(path.str());
+ }
+ {
+ SmallString<256> path(StringRef(LLVM_INSTALL_CMAKEDIR));
+ sys::fs::make_absolute(ActivePrefix, path);
+ ActiveCMakeDir = std::string(path.str());
+ SmallString<256> Path(LLVM_INSTALL_CMAKEDIR);
+ sys::fs::make_absolute(ActivePrefix, Path);
+ ActiveCMakeDir = std::string(Path.str());
+ }
ActiveIncludeOption = "-I" + ActiveIncludeDir;
}
diff --git a/tools/lto/CMakeLists.txt b/tools/lto/CMakeLists.txt
index 0af29ad762c5..37b99b83e35c 100644
--- a/tools/lto/CMakeLists.txt
+++ b/tools/lto/CMakeLists.txt
@@ -33,7 +33,7 @@ add_llvm_library(${LTO_LIBRARY_NAME} ${LTO_LIBRARY_TYPE} INSTALL_WITH_TOOLCHAIN
${SOURCES} DEPENDS intrinsics_gen)
install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/lto.h
- DESTINATION include/llvm-c
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm-c
COMPONENT LTO)
if (APPLE)
diff --git a/tools/opt-viewer/CMakeLists.txt b/tools/opt-viewer/CMakeLists.txt
index ead73ec13a8f..250362021f17 100644
--- a/tools/opt-viewer/CMakeLists.txt
+++ b/tools/opt-viewer/CMakeLists.txt
@@ -8,7 +8,7 @@ set (files
foreach (file ${files})
install(PROGRAMS ${file}
- DESTINATION share/opt-viewer
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/opt-viewer
COMPONENT opt-viewer)
endforeach (file)
diff --git a/tools/remarks-shlib/CMakeLists.txt b/tools/remarks-shlib/CMakeLists.txt
index 865436247270..ce1daa62f6ab 100644
--- a/tools/remarks-shlib/CMakeLists.txt
+++ b/tools/remarks-shlib/CMakeLists.txt
@@ -19,7 +19,7 @@ if(LLVM_ENABLE_PIC)
endif()
install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/Remarks.h
- DESTINATION include/llvm-c
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/llvm-c
COMPONENT Remarks)
if (APPLE)

View File

@ -1,16 +1,16 @@
pkgbase="mingw-w64-nac3artiq"
pkgname="mingw-w64-x86_64-nac3artiq"
pkgname="mingw-w64-clang-x86_64-nac3artiq"
pkgver=1.0
pkgrel=1
pkgdesc="New ARTIQ compiler 3"
arch=("any")
mingw_arch=("mingw64")
mingw_arch=("clang64")
url="https://m-labs.hk"
license=("LGPL")
source=("nac3artiq.pyd")
noextract=("nac3artiq.pyd")
sha256sums=("SKIP")
depends=("mingw-w64-x86_64-python")
depends=("mingw-w64-clang-x86_64-python")
prepare() {
true
@ -21,6 +21,6 @@ build() {
}
package() {
mkdir -p $pkgdir/mingw64/lib/python3.9/site-packages
cp ${srcdir}/nac3artiq.pyd $pkgdir/mingw64/lib/python3.9/site-packages
mkdir -p $pkgdir/clang64/lib/python3.11/site-packages
cp ${srcdir}/nac3artiq.pyd $pkgdir/clang64/lib/python3.11/site-packages
}

View File

@ -21,11 +21,11 @@ let
text =
''
implementation=CPython
version=3.9
version=3.11
shared=true
abi3=false
lib_name=python3.9
lib_dir=${msys2-env}/mingw64/lib
lib_name=python3.11
lib_dir=${msys2-env}/clang64/lib
pointer_width=64
build_flags=WITH_THREAD
suppress_build_script_link_lines=false
@ -34,14 +34,14 @@ let
in rec {
llvm-nac3 = pkgs.stdenvNoCC.mkDerivation rec {
pname = "llvm-nac3-msys2";
version = "13.0.1";
version = "14.0.6";
src-llvm = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/llvm-${version}.src.tar.xz";
sha256 = "sha256-7GuA2Cw4SsrS3BkpA6bPLNuv+4ibhL+5janXHmMPyDQ=";
sha256 = "sha256-BQki7KrKV4H99mMeqSvHFRg/IC+dLxUUcibwI0FPYZo=";
};
src-clang = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/clang-${version}.src.tar.xz";
sha256 = "sha256-eHqeLZn1yHIKoXc+S+AJRhzTDTvUD90kWR5HNGfJF8k=";
sha256 = "sha256-K1hHtqYxGLnv5chVSDY8gf/glrZsOzZ16VPiY0KuQDE=";
};
buildInputs = [ pkgs.wineWowPackages.stable ];
phases = [ "unpackPhase" "patchPhase" "configurePhase" "buildPhase" "installPhase" ];
@ -49,6 +49,7 @@ in rec {
''
mkdir llvm
tar xf ${src-llvm} -C llvm --strip-components=1
mv llvm/Modules/* llvm/cmake/modules # work around https://github.com/llvm/llvm-project/issues/53281
mkdir clang
tar xf ${src-clang} -C clang --strip-components=1
cd llvm
@ -60,15 +61,15 @@ in rec {
''
export HOME=`mktemp -d`
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin
export WINEPATH=Z:${msys2-env}/clang64/bin
${silenceFontconfig}
mkdir build
cd build
wine64 cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_UNWIND_TABLES=OFF -DLLVM_ENABLE_THREADS=OFF -DLLVM_TARGETS_TO_BUILD=X86\;ARM\;RISCV -DLLVM_LINK_LLVM_DYLIB=OFF -DLLVM_ENABLE_FFI=OFF -DFFI_INCLUDE_DIR=fck-cmake -DFFI_LIBRARY_DIR=fck-cmake -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_INSTALL_PREFIX=Z:$out
wine64 cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_UNWIND_TABLES=OFF -DLLVM_ENABLE_THREADS=ON -DLLVM_TARGETS_TO_BUILD=X86\;ARM\;RISCV -DLLVM_LINK_LLVM_DYLIB=OFF -DLLVM_ENABLE_FFI=OFF -DFFI_INCLUDE_DIR=fck-cmake -DFFI_LIBRARY_DIR=fck-cmake -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_ENABLE_PROJECTS=clang -DCMAKE_INSTALL_PREFIX=Z:$out
'';
buildPhase =
''
wine64 ninja
wine64 ninja -j $NIX_BUILD_CORES
'';
installPhase =
''
@ -76,18 +77,28 @@ in rec {
'';
dontFixup = true;
};
llvm-tools-irrt = pkgs.runCommandNoCC "llvm-tools-irrt" {}
''
mkdir -p $out/bin
ln -s ${llvm-nac3}/bin/clang.exe $out/bin/clang-irrt.exe
ln -s ${llvm-nac3}/bin/llvm-as.exe $out/bin/llvm-as-irrt.exe
'';
nac3artiq = pkgs.rustPlatform.buildRustPackage {
name = "nac3artiq-msys2";
src = ../../.;
cargoLock = { lockFile = ../../Cargo.lock; };
cargoLock = {
lockFile = ../../Cargo.lock;
};
nativeBuildInputs = [ pkgs.wineWowPackages.stable ];
buildPhase =
''
export HOME=`mktemp -d`
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin\;Z:${llvm-nac3}/bin
export WINEPATH=Z:${msys2-env}/clang64/bin\;Z:${llvm-nac3}/bin\;Z:${llvm-tools-irrt}/bin
${silenceFontconfig}
export PYO3_CONFIG_FILE=Z:${pyo3-mingw-config}
export CC=clang
export LLVM_SYS_140_PREFIX=Z:${llvm-nac3}
wine64 cargo build --release -p nac3artiq
'';
installPhase =
@ -96,6 +107,7 @@ in rec {
cp target/release/nac3artiq.dll $out/nac3artiq.pyd
echo file binary-dist $out/nac3artiq.pyd >> $out/nix-support/hydra-build-products
'';
doCheck = false; # https://git.m-labs.hk/M-Labs/nac3/issues/358
checkPhase =
''
wine64 cargo test --release
@ -120,42 +132,10 @@ in rec {
echo file msys2 $out/*.pkg.tar.zst >> $out/nix-support/hydra-build-products
'';
};
lld = pkgs.stdenvNoCC.mkDerivation rec {
pname = "lld-msys2";
version = "13.0.1";
src = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/lld-${version}.src.tar.xz";
sha256 = "sha256-Zmr3Rei/e2gFM7TRi3ox3HyrV1sebk0mGSK7r9lkTPs=";
};
buildInputs = [ pkgs.wineWowPackages.stable ];
phases = [ "unpackPhase" "patchPhase" "configurePhase" "buildPhase" "installPhase" ];
patches = [ ./lld-disable-macho.diff ];
configurePhase =
''
export HOME=`mktemp -d`
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin\;Z:${llvm-nac3}/bin
${silenceFontconfig}
mkdir build
cd build
wine64 cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=Z:$out
'';
buildPhase =
''
wine64 ninja
'';
installPhase =
''
mkdir -p $out $out/nix-support
cp bin/ld.lld.exe $out
echo file binary-dist $out/ld.lld.exe >> $out/nix-support/hydra-build-products
'';
dontFixup = true;
};
wine-msys2 = pkgs.writeShellScriptBin "wine-msys2"
''
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin\;Z:${llvm-nac3}/bin
export WINEPATH=Z:${msys2-env}/clang64/bin\;Z:${llvm-nac3}/bin\;Z:${llvm-tools-irrt}/bin
export PYO3_CONFIG_FILE=Z:${pyo3-mingw-config}
exec ${pkgs.wineWowPackages.stable}/bin/wine64 cmd
'';
@ -163,7 +143,7 @@ in rec {
''
export HOME=`mktemp -d`
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin
export WINEPATH=Z:${msys2-env}/clang64/bin
${silenceFontconfig}
exec ${pkgs.wineWowPackages.stable}/bin/wine64 $@
'';

View File

@ -1,36 +0,0 @@
diff '--color=auto' -Naur lld-13.0.1.src/CMakeLists.txt lld-13.0.1.src-new/CMakeLists.txt
--- lld-13.0.1.src/CMakeLists.txt 2022-01-21 05:31:59.000000000 +0800
+++ lld-13.0.1.src-new/CMakeLists.txt 2022-03-27 18:26:30.284921982 +0800
@@ -206,7 +206,6 @@
add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)
-add_subdirectory(MachO)
add_subdirectory(MinGW)
add_subdirectory(wasm)
diff '--color=auto' -Naur lld-13.0.1.src/tools/lld/CMakeLists.txt lld-13.0.1.src-new/tools/lld/CMakeLists.txt
--- lld-13.0.1.src/tools/lld/CMakeLists.txt 2022-01-21 05:31:59.000000000 +0800
+++ lld-13.0.1.src-new/tools/lld/CMakeLists.txt 2022-03-27 18:26:40.805046295 +0800
@@ -15,7 +15,6 @@
lldCOFF
lldDriver
lldELF
- lldMachO2
lldMinGW
lldWasm
)
diff '--color=auto' -Naur lld-13.0.1.src/tools/lld/lld.cpp lld-13.0.1.src-new/tools/lld/lld.cpp
--- lld-13.0.1.src/tools/lld/lld.cpp 2022-01-21 05:31:59.000000000 +0800
+++ lld-13.0.1.src-new/tools/lld/lld.cpp 2022-03-27 08:43:54.205524156 +0800
@@ -148,10 +148,6 @@
return !elf::link(args, exitEarly, stdoutOS, stderrOS);
case WinLink:
return !coff::link(args, exitEarly, stdoutOS, stderrOS);
- case Darwin:
- return !macho::link(args, exitEarly, stdoutOS, stderrOS);
- case DarwinOld:
- return !mach_o::link(args, exitEarly, stdoutOS, stderrOS);
case Wasm:
return !lld::wasm::link(args, exitEarly, stdoutOS, stderrOS);
default:

View File

@ -7,18 +7,21 @@ MSYS2DIR=`pwd`/msys2
mkdir -p $MSYS2DIR/var/lib/pacman $MSYS2DIR/msys/etc
curl -L https://mirror.msys2.org/msys/x86_64/pacman-mirrors-20220205-1-any.pkg.tar.zst | tar xvf - -C $MSYS2DIR --zstd
curl -L https://raw.githubusercontent.com/msys2/MSYS2-packages/master/pacman/pacman.conf | grep -v SigLevel | sed s\|/etc/pacman.d\|$MSYS2DIR/etc/pacman.d\|g > $MSYS2DIR/etc/pacman.conf
curl -L https://raw.githubusercontent.com/msys2/MSYS2-packages/master/pacman/pacman.conf | sed "s|SigLevel = Required|SigLevel = Never|g" | sed "s|/etc/pacman.d|$MSYS2DIR/etc/pacman.d|g" > $MSYS2DIR/etc/pacman.conf
fakeroot pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf -Syy
pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf --cachedir $MSYS2DIR/msys/cache -Sp mingw-w64-x86_64-rust mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-python3.9 mingw-w64-x86_64-python-numpy mingw-w64-x86_64-python-setuptools > $MSYS2DIR/packages.txt
pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf --cachedir $MSYS2DIR/msys/cache -Sp mingw-w64-clang-x86_64-rust mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-ninja mingw-w64-clang-x86_64-python3.11 mingw-w64-clang-x86_64-python-numpy mingw-w64-clang-x86_64-python-setuptools > $MSYS2DIR/packages.txt
echo "{ pkgs } : [" > msys2_packages.nix
while read package; do
hash=$(nix-prefetch-url $package)
basename=${package##*/}
name=${basename//\~/}
hash=$(nix-prefetch-url $package --name $name)
echo "
(pkgs.fetchurl {
url = \"$package\";
sha256 = \"$hash\";
name = \"$name\";
})" >> msys2_packages.nix
done < $MSYS2DIR/packages.txt
echo "]" >> msys2_packages.nix

View File

@ -1,307 +1,350 @@
{ pkgs } : [
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libiconv-1.16-2-any.pkg.tar.zst";
sha256 = "0nr8gaqz7vhjsqq8ys3z63bd62fz548r9n0sncz513ra04wg7la4";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libunwind-18.1.2-1-any.pkg.tar.zst";
sha256 = "0ksz7xz1lbwsmdr9sa1444k0dlfkbd8k11pq7w08ir7r1wjy6fid";
name = "mingw-w64-clang-x86_64-libunwind-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zlib-1.2.12-1-any.pkg.tar.zst";
sha256 = "1b461ic5s3hjk3y70ldik82ny08rdywn1zfqa8d2jyyvnh4dya77";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libc++-18.1.2-1-any.pkg.tar.zst";
sha256 = "0r8skyjqv4cpkqif0niakx4hdpkscil1zf6mzj34pqna0j5gdnq2";
name = "mingw-w64-clang-x86_64-libc++-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-binutils-2.38-2-any.pkg.tar.zst";
sha256 = "121jz2nmfk0qgkwjll8bg3kavmzpp14raid4az44p10vfdlla7f6";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libffi-3.4.6-1-any.pkg.tar.zst";
sha256 = "1q6gms980985bp087rnnpvz2fwfakgm5266izfk3b1mbp620s1yv";
name = "mingw-w64-clang-x86_64-libffi-3.4.6-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-headers-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "0wyg5ad3fh2lwd7avxvpncipj5wxmp647l43wzr1l3rrkd820yy3";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libiconv-1.17-4-any.pkg.tar.zst";
sha256 = "1g2bkhgf60dywccxw911ydyigf3m25yqfh81m5099swr7mjsmzyf";
name = "mingw-w64-clang-x86_64-libiconv-1.17-4-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-crt-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "0bnzwgf395fbwbsq8900prj409b081hi0dd76kak6d971xqyy2r4";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-gettext-runtime-0.22.5-2-any.pkg.tar.zst";
sha256 = "0ll6ci6d3mc7g04q0xixjc209bh8r874dqbczgns69jsad3wg6mi";
name = "mingw-w64-clang-x86_64-gettext-runtime-0.22.5-2-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-isl-0.24-1-any.pkg.tar.zst";
sha256 = "0dngp6p1yw3i9mvwg9rl888dqa7fjs8xczx1lqacw7lj98q1396d";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-xz-5.6.1-1-any.pkg.tar.zst";
sha256 = "14p4xxaxjjy6j1ingji82xhai1mc1gls5ali6z40fbb2ylxkaggs";
name = "mingw-w64-clang-x86_64-xz-5.6.1-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gmp-6.2.1-3-any.pkg.tar.zst";
sha256 = "170640c8j81gl67kp85kr8kmg5axsl1vqwn9g7cx6vcr638qax9c";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-zlib-1.3.1-1-any.pkg.tar.zst";
sha256 = "06i9xjsskf4ddb2ph4h31md5c7imj9mzjhd4lc4q44j8dmpc1w5p";
name = "mingw-w64-clang-x86_64-zlib-1.3.1-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-mpfr-4.1.0.p13-1-any.pkg.tar.zst";
sha256 = "17klcf17mddd7hsrak920zglqh00drqjdh6dxh3v3c4y62xj1qr6";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libxml2-2.12.6-1-any.pkg.tar.zst";
sha256 = "177b3rmsknqq6hf0zqwva71s3avh20ca7vzznp2ls2z5qm8vhhlp";
name = "mingw-w64-clang-x86_64-libxml2-2.12.6-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-mpc-1.2.1-1-any.pkg.tar.zst";
sha256 = "0761i6aga4982v6mw1hgqrrqznki0c8v93xkpf5fqmsjysfncscc";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-zstd-1.5.5-1-any.pkg.tar.zst";
sha256 = "07739wmwgxf0d6db4p8w302a6jwcm01aafr1s8jvcl5k1h5a1m2m";
name = "mingw-w64-clang-x86_64-zstd-1.5.5-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libwinpthread-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "181fm72bi6cs348hixkqjzivizzcsyn2lxvnbqprx4366prjf7nn";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-llvm-libs-18.1.2-1-any.pkg.tar.zst";
sha256 = "0ibiy01v16naik9pj32ch7a9pkbw4yrn3gyq7p0y6kcc63fkjazy";
name = "mingw-w64-clang-x86_64-llvm-libs-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gcc-libs-11.2.0-10-any.pkg.tar.zst";
sha256 = "1n8q09dwh0ghaw3p3bgxi3q0848gsjzd210bgp7qy05hv73b8kc1";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-llvm-18.1.2-1-any.pkg.tar.zst";
sha256 = "1hcfz6nb6svmmcqzfrdi96az2x7mzj0cispdv2ssbgn7nkf19pi0";
name = "mingw-w64-clang-x86_64-llvm-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-windows-default-manifest-6.4-4-any.pkg.tar.zst";
sha256 = "1ylipf8k9j7bgmwndkib2l29mds394i7jcij7a6ciag4kynlhsvi";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-clang-libs-18.1.2-1-any.pkg.tar.zst";
sha256 = "1k17d18g7rmq2ph4kq1mf84vs8133jzf52nkv6syh39ypjga67wa";
name = "mingw-w64-clang-x86_64-clang-libs-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-winpthreads-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "0n3pim85wlsc93y9sh06rnfraqgzbz300sp9hd8n7wgvcsmpj9rx";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-compiler-rt-18.1.2-1-any.pkg.tar.zst";
sha256 = "1w2j0vs888haz9shjr1l8dc4j957sk1p0377zzipkbqnzqwjf1z8";
name = "mingw-w64-clang-x86_64-compiler-rt-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zstd-1.5.2-2-any.pkg.tar.zst";
sha256 = "1f14wbc1yvjgv3rbwhv75391l55gcm0as6ipba20vw8phz4ax8ds";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-headers-git-11.0.0.r655.gdbfdf8025-1-any.pkg.tar.zst";
sha256 = "18csfwlk2h9pr4411crx1b41qjzn5jgbssm3h109nzwbdizkp62h";
name = "mingw-w64-clang-x86_64-headers-git-11.0.0.r655.gdbfdf8025-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gcc-11.2.0-10-any.pkg.tar.zst";
sha256 = "0h6pi1xrxbrg27klbj5i5rjl8ydz78lpjfhb9pdayxjr8rs5nblq";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-crt-git-11.0.0.r655.gdbfdf8025-1-any.pkg.tar.zst";
sha256 = "03l1zkrxgxxssp430xcv2gch1d03rbnbk1c0vgiqxigcs8lljh2g";
name = "mingw-w64-clang-x86_64-crt-git-11.0.0.r655.gdbfdf8025-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-c-ares-1.18.1-1-any.pkg.tar.zst";
sha256 = "13j7zx9773k0gx3wbqq38jkcndyjpnm7dfb85i8q2dda27g4iq2m";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-lld-18.1.2-1-any.pkg.tar.zst";
sha256 = "1ai4gl7ybpk9n10jmbpf3zzfa893m1krj5qhf44ajln0jabdfnbn";
name = "mingw-w64-clang-x86_64-lld-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-brotli-1.0.9-4-any.pkg.tar.zst";
sha256 = "0vn42aqam6m9755vy32qr626xqglb6vsbmywdyvvagzmm8s5fxrg";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libwinpthread-git-11.0.0.r655.gdbfdf8025-1-any.pkg.tar.zst";
sha256 = "1svhjzwhvl4ldl439jhgfy47g05y2af1cjqvydgijn1dd4g8y8vq";
name = "mingw-w64-clang-x86_64-libwinpthread-git-11.0.0.r655.gdbfdf8025-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-expat-2.4.8-1-any.pkg.tar.zst";
sha256 = "1qkw4k61ddaflns5ms0xh0czbx99wxhs0dfbk8sv8by2rkshl51k";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-winpthreads-git-11.0.0.r655.gdbfdf8025-1-any.pkg.tar.zst";
sha256 = "0jxdhkl256vnr13xf1x3fyjrdf764zg70xcs3gki3rg109f0a6xk";
name = "mingw-w64-clang-x86_64-winpthreads-git-11.0.0.r655.gdbfdf8025-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gettext-0.21-3-any.pkg.tar.zst";
sha256 = "1gy7fmn6jc13ipnyyq44gyhv8rvz5cy7gz1dm3wrna80hjnzli5v";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-clang-18.1.2-1-any.pkg.tar.zst";
sha256 = "0ahfic7vdfv96k5v7fdkgk1agk28l833xjn2igrmbvqg96ak0w6n";
name = "mingw-w64-clang-x86_64-clang-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libunistring-0.9.10-4-any.pkg.tar.zst";
sha256 = "0fimgakzgxrnnqk5rkvnbwl6lyqdrpgl2jcbcagqjv1swdmav97m";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-c-ares-1.27.0-1-any.pkg.tar.zst";
sha256 = "06y3sgqv6a0gr3dsbzs36jrj8adklssgjqi2ms5clsyq6ay4f91r";
name = "mingw-w64-clang-x86_64-c-ares-1.27.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libidn2-2.3.2-1-any.pkg.tar.zst";
sha256 = "0p694g8g716zyvcxc48xmdanjyrkp3ir4naqlradrw2szy9bj800";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-brotli-1.1.0-1-any.pkg.tar.zst";
sha256 = "113mha41q53cx0hw13cq1xdf7zbsd58sh8cl1cd7xzg1q69n60w2";
name = "mingw-w64-clang-x86_64-brotli-1.1.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libpsl-0.21.1-4-any.pkg.tar.zst";
sha256 = "083nng8zis1v2lshnqymxnalprr8g6gdwf84il5ys1ga90pi6bbn";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libunistring-1.1-1-any.pkg.tar.zst";
sha256 = "16myvbg33q5s7jl30w5qd8n8f1r05335ms8r61234vn52n32l2c4";
name = "mingw-w64-clang-x86_64-libunistring-1.1-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libtasn1-4.18.0-1-any.pkg.tar.zst";
sha256 = "0lr1c33d2mkm51kq027bxcj2735vk3nndmn8g02d2v73h6akl48k";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libidn2-2.3.7-2-any.pkg.tar.zst";
sha256 = "07k8zh5nb2s82md7lz22r8gim8214rhlg586lywck3zcla98jv1w";
name = "mingw-w64-clang-x86_64-libidn2-2.3.7-2-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libffi-3.3-4-any.pkg.tar.zst";
sha256 = "0iswfiql785ngavdz3qdxahj6wn531j5cwij945gbr9q6wbav0bi";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libpsl-0.21.5-2-any.pkg.tar.zst";
sha256 = "1mpx77q5g8pj45s8wgc52c4ww2r93080p6d559p56f558a3cl317";
name = "mingw-w64-clang-x86_64-libpsl-0.21.5-2-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-p11-kit-0.24.1-2-any.pkg.tar.zst";
sha256 = "1rx1sjhda4g42qsvmw9nvpdk14ag67sxgfiydivg55hxqjxsvk9p";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libtasn1-4.19.0-1-any.pkg.tar.zst";
sha256 = "19m59mjxww26ah2gk9c0i512fmqpyaj6r5na564kmg6wpwvkihcj";
name = "mingw-w64-clang-x86_64-libtasn1-4.19.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-ca-certificates-20210119-1-any.pkg.tar.zst";
sha256 = "0ia48njn92shddlq3f707wz6dvj0j1k60iscs6vybybw33ijhgsq";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-p11-kit-0.25.3-2-any.pkg.tar.zst";
sha256 = "1jrwkc4lvw5hm5rqmi5gqh7mfkbqfa5gi81zjij0krnl0gaxw3c8";
name = "mingw-w64-clang-x86_64-p11-kit-0.25.3-2-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-openssl-1.1.1.n-1-any.pkg.tar.zst";
sha256 = "0gb6nswwm4b66w4x2zydha23fck967hzb5gckwlv05dws13hbh22";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-ca-certificates-20240203-1-any.pkg.tar.zst";
sha256 = "1q5nxhsk04gidz66ai5wgd4dr04lfyakkfja9p0r5hrgg4ppqqjg";
name = "mingw-w64-clang-x86_64-ca-certificates-20240203-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libssh2-1.10.0-1-any.pkg.tar.zst";
sha256 = "1f27an41hxrfs9jifq0708c484ps3zmb582gmsy7xn5idg3wk03d";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-openssl-3.2.1-1-any.pkg.tar.zst";
sha256 = "0ix2r4ll09m2z5vz2k94gmwfs0pp3ipvjdimwzx7v6xhcs2l25lz";
name = "mingw-w64-clang-x86_64-openssl-3.2.1-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-jansson-2.14-2-any.pkg.tar.zst";
sha256 = "0hwvcyp7mcvljii87mv0d467whr5j8i8rjkkam7r784qrp9i49ds";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libssh2-1.11.0-2-any.pkg.tar.zst";
sha256 = "0l2m823gm1rvnjmqm5ads17mxz1bhpzai5ixyhnkpzrsjxd1ygy5";
name = "mingw-w64-clang-x86_64-libssh2-1.11.0-2-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-jemalloc-5.2.1-2-any.pkg.tar.zst";
sha256 = "04lf1b1sdbb8ncbimbb9q0lv7qlc3s814p2001zsxa2dhdc4xdri";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-nghttp2-1.60.0-1-any.pkg.tar.zst";
sha256 = "0wxw8266hf4qd2m4zpgb1wvlrnaksmcrs0kh5y9zpf2y5sy8f2bq";
name = "mingw-w64-clang-x86_64-nghttp2-1.60.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-xz-5.2.5-2-any.pkg.tar.zst";
sha256 = "1w12cbn6zq2szfa1wgr019i4ayv9x29d1nnh5vlq26l6fmzd3j1g";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-curl-8.6.0-1-any.pkg.tar.zst";
sha256 = "1racc7cyzj22kink9w8m8jv73ji5hfg6r6d1ka9dqmvcbx04r8p0";
name = "mingw-w64-clang-x86_64-curl-8.6.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libxml2-2.9.13-1-any.pkg.tar.zst";
sha256 = "093fm3i8018mig0wy2p53z8izm0bfqlviacray79j2kv1bq8xyn6";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-rust-1.76.0-1-any.pkg.tar.zst";
sha256 = "0ny3bvwvn5wmqrxzhdfw34akr0kj0m7rg9lg3w5yibqz2mkqhk11";
name = "mingw-w64-clang-x86_64-rust-1.76.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-nghttp2-1.47.0-1-any.pkg.tar.zst";
sha256 = "0lwylch8s7blr2hdngs2v0syh1gnicm0z4wpi28ifwdp4kzrh7mh";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-pkgconf-1~2.1.1-1-any.pkg.tar.zst";
sha256 = "00kxqg9ds4q74lxrzjh8z0858smqbi1j9r06s0zjadsql0ln98cq";
name = "mingw-w64-clang-x86_64-pkgconf-12.1.1-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-curl-7.82.0-1-any.pkg.tar.zst";
sha256 = "0czysrplb3lgd59v4c4v8sihbcs3hdih9d8iqrhkf28n7vds859i";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-expat-2.6.2-1-any.pkg.tar.zst";
sha256 = "0kj1vzjh3qh7d2g47avlgk7a6j4nc62111hy1m63jwq0alc01k38";
name = "mingw-w64-clang-x86_64-expat-2.6.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-rust-1.59.0-1-any.pkg.tar.zst";
sha256 = "1c1yr7w3h2ybbx6cywqgpns4ii0dws7jqcix8fiv0rbkjq5hxlsv";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-jsoncpp-1.9.5-3-any.pkg.tar.zst";
sha256 = "1a8mdn4ram9pgqpx5fwxmhcmzc6bh1fq1s4m37xh0d8p6fpncv10";
name = "mingw-w64-clang-x86_64-jsoncpp-1.9.5-3-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-pkgconf-1.8.0-2-any.pkg.tar.zst";
sha256 = "18gzbyc949rvaisdxrf4lyx349xigzpp4dk5a9jj9ghn8zfa1wlg";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-bzip2-1.0.8-3-any.pkg.tar.zst";
sha256 = "1n8zf2kk1xj7wiszp6mjchy1yzpalddbj0cj17qm625ags2vzflm";
name = "mingw-w64-clang-x86_64-bzip2-1.0.8-3-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-jsoncpp-1.9.4-2-any.pkg.tar.zst";
sha256 = "0xqckav97gsaazdfn4395jz0ma0i3snvs1g4ghb7s5jsxbwrhr82";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libb2-0.98.1-2-any.pkg.tar.zst";
sha256 = "0555dvb2xs6695sz5ndrx6y0cz3qa5cg0m5v8q1md13ssg76vlh6";
name = "mingw-w64-clang-x86_64-libb2-0.98.1-2-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-bzip2-1.0.8-2-any.pkg.tar.zst";
sha256 = "1kqg3aw439cdyhnf02rlfr1pw1n8v9xxvq2alhn7aw6nd8qhw7z5";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-lz4-1.9.4-1-any.pkg.tar.zst";
sha256 = "0nn7cy25j53q5ckkx4n4f77w00xdwwf5wjswm374shvvs58nlln0";
name = "mingw-w64-clang-x86_64-lz4-1.9.4-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libb2-0.98.1-2-any.pkg.tar.zst";
sha256 = "1nj669rn1i6fxrwmsqmr9n49p34wxvhn0xlsn9spr6aq1hz73b41";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libtre-git-r177.07e66d0-2-any.pkg.tar.zst";
sha256 = "0fc9hxsdks1xy5fv0rcna433hlzf6jhs77hg0hfzkzhn06f9alp4";
name = "mingw-w64-clang-x86_64-libtre-git-r177.07e66d0-2-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lz4-1.9.3-1-any.pkg.tar.zst";
sha256 = "0fxvabi93cxfybbn49hlr3wgzs4p7fw5shfa055222apkxnncm92";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libsystre-1.0.1-5-any.pkg.tar.zst";
sha256 = "05qsn8fkks4f93jkas43s47axqqgx5m64b45p462si3nlb8cjirq";
name = "mingw-w64-clang-x86_64-libsystre-1.0.1-5-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libtre-git-r128.6fb7206-2-any.pkg.tar.xz";
sha256 = "0dp3ca83j8jlx32gml2qvqpwp5b42q8r98gf6hyiki45d910wb7x";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libarchive-3.7.2-1-any.pkg.tar.zst";
sha256 = "1p84yh6yzkdpmr02vyvgz16x5gycckah25jkdc2py09l7iw96bmw";
name = "mingw-w64-clang-x86_64-libarchive-3.7.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libsystre-1.0.1-4-any.pkg.tar.xz";
sha256 = "037gkzaaj8kp5nspcbc8ll64s9b3mj8d6m663lk1za94bq2axff1";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-libuv-1.48.0-1-any.pkg.tar.zst";
sha256 = "0kfzanvx7hg7bvy35h2z2vcfxvwn44sikd36mvzhkv6c3c6y84sn";
name = "mingw-w64-clang-x86_64-libuv-1.48.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-nettle-3.7.3-3-any.pkg.tar.zst";
sha256 = "10p1jlik2zhqnpphk3k1q6k2my6j0zig49r5fs21a8f6l0gakj1x";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-ninja-1.11.1-3-any.pkg.tar.zst";
sha256 = "13wjfmyfr952n3ydpldjlwx1nla5xpyvr96ng8pfbyw4z900v5ms";
name = "mingw-w64-clang-x86_64-ninja-1.11.1-3-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libarchive-3.6.0-1-any.pkg.tar.zst";
sha256 = "1jd0rj49il09a56dnvgyjzmjj37pdspqhkfm8smgakgsgn9wkm46";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-rhash-1.4.4-3-any.pkg.tar.zst";
sha256 = "1ysbxirpfr0yf7pvyps75lnwc897w2a2kcid3nb4j6ilw6n64jmc";
name = "mingw-w64-clang-x86_64-rhash-1.4.4-3-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libuv-1.42.0-3-any.pkg.tar.zst";
sha256 = "0mg4j5lqmxlhgrs9bnkb1bhj3mfpvjvvkzpjyy87y2m2k11ffbja";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-cmake-3.29.0-1-any.pkg.tar.zst";
sha256 = "0l79lf6zihn0k8hz93qnjnq259y45yq19235g9c444jc2w093si1";
name = "mingw-w64-clang-x86_64-cmake-3.29.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-rhash-1.4.2-1-any.pkg.tar.zst";
sha256 = "0yhv8pra83cs0mk0n40w0k12z32slxs88h1p9z2ixvyigf8w86ml";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-mpdecimal-4.0.0-1-any.pkg.tar.zst";
sha256 = "0hrhbjgi0g3jqpw8himshqw6vazm5sxhsfmyg386nbrxwnfgl1gb";
name = "mingw-w64-clang-x86_64-mpdecimal-4.0.0-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-cmake-3.23.0-1-any.pkg.tar.zst";
sha256 = "1gcmm3bd29zfd88ack6xdqwmbskyvhb4ymrfspayk4jfcj4svky5";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-ncurses-6.4.20231217-1-any.pkg.tar.zst";
sha256 = "00046d52zsr8zjifl7h22jfihhh53h20ipvbqmvf9myssw2fwjza";
name = "mingw-w64-clang-x86_64-ncurses-6.4.20231217-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-ninja-1.10.2-3-any.pkg.tar.zst";
sha256 = "1wsylz9m3hm6lq71qfyc3hc8rmxnv3kp602d1yvvh8nvb8pgza1y";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-termcap-1.3.1-7-any.pkg.tar.zst";
sha256 = "17ha468qavwin800cc3b7c3xdggwk2gakasfxg7jdx7616d99l0n";
name = "mingw-w64-clang-x86_64-termcap-1.3.1-7-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-mpdecimal-2.5.1-1-any.pkg.tar.zst";
sha256 = "0cpyacmciyzbsar1aka5y592g2gpa4i6a58j3bjdmfjdnpm0j08a";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-readline-8.2.010-1-any.pkg.tar.zst";
sha256 = "1s47pd5iz8y3hspsxn4pnp0v3m05ccia40v5nfvx0rmwgvcaz82v";
name = "mingw-w64-clang-x86_64-readline-8.2.010-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-ncurses-6.3-3-any.pkg.tar.zst";
sha256 = "1kicmq9xy4mh5rzzf107jikpmql0h78618b3xpks5l59c1hm8ril";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-tcl-8.6.13-1-any.pkg.tar.zst";
sha256 = "0paaqwk0sfy2zxwlxkmxf2bqq46lyg0sx7cqgzknvazwx8xa2z4x";
name = "mingw-w64-clang-x86_64-tcl-8.6.13-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-termcap-1.3.1-6-any.pkg.tar.zst";
sha256 = "1wgbzj53vmv1vm3igjan635j5ims4x19s2y6mgvvc46zgndc2bvq";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-sqlite3-3.45.2-1-any.pkg.tar.zst";
sha256 = "1icvw3f08cgi94p0177i46v72wgpsxw95p6kd0sm2w3vj0qlqbcw";
name = "mingw-w64-clang-x86_64-sqlite3-3.45.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-readline-8.1.001-1-any.pkg.tar.zst";
sha256 = "0q04sz2ibvcd69hb681j4s6zyakm4i7zpk12qajj38l17v9qmlac";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-tk-8.6.12-2-any.pkg.tar.zst";
sha256 = "0pi74q91vl6vw8vvmmwnvrgai3b1aanp0zhca5qsmv8ljh2wdgzx";
name = "mingw-w64-clang-x86_64-tk-8.6.12-2-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-tcl-8.6.11-5-any.pkg.tar.zst";
sha256 = "0f3p6x55d0370khpp77xpr1dwhfhrlb8b1wjxxb96y0x67q1casm";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-tzdata-2024a-1-any.pkg.tar.zst";
sha256 = "1lsfn3759cyf56zlmfvgy6ihs4iks6zhlnrbfmnq5wml02k936ji";
name = "mingw-w64-clang-x86_64-tzdata-2024a-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-sqlite3-3.38.2-1-any.pkg.tar.zst";
sha256 = "14agi4611h5400j7bqh1w5i5qbhp4hp6apzaddilxq97y37vj90q";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-python-3.11.8-1-any.pkg.tar.zst";
sha256 = "0djpf4k8s25nys6nrm2x2v134lcgzhhbjs37ihkg0b3sxmmc3b0p";
name = "mingw-w64-clang-x86_64-python-3.11.8-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-tk-8.6.11.1-2-any.pkg.tar.zst";
sha256 = "0awr7hzxliyvrkh0ywrga69lcnl5g41i7d4w4azhdwk7i60i1s40";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-openmp-18.1.2-1-any.pkg.tar.zst";
sha256 = "1v9wm3ja3a7a7yna2bpqky481qf244wc98kfdl7l03k7rkvvydpl";
name = "mingw-w64-clang-x86_64-openmp-18.1.2-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-tzdata-2022a-1-any.pkg.tar.zst";
sha256 = "0z1q4359q5vfs77a9wnhmf2i9y3ldfmpijjgzqv4za1grmyj6whd";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-openblas-0.3.26-1-any.pkg.tar.zst";
sha256 = "0kdr72y5lc9dl9s1bjrw8g21qmv2iwd1xvn1r21170i277wsmqiv";
name = "mingw-w64-clang-x86_64-openblas-0.3.26-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-3.9.11-2-any.pkg.tar.zst";
sha256 = "05znxaybrm8affs83a51872iksa9yd4qk692lr3rjdjy3cbxkhca";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-python-numpy-1.26.4-1-any.pkg.tar.zst";
sha256 = "00h0ap954cjwlsc3p01fjwy7s3nlzs90v0kmnrzxm0rljmvn4jkf";
name = "mingw-w64-clang-x86_64-python-numpy-1.26.4-1-any.pkg.tar.zst";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-gcc-libgfortran-11.2.0-10-any.pkg.tar.zst";
sha256 = "13qdcb614sz7w10b2snmp05qh4c7wf24qmfmzssxjjz8ld6p8b90";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-openblas-0.3.20-2-any.pkg.tar.zst";
sha256 = "1cj0j06b2xhcvq9v6jx5cn130r8pkv2xa885f3z3z98kmrifjw0l";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-numpy-1.21.5-1-any.pkg.tar.zst";
sha256 = "10bhfq65nrzxipgy75bqaad74daif4ay06phwvbx70b9j0wm33c3";
})
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-setuptools-59.8.0-2-any.pkg.tar.zst";
sha256 = "1jbvsmh1r00yb6fm8by6z7xp9069f9lkj73jrsnxmflmvpdkjl5c";
url = "https://mirror.msys2.org/mingw/clang64/mingw-w64-clang-x86_64-python-setuptools-69.1.1-1-any.pkg.tar.zst";
sha256 = "1mc56anasj0v92nlg84m3pa7dbqgjakxw0b4ibqlrr9cq0xzsg4b";
name = "mingw-w64-clang-x86_64-python-setuptools-69.1.1-1-any.pkg.tar.zst";
})
]

View File

@ -2,7 +2,7 @@
name = "runkernel"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2018"
edition = "2021"
[dependencies]
libloading = "0.7"
libloading = "0.8"