forked from M-Labs/nac3
Compare commits
99 Commits
Author | SHA1 | Date | |
---|---|---|---|
8031e9143c | |||
03be4697c5 | |||
6328e1e740 | |||
0e61c9f658 | |||
983017a693 | |||
5055c02200 | |||
b1106a6f20 | |||
74ffff6a8b | |||
ce52c17781 | |||
22d09939a6 | |||
1426ee20ef | |||
bb3e6b64b8 | |||
639c31700e | |||
3ce649c0b7 | |||
6b1cba59e0 | |||
a2360870b7 | |||
f3324de3b9 | |||
cb7f667e4d | |||
59c7478950 | |||
ee72072c16 | |||
c53a774994 | |||
5864c07352 | |||
800edf35db | |||
65f3747b96 | |||
b0526ba29f | |||
0a481ec880 | |||
65fa85815a | |||
e4f6fbeeeb | |||
f5285fbf7f | |||
b82926297b | |||
8142e01aeb | |||
e8c967cc29 | |||
902fd97a0a | |||
d73a47b096 | |||
6a29ffcfbc | |||
ae7b1e4391 | |||
cdd0b2011e | |||
34f6c3eaf8 | |||
088f026356 | |||
d23da9966d | |||
dd7099e92c | |||
0bbe5a63b5 | |||
d119b0dc8b | |||
d986449c63 | |||
1a1713af5a | |||
f501269ae0 | |||
24d2b34dcc | |||
c70923f0fe | |||
77c975ead4 | |||
7a05bba99c | |||
dfb3cc7283 | |||
8d9eff1bd2 | |||
3c651cc653 | |||
067b1b0553 | |||
b1d5163936 | |||
01e0ce2eee | |||
f0397b7e09 | |||
60a73dc1c4 | |||
683daae31e | |||
b565211df6 | |||
1e5fd99a88 | |||
2df4a5e9bd | |||
b006bdd96b | |||
fffdaf36d3 | |||
acb47154dc | |||
e917c57339 | |||
eb6f0a1abc | |||
af5bbcac11 | |||
f64320b68e | |||
b988af86bf | |||
4fdf1a37d2 | |||
24f6b2d772 | |||
6265d53ad5 | |||
36d502dc31 | |||
82a580c5c6 | |||
715dc71396 | |||
064aa0411f | |||
57552fb2f6 | |||
35e9c5b38e | |||
0a761cb263 | |||
67f42185de | |||
69542c38a2 | |||
2df22e29f7 | |||
a078481cd2 | |||
c37c7e8975 | |||
0d8cb909dd | |||
529fa67855 | |||
f52ba9f151 | |||
6bcdc3ce00 | |||
c32c68b0b0 | |||
d394b24304 | |||
68da9b0ecf | |||
eec62c3bbb | |||
b521bc0c82 | |||
96e98947cc | |||
87a637b448 | |||
bdeeced122 | |||
37df08b803 | |||
05fd1a5199 |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=lld"]
|
532
Cargo.lock
generated
532
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
6
flake.lock
generated
6
flake.lock
generated
@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1736798957,
|
||||
"narHash": "sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ=",
|
||||
"lastModified": 1744098102,
|
||||
"narHash": "sha256-tzCdyIJj9AjysC3OuKA+tMD/kDEDAF9mICPDU7ix0JA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9abb87b552b7f55ac8916b6fc9e5cb486656a2f3",
|
||||
"rev": "c8cd81426f45942bb2906d5ed2fe21d2f19d95b7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
30
flake.nix
30
flake.nix
@ -13,8 +13,8 @@
|
||||
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
|
||||
ln -s ${pkgs.llvmPackages_16.clang-unwrapped}/bin/clang $out/bin/clang-irrt
|
||||
ln -s ${pkgs.llvmPackages_16.llvm.out}/bin/llvm-as $out/bin/llvm-as-irrt
|
||||
'';
|
||||
demo-linalg-stub = pkgs.rustPlatform.buildRustPackage {
|
||||
name = "demo-linalg-stub";
|
||||
@ -39,9 +39,12 @@
|
||||
src = self;
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
outputHashes = {
|
||||
"inkwell-0.5.0" = "sha256-Qsqy/fhD/qK0rKBeXetE99vW9G9XAePxlXv0An3Yeuo=";
|
||||
};
|
||||
};
|
||||
passthru.cargoLock = cargoLock;
|
||||
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_14.clang) llvm-tools-irrt pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
|
||||
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_16.clang) llvm-tools-irrt pkgs.llvmPackages_16.llvm.out pkgs.llvmPackages_16.bintools llvm-nac3 ];
|
||||
buildInputs = [ pkgs.python3 llvm-nac3 ];
|
||||
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ps.scipy ])) ];
|
||||
checkPhase =
|
||||
@ -77,7 +80,7 @@
|
||||
|
||||
# LLVM PGO support
|
||||
llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
|
||||
stdenv = pkgs.llvmPackages_14.stdenv;
|
||||
stdenv = pkgs.llvmPackages_16.stdenv;
|
||||
extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ];
|
||||
};
|
||||
nac3artiq-instrumented = pkgs.python3Packages.toPythonModule (
|
||||
@ -85,13 +88,13 @@
|
||||
name = "nac3artiq-instrumented";
|
||||
src = self;
|
||||
inherit (nac3artiq) cargoLock;
|
||||
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt llvm-nac3-instrumented ];
|
||||
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools 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_14.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_16.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
|
||||
'';
|
||||
installPhase =
|
||||
''
|
||||
@ -113,13 +116,14 @@
|
||||
(pkgs.fetchFromGitHub {
|
||||
owner = "m-labs";
|
||||
repo = "artiq";
|
||||
rev = "28c9de3e251daa89a8c9fd79d5ab64a3ec03bac6";
|
||||
sha256 = "sha256-vAvpbHc5B+1wtG8zqN7j9dQE1ON+i22v+uqA+tw6Gak=";
|
||||
rev = "554b0749ca5985bf4d006c4f29a05e83de0a226d";
|
||||
sha256 = "sha256-3eSNHTSlmdzLMcEMIspxqjmjrcQe4aIGqIfRgquUg18=";
|
||||
})
|
||||
];
|
||||
buildInputs = [
|
||||
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ps.jsonschema ps.lmdb ps.platformdirs nac3artiq-instrumented ]))
|
||||
pkgs.llvmPackages_14.llvm.out
|
||||
pkgs.llvmPackages_16.llvm.out
|
||||
pkgs.llvmPackages_16.bintools
|
||||
];
|
||||
phases = [ "buildPhase" "installPhase" ];
|
||||
buildPhase =
|
||||
@ -139,7 +143,7 @@
|
||||
'';
|
||||
};
|
||||
llvm-nac3-pgo = pkgs.callPackage ./nix/llvm {
|
||||
stdenv = pkgs.llvmPackages_14.stdenv;
|
||||
stdenv = pkgs.llvmPackages_16.stdenv;
|
||||
extraCmakeFlags = [ "-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata" ];
|
||||
};
|
||||
nac3artiq-pgo = pkgs.python3Packages.toPythonModule (
|
||||
@ -147,7 +151,7 @@
|
||||
name = "nac3artiq-pgo";
|
||||
src = self;
|
||||
inherit (nac3artiq) cargoLock;
|
||||
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt llvm-nac3-pgo ];
|
||||
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-pgo ];
|
||||
buildInputs = [ pkgs.python3 llvm-nac3-pgo ];
|
||||
cargoBuildFlags = [ "--package" "nac3artiq" ];
|
||||
cargoTestFlags = [ "--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq" ];
|
||||
@ -168,12 +172,12 @@
|
||||
buildInputs = with pkgs; [
|
||||
# build dependencies
|
||||
packages.x86_64-linux.llvm-nac3
|
||||
(pkgs.wrapClangMulti llvmPackages_14.clang) llvmPackages_14.llvm.out # for running nac3standalone demos
|
||||
(pkgs.wrapClangMulti llvmPackages_16.clang) llvmPackages_16.llvm.out llvmPackages_16.bintools # for running nac3standalone demos
|
||||
packages.x86_64-linux.llvm-tools-irrt
|
||||
cargo
|
||||
rustc
|
||||
# runtime dependencies
|
||||
lld_14 # for running kernels on the host
|
||||
lld_16 # for running kernels on the host
|
||||
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ]))
|
||||
# development tools
|
||||
cargo-insta
|
||||
|
@ -2,17 +2,18 @@
|
||||
name = "nac3artiq"
|
||||
version = "0.1.0"
|
||||
authors = ["M-Labs"]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
name = "nac3artiq"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.13"
|
||||
pyo3 = { version = "0.21", features = ["extension-module", "gil-refs"] }
|
||||
indexmap = "2.8"
|
||||
itertools = "0.14"
|
||||
pyo3 = { version = "0.24", features = ["extension-module"] }
|
||||
parking_lot = "0.12"
|
||||
tempfile = "3.13"
|
||||
tempfile = "3.19"
|
||||
nac3core = { path = "../nac3core" }
|
||||
nac3ld = { path = "../nac3ld" }
|
||||
|
||||
|
84
nac3artiq/demo/context_managers.py
Normal file
84
nac3artiq/demo/context_managers.py
Normal file
@ -0,0 +1,84 @@
|
||||
from numpy import int32
|
||||
from min_artiq import *
|
||||
|
||||
@nac3
|
||||
class PassContextManager:
|
||||
@kernel
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@kernel
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
@kernel
|
||||
def __exit__(self):
|
||||
pass
|
||||
|
||||
@nac3
|
||||
class CustomDataContextManager:
|
||||
a: Kernel[int32]
|
||||
b: Kernel[int32]
|
||||
|
||||
@kernel
|
||||
def __init__(self, a: int32, b: int32):
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
@kernel
|
||||
def __enter__(self):
|
||||
self.a += 1
|
||||
print_int32(self.a)
|
||||
|
||||
@kernel
|
||||
def __exit__(self):
|
||||
self.b += 1
|
||||
print_int32(self.b)
|
||||
|
||||
@nac3
|
||||
class WithAsContextManager:
|
||||
a: Kernel[int32]
|
||||
|
||||
@kernel
|
||||
def __init__(self, val: int32):
|
||||
self.a = val
|
||||
|
||||
@kernel
|
||||
def __enter__(self) -> int32:
|
||||
print_int32(self.a)
|
||||
return self.a
|
||||
|
||||
@kernel
|
||||
def __exit__(self):
|
||||
print_int32(self.a)
|
||||
|
||||
@nac3
|
||||
class CtxMgrTest:
|
||||
core: KernelInvariant[Core]
|
||||
|
||||
def __init__(self):
|
||||
self.core = Core()
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
x = 0
|
||||
a = PassContextManager()
|
||||
with a:
|
||||
x += 1
|
||||
|
||||
h = CustomDataContextManager(1, 2)
|
||||
with h:
|
||||
x += h.a + h.b
|
||||
|
||||
with WithAsContextManager(15) as num:
|
||||
x += num
|
||||
|
||||
with WithAsContextManager(10) as c, WithAsContextManager(5) as d:
|
||||
e: int32 = c + d
|
||||
x += e
|
||||
|
||||
print_int32(x)
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
CtxMgrTest().run()
|
27
nac3artiq/demo/demo_import_mod.py
Normal file
27
nac3artiq/demo/demo_import_mod.py
Normal file
@ -0,0 +1,27 @@
|
||||
from min_artiq import kernel, KernelInvariant, nac3
|
||||
import min_artiq as artiq
|
||||
|
||||
|
||||
@nac3
|
||||
class Demo:
|
||||
core: KernelInvariant[artiq.Core]
|
||||
led0: KernelInvariant[artiq.TTLOut]
|
||||
led1: KernelInvariant[artiq.TTLOut]
|
||||
|
||||
def __init__(self):
|
||||
self.core = artiq.Core()
|
||||
self.led0 = artiq.TTLOut(self.core, 18)
|
||||
self.led1 = artiq.TTLOut(self.core, 19)
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.core.reset()
|
||||
while True:
|
||||
with artiq.parallel:
|
||||
self.led0.pulse(100.*artiq.ms)
|
||||
self.led1.pulse(100.*artiq.ms)
|
||||
self.core.delay(100.*artiq.ms)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Demo().run()
|
@ -1,9 +1,9 @@
|
||||
from inspect import getfullargspec
|
||||
from functools import wraps
|
||||
from types import SimpleNamespace
|
||||
from numpy import int32, int64
|
||||
from typing import Generic, TypeVar
|
||||
from math import floor, ceil
|
||||
from numpy import int32, int64, uint32, uint64, float64, bool_, str_, ndarray
|
||||
from types import GenericAlias, ModuleType, SimpleNamespace
|
||||
from typing import _GenericAlias, Generic, TypeVar
|
||||
|
||||
import nac3artiq
|
||||
|
||||
@ -16,7 +16,7 @@ __all__ = [
|
||||
"rpc", "ms", "us", "ns",
|
||||
"print_int32", "print_int64",
|
||||
"Core", "TTLOut",
|
||||
"parallel", "sequential"
|
||||
"parallel", "legacy_parallel", "sequential"
|
||||
]
|
||||
|
||||
|
||||
@ -40,10 +40,10 @@ class Option(Generic[T]):
|
||||
|
||||
def is_none(self):
|
||||
return self._nac3_option is None
|
||||
|
||||
|
||||
def is_some(self):
|
||||
return not self.is_none()
|
||||
|
||||
|
||||
def unwrap(self):
|
||||
if self.is_none():
|
||||
raise UnwrapNoneError()
|
||||
@ -54,7 +54,7 @@ class Option(Generic[T]):
|
||||
return "none"
|
||||
else:
|
||||
return "Some({})".format(repr(self._nac3_option))
|
||||
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.is_none():
|
||||
return "none"
|
||||
@ -85,13 +85,46 @@ def ceil64(x):
|
||||
import device_db
|
||||
core_arguments = device_db.device_db["core"]["arguments"]
|
||||
|
||||
artiq_builtins = {
|
||||
"none": none,
|
||||
"virtual": virtual,
|
||||
"_ConstGenericMarker": _ConstGenericMarker,
|
||||
"Option": Option,
|
||||
builtins = {
|
||||
"int": int,
|
||||
"float": float,
|
||||
"bool": bool,
|
||||
"str": str,
|
||||
"list": list,
|
||||
"tuple": tuple,
|
||||
"Exception": Exception,
|
||||
|
||||
"types": {
|
||||
"GenericAlias": GenericAlias,
|
||||
"ModuleType": ModuleType,
|
||||
},
|
||||
|
||||
"typing": {
|
||||
"_GenericAlias": _GenericAlias,
|
||||
"TypeVar": TypeVar,
|
||||
},
|
||||
|
||||
"numpy": {
|
||||
"int32": int32,
|
||||
"int64": int64,
|
||||
"uint32": uint32,
|
||||
"uint64": uint64,
|
||||
"float64": float64,
|
||||
"bool_": bool_,
|
||||
"str_": str_,
|
||||
"ndarray": ndarray,
|
||||
},
|
||||
|
||||
"artiq": {
|
||||
"Kernel": Kernel,
|
||||
"KernelInvariant": KernelInvariant,
|
||||
"_ConstGenericMarker": _ConstGenericMarker,
|
||||
"none": none,
|
||||
"virtual": virtual,
|
||||
"Option": Option,
|
||||
},
|
||||
}
|
||||
compiler = nac3artiq.NAC3(core_arguments["target"], artiq_builtins)
|
||||
compiler = nac3artiq.NAC3(core_arguments["target"], builtins)
|
||||
allow_registration = True
|
||||
# Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side.
|
||||
registered_functions = set()
|
||||
@ -152,9 +185,9 @@ def nac3(cls):
|
||||
return cls
|
||||
|
||||
|
||||
ms = 1e-3
|
||||
us = 1e-6
|
||||
ns = 1e-9
|
||||
ms: KernelInvariant[float] = 1e-3
|
||||
us: KernelInvariant[float] = 1e-6
|
||||
ns: KernelInvariant[float] = 1e-9
|
||||
|
||||
@extern
|
||||
def rtio_init():
|
||||
@ -245,7 +278,7 @@ class Core:
|
||||
embedding = EmbeddingMap()
|
||||
|
||||
if allow_registration:
|
||||
compiler.analyze(registered_functions, registered_classes, set())
|
||||
compiler.analyze(registered_functions, registered_classes, special_ids, set())
|
||||
allow_registration = False
|
||||
|
||||
if hasattr(method, "__self__"):
|
||||
@ -335,5 +368,12 @@ class UnwrapNoneError(Exception):
|
||||
"""raised when unwrapping a none value"""
|
||||
artiq_builtin = True
|
||||
|
||||
parallel = KernelContextManager()
|
||||
sequential = KernelContextManager()
|
||||
parallel: KernelInvariant[KernelContextManager] = KernelContextManager()
|
||||
legacy_parallel: KernelInvariant[KernelContextManager] = KernelContextManager()
|
||||
sequential: KernelInvariant[KernelContextManager] = KernelContextManager()
|
||||
|
||||
special_ids = {
|
||||
"parallel": id(parallel),
|
||||
"legacy_parallel": id(legacy_parallel),
|
||||
"sequential": id(sequential),
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::{hash_map::DefaultHasher, HashMap},
|
||||
collections::{HashMap, hash_map::DefaultHasher},
|
||||
hash::{Hash, Hasher},
|
||||
iter::once,
|
||||
mem,
|
||||
@ -8,42 +8,47 @@ use std::{
|
||||
|
||||
use itertools::Itertools;
|
||||
use pyo3::{
|
||||
types::{PyDict, PyList},
|
||||
PyObject, PyResult, Python,
|
||||
prelude::*,
|
||||
types::{PyDict, PyList},
|
||||
};
|
||||
|
||||
use super::{symbol_resolver::InnerResolver, timeline::TimeFns};
|
||||
use nac3core::{
|
||||
codegen::{
|
||||
expr::{destructure_range, gen_call},
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::{create_fn_and_call, destructure_range, gen_call, infer_and_call_function},
|
||||
llvm_intrinsics::{call_int_smax, call_memcpy, call_stackrestore, call_stacksave},
|
||||
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
|
||||
type_aligned_alloca,
|
||||
types::ndarray::NDArrayType,
|
||||
types::{RangeType, ndarray::NDArrayType},
|
||||
values::{
|
||||
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue, RangeValue,
|
||||
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue,
|
||||
UntypedArrayLikeAccessor,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
inkwell::{
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
context::Context,
|
||||
module::Linkage,
|
||||
targets::TargetMachine,
|
||||
types::{BasicType, IntType},
|
||||
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
},
|
||||
nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef},
|
||||
symbol_resolver::ValueEnum,
|
||||
toplevel::{
|
||||
helper::{extract_ndims, PrimDef},
|
||||
numpy::unpack_ndarray_var_tys,
|
||||
DefinitionId, GenCall,
|
||||
helper::{PrimDef, extract_ndims},
|
||||
numpy::unpack_ndarray_var_tys,
|
||||
},
|
||||
typecheck::{
|
||||
type_inferencer::PrimitiveStore,
|
||||
typedef::{FunSignature, FuncArg, Type, TypeEnum, VarMap, iter_type_vars},
|
||||
},
|
||||
typecheck::typedef::{iter_type_vars, FunSignature, FuncArg, Type, TypeEnum, VarMap},
|
||||
};
|
||||
|
||||
use super::{SpecialPythonId, symbol_resolver::InnerResolver, timeline::TimeFns};
|
||||
|
||||
/// The parallelism mode within a block.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
enum ParallelMode {
|
||||
@ -83,6 +88,9 @@ pub struct ArtiqCodeGenerator<'a> {
|
||||
/// 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,
|
||||
|
||||
/// Specially treated python IDs to identify `with parallel` and `with sequential` blocks.
|
||||
special_ids: SpecialPythonId,
|
||||
}
|
||||
|
||||
impl<'a> ArtiqCodeGenerator<'a> {
|
||||
@ -90,6 +98,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
||||
name: String,
|
||||
size_t: IntType<'_>,
|
||||
timeline: &'a (dyn TimeFns + Sync),
|
||||
special_ids: SpecialPythonId,
|
||||
) -> ArtiqCodeGenerator<'a> {
|
||||
assert!(matches!(size_t.get_bit_width(), 32 | 64));
|
||||
ArtiqCodeGenerator {
|
||||
@ -100,6 +109,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
||||
end: None,
|
||||
timeline,
|
||||
parallel_mode: ParallelMode::None,
|
||||
special_ids,
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,9 +119,10 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
||||
ctx: &Context,
|
||||
target_machine: &TargetMachine,
|
||||
timeline: &'a (dyn TimeFns + Sync),
|
||||
special_ids: SpecialPythonId,
|
||||
) -> ArtiqCodeGenerator<'a> {
|
||||
let llvm_usize = ctx.ptr_sized_int_type(&target_machine.get_target_data(), None);
|
||||
Self::new(name, llvm_usize, timeline)
|
||||
Self::new(name, llvm_usize, timeline, special_ids)
|
||||
}
|
||||
|
||||
/// If the generator is currently in a direct-`parallel` block context, emits IR that resets the
|
||||
@ -180,11 +191,7 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
|
||||
}
|
||||
|
||||
fn get_size_type<'ctx>(&self, ctx: &'ctx Context) -> IntType<'ctx> {
|
||||
if self.size_t == 32 {
|
||||
ctx.i32_type()
|
||||
} else {
|
||||
ctx.i64_type()
|
||||
}
|
||||
if self.size_t == 32 { ctx.i32_type() } else { ctx.i64_type() }
|
||||
}
|
||||
|
||||
fn gen_block<'ctx, 'a, 'c, I: Iterator<Item = &'c Stmt<Option<Type>>>>(
|
||||
@ -257,122 +264,140 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
|
||||
// - 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 resolver = ctx.resolver.clone();
|
||||
if let Some(static_value) =
|
||||
if let Some((_ptr, static_value, _counter)) = ctx.var_assignment.get(id) {
|
||||
static_value.clone()
|
||||
} else if let Some(ValueEnum::Static(val)) =
|
||||
resolver.get_symbol_value(*id, ctx, self)
|
||||
{
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
{
|
||||
let python_id = static_value.get_unique_identifier();
|
||||
if python_id == self.special_ids.parallel
|
||||
|| python_id == self.special_ids.legacy_parallel
|
||||
{
|
||||
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(
|
||||
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 },
|
||||
custom: Some(ctx.primitives.int64),
|
||||
};
|
||||
let start = self
|
||||
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
|
||||
.unwrap();
|
||||
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 },
|
||||
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 = if python_id == self.special_ids.parallel {
|
||||
ParallelMode::Deep
|
||||
} else if python_id == self.special_ids.legacy_parallel {
|
||||
ParallelMode::Legacy
|
||||
} else {
|
||||
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,
|
||||
old_start.custom.unwrap(),
|
||||
)?
|
||||
} else {
|
||||
self.timeline.emit_now_mu(ctx)
|
||||
};
|
||||
end_expr.custom.unwrap(),
|
||||
)?;
|
||||
|
||||
// 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 },
|
||||
custom: Some(ctx.primitives.int64),
|
||||
};
|
||||
let start = self
|
||||
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
|
||||
.unwrap();
|
||||
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 },
|
||||
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!(),
|
||||
};
|
||||
// inside a sequential block
|
||||
if old_start.is_none() {
|
||||
self.timeline.emit_at_mu(ctx, end_val);
|
||||
}
|
||||
|
||||
self.gen_block(ctx, body.iter())?;
|
||||
// inside a parallel block, should update the outer max now_mu
|
||||
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
|
||||
|
||||
let current = ctx.builder.get_insert_block().unwrap();
|
||||
self.parallel_mode = old_parallel_mode;
|
||||
self.end = old_end;
|
||||
self.start = old_start;
|
||||
|
||||
// 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
|
||||
};
|
||||
if reset_position {
|
||||
ctx.builder.position_at_end(current);
|
||||
}
|
||||
|
||||
// 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(),
|
||||
)?;
|
||||
return Ok(());
|
||||
} else if python_id == self.special_ids.sequential {
|
||||
// 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;
|
||||
|
||||
// inside a sequential block
|
||||
if old_start.is_none() {
|
||||
self.timeline.emit_at_mu(ctx, end_val);
|
||||
// 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(());
|
||||
}
|
||||
|
||||
// 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(());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -389,12 +414,7 @@ fn gen_rpc_tag(
|
||||
) -> Result<(), String> {
|
||||
use nac3core::typecheck::typedef::TypeEnum::*;
|
||||
|
||||
let int32 = ctx.primitives.int32;
|
||||
let int64 = ctx.primitives.int64;
|
||||
let float = ctx.primitives.float;
|
||||
let bool = ctx.primitives.bool;
|
||||
let str = ctx.primitives.str;
|
||||
let none = ctx.primitives.none;
|
||||
let PrimitiveStore { int32, int64, float, bool, str, none, .. } = ctx.primitives;
|
||||
|
||||
if ctx.unifier.unioned(ty, int32) {
|
||||
buffer.push(b'i');
|
||||
@ -430,7 +450,10 @@ fn gen_rpc_tag(
|
||||
&*ctx.unifier.get_ty_immutable(ndarray_ndims)
|
||||
{
|
||||
if values.len() != 1 {
|
||||
return Err(format!("NDArray types with multiple literal bounds for ndims is not supported: {}", ctx.unifier.stringify(ty)));
|
||||
return Err(format!(
|
||||
"NDArray types with multiple literal bounds for ndims is not supported: {}",
|
||||
ctx.unifier.stringify(ty)
|
||||
));
|
||||
}
|
||||
|
||||
let value = values[0].clone();
|
||||
@ -470,14 +493,13 @@ fn format_rpc_arg<'ctx>(
|
||||
// NAC3: NDArray = { usize, usize*, T* }
|
||||
// libproto_artiq: NDArray = [data[..], dim_sz[..]]
|
||||
|
||||
let llvm_i1 = ctx.ctx.bool_type();
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
|
||||
let (elem_ty, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, arg_ty);
|
||||
let ndims = extract_ndims(&ctx.unifier, ndims);
|
||||
let dtype = ctx.get_llvm_type(generator, elem_ty);
|
||||
let ndarray =
|
||||
NDArrayType::new(ctx, dtype, ndims).map_value(arg.into_pointer_value(), None);
|
||||
let ndarray = NDArrayType::new(ctx, dtype, ndims)
|
||||
.map_pointer_value(arg.into_pointer_value(), None);
|
||||
|
||||
let ndims = llvm_usize.const_int(ndims, false);
|
||||
|
||||
@ -488,11 +510,11 @@ fn format_rpc_arg<'ctx>(
|
||||
|
||||
let sizeof_usize = llvm_usize.size_of();
|
||||
let sizeof_usize =
|
||||
ctx.builder.build_int_z_extend_or_bit_cast(sizeof_usize, llvm_usize, "").unwrap();
|
||||
ctx.builder.build_int_truncate_or_bit_cast(sizeof_usize, llvm_usize, "").unwrap();
|
||||
|
||||
let sizeof_pdata = dtype.ptr_type(AddressSpace::default()).size_of();
|
||||
let sizeof_pdata =
|
||||
ctx.builder.build_int_z_extend_or_bit_cast(sizeof_pdata, llvm_usize, "").unwrap();
|
||||
ctx.builder.build_int_truncate_or_bit_cast(sizeof_pdata, llvm_usize, "").unwrap();
|
||||
|
||||
let sizeof_buf_shape = ctx.builder.build_int_mul(sizeof_usize, ndims, "").unwrap();
|
||||
let sizeof_buf = ctx.builder.build_int_add(sizeof_buf_shape, sizeof_pdata, "").unwrap();
|
||||
@ -507,13 +529,13 @@ fn format_rpc_arg<'ctx>(
|
||||
// Write to `buf->data`
|
||||
let carray_data = carray.load_data(ctx);
|
||||
let carray_data = ctx.builder.build_pointer_cast(carray_data, llvm_pi8, "").unwrap();
|
||||
call_memcpy(ctx, buf_data, carray_data, sizeof_pdata, llvm_i1.const_zero());
|
||||
call_memcpy(ctx, buf_data, carray_data, sizeof_pdata);
|
||||
|
||||
// Write to `buf->shape`
|
||||
let carray_shape = ndarray.shape().base_ptr(ctx, generator);
|
||||
let carray_shape_i8 =
|
||||
ctx.builder.build_pointer_cast(carray_shape, llvm_pi8, "").unwrap();
|
||||
call_memcpy(ctx, buf_shape, carray_shape_i8, sizeof_buf_shape, llvm_i1.const_zero());
|
||||
call_memcpy(ctx, buf_shape, carray_shape_i8, sizeof_buf_shape);
|
||||
|
||||
buf.base_ptr(ctx, generator)
|
||||
}
|
||||
@ -761,7 +783,7 @@ fn format_rpc_ret<'ctx>(
|
||||
ctx.builder.build_unconditional_branch(head_bb).unwrap();
|
||||
|
||||
ctx.builder.position_at_end(tail_bb);
|
||||
ndarray.as_base_value().into()
|
||||
ndarray.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => {
|
||||
@ -813,7 +835,7 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
let ptr_type = int8.ptr_type(AddressSpace::default());
|
||||
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
|
||||
|
||||
let service_id = int32.const_int(fun.1 .0 as u64, false);
|
||||
let service_id = int32.const_int(fun.1.0 as u64, false);
|
||||
// -- setup rpc tags
|
||||
let mut tag = Vec::new();
|
||||
if obj.is_some() {
|
||||
@ -836,7 +858,7 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
let tag_arr_ptr = ctx.module.add_global(
|
||||
int8.array_type(tag.len() as u32),
|
||||
None,
|
||||
format!("tagptr{}", fun.1 .0).as_str(),
|
||||
format!("tagptr{}", fun.1.0).as_str(),
|
||||
);
|
||||
tag_arr_ptr.set_initializer(&int8.const_array(
|
||||
&tag.iter().map(|v| int8.const_int(u64::from(*v), false)).collect::<Vec<_>>(),
|
||||
@ -914,47 +936,14 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
}
|
||||
|
||||
// call
|
||||
if is_async {
|
||||
let rpc_send_async = ctx.module.get_function("rpc_send_async").unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
"rpc_send_async",
|
||||
ctx.ctx.void_type().fn_type(
|
||||
&[
|
||||
int32.into(),
|
||||
tag_ptr_type.ptr_type(AddressSpace::default()).into(),
|
||||
ptr_type.ptr_type(AddressSpace::default()).into(),
|
||||
],
|
||||
false,
|
||||
),
|
||||
None,
|
||||
)
|
||||
});
|
||||
ctx.builder
|
||||
.build_call(
|
||||
rpc_send_async,
|
||||
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
|
||||
"rpc.send",
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
let rpc_send = ctx.module.get_function("rpc_send").unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
"rpc_send",
|
||||
ctx.ctx.void_type().fn_type(
|
||||
&[
|
||||
int32.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")
|
||||
.unwrap();
|
||||
}
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
if is_async { "rpc_send_async" } else { "rpc_send" },
|
||||
None,
|
||||
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
|
||||
Some("rpc.send"),
|
||||
None,
|
||||
);
|
||||
|
||||
// reclaim stack space used by arguments
|
||||
call_stackrestore(ctx, stackptr);
|
||||
@ -982,7 +971,7 @@ pub fn attributes_writeback<'ctx>(
|
||||
return_obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
) -> Result<(), String> {
|
||||
Python::with_gil(|py| -> PyResult<Result<(), String>> {
|
||||
let host_attributes: &PyList = host_attributes.downcast(py)?;
|
||||
let host_attributes = host_attributes.downcast_bound::<PyList>(py)?;
|
||||
let top_levels = ctx.top_level.definitions.read();
|
||||
let globals = inner_resolver.global_value_ids.read();
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
@ -995,7 +984,7 @@ pub fn attributes_writeback<'ctx>(
|
||||
}
|
||||
|
||||
for val in (*globals).values() {
|
||||
let val = val.as_ref(py);
|
||||
let val = val.bind(py);
|
||||
let ty = inner_resolver.get_obj_type(
|
||||
py,
|
||||
val,
|
||||
@ -1026,7 +1015,7 @@ pub fn attributes_writeback<'ctx>(
|
||||
*field_ty,
|
||||
ctx.build_gep_and_load(
|
||||
obj.into_pointer_value(),
|
||||
&[zero, int32.const_int(index as u64, false)],
|
||||
&[zero, int32.const_int(index.unwrap() as u64, false)],
|
||||
None,
|
||||
),
|
||||
));
|
||||
@ -1067,7 +1056,7 @@ pub fn attributes_writeback<'ctx>(
|
||||
*field_ty,
|
||||
ctx.build_gep_and_load(
|
||||
obj.into_pointer_value(),
|
||||
&[zero, int32.const_int(index as u64, false)],
|
||||
&[zero, int32.const_int(index.unwrap() as u64, false)],
|
||||
None,
|
||||
),
|
||||
));
|
||||
@ -1168,29 +1157,22 @@ fn polymorphic_print<'ctx>(
|
||||
debug_assert!(!fmt.is_empty());
|
||||
debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8);
|
||||
|
||||
let fn_name = if as_rtio { "rtio_log" } else { "core_log" };
|
||||
let print_fn = ctx.module.get_function(fn_name).unwrap_or_else(|| {
|
||||
let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
|
||||
let fn_t = if as_rtio {
|
||||
let llvm_void = ctx.ctx.void_type();
|
||||
llvm_void.fn_type(&[llvm_pi8.into()], true)
|
||||
} else {
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
llvm_i32.fn_type(&[llvm_pi8.into()], true)
|
||||
};
|
||||
ctx.module.add_function(fn_name, fn_t, None)
|
||||
});
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
|
||||
|
||||
let fmt = ctx.gen_string(generator, fmt);
|
||||
let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value();
|
||||
|
||||
ctx.builder
|
||||
.build_call(
|
||||
print_fn,
|
||||
&once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(),
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
create_fn_and_call(
|
||||
ctx,
|
||||
if as_rtio { "rtio_log" } else { "core_log" },
|
||||
if as_rtio { None } else { Some(llvm_i32.into()) },
|
||||
&[llvm_pi8.into()],
|
||||
&once(fmt.into()).chain(args).collect_vec(),
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
};
|
||||
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
@ -1383,7 +1365,7 @@ fn polymorphic_print<'ctx>(
|
||||
|
||||
let (dtype, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ty)
|
||||
.map_value(value.into_pointer_value(), None);
|
||||
.map_pointer_value(value.into_pointer_value(), None);
|
||||
|
||||
let num_0 = llvm_usize.const_zero();
|
||||
|
||||
@ -1431,7 +1413,7 @@ fn polymorphic_print<'ctx>(
|
||||
fmt.push_str("range(");
|
||||
flush(ctx, generator, &mut fmt, &mut args);
|
||||
|
||||
let val = RangeValue::from_pointer_value(value.into_pointer_value(), None);
|
||||
let val = RangeType::new(ctx).map_pointer_value(value.into_pointer_value(), None);
|
||||
|
||||
let (start, stop, step) = destructure_range(ctx, val);
|
||||
|
||||
|
273
nac3artiq/src/debug.rs
Normal file
273
nac3artiq/src/debug.rs
Normal file
@ -0,0 +1,273 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core::{toplevel::TopLevelDef, typecheck::typedef::Unifier};
|
||||
|
||||
use super::{InnerResolver, symbol_resolver::PyValueHandle};
|
||||
|
||||
impl InnerResolver {
|
||||
pub fn debug_str(&self, tld: Option<&[TopLevelDef]>, unifier: &Option<&mut Unifier>) -> String {
|
||||
fn fmt_elems(elems: &str) -> String {
|
||||
if elems.is_empty() { String::new() } else { format!("\n{elems}\n\t") }
|
||||
}
|
||||
fn stringify_pyvalue_handle(handle: &PyValueHandle) -> String {
|
||||
format!("(id: {}, value: {})", handle.0, handle.1)
|
||||
}
|
||||
fn stringify_tld(tld: &TopLevelDef) -> String {
|
||||
match tld {
|
||||
TopLevelDef::Module { name, .. } => {
|
||||
format!("TopLevelDef::Module {{ name: {name} }}")
|
||||
}
|
||||
TopLevelDef::Class { name, .. } => {
|
||||
format!("TopLevelDef::Class {{ name: {name} }}")
|
||||
}
|
||||
TopLevelDef::Function { name, .. } => {
|
||||
format!("TopLevelDef::Function {{ name: {name} }}")
|
||||
}
|
||||
TopLevelDef::Variable { name, .. } => {
|
||||
format!("TopLevelDef::Variable {{ name: {name} }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut str = String::new();
|
||||
str.push_str("nac3artiq::InnerResolver {");
|
||||
|
||||
{
|
||||
let id_to_type = self.id_to_type.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_type: {{{}}},",
|
||||
fmt_elems(
|
||||
id_to_type
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| {
|
||||
let ty_str = unifier.as_ref().map_or_else(
|
||||
|| format!("{v:?}"),
|
||||
|unifier| unifier.stringify(*v),
|
||||
);
|
||||
format!("\t\t{k} -> {ty_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
),
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let id_to_def = self.id_to_def.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_def: {{{}}},",
|
||||
fmt_elems(
|
||||
id_to_def
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| {
|
||||
let tld_str = tld.map_or_else(
|
||||
|| format!("{v:?}"),
|
||||
|tlds| stringify_tld(&tlds[v.0]),
|
||||
);
|
||||
format!("\t\t{k} -> {tld_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let id_to_pyval = self.id_to_pyval.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_pyval: {{{}}},",
|
||||
fmt_elems(
|
||||
id_to_pyval
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| { format!("\t\t{k} -> {}", stringify_pyvalue_handle(v)) })
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let id_to_primitive = self.id_to_primitive.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_primitive: {{{}}},",
|
||||
fmt_elems(
|
||||
id_to_primitive
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| { format!("\t\t{k} -> {v:?}") })
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let field_to_val = self.field_to_val.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tfield_to_val: {{{}}},",
|
||||
fmt_elems(
|
||||
field_to_val
|
||||
.iter()
|
||||
.sorted_by_key(|((id, _), _)| *id)
|
||||
.map(|((id, name), pyval)| {
|
||||
format!(
|
||||
"\t\t({id}, {name}) -> {}",
|
||||
pyval.as_ref().map_or_else(
|
||||
|| String::from("None"),
|
||||
|pyval| format!(
|
||||
"Some({})",
|
||||
stringify_pyvalue_handle(pyval)
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let global_value_ids = self.global_value_ids.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tglobal_value_ids: {{{}}},",
|
||||
fmt_elems(
|
||||
global_value_ids
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let pyid_to_def = self.pyid_to_def.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tpyid_to_def: {{{}}},",
|
||||
fmt_elems(
|
||||
pyid_to_def
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| {
|
||||
let tld_str = tld.map_or_else(
|
||||
|| format!("{v:?}"),
|
||||
|tlds| stringify_tld(&tlds[v.0]),
|
||||
);
|
||||
format!("\t\t{k} -> {tld_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let pyid_to_type = self.pyid_to_type.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tpyid_to_type: {{{}}},",
|
||||
fmt_elems(
|
||||
pyid_to_type
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| {
|
||||
let ty_str = unifier.as_ref().map_or_else(
|
||||
|| format!("{v:?}"),
|
||||
|unifier| unifier.stringify(*v),
|
||||
);
|
||||
format!("\t\t{k} -> {ty_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let string_store = self.string_store.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tstring_store: {{{}}},",
|
||||
fmt_elems(
|
||||
string_store
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
let exception_ids = self.exception_ids.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\texception_ids: {{{}}},",
|
||||
fmt_elems(
|
||||
exception_ids
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
let name_to_pyid = &self.name_to_pyid;
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tname_to_pyid: {{{}}},",
|
||||
fmt_elems(
|
||||
name_to_pyid
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
let module = &self.module;
|
||||
str.push_str(format!("\n\tmodule: {module}").as_str());
|
||||
|
||||
str.push_str("\n}");
|
||||
|
||||
str
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
#![deny(future_incompatible, let_underscore, nonstandard_style, clippy::all)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(
|
||||
unsafe_op_in_unsafe_fn,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::enum_glob_use,
|
||||
@ -11,18 +10,21 @@
|
||||
)]
|
||||
|
||||
use std::{
|
||||
cell::LazyCell,
|
||||
collections::{HashMap, HashSet},
|
||||
fs,
|
||||
io::Write,
|
||||
path::Path,
|
||||
process::Command,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use itertools::Itertools;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use pyo3::{
|
||||
create_exception, exceptions,
|
||||
IntoPyObjectExt, create_exception, exceptions,
|
||||
prelude::*,
|
||||
types::{PyBytes, PyDict, PyNone, PySet},
|
||||
};
|
||||
@ -30,17 +32,17 @@ use tempfile::{self, TempDir};
|
||||
|
||||
use nac3core::{
|
||||
codegen::{
|
||||
concrete_type::ConcreteTypeStore, gen_func_impl, irrt::load_irrt, CodeGenLLVMOptions,
|
||||
CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, WithCall, WorkerRegistry,
|
||||
CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, WithCall,
|
||||
WorkerRegistry, concrete_type::ConcreteTypeStore, gen_func_impl, irrt::load_irrt,
|
||||
},
|
||||
inkwell::{
|
||||
OptimizationLevel,
|
||||
context::Context,
|
||||
memory_buffer::MemoryBuffer,
|
||||
module::{FlagBehavior, Linkage, Module},
|
||||
passes::PassBuilderOptions,
|
||||
support::is_multithreaded,
|
||||
targets::*,
|
||||
OptimizationLevel,
|
||||
},
|
||||
nac3parser::{
|
||||
ast::{self, Constant, ExprKind, Located, Stmt, StmtKind, StrRef},
|
||||
@ -48,27 +50,31 @@ use nac3core::{
|
||||
},
|
||||
symbol_resolver::SymbolResolver,
|
||||
toplevel::{
|
||||
DefinitionId, GenCall, TopLevelDef,
|
||||
builtins::get_exn_constructor,
|
||||
composer::{BuiltinFuncCreator, BuiltinFuncSpec, ComposerConfig, TopLevelComposer},
|
||||
DefinitionId, GenCall, TopLevelDef,
|
||||
},
|
||||
typecheck::{
|
||||
type_inferencer::PrimitiveStore,
|
||||
typedef::{into_var_map, FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap},
|
||||
typedef::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap, into_var_map},
|
||||
},
|
||||
};
|
||||
use nac3ld::Linker;
|
||||
|
||||
use codegen::{
|
||||
attributes_writeback, gen_core_log, gen_rtio_log, rpc_codegen_callback, ArtiqCodeGenerator,
|
||||
ArtiqCodeGenerator, attributes_writeback, gen_core_log, gen_rtio_log, rpc_codegen_callback,
|
||||
};
|
||||
use symbol_resolver::{DeferredEvaluationStore, InnerResolver, PythonHelper, Resolver};
|
||||
use timeline::TimeFns;
|
||||
|
||||
mod codegen;
|
||||
mod debug;
|
||||
mod symbol_resolver;
|
||||
mod timeline;
|
||||
|
||||
const ENV_NAC3_EMIT_LLVM_BC: &str = "NAC3_EMIT_LLVM_BC";
|
||||
const ENV_NAC3_EMIT_LLVM_LL: &str = "NAC3_EMIT_LLVM_LL";
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
enum Isa {
|
||||
Host,
|
||||
@ -83,7 +89,7 @@ impl Isa {
|
||||
match self {
|
||||
Isa::Host => TargetMachine::get_default_triple(),
|
||||
Isa::RiscV32G | Isa::RiscV32IMA => TargetTriple::create("riscv32-unknown-linux"),
|
||||
Isa::CortexA9 => TargetTriple::create("armv7-unknown-linux-gnueabihf"),
|
||||
Isa::CortexA9 => TargetTriple::create("armv7-unknown-linux-eabihf"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,9 +166,21 @@ pub struct PrimitivePythonId {
|
||||
virtual_id: u64,
|
||||
option: u64,
|
||||
module: u64,
|
||||
kernel: u64,
|
||||
kernel_invariant: u64,
|
||||
}
|
||||
|
||||
type TopLevelComponent = (Stmt, String, PyObject);
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SpecialPythonId {
|
||||
parallel: u64,
|
||||
legacy_parallel: u64,
|
||||
sequential: u64,
|
||||
}
|
||||
|
||||
/// An [`IndexMap`] storing the `id()` of values, mapped to a handle of the value itself.
|
||||
type PyValueMap = IndexMap<u64, Arc<PyObject>>;
|
||||
|
||||
type TopLevelComponent = (Stmt, String, Arc<PyObject>);
|
||||
|
||||
// TopLevelComposer is unsendable as it holds the unification table, which is
|
||||
// unsendable due to Rc. Arc would cause a performance hit.
|
||||
@ -179,6 +197,7 @@ struct Nac3 {
|
||||
string_store: Arc<RwLock<HashMap<String, i32>>>,
|
||||
exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
|
||||
deferred_eval_store: DeferredEvaluationStore,
|
||||
special_ids: SpecialPythonId,
|
||||
/// LLVM-related options for code generation.
|
||||
llvm_options: CodeGenLLVMOptions,
|
||||
}
|
||||
@ -188,17 +207,17 @@ create_exception!(nac3artiq, CompileError, exceptions::PyException);
|
||||
impl Nac3 {
|
||||
fn register_module(
|
||||
&mut self,
|
||||
module: &PyObject,
|
||||
module: &Arc<PyObject>,
|
||||
registered_class_ids: &HashSet<u64>,
|
||||
) -> PyResult<()> {
|
||||
let (module_name, source_file, source) =
|
||||
Python::with_gil(|py| -> PyResult<(String, String, String)> {
|
||||
let module: &PyAny = module.extract(py)?;
|
||||
let module = module.bind(py);
|
||||
let source_file = module.getattr("__file__");
|
||||
let (source_file, source) = if let Ok(source_file) = source_file {
|
||||
let source_file = source_file.extract()?;
|
||||
let source_file = source_file.extract::<&str>()?;
|
||||
(
|
||||
source_file,
|
||||
source_file.to_string(),
|
||||
fs::read_to_string(source_file).map_err(|e| {
|
||||
exceptions::PyIOError::new_err(format!(
|
||||
"failed to read input file: {e}"
|
||||
@ -208,18 +227,26 @@ impl Nac3 {
|
||||
} else {
|
||||
// kernels submitted by content have no file
|
||||
// but still can provide source by StringLoader
|
||||
let get_src_fn = module
|
||||
.getattr("__loader__")?
|
||||
.extract::<PyObject>()?
|
||||
.getattr(py, "get_source")?;
|
||||
("<expcontent>", get_src_fn.call1(py, (PyNone::get(py),))?.extract(py)?)
|
||||
let get_src_fn = module.getattr("__loader__")?.getattr("get_source")?;
|
||||
(String::from("<expcontent>"), get_src_fn.call1((PyNone::get(py),))?.extract()?)
|
||||
};
|
||||
Ok((module.getattr("__name__")?.extract()?, source_file.to_string(), source))
|
||||
Ok((module.getattr("__name__")?.extract()?, source_file, source))
|
||||
})?;
|
||||
|
||||
let parser_result = parse_program(&source, source_file.into())
|
||||
.map_err(|e| exceptions::PySyntaxError::new_err(format!("parse error: {e}")))?;
|
||||
|
||||
let id_fn = LazyCell::new(|| {
|
||||
Python::with_gil(|py| {
|
||||
PyModule::import(py, "builtins").unwrap().getattr("id").unwrap().unbind()
|
||||
})
|
||||
});
|
||||
let get_type_hints_fn = LazyCell::new(|| {
|
||||
Python::with_gil(|py| {
|
||||
PyModule::import(py, "typing").unwrap().getattr("get_type_hints").unwrap().unbind()
|
||||
})
|
||||
});
|
||||
|
||||
for mut stmt in parser_result {
|
||||
let include = match stmt.node {
|
||||
StmtKind::ClassDef { ref decorator_list, ref mut body, ref mut bases, .. } => {
|
||||
@ -236,15 +263,15 @@ impl Nac3 {
|
||||
// Drop unregistered (i.e. host-only) base classes.
|
||||
bases.retain(|base| {
|
||||
Python::with_gil(|py| -> PyResult<bool> {
|
||||
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
|
||||
match &base.node {
|
||||
ExprKind::Name { id, .. } => {
|
||||
if *id == "Exception".into() {
|
||||
Ok(true)
|
||||
} else {
|
||||
let base_obj =
|
||||
module.getattr(py, id.to_string().as_str())?;
|
||||
let base_id = id_fn.call1((base_obj,))?.extract()?;
|
||||
module.bind(py).getattr(id.to_string().as_str())?;
|
||||
let base_id =
|
||||
id_fn.bind(py).call1((base_obj,))?.extract()?;
|
||||
Ok(registered_class_ids.contains(&base_id))
|
||||
}
|
||||
}
|
||||
@ -277,10 +304,28 @@ impl Nac3 {
|
||||
}
|
||||
})
|
||||
}
|
||||
// Allow global variable declaration with `Kernel` type annotation
|
||||
StmtKind::AnnAssign { ref annotation, .. } => {
|
||||
matches!(&annotation.node, ExprKind::Subscript { value, .. } if matches!(&value.node, ExprKind::Name {id, ..} if id == &"Kernel".into()))
|
||||
}
|
||||
|
||||
// Allow global variable declaration with `Kernel` or `KernelInvariant` type annotation
|
||||
StmtKind::AnnAssign { ref target, .. } => match &target.node {
|
||||
ExprKind::Name { id, .. } => Python::with_gil(|py| {
|
||||
let py_type_hints =
|
||||
get_type_hints_fn.bind(py).call1((module.bind(py),)).unwrap();
|
||||
let py_type_hints = py_type_hints.downcast::<PyDict>().unwrap();
|
||||
let var_type_hint =
|
||||
py_type_hints.get_item(id.to_string().as_str()).unwrap().unwrap();
|
||||
let var_type = var_type_hint.getattr_opt("__origin__").unwrap();
|
||||
if let Some(var_type) = var_type {
|
||||
let var_type_id = id_fn.bind(py).call1((var_type,)).unwrap();
|
||||
let var_type_id = var_type_id.extract::<u64>().unwrap();
|
||||
|
||||
[self.primitive_ids.kernel, self.primitive_ids.kernel_invariant]
|
||||
.contains(&var_type_id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}),
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@ -313,7 +358,7 @@ impl Nac3 {
|
||||
None => {
|
||||
return Some(format!(
|
||||
"object launching kernel does not have method `{method_name}`"
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -334,7 +379,7 @@ impl Nac3 {
|
||||
None if default_value.is_none() => {
|
||||
return Some(format!(
|
||||
"argument `{name}` not provided when launching kernel function"
|
||||
))
|
||||
));
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
@ -348,7 +393,7 @@ impl Nac3 {
|
||||
Err(e) => {
|
||||
return Some(format!(
|
||||
"type error ({e}) at parameter #{i} when calling kernel function"
|
||||
))
|
||||
));
|
||||
}
|
||||
};
|
||||
if let Err(e) = unifier.unify(in_ty, *ty) {
|
||||
@ -422,13 +467,13 @@ impl Nac3 {
|
||||
]
|
||||
}
|
||||
|
||||
fn compile_method<T>(
|
||||
fn compile_method<'py, T>(
|
||||
&self,
|
||||
obj: &PyAny,
|
||||
obj: &Bound<'py, PyAny>,
|
||||
method_name: &str,
|
||||
args: Vec<&PyAny>,
|
||||
embedding_map: &PyAny,
|
||||
py: Python,
|
||||
args: Vec<Bound<'py, PyAny>>,
|
||||
embedding_map: &Bound<'py, PyAny>,
|
||||
py: Python<'py>,
|
||||
link_fn: &dyn Fn(&Module) -> PyResult<T>,
|
||||
) -> PyResult<T> {
|
||||
let size_t = self.isa.get_size_type(&Context::create());
|
||||
@ -444,19 +489,20 @@ impl Nac3 {
|
||||
let id_fn = builtins.getattr("id")?;
|
||||
let issubclass = builtins.getattr("issubclass")?;
|
||||
let exn_class = builtins.getattr("Exception")?;
|
||||
let store_obj = embedding_map.getattr("store_object").unwrap().to_object(py);
|
||||
let store_str = embedding_map.getattr("store_str").unwrap().to_object(py);
|
||||
let store_fun = embedding_map.getattr("store_function").unwrap().to_object(py);
|
||||
let host_attributes = embedding_map.getattr("attributes_writeback").unwrap().to_object(py);
|
||||
let store_obj = embedding_map.getattr("store_object").unwrap();
|
||||
let store_str = embedding_map.getattr("store_str").unwrap();
|
||||
let store_fun = embedding_map.getattr("store_function").unwrap().into_py_any(py)?;
|
||||
let host_attributes =
|
||||
embedding_map.getattr("attributes_writeback").unwrap().into_py_any(py)?;
|
||||
let global_value_ids: Arc<RwLock<HashMap<_, _>>> = Arc::new(RwLock::new(HashMap::new()));
|
||||
let helper = PythonHelper {
|
||||
id_fn: builtins.getattr("id").unwrap().to_object(py),
|
||||
len_fn: builtins.getattr("len").unwrap().to_object(py),
|
||||
type_fn: builtins.getattr("type").unwrap().to_object(py),
|
||||
origin_ty_fn: typings.getattr("get_origin").unwrap().to_object(py),
|
||||
args_ty_fn: typings.getattr("get_args").unwrap().to_object(py),
|
||||
store_obj: store_obj.clone(),
|
||||
store_str,
|
||||
id_fn: Arc::new(builtins.getattr("id").unwrap().into_py_any(py)?),
|
||||
len_fn: Arc::new(builtins.getattr("len").unwrap().into_py_any(py)?),
|
||||
type_fn: Arc::new(builtins.getattr("type").unwrap().into_py_any(py)?),
|
||||
origin_ty_fn: Arc::new(typings.getattr("get_origin").unwrap().into_py_any(py)?),
|
||||
args_ty_fn: Arc::new(typings.getattr("get_args").unwrap().into_py_any(py)?),
|
||||
store_obj: Arc::new(store_obj.clone().into_py_any(py)?),
|
||||
store_str: Arc::new(store_str.into_py_any(py)?),
|
||||
};
|
||||
|
||||
let pyid_to_type = Arc::new(RwLock::new(HashMap::<u64, Type>::new()));
|
||||
@ -479,14 +525,14 @@ impl Nac3 {
|
||||
|
||||
let mut rpc_ids = vec![];
|
||||
for (stmt, path, module) in &self.top_levels {
|
||||
let py_module: &PyAny = module.extract(py)?;
|
||||
let py_module = module.bind(py);
|
||||
let module_id: u64 = id_fn.call1((py_module,))?.extract()?;
|
||||
let module_name: String = py_module.getattr("__name__")?.extract()?;
|
||||
let helper = helper.clone();
|
||||
let class_obj;
|
||||
if let StmtKind::ClassDef { name, .. } = &stmt.node {
|
||||
let class = py_module.getattr(name.to_string().as_str()).unwrap();
|
||||
if issubclass.call1((class, exn_class)).unwrap().extract().unwrap()
|
||||
if issubclass.call1((&class, &exn_class)).unwrap().extract().unwrap()
|
||||
&& class.getattr("artiq_builtin").is_err()
|
||||
{
|
||||
class_obj = Some(class);
|
||||
@ -499,8 +545,8 @@ impl Nac3 {
|
||||
let (name_to_pyid, resolver, _, _) =
|
||||
module_to_resolver_cache.get(&module_id).cloned().unwrap_or_else(|| {
|
||||
let mut name_to_pyid: HashMap<StrRef, u64> = HashMap::new();
|
||||
let members: &PyDict =
|
||||
py_module.getattr("__dict__").unwrap().downcast().unwrap();
|
||||
let members = py_module.getattr("__dict__").unwrap();
|
||||
let members = members.downcast::<PyDict>().unwrap();
|
||||
for (key, val) in members {
|
||||
let key: &str = key.extract().unwrap();
|
||||
let val = id_fn.call1((val,)).unwrap().extract().unwrap();
|
||||
@ -546,52 +592,79 @@ impl Nac3 {
|
||||
if let Some(class_obj) = class_obj {
|
||||
self.exception_ids
|
||||
.write()
|
||||
.insert(def_id.0, store_obj.call1(py, (class_obj,))?.extract(py)?);
|
||||
.insert(def_id.0, store_obj.call1((class_obj,))?.extract()?);
|
||||
}
|
||||
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { decorator_list, .. } => {
|
||||
if decorator_list
|
||||
.iter()
|
||||
.any(|decorator| decorator_id_string(decorator) == Some("rpc".to_string()))
|
||||
{
|
||||
store_fun
|
||||
.call1(
|
||||
py,
|
||||
(
|
||||
def_id.0.into_py(py),
|
||||
module.getattr(py, name.to_string().as_str()).unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let is_async = decorator_list.iter().any(|decorator| {
|
||||
decorator_get_flags(decorator)
|
||||
.iter()
|
||||
.any(|constant| *constant == Constant::Str("async".into()))
|
||||
});
|
||||
rpc_ids.push((None, def_id, is_async));
|
||||
}
|
||||
}
|
||||
StmtKind::ClassDef { name, body, .. } => {
|
||||
let class_name = name.to_string();
|
||||
let class_obj = module.getattr(py, class_name.as_str()).unwrap();
|
||||
for stmt in body {
|
||||
if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node {
|
||||
if decorator_list.iter().any(|decorator| {
|
||||
decorator_id_string(decorator) == Some("rpc".to_string())
|
||||
}) {
|
||||
for decorator in decorator_list {
|
||||
if let Some(decorator_str) = decorator_id_string(decorator) {
|
||||
if decorator_str == "rpc" {
|
||||
store_fun
|
||||
.call1(
|
||||
py,
|
||||
(
|
||||
def_id.0.into_py_any(py)?,
|
||||
module
|
||||
.bind(py)
|
||||
.getattr(name.to_string().as_str())
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let is_async = decorator_list.iter().any(|decorator| {
|
||||
decorator_get_flags(decorator)
|
||||
.iter()
|
||||
.any(|constant| *constant == Constant::Str("async".into()))
|
||||
});
|
||||
if name == &"__init__".into() {
|
||||
return Err(CompileError::new_err(format!(
|
||||
"compilation failed\n----------\nThe constructor of class {} should not be decorated with rpc decorator (at {})",
|
||||
class_name, stmt.location
|
||||
)));
|
||||
rpc_ids.push((None, def_id, is_async));
|
||||
} else if decorator_str != "kernel"
|
||||
&& decorator_str != "portable"
|
||||
&& decorator_str != "extern"
|
||||
{
|
||||
return Err(CompileError::new_err(format!(
|
||||
"compilation failed\n----------\nDecorator {} is not supported (at {})",
|
||||
decorator_id_string(decorator).unwrap(),
|
||||
stmt.location
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
StmtKind::ClassDef { name, body, .. } => {
|
||||
let class_name = name.to_string();
|
||||
let class_obj = module.bind(py).getattr(class_name.as_str()).unwrap();
|
||||
for stmt in body {
|
||||
if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node {
|
||||
for decorator in decorator_list {
|
||||
if let Some(decorator_str) = decorator_id_string(decorator) {
|
||||
if decorator_str == "rpc" {
|
||||
let is_async = decorator_list.iter().any(|decorator| {
|
||||
decorator_get_flags(decorator).iter().any(|constant| {
|
||||
*constant == Constant::Str("async".into())
|
||||
})
|
||||
});
|
||||
if name == &"__init__".into() {
|
||||
return Err(CompileError::new_err(format!(
|
||||
"compilation failed\n----------\nThe constructor of class {} should not be decorated with rpc decorator (at {})",
|
||||
class_name, stmt.location
|
||||
)));
|
||||
}
|
||||
rpc_ids.push((
|
||||
Some((class_obj.clone(), *name)),
|
||||
def_id,
|
||||
is_async,
|
||||
));
|
||||
} else if decorator_str != "kernel"
|
||||
&& decorator_str != "portable"
|
||||
{
|
||||
return Err(CompileError::new_err(format!(
|
||||
"compilation failed\n----------\nDecorator {} is not supported (at {})",
|
||||
decorator_id_string(decorator).unwrap(),
|
||||
stmt.location
|
||||
)));
|
||||
}
|
||||
}
|
||||
rpc_ids.push((Some((class_obj.clone(), *name)), def_id, is_async));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -635,7 +708,7 @@ impl Nac3 {
|
||||
let mut arg_names = vec![];
|
||||
for (i, arg) in args.into_iter().enumerate() {
|
||||
let name = format!("tmp{i}");
|
||||
module.add(&name, arg)?;
|
||||
module.add(&*name, &arg)?;
|
||||
name_to_pyid.insert(name.clone().into(), id_fun.call1((arg,))?.extract()?);
|
||||
arg_names.push(name);
|
||||
}
|
||||
@ -657,7 +730,7 @@ impl Nac3 {
|
||||
id_to_primitive: RwLock::default(),
|
||||
field_to_val: RwLock::default(),
|
||||
name_to_pyid,
|
||||
module: module.to_object(py),
|
||||
module: Arc::new(module.into_py_any(py)?),
|
||||
helper: helper.clone(),
|
||||
string_store: self.string_store.clone(),
|
||||
exception_ids: self.exception_ids.clone(),
|
||||
@ -731,10 +804,8 @@ impl Nac3 {
|
||||
.call1(
|
||||
py,
|
||||
(
|
||||
id.0.into_py(py),
|
||||
class_def
|
||||
.getattr(py, name.to_string().as_str())
|
||||
.unwrap(),
|
||||
id.0.into_py_any(py)?,
|
||||
class_def.getattr(name.to_string().as_str()).unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
@ -744,7 +815,7 @@ impl Nac3 {
|
||||
TopLevelDef::Variable { .. } => {
|
||||
return Err(CompileError::new_err(String::from(
|
||||
"Unsupported @rpc annotation on global variable",
|
||||
)))
|
||||
)));
|
||||
}
|
||||
TopLevelDef::Module { .. } => {
|
||||
unreachable!("Type module cannot be decorated with @rpc")
|
||||
@ -797,6 +868,7 @@ impl Nac3 {
|
||||
&context,
|
||||
&self.get_llvm_target_machine(),
|
||||
self.time_fns,
|
||||
self.special_ids.clone(),
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
@ -813,6 +885,7 @@ impl Nac3 {
|
||||
&context,
|
||||
&self.get_llvm_target_machine(),
|
||||
self.time_fns,
|
||||
self.special_ids.clone(),
|
||||
);
|
||||
let module = context.create_module("main");
|
||||
let target_machine = self.llvm_options.create_target_machine().unwrap();
|
||||
@ -866,6 +939,18 @@ impl Nac3 {
|
||||
|
||||
embedding_map.setattr("expects_return", has_return).unwrap();
|
||||
|
||||
let emit_llvm_bc = std::env::var(ENV_NAC3_EMIT_LLVM_BC).is_ok();
|
||||
let emit_llvm_ll = std::env::var(ENV_NAC3_EMIT_LLVM_LL).is_ok();
|
||||
|
||||
let emit_llvm = |module: &Module<'_>, filename: &str| {
|
||||
if emit_llvm_bc {
|
||||
module.write_bitcode_to_path(Path::new(format!("{filename}.bc").as_str()));
|
||||
}
|
||||
if emit_llvm_ll {
|
||||
module.print_to_file(Path::new(format!("{filename}.ll").as_str())).unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
// Link all modules into `main`.
|
||||
let buffers = membuffers.lock();
|
||||
let main = context
|
||||
@ -874,6 +959,8 @@ impl Nac3 {
|
||||
"main",
|
||||
))
|
||||
.unwrap();
|
||||
emit_llvm(&main, "main");
|
||||
|
||||
for buffer in buffers.iter().rev().skip(1) {
|
||||
let other = context
|
||||
.create_module_from_ir(MemoryBuffer::create_from_memory_range(buffer, "main"))
|
||||
@ -881,7 +968,10 @@ impl Nac3 {
|
||||
|
||||
main.link_in_module(other).map_err(|err| CompileError::new_err(err.to_string()))?;
|
||||
}
|
||||
emit_llvm(&main, "main.merged");
|
||||
|
||||
main.link_in_module(irrt).map_err(|err| CompileError::new_err(err.to_string()))?;
|
||||
emit_llvm(&main, "main.fat");
|
||||
|
||||
let mut function_iter = main.get_first_function();
|
||||
while let Some(func) = function_iter {
|
||||
@ -901,6 +991,8 @@ impl Nac3 {
|
||||
global_option = global.get_next_global();
|
||||
}
|
||||
|
||||
emit_llvm(&main, "main.pre-opt");
|
||||
|
||||
let target_machine = self
|
||||
.llvm_options
|
||||
.target
|
||||
@ -915,12 +1007,15 @@ impl Nac3 {
|
||||
panic!("Failed to run optimization for module `main`: {}", err.to_string());
|
||||
}
|
||||
|
||||
emit_llvm(&main, "main.post-opt");
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let string_store = self.string_store.read();
|
||||
let mut string_store_vec = string_store.iter().collect::<Vec<_>>();
|
||||
string_store_vec.sort_by(|(_s1, key1), (_s2, key2)| key1.cmp(key2));
|
||||
for (s, key) in string_store_vec {
|
||||
let embed_key: i32 = helper.store_str.call1(py, (s,)).unwrap().extract(py).unwrap();
|
||||
let embed_key: i32 =
|
||||
helper.store_str.bind(py).call1((s,)).unwrap().extract().unwrap();
|
||||
assert_eq!(
|
||||
embed_key, *key,
|
||||
"string {s} is out of sync between embedding map (key={embed_key}) and \
|
||||
@ -1030,7 +1125,7 @@ fn add_exceptions(
|
||||
#[pymethods]
|
||||
impl Nac3 {
|
||||
#[new]
|
||||
fn new(isa: &str, artiq_builtins: &PyDict, py: Python) -> PyResult<Self> {
|
||||
fn new<'py>(isa: &str, artiq_builtins: &Bound<'py, PyDict>, py: Python<'py>) -> PyResult<Self> {
|
||||
let isa = match isa {
|
||||
"host" => Isa::Host,
|
||||
"rv32g" => Isa::RiscV32G,
|
||||
@ -1097,42 +1192,59 @@ impl Nac3 {
|
||||
|
||||
let builtins_mod = PyModule::import(py, "builtins").unwrap();
|
||||
let id_fn = builtins_mod.getattr("id").unwrap();
|
||||
let numpy_mod = PyModule::import(py, "numpy").unwrap();
|
||||
let typing_mod = PyModule::import(py, "typing").unwrap();
|
||||
let types_mod = PyModule::import(py, "types").unwrap();
|
||||
|
||||
let get_id = |x: &PyAny| id_fn.call1((x,)).and_then(PyAny::extract).unwrap();
|
||||
let get_attr_id = |obj: &PyModule, attr| {
|
||||
id_fn.call1((obj.getattr(attr).unwrap(),)).unwrap().extract().unwrap()
|
||||
let get_id = |x: &Bound<PyAny>| id_fn.call1((x,)).and_then(|id| id.extract()).unwrap();
|
||||
let get_artiq_builtin = |mod_name: Option<&str>, name: &str| -> Bound<PyAny> {
|
||||
if let Some(mod_name) = mod_name {
|
||||
artiq_builtins
|
||||
.get_item(mod_name)
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| {
|
||||
panic!("no module key '{mod_name}' present in artiq_builtins")
|
||||
})
|
||||
.downcast::<PyDict>()
|
||||
.unwrap()
|
||||
.get_item(name)
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| {
|
||||
panic!("no key '{name}' present in artiq_builtins.{mod_name}")
|
||||
})
|
||||
} else {
|
||||
artiq_builtins
|
||||
.get_item(name)
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| panic!("no key '{name}' present in artiq_builtins"))
|
||||
}
|
||||
};
|
||||
|
||||
let primitive_ids = PrimitivePythonId {
|
||||
virtual_id: get_id(artiq_builtins.get_item("virtual").ok().flatten().unwrap()),
|
||||
virtual_id: get_id(&get_artiq_builtin(Some("artiq"), "virtual")),
|
||||
generic_alias: (
|
||||
get_attr_id(typing_mod, "_GenericAlias"),
|
||||
get_attr_id(types_mod, "GenericAlias"),
|
||||
get_id(&get_artiq_builtin(Some("typing"), "_GenericAlias")),
|
||||
get_id(&get_artiq_builtin(Some("types"), "GenericAlias")),
|
||||
),
|
||||
none: get_id(artiq_builtins.get_item("none").ok().flatten().unwrap()),
|
||||
typevar: get_attr_id(typing_mod, "TypeVar"),
|
||||
const_generic_marker: get_id(
|
||||
artiq_builtins.get_item("_ConstGenericMarker").ok().flatten().unwrap(),
|
||||
),
|
||||
int: get_attr_id(builtins_mod, "int"),
|
||||
int32: get_attr_id(numpy_mod, "int32"),
|
||||
int64: get_attr_id(numpy_mod, "int64"),
|
||||
uint32: get_attr_id(numpy_mod, "uint32"),
|
||||
uint64: get_attr_id(numpy_mod, "uint64"),
|
||||
bool: get_attr_id(builtins_mod, "bool"),
|
||||
np_bool_: get_attr_id(numpy_mod, "bool_"),
|
||||
string: get_attr_id(builtins_mod, "str"),
|
||||
np_str_: get_attr_id(numpy_mod, "str_"),
|
||||
float: get_attr_id(builtins_mod, "float"),
|
||||
float64: get_attr_id(numpy_mod, "float64"),
|
||||
list: get_attr_id(builtins_mod, "list"),
|
||||
ndarray: get_attr_id(numpy_mod, "ndarray"),
|
||||
tuple: get_attr_id(builtins_mod, "tuple"),
|
||||
exception: get_attr_id(builtins_mod, "Exception"),
|
||||
option: get_id(artiq_builtins.get_item("Option").ok().flatten().unwrap()),
|
||||
module: get_attr_id(types_mod, "ModuleType"),
|
||||
none: get_id(&get_artiq_builtin(Some("artiq"), "none")),
|
||||
typevar: get_id(&get_artiq_builtin(Some("typing"), "TypeVar")),
|
||||
const_generic_marker: get_id(&get_artiq_builtin(Some("artiq"), "_ConstGenericMarker")),
|
||||
int: get_id(&get_artiq_builtin(None, "int")),
|
||||
int32: get_id(&get_artiq_builtin(Some("numpy"), "int32")),
|
||||
int64: get_id(&get_artiq_builtin(Some("numpy"), "int64")),
|
||||
uint32: get_id(&get_artiq_builtin(Some("numpy"), "uint32")),
|
||||
uint64: get_id(&get_artiq_builtin(Some("numpy"), "uint64")),
|
||||
bool: get_id(&get_artiq_builtin(None, "bool")),
|
||||
np_bool_: get_id(&get_artiq_builtin(Some("numpy"), "bool_")),
|
||||
string: get_id(&get_artiq_builtin(None, "str")),
|
||||
np_str_: get_id(&get_artiq_builtin(Some("numpy"), "str_")),
|
||||
float: get_id(&get_artiq_builtin(None, "float")),
|
||||
float64: get_id(&get_artiq_builtin(Some("numpy"), "float64")),
|
||||
list: get_id(&get_artiq_builtin(None, "list")),
|
||||
ndarray: get_id(&get_artiq_builtin(Some("numpy"), "ndarray")),
|
||||
tuple: get_id(&get_artiq_builtin(None, "tuple")),
|
||||
exception: get_id(&get_artiq_builtin(None, "Exception")),
|
||||
option: get_id(&get_artiq_builtin(Some("artiq"), "Option")),
|
||||
module: get_id(&get_artiq_builtin(Some("types"), "ModuleType")),
|
||||
kernel: get_id(&get_artiq_builtin(Some("artiq"), "Kernel")),
|
||||
kernel_invariant: get_id(&get_artiq_builtin(Some("artiq"), "KernelInvariant")),
|
||||
};
|
||||
|
||||
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
|
||||
@ -1192,6 +1304,7 @@ impl Nac3 {
|
||||
string_store: Arc::new(string_store.into()),
|
||||
exception_ids: Arc::default(),
|
||||
deferred_eval_store: DeferredEvaluationStore::new(),
|
||||
special_ids: SpecialPythonId::default(),
|
||||
llvm_options: CodeGenLLVMOptions {
|
||||
opt_level: OptimizationLevel::Default,
|
||||
target: isa.get_llvm_target_options(),
|
||||
@ -1199,36 +1312,45 @@ impl Nac3 {
|
||||
})
|
||||
}
|
||||
|
||||
fn analyze(
|
||||
fn analyze<'py>(
|
||||
&mut self,
|
||||
functions: &PySet,
|
||||
classes: &PySet,
|
||||
content_modules: &PySet,
|
||||
functions: &Bound<'py, PySet>,
|
||||
classes: &Bound<'py, PySet>,
|
||||
special_ids: &Bound<'py, PyDict>,
|
||||
content_modules: &Bound<'py, PySet>,
|
||||
) -> PyResult<()> {
|
||||
let (modules, class_ids) =
|
||||
Python::with_gil(|py| -> PyResult<(HashMap<u64, PyObject>, HashSet<u64>)> {
|
||||
let mut modules: HashMap<u64, PyObject> = HashMap::new();
|
||||
Python::with_gil(|py| -> PyResult<(PyValueMap, HashSet<u64>)> {
|
||||
let mut modules: IndexMap<u64, Arc<PyObject>> = IndexMap::new();
|
||||
let mut class_ids: HashSet<u64> = HashSet::new();
|
||||
|
||||
let id_fn = PyModule::import(py, "builtins")?.getattr("id")?;
|
||||
let getmodule_fn = PyModule::import(py, "inspect")?.getattr("getmodule")?;
|
||||
|
||||
for function in functions {
|
||||
let module: PyObject = getmodule_fn.call1((function,))?.extract()?;
|
||||
if !module.is_none(py) {
|
||||
modules.insert(id_fn.call1((&module,))?.extract()?, module);
|
||||
let module = getmodule_fn.call1((&function,))?;
|
||||
if !module.is_none() {
|
||||
modules.insert(
|
||||
id_fn.call1((&module,))?.extract()?,
|
||||
Arc::new(module.into_py_any(py)?),
|
||||
);
|
||||
}
|
||||
}
|
||||
for class in classes {
|
||||
let module: PyObject = getmodule_fn.call1((class,))?.extract()?;
|
||||
if !module.is_none(py) {
|
||||
modules.insert(id_fn.call1((&module,))?.extract()?, module);
|
||||
let module = getmodule_fn.call1((&class,))?;
|
||||
if !module.is_none() {
|
||||
modules.insert(
|
||||
id_fn.call1((&module,))?.extract()?,
|
||||
Arc::new(module.into_py_any(py)?),
|
||||
);
|
||||
}
|
||||
class_ids.insert(id_fn.call1((class,))?.extract()?);
|
||||
class_ids.insert(id_fn.call1((&class,))?.extract()?);
|
||||
}
|
||||
for module in content_modules {
|
||||
let module: PyObject = module.extract()?;
|
||||
modules.insert(id_fn.call1((&module,))?.extract()?, module);
|
||||
modules.insert(
|
||||
id_fn.call1((&module,))?.extract()?,
|
||||
Arc::new(module.into_py_any(py)?),
|
||||
);
|
||||
}
|
||||
Ok((modules, class_ids))
|
||||
})?;
|
||||
@ -1236,22 +1358,40 @@ impl Nac3 {
|
||||
for module in modules.into_values() {
|
||||
self.register_module(&module, &class_ids)?;
|
||||
}
|
||||
|
||||
self.special_ids = SpecialPythonId {
|
||||
parallel: special_ids.get_item("parallel").ok().flatten().unwrap().extract().unwrap(),
|
||||
legacy_parallel: special_ids
|
||||
.get_item("legacy_parallel")
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
sequential: special_ids
|
||||
.get_item("sequential")
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_method_to_file(
|
||||
fn compile_method_to_file<'py>(
|
||||
&mut self,
|
||||
obj: &PyAny,
|
||||
obj: &Bound<'py, PyAny>,
|
||||
method_name: &str,
|
||||
args: Vec<&PyAny>,
|
||||
args: Vec<Bound<'py, PyAny>>,
|
||||
filename: &str,
|
||||
embedding_map: &PyAny,
|
||||
py: Python,
|
||||
embedding_map: &Bound<'py, PyAny>,
|
||||
py: Python<'py>,
|
||||
) -> PyResult<()> {
|
||||
let target_machine = self.get_llvm_target_machine();
|
||||
|
||||
if self.isa == Isa::Host {
|
||||
let link_fn = |module: &Module| {
|
||||
let link_fn = |module: &Module| {
|
||||
if self.isa == Isa::Host {
|
||||
let working_directory = self.working_directory.path().to_owned();
|
||||
target_machine
|
||||
.write_to_file(module, FileType::Object, &working_directory.join("module.o"))
|
||||
@ -1261,11 +1401,7 @@ impl Nac3 {
|
||||
working_directory.join("module.o").to_string_lossy().to_string(),
|
||||
)?;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
|
||||
} else {
|
||||
let link_fn = |module: &Module| {
|
||||
} else {
|
||||
let object_mem = target_machine
|
||||
.write_to_memory_buffer(module, FileType::Object)
|
||||
.expect("couldn't write module to object file buffer");
|
||||
@ -1279,24 +1415,23 @@ impl Nac3 {
|
||||
} else {
|
||||
Err(CompileError::new_err("linker failed to process object file"))
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
|
||||
}
|
||||
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
|
||||
}
|
||||
|
||||
fn compile_method_to_mem(
|
||||
fn compile_method_to_mem<'py>(
|
||||
&mut self,
|
||||
obj: &PyAny,
|
||||
obj: &Bound<'py, PyAny>,
|
||||
method_name: &str,
|
||||
args: Vec<&PyAny>,
|
||||
embedding_map: &PyAny,
|
||||
py: Python,
|
||||
args: Vec<Bound<'py, PyAny>>,
|
||||
embedding_map: &Bound<'py, PyAny>,
|
||||
py: Python<'py>,
|
||||
) -> PyResult<PyObject> {
|
||||
let target_machine = self.get_llvm_target_machine();
|
||||
|
||||
if self.isa == Isa::Host {
|
||||
let link_fn = |module: &Module| {
|
||||
let link_fn = |module: &Module| {
|
||||
if self.isa == Isa::Host {
|
||||
let working_directory = self.working_directory.path().to_owned();
|
||||
target_machine
|
||||
.write_to_file(module, FileType::Object, &working_directory.join("module.o"))
|
||||
@ -1310,11 +1445,7 @@ impl Nac3 {
|
||||
)?;
|
||||
|
||||
Ok(PyBytes::new(py, &fs::read(filename).unwrap()).into())
|
||||
};
|
||||
|
||||
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
|
||||
} else {
|
||||
let link_fn = |module: &Module| {
|
||||
} else {
|
||||
let object_mem = target_machine
|
||||
.write_to_memory_buffer(module, FileType::Object)
|
||||
.expect("couldn't write module to object file buffer");
|
||||
@ -1323,20 +1454,20 @@ impl Nac3 {
|
||||
} else {
|
||||
Err(CompileError::new_err("linker failed to process object file"))
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
|
||||
}
|
||||
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "init-llvm-profile")]
|
||||
extern "C" {
|
||||
unsafe extern "C" {
|
||||
fn __llvm_profile_initialize();
|
||||
}
|
||||
|
||||
#[pymodule]
|
||||
fn nac3artiq(py: Python, m: &PyModule) -> PyResult<()> {
|
||||
fn nac3artiq<'py>(py: Python<'py>, m: &Bound<'py, PyModule>) -> PyResult<()> {
|
||||
#[cfg(feature = "init-llvm-profile")]
|
||||
unsafe {
|
||||
__llvm_profile_initialize();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,6 @@
|
||||
use itertools::Either;
|
||||
|
||||
use nac3core::{
|
||||
codegen::CodeGenContext,
|
||||
inkwell::{
|
||||
values::{BasicValueEnum, CallSiteValue},
|
||||
AddressSpace, AtomicOrdering,
|
||||
},
|
||||
codegen::{CodeGenContext, expr::infer_and_call_function},
|
||||
inkwell::{AddressSpace, AtomicOrdering, values::BasicValueEnum},
|
||||
};
|
||||
|
||||
/// Functions for manipulating the timeline.
|
||||
@ -288,36 +283,27 @@ pub struct ExternTimeFns {}
|
||||
|
||||
impl TimeFns for ExternTimeFns {
|
||||
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")
|
||||
.map(CallSiteValue::try_as_basic_value)
|
||||
.map(Either::unwrap_left)
|
||||
.unwrap()
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
"now_mu",
|
||||
Some(ctx.ctx.i64_type().into()),
|
||||
&[],
|
||||
Some("now_mu"),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
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",
|
||||
ctx.ctx.void_type().fn_type(&[ctx.ctx.i64_type().into()], false),
|
||||
None,
|
||||
)
|
||||
});
|
||||
ctx.builder.build_call(at_mu, &[t.into()], "at_mu").unwrap();
|
||||
assert_eq!(t.get_type(), ctx.ctx.i64_type().into());
|
||||
|
||||
infer_and_call_function(ctx, "at_mu", None, &[t], Some("at_mu"), None);
|
||||
}
|
||||
|
||||
fn emit_delay_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, dt: BasicValueEnum<'ctx>) {
|
||||
let delay_mu = ctx.module.get_function("delay_mu").unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
"delay_mu",
|
||||
ctx.ctx.void_type().fn_type(&[ctx.ctx.i64_type().into()], false),
|
||||
None,
|
||||
)
|
||||
});
|
||||
ctx.builder.build_call(delay_mu, &[dt.into()], "delay_mu").unwrap();
|
||||
assert_eq!(dt.get_type(), ctx.ctx.i64_type().into());
|
||||
|
||||
infer_and_call_function(ctx, "delay_mu", None, &[dt], Some("delay_mu"), None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "nac3ast"
|
||||
version = "0.1.0"
|
||||
authors = ["RustPython Team", "M-Labs"]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
default = ["constant-optimization", "fold"]
|
||||
@ -11,5 +11,5 @@ fold = []
|
||||
|
||||
[dependencies]
|
||||
parking_lot = "0.12"
|
||||
string-interner = "0.17"
|
||||
string-interner = "0.19"
|
||||
fxhash = "0.2"
|
||||
|
@ -6,7 +6,7 @@ pub use crate::location::Location;
|
||||
use fxhash::FxBuildHasher;
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
use std::{cell::RefCell, collections::HashMap, fmt, sync::LazyLock};
|
||||
use string_interner::{symbol::SymbolU32, DefaultBackend, StringInterner};
|
||||
use string_interner::{DefaultBackend, StringInterner, symbol::SymbolU32};
|
||||
|
||||
pub type Interner = StringInterner<DefaultBackend, FxBuildHasher>;
|
||||
static INTERNER: LazyLock<Mutex<Interner>> =
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::StrRef;
|
||||
use crate::constant;
|
||||
use crate::fold::Fold;
|
||||
use crate::StrRef;
|
||||
|
||||
pub(crate) trait Foldable<T, U> {
|
||||
type Mapped;
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "nac3core"
|
||||
version = "0.1.0"
|
||||
authors = ["M-Labs"]
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
default = ["derive"]
|
||||
@ -10,25 +10,26 @@ derive = ["dep:nac3core_derive"]
|
||||
no-escape-analysis = []
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.13"
|
||||
itertools = "0.14"
|
||||
crossbeam = "0.8"
|
||||
indexmap = "2.6"
|
||||
indexmap = "2.8"
|
||||
parking_lot = "0.12"
|
||||
rayon = "1.10"
|
||||
nac3core_derive = { path = "nac3core_derive", optional = true }
|
||||
nac3parser = { path = "../nac3parser" }
|
||||
strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
strum = "0.27"
|
||||
strum_macros = "0.27"
|
||||
|
||||
[dependencies.inkwell]
|
||||
version = "0.5"
|
||||
git = "https://github.com/Derppening/inkwell"
|
||||
tag = "0.5.0_llvm15-typed-ptr"
|
||||
default-features = false
|
||||
features = ["llvm14-0-prefer-dynamic", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
|
||||
features = ["llvm16-0-prefer-dynamic", "target-x86", "target-arm", "target-riscv", "no-libffi-linking", "typed-pointers"]
|
||||
|
||||
[dev-dependencies]
|
||||
test-case = "1.2.0"
|
||||
test-case = "3.3"
|
||||
indoc = "2.0"
|
||||
insta = "=1.11.0"
|
||||
insta = "1.42"
|
||||
function_name = "0.3"
|
||||
|
||||
[build-dependencies]
|
||||
regex = "1.10"
|
||||
regex = "1.11"
|
||||
|
@ -28,6 +28,8 @@ fn main() {
|
||||
"-fno-exceptions",
|
||||
"-fno-rtti",
|
||||
"-emit-llvm",
|
||||
"-Xclang",
|
||||
"-no-opaque-pointers",
|
||||
"-S",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
@ -100,6 +102,7 @@ fn main() {
|
||||
|
||||
let mut llvm_as = Command::new("llvm-as-irrt")
|
||||
.stdin(Stdio::piped())
|
||||
.arg("-opaque-pointers=0")
|
||||
.arg("-o")
|
||||
.arg(out_dir.join("irrt.bc"))
|
||||
.spawn()
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "irrt/cc-builtins.hpp"
|
||||
#include "irrt/exception.hpp"
|
||||
#include "irrt/list.hpp"
|
||||
#include "irrt/math.hpp"
|
||||
|
34
nac3core/irrt/irrt/cc-builtins.hpp
Normal file
34
nac3core/irrt/irrt/cc-builtins.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#define DEF_builtin_unary(RET, NAME, TY) \
|
||||
RET __nac3_##NAME(TY v) { return __builtin_##NAME(v); }
|
||||
#define DEF_builtin_binary(RET, NAME, TY1, TY2) \
|
||||
RET __nac3_##NAME(TY1 v1, TY2 v2) { return __builtin_##NAME(v1, v2); }
|
||||
|
||||
DEF_builtin_unary(bool, isinf, double);
|
||||
DEF_builtin_unary(bool, isnan, double);
|
||||
DEF_builtin_unary(double, tan, double);
|
||||
DEF_builtin_unary(double, asin, double);
|
||||
DEF_builtin_unary(double, acos, double);
|
||||
DEF_builtin_unary(double, atan, double);
|
||||
DEF_builtin_unary(double, sinh, double);
|
||||
DEF_builtin_unary(double, cosh, double);
|
||||
DEF_builtin_unary(double, tanh, double);
|
||||
DEF_builtin_unary(double, asinh, double);
|
||||
DEF_builtin_unary(double, acosh, double);
|
||||
DEF_builtin_unary(double, atanh, double);
|
||||
DEF_builtin_unary(double, expm1, double);
|
||||
DEF_builtin_unary(double, cbrt, double);
|
||||
DEF_builtin_unary(double, erf, double);
|
||||
DEF_builtin_unary(double, erfc, double);
|
||||
|
||||
#define __builtin_gamma __builtin_tgamma
|
||||
DEF_builtin_unary(double, gamma, double);
|
||||
#undef __builtin_gamma
|
||||
|
||||
DEF_builtin_binary(double, atan2, double, double);
|
||||
DEF_builtin_binary(double, hypot, double, double);
|
||||
DEF_builtin_binary(double, nextafter, double, double);
|
||||
DEF_builtin_binary(double, ldexp, double, int);
|
||||
}
|
@ -34,39 +34,6 @@ DEF_nac3_int_exp_(int64_t);
|
||||
DEF_nac3_int_exp_(uint32_t);
|
||||
DEF_nac3_int_exp_(uint64_t);
|
||||
|
||||
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
|
||||
@ -76,7 +43,7 @@ double __nac3_gammaln(double x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
return lgamma(x);
|
||||
return __builtin_lgamma(x);
|
||||
}
|
||||
|
||||
double j0(double x);
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nac3core_derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
@ -2,8 +2,8 @@ use proc_macro::TokenStream;
|
||||
use proc_macro_error::{abort, proc_macro_error};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse_macro_input, spanned::Spanned, Data, DataStruct, Expr, ExprField, ExprMethodCall,
|
||||
ExprPath, GenericArgument, Ident, LitStr, Path, PathArguments, Type, TypePath,
|
||||
Data, DataStruct, Expr, ExprField, ExprMethodCall, ExprPath, GenericArgument, Ident, LitStr,
|
||||
Path, PathArguments, Type, TypePath, parse_macro_input, spanned::Spanned,
|
||||
};
|
||||
|
||||
/// Extracts all generic arguments of a [`Type`] into a [`Vec`].
|
||||
@ -59,11 +59,7 @@ fn replace_top_level_receiver(expr: &mut Expr, ident: Ident) -> Option<&mut Expr
|
||||
| Expr::Field(ExprField { base: operand, .. }) = expr
|
||||
{
|
||||
return if extract_dot_operand(operand).is_some() {
|
||||
if replace_top_level_receiver(operand, ident).is_some() {
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if replace_top_level_receiver(operand, ident).is_some() { Some(expr) } else { None }
|
||||
} else {
|
||||
*operand = Box::new(Expr::Path(ExprPath {
|
||||
attrs: Vec::default(),
|
||||
@ -105,7 +101,7 @@ fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
|
||||
abort!(
|
||||
path,
|
||||
format!(
|
||||
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
|
||||
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
|
||||
quote!(#expr).to_string(),
|
||||
)
|
||||
)
|
||||
@ -154,7 +150,7 @@ fn normalize_value_expr(expr: &Expr) -> proc_macro2::TokenStream {
|
||||
abort!(
|
||||
expr,
|
||||
format!(
|
||||
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
|
||||
"Expected one of `size_t`, `usize`, or an implicit call expression in #[value_type(...)], found {}",
|
||||
quote!(#expr).to_string(),
|
||||
)
|
||||
)
|
||||
@ -224,10 +220,9 @@ pub fn derive(input: TokenStream) -> TokenStream {
|
||||
let Data::Struct(DataStruct { fields, .. }) = &input.data else {
|
||||
abort!(input, "Only structs with named fields are supported");
|
||||
};
|
||||
if let Err(err_span) =
|
||||
fields
|
||||
.iter()
|
||||
.try_for_each(|field| if field.ident.is_some() { Ok(()) } else { Err(field.span()) })
|
||||
if let Err(err_span) = fields
|
||||
.iter()
|
||||
.try_for_each(|field| if field.ident.is_some() { Ok(()) } else { Err(field.span()) })
|
||||
{
|
||||
abort!(err_span, "Only structs with named fields are supported");
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nac3core::{
|
||||
codegen::types::structure::StructField,
|
||||
inkwell::{
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
values::{IntValue, PointerValue},
|
||||
},
|
||||
};
|
||||
use nac3core_derive::StructFields;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nac3core::{
|
||||
codegen::types::structure::StructField,
|
||||
inkwell::{
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
values::{IntValue, PointerValue},
|
||||
},
|
||||
};
|
||||
use nac3core_derive::StructFields;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nac3core::{
|
||||
codegen::types::structure::StructField,
|
||||
inkwell::{
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
values::{IntValue, PointerValue},
|
||||
},
|
||||
};
|
||||
use nac3core_derive::StructFields;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nac3core::{
|
||||
codegen::types::structure::StructField,
|
||||
inkwell::{
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
values::{IntValue, PointerValue},
|
||||
},
|
||||
};
|
||||
use nac3core_derive::StructFields;
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nac3core::{
|
||||
codegen::types::structure::StructField,
|
||||
inkwell::{
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
values::{IntValue, PointerValue},
|
||||
},
|
||||
};
|
||||
use nac3core_derive::StructFields;
|
||||
|
@ -1,26 +1,26 @@
|
||||
use inkwell::{
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValue, BasicValueEnum, IntValue},
|
||||
FloatPredicate, IntPredicate, OptimizationLevel,
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValueEnum, IntValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::destructure_range,
|
||||
extern_fns, irrt,
|
||||
irrt::calculate_len_for_slice_range,
|
||||
llvm_intrinsics,
|
||||
macros::codegen_unreachable,
|
||||
types::{ndarray::NDArrayType, ListType, TupleType},
|
||||
types::{ListType, RangeType, TupleType, ndarray::NDArrayType},
|
||||
values::{
|
||||
ProxyValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
|
||||
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
|
||||
ProxyValue, RangeValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
use crate::{
|
||||
toplevel::{
|
||||
helper::{arraylike_flatten_element_type, extract_ndims, PrimDef},
|
||||
helper::{PrimDef, arraylike_flatten_element_type, extract_ndims},
|
||||
numpy::unpack_ndarray_var_tys,
|
||||
},
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
@ -47,14 +47,14 @@ pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let range_ty = ctx.primitives.range;
|
||||
|
||||
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
|
||||
let arg = RangeValue::from_pointer_value(arg.into_pointer_value(), Some("range"));
|
||||
let arg = RangeType::new(ctx).map_pointer_value(arg.into_pointer_value(), Some("range"));
|
||||
let (start, end, step) = destructure_range(ctx, arg);
|
||||
calculate_len_for_slice_range(generator, ctx, start, end, step)
|
||||
} else {
|
||||
match &*ctx.unifier.get_ty_immutable(arg_ty) {
|
||||
TypeEnum::TTuple { .. } => {
|
||||
let tuple = TupleType::from_unifier_type(generator, ctx, arg_ty)
|
||||
.map_value(arg.into_struct_value(), None);
|
||||
.map_struct_value(arg.into_struct_value(), None);
|
||||
llvm_i32.const_int(tuple.get_type().num_elements().into(), false)
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, arg_ty)
|
||||
.map_value(arg.into_pointer_value(), None);
|
||||
.map_pointer_value(arg.into_pointer_value(), None);
|
||||
ctx.builder
|
||||
.build_int_truncate_or_bit_cast(ndarray.len(ctx), llvm_i32, "len")
|
||||
.unwrap()
|
||||
@ -72,7 +72,7 @@ pub fn call_len<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
let list = ListType::from_unifier_type(generator, ctx, arg_ty)
|
||||
.map_value(arg.into_pointer_value(), None);
|
||||
.map_pointer_value(arg.into_pointer_value(), None);
|
||||
ctx.builder
|
||||
.build_int_truncate_or_bit_cast(list.load_size(ctx, None), llvm_i32, "len")
|
||||
.unwrap()
|
||||
@ -99,17 +99,21 @@ pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
|
||||
}
|
||||
|
||||
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 32 => {
|
||||
debug_assert!([ctx.primitives.int32, ctx.primitives.uint32,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[ctx.primitives.int32, ctx.primitives.uint32,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
n.into()
|
||||
}
|
||||
|
||||
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 64 => {
|
||||
debug_assert!([ctx.primitives.int64, ctx.primitives.uint64,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[ctx.primitives.int64, ctx.primitives.uint64,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
ctx.builder.build_int_truncate(n, llvm_i32, "trunc").map(Into::into).unwrap()
|
||||
}
|
||||
@ -126,7 +130,8 @@ pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -137,7 +142,7 @@ pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, "int32", &[n_ty]),
|
||||
@ -154,9 +159,11 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
Ok(match n {
|
||||
BasicValueEnum::IntValue(n) if matches!(n.get_type().get_bit_width(), 1 | 8 | 32) => {
|
||||
debug_assert!([ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.uint32,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.uint32,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
if ctx.unifier.unioned(n_ty, ctx.primitives.int32) {
|
||||
ctx.builder.build_int_s_extend(n, llvm_i64, "sext").map(Into::into).unwrap()
|
||||
@ -166,9 +173,11 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
|
||||
}
|
||||
|
||||
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 64 => {
|
||||
debug_assert!([ctx.primitives.int64, ctx.primitives.uint64,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[ctx.primitives.int64, ctx.primitives.uint64,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
n.into()
|
||||
}
|
||||
@ -186,7 +195,8 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -197,7 +207,7 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, "int64", &[n_ty]),
|
||||
@ -220,9 +230,11 @@ pub fn call_uint32<'ctx, G: CodeGenerator + ?Sized>(
|
||||
}
|
||||
|
||||
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 32 => {
|
||||
debug_assert!([ctx.primitives.int32, ctx.primitives.uint32,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[ctx.primitives.int32, ctx.primitives.uint32,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
n.into()
|
||||
}
|
||||
@ -262,7 +274,8 @@ pub fn call_uint32<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -273,7 +286,7 @@ pub fn call_uint32<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, "uint32", &[n_ty]),
|
||||
@ -290,9 +303,11 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
Ok(match n {
|
||||
BasicValueEnum::IntValue(n) if matches!(n.get_type().get_bit_width(), 1 | 8 | 32) => {
|
||||
debug_assert!([ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.uint32,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.uint32,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
if ctx.unifier.unioned(n_ty, ctx.primitives.int32) {
|
||||
ctx.builder.build_int_s_extend(n, llvm_i64, "sext").map(Into::into).unwrap()
|
||||
@ -302,9 +317,11 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
|
||||
}
|
||||
|
||||
BasicValueEnum::IntValue(n) if n.get_type().get_bit_width() == 64 => {
|
||||
debug_assert!([ctx.primitives.int64, ctx.primitives.uint64,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[ctx.primitives.int64, ctx.primitives.uint64,]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
n.into()
|
||||
}
|
||||
@ -327,7 +344,8 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -338,7 +356,7 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, "uint64", &[n_ty]),
|
||||
@ -355,15 +373,17 @@ pub fn call_float<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
Ok(match n {
|
||||
BasicValueEnum::IntValue(n) if matches!(n.get_type().get_bit_width(), 1 | 8 | 32 | 64) => {
|
||||
debug_assert!([
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
if [ctx.primitives.bool, ctx.primitives.int32, ctx.primitives.int64]
|
||||
.iter()
|
||||
@ -391,7 +411,8 @@ pub fn call_float<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -402,7 +423,7 @@ pub fn call_float<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, "float", &[n_ty]),
|
||||
@ -435,7 +456,8 @@ pub fn call_round<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -448,7 +470,7 @@ pub fn call_round<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
|
||||
@ -474,7 +496,8 @@ pub fn call_numpy_round<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -485,7 +508,7 @@ pub fn call_numpy_round<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
|
||||
@ -508,14 +531,16 @@ pub fn call_bool<'ctx, G: CodeGenerator + ?Sized>(
|
||||
}
|
||||
|
||||
BasicValueEnum::IntValue(n) => {
|
||||
debug_assert!([
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty)));
|
||||
debug_assert!(
|
||||
[
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(n_ty, *ty))
|
||||
);
|
||||
|
||||
ctx.builder
|
||||
.build_int_compare(IntPredicate::NE, n, n.get_type().const_zero(), FN_NAME)
|
||||
@ -536,7 +561,8 @@ pub fn call_bool<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -550,7 +576,7 @@ pub fn call_bool<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
|
||||
@ -587,7 +613,8 @@ pub fn call_floor<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -600,7 +627,7 @@ pub fn call_floor<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
|
||||
@ -637,7 +664,8 @@ pub fn call_ceil<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if n_ty.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::NDArray.id()) =>
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, n_ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, n_ty).map_pointer_value(n, None);
|
||||
|
||||
let result = ndarray
|
||||
.map(
|
||||
@ -650,7 +678,7 @@ pub fn call_ceil<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
|
||||
@ -673,15 +701,17 @@ pub fn call_min<'ctx>(
|
||||
|
||||
match (m, n) {
|
||||
(BasicValueEnum::IntValue(m), BasicValueEnum::IntValue(n)) => {
|
||||
debug_assert!([
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(common_ty, *ty)));
|
||||
debug_assert!(
|
||||
[
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(common_ty, *ty))
|
||||
);
|
||||
|
||||
if [ctx.primitives.int32, ctx.primitives.int64]
|
||||
.iter()
|
||||
@ -716,16 +746,18 @@ pub fn call_numpy_minimum<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
Ok(match (x1, x2) {
|
||||
(BasicValueEnum::IntValue(x1), BasicValueEnum::IntValue(x2)) => {
|
||||
debug_assert!([
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
ctx.primitives.float,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(common_ty.unwrap(), *ty)));
|
||||
debug_assert!(
|
||||
[
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
ctx.primitives.float,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(common_ty.unwrap(), *ty))
|
||||
);
|
||||
|
||||
call_min(ctx, (x1_ty, x1.into()), (x2_ty, x2.into()))
|
||||
}
|
||||
@ -767,7 +799,7 @@ pub fn call_numpy_minimum<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
|
||||
@ -790,15 +822,17 @@ pub fn call_max<'ctx>(
|
||||
|
||||
match (m, n) {
|
||||
(BasicValueEnum::IntValue(m), BasicValueEnum::IntValue(n)) => {
|
||||
debug_assert!([
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(common_ty, *ty)));
|
||||
debug_assert!(
|
||||
[
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(common_ty, *ty))
|
||||
);
|
||||
|
||||
if [ctx.primitives.int32, ctx.primitives.int64]
|
||||
.iter()
|
||||
@ -835,16 +869,18 @@ pub fn call_numpy_max_min<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
Ok(match a {
|
||||
BasicValueEnum::IntValue(_) | BasicValueEnum::FloatValue(_) => {
|
||||
debug_assert!([
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
ctx.primitives.float,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(a_ty, *ty)));
|
||||
debug_assert!(
|
||||
[
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
ctx.primitives.float,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(a_ty, *ty))
|
||||
);
|
||||
|
||||
match fn_name {
|
||||
"np_argmin" | "np_argmax" => llvm_int64.const_zero().into(),
|
||||
@ -858,7 +894,8 @@ pub fn call_numpy_max_min<'ctx, G: CodeGenerator + ?Sized>(
|
||||
{
|
||||
let (elem_ty, _) = unpack_ndarray_var_tys(&mut ctx.unifier, a_ty);
|
||||
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, a_ty).map_value(n, None);
|
||||
let ndarray =
|
||||
NDArrayType::from_unifier_type(generator, ctx, a_ty).map_pointer_value(n, None);
|
||||
let llvm_dtype = ndarray.get_type().element_type();
|
||||
|
||||
let zero = llvm_usize.const_zero();
|
||||
@ -975,16 +1012,18 @@ pub fn call_numpy_maximum<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
Ok(match (x1, x2) {
|
||||
(BasicValueEnum::IntValue(x1), BasicValueEnum::IntValue(x2)) => {
|
||||
debug_assert!([
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
ctx.primitives.float,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(common_ty.unwrap(), *ty)));
|
||||
debug_assert!(
|
||||
[
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
ctx.primitives.float,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(common_ty.unwrap(), *ty))
|
||||
);
|
||||
|
||||
call_max(ctx, (x1_ty, x1.into()), (x2_ty, x2.into()))
|
||||
}
|
||||
@ -1026,7 +1065,7 @@ pub fn call_numpy_maximum<'ctx, G: CodeGenerator + ?Sized>(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
result.as_base_value().into()
|
||||
result.as_abi_value(ctx).into()
|
||||
}
|
||||
|
||||
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
|
||||
@ -1090,15 +1129,17 @@ pub fn call_abs<'ctx, G: CodeGenerator + ?Sized>(
|
||||
&|_ctx, elem_ty| elem_ty,
|
||||
&|_generator, ctx, val_ty, val| match val {
|
||||
BasicValueEnum::IntValue(n) => Some({
|
||||
debug_assert!([
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(val_ty, *ty)));
|
||||
debug_assert!(
|
||||
[
|
||||
ctx.primitives.bool,
|
||||
ctx.primitives.int32,
|
||||
ctx.primitives.uint32,
|
||||
ctx.primitives.int64,
|
||||
ctx.primitives.uint64,
|
||||
]
|
||||
.iter()
|
||||
.any(|ty| ctx.unifier.unioned(val_ty, *ty))
|
||||
);
|
||||
|
||||
if [ctx.primitives.int32, ctx.primitives.int64]
|
||||
.iter()
|
||||
@ -1183,7 +1224,7 @@ macro_rules! create_helper_call_numpy_unary_elementwise_float_to_bool {
|
||||
BasicValueEnum::FloatValue(n) => {
|
||||
debug_assert!(ctx.unifier.unioned(n_ty, ctx.primitives.float));
|
||||
|
||||
let ret = $on_scalar(generator, ctx, n);
|
||||
let ret = $on_scalar(ctx, n, Option::<&str>::None);
|
||||
Some(generator.bool_to_i8(ctx, ret).into())
|
||||
}
|
||||
_ => None,
|
||||
@ -1252,55 +1293,55 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_tan,
|
||||
"np_tan",
|
||||
extern_fns::call_tan
|
||||
irrt::call_tan
|
||||
);
|
||||
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_arcsin,
|
||||
"np_arcsin",
|
||||
extern_fns::call_asin
|
||||
irrt::call_asin
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_arccos,
|
||||
"np_arccos",
|
||||
extern_fns::call_acos
|
||||
irrt::call_acos
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_arctan,
|
||||
"np_arctan",
|
||||
extern_fns::call_atan
|
||||
irrt::call_atan
|
||||
);
|
||||
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_sinh,
|
||||
"np_sinh",
|
||||
extern_fns::call_sinh
|
||||
irrt::call_sinh
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_cosh,
|
||||
"np_cosh",
|
||||
extern_fns::call_cosh
|
||||
irrt::call_cosh
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_tanh,
|
||||
"np_tanh",
|
||||
extern_fns::call_tanh
|
||||
irrt::call_tanh
|
||||
);
|
||||
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_arcsinh,
|
||||
"np_arcsinh",
|
||||
extern_fns::call_asinh
|
||||
irrt::call_asinh
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_arccosh,
|
||||
"np_arccosh",
|
||||
extern_fns::call_acosh
|
||||
irrt::call_acosh
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_arctanh,
|
||||
"np_arctanh",
|
||||
extern_fns::call_atanh
|
||||
irrt::call_atanh
|
||||
);
|
||||
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
@ -1316,7 +1357,7 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_expm1,
|
||||
"np_expm1",
|
||||
extern_fns::call_expm1
|
||||
irrt::call_expm1
|
||||
);
|
||||
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
@ -1343,7 +1384,7 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_numpy_cbrt,
|
||||
"np_cbrt",
|
||||
extern_fns::call_cbrt
|
||||
irrt::call_cbrt
|
||||
);
|
||||
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
@ -1360,17 +1401,17 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_scipy_special_erf,
|
||||
"sp_spec_erf",
|
||||
extern_fns::call_erf
|
||||
irrt::call_erf
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_scipy_special_erfc,
|
||||
"sp_spec_erfc",
|
||||
extern_fns::call_erfc
|
||||
irrt::call_erfc
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_scipy_special_gamma,
|
||||
"sp_spec_gamma",
|
||||
|ctx, val, _| irrt::call_gamma(ctx, val)
|
||||
irrt::call_gamma
|
||||
);
|
||||
create_helper_call_numpy_unary_elementwise_float_to_float!(
|
||||
call_scipy_special_gammaln,
|
||||
@ -1411,7 +1452,7 @@ pub fn call_numpy_arctan2<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
match (x1_scalar, x2_scalar) {
|
||||
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
|
||||
Ok(extern_fns::call_atan2(ctx, x1, x2, None).into())
|
||||
Ok(irrt::call_atan2(ctx, x1, x2, None).into())
|
||||
}
|
||||
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
|
||||
}
|
||||
@ -1549,7 +1590,7 @@ pub fn call_numpy_ldexp<'ctx, G: CodeGenerator + ?Sized>(
|
||||
(BasicValueEnum::FloatValue(x1_scalar), BasicValueEnum::IntValue(x2_scalar)) => {
|
||||
debug_assert_eq!(x1.get_dtype(), ctx.ctx.f64_type().into());
|
||||
debug_assert_eq!(x2.get_dtype(), ctx.ctx.i32_type().into());
|
||||
Ok(extern_fns::call_ldexp(ctx, x1_scalar, x2_scalar, None).into())
|
||||
Ok(irrt::call_ldexp(ctx, x1_scalar, x2_scalar, None).into())
|
||||
}
|
||||
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
|
||||
}
|
||||
@ -1583,7 +1624,7 @@ pub fn call_numpy_hypot<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
match (x1_scalar, x2_scalar) {
|
||||
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
|
||||
Ok(extern_fns::call_hypot(ctx, x1, x2, None).into())
|
||||
Ok(irrt::call_hypot(ctx, x1, x2, None).into())
|
||||
}
|
||||
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
|
||||
}
|
||||
@ -1617,7 +1658,7 @@ pub fn call_numpy_nextafter<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
match (x1_scalar, x2_scalar) {
|
||||
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
|
||||
Ok(extern_fns::call_nextafter(ctx, x1, x2, None).into())
|
||||
Ok(irrt::call_nextafter(ctx, x1, x2, None).into())
|
||||
}
|
||||
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
|
||||
}
|
||||
@ -1638,7 +1679,7 @@ pub fn call_np_linalg_cholesky<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
unsupported_type(ctx, FN_NAME, &[x1_ty]);
|
||||
@ -1653,11 +1694,11 @@ pub fn call_np_linalg_cholesky<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let out_c = out.make_contiguous_ndarray(generator, ctx);
|
||||
extern_fns::call_np_linalg_cholesky(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
out_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
out_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
Ok(out.as_base_value().into())
|
||||
Ok(out.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_qr` linalg function
|
||||
@ -1672,7 +1713,7 @@ pub fn call_np_linalg_qr<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
unsupported_type(ctx, FN_NAME, &[x1_ty]);
|
||||
@ -1699,20 +1740,20 @@ pub fn call_np_linalg_qr<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
extern_fns::call_np_linalg_qr(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
q_c.as_base_value().into(),
|
||||
r_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
q_c.as_abi_value(ctx).into(),
|
||||
r_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
let q = q.as_base_value().as_basic_value_enum();
|
||||
let r = r.as_base_value().as_basic_value_enum();
|
||||
let q = q.as_abi_value(ctx);
|
||||
let r = r.as_abi_value(ctx);
|
||||
let tuple = TupleType::new(ctx, &[q.get_type(), r.get_type()]).construct_from_objects(
|
||||
ctx,
|
||||
[q, r],
|
||||
[q.into(), r.into()],
|
||||
None,
|
||||
);
|
||||
Ok(tuple.as_base_value().into())
|
||||
Ok(tuple.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_svd` linalg function
|
||||
@ -1727,7 +1768,7 @@ pub fn call_np_linalg_svd<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
unsupported_type(ctx, FN_NAME, &[x1_ty]);
|
||||
@ -1760,19 +1801,19 @@ pub fn call_np_linalg_svd<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
extern_fns::call_np_linalg_svd(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
u_c.as_base_value().into(),
|
||||
s_c.as_base_value().into(),
|
||||
vh_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
u_c.as_abi_value(ctx).into(),
|
||||
s_c.as_abi_value(ctx).into(),
|
||||
vh_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
let u = u.as_base_value().as_basic_value_enum();
|
||||
let s = s.as_base_value().as_basic_value_enum();
|
||||
let vh = vh.as_base_value().as_basic_value_enum();
|
||||
let u = u.as_abi_value(ctx);
|
||||
let s = s.as_abi_value(ctx);
|
||||
let vh = vh.as_abi_value(ctx);
|
||||
let tuple = TupleType::new(ctx, &[u.get_type(), s.get_type(), vh.get_type()])
|
||||
.construct_from_objects(ctx, [u, s, vh], None);
|
||||
Ok(tuple.as_base_value().into())
|
||||
.construct_from_objects(ctx, [u.into(), s.into(), vh.into()], None);
|
||||
Ok(tuple.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_inv` linalg function
|
||||
@ -1785,7 +1826,7 @@ pub fn call_np_linalg_inv<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
unsupported_type(ctx, FN_NAME, &[x1_ty]);
|
||||
@ -1800,12 +1841,12 @@ pub fn call_np_linalg_inv<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let out_c = out.make_contiguous_ndarray(generator, ctx);
|
||||
extern_fns::call_np_linalg_inv(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
out_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
out_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(out.as_base_value().into())
|
||||
Ok(out.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_pinv` linalg function
|
||||
@ -1820,7 +1861,7 @@ pub fn call_np_linalg_pinv<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
unsupported_type(ctx, FN_NAME, &[x1_ty]);
|
||||
@ -1845,12 +1886,12 @@ pub fn call_np_linalg_pinv<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let out_c = out.make_contiguous_ndarray(generator, ctx);
|
||||
extern_fns::call_np_linalg_pinv(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
out_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
out_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(out.as_base_value().into())
|
||||
Ok(out.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
/// Invokes the `sp_linalg_lu` linalg function
|
||||
@ -1865,7 +1906,7 @@ pub fn call_sp_linalg_lu<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
unsupported_type(ctx, FN_NAME, &[x1_ty]);
|
||||
@ -1892,20 +1933,20 @@ pub fn call_sp_linalg_lu<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let u_c = u.make_contiguous_ndarray(generator, ctx);
|
||||
extern_fns::call_sp_linalg_lu(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
l_c.as_base_value().into(),
|
||||
u_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
l_c.as_abi_value(ctx).into(),
|
||||
u_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
let l = l.as_base_value().as_basic_value_enum();
|
||||
let u = u.as_base_value().as_basic_value_enum();
|
||||
let l = l.as_abi_value(ctx);
|
||||
let u = u.as_abi_value(ctx);
|
||||
let tuple = TupleType::new(ctx, &[l.get_type(), u.get_type()]).construct_from_objects(
|
||||
ctx,
|
||||
[l, u],
|
||||
[l.into(), u.into()],
|
||||
None,
|
||||
);
|
||||
Ok(tuple.as_base_value().into())
|
||||
Ok(tuple.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_matrix_power` linalg function
|
||||
@ -1953,13 +1994,13 @@ pub fn call_np_linalg_matrix_power<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
extern_fns::call_np_linalg_matrix_power(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
x2_c.as_base_value().into(),
|
||||
out_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
x2_c.as_abi_value(ctx).into(),
|
||||
out_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(out.as_base_value().into())
|
||||
Ok(out.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
/// Invokes the `np_linalg_det` linalg function
|
||||
@ -1974,7 +2015,7 @@ pub fn call_np_linalg_det<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
unsupported_type(ctx, FN_NAME, &[x1_ty]);
|
||||
@ -1993,8 +2034,8 @@ pub fn call_np_linalg_det<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let out_c = det.make_contiguous_ndarray(generator, ctx);
|
||||
extern_fns::call_np_linalg_det(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
out_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
out_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
@ -2013,7 +2054,7 @@ pub fn call_sp_linalg_schur<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
assert_eq!(x1.get_type().ndims(), 2);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
@ -2035,20 +2076,20 @@ pub fn call_sp_linalg_schur<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let z_c = z.make_contiguous_ndarray(generator, ctx);
|
||||
extern_fns::call_sp_linalg_schur(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
t_c.as_base_value().into(),
|
||||
z_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
t_c.as_abi_value(ctx).into(),
|
||||
z_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
let t = t.as_base_value().as_basic_value_enum();
|
||||
let z = z.as_base_value().as_basic_value_enum();
|
||||
let t = t.as_abi_value(ctx);
|
||||
let z = z.as_abi_value(ctx);
|
||||
let tuple = TupleType::new(ctx, &[t.get_type(), z.get_type()]).construct_from_objects(
|
||||
ctx,
|
||||
[t, z],
|
||||
[t.into(), z.into()],
|
||||
None,
|
||||
);
|
||||
Ok(tuple.as_base_value().into())
|
||||
Ok(tuple.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
/// Invokes the `sp_linalg_hessenberg` linalg function
|
||||
@ -2061,7 +2102,7 @@ pub fn call_sp_linalg_hessenberg<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
let BasicValueEnum::PointerValue(x1) = x1 else { unsupported_type(ctx, FN_NAME, &[x1_ty]) };
|
||||
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
|
||||
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(x1, None);
|
||||
assert_eq!(x1.get_type().ndims(), 2);
|
||||
|
||||
if !x1.get_type().element_type().is_float_type() {
|
||||
@ -2083,18 +2124,18 @@ pub fn call_sp_linalg_hessenberg<'ctx, G: CodeGenerator + ?Sized>(
|
||||
let q_c = q.make_contiguous_ndarray(generator, ctx);
|
||||
extern_fns::call_sp_linalg_hessenberg(
|
||||
ctx,
|
||||
x1_c.as_base_value().into(),
|
||||
h_c.as_base_value().into(),
|
||||
q_c.as_base_value().into(),
|
||||
x1_c.as_abi_value(ctx).into(),
|
||||
h_c.as_abi_value(ctx).into(),
|
||||
q_c.as_abi_value(ctx).into(),
|
||||
None,
|
||||
);
|
||||
|
||||
let h = h.as_base_value().as_basic_value_enum();
|
||||
let q = q.as_base_value().as_basic_value_enum();
|
||||
let h = h.as_abi_value(ctx);
|
||||
let q = q.as_abi_value(ctx);
|
||||
let tuple = TupleType::new(ctx, &[h.get_type(), q.get_type()]).construct_from_objects(
|
||||
ctx,
|
||||
[h, q],
|
||||
[h.into(), q.into()],
|
||||
None,
|
||||
);
|
||||
Ok(tuple.as_base_value().into())
|
||||
Ok(tuple.as_abi_value(ctx).into())
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
typecheck::{
|
||||
type_inferencer::PrimitiveStore,
|
||||
typedef::{
|
||||
into_var_map, FunSignature, FuncArg, Type, TypeEnum, TypeVar, TypeVarId, Unifier,
|
||||
FunSignature, FuncArg, Type, TypeEnum, TypeVar, TypeVarId, Unifier, into_var_map,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -6,12 +6,12 @@ use std::{
|
||||
};
|
||||
|
||||
use inkwell::{
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
types::{AnyType, BasicType, BasicTypeEnum},
|
||||
values::{BasicValueEnum, CallSiteValue, FunctionValue, IntValue, PointerValue, StructValue},
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
};
|
||||
use itertools::{izip, Either, Itertools};
|
||||
use itertools::{Either, Itertools, izip};
|
||||
|
||||
use nac3parser::ast::{
|
||||
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
|
||||
@ -19,6 +19,7 @@ use nac3parser::ast::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
CodeGenContext, CodeGenTask, CodeGenerator,
|
||||
concrete_type::{ConcreteFuncArg, ConcreteTypeEnum, ConcreteTypeStore},
|
||||
gen_in_range_check, get_llvm_abi_type, get_llvm_type, get_va_count_arg_name,
|
||||
irrt::*,
|
||||
@ -32,20 +33,21 @@ use super::{
|
||||
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
|
||||
gen_var,
|
||||
},
|
||||
types::{ndarray::NDArrayType, ListType},
|
||||
types::{
|
||||
ExceptionType, ListType, OptionType, RangeType, StringType, TupleType, ndarray::NDArrayType,
|
||||
},
|
||||
values::{
|
||||
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
|
||||
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
|
||||
UntypedArrayLikeAccessor,
|
||||
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
|
||||
},
|
||||
CodeGenContext, CodeGenTask, CodeGenerator,
|
||||
};
|
||||
use crate::{
|
||||
symbol_resolver::{SymbolValue, ValueEnum},
|
||||
toplevel::{
|
||||
helper::{arraylike_flatten_element_type, PrimDef},
|
||||
numpy::unpack_ndarray_var_tys,
|
||||
DefinitionId, TopLevelDef,
|
||||
helper::{PrimDef, arraylike_flatten_element_type, extract_ndims},
|
||||
numpy::unpack_ndarray_var_tys,
|
||||
},
|
||||
typecheck::{
|
||||
magic_methods::{Binop, BinopVariant, HasOpInfo},
|
||||
@ -71,7 +73,7 @@ pub fn get_subst_key(
|
||||
})
|
||||
.unwrap_or_default();
|
||||
vars.extend(fun_vars);
|
||||
let sorted = vars.keys().filter(|id| filter.map_or(true, |v| v.contains(id))).sorted();
|
||||
let sorted = vars.keys().filter(|id| filter.is_none_or(|v| v.contains(id))).sorted();
|
||||
sorted
|
||||
.map(|id| {
|
||||
unifier.internal_stringify(
|
||||
@ -122,7 +124,7 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
|
||||
|
||||
/// Checks the field and attributes of classes
|
||||
/// Returns the index of attr in class fields otherwise returns the attribute value
|
||||
pub fn get_attr_index(&mut self, ty: Type, attr: StrRef) -> (usize, Option<Constant>) {
|
||||
pub fn get_attr_index(&mut self, ty: Type, attr: StrRef) -> (Option<usize>, Option<Constant>) {
|
||||
let obj_id = match &*self.unifier.get_ty(ty) {
|
||||
TypeEnum::TObj { obj_id, .. } => *obj_id,
|
||||
TypeEnum::TModule { module_id, .. } => *module_id,
|
||||
@ -132,13 +134,16 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
|
||||
let def = &self.top_level.definitions.read()[obj_id.0];
|
||||
let (index, value) = if let TopLevelDef::Class { fields, attributes, .. } = &*def.read() {
|
||||
if let Some(field_index) = fields.iter().find_position(|x| x.0 == attr) {
|
||||
(field_index.0, None)
|
||||
(Some(field_index.0), None)
|
||||
} else {
|
||||
let attribute_index = attributes.iter().find_position(|x| x.0 == attr).unwrap();
|
||||
(attribute_index.0, Some(attribute_index.1 .2.clone()))
|
||||
let attribute_index = attributes.iter().find_position(|x| x.0 == attr);
|
||||
(
|
||||
attribute_index.map(|(idx, _)| idx),
|
||||
attribute_index.map(|(_, (_, _, k))| k.clone()),
|
||||
)
|
||||
}
|
||||
} else if let TopLevelDef::Module { attributes, .. } = &*def.read() {
|
||||
(attributes.iter().find_position(|x| x.0 == attr).unwrap().0, None)
|
||||
(attributes.iter().find_position(|x| x.0 == attr).map(|(idx, _)| idx), None)
|
||||
} else {
|
||||
codegen_unreachable!(self)
|
||||
};
|
||||
@ -168,65 +173,27 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
|
||||
SymbolValue::Bool(v) => self.ctx.i8_type().const_int(u64::from(*v), true).into(),
|
||||
SymbolValue::Double(v) => self.ctx.f64_type().const_float(*v).into(),
|
||||
SymbolValue::Str(v) => {
|
||||
let str_ptr = self
|
||||
.builder
|
||||
.build_global_string_ptr(v, "const")
|
||||
.map(|v| v.as_pointer_value().into())
|
||||
.unwrap();
|
||||
let size = self.get_size_type().const_int(v.len() as u64, false);
|
||||
let ty = self.get_llvm_type(generator, self.primitives.str).into_struct_type();
|
||||
ty.const_named_struct(&[str_ptr, size.into()]).into()
|
||||
StringType::new(self).construct_constant(self, v, None).as_abi_value(self).into()
|
||||
}
|
||||
SymbolValue::Tuple(ls) => {
|
||||
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec();
|
||||
let fields = vals.iter().map(BasicValueEnum::get_type).collect_vec();
|
||||
let ty = self.ctx.struct_type(&fields, false);
|
||||
let ptr = gen_var(self, ty.into(), Some("tuple")).unwrap();
|
||||
let zero = self.ctx.i32_type().const_zero();
|
||||
unsafe {
|
||||
for (i, val) in vals.into_iter().enumerate() {
|
||||
let p = self
|
||||
.builder
|
||||
.build_in_bounds_gep(
|
||||
ptr,
|
||||
&[zero, self.ctx.i32_type().const_int(i as u64, false)],
|
||||
"elemptr",
|
||||
)
|
||||
.unwrap();
|
||||
self.builder.build_store(p, val).unwrap();
|
||||
}
|
||||
}
|
||||
self.builder.build_load(ptr, "tup_val").unwrap()
|
||||
TupleType::new(self, &fields)
|
||||
.construct_from_objects(self, vals, Some("tup_val"))
|
||||
.as_abi_value(self)
|
||||
.into()
|
||||
}
|
||||
SymbolValue::OptionSome(v) => {
|
||||
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == self.primitives.option.obj_id(&self.unifier).unwrap() =>
|
||||
{
|
||||
*params.iter().next().unwrap().1
|
||||
}
|
||||
_ => codegen_unreachable!(self, "must be option type"),
|
||||
};
|
||||
let val = self.gen_symbol_val(generator, v, ty);
|
||||
let ptr = generator
|
||||
.gen_var_alloc(self, val.get_type(), Some("default_opt_some"))
|
||||
.unwrap();
|
||||
self.builder.build_store(ptr, val).unwrap();
|
||||
ptr.into()
|
||||
}
|
||||
SymbolValue::OptionNone => {
|
||||
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == self.primitives.option.obj_id(&self.unifier).unwrap() =>
|
||||
{
|
||||
*params.iter().next().unwrap().1
|
||||
}
|
||||
_ => codegen_unreachable!(self, "must be option type"),
|
||||
};
|
||||
let actual_ptr_type =
|
||||
self.get_llvm_type(generator, ty).ptr_type(AddressSpace::default());
|
||||
actual_ptr_type.const_null().into()
|
||||
OptionType::from_unifier_type(generator, self, ty)
|
||||
.construct_some_value(generator, self, &val, None)
|
||||
.as_abi_value(self)
|
||||
.into()
|
||||
}
|
||||
SymbolValue::OptionNone => OptionType::from_unifier_type(generator, self, ty)
|
||||
.construct_empty(generator, self, None)
|
||||
.as_abi_value(self)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,15 +288,10 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
|
||||
if let Some(v) = self.const_strings.get(v) {
|
||||
Some(*v)
|
||||
} else {
|
||||
let str_ptr = self
|
||||
.builder
|
||||
.build_global_string_ptr(v, "const")
|
||||
.map(|v| v.as_pointer_value().into())
|
||||
.unwrap();
|
||||
let size = self.get_size_type().const_int(v.len() as u64, false);
|
||||
let ty = self.get_llvm_type(generator, self.primitives.str);
|
||||
let val =
|
||||
ty.into_struct_type().const_named_struct(&[str_ptr, size.into()]).into();
|
||||
let val = StringType::new(self)
|
||||
.construct_constant(self, v, None)
|
||||
.as_abi_value(self)
|
||||
.into();
|
||||
self.const_strings.insert(v.to_string(), val);
|
||||
Some(val)
|
||||
}
|
||||
@ -619,42 +581,35 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
loc: Location,
|
||||
) {
|
||||
let llvm_i32 = self.ctx.i32_type();
|
||||
let llvm_i64 = self.ctx.i64_type();
|
||||
let llvm_exn = ExceptionType::get_instance(generator, self);
|
||||
|
||||
let zelf = if let Some(exception_val) = self.exception_val {
|
||||
exception_val
|
||||
llvm_exn.map_pointer_value(exception_val, Some("exn"))
|
||||
} else {
|
||||
let ty = self.get_llvm_type(generator, self.primitives.exception).into_pointer_type();
|
||||
let zelf_ty: BasicTypeEnum = ty.get_element_type().into_struct_type().into();
|
||||
let zelf = generator.gen_var_alloc(self, zelf_ty, Some("exn")).unwrap();
|
||||
*self.exception_val.insert(zelf)
|
||||
let zelf = llvm_exn.alloca_var(generator, self, Some("exn"));
|
||||
self.exception_val = Some(zelf.as_abi_value(self));
|
||||
zelf
|
||||
};
|
||||
let int32 = self.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
unsafe {
|
||||
let id_ptr = self.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id").unwrap();
|
||||
let id = self.resolver.get_string_id(name);
|
||||
self.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap();
|
||||
let ptr = self
|
||||
.builder
|
||||
.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg")
|
||||
.unwrap();
|
||||
self.builder.build_store(ptr, msg).unwrap();
|
||||
let i64_zero = self.ctx.i64_type().const_zero();
|
||||
for (i, attr_ind) in [6, 7, 8].iter().enumerate() {
|
||||
let ptr = self
|
||||
.builder
|
||||
.build_in_bounds_gep(
|
||||
zelf,
|
||||
&[zero, int32.const_int(*attr_ind, false)],
|
||||
"exn.param",
|
||||
)
|
||||
.unwrap();
|
||||
let val = params[i].map_or(i64_zero, |v| {
|
||||
self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").unwrap()
|
||||
});
|
||||
self.builder.build_store(ptr, val).unwrap();
|
||||
}
|
||||
}
|
||||
gen_raise(generator, self, Some(&zelf.into()), loc);
|
||||
|
||||
let id = self.resolver.get_string_id(name);
|
||||
zelf.store_name(self, llvm_i32.const_int(id as u64, false));
|
||||
zelf.store_message(self, msg.into_struct_value());
|
||||
zelf.store_params(
|
||||
self,
|
||||
params
|
||||
.iter()
|
||||
.map(|p| {
|
||||
p.map_or(llvm_i64.const_zero(), |v| {
|
||||
self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").unwrap()
|
||||
})
|
||||
})
|
||||
.collect_array()
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
);
|
||||
gen_raise(generator, self, Some(&zelf), loc);
|
||||
}
|
||||
|
||||
pub fn make_assert<G: CodeGenerator + ?Sized>(
|
||||
@ -830,11 +785,10 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
|
||||
let definition = ctx.top_level.definitions.read().get(fun.1 .0).cloned().unwrap();
|
||||
let definition = ctx.top_level.definitions.read().get(fun.1.0).cloned().unwrap();
|
||||
let id;
|
||||
let key;
|
||||
let param_vals;
|
||||
let is_extern;
|
||||
let vararg_arg;
|
||||
|
||||
// Ensure that the function object only contains up to 1 vararg parameter
|
||||
@ -853,7 +807,6 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||
if let Some(callback) = codegen_callback {
|
||||
return callback.run(ctx, obj, fun, params, generator);
|
||||
}
|
||||
is_extern = instance_to_stmt.is_empty();
|
||||
vararg_arg = fun.0.args.iter().find(|arg| arg.is_vararg);
|
||||
let old_key = ctx.get_subst_key(obj.as_ref().map(|a| a.0), fun.0, None);
|
||||
let mut keys = fun.0.args.clone();
|
||||
@ -913,9 +866,10 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||
} else {
|
||||
mapping.insert(
|
||||
k.name,
|
||||
vec![ctx
|
||||
.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty)
|
||||
.into()],
|
||||
vec![
|
||||
ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty)
|
||||
.into(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -985,7 +939,7 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||
instance_to_symbol.get(&key).cloned().ok_or_else(String::new)
|
||||
}
|
||||
TopLevelDef::Class { .. } => {
|
||||
return Ok(Some(generator.gen_constructor(ctx, fun.0, &def, params)?))
|
||||
return Ok(Some(generator.gen_constructor(ctx, fun.0, &def, params)?));
|
||||
}
|
||||
TopLevelDef::Variable { .. } | TopLevelDef::Module { .. } => unreachable!(),
|
||||
}
|
||||
@ -1006,22 +960,11 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||
} else {
|
||||
Some(ctx.get_llvm_abi_type(generator, fun.0.ret))
|
||||
};
|
||||
let has_sret = ret_type.map_or(false, |ret_type| need_sret(ret_type));
|
||||
let mut byrefs = Vec::new();
|
||||
let has_sret = ret_type.is_some_and(|ret_type| need_sret(ret_type));
|
||||
let mut params = args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, arg)| !arg.is_vararg)
|
||||
.map(|(i, arg)| {
|
||||
match ctx.get_llvm_abi_type(generator, arg.ty) {
|
||||
BasicTypeEnum::StructType(ty) if is_extern => {
|
||||
byrefs.push((i, ty));
|
||||
ty.ptr_type(AddressSpace::default()).into()
|
||||
}
|
||||
x => x,
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.filter(|arg| !arg.is_vararg)
|
||||
.map(|arg| ctx.get_llvm_abi_type(generator, arg.ty).into())
|
||||
.collect_vec();
|
||||
if has_sret {
|
||||
params.insert(0, ret_type.unwrap().ptr_type(AddressSpace::default()).into());
|
||||
@ -1035,7 +978,7 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||
_ => ctx.ctx.void_type().fn_type(¶ms, is_vararg),
|
||||
};
|
||||
let fun_val = ctx.module.add_function(&symbol, fun_ty, None);
|
||||
let offset = if has_sret {
|
||||
if has_sret {
|
||||
fun_val.add_attribute(
|
||||
AttributeLoc::Param(0),
|
||||
ctx.ctx.create_type_attribute(
|
||||
@ -1043,23 +986,8 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
|
||||
ret_type.unwrap().as_any_type_enum(),
|
||||
),
|
||||
);
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// The attribute ID used to mark arguments of a structure type.
|
||||
// Structure-Typed parameters of extern functions must **not** be marked as `byval`, as
|
||||
// `byval` explicitly specifies that the argument is to be passed on the stack, which breaks
|
||||
// on most ABIs where the first several arguments are expected to be passed in registers.
|
||||
let passing_attr_id =
|
||||
Attribute::get_named_enum_kind_id(if is_extern { "byref" } else { "byval" });
|
||||
for (i, ty) in byrefs {
|
||||
fun_val.add_attribute(
|
||||
AttributeLoc::Param((i as u32) + offset),
|
||||
ctx.ctx.create_type_attribute(passing_attr_id, ty.as_any_type_enum()),
|
||||
);
|
||||
}
|
||||
|
||||
fun_val
|
||||
});
|
||||
|
||||
@ -1151,7 +1079,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
let iter_val =
|
||||
RangeValue::from_pointer_value(iter_val.into_pointer_value(), Some("range"));
|
||||
RangeType::new(ctx).map_pointer_value(iter_val.into_pointer_value(), Some("range"));
|
||||
let (start, stop, step) = destructure_range(ctx, iter_val);
|
||||
let diff = ctx.builder.build_int_sub(stop, start, "diff").unwrap();
|
||||
// add 1 to the length as the value is rounded to zero
|
||||
@ -1307,7 +1235,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
|
||||
|
||||
emit_cont_bb(ctx, list);
|
||||
|
||||
Ok(Some(list.as_base_value().into()))
|
||||
Ok(Some(list.as_abi_value(ctx).into()))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for a binary operator expression using the [`Type`] and
|
||||
@ -1319,7 +1247,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
op: Binop,
|
||||
right: (&Option<Type>, BasicValueEnum<'ctx>),
|
||||
loc: Location,
|
||||
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
let (left_ty, left_val) = left;
|
||||
let (right_ty, right_val) = right;
|
||||
|
||||
@ -1330,14 +1258,14 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
// which would be unchanged until further unification, which we would never do
|
||||
// when doing code generation for function instances
|
||||
if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
|
||||
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, true).into()))
|
||||
Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, true))
|
||||
} else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) {
|
||||
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, false).into()))
|
||||
Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, false))
|
||||
} else if [Operator::LShift, Operator::RShift].contains(&op.base) {
|
||||
let signed = [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1);
|
||||
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, signed).into()))
|
||||
Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, signed))
|
||||
} else if ty1 == ty2 && ctx.primitives.float == ty1 {
|
||||
Ok(Some(ctx.gen_float_ops(op.base, left_val, right_val).into()))
|
||||
Ok(ctx.gen_float_ops(op.base, left_val, right_val))
|
||||
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
|
||||
// Pow is the only operator that would pass typecheck between float and int
|
||||
assert_eq!(op.base, Operator::Pow);
|
||||
@ -1347,7 +1275,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
right_val.into_int_value(),
|
||||
Some("f_pow_i"),
|
||||
);
|
||||
Ok(Some(res.into()))
|
||||
Ok(res.into())
|
||||
} else if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())
|
||||
|| ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())
|
||||
{
|
||||
@ -1377,7 +1305,10 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
debug_assert!(ctx.unifier.unioned(elem_ty1, elem_ty2));
|
||||
|
||||
let llvm_elem_ty = ctx.get_llvm_type(generator, elem_ty1);
|
||||
let sizeof_elem = llvm_elem_ty.size_of().unwrap();
|
||||
let sizeof_elem = ctx
|
||||
.builder
|
||||
.build_int_truncate_or_bit_cast(llvm_elem_ty.size_of().unwrap(), llvm_usize, "")
|
||||
.unwrap();
|
||||
|
||||
let lhs =
|
||||
ListValue::from_pointer_value(left_val.into_pointer_value(), llvm_usize, None);
|
||||
@ -1392,34 +1323,14 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
let new_list =
|
||||
ListType::new(ctx, &llvm_elem_ty).construct(generator, ctx, size, None);
|
||||
|
||||
let lhs_size = ctx
|
||||
.builder
|
||||
.build_int_z_extend_or_bit_cast(
|
||||
lhs.load_size(ctx, None),
|
||||
sizeof_elem.get_type(),
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
let lhs_size = lhs.load_size(ctx, None);
|
||||
let lhs_len = ctx.builder.build_int_mul(lhs_size, sizeof_elem, "").unwrap();
|
||||
|
||||
let rhs_size = ctx
|
||||
.builder
|
||||
.build_int_z_extend_or_bit_cast(
|
||||
rhs.load_size(ctx, None),
|
||||
sizeof_elem.get_type(),
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
let rhs_size = rhs.load_size(ctx, None);
|
||||
let rhs_len = ctx.builder.build_int_mul(rhs_size, sizeof_elem, "").unwrap();
|
||||
|
||||
let list_ptr = new_list.data().base_ptr(ctx, generator);
|
||||
call_memcpy_generic(
|
||||
ctx,
|
||||
list_ptr,
|
||||
lhs.data().base_ptr(ctx, generator),
|
||||
lhs_len,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
);
|
||||
call_memcpy_generic(ctx, list_ptr, lhs.data().base_ptr(ctx, generator), lhs_len);
|
||||
|
||||
let list_ptr = unsafe {
|
||||
new_list.data().ptr_offset_unchecked(
|
||||
@ -1429,15 +1340,9 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
None,
|
||||
)
|
||||
};
|
||||
call_memcpy_generic(
|
||||
ctx,
|
||||
list_ptr,
|
||||
rhs.data().base_ptr(ctx, generator),
|
||||
rhs_len,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
);
|
||||
call_memcpy_generic(ctx, list_ptr, rhs.data().base_ptr(ctx, generator), rhs_len);
|
||||
|
||||
Ok(Some(new_list.as_base_value().into()))
|
||||
Ok(new_list.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
Operator::Mult => {
|
||||
@ -1475,7 +1380,10 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
let int_val = call_int_smax(ctx, int_val, llvm_usize.const_zero(), None);
|
||||
|
||||
let elem_llvm_ty = ctx.get_llvm_type(generator, elem_ty);
|
||||
let sizeof_elem = elem_llvm_ty.size_of().unwrap();
|
||||
let sizeof_elem = ctx
|
||||
.builder
|
||||
.build_int_truncate_or_bit_cast(elem_llvm_ty.size_of().unwrap(), llvm_usize, "")
|
||||
.unwrap();
|
||||
|
||||
let new_list = ListType::new(ctx, &elem_llvm_ty).construct(
|
||||
generator,
|
||||
@ -1499,14 +1407,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
new_list.data().ptr_offset_unchecked(ctx, generator, &offset, None)
|
||||
};
|
||||
|
||||
let list_size = ctx
|
||||
.builder
|
||||
.build_int_z_extend_or_bit_cast(
|
||||
list_val.load_size(ctx, None),
|
||||
sizeof_elem.get_type(),
|
||||
"",
|
||||
)
|
||||
.unwrap();
|
||||
let list_size = list_val.load_size(ctx, None);
|
||||
|
||||
let memcpy_sz =
|
||||
ctx.builder.build_int_mul(list_size, sizeof_elem, "").unwrap();
|
||||
@ -1516,7 +1417,6 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
ptr,
|
||||
list_val.data().base_ptr(ctx, generator),
|
||||
memcpy_sz,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@ -1524,7 +1424,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
llvm_usize.const_int(1, false),
|
||||
)?;
|
||||
|
||||
Ok(Some(new_list.as_base_value().into()))
|
||||
Ok(new_list.as_abi_value(ctx).into())
|
||||
}
|
||||
|
||||
_ => todo!("Operator not supported"),
|
||||
@ -1563,7 +1463,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
let result = left
|
||||
.matmul(generator, ctx, ty1, (ty2, right), (common_dtype, out))
|
||||
.split_unsized(generator, ctx);
|
||||
Ok(Some(result.to_basic_value_enum().into()))
|
||||
Ok(result.to_basic_value_enum())
|
||||
} else {
|
||||
// For other operations, they are all elementwise operations.
|
||||
|
||||
@ -1594,14 +1494,12 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
op,
|
||||
(&Some(ty2_dtype), right_value),
|
||||
ctx.current_loc,
|
||||
)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, common_dtype)?;
|
||||
)?;
|
||||
|
||||
Ok(result)
|
||||
})
|
||||
.unwrap();
|
||||
Ok(Some(result.as_base_value().into()))
|
||||
Ok(result.as_abi_value(ctx).into())
|
||||
}
|
||||
} else {
|
||||
let left_ty_enum = ctx.unifier.get_ty_immutable(left_ty.unwrap());
|
||||
@ -1650,7 +1548,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
(&signature, fun_id),
|
||||
vec![(None, right_val.into())],
|
||||
)
|
||||
.map(|f| f.map(Into::into))
|
||||
.map(Option::unwrap)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1688,6 +1586,7 @@ pub fn gen_binop_expr<'ctx, G: CodeGenerator>(
|
||||
(&right.custom, right_val),
|
||||
loc,
|
||||
)
|
||||
.map(|res| Some(res.into()))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for a unary operator expression using the [`Type`] and
|
||||
@ -1697,18 +1596,19 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
op: ast::Unaryop,
|
||||
operand: (&Option<Type>, BasicValueEnum<'ctx>),
|
||||
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
let (ty, val) = operand;
|
||||
let ty = ctx.unifier.get_representative(ty.unwrap());
|
||||
|
||||
Ok(Some(if ty == ctx.primitives.bool {
|
||||
Ok(if ty == ctx.primitives.bool {
|
||||
let val = val.into_int_value();
|
||||
if op == ast::Unaryop::Not {
|
||||
let not = ctx.builder.build_not(val, "not").unwrap();
|
||||
let not_bool =
|
||||
ctx.builder.build_and(not, not.get_type().const_int(1, false), "").unwrap();
|
||||
let not = ctx
|
||||
.builder
|
||||
.build_int_compare(IntPredicate::EQ, val, val.get_type().const_zero(), "not")
|
||||
.unwrap();
|
||||
|
||||
not_bool.into()
|
||||
generator.bool_to_int_type(ctx, not, val.get_type()).into()
|
||||
} else {
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
|
||||
@ -1721,7 +1621,6 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
ctx.builder.build_int_z_extend(val, llvm_i32, "").map(Into::into).unwrap(),
|
||||
),
|
||||
)?
|
||||
.unwrap()
|
||||
}
|
||||
} else if [
|
||||
ctx.primitives.int32,
|
||||
@ -1767,7 +1666,7 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
let (ndarray_dtype, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty);
|
||||
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ty)
|
||||
.map_value(val.into_pointer_value(), None);
|
||||
.map_pointer_value(val.into_pointer_value(), None);
|
||||
|
||||
// ndarray uses `~` rather than `not` to perform elementwise inversion, convert it before
|
||||
// passing it to the elementwise codegen function
|
||||
@ -1775,10 +1674,13 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
if op == ast::Unaryop::Invert {
|
||||
ast::Unaryop::Not
|
||||
} else {
|
||||
let ndims = extract_ndims(&ctx.unifier, ty);
|
||||
|
||||
codegen_unreachable!(
|
||||
ctx,
|
||||
"ufunc {} not supported for ndarray[bool, N]",
|
||||
"ufunc {} not supported for ndarray[bool, {}]",
|
||||
op.op_info().method_name,
|
||||
ndims,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@ -1790,16 +1692,14 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
ctx,
|
||||
NDArrayOut::NewNDArray { dtype: ndarray.get_type().element_type() },
|
||||
|generator, ctx, scalar| {
|
||||
gen_unaryop_expr_with_values(generator, ctx, op, (&Some(ndarray_dtype), scalar))?
|
||||
.map(|val| val.to_basic_value_enum(ctx, generator, ndarray_dtype))
|
||||
.unwrap()
|
||||
gen_unaryop_expr_with_values(generator, ctx, op, (&Some(ndarray_dtype), scalar))
|
||||
},
|
||||
)?;
|
||||
|
||||
mapped_ndarray.as_base_value().into()
|
||||
mapped_ndarray.as_abi_value(ctx).into()
|
||||
} else {
|
||||
unimplemented!()
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for a unary operator expression.
|
||||
@ -1819,6 +1719,7 @@ pub fn gen_unaryop_expr<'ctx, G: CodeGenerator>(
|
||||
};
|
||||
|
||||
gen_unaryop_expr_with_values(generator, ctx, op, (&operand.custom, val))
|
||||
.map(|res| Some(res.into()))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for a comparison operator expression using the [`Type`] and
|
||||
@ -1829,7 +1730,7 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
left: (Option<Type>, BasicValueEnum<'ctx>),
|
||||
ops: &[ast::Cmpop],
|
||||
comparators: &[(Option<Type>, BasicValueEnum<'ctx>)],
|
||||
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
debug_assert_eq!(comparators.len(), ops.len());
|
||||
|
||||
if comparators.len() == 1 {
|
||||
@ -1871,19 +1772,13 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
(Some(left_ty_dtype), left_scalar),
|
||||
&[op],
|
||||
&[(Some(right_ty_dtype), right_scalar)],
|
||||
)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
ctx.primitives.bool,
|
||||
)?;
|
||||
|
||||
Ok(generator.bool_to_i8(ctx, val.into_int_value()).into())
|
||||
},
|
||||
)?;
|
||||
|
||||
return Ok(Some(result_ndarray.as_base_value().into()));
|
||||
return Ok(result_ndarray.as_abi_value(ctx).into());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1967,41 +1862,19 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
} else if left_ty == ctx.primitives.str {
|
||||
assert!(ctx.unifier.unioned(left_ty, right_ty));
|
||||
|
||||
let lhs = lhs.into_struct_value();
|
||||
let rhs = rhs.into_struct_value();
|
||||
let llvm_str = StringType::new(ctx);
|
||||
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let lhs = llvm_str.map_struct_value(lhs.into_struct_value(), None);
|
||||
let rhs = llvm_str.map_struct_value(rhs.into_struct_value(), None);
|
||||
|
||||
let plhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap();
|
||||
ctx.builder.build_store(plhs, lhs).unwrap();
|
||||
let prhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap();
|
||||
ctx.builder.build_store(prhs, rhs).unwrap();
|
||||
|
||||
let lhs_ptr = ctx.build_in_bounds_gep_and_load(
|
||||
plhs,
|
||||
&[llvm_usize.const_zero(), llvm_i32.const_zero()],
|
||||
None,
|
||||
).into_pointer_value();
|
||||
let lhs_len = ctx.build_in_bounds_gep_and_load(
|
||||
plhs,
|
||||
&[llvm_usize.const_zero(), llvm_i32.const_int(1, false)],
|
||||
None,
|
||||
).into_int_value();
|
||||
|
||||
let rhs_ptr = ctx.build_in_bounds_gep_and_load(
|
||||
prhs,
|
||||
&[llvm_usize.const_zero(), llvm_i32.const_zero()],
|
||||
None,
|
||||
).into_pointer_value();
|
||||
let rhs_len = ctx.build_in_bounds_gep_and_load(
|
||||
prhs,
|
||||
&[llvm_usize.const_zero(), llvm_i32.const_int(1, false)],
|
||||
None,
|
||||
).into_int_value();
|
||||
let result = call_string_eq(ctx, lhs_ptr, lhs_len, rhs_ptr, rhs_len);
|
||||
let result = call_string_eq(ctx, lhs, rhs);
|
||||
if *op == Cmpop::NotEq {
|
||||
ctx.builder.build_not(result, "").unwrap()
|
||||
gen_unaryop_expr_with_values(
|
||||
generator,
|
||||
ctx,
|
||||
Unaryop::Not,
|
||||
(&Some(ctx.primitives.bool), result.into()),
|
||||
)?.into_int_value()
|
||||
} else {
|
||||
result
|
||||
}
|
||||
@ -2104,9 +1977,6 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
&[Cmpop::Eq],
|
||||
&[(Some(right_elem_ty), right)],
|
||||
)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
|
||||
.unwrap()
|
||||
.into_int_value();
|
||||
|
||||
gen_if_callback(
|
||||
@ -2155,8 +2025,6 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
Unaryop::Not,
|
||||
(&Some(ctx.primitives.bool), acc.into()),
|
||||
)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?
|
||||
.into_int_value()
|
||||
} else {
|
||||
acc
|
||||
@ -2244,11 +2112,6 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
&[op],
|
||||
&[(Some(right_ty), right_elem)],
|
||||
)
|
||||
.transpose()
|
||||
.unwrap()
|
||||
.and_then(|v| {
|
||||
v.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
|
||||
})
|
||||
.map(BasicValueEnum::into_int_value)?;
|
||||
|
||||
Ok(ctx.builder.build_not(
|
||||
@ -2285,7 +2148,12 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
|
||||
// Invert the final value if __ne__
|
||||
if *op == Cmpop::NotEq {
|
||||
ctx.builder.build_not(cmp_phi, "").unwrap()
|
||||
gen_unaryop_expr_with_values(
|
||||
generator,
|
||||
ctx,
|
||||
Unaryop::Not,
|
||||
(&Some(ctx.primitives.bool), cmp_phi.into()),
|
||||
)?.into_int_value()
|
||||
} else {
|
||||
cmp_phi
|
||||
}
|
||||
@ -2310,12 +2178,9 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
|
||||
};
|
||||
|
||||
Ok(prev?.map(|v| ctx.builder.build_and(v, current, "cmp").unwrap()).or(Some(current)))
|
||||
})?;
|
||||
})?.unwrap();
|
||||
|
||||
Ok(Some(match cmp_val {
|
||||
Some(v) => v.into(),
|
||||
None => return Ok(None),
|
||||
}))
|
||||
Ok(cmp_val.into())
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for a comparison operator expression.
|
||||
@ -2362,6 +2227,7 @@ pub fn gen_cmpop_expr<'ctx, G: CodeGenerator>(
|
||||
ops,
|
||||
comparator_vals.as_slice(),
|
||||
)
|
||||
.map(|res| Some(res.into()))
|
||||
}
|
||||
|
||||
/// See [`CodeGenerator::gen_expr`].
|
||||
@ -2391,16 +2257,13 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
const_val.into()
|
||||
}
|
||||
ExprKind::Name { id, .. } if id == &"none".into() => {
|
||||
match (
|
||||
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
|
||||
ctx.unifier.get_ty(ctx.primitives.option).as_ref(),
|
||||
) {
|
||||
(TypeEnum::TObj { obj_id, params, .. }, TypeEnum::TObj { obj_id: opt_id, .. })
|
||||
if *obj_id == *opt_id =>
|
||||
match &*ctx.unifier.get_ty(expr.custom.unwrap()) {
|
||||
TypeEnum::TObj { obj_id, .. }
|
||||
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
ctx.get_llvm_type(generator, *params.iter().next().unwrap().1)
|
||||
.ptr_type(AddressSpace::default())
|
||||
.const_null()
|
||||
OptionType::from_unifier_type(generator, ctx, expr.custom.unwrap())
|
||||
.construct_empty(generator, ctx, None)
|
||||
.as_abi_value(ctx)
|
||||
.into()
|
||||
}
|
||||
_ => codegen_unreachable!(ctx, "must be option type"),
|
||||
@ -2493,7 +2356,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
);
|
||||
ctx.builder.build_store(elem_ptr, *v).unwrap();
|
||||
}
|
||||
arr_str_ptr.as_base_value().into()
|
||||
arr_str_ptr.as_abi_value(ctx).into()
|
||||
}
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
let elements_val = elts
|
||||
@ -2601,7 +2464,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
let (index, _) = ctx.get_attr_index(value.custom.unwrap(), *attr);
|
||||
Ok(ValueEnum::Dynamic(ctx.build_gep_and_load(
|
||||
v.into_pointer_value(),
|
||||
&[zero, int32.const_int(index as u64, false)],
|
||||
&[zero, int32.const_int(index.unwrap() as u64, false)],
|
||||
None,
|
||||
))) as Result<_, String>
|
||||
},
|
||||
@ -2618,7 +2481,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
}
|
||||
ValueEnum::Dynamic(ctx.build_gep_and_load(
|
||||
v.into_pointer_value(),
|
||||
&[zero, int32.const_int(index as u64, false)],
|
||||
&[zero, int32.const_int(index.unwrap() as u64, false)],
|
||||
None,
|
||||
))
|
||||
}
|
||||
@ -2703,7 +2566,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
}
|
||||
ExprKind::UnaryOp { op, operand } => return gen_unaryop_expr(generator, ctx, *op, operand),
|
||||
ExprKind::Compare { left, ops, comparators } => {
|
||||
return gen_cmpop_expr(generator, ctx, left, ops, comparators)
|
||||
return gen_cmpop_expr(generator, ctx, left, ops, comparators);
|
||||
}
|
||||
ExprKind::IfExp { test, body, orelse } => {
|
||||
let test = match generator.gen_expr(ctx, test)? {
|
||||
@ -2885,8 +2748,12 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
};
|
||||
}
|
||||
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
|
||||
let not_null =
|
||||
ctx.builder.build_is_not_null(ptr, "unwrap_not_null").unwrap();
|
||||
let option = OptionType::from_pointer_type(
|
||||
ptr.get_type(),
|
||||
ctx.get_size_type(),
|
||||
)
|
||||
.map_pointer_value(ptr, None);
|
||||
let not_null = option.is_some(ctx);
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
not_null,
|
||||
@ -2895,12 +2762,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
[None, None, None],
|
||||
expr.location,
|
||||
);
|
||||
return Ok(Some(
|
||||
ctx.builder
|
||||
.build_load(ptr, "unwrap_some_load")
|
||||
.map(Into::into)
|
||||
.unwrap(),
|
||||
));
|
||||
return Ok(Some(unsafe { option.load(ctx).into() }));
|
||||
}
|
||||
ValueEnum::Dynamic(_) => {
|
||||
codegen_unreachable!(ctx, "option must be static or ptr")
|
||||
@ -2988,7 +2850,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
v,
|
||||
(start, end, step),
|
||||
);
|
||||
res_array_ret.as_base_value().into()
|
||||
res_array_ret.as_abi_value(ctx).into()
|
||||
} else {
|
||||
let len = v.load_size(ctx, Some("len"));
|
||||
let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? {
|
||||
@ -3043,14 +2905,14 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
|
||||
let ndarray_ty = value.custom.unwrap();
|
||||
let ndarray = ndarray.to_basic_value_enum(ctx, generator, ndarray_ty)?;
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
|
||||
.map_value(ndarray.into_pointer_value(), None);
|
||||
.map_pointer_value(ndarray.into_pointer_value(), None);
|
||||
|
||||
let indices = RustNDIndex::from_subscript_expr(generator, ctx, slice)?;
|
||||
let result = ndarray
|
||||
.index(generator, ctx, &indices)
|
||||
.split_unsized(generator, ctx)
|
||||
.to_basic_value_enum();
|
||||
return Ok(Some(ValueEnum::Dynamic(result)));
|
||||
return Ok(Some(result.into()));
|
||||
}
|
||||
TypeEnum::TTuple { .. } => {
|
||||
let index: u32 =
|
||||
@ -3148,9 +3010,8 @@ pub fn create_and_call_function<'ctx>(
|
||||
value_name: Option<&str>,
|
||||
configure: Option<&dyn Fn(&FunctionValue<'ctx>)>,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
let param_tys = params.iter().map(|(ty, _)| ty).copied().map(BasicTypeEnum::into).collect_vec();
|
||||
let arg_values =
|
||||
params.iter().map(|(_, value)| value).copied().map(BasicValueEnum::into).collect_vec();
|
||||
let param_tys = params.iter().map(|(ty, _)| ty).copied().collect_vec();
|
||||
let arg_values = params.iter().map(|(_, value)| value).copied().collect_vec();
|
||||
|
||||
create_fn_and_call(
|
||||
ctx,
|
||||
|
@ -1,10 +1,9 @@
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue},
|
||||
values::{BasicValueEnum, FloatValue},
|
||||
};
|
||||
use itertools::Either;
|
||||
|
||||
use super::CodeGenContext;
|
||||
use super::{CodeGenContext, expr::infer_and_call_function};
|
||||
|
||||
/// Macro to generate extern function
|
||||
/// Both function return type and function parameter type are `FloatValue`
|
||||
@ -37,8 +36,8 @@ macro_rules! generate_extern_fn {
|
||||
($fn_name:ident, $extern_fn:literal $(,$args:ident)* $(,$attributes:literal)*) => {
|
||||
#[doc = concat!("Invokes the [`", stringify!($extern_fn), "`](https://en.cppreference.com/w/c/numeric/math/", stringify!($llvm_name), ") function." )]
|
||||
pub fn $fn_name<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>
|
||||
$(,$args: FloatValue<'ctx>)*,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
$($args: FloatValue<'ctx>,)*
|
||||
name: Option<&str>,
|
||||
) -> FloatValue<'ctx> {
|
||||
const FN_NAME: &str = $extern_fn;
|
||||
@ -46,99 +45,35 @@ macro_rules! generate_extern_fn {
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
$(debug_assert_eq!($args.get_type(), llvm_f64);)*
|
||||
|
||||
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
|
||||
let fn_type = llvm_f64.fn_type(&[$($args.get_type().into()),*], false);
|
||||
let func = ctx.module.add_function(FN_NAME, fn_type, None);
|
||||
for attr in [$($attributes),*] {
|
||||
func.add_attribute(
|
||||
AttributeLoc::Function,
|
||||
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
|
||||
);
|
||||
}
|
||||
func
|
||||
});
|
||||
|
||||
ctx.builder
|
||||
.build_call(extern_fn, &[$($args.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()
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
FN_NAME,
|
||||
Some(llvm_f64.into()),
|
||||
&[$($args.into()),*],
|
||||
name,
|
||||
Some(&|func| {
|
||||
for attr in [$($attributes),*] {
|
||||
func.add_attribute(
|
||||
AttributeLoc::Function,
|
||||
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
|
||||
);
|
||||
}
|
||||
})
|
||||
)
|
||||
.map(BasicValueEnum::into_float_value)
|
||||
.unwrap()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_extern_fn!("unary", call_tan, "tan");
|
||||
generate_extern_fn!("unary", call_asin, "asin");
|
||||
generate_extern_fn!("unary", call_acos, "acos");
|
||||
generate_extern_fn!("unary", call_atan, "atan");
|
||||
generate_extern_fn!("unary", call_sinh, "sinh");
|
||||
generate_extern_fn!("unary", call_cosh, "cosh");
|
||||
generate_extern_fn!("unary", call_tanh, "tanh");
|
||||
generate_extern_fn!("unary", call_asinh, "asinh");
|
||||
generate_extern_fn!("unary", call_acosh, "acosh");
|
||||
generate_extern_fn!("unary", call_atanh, "atanh");
|
||||
generate_extern_fn!("unary", call_expm1, "expm1");
|
||||
generate_extern_fn!(
|
||||
"unary",
|
||||
call_cbrt,
|
||||
"cbrt",
|
||||
"mustprogress",
|
||||
"nofree",
|
||||
"nosync",
|
||||
"nounwind",
|
||||
"readonly",
|
||||
"willreturn"
|
||||
);
|
||||
generate_extern_fn!("unary", call_erf, "erf", "nounwind");
|
||||
generate_extern_fn!("unary", call_erfc, "erfc", "nounwind");
|
||||
generate_extern_fn!("unary", call_j1, "j1", "nounwind");
|
||||
|
||||
generate_extern_fn!("binary", call_atan2, "atan2");
|
||||
generate_extern_fn!("binary", call_hypot, "hypot", "nounwind");
|
||||
generate_extern_fn!("binary", call_nextafter, "nextafter", "nounwind");
|
||||
|
||||
/// Invokes the [`ldexp`](https://en.cppreference.com/w/c/numeric/math/ldexp) function.
|
||||
pub fn call_ldexp<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
arg: FloatValue<'ctx>,
|
||||
exp: IntValue<'ctx>,
|
||||
name: Option<&str>,
|
||||
) -> FloatValue<'ctx> {
|
||||
const FN_NAME: &str = "ldexp";
|
||||
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
debug_assert_eq!(arg.get_type(), llvm_f64);
|
||||
debug_assert_eq!(exp.get_type(), llvm_i32);
|
||||
|
||||
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
|
||||
let fn_type = llvm_f64.fn_type(&[llvm_f64.into(), llvm_i32.into()], false);
|
||||
let func = ctx.module.add_function(FN_NAME, fn_type, None);
|
||||
for attr in ["mustprogress", "nofree", "nounwind", "willreturn"] {
|
||||
func.add_attribute(
|
||||
AttributeLoc::Function,
|
||||
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
|
||||
);
|
||||
}
|
||||
|
||||
func
|
||||
});
|
||||
|
||||
ctx.builder
|
||||
.build_call(extern_fn, &[arg.into(), exp.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()
|
||||
}
|
||||
|
||||
/// Macro to generate `np_linalg` and `sp_linalg` functions
|
||||
/// The function takes as input `NDArray` and returns ()
|
||||
///
|
||||
/// Arguments:
|
||||
/// * `$fn_name:ident`: The identifier of the rust function to be generated
|
||||
/// * `$extern_fn:literal`: Name of underlying extern function
|
||||
/// * `$extern_fn:ident`: Name of underlying extern function
|
||||
/// * (2/3/4): Number of `NDArray` that function takes as input
|
||||
///
|
||||
/// Note:
|
||||
@ -146,48 +81,51 @@ pub fn call_ldexp<'ctx>(
|
||||
/// It is the responsibility of caller to ensure that output `NDArray` is properly allocated on stack
|
||||
/// The function changes the content of the output `NDArray` in-place
|
||||
macro_rules! generate_linalg_extern_fn {
|
||||
($fn_name:ident, $extern_fn:literal, 2) => {
|
||||
($fn_name:ident, $extern_fn:ident, 2) => {
|
||||
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2);
|
||||
};
|
||||
($fn_name:ident, $extern_fn:literal, 3) => {
|
||||
($fn_name:ident, $extern_fn:ident, 3) => {
|
||||
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2, mat3);
|
||||
};
|
||||
($fn_name:ident, $extern_fn:literal, 4) => {
|
||||
($fn_name:ident, $extern_fn:ident, 4) => {
|
||||
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2, mat3, mat4);
|
||||
};
|
||||
($fn_name:ident, $extern_fn:literal $(,$input_matrix:ident)*) => {
|
||||
#[doc = concat!("Invokes the linalg `", stringify!($extern_fn), " function." )]
|
||||
($fn_name:ident, $extern_fn:ident $(,$input_matrix:ident)*) => {
|
||||
#[doc = concat!("Invokes the linalg `", stringify!($extern_fn), "` function." )]
|
||||
pub fn $fn_name<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>
|
||||
$(,$input_matrix: BasicValueEnum<'ctx>)*,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
$($input_matrix: BasicValueEnum<'ctx>,)*
|
||||
name: Option<&str>,
|
||||
){
|
||||
const FN_NAME: &str = $extern_fn;
|
||||
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| {
|
||||
let fn_type = ctx.ctx.void_type().fn_type(&[$($input_matrix.get_type().into()),*], false);
|
||||
) {
|
||||
const FN_NAME: &str = stringify!($extern_fn);
|
||||
|
||||
let func = ctx.module.add_function(FN_NAME, fn_type, None);
|
||||
for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] {
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
FN_NAME,
|
||||
None,
|
||||
&[$($input_matrix.into(),)*],
|
||||
name,
|
||||
Some(&|func| {
|
||||
func.add_attribute(
|
||||
AttributeLoc::Function,
|
||||
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
|
||||
);
|
||||
}
|
||||
func
|
||||
});
|
||||
|
||||
ctx.builder.build_call(extern_fn, &[$($input_matrix.into(),)*], name.unwrap_or_default()).unwrap();
|
||||
ctx.ctx.create_enum_attribute(
|
||||
Attribute::get_named_enum_kind_id("nounwind"),
|
||||
0,
|
||||
),
|
||||
)
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_linalg_extern_fn!(call_np_linalg_cholesky, "np_linalg_cholesky", 2);
|
||||
generate_linalg_extern_fn!(call_np_linalg_qr, "np_linalg_qr", 3);
|
||||
generate_linalg_extern_fn!(call_np_linalg_svd, "np_linalg_svd", 4);
|
||||
generate_linalg_extern_fn!(call_np_linalg_inv, "np_linalg_inv", 2);
|
||||
generate_linalg_extern_fn!(call_np_linalg_pinv, "np_linalg_pinv", 2);
|
||||
generate_linalg_extern_fn!(call_np_linalg_matrix_power, "np_linalg_matrix_power", 3);
|
||||
generate_linalg_extern_fn!(call_np_linalg_det, "np_linalg_det", 2);
|
||||
generate_linalg_extern_fn!(call_sp_linalg_lu, "sp_linalg_lu", 3);
|
||||
generate_linalg_extern_fn!(call_sp_linalg_schur, "sp_linalg_schur", 3);
|
||||
generate_linalg_extern_fn!(call_sp_linalg_hessenberg, "sp_linalg_hessenberg", 3);
|
||||
generate_linalg_extern_fn!(call_np_linalg_cholesky, np_linalg_cholesky, 2);
|
||||
generate_linalg_extern_fn!(call_np_linalg_qr, np_linalg_qr, 3);
|
||||
generate_linalg_extern_fn!(call_np_linalg_svd, np_linalg_svd, 4);
|
||||
generate_linalg_extern_fn!(call_np_linalg_inv, np_linalg_inv, 2);
|
||||
generate_linalg_extern_fn!(call_np_linalg_pinv, np_linalg_pinv, 2);
|
||||
generate_linalg_extern_fn!(call_np_linalg_matrix_power, np_linalg_matrix_power, 3);
|
||||
generate_linalg_extern_fn!(call_np_linalg_det, np_linalg_det, 2);
|
||||
generate_linalg_extern_fn!(call_sp_linalg_lu, sp_linalg_lu, 3);
|
||||
generate_linalg_extern_fn!(call_sp_linalg_schur, sp_linalg_schur, 3);
|
||||
generate_linalg_extern_fn!(call_sp_linalg_hessenberg, sp_linalg_hessenberg, 3);
|
||||
|
@ -7,7 +7,7 @@ use inkwell::{
|
||||
|
||||
use nac3parser::ast::{Expr, Stmt, StrRef};
|
||||
|
||||
use super::{bool_to_i1, bool_to_i8, expr::*, stmt::*, values::ArraySliceValue, CodeGenContext};
|
||||
use super::{CodeGenContext, bool_to_int_type, expr::*, stmt::*, values::ArraySliceValue};
|
||||
use crate::{
|
||||
symbol_resolver::ValueEnum,
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
@ -248,22 +248,32 @@ pub trait CodeGenerator {
|
||||
gen_block(self, ctx, stmts)
|
||||
}
|
||||
|
||||
/// See [`bool_to_i1`].
|
||||
/// Converts the value of a boolean-like value `bool_value` into an `i1`.
|
||||
fn bool_to_i1<'ctx>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
bool_value: IntValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
bool_to_i1(&ctx.builder, bool_value)
|
||||
self.bool_to_int_type(ctx, bool_value, ctx.ctx.bool_type())
|
||||
}
|
||||
|
||||
/// See [`bool_to_i8`].
|
||||
/// Converts the value of a boolean-like value `bool_value` into an `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)
|
||||
self.bool_to_int_type(ctx, bool_value, ctx.ctx.i8_type())
|
||||
}
|
||||
|
||||
/// See [`bool_to_int_type`].
|
||||
fn bool_to_int_type<'ctx>(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
bool_value: IntValue<'ctx>,
|
||||
ty: IntType<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
bool_to_int_type(&ctx.builder, bool_value, ty)
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,10 +308,6 @@ impl CodeGenerator for DefaultCodeGenerator {
|
||||
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...
|
||||
if self.size_t == 32 {
|
||||
ctx.i32_type()
|
||||
} else {
|
||||
ctx.i64_type()
|
||||
}
|
||||
if self.size_t == 32 { ctx.i32_type() } else { ctx.i64_type() }
|
||||
}
|
||||
}
|
||||
|
131
nac3core/src/codegen/irrt/cc_builtins.rs
Normal file
131
nac3core/src/codegen/irrt/cc_builtins.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use inkwell::{
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
values::{BasicValueEnum, FloatValue, IntValue},
|
||||
};
|
||||
|
||||
use crate::codegen::{CodeGenContext, expr::infer_and_call_function};
|
||||
|
||||
/// Generates a call to [`isinf`](https://en.cppreference.com/w/c/numeric/math/isinf) in IR. Returns
|
||||
/// an `i1` representing the result.
|
||||
pub fn call_isinf<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
v: FloatValue<'ctx>,
|
||||
name: Option<&str>,
|
||||
) -> IntValue<'ctx> {
|
||||
let llvm_i1 = ctx.ctx.bool_type();
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
|
||||
assert_eq!(v.get_type(), llvm_f64);
|
||||
|
||||
infer_and_call_function(ctx, "__nac3_isinf", Some(llvm_i1.into()), &[v.into()], name, None)
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Generates a call to [`isnan`](https://en.cppreference.com/w/c/numeric/math/isnan) in IR. Returns
|
||||
/// an `i1` representing the result.
|
||||
pub fn call_isnan<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
v: FloatValue<'ctx>,
|
||||
name: Option<&str>,
|
||||
) -> IntValue<'ctx> {
|
||||
let llvm_i1 = ctx.ctx.bool_type();
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
|
||||
assert_eq!(v.get_type(), llvm_f64);
|
||||
|
||||
infer_and_call_function(ctx, "__nac3_isnan", Some(llvm_i1.into()), &[v.into()], name, None)
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Macro to generate N-ary functions accepting an arbitrary number of `f64` as arguments and
|
||||
/// returning `f64`.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// - `$fn_name:ident`: The name of the Rust function to be generated.
|
||||
/// - `$builtin_fn:ident`: The name of the builtin function to be invoked in the body of the
|
||||
/// generated function. The corresponding function in IRRT must be prefixed with `__nac3_`.
|
||||
/// - `$(,$args:ident)*`: The parameter name(s) to the IRRT function.
|
||||
macro_rules! generate_f64_nary_fn {
|
||||
($fn_name:ident, $builtin_fn:ident $(,$args:ident)* $(,)?) => {
|
||||
#[doc = concat!("Generates a call to [`", stringify!($builtin_fn), "`](https://en.cppreference.com/w/c/numeric/math/", stringify!($builtin_fn), ") in IR." )]
|
||||
pub fn $fn_name<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
$($args: FloatValue<'ctx>,)*
|
||||
name: Option<&str>,
|
||||
) -> FloatValue<'ctx> {
|
||||
const FN_NAME: &str = concat!("__nac3_", stringify!($builtin_fn));
|
||||
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
$(debug_assert_eq!($args.get_type(), llvm_f64);)*
|
||||
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
FN_NAME,
|
||||
Some(llvm_f64.into()),
|
||||
&[$($args.into()),*],
|
||||
name,
|
||||
Some(&|func| {
|
||||
func.add_attribute(
|
||||
AttributeLoc::Function,
|
||||
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0)
|
||||
);
|
||||
}),
|
||||
)
|
||||
.map(BasicValueEnum::into_float_value)
|
||||
.unwrap()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_f64_nary_fn!(call_tan, tan, arg);
|
||||
generate_f64_nary_fn!(call_asin, asin, arg);
|
||||
generate_f64_nary_fn!(call_acos, acos, arg);
|
||||
generate_f64_nary_fn!(call_atan, atan, arg);
|
||||
generate_f64_nary_fn!(call_sinh, sinh, arg);
|
||||
generate_f64_nary_fn!(call_cosh, cosh, arg);
|
||||
generate_f64_nary_fn!(call_tanh, tanh, arg);
|
||||
generate_f64_nary_fn!(call_asinh, asinh, arg);
|
||||
generate_f64_nary_fn!(call_acosh, acosh, arg);
|
||||
generate_f64_nary_fn!(call_atanh, atanh, arg);
|
||||
generate_f64_nary_fn!(call_expm1, expm1, arg);
|
||||
generate_f64_nary_fn!(call_cbrt, cbrt, arg);
|
||||
generate_f64_nary_fn!(call_erf, erf, arg);
|
||||
generate_f64_nary_fn!(call_erfc, erfc, arg);
|
||||
generate_f64_nary_fn!(call_gamma, gamma, z);
|
||||
generate_f64_nary_fn!(call_atan2, atan2, y, x);
|
||||
generate_f64_nary_fn!(call_hypot, hypot, x, y);
|
||||
generate_f64_nary_fn!(call_nextafter, nextafter, from, to);
|
||||
|
||||
/// Invokes the [`ldexp`](https://en.cppreference.com/w/c/numeric/math/ldexp) function.
|
||||
pub fn call_ldexp<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
arg: FloatValue<'ctx>,
|
||||
exp: IntValue<'ctx>,
|
||||
name: Option<&str>,
|
||||
) -> FloatValue<'ctx> {
|
||||
const FN_NAME: &str = "__nac3_ldexp";
|
||||
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
debug_assert_eq!(arg.get_type(), llvm_f64);
|
||||
debug_assert_eq!(exp.get_type(), llvm_i32);
|
||||
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
FN_NAME,
|
||||
Some(llvm_f64.into()),
|
||||
&[arg.into(), exp.into()],
|
||||
name,
|
||||
Some(&|func| {
|
||||
func.add_attribute(
|
||||
AttributeLoc::Function,
|
||||
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id("nounwind"), 0),
|
||||
);
|
||||
}),
|
||||
)
|
||||
.map(BasicValueEnum::into_float_value)
|
||||
.unwrap()
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
use inkwell::{
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValueEnum, CallSiteValue, IntValue},
|
||||
AddressSpace, IntPredicate,
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValueEnum, IntValue},
|
||||
};
|
||||
use itertools::Either;
|
||||
|
||||
use super::calculate_len_for_slice_range;
|
||||
use crate::codegen::{
|
||||
macros::codegen_unreachable,
|
||||
values::{ArrayLikeValue, ListValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::infer_and_call_function,
|
||||
macros::codegen_unreachable,
|
||||
stmt::gen_if_callback,
|
||||
values::{ArrayLikeValue, ListValue},
|
||||
};
|
||||
|
||||
/// This function handles 'end' **inclusively**.
|
||||
@ -36,25 +37,6 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
||||
assert_eq!(src_idx.2.get_type(), llvm_i32);
|
||||
|
||||
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", llvm_pi8);
|
||||
let slice_assign_fun = {
|
||||
let ty_vec = vec![
|
||||
llvm_i32.into(), // dest start idx
|
||||
llvm_i32.into(), // dest end idx
|
||||
llvm_i32.into(), // dest step
|
||||
elem_ptr_type.into(), // dest arr ptr
|
||||
llvm_i32.into(), // dest arr len
|
||||
llvm_i32.into(), // src start idx
|
||||
llvm_i32.into(), // src end idx
|
||||
llvm_i32.into(), // src step
|
||||
elem_ptr_type.into(), // src arr ptr
|
||||
llvm_i32.into(), // src arr len
|
||||
llvm_i32.into(), // size
|
||||
];
|
||||
ctx.module.get_function(fun_symbol).unwrap_or_else(|| {
|
||||
let fn_t = llvm_i32.fn_type(ty_vec.as_slice(), false);
|
||||
ctx.module.add_function(fun_symbol, fn_t, None)
|
||||
})
|
||||
};
|
||||
|
||||
let zero = llvm_i32.const_zero();
|
||||
let one = llvm_i32.const_int(1, false);
|
||||
@ -127,7 +109,7 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
||||
);
|
||||
|
||||
let new_len = {
|
||||
let args = vec![
|
||||
let args = [
|
||||
dest_idx.0.into(), // dest start idx
|
||||
dest_idx.1.into(), // dest end idx
|
||||
dest_idx.2.into(), // dest step
|
||||
@ -150,25 +132,35 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
|
||||
}
|
||||
.into(),
|
||||
];
|
||||
ctx.builder
|
||||
.build_call(slice_assign_fun, args.as_slice(), "slice_assign")
|
||||
.map(CallSiteValue::try_as_basic_value)
|
||||
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
||||
.map(Either::unwrap_left)
|
||||
.unwrap()
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
fun_symbol,
|
||||
Some(llvm_i32.into()),
|
||||
&args,
|
||||
Some("slice_assign"),
|
||||
None,
|
||||
)
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// update length
|
||||
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).unwrap();
|
||||
ctx.builder.position_at_end(update_bb);
|
||||
let new_len =
|
||||
ctx.builder.build_int_z_extend_or_bit_cast(new_len, llvm_usize, "new_len").unwrap();
|
||||
dest_arr.store_size(ctx, new_len);
|
||||
ctx.builder.build_unconditional_branch(cont_bb).unwrap();
|
||||
ctx.builder.position_at_end(cont_bb);
|
||||
gen_if_callback(
|
||||
generator,
|
||||
ctx,
|
||||
|_, ctx| {
|
||||
Ok(ctx
|
||||
.builder
|
||||
.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update")
|
||||
.unwrap())
|
||||
},
|
||||
|_, ctx| {
|
||||
let new_len =
|
||||
ctx.builder.build_int_z_extend_or_bit_cast(new_len, llvm_usize, "new_len").unwrap();
|
||||
dest_arr.store_size(ctx, new_len);
|
||||
Ok(())
|
||||
},
|
||||
|_, _| Ok(()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use inkwell::{
|
||||
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue},
|
||||
IntPredicate,
|
||||
values::{BasicValueEnum, FloatValue, IntValue},
|
||||
};
|
||||
use itertools::Either;
|
||||
|
||||
use crate::codegen::{
|
||||
expr::infer_and_call_function,
|
||||
macros::codegen_unreachable,
|
||||
{CodeGenContext, CodeGenerator},
|
||||
};
|
||||
@ -18,18 +18,16 @@ pub fn integer_power<'ctx, G: CodeGenerator + ?Sized>(
|
||||
exp: IntValue<'ctx>,
|
||||
signed: bool,
|
||||
) -> IntValue<'ctx> {
|
||||
let symbol = match (base.get_type().get_bit_width(), exp.get_type().get_bit_width(), signed) {
|
||||
let base_type = base.get_type();
|
||||
|
||||
let symbol = match (base_type.get_bit_width(), exp.get_type().get_bit_width(), signed) {
|
||||
(32, 32, true) => "__nac3_int_exp_int32_t",
|
||||
(64, 64, true) => "__nac3_int_exp_int64_t",
|
||||
(32, 32, false) => "__nac3_int_exp_uint32_t",
|
||||
(64, 64, false) => "__nac3_int_exp_uint64_t",
|
||||
_ => codegen_unreachable!(ctx),
|
||||
};
|
||||
let base_type = base.get_type();
|
||||
let pow_fun = ctx.module.get_function(symbol).unwrap_or_else(|| {
|
||||
let fn_type = base_type.fn_type(&[base_type.into(), base_type.into()], false);
|
||||
ctx.module.add_function(symbol, fn_type, None)
|
||||
});
|
||||
|
||||
// throw exception when exp < 0
|
||||
let ge_zero = ctx
|
||||
.builder
|
||||
@ -48,85 +46,17 @@ pub fn integer_power<'ctx, G: CodeGenerator + ?Sized>(
|
||||
[None, None, None],
|
||||
ctx.current_loc,
|
||||
);
|
||||
ctx.builder
|
||||
.build_call(pow_fun, &[base.into(), exp.into()], "call_int_pow")
|
||||
.map(CallSiteValue::try_as_basic_value)
|
||||
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
||||
.map(Either::unwrap_left)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// 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 llvm_i32 = ctx.ctx.i32_type();
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
|
||||
assert_eq!(v.get_type(), llvm_f64);
|
||||
|
||||
let intrinsic_fn = ctx.module.get_function("__nac3_isinf").unwrap_or_else(|| {
|
||||
let fn_type = llvm_i32.fn_type(&[llvm_f64.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 llvm_i32 = ctx.ctx.i32_type();
|
||||
let llvm_f64 = ctx.ctx.f64_type();
|
||||
|
||||
assert_eq!(v.get_type(), llvm_f64);
|
||||
|
||||
let intrinsic_fn = ctx.module.get_function("__nac3_isnan").unwrap_or_else(|| {
|
||||
let fn_type = llvm_i32.fn_type(&[llvm_f64.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();
|
||||
|
||||
assert_eq!(v.get_type(), llvm_f64);
|
||||
|
||||
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()
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
symbol,
|
||||
Some(base_type.into()),
|
||||
&[base.into(), exp.into()],
|
||||
Some("call_int_pow"),
|
||||
None,
|
||||
)
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Generates a call to `gammaln` in IR. Returns an `f64` representing the result.
|
||||
@ -135,17 +65,16 @@ pub fn call_gammaln<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -
|
||||
|
||||
assert_eq!(v.get_type(), llvm_f64);
|
||||
|
||||
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()
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
"__nac3_gammaln",
|
||||
Some(llvm_f64.into()),
|
||||
&[v.into()],
|
||||
Some("gammaln"),
|
||||
None,
|
||||
)
|
||||
.map(BasicValueEnum::into_float_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Generates a call to `j0` in IR. Returns an `f64` representing the result.
|
||||
@ -154,15 +83,7 @@ pub fn call_j0<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> Flo
|
||||
|
||||
assert_eq!(v.get_type(), llvm_f64);
|
||||
|
||||
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)
|
||||
infer_and_call_function(ctx, "__nac3_j0", Some(llvm_f64.into()), &[v.into()], Some("j0"), None)
|
||||
.map(BasicValueEnum::into_float_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -1,22 +1,24 @@
|
||||
use inkwell::{
|
||||
IntPredicate,
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
context::Context,
|
||||
memory_buffer::MemoryBuffer,
|
||||
module::Module,
|
||||
values::{BasicValue, BasicValueEnum, IntValue},
|
||||
IntPredicate,
|
||||
};
|
||||
|
||||
use nac3parser::ast::Expr;
|
||||
|
||||
use super::{CodeGenContext, CodeGenerator};
|
||||
use crate::{symbol_resolver::SymbolResolver, typecheck::typedef::Type};
|
||||
pub use cc_builtins::*;
|
||||
pub use list::*;
|
||||
pub use math::*;
|
||||
pub use range::*;
|
||||
pub use slice::*;
|
||||
pub use string::*;
|
||||
|
||||
mod cc_builtins;
|
||||
mod list;
|
||||
mod math;
|
||||
pub mod ndarray;
|
||||
|
@ -1,10 +1,10 @@
|
||||
use inkwell::{types::BasicTypeEnum, values::IntValue};
|
||||
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::infer_and_call_function,
|
||||
irrt::get_usize_dependent_function_name,
|
||||
values::{ndarray::NDArrayValue, ListValue, ProxyValue, TypedArrayLikeAccessor},
|
||||
CodeGenContext, CodeGenerator,
|
||||
values::{ListValue, ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
|
||||
};
|
||||
|
||||
/// Generates a call to `__nac3_ndarray_array_set_and_validate_list_shape`.
|
||||
@ -36,7 +36,7 @@ pub fn call_nac3_ndarray_array_set_and_validate_list_shape<'ctx, G: CodeGenerato
|
||||
ctx,
|
||||
&name,
|
||||
None,
|
||||
&[list.as_base_value().into(), ndims.into(), shape.base_ptr(ctx, generator).into()],
|
||||
&[list.as_abi_value(ctx).into(), ndims.into(), shape.base_ptr(ctx, generator).into()],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
@ -65,7 +65,7 @@ pub fn call_nac3_ndarray_array_write_list_to_array<'ctx>(
|
||||
ctx,
|
||||
&name,
|
||||
None,
|
||||
&[list.as_base_value().into(), ndarray.as_base_value().into()],
|
||||
&[list.as_abi_value(ctx).into(), ndarray.as_abi_value(ctx).into()],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
@ -1,15 +1,13 @@
|
||||
use inkwell::{
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
};
|
||||
|
||||
use crate::codegen::{
|
||||
expr::{create_and_call_function, infer_and_call_function},
|
||||
irrt::get_usize_dependent_function_name,
|
||||
types::ProxyType,
|
||||
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::infer_and_call_function,
|
||||
irrt::get_usize_dependent_function_name,
|
||||
values::{ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
|
||||
};
|
||||
|
||||
/// Generates a call to `__nac3_ndarray_util_assert_shape_no_negative`.
|
||||
@ -21,24 +19,17 @@ pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator +
|
||||
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
||||
) {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(shape.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(shape.element_type(ctx, generator), llvm_usize.into());
|
||||
|
||||
let name =
|
||||
get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_shape_no_negative");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
Some(llvm_usize.into()),
|
||||
&[
|
||||
(llvm_usize.into(), shape.size(ctx, generator).into()),
|
||||
(llvm_pusize.into(), shape.base_ptr(ctx, generator).into()),
|
||||
],
|
||||
&[shape.size(ctx, generator).into(), shape.base_ptr(ctx, generator).into()],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
@ -55,29 +46,22 @@ pub fn call_nac3_ndarray_util_assert_output_shape_same<'ctx, G: CodeGenerator +
|
||||
output_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
||||
) {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(ndarray_shape.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(output_shape.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(ndarray_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
assert_eq!(output_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
|
||||
let name =
|
||||
get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_output_shape_same");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
Some(llvm_usize.into()),
|
||||
&[
|
||||
(llvm_usize.into(), ndarray_shape.size(ctx, generator).into()),
|
||||
(llvm_pusize.into(), ndarray_shape.base_ptr(ctx, generator).into()),
|
||||
(llvm_usize.into(), output_shape.size(ctx, generator).into()),
|
||||
(llvm_pusize.into(), output_shape.base_ptr(ctx, generator).into()),
|
||||
ndarray_shape.size(ctx, generator).into(),
|
||||
ndarray_shape.base_ptr(ctx, generator).into(),
|
||||
output_shape.size(ctx, generator).into(),
|
||||
output_shape.base_ptr(ctx, generator).into(),
|
||||
],
|
||||
None,
|
||||
None,
|
||||
@ -93,15 +77,14 @@ pub fn call_nac3_ndarray_size<'ctx>(
|
||||
ndarray: NDArrayValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_size");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
Some(llvm_usize.into()),
|
||||
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
|
||||
&[ndarray.as_abi_value(ctx).into()],
|
||||
Some("size"),
|
||||
None,
|
||||
)
|
||||
@ -118,15 +101,14 @@ pub fn call_nac3_ndarray_nbytes<'ctx>(
|
||||
ndarray: NDArrayValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_nbytes");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
Some(llvm_usize.into()),
|
||||
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
|
||||
&[ndarray.as_abi_value(ctx).into()],
|
||||
Some("nbytes"),
|
||||
None,
|
||||
)
|
||||
@ -143,15 +125,14 @@ pub fn call_nac3_ndarray_len<'ctx>(
|
||||
ndarray: NDArrayValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_len");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
Some(llvm_usize.into()),
|
||||
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
|
||||
&[ndarray.as_abi_value(ctx).into()],
|
||||
Some("len"),
|
||||
None,
|
||||
)
|
||||
@ -167,15 +148,14 @@ pub fn call_nac3_ndarray_is_c_contiguous<'ctx>(
|
||||
ndarray: NDArrayValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let llvm_i1 = ctx.ctx.bool_type();
|
||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_is_c_contiguous");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
Some(llvm_i1.into()),
|
||||
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
|
||||
&[ndarray.as_abi_value(ctx).into()],
|
||||
Some("is_c_contiguous"),
|
||||
None,
|
||||
)
|
||||
@ -194,17 +174,16 @@ pub fn call_nac3_ndarray_get_nth_pelement<'ctx>(
|
||||
let llvm_i8 = ctx.ctx.i8_type();
|
||||
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||
|
||||
assert_eq!(index.get_type(), llvm_usize);
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_get_nth_pelement");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
Some(llvm_pi8.into()),
|
||||
&[(llvm_ndarray.into(), ndarray.as_base_value().into()), (llvm_usize.into(), index.into())],
|
||||
&[ndarray.as_abi_value(ctx).into(), index.into()],
|
||||
Some("pelement"),
|
||||
None,
|
||||
)
|
||||
@ -226,24 +205,16 @@ pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized
|
||||
let llvm_i8 = ctx.ctx.i8_type();
|
||||
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(indices.element_type(ctx, generator), llvm_usize.into());
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_get_pelement_by_indices");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
Some(llvm_pi8.into()),
|
||||
&[
|
||||
(llvm_ndarray.into(), ndarray.as_base_value().into()),
|
||||
(llvm_pusize.into(), indices.base_ptr(ctx, generator).into()),
|
||||
],
|
||||
&[ndarray.as_abi_value(ctx).into(), indices.base_ptr(ctx, generator).into()],
|
||||
Some("pelement"),
|
||||
None,
|
||||
)
|
||||
@ -258,18 +229,9 @@ pub fn call_nac3_ndarray_set_strides_by_shape<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
ndarray: NDArrayValue<'ctx>,
|
||||
) {
|
||||
let llvm_ndarray = ndarray.get_type().as_base_type();
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_set_strides_by_shape");
|
||||
|
||||
create_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
None,
|
||||
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
infer_and_call_function(ctx, &name, None, &[ndarray.as_abi_value(ctx).into()], None, None);
|
||||
}
|
||||
|
||||
/// Generates a call to `__nac3_ndarray_copy_data`.
|
||||
@ -288,7 +250,7 @@ pub fn call_nac3_ndarray_copy_data<'ctx>(
|
||||
ctx,
|
||||
&name,
|
||||
None,
|
||||
&[src_ndarray.as_base_value().into(), dst_ndarray.as_base_value().into()],
|
||||
&[src_ndarray.as_abi_value(ctx).into(), dst_ndarray.as_abi_value(ctx).into()],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
@ -1,14 +1,14 @@
|
||||
use inkwell::values::IntValue;
|
||||
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::infer_and_call_function,
|
||||
irrt::get_usize_dependent_function_name,
|
||||
types::{ndarray::ShapeEntryType, ProxyType},
|
||||
types::{ProxyType, ndarray::ShapeEntryType},
|
||||
values::{
|
||||
ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor,
|
||||
TypedArrayLikeMutator,
|
||||
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeMutator,
|
||||
ndarray::NDArrayValue,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
|
||||
/// Generates a call to `__nac3_ndarray_broadcast_to`.
|
||||
@ -30,7 +30,7 @@ pub fn call_nac3_ndarray_broadcast_to<'ctx>(
|
||||
ctx,
|
||||
&name,
|
||||
None,
|
||||
&[src_ndarray.as_base_value().into(), dst_ndarray.as_base_value().into()],
|
||||
&[src_ndarray.as_abi_value(ctx).into(), dst_ndarray.as_abi_value(ctx).into()],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
@ -55,12 +55,13 @@ pub fn call_nac3_ndarray_broadcast_shapes<'ctx, G, Shape>(
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
|
||||
assert_eq!(num_shape_entries.get_type(), llvm_usize);
|
||||
assert!(ShapeEntryType::is_type(
|
||||
generator,
|
||||
ctx.ctx,
|
||||
shape_entries.base_ptr(ctx, generator).get_type()
|
||||
)
|
||||
.is_ok());
|
||||
assert!(
|
||||
ShapeEntryType::is_representable(
|
||||
shape_entries.base_ptr(ctx, generator).get_type(),
|
||||
llvm_usize,
|
||||
)
|
||||
.is_ok()
|
||||
);
|
||||
assert_eq!(dst_ndims.get_type(), llvm_usize);
|
||||
assert_eq!(dst_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::infer_and_call_function,
|
||||
irrt::get_usize_dependent_function_name,
|
||||
values::{ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
values::{ArrayLikeValue, ArraySliceValue, ProxyValue, ndarray::NDArrayValue},
|
||||
};
|
||||
|
||||
/// Generates a call to `__nac3_ndarray_index`.
|
||||
@ -25,8 +25,8 @@ pub fn call_nac3_ndarray_index<'ctx, G: CodeGenerator + ?Sized>(
|
||||
&[
|
||||
indices.size(ctx, generator).into(),
|
||||
indices.base_ptr(ctx, generator).into(),
|
||||
src_ndarray.as_base_value().into(),
|
||||
dst_ndarray.as_base_value().into(),
|
||||
src_ndarray.as_abi_value(ctx).into(),
|
||||
dst_ndarray.as_abi_value(ctx).into(),
|
||||
],
|
||||
None,
|
||||
None,
|
||||
|
@ -1,18 +1,13 @@
|
||||
use inkwell::{
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValueEnum, IntValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use inkwell::values::{BasicValueEnum, IntValue};
|
||||
|
||||
use crate::codegen::{
|
||||
expr::{create_and_call_function, infer_and_call_function},
|
||||
irrt::get_usize_dependent_function_name,
|
||||
types::ProxyType,
|
||||
values::{
|
||||
ndarray::{NDArrayValue, NDIterValue},
|
||||
ProxyValue, TypedArrayLikeAccessor,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::infer_and_call_function,
|
||||
irrt::get_usize_dependent_function_name,
|
||||
values::{
|
||||
ProxyValue, TypedArrayLikeAccessor,
|
||||
ndarray::{NDArrayValue, NDIterValue},
|
||||
},
|
||||
};
|
||||
|
||||
/// Generates a call to `__nac3_nditer_initialize`.
|
||||
@ -26,23 +21,19 @@ pub fn call_nac3_nditer_initialize<'ctx, G: CodeGenerator + ?Sized>(
|
||||
indices: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
|
||||
) {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
|
||||
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(indices.element_type(ctx, generator), llvm_usize.into());
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_initialize");
|
||||
|
||||
create_and_call_function(
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&name,
|
||||
None,
|
||||
&[
|
||||
(iter.get_type().as_base_type().into(), iter.as_base_value().into()),
|
||||
(ndarray.get_type().as_base_type().into(), ndarray.as_base_value().into()),
|
||||
(llvm_pusize.into(), indices.base_ptr(ctx, generator).into()),
|
||||
iter.as_abi_value(ctx).into(),
|
||||
ndarray.as_abi_value(ctx).into(),
|
||||
indices.base_ptr(ctx, generator).into(),
|
||||
],
|
||||
None,
|
||||
None,
|
||||
@ -63,7 +54,7 @@ pub fn call_nac3_nditer_has_element<'ctx>(
|
||||
ctx,
|
||||
&name,
|
||||
Some(ctx.ctx.bool_type().into()),
|
||||
&[iter.as_base_value().into()],
|
||||
&[iter.as_abi_value(ctx).into()],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
@ -77,5 +68,5 @@ pub fn call_nac3_nditer_has_element<'ctx>(
|
||||
pub fn call_nac3_nditer_next<'ctx>(ctx: &CodeGenContext<'ctx, '_>, iter: NDIterValue<'ctx>) {
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_next");
|
||||
|
||||
infer_and_call_function(ctx, &name, None, &[iter.as_base_value().into()], None, None);
|
||||
infer_and_call_function(ctx, &name, None, &[iter.as_abi_value(ctx).into()], None, None);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use inkwell::{types::BasicTypeEnum, values::IntValue};
|
||||
use inkwell::values::IntValue;
|
||||
|
||||
use crate::codegen::{
|
||||
expr::infer_and_call_function, irrt::get_usize_dependent_function_name,
|
||||
values::TypedArrayLikeAccessor, CodeGenContext, CodeGenerator,
|
||||
CodeGenContext, CodeGenerator, expr::infer_and_call_function,
|
||||
irrt::get_usize_dependent_function_name, values::TypedArrayLikeAccessor,
|
||||
};
|
||||
|
||||
/// Generates a call to `__nac3_ndarray_matmul_calculate_shapes`.
|
||||
@ -22,26 +22,12 @@ pub fn call_nac3_ndarray_matmul_calculate_shapes<'ctx, G: CodeGenerator + ?Sized
|
||||
) {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(a_shape.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(b_shape.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(new_a_shape.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(new_b_shape.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(
|
||||
BasicTypeEnum::try_from(dst_shape.element_type(ctx, generator)).unwrap(),
|
||||
llvm_usize.into()
|
||||
);
|
||||
assert_eq!(a_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
assert_eq!(b_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
assert_eq!(final_ndims.get_type(), llvm_usize);
|
||||
assert_eq!(new_a_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
assert_eq!(new_b_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
assert_eq!(dst_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
|
||||
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_matmul_calculate_shapes");
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
use inkwell::values::IntValue;
|
||||
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::infer_and_call_function,
|
||||
irrt::get_usize_dependent_function_name,
|
||||
values::{ArrayLikeValue, ArraySliceValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
|
||||
/// Generates a call to `__nac3_ndarray_reshape_resolve_and_check_new_shape`.
|
||||
|
@ -1,10 +1,10 @@
|
||||
use inkwell::{values::IntValue, AddressSpace};
|
||||
use inkwell::{AddressSpace, values::IntValue};
|
||||
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::infer_and_call_function,
|
||||
irrt::get_usize_dependent_function_name,
|
||||
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
|
||||
CodeGenContext, CodeGenerator,
|
||||
values::{ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
|
||||
};
|
||||
|
||||
/// Generates a call to `__nac3_ndarray_transpose`.
|
||||
@ -34,8 +34,8 @@ pub fn call_nac3_ndarray_transpose<'ctx, G: CodeGenerator + ?Sized>(
|
||||
&name,
|
||||
None,
|
||||
&[
|
||||
src_ndarray.as_base_value().into(),
|
||||
dst_ndarray.as_base_value().into(),
|
||||
src_ndarray.as_abi_value(ctx).into(),
|
||||
dst_ndarray.as_abi_value(ctx).into(),
|
||||
axes.map_or(llvm_usize.const_zero(), |axes| axes.size(ctx, generator)).into(),
|
||||
axes.map_or(llvm_usize.ptr_type(AddressSpace::default()).const_null(), |axes| {
|
||||
axes.base_ptr(ctx, generator)
|
||||
|
@ -1,10 +1,9 @@
|
||||
use inkwell::{
|
||||
values::{BasicValueEnum, CallSiteValue, IntValue},
|
||||
IntPredicate,
|
||||
values::{BasicValueEnum, IntValue},
|
||||
};
|
||||
use itertools::Either;
|
||||
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator, expr::infer_and_call_function};
|
||||
|
||||
/// Invokes the `__nac3_range_slice_len` in IRRT.
|
||||
///
|
||||
@ -23,16 +22,10 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
|
||||
const SYMBOL: &str = "__nac3_range_slice_len";
|
||||
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
|
||||
assert_eq!(start.get_type(), llvm_i32);
|
||||
assert_eq!(end.get_type(), llvm_i32);
|
||||
assert_eq!(step.get_type(), llvm_i32);
|
||||
|
||||
let len_func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
|
||||
let fn_t = llvm_i32.fn_type(&[llvm_i32.into(), llvm_i32.into(), llvm_i32.into()], false);
|
||||
ctx.module.add_function(SYMBOL, fn_t, None)
|
||||
});
|
||||
|
||||
// assert step != 0, throw exception if not
|
||||
let not_zero = ctx
|
||||
.builder
|
||||
@ -47,10 +40,14 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
|
||||
ctx.current_loc,
|
||||
);
|
||||
|
||||
ctx.builder
|
||||
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
|
||||
.map(CallSiteValue::try_as_basic_value)
|
||||
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
||||
.map(Either::unwrap_left)
|
||||
.unwrap()
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
SYMBOL,
|
||||
Some(llvm_i32.into()),
|
||||
&[start.into(), end.into(), step.into()],
|
||||
Some("calc_len"),
|
||||
None,
|
||||
)
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue};
|
||||
use itertools::Either;
|
||||
use inkwell::values::{BasicValueEnum, IntValue};
|
||||
|
||||
use nac3parser::ast::Expr;
|
||||
|
||||
use crate::{
|
||||
codegen::{CodeGenContext, CodeGenerator},
|
||||
codegen::{CodeGenContext, CodeGenerator, expr::infer_and_call_function},
|
||||
typecheck::typedef::Type,
|
||||
};
|
||||
|
||||
@ -17,23 +16,26 @@ pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>(
|
||||
length: IntValue<'ctx>,
|
||||
) -> 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();
|
||||
let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into()], false);
|
||||
ctx.module.add_function(SYMBOL, fn_t, None)
|
||||
});
|
||||
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
assert_eq!(length.get_type(), llvm_i32);
|
||||
|
||||
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")
|
||||
.map(CallSiteValue::try_as_basic_value)
|
||||
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
||||
.map(Either::unwrap_left)
|
||||
.unwrap(),
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
SYMBOL,
|
||||
Some(llvm_i32.into()),
|
||||
&[i, length.into()],
|
||||
Some("bounded_ind"),
|
||||
None,
|
||||
)
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
@ -1,45 +1,31 @@
|
||||
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue, PointerValue};
|
||||
use itertools::Either;
|
||||
use inkwell::values::{BasicValueEnum, IntValue};
|
||||
|
||||
use super::get_usize_dependent_function_name;
|
||||
use crate::codegen::CodeGenContext;
|
||||
use crate::codegen::{CodeGenContext, expr::infer_and_call_function, values::StringValue};
|
||||
|
||||
/// Generates a call to string equality comparison. Returns an `i1` representing whether the strings are equal.
|
||||
pub fn call_string_eq<'ctx>(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
str1_ptr: PointerValue<'ctx>,
|
||||
str1_len: IntValue<'ctx>,
|
||||
str2_ptr: PointerValue<'ctx>,
|
||||
str2_len: IntValue<'ctx>,
|
||||
str1: StringValue<'ctx>,
|
||||
str2: StringValue<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let llvm_i1 = ctx.ctx.bool_type();
|
||||
|
||||
let func_name = get_usize_dependent_function_name(ctx, "nac3_str_eq");
|
||||
|
||||
let func = ctx.module.get_function(&func_name).unwrap_or_else(|| {
|
||||
ctx.module.add_function(
|
||||
&func_name,
|
||||
llvm_i1.fn_type(
|
||||
&[
|
||||
str1_ptr.get_type().into(),
|
||||
str1_len.get_type().into(),
|
||||
str2_ptr.get_type().into(),
|
||||
str2_len.get_type().into(),
|
||||
],
|
||||
false,
|
||||
),
|
||||
None,
|
||||
)
|
||||
});
|
||||
|
||||
ctx.builder
|
||||
.build_call(
|
||||
func,
|
||||
&[str1_ptr.into(), str1_len.into(), str2_ptr.into(), str2_len.into()],
|
||||
"str_eq_call",
|
||||
)
|
||||
.map(CallSiteValue::try_as_basic_value)
|
||||
.map(|v| v.map_left(BasicValueEnum::into_int_value))
|
||||
.map(Either::unwrap_left)
|
||||
.unwrap()
|
||||
infer_and_call_function(
|
||||
ctx,
|
||||
&func_name,
|
||||
Some(llvm_i1.into()),
|
||||
&[
|
||||
str1.extract_ptr(ctx).into(),
|
||||
str1.extract_len(ctx).into(),
|
||||
str2.extract_ptr(ctx).into(),
|
||||
str2.extract_len(ctx).into(),
|
||||
],
|
||||
Some("str_eq_call"),
|
||||
None,
|
||||
)
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use inkwell::{
|
||||
AddressSpace,
|
||||
intrinsics::Intrinsic,
|
||||
types::AnyTypeEnum::IntType,
|
||||
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
};
|
||||
use itertools::Either;
|
||||
|
||||
@ -98,41 +98,29 @@ pub fn call_stackrestore<'ctx>(ctx: &CodeGenContext<'ctx, '_>, ptr: PointerValue
|
||||
/// * `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>,
|
||||
) {
|
||||
const FN_NAME: &str = "llvm.memcpy";
|
||||
|
||||
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);
|
||||
debug_assert_eq!(len.get_type(), ctx.get_size_type());
|
||||
|
||||
let llvm_dest_t = dest.get_type();
|
||||
let llvm_src_t = src.get_type();
|
||||
let llvm_len_t = len.get_type();
|
||||
|
||||
let intrinsic_fn = Intrinsic::find(FN_NAME)
|
||||
.and_then(|intrinsic| {
|
||||
intrinsic.get_declaration(
|
||||
&ctx.module,
|
||||
&[llvm_dest_t.into(), llvm_src_t.into(), llvm_len_t.into()],
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
let target_data =
|
||||
ctx.registry.llvm_options.create_target_machine().map(|tm| tm.get_target_data()).unwrap();
|
||||
let dest_alignment = target_data.get_abi_alignment(&llvm_dest_t);
|
||||
let src_alignment = target_data.get_abi_alignment(&llvm_src_t);
|
||||
|
||||
ctx.builder
|
||||
.build_call(intrinsic_fn, &[dest.into(), src.into(), len.into(), is_volatile.into()], "")
|
||||
.unwrap();
|
||||
ctx.builder.build_memcpy(dest, dest_alignment, src, src_alignment, len).unwrap();
|
||||
}
|
||||
|
||||
/// Invokes the `llvm.memcpy` intrinsic.
|
||||
@ -144,7 +132,6 @@ pub fn call_memcpy_generic<'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());
|
||||
@ -169,7 +156,7 @@ pub fn call_memcpy_generic<'ctx>(
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
call_memcpy(ctx, dest, src, len, is_volatile);
|
||||
call_memcpy(ctx, dest, src, len);
|
||||
}
|
||||
|
||||
/// Invokes the `llvm.memcpy` intrinsic.
|
||||
@ -183,11 +170,10 @@ pub fn call_memcpy_generic_array<'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 llvm_sizeof_expr_t = llvm_i8.size_of().get_type();
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
|
||||
let dest_elem_t = dest.get_type().get_element_type();
|
||||
let src_elem_t = src.get_type().get_element_type();
|
||||
@ -209,10 +195,13 @@ pub fn call_memcpy_generic_array<'ctx>(
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let len = ctx.builder.build_int_z_extend_or_bit_cast(len, llvm_sizeof_expr_t, "").unwrap();
|
||||
let len = ctx.builder.build_int_mul(len, src_elem_t.size_of().unwrap(), "").unwrap();
|
||||
let sizeof_elem = ctx
|
||||
.builder
|
||||
.build_int_truncate_or_bit_cast(src_elem_t.size_of().unwrap(), llvm_usize, "")
|
||||
.unwrap();
|
||||
let len = ctx.builder.build_int_mul(len, sizeof_elem, "").unwrap();
|
||||
|
||||
call_memcpy(ctx, dest, src, len, is_volatile);
|
||||
call_memcpy(ctx, dest, src, len);
|
||||
}
|
||||
|
||||
/// Macro to find and generate build call for llvm intrinsic (body of llvm intrinsic function)
|
||||
|
@ -2,14 +2,15 @@ use std::{
|
||||
cell::OnceCell,
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
},
|
||||
thread,
|
||||
};
|
||||
|
||||
use crossbeam::channel::{unbounded, Receiver, Sender};
|
||||
use crossbeam::channel::{Receiver, Sender, unbounded};
|
||||
use inkwell::{
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
basic_block::BasicBlock,
|
||||
builder::Builder,
|
||||
@ -22,7 +23,6 @@ use inkwell::{
|
||||
targets::{CodeModel, RelocMode, Target, TargetMachine, TargetTriple},
|
||||
types::{AnyType, BasicType, BasicTypeEnum, IntType},
|
||||
values::{BasicValueEnum, FunctionValue, IntValue, PhiValue, PointerValue},
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
@ -32,9 +32,9 @@ use nac3parser::ast::{Location, Stmt, StrRef};
|
||||
use crate::{
|
||||
symbol_resolver::{StaticValue, SymbolResolver},
|
||||
toplevel::{
|
||||
helper::{extract_ndims, PrimDef},
|
||||
numpy::unpack_ndarray_var_tys,
|
||||
TopLevelContext, TopLevelDef,
|
||||
helper::{PrimDef, extract_ndims},
|
||||
numpy::unpack_ndarray_var_tys,
|
||||
},
|
||||
typecheck::{
|
||||
type_inferencer::{CodeLocation, PrimitiveStore},
|
||||
@ -43,7 +43,10 @@ use crate::{
|
||||
};
|
||||
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
|
||||
pub use generator::{CodeGenerator, DefaultCodeGenerator};
|
||||
use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType, TupleType};
|
||||
use types::{
|
||||
ExceptionType, ListType, OptionType, ProxyType, RangeType, StringType, TupleType,
|
||||
ndarray::NDArrayType,
|
||||
};
|
||||
|
||||
pub mod builtin_fns;
|
||||
pub mod concrete_type;
|
||||
@ -538,7 +541,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
if PrimDef::contains_id(*obj_id) {
|
||||
return match &*unifier.get_ty_immutable(ty) {
|
||||
TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => {
|
||||
get_llvm_type(
|
||||
let element_type = get_llvm_type(
|
||||
ctx,
|
||||
module,
|
||||
generator,
|
||||
@ -546,9 +549,9 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
top_level,
|
||||
type_cache,
|
||||
*params.iter().next().unwrap().1,
|
||||
)
|
||||
.ptr_type(AddressSpace::default())
|
||||
.into()
|
||||
);
|
||||
|
||||
OptionType::new_with_generator(generator, ctx, &element_type).as_abi_type().into()
|
||||
}
|
||||
|
||||
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
@ -562,7 +565,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
*params.iter().next().unwrap().1,
|
||||
);
|
||||
|
||||
ListType::new_with_generator(generator, ctx, element_type).as_base_type().into()
|
||||
ListType::new_with_generator(generator, ctx, element_type).as_abi_type().into()
|
||||
}
|
||||
|
||||
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
@ -572,7 +575,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
ctx, module, generator, unifier, top_level, type_cache, dtype,
|
||||
);
|
||||
|
||||
NDArrayType::new_with_generator(generator, ctx, element_type, ndims).as_base_type().into()
|
||||
NDArrayType::new_with_generator(generator, ctx, element_type, ndims).as_abi_type().into()
|
||||
}
|
||||
|
||||
_ => unreachable!(
|
||||
@ -626,7 +629,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
|
||||
get_llvm_type(ctx, module, generator, unifier, top_level, type_cache, *ty)
|
||||
})
|
||||
.collect_vec();
|
||||
TupleType::new_with_generator(generator, ctx, &fields).as_base_type().into()
|
||||
TupleType::new_with_generator(generator, ctx, &fields).as_abi_type().into()
|
||||
}
|
||||
TVirtual { .. } => unimplemented!(),
|
||||
_ => unreachable!("{}", ty_enum.get_type_name()),
|
||||
@ -786,34 +789,11 @@ pub fn gen_func_impl<
|
||||
(primitives.float, context.f64_type().into()),
|
||||
(primitives.bool, context.i8_type().into()),
|
||||
(primitives.str, {
|
||||
let name = "str";
|
||||
match module.get_struct_type(name) {
|
||||
None => {
|
||||
let str_type = context.opaque_struct_type("str");
|
||||
let fields = [
|
||||
context.i8_type().ptr_type(AddressSpace::default()).into(),
|
||||
generator.get_size_type(context).into(),
|
||||
];
|
||||
str_type.set_body(&fields, false);
|
||||
str_type.into()
|
||||
}
|
||||
Some(t) => t.as_basic_type_enum(),
|
||||
}
|
||||
StringType::new_with_generator(generator, context).as_abi_type().into()
|
||||
}),
|
||||
(primitives.range, RangeType::new(context).as_base_type().into()),
|
||||
(primitives.range, RangeType::new_with_generator(generator, context).as_abi_type().into()),
|
||||
(primitives.exception, {
|
||||
let name = "Exception";
|
||||
if let Some(t) = module.get_struct_type(name) {
|
||||
t.ptr_type(AddressSpace::default()).as_basic_type_enum()
|
||||
} else {
|
||||
let exception = context.opaque_struct_type("Exception");
|
||||
let int32 = context.i32_type().into();
|
||||
let int64 = context.i64_type().into();
|
||||
let str_ty = module.get_struct_type("str").unwrap().as_basic_type_enum();
|
||||
let fields = [int32, str_ty, int32, int32, str_ty, str_ty, int64, int64, int64];
|
||||
exception.set_body(&fields, false);
|
||||
exception.ptr_type(AddressSpace::default()).as_basic_type_enum()
|
||||
}
|
||||
ExceptionType::new_with_generator(generator, context).as_abi_type().into()
|
||||
}),
|
||||
]
|
||||
.iter()
|
||||
@ -852,7 +832,7 @@ pub fn gen_func_impl<
|
||||
))
|
||||
};
|
||||
|
||||
let has_sret = ret_type.map_or(false, |ty| need_sret(ty));
|
||||
let has_sret = ret_type.is_some_and(|ty| need_sret(ty));
|
||||
let mut params = args
|
||||
.iter()
|
||||
.filter(|arg| !arg.is_vararg)
|
||||
@ -933,7 +913,7 @@ pub fn gen_func_impl<
|
||||
let param_val = param.into_int_value();
|
||||
|
||||
if expected_ty.get_bit_width() == 8 && param_val.get_type().get_bit_width() == 1 {
|
||||
bool_to_i8(&builder, context, param_val)
|
||||
bool_to_int_type(&builder, param_val, context.i8_type())
|
||||
} else {
|
||||
param_val
|
||||
}
|
||||
@ -1048,8 +1028,7 @@ pub fn gen_func_impl<
|
||||
);
|
||||
let generator_llvm_usize = generator.get_size_type(context);
|
||||
assert_eq!(
|
||||
generator_llvm_usize,
|
||||
target_llvm_usize,
|
||||
generator_llvm_usize, target_llvm_usize,
|
||||
"CodeGenerator (size_t = {generator_llvm_usize}) is not compatible with CodeGen Target (size_t = {target_llvm_usize})",
|
||||
);
|
||||
|
||||
@ -1103,43 +1082,29 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
||||
})
|
||||
}
|
||||
|
||||
/// Converts the value of a boolean-like value `bool_value` into an `i1`.
|
||||
fn bool_to_i1<'ctx>(builder: &Builder<'ctx>, bool_value: IntValue<'ctx>) -> IntValue<'ctx> {
|
||||
if bool_value.get_type().get_bit_width() == 1 {
|
||||
bool_value
|
||||
} else {
|
||||
builder
|
||||
.build_int_compare(
|
||||
IntPredicate::NE,
|
||||
bool_value,
|
||||
bool_value.get_type().const_zero(),
|
||||
"tobool",
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the value of a boolean-like value `bool_value` into an `i8`.
|
||||
fn bool_to_i8<'ctx>(
|
||||
/// Converts the value of a boolean-like value `value` into an arbitrary [`IntType`].
|
||||
///
|
||||
/// This has the same semantics as `(ty)(value != 0)` in C.
|
||||
///
|
||||
/// The returned value is guaranteed to either be `0` or `1`, except for `ty == i1` where only the
|
||||
/// least-significant bit would be guaranteed to be `0` or `1`.
|
||||
fn bool_to_int_type<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
ctx: &'ctx Context,
|
||||
bool_value: IntValue<'ctx>,
|
||||
value: IntValue<'ctx>,
|
||||
ty: IntType<'ctx>,
|
||||
) -> IntValue<'ctx> {
|
||||
let value_bits = bool_value.get_type().get_bit_width();
|
||||
match value_bits {
|
||||
8 => bool_value,
|
||||
1 => builder.build_int_z_extend(bool_value, ctx.i8_type(), "frombool").unwrap(),
|
||||
_ => bool_to_i8(
|
||||
// i1 -> i1 : %value ; no-op
|
||||
// i1 -> i<N> : zext i1 %value to i<N> ; guaranteed to be 0 or 1 - see docs
|
||||
// i<M> -> i<N>: zext i1 (icmp eq i<M> %value, 0) to i<N> ; same as i<M> -> i1 -> i<N>
|
||||
match (value.get_type().get_bit_width(), ty.get_bit_width()) {
|
||||
(1, 1) => value,
|
||||
(1, _) => builder.build_int_z_extend(value, ty, "frombool").unwrap(),
|
||||
_ => bool_to_int_type(
|
||||
builder,
|
||||
ctx,
|
||||
builder
|
||||
.build_int_compare(
|
||||
IntPredicate::NE,
|
||||
bool_value,
|
||||
bool_value.get_type().const_zero(),
|
||||
"",
|
||||
)
|
||||
.build_int_compare(IntPredicate::NE, value, value.get_type().const_zero(), "tobool")
|
||||
.unwrap(),
|
||||
ty,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,23 @@
|
||||
use inkwell::{
|
||||
values::{BasicValue, BasicValueEnum, PointerValue},
|
||||
IntPredicate,
|
||||
values::{BasicValue, BasicValueEnum, PointerValue},
|
||||
};
|
||||
|
||||
use nac3parser::ast::StrRef;
|
||||
|
||||
use super::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
macros::codegen_unreachable,
|
||||
stmt::gen_for_callback,
|
||||
types::ndarray::{NDArrayType, NDIterType},
|
||||
values::{ndarray::shape::parse_numpy_int_sequence, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
values::{ProxyValue, ndarray::shape::parse_numpy_int_sequence},
|
||||
};
|
||||
use crate::{
|
||||
symbol_resolver::ValueEnum,
|
||||
toplevel::{
|
||||
DefinitionId,
|
||||
helper::{arraylike_flatten_element_type, extract_ndims},
|
||||
numpy::unpack_ndarray_var_tys,
|
||||
DefinitionId,
|
||||
},
|
||||
typecheck::typedef::{FunSignature, Type},
|
||||
};
|
||||
@ -44,7 +44,7 @@ pub fn gen_ndarray_empty<'ctx>(
|
||||
|
||||
let ndarray = NDArrayType::new(context, llvm_dtype, ndims)
|
||||
.construct_numpy_empty(generator, context, &shape, None);
|
||||
Ok(ndarray.as_base_value())
|
||||
Ok(ndarray.as_abi_value(context))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.zeros`.
|
||||
@ -69,7 +69,7 @@ pub fn gen_ndarray_zeros<'ctx>(
|
||||
|
||||
let ndarray = NDArrayType::new(context, llvm_dtype, ndims)
|
||||
.construct_numpy_zeros(generator, context, dtype, &shape, None);
|
||||
Ok(ndarray.as_base_value())
|
||||
Ok(ndarray.as_abi_value(context))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.ones`.
|
||||
@ -94,7 +94,7 @@ pub fn gen_ndarray_ones<'ctx>(
|
||||
|
||||
let ndarray = NDArrayType::new(context, llvm_dtype, ndims)
|
||||
.construct_numpy_ones(generator, context, dtype, &shape, None);
|
||||
Ok(ndarray.as_base_value())
|
||||
Ok(ndarray.as_abi_value(context))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.full`.
|
||||
@ -127,7 +127,7 @@ pub fn gen_ndarray_full<'ctx>(
|
||||
fill_value_arg,
|
||||
None,
|
||||
);
|
||||
Ok(ndarray.as_base_value())
|
||||
Ok(ndarray.as_abi_value(context))
|
||||
}
|
||||
|
||||
pub fn gen_ndarray_array<'ctx>(
|
||||
@ -166,7 +166,7 @@ pub fn gen_ndarray_array<'ctx>(
|
||||
.construct_numpy_array(generator, context, (obj_ty, obj_arg), copy, None)
|
||||
.atleast_nd(generator, context, ndims);
|
||||
|
||||
Ok(ndarray.as_base_value())
|
||||
Ok(ndarray.as_abi_value(context))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.eye`.
|
||||
@ -225,7 +225,7 @@ pub fn gen_ndarray_eye<'ctx>(
|
||||
|
||||
let ndarray = NDArrayType::new(context, llvm_dtype, 2)
|
||||
.construct_numpy_eye(generator, context, dtype, nrows, ncols, offset, None);
|
||||
Ok(ndarray.as_base_value())
|
||||
Ok(ndarray.as_abi_value(context))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.identity`.
|
||||
@ -253,7 +253,7 @@ pub fn gen_ndarray_identity<'ctx>(
|
||||
.unwrap();
|
||||
let ndarray = NDArrayType::new(context, llvm_dtype, 2)
|
||||
.construct_numpy_identity(generator, context, dtype, n, None);
|
||||
Ok(ndarray.as_base_value())
|
||||
Ok(ndarray.as_abi_value(context))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.copy`.
|
||||
@ -272,9 +272,9 @@ pub fn gen_ndarray_copy<'ctx>(
|
||||
obj.as_ref().unwrap().1.clone().to_basic_value_enum(context, generator, this_ty)?;
|
||||
|
||||
let this = NDArrayType::from_unifier_type(generator, context, this_ty)
|
||||
.map_value(this_arg.into_pointer_value(), None);
|
||||
.map_pointer_value(this_arg.into_pointer_value(), None);
|
||||
let ndarray = this.make_copy(generator, context);
|
||||
Ok(ndarray.as_base_value())
|
||||
Ok(ndarray.as_abi_value(context))
|
||||
}
|
||||
|
||||
/// Generates LLVM IR for `ndarray.fill`.
|
||||
@ -295,7 +295,7 @@ pub fn gen_ndarray_fill<'ctx>(
|
||||
let value_arg = args[0].1.clone().to_basic_value_enum(context, generator, value_ty)?;
|
||||
|
||||
let this = NDArrayType::from_unifier_type(generator, context, this_ty)
|
||||
.map_value(this_arg.into_pointer_value(), None);
|
||||
.map_pointer_value(this_arg.into_pointer_value(), None);
|
||||
this.fill(generator, context, value_arg);
|
||||
Ok(())
|
||||
}
|
||||
@ -316,8 +316,10 @@ pub fn ndarray_dot<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
match (x1, x2) {
|
||||
(BasicValueEnum::PointerValue(n1), BasicValueEnum::PointerValue(n2)) => {
|
||||
let a = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(n1, None);
|
||||
let b = NDArrayType::from_unifier_type(generator, ctx, x2_ty).map_value(n2, None);
|
||||
let a =
|
||||
NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_pointer_value(n1, None);
|
||||
let b =
|
||||
NDArrayType::from_unifier_type(generator, ctx, x2_ty).map_pointer_value(n2, None);
|
||||
|
||||
// TODO: General `np.dot()` https://numpy.org/doc/stable/reference/generated/numpy.dot.html.
|
||||
assert_eq!(a.get_type().ndims(), 1);
|
||||
|
@ -0,0 +1,36 @@
|
||||
---
|
||||
source: nac3core/src/codegen/test.rs
|
||||
expression: "module.print_to_string().to_str().map(str::trim).unwrap()"
|
||||
---
|
||||
; ModuleID = 'test'
|
||||
source_filename = "test"
|
||||
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
|
||||
define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 !dbg !4 {
|
||||
init:
|
||||
%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 willreturn memory(none) }
|
||||
|
||||
!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: 2, scope: !4)
|
||||
!12 = !DILocation(line: 3, column: 8, scope: !4)
|
@ -0,0 +1,42 @@
|
||||
---
|
||||
source: nac3core/src/codegen/test.rs
|
||||
expression: "module.print_to_string().to_str().map(str::trim).unwrap()"
|
||||
---
|
||||
; ModuleID = 'test'
|
||||
source_filename = "test"
|
||||
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-unknown-linux-gnu"
|
||||
|
||||
; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
|
||||
define i32 @testing(i32 %0) local_unnamed_addr #0 !dbg !5 {
|
||||
init:
|
||||
%add.i = shl i32 %0, 1, !dbg !10
|
||||
%mul = add i32 %add.i, 2, !dbg !10
|
||||
ret i32 %mul, !dbg !10
|
||||
}
|
||||
|
||||
; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
|
||||
define i32 @foo.0(i32 %0) local_unnamed_addr #0 !dbg !11 {
|
||||
init:
|
||||
%add = add i32 %0, 1, !dbg !12
|
||||
ret i32 %add, !dbg !12
|
||||
}
|
||||
|
||||
attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) }
|
||||
|
||||
!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)
|
@ -1,35 +1,35 @@
|
||||
use inkwell::{
|
||||
IntPredicate,
|
||||
attributes::{Attribute, AttributeLoc},
|
||||
basic_block::BasicBlock,
|
||||
builder::Builder,
|
||||
types::{BasicType, BasicTypeEnum},
|
||||
values::{BasicValue, BasicValueEnum, FunctionValue, IntValue, PointerValue},
|
||||
IntPredicate,
|
||||
};
|
||||
use itertools::{izip, Itertools};
|
||||
use itertools::{Itertools, izip};
|
||||
|
||||
use nac3parser::ast::{
|
||||
Constant, ExcepthandlerKind, Expr, ExprKind, Location, Stmt, StmtKind, StrRef,
|
||||
};
|
||||
|
||||
use super::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::{destructure_range, gen_binop_expr},
|
||||
gen_in_range_check,
|
||||
irrt::{handle_slice_indices, list_slice_assignment},
|
||||
macros::codegen_unreachable,
|
||||
types::ndarray::NDArrayType,
|
||||
types::{ExceptionType, RangeType, ndarray::NDArrayType},
|
||||
values::{
|
||||
ArrayLikeIndexer, ArraySliceValue, ExceptionValue, ListValue, ProxyValue,
|
||||
ndarray::{RustNDIndex, ScalarOrNDArray},
|
||||
ArrayLikeIndexer, ArraySliceValue, ListValue, ProxyValue, RangeValue,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
use crate::{
|
||||
symbol_resolver::ValueEnum,
|
||||
toplevel::{DefinitionId, TopLevelDef},
|
||||
typecheck::{
|
||||
magic_methods::Binop,
|
||||
typedef::{iter_type_vars, FunSignature, Type, TypeEnum},
|
||||
typedef::{FunSignature, Type, TypeEnum, iter_type_vars},
|
||||
},
|
||||
};
|
||||
|
||||
@ -133,7 +133,7 @@ pub fn gen_store_target<'ctx, G: CodeGenerator>(
|
||||
ptr,
|
||||
&[
|
||||
ctx.ctx.i32_type().const_zero(),
|
||||
ctx.ctx.i32_type().const_int(index as u64, false),
|
||||
ctx.ctx.i32_type().const_int(index.unwrap() as u64, false),
|
||||
],
|
||||
name.unwrap_or(""),
|
||||
)
|
||||
@ -234,7 +234,7 @@ pub fn gen_assign_target_list<'ctx, G: CodeGenerator>(
|
||||
|
||||
let a = starred_target_index; // Number of RHS values before the starred target
|
||||
let b = tuple_tys.len() - (targets.len() - 1 - starred_target_index); // Number of RHS values after the starred target
|
||||
// Thus `tuple[a..b]` is assigned to the starred target.
|
||||
// Thus `tuple[a..b]` is assigned to the starred target.
|
||||
|
||||
// Handle assignment before the starred target
|
||||
for (target, val, val_ty) in
|
||||
@ -440,7 +440,7 @@ pub fn gen_setitem<'ctx, G: CodeGenerator>(
|
||||
// ```
|
||||
|
||||
let target = NDArrayType::from_unifier_type(generator, ctx, target_ty)
|
||||
.map_value(target.into_pointer_value(), None);
|
||||
.map_pointer_value(target.into_pointer_value(), None);
|
||||
let target = target.index(generator, ctx, &key);
|
||||
|
||||
let value = ScalarOrNDArray::from_value(generator, ctx, (value_ty, value))
|
||||
@ -511,7 +511,7 @@ pub fn gen_for<G: CodeGenerator>(
|
||||
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
let iter_val =
|
||||
RangeValue::from_pointer_value(iter_val.into_pointer_value(), Some("range"));
|
||||
RangeType::new(ctx).map_pointer_value(iter_val.into_pointer_value(), Some("range"));
|
||||
// Internal variable for loop; Cannot be assigned
|
||||
let i = generator.gen_var_alloc(ctx, int32.into(), Some("for.i.addr"))?;
|
||||
// Variable declared in "target" expression of the loop; Can be reassigned *or* shadowed
|
||||
@ -1337,43 +1337,19 @@ pub fn exn_constructor<'ctx>(
|
||||
pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
exception: Option<&BasicValueEnum<'ctx>>,
|
||||
exception: Option<&ExceptionValue<'ctx>>,
|
||||
loc: Location,
|
||||
) {
|
||||
if let Some(exception) = exception {
|
||||
unsafe {
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let exception = exception.into_pointer_value();
|
||||
let file_ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(exception, &[zero, int32.const_int(1, false)], "file_ptr")
|
||||
.unwrap();
|
||||
let filename = ctx.gen_string(generator, loc.file.0);
|
||||
ctx.builder.build_store(file_ptr, filename).unwrap();
|
||||
let row_ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(exception, &[zero, int32.const_int(2, false)], "row_ptr")
|
||||
.unwrap();
|
||||
ctx.builder.build_store(row_ptr, int32.const_int(loc.row as u64, false)).unwrap();
|
||||
let col_ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(exception, &[zero, int32.const_int(3, false)], "col_ptr")
|
||||
.unwrap();
|
||||
ctx.builder.build_store(col_ptr, int32.const_int(loc.column as u64, false)).unwrap();
|
||||
exception.store_location(generator, ctx, loc);
|
||||
|
||||
let current_fun = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
|
||||
let name_ptr = ctx
|
||||
.builder
|
||||
.build_in_bounds_gep(exception, &[zero, int32.const_int(4, false)], "name_ptr")
|
||||
.unwrap();
|
||||
ctx.builder.build_store(name_ptr, fun_name).unwrap();
|
||||
}
|
||||
let current_fun = ctx.builder.get_insert_block().and_then(BasicBlock::get_parent).unwrap();
|
||||
let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
|
||||
exception.store_func(ctx, fun_name);
|
||||
|
||||
let raise = get_builtins(generator, ctx, "__nac3_raise");
|
||||
let exception = *exception;
|
||||
ctx.build_call_or_invoke(raise, &[exception], "raise");
|
||||
ctx.build_call_or_invoke(raise, &[exception.as_abi_value(ctx).into()], "raise");
|
||||
} else {
|
||||
let resume = get_builtins(generator, ctx, "__nac3_resume");
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
@ -1492,10 +1468,10 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
||||
ctx.outer_catch_clauses = old_clauses;
|
||||
ctx.unwind_target = old_unwind;
|
||||
ctx.return_target = old_return;
|
||||
ctx.loop_target = old_loop_target.or(ctx.loop_target).take();
|
||||
ctx.loop_target = old_loop_target.or(ctx.loop_target);
|
||||
|
||||
let old_unwind = if finalbody.is_empty() {
|
||||
None
|
||||
old_unwind
|
||||
} else {
|
||||
let final_landingpad = ctx.ctx.append_basic_block(current_fun, "try.catch.final");
|
||||
ctx.builder.position_at_end(final_landingpad);
|
||||
@ -1616,7 +1592,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
||||
}
|
||||
|
||||
ctx.unwind_target = old_unwind;
|
||||
ctx.loop_target = old_loop_target.or(ctx.loop_target).take();
|
||||
ctx.loop_target = old_loop_target.or(ctx.loop_target);
|
||||
ctx.return_target = old_return;
|
||||
|
||||
ctx.builder.position_at_end(landingpad);
|
||||
@ -1712,13 +1688,259 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
|
||||
}
|
||||
|
||||
/// See [`CodeGenerator::gen_with`].
|
||||
pub fn gen_with<G: CodeGenerator>(
|
||||
_: &mut G,
|
||||
_: &mut CodeGenContext<'_, '_>,
|
||||
pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
// TODO: Implement with statement after finishing exceptions
|
||||
Err(format!("With statement with custom types is not yet supported (at {})", stmt.location))
|
||||
let StmtKind::With { items, body, .. } = &stmt.node else { codegen_unreachable!(ctx) };
|
||||
let mut exits = vec![];
|
||||
let mut enters = vec![];
|
||||
|
||||
// prepare enters and exits
|
||||
for item in items {
|
||||
// evaluate the expression first
|
||||
let expr_ty = item.context_expr.custom.unwrap();
|
||||
let expr = generator.gen_expr(ctx, &item.context_expr)?.unwrap();
|
||||
|
||||
// get the __enter__ method signature and ID
|
||||
let TypeEnum::TObj { obj_id, fields, .. } = &*ctx.unifier.get_ty(expr_ty) else {
|
||||
codegen_unreachable!(ctx)
|
||||
};
|
||||
let top_level_defs = ctx.top_level.definitions.read();
|
||||
let def = top_level_defs[obj_id.0].read();
|
||||
let TopLevelDef::Class { methods, .. } = &*def else { codegen_unreachable!(ctx) };
|
||||
let enter_fun_id = methods
|
||||
.iter()
|
||||
.find(|method| method.0 == "__enter__".into())
|
||||
.map(|method| method.2)
|
||||
.unwrap();
|
||||
let enter = fields.get(&"__enter__".into()).copied().unwrap();
|
||||
let TypeEnum::TFunc(enter_signature) = &*ctx.unifier.get_ty(enter.0) else {
|
||||
codegen_unreachable!(ctx)
|
||||
};
|
||||
|
||||
enters.push((
|
||||
expr_ty,
|
||||
expr.clone(),
|
||||
enter_signature.clone(),
|
||||
enter_fun_id,
|
||||
item.optional_vars.clone(),
|
||||
));
|
||||
|
||||
// save __exit__() data to be called later in final stage
|
||||
let exit_fun_id = methods
|
||||
.iter()
|
||||
.find(|method| method.0 == "__exit__".into())
|
||||
.map(|method| method.2)
|
||||
.unwrap();
|
||||
let exit = fields.get(&"__exit__".into()).copied().unwrap();
|
||||
let TypeEnum::TFunc(exit_signature) = &*ctx.unifier.get_ty(exit.0) else {
|
||||
codegen_unreachable!(ctx)
|
||||
};
|
||||
// stack the exits as the exit order is opposite of enter
|
||||
// would be best to reuse try...finally but re-building Stmt vec seems infeasible
|
||||
exits.push((expr_ty, expr, exit_signature.clone(), exit_fun_id));
|
||||
}
|
||||
|
||||
let body_gen_lambda = |ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut G|
|
||||
-> Result<(), String> {
|
||||
for enter in &enters {
|
||||
// call __enter__()
|
||||
let enter_ret = generator.gen_call(
|
||||
ctx,
|
||||
Some((enter.0, enter.1.clone())),
|
||||
(&enter.2, enter.3),
|
||||
Vec::default(),
|
||||
)?;
|
||||
|
||||
// deal with assignments (`as`)
|
||||
if let Some(optional_vars) = &enter.4 {
|
||||
generator.gen_assign(ctx, optional_vars, enter_ret.unwrap().into(), enter.2.ret)?;
|
||||
}
|
||||
}
|
||||
|
||||
// generate the `with` body
|
||||
generator.gen_block(ctx, body.iter())
|
||||
};
|
||||
|
||||
let exit_gen_lambda =
|
||||
|ctx: &mut CodeGenContext<'ctx, 'a>, generator: &mut G| -> Result<(), String> {
|
||||
// call __exit__()s in the reverse order
|
||||
for exit in exits.iter().rev() {
|
||||
generator.gen_call(
|
||||
ctx,
|
||||
Some((exit.0, exit.1.clone())),
|
||||
(&exit.2, exit.3),
|
||||
Vec::default(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// copied and trimmed from gen_try, to cover try (setup, enter)..finally (exit)
|
||||
let personality_symbol = ctx.top_level.personality_symbol.as_ref().unwrap();
|
||||
let personality = ctx.module.get_function(personality_symbol).unwrap_or_else(|| {
|
||||
let ty = ctx.ctx.i32_type().fn_type(&[], true);
|
||||
ctx.module.add_function(personality_symbol, ty, None)
|
||||
});
|
||||
let exception_type = ctx.get_llvm_type(generator, ctx.primitives.exception);
|
||||
let ptr_type = ctx.ctx.i8_type().ptr_type(inkwell::AddressSpace::default());
|
||||
let current_block = ctx.builder.get_insert_block().unwrap();
|
||||
let current_fun = current_block.get_parent().unwrap();
|
||||
let landingpad = ctx.ctx.append_basic_block(current_fun, "with.landingpad");
|
||||
let dispatcher = ctx.ctx.append_basic_block(current_fun, "with.dispatch");
|
||||
let dispatcher_end = dispatcher;
|
||||
ctx.builder.position_at_end(dispatcher);
|
||||
let exn = ctx.builder.build_phi(exception_type, "exn").unwrap();
|
||||
ctx.builder.position_at_end(current_block);
|
||||
|
||||
let mut old_loop_target = None;
|
||||
let final_state =
|
||||
generator.gen_var_alloc(ctx, ptr_type.into(), Some("with.final_state.addr"))?;
|
||||
let mut final_data = Some((final_state, Vec::new(), Vec::new()));
|
||||
if let Some((continue_target, break_target)) = ctx.loop_target {
|
||||
let break_proxy = ctx.ctx.append_basic_block(current_fun, "with.break");
|
||||
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "with.continue");
|
||||
final_proxy(ctx, break_target, break_proxy, final_data.as_mut().unwrap());
|
||||
final_proxy(ctx, continue_target, continue_proxy, final_data.as_mut().unwrap());
|
||||
old_loop_target = ctx.loop_target.replace((continue_proxy, break_proxy));
|
||||
}
|
||||
let return_proxy = ctx.ctx.append_basic_block(current_fun, "with.return");
|
||||
if let Some(return_target) = ctx.return_target {
|
||||
final_proxy(ctx, return_target, return_proxy, final_data.as_mut().unwrap());
|
||||
} else {
|
||||
let return_target = ctx.ctx.append_basic_block(current_fun, "with.return_target");
|
||||
ctx.builder.position_at_end(return_target);
|
||||
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret").unwrap());
|
||||
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue)).unwrap();
|
||||
ctx.builder.position_at_end(current_block);
|
||||
final_proxy(ctx, return_target, return_proxy, final_data.as_mut().unwrap());
|
||||
}
|
||||
let old_return = ctx.return_target.replace(return_proxy);
|
||||
let cleanup = ctx.ctx.append_basic_block(current_fun, "with.cleanup");
|
||||
|
||||
// replace unwind target, clauses stay the same
|
||||
let old_unwind = ctx.unwind_target.replace(landingpad);
|
||||
body_gen_lambda(ctx, generator)?;
|
||||
let body = ctx.builder.get_insert_block().unwrap();
|
||||
// reset old_unwind
|
||||
ctx.unwind_target = old_unwind;
|
||||
ctx.return_target = old_return;
|
||||
ctx.loop_target = old_loop_target.or(ctx.loop_target);
|
||||
|
||||
let final_landingpad = ctx.ctx.append_basic_block(current_fun, "with.catch.final");
|
||||
ctx.builder.position_at_end(final_landingpad);
|
||||
ctx.builder
|
||||
.build_landing_pad(
|
||||
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
|
||||
personality,
|
||||
&[],
|
||||
true,
|
||||
"with.catch.final",
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_unconditional_branch(cleanup).unwrap();
|
||||
ctx.builder.position_at_end(body);
|
||||
let old_unwind = ctx.unwind_target.replace(final_landingpad);
|
||||
|
||||
let mut final_proxy_lambda =
|
||||
|ctx: &mut CodeGenContext<'ctx, 'a>, target: BasicBlock<'ctx>, block: BasicBlock<'ctx>| {
|
||||
final_proxy(ctx, target, block, final_data.as_mut().unwrap());
|
||||
};
|
||||
let redirect = &mut final_proxy_lambda
|
||||
as &mut dyn FnMut(&mut CodeGenContext<'ctx, 'a>, BasicBlock<'ctx>, BasicBlock<'ctx>);
|
||||
|
||||
let resume = get_builtins(generator, ctx, "__nac3_resume");
|
||||
let end_catch = get_builtins(generator, ctx, "__nac3_end_catch");
|
||||
if let Some((continue_target, break_target)) = ctx.loop_target.take() {
|
||||
let break_proxy = ctx.ctx.append_basic_block(current_fun, "with.break");
|
||||
let continue_proxy = ctx.ctx.append_basic_block(current_fun, "with.continue");
|
||||
ctx.builder.position_at_end(break_proxy);
|
||||
ctx.builder.build_call(end_catch, &[], "end_catch").unwrap();
|
||||
ctx.builder.position_at_end(continue_proxy);
|
||||
ctx.builder.build_call(end_catch, &[], "end_catch").unwrap();
|
||||
ctx.builder.position_at_end(body);
|
||||
redirect(ctx, break_target, break_proxy);
|
||||
redirect(ctx, continue_target, continue_proxy);
|
||||
ctx.loop_target = Some((continue_proxy, break_proxy));
|
||||
old_loop_target = Some((continue_target, break_target));
|
||||
}
|
||||
let return_proxy = ctx.ctx.append_basic_block(current_fun, "with.return");
|
||||
ctx.builder.position_at_end(return_proxy);
|
||||
ctx.builder.build_call(end_catch, &[], "end_catch").unwrap();
|
||||
let return_target = ctx.return_target.take().unwrap_or_else(|| {
|
||||
let doreturn = ctx.ctx.append_basic_block(current_fun, "with.doreturn");
|
||||
ctx.builder.position_at_end(doreturn);
|
||||
let return_value = ctx.return_buffer.map(|v| ctx.builder.build_load(v, "$ret").unwrap());
|
||||
ctx.builder.build_return(return_value.as_ref().map(|v| v as &dyn BasicValue)).unwrap();
|
||||
doreturn
|
||||
});
|
||||
redirect(ctx, return_target, return_proxy);
|
||||
ctx.return_target = Some(return_proxy);
|
||||
let old_return = Some(return_target);
|
||||
|
||||
ctx.unwind_target = old_unwind;
|
||||
ctx.loop_target = old_loop_target.or(ctx.loop_target);
|
||||
ctx.return_target = old_return;
|
||||
|
||||
ctx.builder.position_at_end(landingpad);
|
||||
|
||||
let landingpad_value = ctx
|
||||
.builder
|
||||
.build_landing_pad(
|
||||
ctx.ctx.struct_type(&[ptr_type.into(), exception_type], false),
|
||||
personality,
|
||||
&Vec::new(),
|
||||
true,
|
||||
"try.landingpad",
|
||||
)
|
||||
.map(BasicValueEnum::into_struct_value)
|
||||
.unwrap();
|
||||
let exn_val = ctx.builder.build_extract_value(landingpad_value, 1, "exn").unwrap();
|
||||
ctx.builder.build_unconditional_branch(dispatcher).unwrap();
|
||||
exn.add_incoming(&[(&exn_val, landingpad)]);
|
||||
|
||||
if dispatcher_end.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(dispatcher_end);
|
||||
ctx.builder.build_unconditional_branch(cleanup).unwrap();
|
||||
}
|
||||
|
||||
// exception path
|
||||
ctx.builder.position_at_end(cleanup);
|
||||
exit_gen_lambda(ctx, generator)?;
|
||||
ctx.build_call_or_invoke(resume, &[], "resume");
|
||||
ctx.builder.build_unreachable().unwrap();
|
||||
|
||||
// normal path
|
||||
let (final_state, mut final_targets, final_paths) = final_data.unwrap();
|
||||
let tail = ctx.ctx.append_basic_block(current_fun, "with.tail");
|
||||
final_targets.push(tail);
|
||||
let finalizer = ctx.ctx.append_basic_block(current_fun, "with.exits");
|
||||
ctx.builder.position_at_end(finalizer);
|
||||
exit_gen_lambda(ctx, generator)?;
|
||||
let dest = ctx.builder.build_load(final_state, "final_dest").unwrap();
|
||||
ctx.builder.build_indirect_branch(dest, &final_targets).unwrap();
|
||||
for block in &final_paths {
|
||||
if block.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(*block);
|
||||
ctx.builder.build_unconditional_branch(finalizer).unwrap();
|
||||
}
|
||||
}
|
||||
for block in &[body] {
|
||||
if block.get_terminator().is_none() {
|
||||
ctx.builder.position_at_end(*block);
|
||||
unsafe {
|
||||
ctx.builder.build_store(final_state, tail.get_address().unwrap()).unwrap();
|
||||
}
|
||||
ctx.builder.build_unconditional_branch(finalizer).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
ctx.builder.position_at_end(tail);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generates IR for a `return` statement.
|
||||
@ -1860,6 +2082,8 @@ pub fn gen_stmt<G: CodeGenerator>(
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
let exc = ExceptionType::get_instance(generator, ctx)
|
||||
.map_pointer_value(exc.into_pointer_value(), None);
|
||||
gen_raise(generator, ctx, Some(&exc), stmt.location);
|
||||
} else {
|
||||
gen_raise(generator, ctx, None, stmt.location);
|
||||
|
@ -3,29 +3,30 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use function_name::named;
|
||||
use indexmap::IndexMap;
|
||||
use indoc::indoc;
|
||||
use inkwell::{
|
||||
targets::{InitializationConfig, Target},
|
||||
OptimizationLevel,
|
||||
targets::{InitializationConfig, Target},
|
||||
};
|
||||
use nac3parser::{
|
||||
ast::{fold::Fold, FileName, StrRef},
|
||||
ast::{FileName, StrRef, fold::Fold},
|
||||
parser::parse_program,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use super::{
|
||||
concrete_type::ConcreteTypeStore,
|
||||
types::{ndarray::NDArrayType, ListType, ProxyType, RangeType},
|
||||
CodeGenContext, CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator,
|
||||
DefaultCodeGenerator, WithCall, WorkerRegistry,
|
||||
concrete_type::ConcreteTypeStore,
|
||||
types::{ListType, ProxyType, RangeType, ndarray::NDArrayType},
|
||||
};
|
||||
use crate::{
|
||||
symbol_resolver::{SymbolResolver, ValueEnum},
|
||||
toplevel::{
|
||||
composer::{ComposerConfig, TopLevelComposer},
|
||||
DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
|
||||
composer::{ComposerConfig, TopLevelComposer},
|
||||
},
|
||||
typecheck::{
|
||||
type_inferencer::{FunctionData, IdentifierInfo, Inferencer, PrimitiveStore},
|
||||
@ -89,6 +90,7 @@ impl SymbolResolver for Resolver {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[named]
|
||||
fn test_primitives() {
|
||||
let source = indoc! { "
|
||||
c = a + b
|
||||
@ -181,60 +183,10 @@ fn test_primitives() {
|
||||
id: 0,
|
||||
};
|
||||
let f = Arc::new(WithCall::new(Box::new(|module| {
|
||||
// the following IR is equivalent to
|
||||
// ```
|
||||
// ; ModuleID = 'test.ll'
|
||||
// source_filename = "test"
|
||||
//
|
||||
// ; Function Attrs: norecurse nounwind readnone
|
||||
// define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 {
|
||||
// init:
|
||||
// %add = add i32 %1, %0
|
||||
// %cmp = icmp eq i32 %add, 1
|
||||
// %ifexpr = select i1 %cmp, i32 %0, i32 0
|
||||
// ret i32 %ifexpr
|
||||
// }
|
||||
//
|
||||
// attributes #0 = { norecurse nounwind readnone }
|
||||
// ```
|
||||
// after O2 optimization
|
||||
|
||||
let expected = indoc! {"
|
||||
; ModuleID = 'test'
|
||||
source_filename = \"test\"
|
||||
target datalayout = \"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128\"
|
||||
target triple = \"x86_64-unknown-linux-gnu\"
|
||||
|
||||
; 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 %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());
|
||||
insta::assert_snapshot!(
|
||||
function_name!(),
|
||||
module.print_to_string().to_str().map(str::trim).unwrap()
|
||||
);
|
||||
})));
|
||||
|
||||
Target::initialize_all(&InitializationConfig::default());
|
||||
@ -249,6 +201,7 @@ fn test_primitives() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[named]
|
||||
fn test_simple_call() {
|
||||
let source_1 = indoc! { "
|
||||
a = foo(a)
|
||||
@ -383,48 +336,10 @@ fn test_simple_call() {
|
||||
id: 0,
|
||||
};
|
||||
let f = Arc::new(WithCall::new(Box::new(|module| {
|
||||
let expected = indoc! {"
|
||||
; ModuleID = 'test'
|
||||
source_filename = \"test\"
|
||||
target datalayout = \"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128\"
|
||||
target triple = \"x86_64-unknown-linux-gnu\"
|
||||
|
||||
; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
|
||||
define i32 @testing(i32 %0) local_unnamed_addr #0 !dbg !5 {
|
||||
init:
|
||||
%add.i = shl i32 %0, 1, !dbg !10
|
||||
%mul = add i32 %add.i, 2, !dbg !10
|
||||
ret i32 %mul, !dbg !10
|
||||
}
|
||||
|
||||
; 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, !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());
|
||||
insta::assert_snapshot!(
|
||||
function_name!(),
|
||||
module.print_to_string().to_str().map(str::trim).unwrap()
|
||||
);
|
||||
})));
|
||||
|
||||
Target::initialize_all(&InitializationConfig::default());
|
||||
@ -447,15 +362,18 @@ fn test_classes_list_type_new() {
|
||||
let llvm_usize = generator.get_size_type(&ctx);
|
||||
|
||||
let llvm_list = ListType::new_with_generator(&generator, &ctx, llvm_i32.into());
|
||||
assert!(ListType::is_representable(llvm_list.as_base_type(), llvm_usize).is_ok());
|
||||
assert!(ListType::is_representable(llvm_list.as_abi_type(), llvm_usize).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_classes_range_type_new() {
|
||||
let ctx = inkwell::context::Context::create();
|
||||
let generator = DefaultCodeGenerator::new(String::new(), ctx.i64_type());
|
||||
|
||||
let llvm_range = RangeType::new(&ctx);
|
||||
assert!(RangeType::is_representable(llvm_range.as_base_type()).is_ok());
|
||||
let llvm_usize = generator.get_size_type(&ctx);
|
||||
|
||||
let llvm_range = RangeType::new_with_generator(&generator, &ctx);
|
||||
assert!(RangeType::is_representable(llvm_range.as_abi_type(), llvm_usize).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -467,5 +385,5 @@ fn test_classes_ndarray_type_new() {
|
||||
let llvm_usize = generator.get_size_type(&ctx);
|
||||
|
||||
let llvm_ndarray = NDArrayType::new_with_generator(&generator, &ctx, llvm_i32.into(), 2);
|
||||
assert!(NDArrayType::is_representable(llvm_ndarray.as_base_type(), llvm_usize).is_ok());
|
||||
assert!(NDArrayType::is_representable(llvm_ndarray.as_abi_type(), llvm_usize).is_ok());
|
||||
}
|
||||
|
264
nac3core/src/codegen/types/exception.rs
Normal file
264
nac3core/src/codegen/types/exception.rs
Normal file
@ -0,0 +1,264 @@
|
||||
use inkwell::{
|
||||
AddressSpace,
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core_derive::StructFields;
|
||||
|
||||
use super::{
|
||||
ProxyType,
|
||||
structure::{StructField, StructFields, StructProxyType, check_struct_type_matches_fields},
|
||||
};
|
||||
use crate::{
|
||||
codegen::{CodeGenContext, CodeGenerator, values::ExceptionValue},
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
};
|
||||
|
||||
/// Proxy type for an `Exception` in LLVM.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct ExceptionType<'ctx> {
|
||||
ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
||||
pub struct ExceptionStructFields<'ctx> {
|
||||
/// The ID of the exception name.
|
||||
#[value_type(i32_type())]
|
||||
pub name: StructField<'ctx, IntValue<'ctx>>,
|
||||
|
||||
/// The file where the exception originated from.
|
||||
#[value_type(get_struct_type("str").unwrap())]
|
||||
pub file: StructField<'ctx, StructValue<'ctx>>,
|
||||
|
||||
/// The line number where the exception originated from.
|
||||
#[value_type(i32_type())]
|
||||
pub line: StructField<'ctx, IntValue<'ctx>>,
|
||||
|
||||
/// The column number where the exception originated from.
|
||||
#[value_type(i32_type())]
|
||||
pub col: StructField<'ctx, IntValue<'ctx>>,
|
||||
|
||||
/// The function name where the exception originated from.
|
||||
#[value_type(get_struct_type("str").unwrap())]
|
||||
pub func: StructField<'ctx, StructValue<'ctx>>,
|
||||
|
||||
/// The exception message.
|
||||
#[value_type(get_struct_type("str").unwrap())]
|
||||
pub message: StructField<'ctx, StructValue<'ctx>>,
|
||||
|
||||
#[value_type(i64_type())]
|
||||
pub param0: StructField<'ctx, IntValue<'ctx>>,
|
||||
|
||||
#[value_type(i64_type())]
|
||||
pub param1: StructField<'ctx, IntValue<'ctx>>,
|
||||
|
||||
#[value_type(i64_type())]
|
||||
pub param2: StructField<'ctx, IntValue<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx> ExceptionType<'ctx> {
|
||||
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
|
||||
#[must_use]
|
||||
fn fields(
|
||||
ctx: impl AsContextRef<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> ExceptionStructFields<'ctx> {
|
||||
ExceptionStructFields::new(ctx, llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of an `Exception`.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
|
||||
const NAME: &str = "Exception";
|
||||
|
||||
assert!(ctx.get_struct_type("str").is_some());
|
||||
|
||||
if let Some(t) = ctx.get_struct_type(NAME) {
|
||||
t.ptr_type(AddressSpace::default())
|
||||
} else {
|
||||
let exn_ty = ctx.opaque_struct_type(NAME);
|
||||
let field_tys =
|
||||
Self::fields(ctx, llvm_usize).into_iter().map(|field| field.1).collect_vec();
|
||||
exn_ty.set_body(&field_tys, false);
|
||||
exn_ty.ptr_type(AddressSpace::default())
|
||||
}
|
||||
}
|
||||
|
||||
fn new_impl(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> Self {
|
||||
let llvm_str = Self::llvm_type(ctx, llvm_usize);
|
||||
|
||||
Self { ty: llvm_str, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an instance of [`ExceptionType`].
|
||||
#[must_use]
|
||||
pub fn new(ctx: &CodeGenContext<'ctx, '_>) -> Self {
|
||||
Self::new_impl(ctx.ctx, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an instance of [`ExceptionType`].
|
||||
#[must_use]
|
||||
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Self {
|
||||
Self::new_impl(ctx, generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates an [`ExceptionType`] from a [unifier type][Type].
|
||||
#[must_use]
|
||||
pub fn from_unifier_type(ctx: &mut CodeGenContext<'ctx, '_>, ty: Type) -> Self {
|
||||
// Check unifier type
|
||||
assert!(
|
||||
matches!(&*ctx.unifier.get_ty_immutable(ty), TypeEnum::TObj { obj_id, .. } if *obj_id == ctx.primitives.exception.obj_id(&ctx.unifier).unwrap())
|
||||
);
|
||||
|
||||
Self::new_impl(ctx.ctx, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an [`ExceptionType`] from a [`StructType`] representing an `Exception`.
|
||||
#[must_use]
|
||||
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`ExceptionType`] from a [`PointerType`] representing an `Exception`.
|
||||
#[must_use]
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
Self { ty: ptr_ty, llvm_usize }
|
||||
}
|
||||
|
||||
/// Returns an instance of [`ExceptionType`] by obtaining the LLVM representation of the builtin
|
||||
/// `Exception` type.
|
||||
#[must_use]
|
||||
pub fn get_instance<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
) -> Self {
|
||||
Self::from_pointer_type(
|
||||
ctx.get_llvm_type(generator, ctx.primitives.exception).into_pointer_type(),
|
||||
ctx.get_size_type(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocates an instance of [`ExceptionValue`] as if by calling `alloca` on the base type.
|
||||
///
|
||||
/// See [`ProxyType::raw_alloca`].
|
||||
#[must_use]
|
||||
pub fn alloca(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||
self.raw_alloca(ctx, name),
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocates an instance of [`ExceptionValue`] as if by calling `alloca` on the base type.
|
||||
///
|
||||
/// See [`ProxyType::raw_alloca_var`].
|
||||
#[must_use]
|
||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||
self.raw_alloca_var(generator, ctx, name),
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`ExceptionValue`].
|
||||
#[must_use]
|
||||
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`ExceptionValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for ExceptionType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = ExceptionValue<'ctx>;
|
||||
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
let ctx = ty.get_context();
|
||||
|
||||
let llvm_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!("Expected struct type for `list` type, got {llvm_ty}"));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(Self::fields(ctx, llvm_usize), llvm_ty, "exception", &[])
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyType<'ctx> for ExceptionType<'ctx> {
|
||||
type StructFields = ExceptionStructFields<'ctx>;
|
||||
|
||||
fn get_fields(&self) -> Self::StructFields {
|
||||
Self::fields(self.ty.get_context(), self.llvm_usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<ExceptionType<'ctx>> for PointerType<'ctx> {
|
||||
fn from(value: ExceptionType<'ctx>) -> Self {
|
||||
value.as_base_type()
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
use inkwell::{
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace, IntPredicate, OptimizationLevel,
|
||||
context::Context,
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
@ -11,13 +11,14 @@ use nac3core_derive::StructFields;
|
||||
use super::ProxyType;
|
||||
use crate::{
|
||||
codegen::{
|
||||
types::structure::{
|
||||
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
|
||||
},
|
||||
values::{ListValue, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
types::structure::{
|
||||
FieldIndexCounter, StructField, StructFields, StructProxyType,
|
||||
check_struct_type_matches_fields,
|
||||
},
|
||||
values::ListValue,
|
||||
},
|
||||
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
|
||||
typecheck::typedef::{Type, TypeEnum, iter_type_vars},
|
||||
};
|
||||
|
||||
/// Proxy type for a `list` type in LLVM.
|
||||
@ -56,47 +57,12 @@ impl<'ctx> ListStructFields<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ListType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a `list` type, returning [Err] if it does not.
|
||||
pub fn is_representable(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let ctx = llvm_ty.get_context();
|
||||
|
||||
let llvm_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!("Expected struct type for `list` type, got {llvm_ty}"));
|
||||
};
|
||||
|
||||
let fields = ListStructFields::new(ctx, llvm_usize);
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
fields,
|
||||
llvm_ty,
|
||||
"list",
|
||||
&[(fields.items.name(), &|ty| {
|
||||
if ty.is_pointer_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected T* for `list.items`, got {ty}"))
|
||||
}
|
||||
})],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
|
||||
#[must_use]
|
||||
fn fields(item: BasicTypeEnum<'ctx>, llvm_usize: IntType<'ctx>) -> ListStructFields<'ctx> {
|
||||
ListStructFields::new_typed(item, llvm_usize)
|
||||
}
|
||||
|
||||
/// See [`ListType::fields`].
|
||||
// TODO: Move this into e.g. StructProxyType
|
||||
#[must_use]
|
||||
pub fn get_fields(&self, _ctx: &impl AsContextRef<'ctx>) -> ListStructFields<'ctx> {
|
||||
Self::fields(self.item.unwrap_or(self.llvm_usize.into()), self.llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of a `List`.
|
||||
#[must_use]
|
||||
fn llvm_type(
|
||||
@ -181,10 +147,16 @@ impl<'ctx> ListType<'ctx> {
|
||||
Self::new_impl(ctx.ctx, llvm_elem_type, llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`ListType`] from a [`StructType`].
|
||||
#[must_use]
|
||||
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`ListType`] from a [`PointerType`].
|
||||
#[must_use]
|
||||
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
let ctx = ptr_ty.get_context();
|
||||
|
||||
@ -323,9 +295,27 @@ impl<'ctx> ListType<'ctx> {
|
||||
|
||||
/// Converts an existing value into a [`ListValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`ListValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
|
||||
@ -333,36 +323,64 @@ impl<'ctx> ListType<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for ListType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = ListValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
let ctx = ty.get_context();
|
||||
|
||||
let llvm_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!("Expected struct type for `list` type, got {llvm_ty}"));
|
||||
};
|
||||
|
||||
let fields = ListStructFields::new(ctx, llvm_usize);
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
fields,
|
||||
llvm_ty,
|
||||
"list",
|
||||
&[(fields.items.name(), &|ty| {
|
||||
if ty.is_pointer_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected T* for `list.items`, got {ty}"))
|
||||
}
|
||||
})],
|
||||
)
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyType<'ctx> for ListType<'ctx> {
|
||||
type StructFields = ListStructFields<'ctx>;
|
||||
|
||||
fn get_fields(&self) -> Self::StructFields {
|
||||
Self::fields(self.item.unwrap_or(self.llvm_usize.into()), self.llvm_usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<ListType<'ctx>> for PointerType<'ctx> {
|
||||
|
@ -17,8 +17,7 @@
|
||||
//! on the stack.
|
||||
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::BasicType,
|
||||
types::{BasicType, IntType},
|
||||
values::{IntValue, PointerValue},
|
||||
};
|
||||
|
||||
@ -26,38 +25,43 @@ use super::{
|
||||
values::{ArraySliceValue, ProxyValue},
|
||||
{CodeGenContext, CodeGenerator},
|
||||
};
|
||||
pub use exception::*;
|
||||
pub use list::*;
|
||||
pub use option::*;
|
||||
pub use range::*;
|
||||
pub use string::*;
|
||||
pub use tuple::*;
|
||||
|
||||
mod exception;
|
||||
mod list;
|
||||
pub mod ndarray;
|
||||
mod option;
|
||||
mod range;
|
||||
mod string;
|
||||
pub mod structure;
|
||||
mod tuple;
|
||||
pub mod utils;
|
||||
|
||||
/// A LLVM type that is used to represent a corresponding type in NAC3.
|
||||
pub trait ProxyType<'ctx>: Into<Self::Base> {
|
||||
/// The LLVM type of which values of this type possess. This is usually a
|
||||
/// [LLVM pointer type][PointerType] for any non-primitive types.
|
||||
/// The ABI type of which values of this type possess.
|
||||
type ABI: BasicType<'ctx>;
|
||||
|
||||
/// The LLVM type of which values of this type possess.
|
||||
type Base: BasicType<'ctx>;
|
||||
|
||||
/// The type of values represented by this type.
|
||||
type Value: ProxyValue<'ctx, Type = Self>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
/// Checks whether `llvm_ty` can be represented by this [`ProxyType`].
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String>;
|
||||
|
||||
/// Checks whether `llvm_ty` can be represented by this [`ProxyType`].
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String>;
|
||||
/// Checks whether the type represented by `ty` expresses the same type represented by this
|
||||
/// [`ProxyType`].
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String>;
|
||||
|
||||
/// Returns the type that should be used in `alloca` IR statements.
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx>;
|
||||
@ -122,4 +126,10 @@ pub trait ProxyType<'ctx>: Into<Self::Base> {
|
||||
|
||||
/// Returns the [base type][Self::Base] of this proxy.
|
||||
fn as_base_type(&self) -> Self::Base;
|
||||
|
||||
/// Returns this proxy as its ABI type, i.e. the expected type representation if a value of this
|
||||
/// [`ProxyType`] is being passed into or returned from a function.
|
||||
///
|
||||
/// See [`CodeGenContext::get_llvm_abi_type`].
|
||||
fn as_abi_type(&self) -> Self::ABI;
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
use inkwell::{
|
||||
AddressSpace,
|
||||
types::BasicTypeEnum,
|
||||
values::{BasicValueEnum, IntValue},
|
||||
AddressSpace,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
codegen::{
|
||||
irrt,
|
||||
CodeGenContext, CodeGenerator, irrt,
|
||||
stmt::gen_if_else_expr_callback,
|
||||
types::{ndarray::NDArrayType, ListType, ProxyType},
|
||||
types::{ListType, ProxyType, ndarray::NDArrayType},
|
||||
values::{
|
||||
ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue,
|
||||
TypedArrayLikeAdapter, TypedArrayLikeMutator,
|
||||
ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue, TypedArrayLikeAdapter,
|
||||
TypedArrayLikeMutator, ndarray::NDArrayValue,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
toplevel::helper::{arraylike_flatten_element_type, arraylike_get_ndims},
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
@ -151,7 +150,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
(list_ty, list),
|
||||
name,
|
||||
);
|
||||
Ok(Some(ndarray.as_base_value()))
|
||||
Ok(Some(ndarray.as_abi_value(ctx)))
|
||||
},
|
||||
|generator, ctx| {
|
||||
let ndarray = self.construct_numpy_array_from_list_copy_none_impl(
|
||||
@ -160,14 +159,14 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
(list_ty, list),
|
||||
name,
|
||||
);
|
||||
Ok(Some(ndarray.as_base_value()))
|
||||
Ok(Some(ndarray.as_abi_value(ctx)))
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.map(BasicValueEnum::into_pointer_value)
|
||||
.unwrap();
|
||||
|
||||
NDArrayType::new(ctx, dtype, ndims).map_value(ndarray, None)
|
||||
NDArrayType::new(ctx, dtype, ndims).map_pointer_value(ndarray, None)
|
||||
}
|
||||
|
||||
/// Implementation of `np_array(<ndarray>, copy=copy)`.
|
||||
@ -189,18 +188,18 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
|_generator, _ctx| Ok(copy),
|
||||
|generator, ctx| {
|
||||
let ndarray = ndarray.make_copy(generator, ctx); // Force copy
|
||||
Ok(Some(ndarray.as_base_value()))
|
||||
Ok(Some(ndarray.as_abi_value(ctx)))
|
||||
},
|
||||
|_generator, _ctx| {
|
||||
|_generator, ctx| {
|
||||
// No need to copy. Return `ndarray` itself.
|
||||
Ok(Some(ndarray.as_base_value()))
|
||||
Ok(Some(ndarray.as_abi_value(ctx)))
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
.map(BasicValueEnum::into_pointer_value)
|
||||
.unwrap();
|
||||
|
||||
ndarray.get_type().map_value(ndarray_val, name)
|
||||
ndarray.get_type().map_pointer_value(ndarray_val, name)
|
||||
}
|
||||
|
||||
/// Create a new ndarray like
|
||||
@ -222,7 +221,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
if *obj_id == ctx.primitives.list.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
let list = ListType::from_unifier_type(generator, ctx, object_ty)
|
||||
.map_value(object.into_pointer_value(), None);
|
||||
.map_pointer_value(object.into_pointer_value(), None);
|
||||
self.construct_numpy_array_list_impl(generator, ctx, (object_ty, list), copy, name)
|
||||
}
|
||||
|
||||
@ -230,7 +229,7 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, object_ty)
|
||||
.map_value(object.into_pointer_value(), None);
|
||||
.map_pointer_value(object.into_pointer_value(), None);
|
||||
self.construct_numpy_array_ndarray_impl(generator, ctx, ndarray, copy, name)
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
use inkwell::{
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core_derive::StructFields;
|
||||
|
||||
use crate::codegen::{
|
||||
types::{
|
||||
structure::{check_struct_type_matches_fields, StructField, StructFields},
|
||||
ProxyType,
|
||||
},
|
||||
values::{ndarray::ShapeEntryValue, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
types::{
|
||||
ProxyType,
|
||||
structure::{StructField, StructFields, StructProxyType, check_struct_type_matches_fields},
|
||||
},
|
||||
values::ndarray::ShapeEntryValue,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
@ -32,28 +32,6 @@ pub struct ShapeEntryStructFields<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ShapeEntryType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a [`ShapeEntryType`], returning [Err] if it does not.
|
||||
pub fn is_representable(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let ctx = llvm_ty.get_context();
|
||||
|
||||
let llvm_ndarray_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else {
|
||||
return Err(format!(
|
||||
"Expected struct type for `ShapeEntry` type, got {llvm_ndarray_ty}"
|
||||
));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
Self::fields(ctx, llvm_usize),
|
||||
llvm_ndarray_ty,
|
||||
"NDArray",
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
|
||||
#[must_use]
|
||||
fn fields(
|
||||
@ -63,13 +41,6 @@ impl<'ctx> ShapeEntryType<'ctx> {
|
||||
ShapeEntryStructFields::new(ctx, llvm_usize)
|
||||
}
|
||||
|
||||
/// See [`ShapeEntryStructFields::fields`].
|
||||
// TODO: Move this into e.g. StructProxyType
|
||||
#[must_use]
|
||||
pub fn get_fields(&self, ctx: impl AsContextRef<'ctx>) -> ShapeEntryStructFields<'ctx> {
|
||||
Self::fields(ctx, self.llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of a `ShapeEntry`.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
|
||||
@ -100,10 +71,16 @@ impl<'ctx> ShapeEntryType<'ctx> {
|
||||
Self::new_impl(ctx, generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates a [`ShapeEntryType`] from a [`StructType`] representing an `ShapeEntry`.
|
||||
#[must_use]
|
||||
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates a [`ShapeEntryType`] from a [`PointerType`] representing an `ShapeEntry`.
|
||||
#[must_use]
|
||||
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
Self { ty: ptr_ty, llvm_usize }
|
||||
}
|
||||
@ -139,9 +116,27 @@ impl<'ctx> ShapeEntryType<'ctx> {
|
||||
|
||||
/// Converts an existing value into a [`ShapeEntryValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`ShapeEntryValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
|
||||
@ -149,36 +144,58 @@ impl<'ctx> ShapeEntryType<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for ShapeEntryType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = ShapeEntryValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
let ctx = ty.get_context();
|
||||
|
||||
let llvm_ndarray_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else {
|
||||
return Err(format!(
|
||||
"Expected struct type for `ShapeEntry` type, got {llvm_ndarray_ty}"
|
||||
));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
Self::fields(ctx, llvm_usize),
|
||||
llvm_ndarray_ty,
|
||||
"NDArray",
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyType<'ctx> for ShapeEntryType<'ctx> {
|
||||
type StructFields = ShapeEntryStructFields<'ctx>;
|
||||
|
||||
fn get_fields(&self) -> Self::StructFields {
|
||||
Self::fields(self.ty.get_context(), self.llvm_usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<ShapeEntryType<'ctx>> for PointerType<'ctx> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
context::Context,
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
@ -10,14 +10,15 @@ use nac3core_derive::StructFields;
|
||||
|
||||
use crate::{
|
||||
codegen::{
|
||||
types::{
|
||||
structure::{
|
||||
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
|
||||
},
|
||||
ProxyType,
|
||||
},
|
||||
values::{ndarray::ContiguousNDArrayValue, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
types::{
|
||||
ProxyType,
|
||||
structure::{
|
||||
FieldIndexCounter, StructField, StructFields, StructProxyType,
|
||||
check_struct_type_matches_fields,
|
||||
},
|
||||
},
|
||||
values::ndarray::ContiguousNDArrayValue,
|
||||
},
|
||||
toplevel::numpy::unpack_ndarray_var_tys,
|
||||
typecheck::typedef::Type,
|
||||
@ -58,36 +59,6 @@ impl<'ctx> ContiguousNDArrayStructFields<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ContiguousNDArrayType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a `ndarray` type, returning [Err] if it does not.
|
||||
pub fn is_representable(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let ctx = llvm_ty.get_context();
|
||||
|
||||
let llvm_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!(
|
||||
"Expected struct type for `ContiguousNDArray` type, got {llvm_ty}"
|
||||
));
|
||||
};
|
||||
|
||||
let fields = ContiguousNDArrayStructFields::new(ctx, llvm_usize);
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
fields,
|
||||
llvm_ty,
|
||||
"ContiguousNDArray",
|
||||
&[(fields.data.name(), &|ty| {
|
||||
if ty.is_pointer_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected T* for `ContiguousNDArray.data`, got {ty}"))
|
||||
}
|
||||
})],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
|
||||
#[must_use]
|
||||
fn fields(
|
||||
@ -97,13 +68,6 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
||||
ContiguousNDArrayStructFields::new_typed(item, llvm_usize)
|
||||
}
|
||||
|
||||
/// See [`NDArrayType::fields`].
|
||||
// TODO: Move this into e.g. StructProxyType
|
||||
#[must_use]
|
||||
pub fn get_fields(&self) -> ContiguousNDArrayStructFields<'ctx> {
|
||||
Self::fields(self.item, self.llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of an `NDArray`.
|
||||
#[must_use]
|
||||
fn llvm_type(
|
||||
@ -153,14 +117,24 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
||||
Self::new_impl(ctx.ctx, llvm_dtype, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an [`ContiguousNDArrayType`] from a [`StructType`] representing an `NDArray`.
|
||||
#[must_use]
|
||||
pub fn from_struct_type(
|
||||
ty: StructType<'ctx>,
|
||||
item: BasicTypeEnum<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Self {
|
||||
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), item, llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`ContiguousNDArrayType`] from a [`PointerType`] representing an `NDArray`.
|
||||
#[must_use]
|
||||
pub fn from_type(
|
||||
pub fn from_pointer_type(
|
||||
ptr_ty: PointerType<'ctx>,
|
||||
item: BasicTypeEnum<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
Self { ty: ptr_ty, item, llvm_usize }
|
||||
}
|
||||
@ -204,9 +178,28 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
||||
|
||||
/// Converts an existing value into a [`ContiguousNDArrayValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
self.item,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`ContiguousNDArrayValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||
@ -219,36 +212,66 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for ContiguousNDArrayType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = ContiguousNDArrayValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
let ctx = ty.get_context();
|
||||
|
||||
let llvm_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!(
|
||||
"Expected struct type for `ContiguousNDArray` type, got {llvm_ty}"
|
||||
));
|
||||
};
|
||||
|
||||
let fields = ContiguousNDArrayStructFields::new(ctx, llvm_usize);
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
fields,
|
||||
llvm_ty,
|
||||
"ContiguousNDArray",
|
||||
&[(fields.data.name(), &|ty| {
|
||||
if ty.is_pointer_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected T* for `ContiguousNDArray.data`, got {ty}"))
|
||||
}
|
||||
})],
|
||||
)
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyType<'ctx> for ContiguousNDArrayType<'ctx> {
|
||||
type StructFields = ContiguousNDArrayStructFields<'ctx>;
|
||||
|
||||
fn get_fields(&self) -> Self::StructFields {
|
||||
Self::fields(self.item, self.llvm_usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<ContiguousNDArrayType<'ctx>> for PointerType<'ctx> {
|
||||
|
@ -1,12 +1,12 @@
|
||||
use inkwell::{
|
||||
values::{BasicValueEnum, IntValue},
|
||||
IntPredicate,
|
||||
values::{BasicValueEnum, IntValue},
|
||||
};
|
||||
|
||||
use super::NDArrayType;
|
||||
use crate::{
|
||||
codegen::{
|
||||
irrt, types::ProxyType, values::TypedArrayLikeAccessor, CodeGenContext, CodeGenerator,
|
||||
CodeGenContext, CodeGenerator, irrt, types::ProxyType, values::TypedArrayLikeAccessor,
|
||||
},
|
||||
typecheck::typedef::Type,
|
||||
};
|
||||
|
@ -1,23 +1,23 @@
|
||||
use inkwell::{
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core_derive::StructFields;
|
||||
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
types::{
|
||||
structure::{check_struct_type_matches_fields, StructField, StructFields},
|
||||
ProxyType,
|
||||
structure::{StructField, StructFields, StructProxyType, check_struct_type_matches_fields},
|
||||
},
|
||||
values::{
|
||||
ArrayLikeIndexer, ArraySliceValue,
|
||||
ndarray::{NDIndexValue, RustNDIndex},
|
||||
ArrayLikeIndexer, ArraySliceValue, ProxyValue,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
@ -35,25 +35,6 @@ pub struct NDIndexStructFields<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> NDIndexType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a `ndindex` type, returning [Err] if it does not.
|
||||
pub fn is_representable(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let ctx = llvm_ty.get_context();
|
||||
|
||||
let llvm_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!(
|
||||
"Expected struct type for `ContiguousNDArray` type, got {llvm_ty}"
|
||||
));
|
||||
};
|
||||
|
||||
let fields = NDIndexStructFields::new(ctx, llvm_usize);
|
||||
|
||||
check_struct_type_matches_fields(fields, llvm_ty, "NDIndex", &[])
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn fields(
|
||||
ctx: impl AsContextRef<'ctx>,
|
||||
@ -62,11 +43,6 @@ impl<'ctx> NDIndexType<'ctx> {
|
||||
NDIndexStructFields::new(ctx, llvm_usize)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_fields(&self) -> NDIndexStructFields<'ctx> {
|
||||
Self::fields(self.ty.get_context(), self.llvm_usize)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
|
||||
let field_tys =
|
||||
@ -95,8 +71,13 @@ impl<'ctx> NDIndexType<'ctx> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), llvm_usize)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
Self { ty: ptr_ty, llvm_usize }
|
||||
}
|
||||
@ -167,9 +148,26 @@ impl<'ctx> NDIndexType<'ctx> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
|
||||
@ -177,36 +175,55 @@ impl<'ctx> NDIndexType<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for NDIndexType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = NDIndexValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
let ctx = ty.get_context();
|
||||
|
||||
let llvm_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!(
|
||||
"Expected struct type for `ContiguousNDArray` type, got {llvm_ty}"
|
||||
));
|
||||
};
|
||||
|
||||
let fields = NDIndexStructFields::new(ctx, llvm_usize);
|
||||
|
||||
check_struct_type_matches_fields(fields, llvm_ty, "NDIndex", &[])
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyType<'ctx> for NDIndexType<'ctx> {
|
||||
type StructFields = NDIndexStructFields<'ctx>;
|
||||
|
||||
fn get_fields(&self) -> Self::StructFields {
|
||||
Self::fields(self.ty.get_context(), self.llvm_usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<NDIndexType<'ctx>> for PointerType<'ctx> {
|
||||
|
@ -2,16 +2,16 @@ use inkwell::{types::BasicTypeEnum, values::BasicValueEnum};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
stmt::gen_for_callback,
|
||||
types::{
|
||||
ndarray::{NDArrayType, NDIterType},
|
||||
ProxyType,
|
||||
ndarray::{NDArrayType, NDIterType},
|
||||
},
|
||||
values::{
|
||||
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
|
||||
ArrayLikeValue, ProxyValue,
|
||||
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
|
||||
impl<'ctx> NDArrayType<'ctx> {
|
||||
|
@ -1,20 +1,20 @@
|
||||
use inkwell::{
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{BasicValue, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{BasicValue, IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core_derive::StructFields;
|
||||
|
||||
use super::{
|
||||
structure::{check_struct_type_matches_fields, StructField, StructFields},
|
||||
ProxyType,
|
||||
structure::{StructField, StructFields, StructProxyType, check_struct_type_matches_fields},
|
||||
};
|
||||
use crate::{
|
||||
codegen::{
|
||||
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeMutator},
|
||||
values::{TypedArrayLikeMutator, ndarray::NDArrayValue},
|
||||
{CodeGenContext, CodeGenerator},
|
||||
},
|
||||
toplevel::{helper::extract_ndims, numpy::unpack_ndarray_var_tys},
|
||||
@ -62,26 +62,6 @@ pub struct NDArrayStructFields<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> NDArrayType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a `ndarray` type, returning [Err] if it does not.
|
||||
pub fn is_representable(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let ctx = llvm_ty.get_context();
|
||||
|
||||
let llvm_ndarray_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else {
|
||||
return Err(format!("Expected struct type for `NDArray` type, got {llvm_ndarray_ty}"));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
Self::fields(ctx, llvm_usize),
|
||||
llvm_ndarray_ty,
|
||||
"NDArray",
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
|
||||
#[must_use]
|
||||
fn fields(
|
||||
@ -91,13 +71,6 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
NDArrayStructFields::new(ctx, llvm_usize)
|
||||
}
|
||||
|
||||
/// See [`NDArrayType::fields`].
|
||||
// TODO: Move this into e.g. StructProxyType
|
||||
#[must_use]
|
||||
pub fn get_fields(&self, ctx: impl AsContextRef<'ctx>) -> NDArrayStructFields<'ctx> {
|
||||
Self::fields(ctx, self.llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of an `NDArray`.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
|
||||
@ -203,15 +176,26 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
Self::new_impl(ctx.ctx, llvm_dtype, ndims, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an [`NDArrayType`] from a [`StructType`] representing an `NDArray`.
|
||||
#[must_use]
|
||||
pub fn from_struct_type(
|
||||
ty: StructType<'ctx>,
|
||||
dtype: BasicTypeEnum<'ctx>,
|
||||
ndims: u64,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Self {
|
||||
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), dtype, ndims, llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`NDArrayType`] from a [`PointerType`] representing an `NDArray`.
|
||||
#[must_use]
|
||||
pub fn from_type(
|
||||
pub fn from_pointer_type(
|
||||
ptr_ty: PointerType<'ctx>,
|
||||
dtype: BasicTypeEnum<'ctx>,
|
||||
ndims: u64,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
NDArrayType { ty: ptr_ty, dtype, ndims, llvm_usize }
|
||||
}
|
||||
@ -431,9 +415,29 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
|
||||
/// Converts an existing value into a [`NDArrayValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
self.dtype,
|
||||
self.ndims,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`NDArrayValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||
@ -447,36 +451,56 @@ impl<'ctx> NDArrayType<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for NDArrayType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = NDArrayValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
let ctx = ty.get_context();
|
||||
|
||||
let llvm_ndarray_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ndarray_ty else {
|
||||
return Err(format!("Expected struct type for `NDArray` type, got {llvm_ndarray_ty}"));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
Self::fields(ctx, llvm_usize),
|
||||
llvm_ndarray_ty,
|
||||
"NDArray",
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyType<'ctx> for NDArrayType<'ctx> {
|
||||
type StructFields = NDArrayStructFields<'ctx>;
|
||||
|
||||
fn get_fields(&self) -> Self::StructFields {
|
||||
Self::fields(self.ty.get_context(), self.llvm_usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<NDArrayType<'ctx>> for PointerType<'ctx> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use inkwell::{
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
context::{AsContextRef, Context},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
@ -10,13 +10,14 @@ use nac3core_derive::StructFields;
|
||||
|
||||
use super::ProxyType;
|
||||
use crate::codegen::{
|
||||
irrt,
|
||||
types::structure::{check_struct_type_matches_fields, StructField, StructFields},
|
||||
values::{
|
||||
ndarray::{NDArrayValue, NDIterValue},
|
||||
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter,
|
||||
CodeGenContext, CodeGenerator, irrt,
|
||||
types::structure::{
|
||||
StructField, StructFields, StructProxyType, check_struct_type_matches_fields,
|
||||
},
|
||||
values::{
|
||||
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter,
|
||||
ndarray::{NDArrayValue, NDIterValue},
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
@ -44,39 +45,12 @@ pub struct NDIterStructFields<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> NDIterType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a `nditer` type, returning [Err] if it does not.
|
||||
pub fn is_representable(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let ctx = llvm_ty.get_context();
|
||||
|
||||
let llvm_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ty else {
|
||||
return Err(format!("Expected struct type for `NDIter` type, got {llvm_ty}"));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
Self::fields(ctx, llvm_usize),
|
||||
llvm_ndarray_ty,
|
||||
"NDIter",
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
|
||||
#[must_use]
|
||||
fn fields(ctx: impl AsContextRef<'ctx>, llvm_usize: IntType<'ctx>) -> NDIterStructFields<'ctx> {
|
||||
NDIterStructFields::new(ctx, llvm_usize)
|
||||
}
|
||||
|
||||
/// See [`NDIterType::fields`].
|
||||
// TODO: Move this into e.g. StructProxyType
|
||||
#[must_use]
|
||||
pub fn get_fields(&self, ctx: impl AsContextRef<'ctx>) -> NDIterStructFields<'ctx> {
|
||||
Self::fields(ctx, self.llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of an `NDIter`.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> PointerType<'ctx> {
|
||||
@ -107,10 +81,16 @@ impl<'ctx> NDIterType<'ctx> {
|
||||
Self::new_impl(ctx, generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates an [`NDIterType`] from a [`StructType`] representing an `NDIter`.
|
||||
#[must_use]
|
||||
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`NDIterType`] from a [`PointerType`] representing an `NDIter`.
|
||||
#[must_use]
|
||||
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
Self { ty: ptr_ty, llvm_usize }
|
||||
}
|
||||
@ -179,7 +159,8 @@ impl<'ctx> NDIterType<'ctx> {
|
||||
let indices =
|
||||
TypedArrayLikeAdapter::from(indices, |_, _, v| v.into_int_value(), |_, _, v| v.into());
|
||||
|
||||
let nditer = self.map_value(nditer, ndarray, indices.as_slice_value(ctx, generator), None);
|
||||
let nditer =
|
||||
self.map_pointer_value(nditer, ndarray, indices.as_slice_value(ctx, generator), None);
|
||||
|
||||
irrt::ndarray::call_nac3_nditer_initialize(generator, ctx, nditer, ndarray, &indices);
|
||||
|
||||
@ -187,9 +168,30 @@ impl<'ctx> NDIterType<'ctx> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: StructValue<'ctx>,
|
||||
parent: NDArrayValue<'ctx>,
|
||||
indices: ArraySliceValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
parent,
|
||||
indices,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
parent: NDArrayValue<'ctx>,
|
||||
indices: ArraySliceValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
@ -205,36 +207,56 @@ impl<'ctx> NDIterType<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for NDIterType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = NDIterValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
let ctx = ty.get_context();
|
||||
|
||||
let llvm_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ndarray_ty) = llvm_ty else {
|
||||
return Err(format!("Expected struct type for `NDIter` type, got {llvm_ty}"));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
Self::fields(ctx, llvm_usize),
|
||||
llvm_ndarray_ty,
|
||||
"NDIter",
|
||||
&[],
|
||||
)
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyType<'ctx> for NDIterType<'ctx> {
|
||||
type StructFields = NDIterStructFields<'ctx>;
|
||||
|
||||
fn get_fields(&self) -> Self::StructFields {
|
||||
Self::fields(self.ty.get_context(), self.llvm_usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<NDIterType<'ctx>> for PointerType<'ctx> {
|
||||
|
188
nac3core/src/codegen/types/option.rs
Normal file
188
nac3core/src/codegen/types/option.rs
Normal file
@ -0,0 +1,188 @@
|
||||
use inkwell::{
|
||||
AddressSpace,
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{BasicValue, BasicValueEnum, PointerValue},
|
||||
};
|
||||
|
||||
use super::ProxyType;
|
||||
use crate::{
|
||||
codegen::{CodeGenContext, CodeGenerator, values::OptionValue},
|
||||
typecheck::typedef::{Type, TypeEnum, iter_type_vars},
|
||||
};
|
||||
|
||||
/// Proxy type for an `Option` type in LLVM.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct OptionType<'ctx> {
|
||||
ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> OptionType<'ctx> {
|
||||
/// Creates an LLVM type corresponding to the expected structure of an `Option`.
|
||||
#[must_use]
|
||||
fn llvm_type(element_type: &impl BasicType<'ctx>) -> PointerType<'ctx> {
|
||||
element_type.ptr_type(AddressSpace::default())
|
||||
}
|
||||
|
||||
fn new_impl(element_type: &impl BasicType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
let llvm_option = Self::llvm_type(element_type);
|
||||
|
||||
Self { ty: llvm_option, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an instance of [`OptionType`].
|
||||
#[must_use]
|
||||
pub fn new(ctx: &CodeGenContext<'ctx, '_>, element_type: &impl BasicType<'ctx>) -> Self {
|
||||
Self::new_impl(element_type, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an instance of [`OptionType`].
|
||||
#[must_use]
|
||||
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
element_type: &impl BasicType<'ctx>,
|
||||
) -> Self {
|
||||
Self::new_impl(element_type, generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates an [`OptionType`] from a [unifier type][Type].
|
||||
#[must_use]
|
||||
pub fn from_unifier_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ty: Type,
|
||||
) -> Self {
|
||||
// Check unifier type and extract `element_type`
|
||||
let elem_type = match &*ctx.unifier.get_ty_immutable(ty) {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
iter_type_vars(params).next().unwrap().ty
|
||||
}
|
||||
|
||||
_ => panic!("Expected `option` type, but got {}", ctx.unifier.stringify(ty)),
|
||||
};
|
||||
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_elem_type = ctx.get_llvm_type(generator, elem_type);
|
||||
|
||||
Self::new_impl(&llvm_elem_type, llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`OptionType`] from a [`PointerType`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
Self { ty: ptr_ty, llvm_usize }
|
||||
}
|
||||
|
||||
/// Returns the element type of this `Option` type.
|
||||
#[must_use]
|
||||
pub fn element_type(&self) -> BasicTypeEnum<'ctx> {
|
||||
BasicTypeEnum::try_from(self.ty.get_element_type()).unwrap()
|
||||
}
|
||||
|
||||
/// Allocates an [`OptionValue`] on the stack.
|
||||
///
|
||||
/// The returned value will be `Some(v)` if [`value` contains a value][Option::is_some],
|
||||
/// otherwise `none` will be returned.
|
||||
#[must_use]
|
||||
pub fn construct<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: Option<BasicValueEnum<'ctx>>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
let ptr = if let Some(v) = value {
|
||||
let pvar = self.raw_alloca_var(generator, ctx, name);
|
||||
ctx.builder.build_store(pvar, v).unwrap();
|
||||
pvar
|
||||
} else {
|
||||
self.ty.const_null()
|
||||
};
|
||||
|
||||
self.map_pointer_value(ptr, name)
|
||||
}
|
||||
/// Allocates an [`OptionValue`] on the stack.
|
||||
///
|
||||
/// The returned value will always be `none`.
|
||||
#[must_use]
|
||||
pub fn construct_empty<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
self.construct(generator, ctx, None, name)
|
||||
}
|
||||
|
||||
/// Allocates an [`OptionValue`] on the stack.
|
||||
///
|
||||
/// The returned value will be set to `Some(value)`.
|
||||
#[must_use]
|
||||
pub fn construct_some_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: &impl BasicValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
self.construct(generator, ctx, Some(value.as_basic_value_enum()), name)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`OptionValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for OptionType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = OptionValue<'ctx>;
|
||||
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn has_same_repr(ty: Self::Base, _: IntType<'ctx>) -> Result<(), String> {
|
||||
BasicTypeEnum::try_from(ty.get_element_type())
|
||||
.map_err(|()| format!("Expected `ty` to be a BasicTypeEnum, got {ty}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.element_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<OptionType<'ctx>> for PointerType<'ctx> {
|
||||
fn from(value: OptionType<'ctx>) -> Self {
|
||||
value.as_base_type()
|
||||
}
|
||||
}
|
@ -1,25 +1,167 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
AddressSpace,
|
||||
context::Context,
|
||||
types::{AnyTypeEnum, ArrayType, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::{ArrayValue, PointerValue},
|
||||
};
|
||||
|
||||
use super::ProxyType;
|
||||
use crate::codegen::{
|
||||
values::{ProxyValue, RangeValue},
|
||||
{CodeGenContext, CodeGenerator},
|
||||
use crate::{
|
||||
codegen::{
|
||||
values::RangeValue,
|
||||
{CodeGenContext, CodeGenerator},
|
||||
},
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
};
|
||||
|
||||
/// Proxy type for a `range` type in LLVM.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct RangeType<'ctx> {
|
||||
ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
}
|
||||
|
||||
impl<'ctx> RangeType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a `range` type, returning [Err] if it does not.
|
||||
pub fn is_representable(llvm_ty: PointerType<'ctx>) -> Result<(), String> {
|
||||
let llvm_range_ty = llvm_ty.get_element_type();
|
||||
/// Creates an LLVM type corresponding to the expected structure of a `Range`.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context) -> PointerType<'ctx> {
|
||||
// typedef int32_t Range[3];
|
||||
let llvm_i32 = ctx.i32_type();
|
||||
llvm_i32.array_type(3).ptr_type(AddressSpace::default())
|
||||
}
|
||||
|
||||
fn new_impl(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> Self {
|
||||
let llvm_range = Self::llvm_type(ctx);
|
||||
|
||||
RangeType { ty: llvm_range, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an instance of [`RangeType`].
|
||||
#[must_use]
|
||||
pub fn new(ctx: &CodeGenContext<'ctx, '_>) -> Self {
|
||||
Self::new_impl(ctx.ctx, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an instance of [`RangeType`].
|
||||
#[must_use]
|
||||
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Self {
|
||||
Self::new_impl(ctx, generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates an [`RangeType`] from a [unifier type][Type].
|
||||
#[must_use]
|
||||
pub fn from_unifier_type(ctx: &mut CodeGenContext<'ctx, '_>, ty: Type) -> Self {
|
||||
// Check unifier type
|
||||
assert!(
|
||||
matches!(&*ctx.unifier.get_ty_immutable(ty), TypeEnum::TObj { obj_id, .. } if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap())
|
||||
);
|
||||
|
||||
Self::new(ctx)
|
||||
}
|
||||
|
||||
/// Creates an [`RangeType`] from a [`ArrayType`].
|
||||
#[must_use]
|
||||
pub fn from_array_type(arr_ty: ArrayType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
Self::from_pointer_type(arr_ty.ptr_type(AddressSpace::default()), llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`RangeType`] from a [`PointerType`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
|
||||
|
||||
RangeType { ty: ptr_ty, llvm_usize }
|
||||
}
|
||||
|
||||
/// Returns the type of all fields of this `range` type.
|
||||
#[must_use]
|
||||
pub fn value_type(&self) -> IntType<'ctx> {
|
||||
self.as_abi_type().get_element_type().into_array_type().get_element_type().into_int_type()
|
||||
}
|
||||
|
||||
/// Allocates an instance of [`RangeValue`] as if by calling `alloca` on the base type.
|
||||
///
|
||||
/// See [`ProxyType::raw_alloca`].
|
||||
#[must_use]
|
||||
pub fn alloca<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||
self.raw_alloca(ctx, name),
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Allocates an instance of [`RangeValue`] as if by calling `alloca` on the base type.
|
||||
///
|
||||
/// See [`ProxyType::raw_alloca_var`].
|
||||
#[must_use]
|
||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||
self.raw_alloca_var(generator, ctx, name),
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`RangeValue`].
|
||||
#[must_use]
|
||||
pub fn map_array_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: ArrayValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_array_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`RangeValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, self.llvm_usize, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = RangeValue<'ctx>;
|
||||
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn has_same_repr(ty: Self::Base, _: IntType<'ctx>) -> Result<(), String> {
|
||||
let llvm_range_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::ArrayType(llvm_range_ty) = llvm_range_ty else {
|
||||
return Err(format!("Expected array type for `range` type, got {llvm_range_ty}"));
|
||||
};
|
||||
@ -46,106 +188,17 @@ impl<'ctx> RangeType<'ctx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of a `Range`.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context) -> PointerType<'ctx> {
|
||||
// typedef int32_t Range[3];
|
||||
let llvm_i32 = ctx.i32_type();
|
||||
llvm_i32.array_type(3).ptr_type(AddressSpace::default())
|
||||
}
|
||||
|
||||
/// Creates an instance of [`RangeType`].
|
||||
#[must_use]
|
||||
pub fn new(ctx: &'ctx Context) -> Self {
|
||||
let llvm_range = Self::llvm_type(ctx);
|
||||
|
||||
RangeType::from_type(llvm_range)
|
||||
}
|
||||
|
||||
/// Creates an [`RangeType`] from a [`PointerType`].
|
||||
#[must_use]
|
||||
pub fn from_type(ptr_ty: PointerType<'ctx>) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty).is_ok());
|
||||
|
||||
RangeType { ty: ptr_ty }
|
||||
}
|
||||
|
||||
/// Returns the type of all fields of this `range` type.
|
||||
#[must_use]
|
||||
pub fn value_type(&self) -> IntType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_array_type().get_element_type().into_int_type()
|
||||
}
|
||||
|
||||
/// Allocates an instance of [`RangeValue`] as if by calling `alloca` on the base type.
|
||||
///
|
||||
/// See [`ProxyType::raw_alloca`].
|
||||
#[must_use]
|
||||
pub fn alloca<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(self.raw_alloca(ctx, name), name)
|
||||
}
|
||||
|
||||
/// Allocates an instance of [`RangeValue`] as if by calling `alloca` on the base type.
|
||||
///
|
||||
/// See [`ProxyType::raw_alloca_var`].
|
||||
#[must_use]
|
||||
pub fn alloca_var<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||
self.raw_alloca_var(generator, ctx, name),
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`RangeValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(value, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for RangeType<'ctx> {
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = RangeValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
_: &G,
|
||||
_: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty)
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<RangeType<'ctx>> for PointerType<'ctx> {
|
||||
|
177
nac3core/src/codegen/types/string.rs
Normal file
177
nac3core/src/codegen/types/string.rs
Normal file
@ -0,0 +1,177 @@
|
||||
use inkwell::{
|
||||
AddressSpace,
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{GlobalValue, IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core_derive::StructFields;
|
||||
|
||||
use super::{
|
||||
ProxyType,
|
||||
structure::{StructField, StructFields, check_struct_type_matches_fields},
|
||||
};
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator, values::StringValue};
|
||||
|
||||
/// Proxy type for a `str` type in LLVM.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct StringType<'ctx> {
|
||||
ty: StructType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
||||
pub struct StringStructFields<'ctx> {
|
||||
/// Pointer to the first character of the string.
|
||||
#[value_type(i8_type().ptr_type(AddressSpace::default()))]
|
||||
pub ptr: StructField<'ctx, PointerValue<'ctx>>,
|
||||
|
||||
/// Length of the string.
|
||||
#[value_type(usize)]
|
||||
pub len: StructField<'ctx, IntValue<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx> StringType<'ctx> {
|
||||
/// Returns an instance of [`StructFields`] containing all field accessors for this type.
|
||||
#[must_use]
|
||||
fn fields(llvm_usize: IntType<'ctx>) -> StringStructFields<'ctx> {
|
||||
StringStructFields::new(llvm_usize.get_context(), llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of a `str`.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> StructType<'ctx> {
|
||||
const NAME: &str = "str";
|
||||
|
||||
if let Some(t) = ctx.get_struct_type(NAME) {
|
||||
t
|
||||
} else {
|
||||
let str_ty = ctx.opaque_struct_type(NAME);
|
||||
let field_tys = Self::fields(llvm_usize).into_iter().map(|field| field.1).collect_vec();
|
||||
str_ty.set_body(&field_tys, false);
|
||||
str_ty
|
||||
}
|
||||
}
|
||||
|
||||
fn new_impl(ctx: &'ctx Context, llvm_usize: IntType<'ctx>) -> Self {
|
||||
let llvm_str = Self::llvm_type(ctx, llvm_usize);
|
||||
|
||||
Self { ty: llvm_str, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an instance of [`StringType`].
|
||||
#[must_use]
|
||||
pub fn new(ctx: &CodeGenContext<'ctx, '_>) -> Self {
|
||||
Self::new_impl(ctx.ctx, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an instance of [`StringType`].
|
||||
#[must_use]
|
||||
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
) -> Self {
|
||||
Self::new_impl(ctx, generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates an [`StringType`] from a [`StructType`] representing a `str`.
|
||||
#[must_use]
|
||||
pub fn from_struct_type(ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(ty, llvm_usize).is_ok());
|
||||
|
||||
Self { ty, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an [`StringType`] from a [`PointerType`] representing a `str`.
|
||||
#[must_use]
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
Self::from_struct_type(ptr_ty.get_element_type().into_struct_type(), llvm_usize)
|
||||
}
|
||||
|
||||
/// Returns the fields present in this [`StringType`].
|
||||
#[must_use]
|
||||
pub fn get_fields(&self) -> StringStructFields<'ctx> {
|
||||
Self::fields(self.llvm_usize)
|
||||
}
|
||||
|
||||
/// Constructs a global constant string.
|
||||
#[must_use]
|
||||
pub fn construct_constant(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
v: &str,
|
||||
name: Option<&'ctx str>,
|
||||
) -> StringValue<'ctx> {
|
||||
let str_ptr = ctx
|
||||
.builder
|
||||
.build_global_string_ptr(v, "const")
|
||||
.map(GlobalValue::as_pointer_value)
|
||||
.unwrap();
|
||||
let size = ctx.get_size_type().const_int(v.len() as u64, false);
|
||||
self.map_struct_value(
|
||||
self.as_abi_type().const_named_struct(&[str_ptr.into(), size.into()]),
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`StringValue`].
|
||||
#[must_use]
|
||||
pub fn map_struct_value(
|
||||
&self,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(value, self.llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`StringValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(ctx, value, self.llvm_usize, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for StringType<'ctx> {
|
||||
type ABI = StructType<'ctx>;
|
||||
type Base = StructType<'ctx>;
|
||||
type Value = StringValue<'ctx>;
|
||||
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::StructType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected structure type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
check_struct_type_matches_fields(Self::fields(llvm_usize), ty, "str", &[])
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_abi_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<StringType<'ctx>> for StructType<'ctx> {
|
||||
fn from(value: StringType<'ctx>) -> Self {
|
||||
value.as_base_type()
|
||||
}
|
||||
}
|
@ -1,14 +1,56 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use inkwell::{
|
||||
AddressSpace,
|
||||
context::AsContextRef,
|
||||
types::{BasicTypeEnum, IntType, StructType},
|
||||
values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
types::{BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{AggregateValueEnum, BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::ProxyType;
|
||||
use crate::codegen::CodeGenContext;
|
||||
|
||||
/// A LLVM type that is used to represent a corresponding structure-like type in NAC3.
|
||||
pub trait StructProxyType<'ctx>: ProxyType<'ctx, Base = PointerType<'ctx>> {
|
||||
/// The concrete type of [`StructFields`].
|
||||
type StructFields: StructFields<'ctx>;
|
||||
|
||||
/// Whether this [`StructProxyType`] has the same LLVM type representation as
|
||||
/// [`llvm_ty`][StructType].
|
||||
fn has_same_struct_repr(
|
||||
llvm_ty: StructType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
Self::has_same_pointer_repr(llvm_ty.ptr_type(AddressSpace::default()), llvm_usize)
|
||||
}
|
||||
|
||||
/// Whether this [`StructProxyType`] has the same LLVM type representation as
|
||||
/// [`llvm_ty`][PointerType].
|
||||
fn has_same_pointer_repr(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
Self::has_same_repr(llvm_ty, llvm_usize)
|
||||
}
|
||||
|
||||
/// Returns the fields present in this [`StructProxyType`].
|
||||
#[must_use]
|
||||
fn get_fields(&self) -> Self::StructFields;
|
||||
|
||||
/// Returns the [`StructType`].
|
||||
#[must_use]
|
||||
fn get_struct_type(&self) -> StructType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
/// Returns the [`PointerType`] representing this type.
|
||||
#[must_use]
|
||||
fn get_pointer_type(&self) -> PointerType<'ctx> {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait indicating that the structure is a field-wise representation of an LLVM structure.
|
||||
///
|
||||
/// # Usage
|
||||
@ -161,17 +203,38 @@ where
|
||||
|
||||
/// Gets the value of this field for a given `obj`.
|
||||
#[must_use]
|
||||
pub fn get_from_value(&self, obj: StructValue<'ctx>) -> Value {
|
||||
obj.get_field_at_index(self.index).and_then(|value| Value::try_from(value).ok()).unwrap()
|
||||
pub fn extract_value(&self, ctx: &CodeGenContext<'ctx, '_>, obj: StructValue<'ctx>) -> Value {
|
||||
Value::try_from(
|
||||
ctx.builder
|
||||
.build_extract_value(
|
||||
obj,
|
||||
self.index,
|
||||
&format!("{}.{}", obj.get_name().to_str().unwrap(), self.name),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Sets the value of this field for a given `obj`.
|
||||
pub fn set_for_value(&self, obj: StructValue<'ctx>, value: Value) {
|
||||
obj.set_field_at_index(self.index, value);
|
||||
#[must_use]
|
||||
pub fn insert_value(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
obj: StructValue<'ctx>,
|
||||
value: Value,
|
||||
) -> StructValue<'ctx> {
|
||||
let obj_name = obj.get_name().to_str().unwrap();
|
||||
let new_obj_name = if obj_name.chars().all(char::is_numeric) { "" } else { obj_name };
|
||||
|
||||
ctx.builder
|
||||
.build_insert_value(obj, value, self.index, new_obj_name)
|
||||
.map(AggregateValueEnum::into_struct_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Gets the value of this field for a pointer-to-structure.
|
||||
pub fn get(
|
||||
/// Loads the value of this field for a pointer-to-structure.
|
||||
pub fn load(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
pobj: PointerValue<'ctx>,
|
||||
@ -187,8 +250,8 @@ where
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Sets the value of this field for a pointer-to-structure.
|
||||
pub fn set(
|
||||
/// Stores the value of this field for a pointer-to-structure.
|
||||
pub fn store(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
pobj: PointerValue<'ctx>,
|
||||
|
@ -1,16 +1,13 @@
|
||||
use inkwell::{
|
||||
context::Context,
|
||||
types::{BasicType, BasicTypeEnum, IntType, StructType},
|
||||
values::BasicValueEnum,
|
||||
types::{BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{BasicValueEnum, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::ProxyType;
|
||||
use crate::{
|
||||
codegen::{
|
||||
values::{ProxyValue, TupleValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
codegen::{CodeGenContext, CodeGenerator, values::TupleValue},
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
};
|
||||
|
||||
@ -21,11 +18,6 @@ pub struct TupleType<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> TupleType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents any tuple type, returning [Err] if it does not.
|
||||
pub fn is_representable(_value: StructType<'ctx>) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of a tuple.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, tys: &[BasicTypeEnum<'ctx>]) -> StructType<'ctx> {
|
||||
@ -82,12 +74,18 @@ impl<'ctx> TupleType<'ctx> {
|
||||
|
||||
/// Creates an [`TupleType`] from a [`StructType`].
|
||||
#[must_use]
|
||||
pub fn from_type(struct_ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::is_representable(struct_ty).is_ok());
|
||||
pub fn from_struct_type(struct_ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
debug_assert!(Self::has_same_repr(struct_ty, llvm_usize).is_ok());
|
||||
|
||||
TupleType { ty: struct_ty, llvm_usize }
|
||||
}
|
||||
|
||||
/// Creates an [`TupleType`] from a [`PointerType`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
|
||||
Self::from_struct_type(ptr_ty.get_element_type().into_struct_type(), llvm_usize)
|
||||
}
|
||||
|
||||
/// Returns the number of elements present in this [`TupleType`].
|
||||
#[must_use]
|
||||
pub fn num_elements(&self) -> u32 {
|
||||
@ -112,17 +110,13 @@ impl<'ctx> TupleType<'ctx> {
|
||||
/// The caller must ensure that the index is valid.
|
||||
#[must_use]
|
||||
pub unsafe fn type_at_index_unchecked(&self, index: u32) -> BasicTypeEnum<'ctx> {
|
||||
self.ty.get_field_type_at_index_unchecked(index)
|
||||
unsafe { self.ty.get_field_type_at_index_unchecked(index) }
|
||||
}
|
||||
|
||||
/// Constructs a [`TupleValue`] from this type by zero-initializing the tuple value.
|
||||
#[must_use]
|
||||
pub fn construct(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
self.map_value(Self::llvm_type(ctx.ctx, &self.ty.get_field_types()).const_zero(), name)
|
||||
pub fn construct(&self, name: Option<&'ctx str>) -> <Self as ProxyType<'ctx>>::Value {
|
||||
self.map_struct_value(self.as_abi_type().const_zero(), name)
|
||||
}
|
||||
|
||||
/// Constructs a [`TupleValue`] from `objects`. The resulting tuple preserves the order of
|
||||
@ -137,14 +131,15 @@ impl<'ctx> TupleType<'ctx> {
|
||||
let values = objects.into_iter().collect_vec();
|
||||
|
||||
assert_eq!(values.len(), self.num_elements() as usize);
|
||||
assert!(values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, v)| { v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) } }));
|
||||
assert!(
|
||||
values.iter().enumerate().all(|(i, v)| {
|
||||
v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) }
|
||||
})
|
||||
);
|
||||
|
||||
let mut value = self.construct(ctx, name);
|
||||
let mut value = self.construct(name);
|
||||
for (i, val) in values.into_iter().enumerate() {
|
||||
value.store_element(ctx, i as u32, val);
|
||||
value.insert_element(ctx, i as u32, val);
|
||||
}
|
||||
|
||||
value
|
||||
@ -152,37 +147,44 @@ impl<'ctx> TupleType<'ctx> {
|
||||
|
||||
/// Converts an existing value into a [`ListValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
pub fn map_struct_value(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(value, self.llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`TupleValue`].
|
||||
#[must_use]
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(ctx, value, self.llvm_usize, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for TupleType<'ctx> {
|
||||
type ABI = StructType<'ctx>;
|
||||
type Base = StructType<'ctx>;
|
||||
type Value = TupleValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::StructType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected struct type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
_generator: &G,
|
||||
_ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty)
|
||||
fn has_same_repr(_: Self::Base, _: IntType<'ctx>) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
@ -192,6 +194,10 @@ impl<'ctx> ProxyType<'ctx> for TupleType<'ctx> {
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<TupleType<'ctx>> for StructType<'ctx> {
|
||||
|
@ -1,22 +1,23 @@
|
||||
use inkwell::{
|
||||
context::{AsContextRef, Context, ContextRef},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
|
||||
values::IntValue,
|
||||
AddressSpace,
|
||||
context::{AsContextRef, Context, ContextRef},
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3core_derive::StructFields;
|
||||
|
||||
use crate::codegen::{
|
||||
types::{
|
||||
structure::{
|
||||
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
|
||||
},
|
||||
ProxyType,
|
||||
},
|
||||
values::{utils::SliceValue, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
types::{
|
||||
ProxyType,
|
||||
structure::{
|
||||
FieldIndexCounter, StructField, StructFields, StructProxyType,
|
||||
check_struct_type_matches_fields,
|
||||
},
|
||||
},
|
||||
values::utils::SliceValue,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
@ -27,7 +28,7 @@ pub struct SliceType<'ctx> {
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
|
||||
pub struct SliceFields<'ctx> {
|
||||
pub struct SliceStructFields<'ctx> {
|
||||
#[value_type(bool_type())]
|
||||
pub start_defined: StructField<'ctx, IntValue<'ctx>>,
|
||||
#[value_type(usize)]
|
||||
@ -42,14 +43,14 @@ pub struct SliceFields<'ctx> {
|
||||
pub step: StructField<'ctx, IntValue<'ctx>>,
|
||||
}
|
||||
|
||||
impl<'ctx> SliceFields<'ctx> {
|
||||
/// Creates a new instance of [`SliceFields`] with a custom integer type for its range values.
|
||||
impl<'ctx> SliceStructFields<'ctx> {
|
||||
/// Creates a new instance of [`SliceStructFields`] with a custom integer type for its range values.
|
||||
#[must_use]
|
||||
pub fn new_sized(ctx: &impl AsContextRef<'ctx>, int_ty: IntType<'ctx>) -> Self {
|
||||
let ctx = unsafe { ContextRef::new(ctx.as_ctx_ref()) };
|
||||
let mut counter = FieldIndexCounter::default();
|
||||
|
||||
SliceFields {
|
||||
SliceStructFields {
|
||||
start_defined: StructField::create(&mut counter, "start_defined", ctx.bool_type()),
|
||||
start: StructField::create(&mut counter, "start", int_ty),
|
||||
stop_defined: StructField::create(&mut counter, "stop_defined", ctx.bool_type()),
|
||||
@ -61,60 +62,10 @@ impl<'ctx> SliceFields<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> SliceType<'ctx> {
|
||||
/// Checks whether `llvm_ty` represents a `slice` type, returning [Err] if it does not.
|
||||
pub fn is_representable(
|
||||
llvm_ty: PointerType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
let ctx = llvm_ty.get_context();
|
||||
|
||||
let fields = SliceFields::new(ctx, llvm_usize);
|
||||
|
||||
let llvm_ty = llvm_ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!("Expected struct type for `Slice` type, got {llvm_ty}"));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
fields,
|
||||
llvm_ty,
|
||||
"Slice",
|
||||
&[
|
||||
(fields.start.name(), &|ty| {
|
||||
if ty.is_int_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected int type for `Slice.start`, got {ty}"))
|
||||
}
|
||||
}),
|
||||
(fields.stop.name(), &|ty| {
|
||||
if ty.is_int_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected int type for `Slice.stop`, got {ty}"))
|
||||
}
|
||||
}),
|
||||
(fields.step.name(), &|ty| {
|
||||
if ty.is_int_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected int type for `Slice.step`, got {ty}"))
|
||||
}
|
||||
}),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Move this into e.g. StructProxyType
|
||||
#[must_use]
|
||||
pub fn get_fields(&self) -> SliceFields<'ctx> {
|
||||
SliceFields::new_sized(&self.int_ty.get_context(), self.int_ty)
|
||||
}
|
||||
|
||||
/// Creates an LLVM type corresponding to the expected structure of a `Slice`.
|
||||
#[must_use]
|
||||
fn llvm_type(ctx: &'ctx Context, int_ty: IntType<'ctx>) -> PointerType<'ctx> {
|
||||
let field_tys = SliceFields::new_sized(&int_ty.get_context(), int_ty)
|
||||
let field_tys = SliceStructFields::new_sized(&int_ty.get_context(), int_ty)
|
||||
.into_iter()
|
||||
.map(|field| field.1)
|
||||
.collect_vec();
|
||||
@ -134,6 +85,16 @@ impl<'ctx> SliceType<'ctx> {
|
||||
Self::new_impl(ctx.ctx, int_ty, ctx.get_size_type())
|
||||
}
|
||||
|
||||
/// Creates an instance of [`SliceType`] with `int_ty` as its backing integer type.
|
||||
#[must_use]
|
||||
pub fn new_with_generator<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
int_ty: IntType<'ctx>,
|
||||
) -> Self {
|
||||
Self::new_impl(ctx, int_ty, generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates an instance of [`SliceType`] with `usize` as its backing integer type.
|
||||
#[must_use]
|
||||
pub fn new_usize(ctx: &CodeGenContext<'ctx, '_>) -> Self {
|
||||
@ -149,14 +110,24 @@ impl<'ctx> SliceType<'ctx> {
|
||||
Self::new_impl(ctx, generator.get_size_type(ctx), generator.get_size_type(ctx))
|
||||
}
|
||||
|
||||
/// Creates an [`SliceType`] from a [`StructType`] representing a `slice`.
|
||||
#[must_use]
|
||||
pub fn from_struct_type(
|
||||
ty: StructType<'ctx>,
|
||||
int_ty: IntType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Self {
|
||||
Self::from_pointer_type(ty.ptr_type(AddressSpace::default()), int_ty, llvm_usize)
|
||||
}
|
||||
|
||||
/// Creates an [`SliceType`] from a [`PointerType`] representing a `slice`.
|
||||
#[must_use]
|
||||
pub fn from_type(
|
||||
pub fn from_pointer_type(
|
||||
ptr_ty: PointerType<'ctx>,
|
||||
int_ty: IntType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr_ty, int_ty).is_ok());
|
||||
debug_assert!(Self::has_same_repr(ptr_ty, int_ty).is_ok());
|
||||
|
||||
Self { ty: ptr_ty, int_ty, llvm_usize }
|
||||
}
|
||||
@ -201,11 +172,30 @@ impl<'ctx> SliceType<'ctx> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`SliceValue`].
|
||||
#[must_use]
|
||||
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
value: StructValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_struct_value(
|
||||
generator,
|
||||
ctx,
|
||||
value,
|
||||
self.int_ty,
|
||||
self.llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts an existing value into a [`ContiguousNDArrayValue`].
|
||||
#[must_use]
|
||||
pub fn map_value(
|
||||
pub fn map_pointer_value(
|
||||
&self,
|
||||
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
|
||||
value: PointerValue<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> <Self as ProxyType<'ctx>>::Value {
|
||||
<Self as ProxyType<'ctx>>::Value::from_pointer_value(
|
||||
@ -218,36 +208,80 @@ impl<'ctx> SliceType<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyType<'ctx> for SliceType<'ctx> {
|
||||
type ABI = PointerType<'ctx>;
|
||||
type Base = PointerType<'ctx>;
|
||||
type Value = SliceValue<'ctx>;
|
||||
|
||||
fn is_type<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
fn is_representable(
|
||||
llvm_ty: impl BasicType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
if let BasicTypeEnum::PointerType(ty) = llvm_ty.as_basic_type_enum() {
|
||||
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
|
||||
Self::has_same_repr(ty, llvm_usize)
|
||||
} else {
|
||||
Err(format!("Expected pointer type, got {llvm_ty:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
llvm_ty: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_representable(llvm_ty, generator.get_size_type(ctx))
|
||||
fn has_same_repr(ty: Self::Base, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
let ctx = ty.get_context();
|
||||
|
||||
let fields = SliceStructFields::new(ctx, llvm_usize);
|
||||
|
||||
let llvm_ty = ty.get_element_type();
|
||||
let AnyTypeEnum::StructType(llvm_ty) = llvm_ty else {
|
||||
return Err(format!("Expected struct type for `Slice` type, got {llvm_ty}"));
|
||||
};
|
||||
|
||||
check_struct_type_matches_fields(
|
||||
fields,
|
||||
llvm_ty,
|
||||
"Slice",
|
||||
&[
|
||||
(fields.start.name(), &|ty| {
|
||||
if ty.is_int_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected int type for `Slice.start`, got {ty}"))
|
||||
}
|
||||
}),
|
||||
(fields.stop.name(), &|ty| {
|
||||
if ty.is_int_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected int type for `Slice.stop`, got {ty}"))
|
||||
}
|
||||
}),
|
||||
(fields.step.name(), &|ty| {
|
||||
if ty.is_int_type() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected int type for `Slice.step`, got {ty}"))
|
||||
}
|
||||
}),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
fn alloca_type(&self) -> impl BasicType<'ctx> {
|
||||
self.as_base_type().get_element_type().into_struct_type()
|
||||
self.as_abi_type().get_element_type().into_struct_type()
|
||||
}
|
||||
|
||||
fn as_base_type(&self) -> Self::Base {
|
||||
self.ty
|
||||
}
|
||||
|
||||
fn as_abi_type(&self) -> Self::ABI {
|
||||
self.as_base_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyType<'ctx> for SliceType<'ctx> {
|
||||
type StructFields = SliceStructFields<'ctx>;
|
||||
|
||||
fn get_fields(&self) -> Self::StructFields {
|
||||
SliceStructFields::new_sized(&self.ty.get_context(), self.int_ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<SliceType<'ctx>> for PointerType<'ctx> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use inkwell::{
|
||||
IntPredicate,
|
||||
types::AnyTypeEnum,
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
IntPredicate,
|
||||
};
|
||||
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator};
|
||||
|
188
nac3core/src/codegen/values/exception.rs
Normal file
188
nac3core/src/codegen/values/exception.rs
Normal file
@ -0,0 +1,188 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use nac3parser::ast::Location;
|
||||
|
||||
use super::{ProxyValue, StringValue, structure::StructProxyValue};
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
types::{
|
||||
ExceptionType,
|
||||
structure::{StructField, StructProxyType},
|
||||
},
|
||||
};
|
||||
|
||||
/// Proxy type for accessing an `Exception` value in LLVM.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ExceptionValue<'ctx> {
|
||||
value: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
}
|
||||
|
||||
impl<'ctx> ExceptionValue<'ctx> {
|
||||
/// Creates an [`ExceptionValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: StructValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`ExceptionValue`] from a [`PointerValue`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_value(
|
||||
ptr: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
Self { value: ptr, llvm_usize, name }
|
||||
}
|
||||
|
||||
fn name_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().name
|
||||
}
|
||||
|
||||
/// Stores the ID of the exception name into this instance.
|
||||
pub fn store_name(&self, ctx: &CodeGenContext<'ctx, '_>, name: IntValue<'ctx>) {
|
||||
debug_assert_eq!(name.get_type(), ctx.ctx.i32_type());
|
||||
|
||||
self.name_field().store(ctx, self.value, name, self.name);
|
||||
}
|
||||
|
||||
fn file_field(&self) -> StructField<'ctx, StructValue<'ctx>> {
|
||||
self.get_type().get_fields().file
|
||||
}
|
||||
|
||||
/// Stores the file name of the exception source into this instance.
|
||||
pub fn store_file(&self, ctx: &CodeGenContext<'ctx, '_>, file: StructValue<'ctx>) {
|
||||
debug_assert!(StringValue::is_instance(file, self.llvm_usize).is_ok());
|
||||
|
||||
self.file_field().store(ctx, self.value, file, self.name);
|
||||
}
|
||||
|
||||
fn line_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().line
|
||||
}
|
||||
|
||||
fn col_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().col
|
||||
}
|
||||
|
||||
/// Stores the [location][Location] of the exception source into this instance.
|
||||
pub fn store_location<G: CodeGenerator + ?Sized>(
|
||||
&self,
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
location: Location,
|
||||
) {
|
||||
let llvm_i32 = ctx.ctx.i32_type();
|
||||
|
||||
let filename = ctx.gen_string(generator, location.file.0);
|
||||
self.store_file(ctx, filename);
|
||||
|
||||
self.line_field().store(
|
||||
ctx,
|
||||
self.value,
|
||||
llvm_i32.const_int(location.row as u64, false),
|
||||
self.name,
|
||||
);
|
||||
self.col_field().store(
|
||||
ctx,
|
||||
self.value,
|
||||
llvm_i32.const_int(location.column as u64, false),
|
||||
self.name,
|
||||
);
|
||||
}
|
||||
|
||||
fn func_field(&self) -> StructField<'ctx, StructValue<'ctx>> {
|
||||
self.get_type().get_fields().func
|
||||
}
|
||||
|
||||
/// Stores the function name of the exception source into this instance.
|
||||
pub fn store_func(&self, ctx: &CodeGenContext<'ctx, '_>, func: StructValue<'ctx>) {
|
||||
debug_assert!(StringValue::is_instance(func, self.llvm_usize).is_ok());
|
||||
|
||||
self.func_field().store(ctx, self.value, func, self.name);
|
||||
}
|
||||
|
||||
fn message_field(&self) -> StructField<'ctx, StructValue<'ctx>> {
|
||||
self.get_type().get_fields().message
|
||||
}
|
||||
|
||||
/// Stores the exception message into this instance.
|
||||
pub fn store_message(&self, ctx: &CodeGenContext<'ctx, '_>, message: StructValue<'ctx>) {
|
||||
debug_assert!(StringValue::is_instance(message, self.llvm_usize).is_ok());
|
||||
|
||||
self.message_field().store(ctx, self.value, message, self.name);
|
||||
}
|
||||
|
||||
fn param0_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().param0
|
||||
}
|
||||
|
||||
fn param1_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().param1
|
||||
}
|
||||
|
||||
fn param2_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().param2
|
||||
}
|
||||
|
||||
/// Stores the parameters of the exception into this instance.
|
||||
///
|
||||
/// If the parameter does not exist, pass `i64 0` in the parameter slot.
|
||||
pub fn store_params(&self, ctx: &CodeGenContext<'ctx, '_>, params: &[IntValue<'ctx>; 3]) {
|
||||
debug_assert!(params.iter().all(|p| p.get_type() == ctx.ctx.i64_type()));
|
||||
|
||||
[self.param0_field(), self.param1_field(), self.param2_field()]
|
||||
.into_iter()
|
||||
.zip_eq(params)
|
||||
.for_each(|(field, param)| {
|
||||
field.store(ctx, self.value, *param, self.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for ExceptionValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = ExceptionType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyValue<'ctx> for ExceptionValue<'ctx> {}
|
||||
|
||||
impl<'ctx> From<ExceptionValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: ExceptionValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
}
|
||||
}
|
@ -1,14 +1,18 @@
|
||||
use inkwell::{
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
AddressSpace, IntPredicate,
|
||||
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
|
||||
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
};
|
||||
|
||||
use super::{
|
||||
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
||||
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor,
|
||||
UntypedArrayLikeMutator, structure::StructProxyValue,
|
||||
};
|
||||
use crate::codegen::{
|
||||
types::{structure::StructField, ListType, ProxyType},
|
||||
types::{
|
||||
ListType, ProxyType,
|
||||
structure::{StructField, StructProxyType},
|
||||
},
|
||||
{CodeGenContext, CodeGenerator},
|
||||
};
|
||||
|
||||
@ -21,13 +25,24 @@ pub struct ListValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ListValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `list`, returning [Err] if `value` is not an
|
||||
/// instance.
|
||||
pub fn is_representable(
|
||||
value: PointerValue<'ctx>,
|
||||
/// Creates an [`ListValue`] from a [`PointerValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: StructValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
ListType::is_representable(value.get_type(), llvm_usize)
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`ListValue`] from a [`PointerValue`].
|
||||
@ -37,24 +52,18 @@ impl<'ctx> ListValue<'ctx> {
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
ListValue { value: ptr, llvm_usize, name }
|
||||
}
|
||||
|
||||
fn items_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields(&ctx.ctx).items
|
||||
}
|
||||
|
||||
/// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr`
|
||||
/// on the field.
|
||||
fn pptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.items_field(ctx).ptr_by_gep(ctx, self.value, self.name)
|
||||
fn items_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields().items
|
||||
}
|
||||
|
||||
/// Stores the array of data elements `data` into this instance.
|
||||
fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) {
|
||||
self.items_field(ctx).set(ctx, self.value, data, self.name);
|
||||
self.items_field().store(ctx, self.value, data, self.name);
|
||||
}
|
||||
|
||||
/// Convenience method for creating a new array storing data elements with the given element
|
||||
@ -92,15 +101,15 @@ impl<'ctx> ListValue<'ctx> {
|
||||
ListDataProxy(self)
|
||||
}
|
||||
|
||||
fn len_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields(&ctx.ctx).len
|
||||
fn len_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().len
|
||||
}
|
||||
|
||||
/// Stores the `size` of this `list` into this instance.
|
||||
pub fn store_size(&self, ctx: &CodeGenContext<'ctx, '_>, size: IntValue<'ctx>) {
|
||||
debug_assert_eq!(size.get_type(), ctx.get_size_type());
|
||||
|
||||
self.len_field(ctx).set(ctx, self.value, size, self.name);
|
||||
self.len_field().store(ctx, self.value, size, self.name);
|
||||
}
|
||||
|
||||
/// Returns the size of this `list` as a value.
|
||||
@ -109,7 +118,7 @@ impl<'ctx> ListValue<'ctx> {
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> IntValue<'ctx> {
|
||||
self.len_field(ctx).get(ctx, self.value, name)
|
||||
self.len_field().load(ctx, self.value, name)
|
||||
}
|
||||
|
||||
/// Returns an instance of [`ListValue`] with the `items` pointer cast to `i8*`.
|
||||
@ -119,7 +128,7 @@ impl<'ctx> ListValue<'ctx> {
|
||||
let llvm_list_i8 = <Self as ProxyValue>::Type::new(ctx, &llvm_i8);
|
||||
|
||||
Self::from_pointer_value(
|
||||
ctx.builder.build_pointer_cast(self.value, llvm_list_i8.as_base_type(), "").unwrap(),
|
||||
ctx.builder.build_pointer_cast(self.value, llvm_list_i8.as_abi_type(), "").unwrap(),
|
||||
self.llvm_usize,
|
||||
self.name,
|
||||
)
|
||||
@ -127,18 +136,25 @@ impl<'ctx> ListValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for ListValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = ListType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
ListType::from_type(self.as_base_value().get_type(), self.llvm_usize)
|
||||
ListType::from_pointer_type(self.as_base_value().get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyValue<'ctx> for ListValue<'ctx> {}
|
||||
|
||||
impl<'ctx> From<ListValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: ListValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
@ -163,12 +179,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for ListDataProxy<'ctx, '_> {
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
_: &G,
|
||||
) -> PointerValue<'ctx> {
|
||||
let var_name = self.0.name.map(|v| format!("{v}.data")).unwrap_or_default();
|
||||
|
||||
ctx.builder
|
||||
.build_load(self.0.pptr_to_data(ctx), var_name.as_str())
|
||||
.map(BasicValueEnum::into_pointer_value)
|
||||
.unwrap()
|
||||
self.0.items_field().load(ctx, self.0.value, self.0.name)
|
||||
}
|
||||
|
||||
fn size<G: CodeGenerator + ?Sized>(
|
||||
|
@ -1,44 +1,39 @@
|
||||
use inkwell::{context::Context, values::BasicValue};
|
||||
use inkwell::{types::IntType, values::BasicValue};
|
||||
|
||||
use super::types::ProxyType;
|
||||
use crate::codegen::CodeGenerator;
|
||||
use super::{CodeGenContext, types::ProxyType};
|
||||
pub use array::*;
|
||||
pub use exception::*;
|
||||
pub use list::*;
|
||||
pub use option::*;
|
||||
pub use range::*;
|
||||
pub use string::*;
|
||||
pub use tuple::*;
|
||||
|
||||
mod array;
|
||||
mod exception;
|
||||
mod list;
|
||||
pub mod ndarray;
|
||||
mod option;
|
||||
mod range;
|
||||
mod string;
|
||||
pub mod structure;
|
||||
mod tuple;
|
||||
pub mod utils;
|
||||
|
||||
/// A LLVM type that is used to represent a non-primitive value in NAC3.
|
||||
pub trait ProxyValue<'ctx>: Into<Self::Base> {
|
||||
/// The type of LLVM values represented by this instance. This is usually the
|
||||
/// [LLVM pointer type][PointerValue].
|
||||
/// The ABI type of LLVM values represented by this instance.
|
||||
type ABI: BasicValue<'ctx>;
|
||||
|
||||
/// The type of LLVM values represented by this instance.
|
||||
type Base: BasicValue<'ctx>;
|
||||
|
||||
/// The type of this value.
|
||||
type Type: ProxyType<'ctx, Value = Self>;
|
||||
|
||||
/// Checks whether `value` can be represented by this [`ProxyValue`].
|
||||
fn is_instance<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
value: impl BasicValue<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
Self::Type::is_type(generator, ctx, value.as_basic_value_enum().get_type())
|
||||
}
|
||||
|
||||
/// Checks whether `value` can be represented by this [`ProxyValue`].
|
||||
fn is_representable<G: CodeGenerator + ?Sized>(
|
||||
generator: &G,
|
||||
ctx: &'ctx Context,
|
||||
value: Self::Base,
|
||||
) -> Result<(), String> {
|
||||
Self::is_instance(generator, ctx, value.as_basic_value_enum())
|
||||
fn is_instance(value: impl BasicValue<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> {
|
||||
Self::Type::is_representable(value.as_basic_value_enum().get_type(), llvm_usize)
|
||||
}
|
||||
|
||||
/// Returns the [type][ProxyType] of this value.
|
||||
@ -46,4 +41,10 @@ pub trait ProxyValue<'ctx>: Into<Self::Base> {
|
||||
|
||||
/// Returns the [base value][Self::Base] of this proxy.
|
||||
fn as_base_value(&self) -> Self::Base;
|
||||
|
||||
/// Returns this proxy as its ABI value, i.e. the expected value representation if a value
|
||||
/// represented by this [`ProxyValue`] is being passed into or returned from a function.
|
||||
///
|
||||
/// See [`CodeGenContext::get_llvm_abi_type`].
|
||||
fn as_abi_value(&self, ctx: &CodeGenContext<'ctx, '_>) -> Self::ABI;
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{IntValue, PointerValue},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::codegen::{
|
||||
irrt,
|
||||
CodeGenContext, CodeGenerator, irrt,
|
||||
types::{
|
||||
ndarray::{NDArrayType, ShapeEntryType},
|
||||
structure::StructField,
|
||||
ProxyType,
|
||||
ndarray::{NDArrayType, ShapeEntryType},
|
||||
structure::{StructField, StructProxyType},
|
||||
},
|
||||
values::{
|
||||
ndarray::NDArrayValue, ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ProxyValue,
|
||||
TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator,
|
||||
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor,
|
||||
TypedArrayLikeAdapter, TypedArrayLikeMutator, ndarray::NDArrayValue,
|
||||
structure::StructProxyValue,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -26,13 +26,24 @@ pub struct ShapeEntryValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ShapeEntryValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `ShapeEntry`, returning [Err] if `value` is
|
||||
/// not an instance.
|
||||
pub fn is_representable(
|
||||
value: PointerValue<'ctx>,
|
||||
/// Creates an [`ShapeEntryValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: StructValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`ShapeEntryValue`] from a [`PointerValue`].
|
||||
@ -42,43 +53,50 @@ impl<'ctx> ShapeEntryValue<'ctx> {
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
Self { value: ptr, llvm_usize, name }
|
||||
}
|
||||
|
||||
fn ndims_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields(self.value.get_type().get_context()).ndims
|
||||
self.get_type().get_fields().ndims
|
||||
}
|
||||
|
||||
/// Stores the number of dimensions into this value.
|
||||
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
|
||||
self.ndims_field().set(ctx, self.value, value, self.name);
|
||||
self.ndims_field().store(ctx, self.value, value, self.name);
|
||||
}
|
||||
|
||||
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields(self.value.get_type().get_context()).shape
|
||||
self.get_type().get_fields().shape
|
||||
}
|
||||
|
||||
/// Stores the shape into this value.
|
||||
pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
|
||||
self.shape_field().set(ctx, self.value, value, self.name);
|
||||
self.shape_field().store(ctx, self.value, value, self.name);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for ShapeEntryValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = ShapeEntryType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
Self::Type::from_type(self.value.get_type(), self.llvm_usize)
|
||||
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyValue<'ctx> for ShapeEntryValue<'ctx> {}
|
||||
|
||||
impl<'ctx> From<ShapeEntryValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: ShapeEntryValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
@ -149,9 +167,11 @@ fn broadcast_shapes<'ctx, G, Shape>(
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let llvm_shape_ty = ShapeEntryType::new(ctx);
|
||||
|
||||
assert!(in_shape_entries
|
||||
.iter()
|
||||
.all(|entry| entry.0.element_type(ctx, generator) == llvm_usize.into()));
|
||||
assert!(
|
||||
in_shape_entries
|
||||
.iter()
|
||||
.all(|entry| entry.0.element_type(ctx, generator) == llvm_usize.into())
|
||||
);
|
||||
assert_eq!(broadcast_shape.element_type(ctx, generator), llvm_usize.into());
|
||||
|
||||
// Prepare input shape entries to be passed to `call_nac3_ndarray_broadcast_shapes`.
|
||||
@ -167,7 +187,7 @@ fn broadcast_shapes<'ctx, G, Shape>(
|
||||
None,
|
||||
)
|
||||
};
|
||||
let shape_entry = llvm_shape_ty.map_value(pshape_entry, None);
|
||||
let shape_entry = llvm_shape_ty.map_pointer_value(pshape_entry, None);
|
||||
|
||||
let in_ndims = llvm_usize.const_int(*in_ndims, false);
|
||||
shape_entry.store_ndims(ctx, in_ndims);
|
||||
|
@ -1,17 +1,18 @@
|
||||
use inkwell::{
|
||||
types::{BasicType, BasicTypeEnum, IntType},
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
types::{BasicType, BasicTypeEnum, IntType},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
|
||||
use super::{ArrayLikeValue, NDArrayValue, ProxyValue};
|
||||
use super::NDArrayValue;
|
||||
use crate::codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
stmt::gen_if_callback,
|
||||
types::{
|
||||
ndarray::{ContiguousNDArrayType, NDArrayType},
|
||||
structure::StructField,
|
||||
structure::{StructField, StructProxyType},
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
values::{ArrayLikeValue, ProxyValue, structure::StructProxyValue},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -23,13 +24,25 @@ pub struct ContiguousNDArrayValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ContiguousNDArrayValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `ContiguousNDArray`, returning [Err] if `value` is
|
||||
/// not an instance.
|
||||
pub fn is_representable(
|
||||
value: PointerValue<'ctx>,
|
||||
/// Creates an [`ContiguousNDArrayValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: StructValue<'ctx>,
|
||||
dtype: BasicTypeEnum<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, dtype, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`ContiguousNDArrayValue`] from a [`PointerValue`].
|
||||
@ -40,7 +53,7 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
Self { value: ptr, item: dtype, llvm_usize, name }
|
||||
}
|
||||
@ -50,7 +63,7 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
|
||||
self.ndims_field().set(ctx, self.as_base_value(), value, self.name);
|
||||
self.ndims_field().store(ctx, self.as_abi_value(ctx), value, self.name);
|
||||
}
|
||||
|
||||
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
@ -58,11 +71,11 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
|
||||
self.shape_field().set(ctx, self.as_base_value(), value, self.name);
|
||||
self.shape_field().store(ctx, self.as_abi_value(ctx), value, self.name);
|
||||
}
|
||||
|
||||
pub fn load_shape(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.shape_field().get(ctx, self.value, self.name)
|
||||
self.shape_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
@ -70,20 +83,21 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
|
||||
self.data_field().set(ctx, self.as_base_value(), value, self.name);
|
||||
self.data_field().store(ctx, self.as_abi_value(ctx), value, self.name);
|
||||
}
|
||||
|
||||
pub fn load_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.data_field().get(ctx, self.value, self.name)
|
||||
self.data_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for ContiguousNDArrayValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = ContiguousNDArrayType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
<Self as ProxyValue<'ctx>>::Type::from_type(
|
||||
<Self as ProxyValue<'ctx>>::Type::from_pointer_type(
|
||||
self.as_base_value().get_type(),
|
||||
self.item,
|
||||
self.llvm_usize,
|
||||
@ -93,8 +107,14 @@ impl<'ctx> ProxyValue<'ctx> for ContiguousNDArrayValue<'ctx> {
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyValue<'ctx> for ContiguousNDArrayValue<'ctx> {}
|
||||
|
||||
impl<'ctx> From<ContiguousNDArrayValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: ContiguousNDArrayValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
@ -133,7 +153,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
|_, ctx| Ok(self.is_c_contiguous(ctx)),
|
||||
|_, ctx| {
|
||||
// This ndarray is contiguous.
|
||||
let data = self.data_field(ctx).get(ctx, self.as_base_value(), self.name);
|
||||
let data = self.data_field().load(ctx, self.as_abi_value(ctx), self.name);
|
||||
let data = ctx
|
||||
.builder
|
||||
.build_pointer_cast(data, result.item.ptr_type(AddressSpace::default()), "")
|
||||
|
@ -2,9 +2,9 @@ use inkwell::values::{BasicValue, BasicValueEnum};
|
||||
|
||||
use super::{NDArrayValue, NDIterValue, ScalarOrNDArray};
|
||||
use crate::codegen::{
|
||||
stmt::{gen_for_callback, BreakContinueHooks},
|
||||
types::ndarray::NDIterType,
|
||||
CodeGenContext, CodeGenerator,
|
||||
stmt::{BreakContinueHooks, gen_for_callback},
|
||||
types::ndarray::NDIterType,
|
||||
};
|
||||
|
||||
impl<'ctx> NDArrayValue<'ctx> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
types::IntType,
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
@ -9,14 +9,15 @@ use nac3parser::ast::{Expr, ExprKind};
|
||||
|
||||
use crate::{
|
||||
codegen::{
|
||||
irrt,
|
||||
CodeGenContext, CodeGenerator, irrt,
|
||||
types::{
|
||||
ndarray::{NDArrayType, NDIndexType},
|
||||
structure::StructField,
|
||||
structure::{StructField, StructProxyType},
|
||||
utils::SliceType,
|
||||
},
|
||||
values::{ndarray::NDArrayValue, utils::RustSlice, ProxyValue},
|
||||
CodeGenContext, CodeGenerator,
|
||||
values::{
|
||||
ProxyValue, ndarray::NDArrayValue, structure::StructProxyValue, utils::RustSlice,
|
||||
},
|
||||
},
|
||||
typecheck::typedef::Type,
|
||||
};
|
||||
@ -30,13 +31,24 @@ pub struct NDIndexValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> NDIndexValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `ndindex`, returning [Err] if `value` is not an
|
||||
/// instance.
|
||||
pub fn is_representable(
|
||||
value: PointerValue<'ctx>,
|
||||
/// Creates an [`NDIndexValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: StructValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`NDIndexValue`] from a [`PointerValue`].
|
||||
@ -46,7 +58,7 @@ impl<'ctx> NDIndexValue<'ctx> {
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
Self { value: ptr, llvm_usize, name }
|
||||
}
|
||||
@ -56,11 +68,11 @@ impl<'ctx> NDIndexValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn load_type(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.type_field().get(ctx, self.value, self.name)
|
||||
self.type_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
pub fn store_type(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
|
||||
self.type_field().set(ctx, self.value, value, self.name);
|
||||
self.type_field().store(ctx, self.value, value, self.name);
|
||||
}
|
||||
|
||||
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
@ -68,27 +80,34 @@ impl<'ctx> NDIndexValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn load_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.data_field().get(ctx, self.value, self.name)
|
||||
self.data_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
|
||||
self.data_field().set(ctx, self.value, value, self.name);
|
||||
self.data_field().store(ctx, self.value, value, self.name);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for NDIndexValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = NDIndexType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
Self::Type::from_type(self.value.get_type(), self.llvm_usize)
|
||||
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyValue<'ctx> for NDIndexValue<'ctx> {}
|
||||
|
||||
impl<'ctx> From<NDIndexValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: NDIndexValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
|
@ -1,11 +1,11 @@
|
||||
use inkwell::{types::BasicTypeEnum, values::BasicValueEnum};
|
||||
|
||||
use crate::codegen::{
|
||||
values::{
|
||||
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
|
||||
ProxyValue,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
values::{
|
||||
ProxyValue,
|
||||
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
|
||||
},
|
||||
};
|
||||
|
||||
impl<'ctx> NDArrayValue<'ctx> {
|
||||
|
@ -5,6 +5,7 @@ use nac3parser::ast::Operator;
|
||||
use super::{NDArrayOut, NDArrayValue, RustNDIndex};
|
||||
use crate::{
|
||||
codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
expr::gen_binop_expr_with_values,
|
||||
irrt,
|
||||
stmt::gen_for_callback_incrementing,
|
||||
@ -13,7 +14,6 @@ use crate::{
|
||||
ArrayLikeValue, ArraySliceValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
|
||||
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
toplevel::helper::arraylike_flatten_element_type,
|
||||
typecheck::{magic_methods::Binop, typedef::Type},
|
||||
@ -213,9 +213,7 @@ fn matmul_at_least_2d<'ctx, G: CodeGenerator>(
|
||||
Binop::normal(Operator::Mult),
|
||||
(&Some(rhs_dtype), b_kj),
|
||||
ctx.current_loc,
|
||||
)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, dst_dtype)?;
|
||||
)?;
|
||||
|
||||
// dst_[...]ij += x
|
||||
let dst_ij = ctx.builder.build_load(pdst_ij, "").unwrap();
|
||||
@ -226,9 +224,7 @@ fn matmul_at_least_2d<'ctx, G: CodeGenerator>(
|
||||
Binop::normal(Operator::Add),
|
||||
(&Some(dst_dtype), x),
|
||||
ctx.current_loc,
|
||||
)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, dst_dtype)?;
|
||||
)?;
|
||||
ctx.builder.build_store(pdst_ij, dst_ij).unwrap();
|
||||
|
||||
Ok(())
|
||||
|
@ -1,25 +1,28 @@
|
||||
use std::iter::repeat_n;
|
||||
|
||||
use inkwell::{
|
||||
types::{AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
|
||||
values::{BasicValue, BasicValueEnum, IntValue, PointerValue},
|
||||
AddressSpace, IntPredicate,
|
||||
types::{AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
|
||||
values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::{
|
||||
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TupleValue, TypedArrayLikeAccessor,
|
||||
TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor,
|
||||
UntypedArrayLikeMutator,
|
||||
UntypedArrayLikeMutator, structure::StructProxyValue,
|
||||
};
|
||||
use crate::{
|
||||
codegen::{
|
||||
irrt,
|
||||
CodeGenContext, CodeGenerator, irrt,
|
||||
llvm_intrinsics::{call_int_umin, call_memcpy_generic_array},
|
||||
stmt::gen_for_callback_incrementing,
|
||||
type_aligned_alloca,
|
||||
types::{ndarray::NDArrayType, structure::StructField, TupleType},
|
||||
CodeGenContext, CodeGenerator,
|
||||
types::{
|
||||
TupleType,
|
||||
ndarray::NDArrayType,
|
||||
structure::{StructField, StructProxyType},
|
||||
},
|
||||
},
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
};
|
||||
@ -49,13 +52,26 @@ pub struct NDArrayValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> NDArrayValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `NDArray`, returning [Err] if `value` is not an
|
||||
/// instance.
|
||||
pub fn is_representable(
|
||||
value: PointerValue<'ctx>,
|
||||
/// Creates an [`NDArrayValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: StructValue<'ctx>,
|
||||
dtype: BasicTypeEnum<'ctx>,
|
||||
ndims: u64,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
NDArrayType::is_representable(value.get_type(), llvm_usize)
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, dtype, ndims, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`NDArrayValue`] from a [`PointerValue`].
|
||||
@ -67,57 +83,50 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
NDArrayValue { value: ptr, dtype, ndims, llvm_usize, name }
|
||||
}
|
||||
|
||||
fn ndims_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields(ctx.ctx).ndims
|
||||
}
|
||||
|
||||
/// Returns the pointer to the field storing the number of dimensions of this `NDArray`.
|
||||
fn ptr_to_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.ndims_field(ctx).ptr_by_gep(ctx, self.value, self.name)
|
||||
fn ndims_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().ndims
|
||||
}
|
||||
|
||||
/// Stores the number of dimensions `ndims` into this instance.
|
||||
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, ndims: IntValue<'ctx>) {
|
||||
debug_assert_eq!(ndims.get_type(), ctx.get_size_type());
|
||||
|
||||
let pndims = self.ptr_to_ndims(ctx);
|
||||
ctx.builder.build_store(pndims, ndims).unwrap();
|
||||
self.ndims_field().store(ctx, self.value, ndims, self.name);
|
||||
}
|
||||
|
||||
/// Returns the number of dimensions of this `NDArray` as a value.
|
||||
pub fn load_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
let pndims = self.ptr_to_ndims(ctx);
|
||||
ctx.builder.build_load(pndims, "").map(BasicValueEnum::into_int_value).unwrap()
|
||||
self.ndims_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
fn itemsize_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields(ctx.ctx).itemsize
|
||||
fn itemsize_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().itemsize
|
||||
}
|
||||
|
||||
/// Stores the size of each element `itemsize` into this instance.
|
||||
pub fn store_itemsize(&self, ctx: &CodeGenContext<'ctx, '_>, itemsize: IntValue<'ctx>) {
|
||||
debug_assert_eq!(itemsize.get_type(), ctx.get_size_type());
|
||||
|
||||
self.itemsize_field(ctx).set(ctx, self.value, itemsize, self.name);
|
||||
self.itemsize_field().store(ctx, self.value, itemsize, self.name);
|
||||
}
|
||||
|
||||
/// Returns the size of each element of this `NDArray` as a value.
|
||||
pub fn load_itemsize(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.itemsize_field(ctx).get(ctx, self.value, self.name)
|
||||
self.itemsize_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
fn shape_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields(ctx.ctx).shape
|
||||
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields().shape
|
||||
}
|
||||
|
||||
/// Stores the array of dimension sizes `dims` into this instance.
|
||||
fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, dims: PointerValue<'ctx>) {
|
||||
self.shape_field(ctx).set(ctx, self.as_base_value(), dims, self.name);
|
||||
self.shape_field().store(ctx, self.value, dims, self.name);
|
||||
}
|
||||
|
||||
/// Convenience method for creating a new array storing dimension sizes with the given `size`.
|
||||
@ -136,16 +145,13 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
NDArrayShapeProxy(self)
|
||||
}
|
||||
|
||||
fn strides_field(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields(ctx.ctx).strides
|
||||
fn strides_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields().strides
|
||||
}
|
||||
|
||||
/// Stores the array of stride sizes `strides` into this instance.
|
||||
fn store_strides(&self, ctx: &CodeGenContext<'ctx, '_>, strides: PointerValue<'ctx>) {
|
||||
self.strides_field(ctx).set(ctx, self.as_base_value(), strides, self.name);
|
||||
self.strides_field().store(ctx, self.value, strides, self.name);
|
||||
}
|
||||
|
||||
/// Convenience method for creating a new array storing the stride with the given `size`.
|
||||
@ -164,14 +170,14 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
NDArrayStridesProxy(self)
|
||||
}
|
||||
|
||||
fn data_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields(ctx.ctx).data
|
||||
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields().data
|
||||
}
|
||||
|
||||
/// Returns the double-indirection pointer to the `data` array, as if by calling `getelementptr`
|
||||
/// on the field.
|
||||
pub fn ptr_to_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.data_field(ctx).ptr_by_gep(ctx, self.value, self.name)
|
||||
self.data_field().ptr_by_gep(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
/// Stores the array of data elements `data` into this instance.
|
||||
@ -180,7 +186,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
.builder
|
||||
.build_bit_cast(data, ctx.ctx.i8_type().ptr_type(AddressSpace::default()), "")
|
||||
.unwrap();
|
||||
self.data_field(ctx).set(ctx, self.as_base_value(), data.into_pointer_value(), self.name);
|
||||
self.data_field().store(ctx, self.value, data.into_pointer_value(), self.name);
|
||||
}
|
||||
|
||||
/// Convenience method for creating a new array storing data elements with the given element
|
||||
@ -219,13 +225,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
) {
|
||||
let num_items = self.load_ndims(ctx);
|
||||
|
||||
call_memcpy_generic_array(
|
||||
ctx,
|
||||
self.shape().base_ptr(ctx, generator),
|
||||
shape,
|
||||
num_items,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
);
|
||||
call_memcpy_generic_array(ctx, self.shape().base_ptr(ctx, generator), shape, num_items);
|
||||
}
|
||||
|
||||
/// Copy shape dimensions from an ndarray.
|
||||
@ -251,13 +251,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
) {
|
||||
let num_items = self.load_ndims(ctx);
|
||||
|
||||
call_memcpy_generic_array(
|
||||
ctx,
|
||||
self.strides().base_ptr(ctx, generator),
|
||||
strides,
|
||||
num_items,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
);
|
||||
call_memcpy_generic_array(ctx, self.strides().base_ptr(ctx, generator), strides, num_items);
|
||||
}
|
||||
|
||||
/// Copy strides dimensions from an ndarray.
|
||||
@ -471,11 +465,12 @@ impl<'ctx> NDArrayValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = NDArrayType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
NDArrayType::from_type(
|
||||
NDArrayType::from_pointer_type(
|
||||
self.as_base_value().get_type(),
|
||||
self.dtype,
|
||||
self.ndims,
|
||||
@ -486,8 +481,14 @@ impl<'ctx> ProxyValue<'ctx> for NDArrayValue<'ctx> {
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyValue<'ctx> for NDArrayValue<'ctx> {}
|
||||
|
||||
impl<'ctx> From<NDArrayValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: NDArrayValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
@ -512,7 +513,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayShapeProxy<'ctx, '_> {
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
_: &G,
|
||||
) -> PointerValue<'ctx> {
|
||||
self.0.shape_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
|
||||
self.0.shape_field().load(ctx, self.0.value, self.0.name)
|
||||
}
|
||||
|
||||
fn size<G: CodeGenerator + ?Sized>(
|
||||
@ -610,7 +611,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayStridesProxy<'ctx, '_> {
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
_: &G,
|
||||
) -> PointerValue<'ctx> {
|
||||
self.0.strides_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
|
||||
self.0.strides_field().load(ctx, self.0.value, self.0.name)
|
||||
}
|
||||
|
||||
fn size<G: CodeGenerator + ?Sized>(
|
||||
@ -708,7 +709,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayDataProxy<'ctx, '_> {
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
_: &G,
|
||||
) -> PointerValue<'ctx> {
|
||||
self.0.data_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
|
||||
self.0.data_field().load(ctx, self.0.value, self.0.name)
|
||||
}
|
||||
|
||||
fn size<G: CodeGenerator + ?Sized>(
|
||||
@ -962,7 +963,7 @@ impl<'ctx> ScalarOrNDArray<'ctx> {
|
||||
if *obj_id == ctx.primitives.ndarray.obj_id(&ctx.unifier).unwrap() =>
|
||||
{
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, object_ty)
|
||||
.map_value(object.into_pointer_value(), None);
|
||||
.map_pointer_value(object.into_pointer_value(), None);
|
||||
ScalarOrNDArray::NDArray(ndarray)
|
||||
}
|
||||
|
||||
@ -975,7 +976,7 @@ impl<'ctx> ScalarOrNDArray<'ctx> {
|
||||
pub fn to_basic_value_enum(self) -> BasicValueEnum<'ctx> {
|
||||
match self {
|
||||
ScalarOrNDArray::Scalar(scalar) => scalar,
|
||||
ScalarOrNDArray::NDArray(ndarray) => ndarray.as_base_value().into(),
|
||||
ScalarOrNDArray::NDArray(ndarray) => ndarray.value.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,18 @@
|
||||
use inkwell::{
|
||||
types::{BasicType, IntType},
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
AddressSpace,
|
||||
types::{BasicType, IntType},
|
||||
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
};
|
||||
|
||||
use super::{NDArrayValue, ProxyValue};
|
||||
use super::NDArrayValue;
|
||||
use crate::codegen::{
|
||||
irrt,
|
||||
stmt::{gen_for_callback, BreakContinueHooks},
|
||||
types::{ndarray::NDIterType, structure::StructField},
|
||||
values::{ArraySliceValue, TypedArrayLikeAdapter},
|
||||
CodeGenContext, CodeGenerator,
|
||||
CodeGenContext, CodeGenerator, irrt,
|
||||
stmt::{BreakContinueHooks, gen_for_callback},
|
||||
types::{
|
||||
ndarray::NDIterType,
|
||||
structure::{StructField, StructProxyType},
|
||||
},
|
||||
values::{ArraySliceValue, ProxyValue, TypedArrayLikeAdapter, structure::StructProxyValue},
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -23,13 +25,26 @@ pub struct NDIterValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> NDIterValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `NDArray`, returning [Err] if `value` is not an
|
||||
/// instance.
|
||||
pub fn is_representable(
|
||||
value: PointerValue<'ctx>,
|
||||
/// Creates an [`NDArrayValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: StructValue<'ctx>,
|
||||
parent: NDArrayValue<'ctx>,
|
||||
indices: ArraySliceValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
<Self as ProxyValue>::Type::is_representable(value.get_type(), llvm_usize)
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, parent, indices, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`NDArrayValue`] from a [`PointerValue`].
|
||||
@ -41,7 +56,7 @@ impl<'ctx> NDIterValue<'ctx> {
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
Self { value: ptr, parent, indices, llvm_usize, name }
|
||||
}
|
||||
@ -65,11 +80,8 @@ impl<'ctx> NDIterValue<'ctx> {
|
||||
irrt::ndarray::call_nac3_nditer_next(ctx, *self);
|
||||
}
|
||||
|
||||
fn element_field(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields(ctx.ctx).element
|
||||
fn element_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields().element
|
||||
}
|
||||
|
||||
/// Get pointer to the current element.
|
||||
@ -77,7 +89,7 @@ impl<'ctx> NDIterValue<'ctx> {
|
||||
pub fn get_pointer(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
let elem_ty = self.parent.dtype;
|
||||
|
||||
let p = self.element_field(ctx).get(ctx, self.as_base_value(), self.name);
|
||||
let p = self.element_field().load(ctx, self.as_abi_value(ctx), self.name);
|
||||
ctx.builder
|
||||
.build_pointer_cast(p, elem_ty.ptr_type(AddressSpace::default()), "element")
|
||||
.unwrap()
|
||||
@ -90,14 +102,14 @@ impl<'ctx> NDIterValue<'ctx> {
|
||||
ctx.builder.build_load(p, "value").unwrap()
|
||||
}
|
||||
|
||||
fn nth_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields(ctx.ctx).nth
|
||||
fn nth_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().nth
|
||||
}
|
||||
|
||||
/// Get the index of the current element if this ndarray were a flat ndarray.
|
||||
#[must_use]
|
||||
pub fn get_index(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.nth_field(ctx).get(ctx, self.as_base_value(), self.name)
|
||||
self.nth_field().load(ctx, self.as_abi_value(ctx), self.name)
|
||||
}
|
||||
|
||||
/// Get the indices of the current element.
|
||||
@ -114,18 +126,25 @@ impl<'ctx> NDIterValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for NDIterValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = NDIterType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
NDIterType::from_type(self.as_base_value().get_type(), self.llvm_usize)
|
||||
NDIterType::from_pointer_type(self.as_base_value().get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyValue<'ctx> for NDIterValue<'ctx> {}
|
||||
|
||||
impl<'ctx> From<NDIterValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: NDIterValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
|
@ -2,13 +2,13 @@ use inkwell::values::{BasicValueEnum, IntValue};
|
||||
|
||||
use crate::{
|
||||
codegen::{
|
||||
CodeGenContext, CodeGenerator,
|
||||
stmt::gen_for_callback_incrementing,
|
||||
types::{ListType, TupleType},
|
||||
values::{
|
||||
ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
|
||||
TypedArrayLikeMutator, UntypedArrayLikeAccessor,
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
},
|
||||
typecheck::typedef::{Type, TypeEnum},
|
||||
};
|
||||
@ -29,7 +29,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
(input_seq_ty, input_seq): (Type, BasicValueEnum<'ctx>),
|
||||
) -> impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>> {
|
||||
) -> impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>> + use<'ctx, G> {
|
||||
let llvm_usize = ctx.get_size_type();
|
||||
let zero = llvm_usize.const_zero();
|
||||
let one = llvm_usize.const_int(1, false);
|
||||
@ -42,7 +42,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
|
||||
// 1. A list of `int32`; e.g., `np.empty([600, 800, 3])`
|
||||
|
||||
let input_seq = ListType::from_unifier_type(generator, ctx, input_seq_ty)
|
||||
.map_value(input_seq.into_pointer_value(), None);
|
||||
.map_pointer_value(input_seq.into_pointer_value(), None);
|
||||
|
||||
let len = input_seq.load_size(ctx, None);
|
||||
// TODO: Find a way to remove this mid-BB allocation
|
||||
@ -86,7 +86,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
|
||||
// 2. A tuple of ints; e.g., `np.empty((600, 800, 3))`
|
||||
|
||||
let input_seq = TupleType::from_unifier_type(generator, ctx, input_seq_ty)
|
||||
.map_value(input_seq.into_struct_value(), None);
|
||||
.map_struct_value(input_seq.into_struct_value(), None);
|
||||
|
||||
let len = input_seq.get_type().num_elements();
|
||||
|
||||
@ -106,7 +106,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
|
||||
|
||||
for i in 0..input_seq.get_type().num_elements() {
|
||||
// Get the i-th element off of the tuple and load it into `result`.
|
||||
let int = input_seq.load_element(ctx, i).into_int_value();
|
||||
let int = input_seq.extract_element(ctx, i).into_int_value();
|
||||
let int = ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
|
||||
|
||||
unsafe {
|
||||
|
@ -4,14 +4,13 @@ use inkwell::values::{IntValue, PointerValue};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::codegen::{
|
||||
irrt,
|
||||
CodeGenContext, CodeGenerator, irrt,
|
||||
stmt::gen_if_callback,
|
||||
types::ndarray::NDArrayType,
|
||||
values::{
|
||||
ndarray::{NDArrayValue, RustNDIndex},
|
||||
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
|
||||
ndarray::{NDArrayValue, RustNDIndex},
|
||||
},
|
||||
CodeGenContext, CodeGenerator,
|
||||
};
|
||||
|
||||
impl<'ctx> NDArrayValue<'ctx> {
|
||||
|
75
nac3core/src/codegen/values/option.rs
Normal file
75
nac3core/src/codegen/values/option.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{BasicValueEnum, IntValue, PointerValue},
|
||||
};
|
||||
|
||||
use super::ProxyValue;
|
||||
use crate::codegen::{CodeGenContext, types::OptionType};
|
||||
|
||||
/// Proxy type for accessing a `Option` value in LLVM.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct OptionValue<'ctx> {
|
||||
value: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
}
|
||||
|
||||
impl<'ctx> OptionValue<'ctx> {
|
||||
/// Creates an [`OptionValue`] from a [`PointerValue`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_value(
|
||||
ptr: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
Self { value: ptr, llvm_usize, name }
|
||||
}
|
||||
|
||||
/// Returns an `i1` indicating if this `Option` instance does not hold a value.
|
||||
#[must_use]
|
||||
pub fn is_none(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
ctx.builder.build_is_null(self.value, "").unwrap()
|
||||
}
|
||||
|
||||
/// Returns an `i1` indicating if this `Option` instance contains a value.
|
||||
#[must_use]
|
||||
pub fn is_some(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
ctx.builder.build_is_not_null(self.value, "").unwrap()
|
||||
}
|
||||
|
||||
/// Loads the value present in this `Option` instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that this `option` value [contains a value][Self::is_some].
|
||||
#[must_use]
|
||||
pub unsafe fn load(&self, ctx: &CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
|
||||
ctx.builder.build_load(self.value, "").unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for OptionValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = OptionType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
Self::Type::from_pointer_type(self.value.get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<OptionValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: OptionValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
}
|
||||
}
|
@ -1,27 +1,50 @@
|
||||
use inkwell::values::{BasicValueEnum, IntValue, PointerValue};
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{ArrayValue, BasicValueEnum, IntValue, PointerValue},
|
||||
};
|
||||
|
||||
use super::ProxyValue;
|
||||
use crate::codegen::{types::RangeType, CodeGenContext};
|
||||
use crate::codegen::{CodeGenContext, CodeGenerator, types::RangeType};
|
||||
|
||||
/// Proxy type for accessing a `range` value in LLVM.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct RangeValue<'ctx> {
|
||||
value: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
}
|
||||
|
||||
impl<'ctx> RangeValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `range`, returning [Err] if `value` is not an instance.
|
||||
pub fn is_representable(value: PointerValue<'ctx>) -> Result<(), String> {
|
||||
RangeType::is_representable(value.get_type())
|
||||
/// Creates an [`RangeValue`] from a [`PointerValue`].
|
||||
#[must_use]
|
||||
pub fn from_array_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: ArrayValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`RangeValue`] from a [`PointerValue`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_value(ptr: PointerValue<'ctx>, name: Option<&'ctx str>) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr).is_ok());
|
||||
pub fn from_pointer_value(
|
||||
ptr: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
RangeValue { value: ptr, name }
|
||||
RangeValue { value: ptr, llvm_usize, name }
|
||||
}
|
||||
|
||||
fn ptr_to_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
@ -31,7 +54,7 @@ impl<'ctx> RangeValue<'ctx> {
|
||||
unsafe {
|
||||
ctx.builder
|
||||
.build_in_bounds_gep(
|
||||
self.as_base_value(),
|
||||
self.as_abi_value(ctx),
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_int(0, false)],
|
||||
var_name.as_str(),
|
||||
)
|
||||
@ -46,7 +69,7 @@ impl<'ctx> RangeValue<'ctx> {
|
||||
unsafe {
|
||||
ctx.builder
|
||||
.build_in_bounds_gep(
|
||||
self.as_base_value(),
|
||||
self.as_abi_value(ctx),
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_int(1, false)],
|
||||
var_name.as_str(),
|
||||
)
|
||||
@ -61,7 +84,7 @@ impl<'ctx> RangeValue<'ctx> {
|
||||
unsafe {
|
||||
ctx.builder
|
||||
.build_in_bounds_gep(
|
||||
self.as_base_value(),
|
||||
self.as_abi_value(ctx),
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_int(2, false)],
|
||||
var_name.as_str(),
|
||||
)
|
||||
@ -134,16 +157,21 @@ impl<'ctx> RangeValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for RangeValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = RangeType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
RangeType::from_type(self.value.get_type())
|
||||
RangeType::from_pointer_type(self.value.get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<RangeValue<'ctx>> for PointerValue<'ctx> {
|
||||
|
87
nac3core/src/codegen/values/string.rs
Normal file
87
nac3core/src/codegen/values/string.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
};
|
||||
|
||||
use crate::codegen::{
|
||||
CodeGenContext,
|
||||
types::{StringType, structure::StructField},
|
||||
values::ProxyValue,
|
||||
};
|
||||
|
||||
/// Proxy type for accessing a `str` value in LLVM.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StringValue<'ctx> {
|
||||
value: StructValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
}
|
||||
|
||||
impl<'ctx> StringValue<'ctx> {
|
||||
/// Creates an [`StringValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value(
|
||||
val: StructValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_instance(val, llvm_usize).is_ok());
|
||||
|
||||
Self { value: val, llvm_usize, name }
|
||||
}
|
||||
|
||||
/// Creates an [`StringValue`] from a [`PointerValue`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_value(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let val = ctx.builder.build_load(ptr, "").map(BasicValueEnum::into_struct_value).unwrap();
|
||||
|
||||
Self::from_struct_value(val, llvm_usize, name)
|
||||
}
|
||||
|
||||
fn ptr_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
|
||||
self.get_type().get_fields().ptr
|
||||
}
|
||||
|
||||
/// Returns the pointer to the beginning of the string.
|
||||
pub fn extract_ptr(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.ptr_field().extract_value(ctx, self.value)
|
||||
}
|
||||
|
||||
fn len_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
self.get_type().get_fields().len
|
||||
}
|
||||
|
||||
/// Returns the length of the string.
|
||||
pub fn extract_len(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.len_field().extract_value(ctx, self.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for StringValue<'ctx> {
|
||||
type ABI = StructValue<'ctx>;
|
||||
type Base = StructValue<'ctx>;
|
||||
type Type = StringType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
Self::Type::from_struct_type(self.value.get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<StringValue<'ctx>> for StructValue<'ctx> {
|
||||
fn from(value: StringValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
}
|
||||
}
|
24
nac3core/src/codegen/values/structure.rs
Normal file
24
nac3core/src/codegen/values/structure.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use inkwell::values::{BasicValueEnum, PointerValue, StructValue};
|
||||
|
||||
use super::ProxyValue;
|
||||
use crate::codegen::{CodeGenContext, types::structure::StructProxyType};
|
||||
|
||||
/// An LLVM value that is used to represent a corresponding structure-like value in NAC3.
|
||||
pub trait StructProxyValue<'ctx>:
|
||||
ProxyValue<'ctx, Base = PointerValue<'ctx>, Type: StructProxyType<'ctx, Value = Self>>
|
||||
{
|
||||
/// Returns this value as a [`StructValue`].
|
||||
#[must_use]
|
||||
fn get_struct_value(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructValue<'ctx> {
|
||||
ctx.builder
|
||||
.build_load(self.get_pointer_value(ctx), "")
|
||||
.map(BasicValueEnum::into_struct_value)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Returns this value as a [`PointerValue`].
|
||||
#[must_use]
|
||||
fn get_pointer_value(&self, _: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{BasicValue, BasicValueEnum, StructValue},
|
||||
values::{BasicValue, BasicValueEnum, PointerValue, StructValue},
|
||||
};
|
||||
|
||||
use super::ProxyValue;
|
||||
use crate::codegen::{types::TupleType, CodeGenContext};
|
||||
use crate::codegen::{CodeGenContext, types::TupleType};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TupleValue<'ctx> {
|
||||
@ -14,15 +14,6 @@ pub struct TupleValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> TupleValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `tuple`, returning [Err] if `value` is not an
|
||||
/// instance.
|
||||
pub fn is_representable(
|
||||
value: StructValue<'ctx>,
|
||||
_llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
TupleType::is_representable(value.get_type())
|
||||
}
|
||||
|
||||
/// Creates an [`TupleValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value(
|
||||
@ -30,13 +21,31 @@ impl<'ctx> TupleValue<'ctx> {
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(value, llvm_usize).is_ok());
|
||||
debug_assert!(Self::is_instance(value, llvm_usize).is_ok());
|
||||
|
||||
Self { value, llvm_usize, name }
|
||||
}
|
||||
|
||||
/// Creates an [`TupleValue`] from a [`PointerValue`].
|
||||
#[must_use]
|
||||
pub fn from_pointer_value(
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
ptr: PointerValue<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
Self::from_struct_value(
|
||||
ctx.builder
|
||||
.build_load(ptr, name.unwrap_or_default())
|
||||
.map(BasicValueEnum::into_struct_value)
|
||||
.unwrap(),
|
||||
llvm_usize,
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
/// Stores a value into the tuple element at the given `index`.
|
||||
pub fn store_element(
|
||||
pub fn insert_element(
|
||||
&mut self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
index: u32,
|
||||
@ -54,7 +63,11 @@ impl<'ctx> TupleValue<'ctx> {
|
||||
}
|
||||
|
||||
/// Loads a value from the tuple element at the given `index`.
|
||||
pub fn load_element(&self, ctx: &CodeGenContext<'ctx, '_>, index: u32) -> BasicValueEnum<'ctx> {
|
||||
pub fn extract_element(
|
||||
&self,
|
||||
ctx: &CodeGenContext<'ctx, '_>,
|
||||
index: u32,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
ctx.builder
|
||||
.build_extract_value(
|
||||
self.value,
|
||||
@ -66,16 +79,21 @@ impl<'ctx> TupleValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for TupleValue<'ctx> {
|
||||
type ABI = StructValue<'ctx>;
|
||||
type Base = StructValue<'ctx>;
|
||||
type Type = TupleType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
TupleType::from_type(self.as_base_value().get_type(), self.llvm_usize)
|
||||
TupleType::from_struct_type(self.as_base_value().get_type(), self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> From<TupleValue<'ctx>> for StructValue<'ctx> {
|
||||
|
@ -1,15 +1,18 @@
|
||||
use inkwell::{
|
||||
types::IntType,
|
||||
values::{IntValue, PointerValue},
|
||||
values::{IntValue, PointerValue, StructValue},
|
||||
};
|
||||
|
||||
use nac3parser::ast::Expr;
|
||||
|
||||
use crate::{
|
||||
codegen::{
|
||||
types::{structure::StructField, utils::SliceType},
|
||||
values::ProxyValue,
|
||||
CodeGenContext, CodeGenerator,
|
||||
types::{
|
||||
structure::{StructField, StructProxyType},
|
||||
utils::SliceType,
|
||||
},
|
||||
values::{ProxyValue, structure::StructProxyValue},
|
||||
},
|
||||
typecheck::typedef::Type,
|
||||
};
|
||||
@ -24,13 +27,25 @@ pub struct SliceValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> SliceValue<'ctx> {
|
||||
/// Checks whether `value` is an instance of `ContiguousNDArray`, returning [Err] if `value` is
|
||||
/// not an instance.
|
||||
pub fn is_representable(
|
||||
value: PointerValue<'ctx>,
|
||||
/// Creates an [`SliceValue`] from a [`StructValue`].
|
||||
#[must_use]
|
||||
pub fn from_struct_value<G: CodeGenerator + ?Sized>(
|
||||
generator: &mut G,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
val: StructValue<'ctx>,
|
||||
int_ty: IntType<'ctx>,
|
||||
llvm_usize: IntType<'ctx>,
|
||||
) -> Result<(), String> {
|
||||
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
let pval = generator
|
||||
.gen_var_alloc(
|
||||
ctx,
|
||||
val.get_type().into(),
|
||||
name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)
|
||||
.unwrap();
|
||||
ctx.builder.build_store(pval, val).unwrap();
|
||||
Self::from_pointer_value(pval, int_ty, llvm_usize, name)
|
||||
}
|
||||
|
||||
/// Creates an [`SliceValue`] from a [`PointerValue`].
|
||||
@ -41,7 +56,7 @@ impl<'ctx> SliceValue<'ctx> {
|
||||
llvm_usize: IntType<'ctx>,
|
||||
name: Option<&'ctx str>,
|
||||
) -> Self {
|
||||
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
|
||||
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
|
||||
|
||||
Self { value: ptr, int_ty, llvm_usize, name }
|
||||
}
|
||||
@ -51,7 +66,7 @@ impl<'ctx> SliceValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn load_start_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.start_defined_field().get(ctx, self.value, self.name)
|
||||
self.start_defined_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
fn start_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
@ -59,22 +74,22 @@ impl<'ctx> SliceValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.start_field().get(ctx, self.value, self.name)
|
||||
self.start_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
pub fn store_start(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) {
|
||||
match value {
|
||||
Some(start) => {
|
||||
self.start_defined_field().set(
|
||||
self.start_defined_field().store(
|
||||
ctx,
|
||||
self.value,
|
||||
ctx.ctx.bool_type().const_all_ones(),
|
||||
self.name,
|
||||
);
|
||||
self.start_field().set(ctx, self.value, start, self.name);
|
||||
self.start_field().store(ctx, self.value, start, self.name);
|
||||
}
|
||||
|
||||
None => self.start_defined_field().set(
|
||||
None => self.start_defined_field().store(
|
||||
ctx,
|
||||
self.value,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
@ -88,7 +103,7 @@ impl<'ctx> SliceValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn load_stop_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.stop_defined_field().get(ctx, self.value, self.name)
|
||||
self.stop_defined_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
fn stop_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
@ -96,22 +111,22 @@ impl<'ctx> SliceValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn load_stop(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.stop_field().get(ctx, self.value, self.name)
|
||||
self.stop_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
pub fn store_stop(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) {
|
||||
match value {
|
||||
Some(stop) => {
|
||||
self.stop_defined_field().set(
|
||||
self.stop_defined_field().store(
|
||||
ctx,
|
||||
self.value,
|
||||
ctx.ctx.bool_type().const_all_ones(),
|
||||
self.name,
|
||||
);
|
||||
self.stop_field().set(ctx, self.value, stop, self.name);
|
||||
self.stop_field().store(ctx, self.value, stop, self.name);
|
||||
}
|
||||
|
||||
None => self.stop_defined_field().set(
|
||||
None => self.stop_defined_field().store(
|
||||
ctx,
|
||||
self.value,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
@ -125,7 +140,7 @@ impl<'ctx> SliceValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn load_step_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.step_defined_field().get(ctx, self.value, self.name)
|
||||
self.step_defined_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
fn step_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
|
||||
@ -133,22 +148,22 @@ impl<'ctx> SliceValue<'ctx> {
|
||||
}
|
||||
|
||||
pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
|
||||
self.step_field().get(ctx, self.value, self.name)
|
||||
self.step_field().load(ctx, self.value, self.name)
|
||||
}
|
||||
|
||||
pub fn store_step(&self, ctx: &CodeGenContext<'ctx, '_>, value: Option<IntValue<'ctx>>) {
|
||||
match value {
|
||||
Some(step) => {
|
||||
self.step_defined_field().set(
|
||||
self.step_defined_field().store(
|
||||
ctx,
|
||||
self.value,
|
||||
ctx.ctx.bool_type().const_all_ones(),
|
||||
self.name,
|
||||
);
|
||||
self.step_field().set(ctx, self.value, step, self.name);
|
||||
self.step_field().store(ctx, self.value, step, self.name);
|
||||
}
|
||||
|
||||
None => self.step_defined_field().set(
|
||||
None => self.step_defined_field().store(
|
||||
ctx,
|
||||
self.value,
|
||||
ctx.ctx.bool_type().const_zero(),
|
||||
@ -159,18 +174,25 @@ impl<'ctx> SliceValue<'ctx> {
|
||||
}
|
||||
|
||||
impl<'ctx> ProxyValue<'ctx> for SliceValue<'ctx> {
|
||||
type ABI = PointerValue<'ctx>;
|
||||
type Base = PointerValue<'ctx>;
|
||||
type Type = SliceType<'ctx>;
|
||||
|
||||
fn get_type(&self) -> Self::Type {
|
||||
Self::Type::from_type(self.value.get_type(), self.int_ty, self.llvm_usize)
|
||||
Self::Type::from_pointer_type(self.value.get_type(), self.int_ty, self.llvm_usize)
|
||||
}
|
||||
|
||||
fn as_base_value(&self) -> Self::Base {
|
||||
self.value
|
||||
}
|
||||
|
||||
fn as_abi_value(&self, _: &CodeGenContext<'ctx, '_>) -> Self::ABI {
|
||||
self.as_base_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> StructProxyValue<'ctx> for SliceValue<'ctx> {}
|
||||
|
||||
impl<'ctx> From<SliceValue<'ctx>> for PointerValue<'ctx> {
|
||||
fn from(value: SliceValue<'ctx>) -> Self {
|
||||
value.as_base_value()
|
||||
|
@ -6,14 +6,14 @@ use std::{
|
||||
};
|
||||
|
||||
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
|
||||
use itertools::{izip, Itertools};
|
||||
use itertools::{Itertools, izip};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use nac3parser::ast::{Constant, Expr, Location, StrRef};
|
||||
|
||||
use crate::{
|
||||
codegen::{CodeGenContext, CodeGenerator},
|
||||
toplevel::{type_annotation::TypeAnnotation, DefinitionId, TopLevelDef},
|
||||
toplevel::{DefinitionId, TopLevelDef, type_annotation::TypeAnnotation},
|
||||
typecheck::{
|
||||
type_inferencer::PrimitiveStore,
|
||||
typedef::{Type, TypeEnum, Unifier, VarMap},
|
||||
|
@ -1,13 +1,13 @@
|
||||
use std::iter::once;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use inkwell::{values::BasicValue, IntPredicate};
|
||||
use inkwell::{IntPredicate, values::BasicValue};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use super::{
|
||||
helper::{
|
||||
arraylike_flatten_element_type, debug_assert_prim_is_allowed, extract_ndims,
|
||||
make_exception_fields, PrimDef, PrimDefDetails,
|
||||
PrimDef, PrimDefDetails, arraylike_flatten_element_type, debug_assert_prim_is_allowed,
|
||||
extract_ndims, make_exception_fields,
|
||||
},
|
||||
numpy::{make_ndarray_ty, unpack_ndarray_var_tys},
|
||||
*,
|
||||
@ -17,14 +17,14 @@ use crate::{
|
||||
builtin_fns,
|
||||
numpy::*,
|
||||
stmt::{exn_constructor, gen_if_callback},
|
||||
types::ndarray::NDArrayType,
|
||||
types::{RangeType, ndarray::NDArrayType},
|
||||
values::{
|
||||
ndarray::{shape::parse_numpy_int_sequence, ScalarOrNDArray},
|
||||
ProxyValue, RangeValue,
|
||||
ProxyValue,
|
||||
ndarray::{ScalarOrNDArray, shape::parse_numpy_int_sequence},
|
||||
},
|
||||
},
|
||||
symbol_resolver::SymbolValue,
|
||||
typecheck::typedef::{into_var_map, iter_type_vars, TypeVar, VarMap},
|
||||
typecheck::typedef::{TypeVar, VarMap, into_var_map, iter_type_vars},
|
||||
};
|
||||
|
||||
type BuiltinInfo = Vec<(Arc<RwLock<TopLevelDef>>, Option<Stmt>)>;
|
||||
@ -36,9 +36,7 @@ pub fn get_exn_constructor(
|
||||
unifier: &mut Unifier,
|
||||
primitives: &PrimitiveStore,
|
||||
) -> (TopLevelDef, TopLevelDef, Type, Type) {
|
||||
let int32 = primitives.int32;
|
||||
let int64 = primitives.int64;
|
||||
let string = primitives.str;
|
||||
let PrimitiveStore { int32, int64, str: string, .. } = *primitives;
|
||||
let exception_fields = make_exception_fields(int32, int64, string);
|
||||
let exn_cons_args = vec![
|
||||
FuncArg {
|
||||
@ -481,7 +479,9 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
assert_eq!(simple_name, &exp_simple_name.into());
|
||||
}
|
||||
_ => {
|
||||
panic!("Class/function variant of the constructed TopLevelDef of PrimDef {prim:?} is different than what is defined by {prim:?}")
|
||||
panic!(
|
||||
"Class/function variant of the constructed TopLevelDef of PrimDef {prim:?} is different than what is defined by {prim:?}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -577,7 +577,7 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
let (zelf_ty, zelf) = obj.unwrap();
|
||||
let zelf =
|
||||
zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value();
|
||||
let zelf = RangeValue::from_pointer_value(zelf, Some("range"));
|
||||
let zelf = RangeType::new(ctx).map_pointer_value(zelf, Some("range"));
|
||||
|
||||
let mut start = None;
|
||||
let mut stop = None;
|
||||
@ -664,7 +664,7 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
zelf.store_end(ctx, stop);
|
||||
zelf.store_step(ctx, step);
|
||||
|
||||
Ok(Some(zelf.as_base_value().into()))
|
||||
Ok(Some(zelf.as_abi_value(ctx).into()))
|
||||
},
|
||||
)))),
|
||||
loc: None,
|
||||
@ -1280,7 +1280,7 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
let ndarray =
|
||||
args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?;
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
|
||||
.map_value(ndarray.into_pointer_value(), None);
|
||||
.map_pointer_value(ndarray.into_pointer_value(), None);
|
||||
|
||||
let size = ctx
|
||||
.builder
|
||||
@ -1312,7 +1312,7 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
args[0].1.clone().to_basic_value_enum(ctx, generator, ndarray_ty)?;
|
||||
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
|
||||
.map_value(ndarray.into_pointer_value(), None);
|
||||
.map_pointer_value(ndarray.into_pointer_value(), None);
|
||||
|
||||
let result_tuple = match prim {
|
||||
PrimDef::FunNpShape => ndarray.make_shape_tuple(generator, ctx),
|
||||
@ -1320,7 +1320,7 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(Some(result_tuple.as_base_value().into()))
|
||||
Ok(Some(result_tuple.as_abi_value(ctx).into()))
|
||||
}),
|
||||
)
|
||||
}
|
||||
@ -1353,10 +1353,10 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, arg_ty)
|
||||
.map_value(arg_val.into_pointer_value(), None);
|
||||
.map_pointer_value(arg_val.into_pointer_value(), None);
|
||||
|
||||
let ndarray = ndarray.transpose(generator, ctx, None); // TODO: Add axes argument
|
||||
Ok(Some(ndarray.as_base_value().into()))
|
||||
Ok(Some(ndarray.as_abi_value(ctx).into()))
|
||||
}),
|
||||
),
|
||||
|
||||
@ -1391,7 +1391,7 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
args[1].1.clone().to_basic_value_enum(ctx, generator, shape_ty)?;
|
||||
|
||||
let ndarray = NDArrayType::from_unifier_type(generator, ctx, ndarray_ty)
|
||||
.map_value(ndarray_val.into_pointer_value(), None);
|
||||
.map_pointer_value(ndarray_val.into_pointer_value(), None);
|
||||
|
||||
let shape = parse_numpy_int_sequence(generator, ctx, (shape_ty, shape_val));
|
||||
|
||||
@ -1410,7 +1410,7 @@ impl<'a> BuiltinBuilder<'a> {
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok(Some(new_ndarray.as_base_value().as_basic_value_enum()))
|
||||
Ok(Some(new_ndarray.as_abi_value(ctx).as_basic_value_enum()))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use nac3parser::ast::{fold::Fold, ExprKind, Ident};
|
||||
use nac3parser::ast::{ExprKind, Ident, fold::Fold};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
@ -265,8 +265,7 @@ impl TopLevelComposer {
|
||||
if self.keyword_list.contains(class_name) {
|
||||
return Err(format!(
|
||||
"cannot use keyword `{}` as a class name (at {})",
|
||||
class_name,
|
||||
ast.location
|
||||
class_name, ast.location
|
||||
));
|
||||
}
|
||||
let fully_qualified_class_name = if mod_path.is_empty() {
|
||||
@ -277,8 +276,7 @@ impl TopLevelComposer {
|
||||
if !defined_names.insert(fully_qualified_class_name.into()) {
|
||||
return Err(format!(
|
||||
"duplicate definition of class `{}` (at {})",
|
||||
class_name,
|
||||
ast.location
|
||||
class_name, ast.location
|
||||
));
|
||||
}
|
||||
|
||||
@ -294,7 +292,7 @@ impl TopLevelComposer {
|
||||
resolver.clone(),
|
||||
fully_qualified_class_name,
|
||||
Some(constructor_ty),
|
||||
Some(ast.location)
|
||||
Some(ast.location),
|
||||
))),
|
||||
None,
|
||||
);
|
||||
@ -321,8 +319,7 @@ impl TopLevelComposer {
|
||||
if self.keyword_list.contains(method_name) {
|
||||
return Err(format!(
|
||||
"cannot use keyword `{}` as a method name (at {})",
|
||||
method_name,
|
||||
b.location
|
||||
method_name, b.location
|
||||
));
|
||||
}
|
||||
let global_class_method_name = Self::make_class_method_name(
|
||||
@ -332,8 +329,7 @@ impl TopLevelComposer {
|
||||
if !defined_names.insert(global_class_method_name.clone()) {
|
||||
return Err(format!(
|
||||
"class method `{}` defined twice (at {})",
|
||||
global_class_method_name,
|
||||
b.location
|
||||
global_class_method_name, b.location
|
||||
));
|
||||
}
|
||||
let method_def_id = self.definition_ast_list.len() + {
|
||||
@ -380,7 +376,11 @@ impl TopLevelComposer {
|
||||
self.definition_ast_list.push((def, Some(ast)));
|
||||
}
|
||||
|
||||
let result_ty = if allow_no_constructor || contains_constructor { Some(constructor_ty) } else { None };
|
||||
let result_ty = if allow_no_constructor || contains_constructor {
|
||||
Some(constructor_ty)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok((class_name, DefinitionId(class_def_id), result_ty))
|
||||
}
|
||||
|
||||
@ -393,8 +393,7 @@ impl TopLevelComposer {
|
||||
if !defined_names.insert(global_fun_name.clone()) {
|
||||
return Err(format!(
|
||||
"top level function `{}` defined twice (at {})",
|
||||
global_fun_name,
|
||||
ast.location
|
||||
global_fun_name, ast.location
|
||||
));
|
||||
}
|
||||
|
||||
@ -408,7 +407,7 @@ impl TopLevelComposer {
|
||||
// dummy here, unify with correct type later
|
||||
ty_to_be_unified,
|
||||
resolver,
|
||||
Some(ast.location)
|
||||
Some(ast.location),
|
||||
))
|
||||
.into(),
|
||||
Some(ast),
|
||||
@ -432,7 +431,10 @@ impl TopLevelComposer {
|
||||
// Make callers use `register_top_level_var` instead, as it provides more
|
||||
// fine-grained control over which symbols to register, while also simplifying the
|
||||
// usage of this function.
|
||||
panic!("Registration of top-level Assign statements must use TopLevelComposer::register_top_level_var (at {})", ast.location);
|
||||
panic!(
|
||||
"Registration of top-level Assign statements must use TopLevelComposer::register_top_level_var (at {})",
|
||||
ast.location
|
||||
);
|
||||
}
|
||||
|
||||
ast::StmtKind::AnnAssign { target, annotation, .. } => {
|
||||
@ -461,9 +463,9 @@ impl TopLevelComposer {
|
||||
|
||||
/// Registers a top-level variable with the given `name` into the composer.
|
||||
///
|
||||
/// `annotation` - The type annotation of the top-level variable, or [`None`] if no type
|
||||
/// annotation is provided.
|
||||
/// `location` - The location of the top-level variable.
|
||||
/// - `annotation` - The type annotation of the top-level variable, or [`None`] if no type
|
||||
/// annotation is provided.
|
||||
/// - `location` - The location of the top-level variable.
|
||||
pub fn register_top_level_var(
|
||||
&mut self,
|
||||
name: Ident,
|
||||
@ -1234,7 +1236,7 @@ impl TopLevelComposer {
|
||||
ExprKind::Subscript { value, slice, .. }
|
||||
if matches!(
|
||||
&value.node,
|
||||
ast::ExprKind::Name { id, .. } if core_config.kernel_ann.map_or(false, |c| id == &c.into())
|
||||
ast::ExprKind::Name { id, .. } if core_config.kernel_ann.is_some_and(|c| id == &c.into())
|
||||
) =>
|
||||
{
|
||||
(slice, true)
|
||||
@ -1405,14 +1407,14 @@ impl TopLevelComposer {
|
||||
);
|
||||
if !ok {
|
||||
return Err(HashSet::from([format!(
|
||||
"method {class_method_name} has same name as ancestors' method, but incompatible type"),
|
||||
]));
|
||||
"method {class_method_name} has same name as ancestors' method, but incompatible type"
|
||||
)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
class_methods_def.clear();
|
||||
class_methods_def
|
||||
.extend(new_child_methods.iter().map(|f| (*f.0, f.1 .0, f.1 .1)).collect_vec());
|
||||
.extend(new_child_methods.iter().map(|f| (*f.0, f.1.0, f.1.1)).collect_vec());
|
||||
|
||||
// handle class fields
|
||||
let mut new_child_fields: IndexMap<StrRef, (Type, bool)> =
|
||||
@ -1441,10 +1443,10 @@ impl TopLevelComposer {
|
||||
|
||||
class_fields_def.clear();
|
||||
class_fields_def
|
||||
.extend(new_child_fields.iter().map(|f| (*f.0, f.1 .0, f.1 .1)).collect_vec());
|
||||
.extend(new_child_fields.iter().map(|f| (*f.0, f.1.0, f.1.1)).collect_vec());
|
||||
class_attribute_def.clear();
|
||||
class_attribute_def.extend(
|
||||
new_child_attributes.iter().map(|f| (*f.0, f.1 .0, f.1 .1.clone())).collect_vec(),
|
||||
new_child_attributes.iter().map(|f| (*f.0, f.1.0, f.1.1.clone())).collect_vec(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@ -1521,8 +1523,7 @@ impl TopLevelComposer {
|
||||
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
|
||||
{
|
||||
// create constructor for these classes
|
||||
let string = primitives_ty.str;
|
||||
let int64 = primitives_ty.int64;
|
||||
let PrimitiveStore { str: string, int64, .. } = *primitives_ty;
|
||||
let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature {
|
||||
args: vec![
|
||||
FuncArg {
|
||||
@ -1622,14 +1623,10 @@ impl TopLevelComposer {
|
||||
)?;
|
||||
for (f, _, _) in fields {
|
||||
if !all_inited.contains(f) {
|
||||
return Err(HashSet::from([
|
||||
format!(
|
||||
"fields `{}` of class `{}` not fully initialized in the initializer (at {})",
|
||||
f,
|
||||
class_name,
|
||||
body[0].location,
|
||||
),
|
||||
]));
|
||||
return Err(HashSet::from([format!(
|
||||
"fields `{}` of class `{}` not fully initialized in the initializer (at {})",
|
||||
f, class_name, body[0].location,
|
||||
)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1901,8 +1898,8 @@ impl TopLevelComposer {
|
||||
let base_repr = inferencer.unifier.stringify(*base);
|
||||
let subtype_repr = inferencer.unifier.stringify(*subtype);
|
||||
return Err(HashSet::from([format!(
|
||||
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"),
|
||||
]));
|
||||
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"
|
||||
)]));
|
||||
}
|
||||
};
|
||||
let subtype_entry = defs[subtype_id.0].read();
|
||||
@ -1916,8 +1913,8 @@ impl TopLevelComposer {
|
||||
let base_repr = inferencer.unifier.stringify(*base);
|
||||
let subtype_repr = inferencer.unifier.stringify(*subtype);
|
||||
return Err(HashSet::from([format!(
|
||||
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"),
|
||||
]));
|
||||
"Expected a subtype of {base_repr}, but got {subtype_repr} (at {loc})"
|
||||
)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2002,13 +1999,15 @@ impl TopLevelComposer {
|
||||
ExprKind::Subscript { value, slice, .. }
|
||||
if matches!(
|
||||
&value.node,
|
||||
ast::ExprKind::Name { id, .. } if self.core_config.kernel_ann.map_or(false, |c| id == &c.into())
|
||||
ast::ExprKind::Name { id, .. } if self.core_config.kernel_ann.is_some_and(|c| id == &c.into()) || id == &self.core_config.kernel_invariant_ann.into()
|
||||
) =>
|
||||
{
|
||||
slice
|
||||
}
|
||||
_ if self.core_config.kernel_ann.is_none() => ty_decl,
|
||||
_ => unreachable!("Global variables should be annotated with Kernel[]"), // ignore fields annotated otherwise
|
||||
_ => unreachable!(
|
||||
"Global variables should be annotated with Kernel[] or KernelInvariant[]"
|
||||
), // ignore fields annotated otherwise
|
||||
};
|
||||
|
||||
let ty_annotation = parse_ast_to_type_annotation_kinds(
|
||||
|
@ -8,7 +8,7 @@ use nac3parser::ast::{Constant, ExprKind, Location};
|
||||
use super::{numpy::unpack_ndarray_var_tys, *};
|
||||
use crate::{
|
||||
symbol_resolver::SymbolValue,
|
||||
typecheck::typedef::{into_var_map, iter_type_vars, Mapping, TypeVarId, VarMap},
|
||||
typecheck::typedef::{Mapping, TypeVarId, VarMap, into_var_map, iter_type_vars},
|
||||
};
|
||||
|
||||
/// All primitive types and functions in nac3core.
|
||||
@ -757,7 +757,7 @@ impl TopLevelComposer {
|
||||
return Err(HashSet::from([format!(
|
||||
"redundant type annotation for class fields at {}",
|
||||
s.location
|
||||
)]))
|
||||
)]));
|
||||
}
|
||||
ast::StmtKind::Assign { targets, .. } => {
|
||||
for t in targets {
|
||||
@ -1038,7 +1038,10 @@ impl TopLevelComposer {
|
||||
}
|
||||
ast::ExprKind::Name { .. } | ast::ExprKind::Subscript { .. } => {
|
||||
if has_base {
|
||||
return Err(HashSet::from([format!("a class definition can only have at most one base class declaration and one generic declaration (at {})", b.location )]));
|
||||
return Err(HashSet::from([format!(
|
||||
"a class definition can only have at most one base class declaration and one generic declaration (at {})",
|
||||
b.location
|
||||
)]));
|
||||
}
|
||||
has_base = true;
|
||||
// the function parse_ast_to make sure that no type var occurred in
|
||||
@ -1233,7 +1236,9 @@ pub fn arraylike_get_ndims(unifier: &mut Unifier, ty: Type) -> u64 {
|
||||
};
|
||||
|
||||
if values.len() > 1 {
|
||||
todo!("Getting num of dimensions for ndarray with more than one ndim bound is unimplemented")
|
||||
todo!(
|
||||
"Getting num of dimensions for ndarray with more than one ndim bound is unimplemented"
|
||||
)
|
||||
}
|
||||
|
||||
u64::try_from(values[0].clone()).unwrap()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user