Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

137 changed files with 3948 additions and 6021 deletions

View File

@ -1,2 +0,0 @@
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

530
Cargo.lock generated

File diff suppressed because it is too large Load Diff

6
flake.lock generated
View File

@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1744098102,
"narHash": "sha256-tzCdyIJj9AjysC3OuKA+tMD/kDEDAF9mICPDU7ix0JA=",
"lastModified": 1736798957,
"narHash": "sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c8cd81426f45942bb2906d5ed2fe21d2f19d95b7",
"rev": "9abb87b552b7f55ac8916b6fc9e5cb486656a2f3",
"type": "github"
},
"original": {

View File

@ -13,8 +13,8 @@
llvm-tools-irrt = pkgs.runCommandNoCC "llvm-tools-irrt" {}
''
mkdir -p $out/bin
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
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
'';
demo-linalg-stub = pkgs.rustPlatform.buildRustPackage {
name = "demo-linalg-stub";
@ -39,12 +39,9 @@
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_16.clang) llvm-tools-irrt pkgs.llvmPackages_16.llvm.out pkgs.llvmPackages_16.bintools llvm-nac3 ];
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_14.clang) llvm-tools-irrt pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
buildInputs = [ pkgs.python3 llvm-nac3 ];
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ps.scipy ])) ];
checkPhase =
@ -80,7 +77,7 @@
# LLVM PGO support
llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_16.stdenv;
stdenv = pkgs.llvmPackages_14.stdenv;
extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ];
};
nac3artiq-instrumented = pkgs.python3Packages.toPythonModule (
@ -88,13 +85,13 @@
name = "nac3artiq-instrumented";
src = self;
inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-instrumented ];
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt llvm-nac3-instrumented ];
buildInputs = [ pkgs.python3 llvm-nac3-instrumented ];
cargoBuildFlags = [ "--package" "nac3artiq" "--features" "init-llvm-profile" ];
doCheck = false;
configurePhase =
''
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_16.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_14.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
'';
installPhase =
''
@ -116,14 +113,13 @@
(pkgs.fetchFromGitHub {
owner = "m-labs";
repo = "artiq";
rev = "554b0749ca5985bf4d006c4f29a05e83de0a226d";
sha256 = "sha256-3eSNHTSlmdzLMcEMIspxqjmjrcQe4aIGqIfRgquUg18=";
rev = "28c9de3e251daa89a8c9fd79d5ab64a3ec03bac6";
sha256 = "sha256-vAvpbHc5B+1wtG8zqN7j9dQE1ON+i22v+uqA+tw6Gak=";
})
];
buildInputs = [
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ps.jsonschema ps.lmdb ps.platformdirs nac3artiq-instrumented ]))
pkgs.llvmPackages_16.llvm.out
pkgs.llvmPackages_16.bintools
pkgs.llvmPackages_14.llvm.out
];
phases = [ "buildPhase" "installPhase" ];
buildPhase =
@ -143,7 +139,7 @@
'';
};
llvm-nac3-pgo = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_16.stdenv;
stdenv = pkgs.llvmPackages_14.stdenv;
extraCmakeFlags = [ "-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata" ];
};
nac3artiq-pgo = pkgs.python3Packages.toPythonModule (
@ -151,7 +147,7 @@
name = "nac3artiq-pgo";
src = self;
inherit (nac3artiq) cargoLock;
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-pgo ];
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt llvm-nac3-pgo ];
buildInputs = [ pkgs.python3 llvm-nac3-pgo ];
cargoBuildFlags = [ "--package" "nac3artiq" ];
cargoTestFlags = [ "--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq" ];
@ -172,12 +168,12 @@
buildInputs = with pkgs; [
# build dependencies
packages.x86_64-linux.llvm-nac3
(pkgs.wrapClangMulti llvmPackages_16.clang) llvmPackages_16.llvm.out llvmPackages_16.bintools # for running nac3standalone demos
(pkgs.wrapClangMulti llvmPackages_14.clang) llvmPackages_14.llvm.out # for running nac3standalone demos
packages.x86_64-linux.llvm-tools-irrt
cargo
rustc
# runtime dependencies
lld_16 # for running kernels on the host
lld_14 # for running kernels on the host
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ]))
# development tools
cargo-insta

View File

@ -2,18 +2,17 @@
name = "nac3artiq"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2024"
edition = "2021"
[lib]
name = "nac3artiq"
crate-type = ["cdylib"]
[dependencies]
indexmap = "2.8"
itertools = "0.14"
pyo3 = { version = "0.24", features = ["extension-module"] }
itertools = "0.13"
pyo3 = { version = "0.21", features = ["extension-module", "gil-refs"] }
parking_lot = "0.12"
tempfile = "3.19"
tempfile = "3.13"
nac3core = { path = "../nac3core" }
nac3ld = { path = "../nac3ld" }

View File

@ -1,84 +0,0 @@
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()

View File

@ -1,27 +0,0 @@
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()

View File

@ -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", "legacy_parallel", "sequential"
"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,46 +85,13 @@ def ceil64(x):
import device_db
core_arguments = device_db.device_db["core"]["arguments"]
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,
},
artiq_builtins = {
"none": none,
"virtual": virtual,
"_ConstGenericMarker": _ConstGenericMarker,
"Option": Option,
}
compiler = nac3artiq.NAC3(core_arguments["target"], builtins)
compiler = nac3artiq.NAC3(core_arguments["target"], artiq_builtins)
allow_registration = True
# Delay NAC3 analysis until all referenced variables are supposed to exist on the CPython side.
registered_functions = set()
@ -185,9 +152,9 @@ def nac3(cls):
return cls
ms: KernelInvariant[float] = 1e-3
us: KernelInvariant[float] = 1e-6
ns: KernelInvariant[float] = 1e-9
ms = 1e-3
us = 1e-6
ns = 1e-9
@extern
def rtio_init():
@ -278,7 +245,7 @@ class Core:
embedding = EmbeddingMap()
if allow_registration:
compiler.analyze(registered_functions, registered_classes, special_ids, set())
compiler.analyze(registered_functions, registered_classes, set())
allow_registration = False
if hasattr(method, "__self__"):
@ -368,12 +335,5 @@ class UnwrapNoneError(Exception):
"""raised when unwrapping a none value"""
artiq_builtin = True
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),
}
parallel = KernelContextManager()
sequential = KernelContextManager()

View File

@ -1,5 +1,5 @@
use std::{
collections::{HashMap, hash_map::DefaultHasher},
collections::{hash_map::DefaultHasher, HashMap},
hash::{Hash, Hasher},
iter::once,
mem,
@ -8,47 +8,42 @@ use std::{
use itertools::Itertools;
use pyo3::{
PyObject, PyResult, Python,
prelude::*,
types::{PyDict, PyList},
PyObject, PyResult, Python,
};
use super::{symbol_resolver::InnerResolver, timeline::TimeFns};
use nac3core::{
codegen::{
CodeGenContext, CodeGenerator,
expr::{create_fn_and_call, destructure_range, gen_call, infer_and_call_function},
expr::{destructure_range, gen_call},
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::{RangeType, ndarray::NDArrayType},
types::ndarray::NDArrayType,
values::{
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue,
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue, RangeValue,
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::{
DefinitionId, GenCall,
helper::{PrimDef, extract_ndims},
helper::{extract_ndims, PrimDef},
numpy::unpack_ndarray_var_tys,
DefinitionId, GenCall,
},
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 {
@ -88,9 +83,6 @@ 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> {
@ -98,7 +90,6 @@ 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 {
@ -109,7 +100,6 @@ impl<'a> ArtiqCodeGenerator<'a> {
end: None,
timeline,
parallel_mode: ParallelMode::None,
special_ids,
}
}
@ -119,10 +109,9 @@ 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, special_ids)
Self::new(name, llvm_usize, timeline)
}
/// If the generator is currently in a direct-`parallel` block context, emits IR that resets the
@ -191,7 +180,11 @@ 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>>>>(
@ -264,140 +257,122 @@ 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 {
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;
if id == &"parallel".into() || id == &"legacy_parallel".into() {
let old_start = self.start.take();
let old_end = self.end.take();
let old_parallel_mode = self.parallel_mode;
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(
ctx,
self,
old_start.custom.unwrap(),
)?
} else {
self.timeline.emit_now_mu(ctx)
};
// Emulate variable allocation, as we need to use the CodeGenContext
// HashMap to store our variable due to lifetime limitation
// Note: we should be able to store variables directly if generic
// associative type is used by limiting the lifetime of CodeGenerator to
// the LLVM Context.
// The name is guaranteed to be unique as users cannot use this as variable
// name.
self.start = old_start.clone().map_or_else(
|| {
let start = format!("with-{}-start", self.name_counter).into();
let start_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: start, ctx: *name_ctx },
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(
let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(
ctx,
self,
end_expr.custom.unwrap(),
)?;
old_start.custom.unwrap(),
)?
} else {
self.timeline.emit_now_mu(ctx)
};
// inside a sequential block
if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val);
}
// 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 parallel block, should update the outer max now_mu
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
self.gen_block(ctx, body.iter())?;
self.parallel_mode = old_parallel_mode;
self.end = old_end;
self.start = old_start;
let current = ctx.builder.get_insert_block().unwrap();
if reset_position {
ctx.builder.position_at_end(current);
}
// 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
};
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;
// 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(),
)?;
// 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 sequential block
if old_start.is_none() {
self.timeline.emit_at_mu(ctx, end_val);
}
// inside a parallel block, should update the outer max now_mu
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
self.parallel_mode = old_parallel_mode;
self.end = old_end;
self.start = old_start;
if reset_position {
ctx.builder.position_at_end(current);
}
return Ok(());
} else if id == &"sequential".into() {
// For deep parallel, temporarily take away start to avoid function calls in
// the block from resetting the timeline.
// This does not affect legacy parallel, as the timeline will be reset after
// this block finishes execution.
let start = self.start.take();
self.gen_block(ctx, body.iter())?;
self.start = start;
// Reset the timeline when we are exiting the sequential block
// Legacy parallel does not need this, since it will be reset after codegen
// for this statement is completed
if self.parallel_mode == ParallelMode::Deep {
self.timeline_reset_start(ctx)?;
}
return Ok(());
}
}
}
@ -414,7 +389,12 @@ fn gen_rpc_tag(
) -> Result<(), String> {
use nac3core::typecheck::typedef::TypeEnum::*;
let PrimitiveStore { int32, int64, float, bool, str, none, .. } = ctx.primitives;
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;
if ctx.unifier.unioned(ty, int32) {
buffer.push(b'i');
@ -450,10 +430,7 @@ 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();
@ -493,13 +470,14 @@ 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_pointer_value(arg.into_pointer_value(), None);
let ndarray =
NDArrayType::new(ctx, dtype, ndims).map_value(arg.into_pointer_value(), None);
let ndims = llvm_usize.const_int(ndims, false);
@ -510,11 +488,11 @@ fn format_rpc_arg<'ctx>(
let sizeof_usize = llvm_usize.size_of();
let sizeof_usize =
ctx.builder.build_int_truncate_or_bit_cast(sizeof_usize, llvm_usize, "").unwrap();
ctx.builder.build_int_z_extend_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_truncate_or_bit_cast(sizeof_pdata, llvm_usize, "").unwrap();
ctx.builder.build_int_z_extend_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();
@ -529,13 +507,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);
call_memcpy(ctx, buf_data, carray_data, sizeof_pdata, llvm_i1.const_zero());
// 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);
call_memcpy(ctx, buf_shape, carray_shape_i8, sizeof_buf_shape, llvm_i1.const_zero());
buf.base_ptr(ctx, generator)
}
@ -783,7 +761,7 @@ fn format_rpc_ret<'ctx>(
ctx.builder.build_unconditional_branch(head_bb).unwrap();
ctx.builder.position_at_end(tail_bb);
ndarray.as_abi_value(ctx).into()
ndarray.as_base_value().into()
}
_ => {
@ -835,7 +813,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() {
@ -858,7 +836,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<_>>(),
@ -936,14 +914,47 @@ fn rpc_codegen_callback_fn<'ctx>(
}
// call
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,
);
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();
}
// reclaim stack space used by arguments
call_stackrestore(ctx, stackptr);
@ -971,7 +982,7 @@ pub fn attributes_writeback<'ctx>(
return_obj: Option<(Type, ValueEnum<'ctx>)>,
) -> Result<(), String> {
Python::with_gil(|py| -> PyResult<Result<(), String>> {
let host_attributes = host_attributes.downcast_bound::<PyList>(py)?;
let host_attributes: &PyList = host_attributes.downcast(py)?;
let top_levels = ctx.top_level.definitions.read();
let globals = inner_resolver.global_value_ids.read();
let int32 = ctx.ctx.i32_type();
@ -984,7 +995,7 @@ pub fn attributes_writeback<'ctx>(
}
for val in (*globals).values() {
let val = val.bind(py);
let val = val.as_ref(py);
let ty = inner_resolver.get_obj_type(
py,
val,
@ -1015,7 +1026,7 @@ pub fn attributes_writeback<'ctx>(
*field_ty,
ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index.unwrap() as u64, false)],
&[zero, int32.const_int(index as u64, false)],
None,
),
));
@ -1056,7 +1067,7 @@ pub fn attributes_writeback<'ctx>(
*field_ty,
ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index.unwrap() as u64, false)],
&[zero, int32.const_int(index as u64, false)],
None,
),
));
@ -1157,22 +1168,29 @@ fn polymorphic_print<'ctx>(
debug_assert!(!fmt.is_empty());
debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8);
let llvm_i32 = ctx.ctx.i32_type();
let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
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 fmt = ctx.gen_string(generator, fmt);
let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value();
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,
);
ctx.builder
.build_call(
print_fn,
&once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(),
"",
)
.unwrap();
};
let llvm_i32 = ctx.ctx.i32_type();
@ -1365,7 +1383,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_pointer_value(value.into_pointer_value(), None);
.map_value(value.into_pointer_value(), None);
let num_0 = llvm_usize.const_zero();
@ -1413,7 +1431,7 @@ fn polymorphic_print<'ctx>(
fmt.push_str("range(");
flush(ctx, generator, &mut fmt, &mut args);
let val = RangeType::new(ctx).map_pointer_value(value.into_pointer_value(), None);
let val = RangeValue::from_pointer_value(value.into_pointer_value(), None);
let (start, stop, step) = destructure_range(ctx, val);

View File

@ -1,273 +0,0 @@
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
}
}

View File

@ -1,6 +1,7 @@
#![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,
@ -10,21 +11,18 @@
)]
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::{
IntoPyObjectExt, create_exception, exceptions,
create_exception, exceptions,
prelude::*,
types::{PyBytes, PyDict, PyNone, PySet},
};
@ -32,17 +30,17 @@ use tempfile::{self, TempDir};
use nac3core::{
codegen::{
CodeGenLLVMOptions, CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, WithCall,
WorkerRegistry, concrete_type::ConcreteTypeStore, gen_func_impl, irrt::load_irrt,
concrete_type::ConcreteTypeStore, gen_func_impl, irrt::load_irrt, CodeGenLLVMOptions,
CodeGenTargetMachineOptions, CodeGenTask, CodeGenerator, WithCall, WorkerRegistry,
},
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},
@ -50,31 +48,27 @@ 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::{FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap, into_var_map},
typedef::{into_var_map, FunSignature, FuncArg, Type, TypeEnum, Unifier, VarMap},
},
};
use nac3ld::Linker;
use codegen::{
ArtiqCodeGenerator, attributes_writeback, gen_core_log, gen_rtio_log, rpc_codegen_callback,
attributes_writeback, gen_core_log, gen_rtio_log, rpc_codegen_callback, ArtiqCodeGenerator,
};
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,
@ -89,7 +83,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-eabihf"),
Isa::CortexA9 => TargetTriple::create("armv7-unknown-linux-gnueabihf"),
}
}
@ -166,21 +160,9 @@ pub struct PrimitivePythonId {
virtual_id: u64,
option: u64,
module: u64,
kernel: u64,
kernel_invariant: u64,
}
#[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>);
type TopLevelComponent = (Stmt, String, PyObject);
// TopLevelComposer is unsendable as it holds the unification table, which is
// unsendable due to Rc. Arc would cause a performance hit.
@ -197,7 +179,6 @@ 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,
}
@ -207,17 +188,17 @@ create_exception!(nac3artiq, CompileError, exceptions::PyException);
impl Nac3 {
fn register_module(
&mut self,
module: &Arc<PyObject>,
module: &PyObject,
registered_class_ids: &HashSet<u64>,
) -> PyResult<()> {
let (module_name, source_file, source) =
Python::with_gil(|py| -> PyResult<(String, String, String)> {
let module = module.bind(py);
let module: &PyAny = module.extract(py)?;
let source_file = module.getattr("__file__");
let (source_file, source) = if let Ok(source_file) = source_file {
let source_file = source_file.extract::<&str>()?;
let source_file = source_file.extract()?;
(
source_file.to_string(),
source_file,
fs::read_to_string(source_file).map_err(|e| {
exceptions::PyIOError::new_err(format!(
"failed to read input file: {e}"
@ -227,26 +208,18 @@ impl Nac3 {
} else {
// kernels submitted by content have no file
// but still can provide source by StringLoader
let get_src_fn = module.getattr("__loader__")?.getattr("get_source")?;
(String::from("<expcontent>"), get_src_fn.call1((PyNone::get(py),))?.extract()?)
let get_src_fn = module
.getattr("__loader__")?
.extract::<PyObject>()?
.getattr(py, "get_source")?;
("<expcontent>", get_src_fn.call1(py, (PyNone::get(py),))?.extract(py)?)
};
Ok((module.getattr("__name__")?.extract()?, source_file, source))
Ok((module.getattr("__name__")?.extract()?, source_file.to_string(), 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, .. } => {
@ -263,15 +236,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.bind(py).getattr(id.to_string().as_str())?;
let base_id =
id_fn.bind(py).call1((base_obj,))?.extract()?;
module.getattr(py, id.to_string().as_str())?;
let base_id = id_fn.call1((base_obj,))?.extract()?;
Ok(registered_class_ids.contains(&base_id))
}
}
@ -304,28 +277,10 @@ impl Nac3 {
}
})
}
// 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,
},
// 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()))
}
_ => false,
};
@ -358,7 +313,7 @@ impl Nac3 {
None => {
return Some(format!(
"object launching kernel does not have method `{method_name}`"
));
))
}
}
} else {
@ -379,7 +334,7 @@ impl Nac3 {
None if default_value.is_none() => {
return Some(format!(
"argument `{name}` not provided when launching kernel function"
));
))
}
_ => break,
};
@ -393,7 +348,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) {
@ -467,13 +422,13 @@ impl Nac3 {
]
}
fn compile_method<'py, T>(
fn compile_method<T>(
&self,
obj: &Bound<'py, PyAny>,
obj: &PyAny,
method_name: &str,
args: Vec<Bound<'py, PyAny>>,
embedding_map: &Bound<'py, PyAny>,
py: Python<'py>,
args: Vec<&PyAny>,
embedding_map: &PyAny,
py: Python,
link_fn: &dyn Fn(&Module) -> PyResult<T>,
) -> PyResult<T> {
let size_t = self.isa.get_size_type(&Context::create());
@ -489,20 +444,19 @@ 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();
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 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 global_value_ids: Arc<RwLock<HashMap<_, _>>> = Arc::new(RwLock::new(HashMap::new()));
let helper = PythonHelper {
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)?),
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,
};
let pyid_to_type = Arc::new(RwLock::new(HashMap::<u64, Type>::new()));
@ -525,14 +479,14 @@ impl Nac3 {
let mut rpc_ids = vec![];
for (stmt, path, module) in &self.top_levels {
let py_module = module.bind(py);
let py_module: &PyAny = module.extract(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);
@ -545,8 +499,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 = py_module.getattr("__dict__").unwrap();
let members = members.downcast::<PyDict>().unwrap();
let members: &PyDict =
py_module.getattr("__dict__").unwrap().downcast().unwrap();
for (key, val) in members {
let key: &str = key.extract().unwrap();
let val = id_fn.call1((val,)).unwrap().extract().unwrap();
@ -592,79 +546,52 @@ impl Nac3 {
if let Some(class_obj) = class_obj {
self.exception_ids
.write()
.insert(def_id.0, store_obj.call1((class_obj,))?.extract()?);
.insert(def_id.0, store_obj.call1(py, (class_obj,))?.extract(py)?);
}
match &stmt.node {
StmtKind::FunctionDef { decorator_list, .. } => {
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();
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())
}) {
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));
} 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
)));
}
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));
}
}
}
@ -708,7 +635,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);
}
@ -730,7 +657,7 @@ impl Nac3 {
id_to_primitive: RwLock::default(),
field_to_val: RwLock::default(),
name_to_pyid,
module: Arc::new(module.into_py_any(py)?),
module: module.to_object(py),
helper: helper.clone(),
string_store: self.string_store.clone(),
exception_ids: self.exception_ids.clone(),
@ -804,8 +731,10 @@ impl Nac3 {
.call1(
py,
(
id.0.into_py_any(py)?,
class_def.getattr(name.to_string().as_str()).unwrap(),
id.0.into_py(py),
class_def
.getattr(py, name.to_string().as_str())
.unwrap(),
),
)
.unwrap();
@ -815,7 +744,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")
@ -868,7 +797,6 @@ impl Nac3 {
&context,
&self.get_llvm_target_machine(),
self.time_fns,
self.special_ids.clone(),
))
})
.collect();
@ -885,7 +813,6 @@ 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();
@ -939,18 +866,6 @@ 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
@ -959,8 +874,6 @@ 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"))
@ -968,10 +881,7 @@ 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 {
@ -991,8 +901,6 @@ impl Nac3 {
global_option = global.get_next_global();
}
emit_llvm(&main, "main.pre-opt");
let target_machine = self
.llvm_options
.target
@ -1007,15 +915,12 @@ 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.bind(py).call1((s,)).unwrap().extract().unwrap();
let embed_key: i32 = helper.store_str.call1(py, (s,)).unwrap().extract(py).unwrap();
assert_eq!(
embed_key, *key,
"string {s} is out of sync between embedding map (key={embed_key}) and \
@ -1125,7 +1030,7 @@ fn add_exceptions(
#[pymethods]
impl Nac3 {
#[new]
fn new<'py>(isa: &str, artiq_builtins: &Bound<'py, PyDict>, py: Python<'py>) -> PyResult<Self> {
fn new(isa: &str, artiq_builtins: &PyDict, py: Python) -> PyResult<Self> {
let isa = match isa {
"host" => Isa::Host,
"rv32g" => Isa::RiscV32G,
@ -1192,59 +1097,42 @@ 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: &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 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 primitive_ids = PrimitivePythonId {
virtual_id: get_id(&get_artiq_builtin(Some("artiq"), "virtual")),
virtual_id: get_id(artiq_builtins.get_item("virtual").ok().flatten().unwrap()),
generic_alias: (
get_id(&get_artiq_builtin(Some("typing"), "_GenericAlias")),
get_id(&get_artiq_builtin(Some("types"), "GenericAlias")),
get_attr_id(typing_mod, "_GenericAlias"),
get_attr_id(types_mod, "GenericAlias"),
),
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")),
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"),
};
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
@ -1304,7 +1192,6 @@ 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(),
@ -1312,45 +1199,36 @@ impl Nac3 {
})
}
fn analyze<'py>(
fn analyze(
&mut self,
functions: &Bound<'py, PySet>,
classes: &Bound<'py, PySet>,
special_ids: &Bound<'py, PyDict>,
content_modules: &Bound<'py, PySet>,
functions: &PySet,
classes: &PySet,
content_modules: &PySet,
) -> PyResult<()> {
let (modules, class_ids) =
Python::with_gil(|py| -> PyResult<(PyValueMap, HashSet<u64>)> {
let mut modules: IndexMap<u64, Arc<PyObject>> = IndexMap::new();
Python::with_gil(|py| -> PyResult<(HashMap<u64, PyObject>, HashSet<u64>)> {
let mut modules: HashMap<u64, PyObject> = HashMap::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 = getmodule_fn.call1((&function,))?;
if !module.is_none() {
modules.insert(
id_fn.call1((&module,))?.extract()?,
Arc::new(module.into_py_any(py)?),
);
let module: PyObject = getmodule_fn.call1((function,))?.extract()?;
if !module.is_none(py) {
modules.insert(id_fn.call1((&module,))?.extract()?, module);
}
}
for class in classes {
let module = getmodule_fn.call1((&class,))?;
if !module.is_none() {
modules.insert(
id_fn.call1((&module,))?.extract()?,
Arc::new(module.into_py_any(py)?),
);
let module: PyObject = getmodule_fn.call1((class,))?.extract()?;
if !module.is_none(py) {
modules.insert(id_fn.call1((&module,))?.extract()?, module);
}
class_ids.insert(id_fn.call1((&class,))?.extract()?);
class_ids.insert(id_fn.call1((class,))?.extract()?);
}
for module in content_modules {
modules.insert(
id_fn.call1((&module,))?.extract()?,
Arc::new(module.into_py_any(py)?),
);
let module: PyObject = module.extract()?;
modules.insert(id_fn.call1((&module,))?.extract()?, module);
}
Ok((modules, class_ids))
})?;
@ -1358,40 +1236,22 @@ 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<'py>(
fn compile_method_to_file(
&mut self,
obj: &Bound<'py, PyAny>,
obj: &PyAny,
method_name: &str,
args: Vec<Bound<'py, PyAny>>,
args: Vec<&PyAny>,
filename: &str,
embedding_map: &Bound<'py, PyAny>,
py: Python<'py>,
embedding_map: &PyAny,
py: Python,
) -> PyResult<()> {
let target_machine = self.get_llvm_target_machine();
let link_fn = |module: &Module| {
if self.isa == Isa::Host {
if self.isa == Isa::Host {
let link_fn = |module: &Module| {
let working_directory = self.working_directory.path().to_owned();
target_machine
.write_to_file(module, FileType::Object, &working_directory.join("module.o"))
@ -1401,7 +1261,11 @@ impl Nac3 {
working_directory.join("module.o").to_string_lossy().to_string(),
)?;
Ok(())
} else {
};
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
} else {
let link_fn = |module: &Module| {
let object_mem = target_machine
.write_to_memory_buffer(module, FileType::Object)
.expect("couldn't write module to object file buffer");
@ -1415,23 +1279,24 @@ 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<'py>(
fn compile_method_to_mem(
&mut self,
obj: &Bound<'py, PyAny>,
obj: &PyAny,
method_name: &str,
args: Vec<Bound<'py, PyAny>>,
embedding_map: &Bound<'py, PyAny>,
py: Python<'py>,
args: Vec<&PyAny>,
embedding_map: &PyAny,
py: Python,
) -> PyResult<PyObject> {
let target_machine = self.get_llvm_target_machine();
let link_fn = |module: &Module| {
if self.isa == Isa::Host {
if self.isa == Isa::Host {
let link_fn = |module: &Module| {
let working_directory = self.working_directory.path().to_owned();
target_machine
.write_to_file(module, FileType::Object, &working_directory.join("module.o"))
@ -1445,7 +1310,11 @@ impl Nac3 {
)?;
Ok(PyBytes::new(py, &fs::read(filename).unwrap()).into())
} else {
};
self.compile_method(obj, method_name, args, embedding_map, py, &link_fn)
} else {
let link_fn = |module: &Module| {
let object_mem = target_machine
.write_to_memory_buffer(module, FileType::Object)
.expect("couldn't write module to object file buffer");
@ -1454,20 +1323,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")]
unsafe extern "C" {
extern "C" {
fn __llvm_profile_initialize();
}
#[pymodule]
fn nac3artiq<'py>(py: Python<'py>, m: &Bound<'py, PyModule>) -> PyResult<()> {
fn nac3artiq(py: Python, m: &PyModule) -> PyResult<()> {
#[cfg(feature = "init-llvm-profile")]
unsafe {
__llvm_profile_initialize();

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,11 @@
use itertools::Either;
use nac3core::{
codegen::{CodeGenContext, expr::infer_and_call_function},
inkwell::{AddressSpace, AtomicOrdering, values::BasicValueEnum},
codegen::CodeGenContext,
inkwell::{
values::{BasicValueEnum, CallSiteValue},
AddressSpace, AtomicOrdering,
},
};
/// Functions for manipulating the timeline.
@ -283,27 +288,36 @@ pub struct ExternTimeFns {}
impl TimeFns for ExternTimeFns {
fn emit_now_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>) -> BasicValueEnum<'ctx> {
infer_and_call_function(
ctx,
"now_mu",
Some(ctx.ctx.i64_type().into()),
&[],
Some("now_mu"),
None,
)
.unwrap()
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()
}
fn emit_at_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, t: BasicValueEnum<'ctx>) {
assert_eq!(t.get_type(), ctx.ctx.i64_type().into());
infer_and_call_function(ctx, "at_mu", None, &[t], Some("at_mu"), None);
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();
}
fn emit_delay_mu<'ctx>(&self, ctx: &mut CodeGenContext<'ctx, '_>, dt: BasicValueEnum<'ctx>) {
assert_eq!(dt.get_type(), ctx.ctx.i64_type().into());
infer_and_call_function(ctx, "delay_mu", None, &[dt], Some("delay_mu"), None);
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();
}
}

View File

@ -2,7 +2,7 @@
name = "nac3ast"
version = "0.1.0"
authors = ["RustPython Team", "M-Labs"]
edition = "2024"
edition = "2021"
[features]
default = ["constant-optimization", "fold"]
@ -11,5 +11,5 @@ fold = []
[dependencies]
parking_lot = "0.12"
string-interner = "0.19"
string-interner = "0.17"
fxhash = "0.2"

View File

@ -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::{DefaultBackend, StringInterner, symbol::SymbolU32};
use string_interner::{symbol::SymbolU32, DefaultBackend, StringInterner};
pub type Interner = StringInterner<DefaultBackend, FxBuildHasher>;
static INTERNER: LazyLock<Mutex<Interner>> =

View File

@ -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;

View File

@ -2,7 +2,7 @@
name = "nac3core"
version = "0.1.0"
authors = ["M-Labs"]
edition = "2024"
edition = "2021"
[features]
default = ["derive"]
@ -10,26 +10,25 @@ derive = ["dep:nac3core_derive"]
no-escape-analysis = []
[dependencies]
itertools = "0.14"
itertools = "0.13"
crossbeam = "0.8"
indexmap = "2.8"
indexmap = "2.6"
parking_lot = "0.12"
rayon = "1.10"
nac3core_derive = { path = "nac3core_derive", optional = true }
nac3parser = { path = "../nac3parser" }
strum = "0.27"
strum_macros = "0.27"
strum = "0.26"
strum_macros = "0.26"
[dependencies.inkwell]
git = "https://github.com/Derppening/inkwell"
tag = "0.5.0_llvm15-typed-ptr"
version = "0.5"
default-features = false
features = ["llvm16-0-prefer-dynamic", "target-x86", "target-arm", "target-riscv", "no-libffi-linking", "typed-pointers"]
features = ["llvm14-0-prefer-dynamic", "target-x86", "target-arm", "target-riscv", "no-libffi-linking"]
[dev-dependencies]
test-case = "3.3"
test-case = "1.2.0"
indoc = "2.0"
insta = "1.42"
function_name = "0.3"
insta = "=1.11.0"
[build-dependencies]
regex = "1.11"
regex = "1.10"

View File

@ -28,8 +28,6 @@ fn main() {
"-fno-exceptions",
"-fno-rtti",
"-emit-llvm",
"-Xclang",
"-no-opaque-pointers",
"-S",
"-Wall",
"-Wextra",
@ -102,7 +100,6 @@ 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()

View File

@ -1,4 +1,3 @@
#include "irrt/cc-builtins.hpp"
#include "irrt/exception.hpp"
#include "irrt/list.hpp"
#include "irrt/math.hpp"

View File

@ -1,34 +0,0 @@
#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);
}

View File

@ -34,6 +34,39 @@ 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
@ -43,7 +76,7 @@ double __nac3_gammaln(double x) {
return x;
}
return __builtin_lgamma(x);
return lgamma(x);
}
double j0(double x);

View File

@ -1,7 +1,7 @@
[package]
name = "nac3core_derive"
version = "0.1.0"
edition = "2024"
edition = "2021"
[lib]
proc-macro = true

View File

@ -2,8 +2,8 @@ use proc_macro::TokenStream;
use proc_macro_error::{abort, proc_macro_error};
use quote::quote;
use syn::{
Data, DataStruct, Expr, ExprField, ExprMethodCall, ExprPath, GenericArgument, Ident, LitStr,
Path, PathArguments, Type, TypePath, parse_macro_input, spanned::Spanned,
parse_macro_input, spanned::Spanned, Data, DataStruct, Expr, ExprField, ExprMethodCall,
ExprPath, GenericArgument, Ident, LitStr, Path, PathArguments, Type, TypePath,
};
/// Extracts all generic arguments of a [`Type`] into a [`Vec`].
@ -59,7 +59,11 @@ 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(),
@ -101,7 +105,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(),
)
)
@ -150,7 +154,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(),
)
)
@ -220,9 +224,10 @@ 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");
};

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,8 +1,8 @@
use nac3core::{
codegen::types::structure::StructField,
inkwell::{
AddressSpace,
values::{IntValue, PointerValue},
AddressSpace,
},
};
use nac3core_derive::StructFields;

View File

@ -1,26 +1,26 @@
use inkwell::{
FloatPredicate, IntPredicate, OptimizationLevel,
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
values::{BasicValue, BasicValueEnum, IntValue},
FloatPredicate, IntPredicate, OptimizationLevel,
};
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::{ListType, RangeType, TupleType, ndarray::NDArrayType},
types::{ndarray::NDArrayType, ListType, TupleType},
values::{
ProxyValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
ProxyValue, RangeValue, TypedArrayLikeAccessor, UntypedArrayLikeAccessor,
},
CodeGenContext, CodeGenerator,
};
use crate::{
toplevel::{
helper::{PrimDef, arraylike_flatten_element_type, extract_ndims},
helper::{arraylike_flatten_element_type, extract_ndims, PrimDef},
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 = RangeType::new(ctx).map_pointer_value(arg.into_pointer_value(), Some("range"));
let arg = RangeValue::from_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_struct_value(arg.into_struct_value(), None);
.map_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_pointer_value(arg.into_pointer_value(), None);
.map_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_pointer_value(arg.into_pointer_value(), None);
.map_value(arg.into_pointer_value(), None);
ctx.builder
.build_int_truncate_or_bit_cast(list.load_size(ctx, None), llvm_i32, "len")
.unwrap()
@ -99,21 +99,17 @@ 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()
}
@ -130,8 +126,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -142,7 +137,7 @@ pub fn call_int32<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, "int32", &[n_ty]),
@ -159,11 +154,9 @@ 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()
@ -173,11 +166,9 @@ 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()
}
@ -195,8 +186,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -207,7 +197,7 @@ pub fn call_int64<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, "int64", &[n_ty]),
@ -230,11 +220,9 @@ 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()
}
@ -274,8 +262,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -286,7 +273,7 @@ pub fn call_uint32<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, "uint32", &[n_ty]),
@ -303,11 +290,9 @@ 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()
@ -317,11 +302,9 @@ 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()
}
@ -344,8 +327,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -356,7 +338,7 @@ pub fn call_uint64<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, "uint64", &[n_ty]),
@ -373,17 +355,15 @@ 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()
@ -411,8 +391,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -423,7 +402,7 @@ pub fn call_float<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, "float", &[n_ty]),
@ -456,8 +435,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -470,7 +448,7 @@ pub fn call_round<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -496,8 +474,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -508,7 +485,7 @@ pub fn call_numpy_round<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -531,16 +508,14 @@ 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)
@ -561,8 +536,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -576,7 +550,7 @@ pub fn call_bool<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -613,8 +587,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -627,7 +600,7 @@ pub fn call_floor<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -664,8 +637,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, n_ty).map_value(n, None);
let result = ndarray
.map(
@ -678,7 +650,7 @@ pub fn call_ceil<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, FN_NAME, &[n_ty]),
@ -701,17 +673,15 @@ 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()
@ -746,18 +716,16 @@ 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()))
}
@ -799,7 +767,7 @@ pub fn call_numpy_minimum<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
@ -822,17 +790,15 @@ 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()
@ -869,18 +835,16 @@ 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(),
@ -894,8 +858,7 @@ 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_pointer_value(n, None);
let ndarray = NDArrayType::from_unifier_type(generator, ctx, a_ty).map_value(n, None);
let llvm_dtype = ndarray.get_type().element_type();
let zero = llvm_usize.const_zero();
@ -1012,18 +975,16 @@ 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()))
}
@ -1065,7 +1026,7 @@ pub fn call_numpy_maximum<'ctx, G: CodeGenerator + ?Sized>(
)
.unwrap();
result.as_abi_value(ctx).into()
result.as_base_value().into()
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
@ -1129,17 +1090,15 @@ 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()
@ -1224,7 +1183,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(ctx, n, Option::<&str>::None);
let ret = $on_scalar(generator, ctx, n);
Some(generator.bool_to_i8(ctx, ret).into())
}
_ => None,
@ -1293,55 +1252,55 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_tan,
"np_tan",
irrt::call_tan
extern_fns::call_tan
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arcsin,
"np_arcsin",
irrt::call_asin
extern_fns::call_asin
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arccos,
"np_arccos",
irrt::call_acos
extern_fns::call_acos
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arctan,
"np_arctan",
irrt::call_atan
extern_fns::call_atan
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_sinh,
"np_sinh",
irrt::call_sinh
extern_fns::call_sinh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_cosh,
"np_cosh",
irrt::call_cosh
extern_fns::call_cosh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_tanh,
"np_tanh",
irrt::call_tanh
extern_fns::call_tanh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arcsinh,
"np_arcsinh",
irrt::call_asinh
extern_fns::call_asinh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arccosh,
"np_arccosh",
irrt::call_acosh
extern_fns::call_acosh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_arctanh,
"np_arctanh",
irrt::call_atanh
extern_fns::call_atanh
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
@ -1357,7 +1316,7 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_expm1,
"np_expm1",
irrt::call_expm1
extern_fns::call_expm1
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
@ -1384,7 +1343,7 @@ create_helper_call_numpy_unary_elementwise_float_to_float!(
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_numpy_cbrt,
"np_cbrt",
irrt::call_cbrt
extern_fns::call_cbrt
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
@ -1401,17 +1360,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",
irrt::call_erf
extern_fns::call_erf
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_scipy_special_erfc,
"sp_spec_erfc",
irrt::call_erfc
extern_fns::call_erfc
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_scipy_special_gamma,
"sp_spec_gamma",
irrt::call_gamma
|ctx, val, _| irrt::call_gamma(ctx, val)
);
create_helper_call_numpy_unary_elementwise_float_to_float!(
call_scipy_special_gammaln,
@ -1452,7 +1411,7 @@ pub fn call_numpy_arctan2<'ctx, G: CodeGenerator + ?Sized>(
match (x1_scalar, x2_scalar) {
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
Ok(irrt::call_atan2(ctx, x1, x2, None).into())
Ok(extern_fns::call_atan2(ctx, x1, x2, None).into())
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
}
@ -1590,7 +1549,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(irrt::call_ldexp(ctx, x1_scalar, x2_scalar, None).into())
Ok(extern_fns::call_ldexp(ctx, x1_scalar, x2_scalar, None).into())
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
}
@ -1624,7 +1583,7 @@ pub fn call_numpy_hypot<'ctx, G: CodeGenerator + ?Sized>(
match (x1_scalar, x2_scalar) {
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
Ok(irrt::call_hypot(ctx, x1, x2, None).into())
Ok(extern_fns::call_hypot(ctx, x1, x2, None).into())
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
}
@ -1658,7 +1617,7 @@ pub fn call_numpy_nextafter<'ctx, G: CodeGenerator + ?Sized>(
match (x1_scalar, x2_scalar) {
(BasicValueEnum::FloatValue(x1), BasicValueEnum::FloatValue(x2)) => {
Ok(irrt::call_nextafter(ctx, x1, x2, None).into())
Ok(extern_fns::call_nextafter(ctx, x1, x2, None).into())
}
_ => unsupported_type(ctx, FN_NAME, &[x1_ty, x2_ty]),
}
@ -1679,7 +1638,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1694,11 +1653,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_abi_value(ctx).into(),
out_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
out_c.as_base_value().into(),
None,
);
Ok(out.as_abi_value(ctx).into())
Ok(out.as_base_value().into())
}
/// Invokes the `np_linalg_qr` linalg function
@ -1713,7 +1672,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1740,20 +1699,20 @@ pub fn call_np_linalg_qr<'ctx, G: CodeGenerator + ?Sized>(
extern_fns::call_np_linalg_qr(
ctx,
x1_c.as_abi_value(ctx).into(),
q_c.as_abi_value(ctx).into(),
r_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
q_c.as_base_value().into(),
r_c.as_base_value().into(),
None,
);
let q = q.as_abi_value(ctx);
let r = r.as_abi_value(ctx);
let q = q.as_base_value().as_basic_value_enum();
let r = r.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[q.get_type(), r.get_type()]).construct_from_objects(
ctx,
[q.into(), r.into()],
[q, r],
None,
);
Ok(tuple.as_abi_value(ctx).into())
Ok(tuple.as_base_value().into())
}
/// Invokes the `np_linalg_svd` linalg function
@ -1768,7 +1727,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1801,19 +1760,19 @@ pub fn call_np_linalg_svd<'ctx, G: CodeGenerator + ?Sized>(
extern_fns::call_np_linalg_svd(
ctx,
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(),
x1_c.as_base_value().into(),
u_c.as_base_value().into(),
s_c.as_base_value().into(),
vh_c.as_base_value().into(),
None,
);
let u = u.as_abi_value(ctx);
let s = s.as_abi_value(ctx);
let vh = vh.as_abi_value(ctx);
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 tuple = TupleType::new(ctx, &[u.get_type(), s.get_type(), vh.get_type()])
.construct_from_objects(ctx, [u.into(), s.into(), vh.into()], None);
Ok(tuple.as_abi_value(ctx).into())
.construct_from_objects(ctx, [u, s, vh], None);
Ok(tuple.as_base_value().into())
}
/// Invokes the `np_linalg_inv` linalg function
@ -1826,7 +1785,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1841,12 +1800,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_abi_value(ctx).into(),
out_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
out_c.as_base_value().into(),
None,
);
Ok(out.as_abi_value(ctx).into())
Ok(out.as_base_value().into())
}
/// Invokes the `np_linalg_pinv` linalg function
@ -1861,7 +1820,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1886,12 +1845,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_abi_value(ctx).into(),
out_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
out_c.as_base_value().into(),
None,
);
Ok(out.as_abi_value(ctx).into())
Ok(out.as_base_value().into())
}
/// Invokes the `sp_linalg_lu` linalg function
@ -1906,7 +1865,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -1933,20 +1892,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_abi_value(ctx).into(),
l_c.as_abi_value(ctx).into(),
u_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
l_c.as_base_value().into(),
u_c.as_base_value().into(),
None,
);
let l = l.as_abi_value(ctx);
let u = u.as_abi_value(ctx);
let l = l.as_base_value().as_basic_value_enum();
let u = u.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[l.get_type(), u.get_type()]).construct_from_objects(
ctx,
[l.into(), u.into()],
[l, u],
None,
);
Ok(tuple.as_abi_value(ctx).into())
Ok(tuple.as_base_value().into())
}
/// Invokes the `np_linalg_matrix_power` linalg function
@ -1994,13 +1953,13 @@ pub fn call_np_linalg_matrix_power<'ctx, G: CodeGenerator + ?Sized>(
extern_fns::call_np_linalg_matrix_power(
ctx,
x1_c.as_abi_value(ctx).into(),
x2_c.as_abi_value(ctx).into(),
out_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
x2_c.as_base_value().into(),
out_c.as_base_value().into(),
None,
);
Ok(out.as_abi_value(ctx).into())
Ok(out.as_base_value().into())
}
/// Invokes the `np_linalg_det` linalg function
@ -2015,7 +1974,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
if !x1.get_type().element_type().is_float_type() {
unsupported_type(ctx, FN_NAME, &[x1_ty]);
@ -2034,8 +1993,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_abi_value(ctx).into(),
out_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
out_c.as_base_value().into(),
None,
);
@ -2054,7 +2013,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
assert_eq!(x1.get_type().ndims(), 2);
if !x1.get_type().element_type().is_float_type() {
@ -2076,20 +2035,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_abi_value(ctx).into(),
t_c.as_abi_value(ctx).into(),
z_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
t_c.as_base_value().into(),
z_c.as_base_value().into(),
None,
);
let t = t.as_abi_value(ctx);
let z = z.as_abi_value(ctx);
let t = t.as_base_value().as_basic_value_enum();
let z = z.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[t.get_type(), z.get_type()]).construct_from_objects(
ctx,
[t.into(), z.into()],
[t, z],
None,
);
Ok(tuple.as_abi_value(ctx).into())
Ok(tuple.as_base_value().into())
}
/// Invokes the `sp_linalg_hessenberg` linalg function
@ -2102,7 +2061,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_pointer_value(x1, None);
let x1 = NDArrayType::from_unifier_type(generator, ctx, x1_ty).map_value(x1, None);
assert_eq!(x1.get_type().ndims(), 2);
if !x1.get_type().element_type().is_float_type() {
@ -2124,18 +2083,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_abi_value(ctx).into(),
h_c.as_abi_value(ctx).into(),
q_c.as_abi_value(ctx).into(),
x1_c.as_base_value().into(),
h_c.as_base_value().into(),
q_c.as_base_value().into(),
None,
);
let h = h.as_abi_value(ctx);
let q = q.as_abi_value(ctx);
let h = h.as_base_value().as_basic_value_enum();
let q = q.as_base_value().as_basic_value_enum();
let tuple = TupleType::new(ctx, &[h.get_type(), q.get_type()]).construct_from_objects(
ctx,
[h.into(), q.into()],
[h, q],
None,
);
Ok(tuple.as_abi_value(ctx).into())
Ok(tuple.as_base_value().into())
}

View File

@ -10,7 +10,7 @@ use crate::{
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{
FunSignature, FuncArg, Type, TypeEnum, TypeVar, TypeVarId, Unifier, into_var_map,
into_var_map, FunSignature, FuncArg, Type, TypeEnum, TypeVar, TypeVarId, Unifier,
},
},
};

View File

@ -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::{Either, Itertools, izip};
use itertools::{izip, Either, Itertools};
use nac3parser::ast::{
self, Boolop, Cmpop, Comprehension, Constant, Expr, ExprKind, Location, Operator, StrRef,
@ -19,7 +19,6 @@ 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::*,
@ -33,21 +32,20 @@ use super::{
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
gen_var,
},
types::{
ExceptionType, ListType, OptionType, RangeType, StringType, TupleType, ndarray::NDArrayType,
},
types::{ndarray::NDArrayType, ListType},
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::{
DefinitionId, TopLevelDef,
helper::{PrimDef, arraylike_flatten_element_type, extract_ndims},
helper::{arraylike_flatten_element_type, PrimDef},
numpy::unpack_ndarray_var_tys,
DefinitionId, TopLevelDef,
},
typecheck::{
magic_methods::{Binop, BinopVariant, HasOpInfo},
@ -73,7 +71,7 @@ pub fn get_subst_key(
})
.unwrap_or_default();
vars.extend(fun_vars);
let sorted = vars.keys().filter(|id| filter.is_none_or(|v| v.contains(id))).sorted();
let sorted = vars.keys().filter(|id| filter.map_or(true, |v| v.contains(id))).sorted();
sorted
.map(|id| {
unifier.internal_stringify(
@ -124,7 +122,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) -> (Option<usize>, Option<Constant>) {
pub fn get_attr_index(&mut self, ty: Type, attr: StrRef) -> (usize, Option<Constant>) {
let obj_id = match &*self.unifier.get_ty(ty) {
TypeEnum::TObj { obj_id, .. } => *obj_id,
TypeEnum::TModule { module_id, .. } => *module_id,
@ -134,16 +132,13 @@ 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) {
(Some(field_index.0), None)
(field_index.0, None)
} else {
let attribute_index = attributes.iter().find_position(|x| x.0 == attr);
(
attribute_index.map(|(idx, _)| idx),
attribute_index.map(|(_, (_, _, k))| k.clone()),
)
let attribute_index = attributes.iter().find_position(|x| x.0 == attr).unwrap();
(attribute_index.0, Some(attribute_index.1 .2.clone()))
}
} else if let TopLevelDef::Module { attributes, .. } = &*def.read() {
(attributes.iter().find_position(|x| x.0 == attr).map(|(idx, _)| idx), None)
(attributes.iter().find_position(|x| x.0 == attr).unwrap().0, None)
} else {
codegen_unreachable!(self)
};
@ -173,27 +168,65 @@ 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) => {
StringType::new(self).construct_constant(self, v, None).as_abi_value(self).into()
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()
}
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();
TupleType::new(self, &fields)
.construct_from_objects(self, vals, Some("tup_val"))
.as_abi_value(self)
.into()
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()
}
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);
OptionType::from_unifier_type(generator, self, ty)
.construct_some_value(generator, self, &val, None)
.as_abi_value(self)
.into()
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()
}
SymbolValue::OptionNone => OptionType::from_unifier_type(generator, self, ty)
.construct_empty(generator, self, None)
.as_abi_value(self)
.into(),
}
}
@ -288,10 +321,15 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
if let Some(v) = self.const_strings.get(v) {
Some(*v)
} else {
let val = StringType::new(self)
.construct_constant(self, v, None)
.as_abi_value(self)
.into();
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();
self.const_strings.insert(v.to_string(), val);
Some(val)
}
@ -581,35 +619,42 @@ 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 {
llvm_exn.map_pointer_value(exception_val, Some("exn"))
exception_val
} else {
let zelf = llvm_exn.alloca_var(generator, self, Some("exn"));
self.exception_val = Some(zelf.as_abi_value(self));
zelf
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 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);
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);
}
pub fn make_assert<G: CodeGenerator + ?Sized>(
@ -785,10 +830,11 @@ 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
@ -807,6 +853,7 @@ 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();
@ -866,10 +913,9 @@ 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()],
);
}
}
@ -939,7 +985,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!(),
}
@ -960,11 +1006,22 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
} else {
Some(ctx.get_llvm_abi_type(generator, fun.0.ret))
};
let has_sret = ret_type.is_some_and(|ret_type| need_sret(ret_type));
let has_sret = ret_type.map_or(false, |ret_type| need_sret(ret_type));
let mut byrefs = Vec::new();
let mut params = args
.iter()
.filter(|arg| !arg.is_vararg)
.map(|arg| ctx.get_llvm_abi_type(generator, arg.ty).into())
.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()
})
.collect_vec();
if has_sret {
params.insert(0, ret_type.unwrap().ptr_type(AddressSpace::default()).into());
@ -978,7 +1035,7 @@ pub fn gen_call<'ctx, G: CodeGenerator>(
_ => ctx.ctx.void_type().fn_type(&params, is_vararg),
};
let fun_val = ctx.module.add_function(&symbol, fun_ty, None);
if has_sret {
let offset = if has_sret {
fun_val.add_attribute(
AttributeLoc::Param(0),
ctx.ctx.create_type_attribute(
@ -986,8 +1043,23 @@ 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
});
@ -1079,7 +1151,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
if *obj_id == ctx.primitives.range.obj_id(&ctx.unifier).unwrap() =>
{
let iter_val =
RangeType::new(ctx).map_pointer_value(iter_val.into_pointer_value(), Some("range"));
RangeValue::from_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
@ -1235,7 +1307,7 @@ pub fn gen_comprehension<'ctx, G: CodeGenerator>(
emit_cont_bb(ctx, list);
Ok(Some(list.as_abi_value(ctx).into()))
Ok(Some(list.as_base_value().into()))
}
/// Generates LLVM IR for a binary operator expression using the [`Type`] and
@ -1247,7 +1319,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
op: Binop,
right: (&Option<Type>, BasicValueEnum<'ctx>),
loc: Location,
) -> Result<BasicValueEnum<'ctx>, String> {
) -> Result<Option<ValueEnum<'ctx>>, String> {
let (left_ty, left_val) = left;
let (right_ty, right_val) = right;
@ -1258,14 +1330,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(ctx.gen_int_ops(generator, op.base, left_val, right_val, true))
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, true).into()))
} else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) {
Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, false))
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, false).into()))
} else if [Operator::LShift, Operator::RShift].contains(&op.base) {
let signed = [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1);
Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, signed))
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, signed).into()))
} else if ty1 == ty2 && ctx.primitives.float == ty1 {
Ok(ctx.gen_float_ops(op.base, left_val, right_val))
Ok(Some(ctx.gen_float_ops(op.base, left_val, right_val).into()))
} 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);
@ -1275,7 +1347,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
right_val.into_int_value(),
Some("f_pow_i"),
);
Ok(res.into())
Ok(Some(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())
{
@ -1305,10 +1377,7 @@ 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 = ctx
.builder
.build_int_truncate_or_bit_cast(llvm_elem_ty.size_of().unwrap(), llvm_usize, "")
.unwrap();
let sizeof_elem = llvm_elem_ty.size_of().unwrap();
let lhs =
ListValue::from_pointer_value(left_val.into_pointer_value(), llvm_usize, None);
@ -1323,14 +1392,34 @@ 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 = lhs.load_size(ctx, 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_len = ctx.builder.build_int_mul(lhs_size, sizeof_elem, "").unwrap();
let rhs_size = rhs.load_size(ctx, None);
let rhs_size = ctx
.builder
.build_int_z_extend_or_bit_cast(
rhs.load_size(ctx, None),
sizeof_elem.get_type(),
"",
)
.unwrap();
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);
call_memcpy_generic(
ctx,
list_ptr,
lhs.data().base_ptr(ctx, generator),
lhs_len,
ctx.ctx.bool_type().const_zero(),
);
let list_ptr = unsafe {
new_list.data().ptr_offset_unchecked(
@ -1340,9 +1429,15 @@ 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);
call_memcpy_generic(
ctx,
list_ptr,
rhs.data().base_ptr(ctx, generator),
rhs_len,
ctx.ctx.bool_type().const_zero(),
);
Ok(new_list.as_abi_value(ctx).into())
Ok(Some(new_list.as_base_value().into()))
}
Operator::Mult => {
@ -1380,10 +1475,7 @@ 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 = ctx
.builder
.build_int_truncate_or_bit_cast(elem_llvm_ty.size_of().unwrap(), llvm_usize, "")
.unwrap();
let sizeof_elem = elem_llvm_ty.size_of().unwrap();
let new_list = ListType::new(ctx, &elem_llvm_ty).construct(
generator,
@ -1407,7 +1499,14 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
new_list.data().ptr_offset_unchecked(ctx, generator, &offset, None)
};
let list_size = list_val.load_size(ctx, 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 memcpy_sz =
ctx.builder.build_int_mul(list_size, sizeof_elem, "").unwrap();
@ -1417,6 +1516,7 @@ 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(())
@ -1424,7 +1524,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
llvm_usize.const_int(1, false),
)?;
Ok(new_list.as_abi_value(ctx).into())
Ok(Some(new_list.as_base_value().into()))
}
_ => todo!("Operator not supported"),
@ -1463,7 +1563,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(result.to_basic_value_enum())
Ok(Some(result.to_basic_value_enum().into()))
} else {
// For other operations, they are all elementwise operations.
@ -1494,12 +1594,14 @@ 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(result.as_abi_value(ctx).into())
Ok(Some(result.as_base_value().into()))
}
} else {
let left_ty_enum = ctx.unifier.get_ty_immutable(left_ty.unwrap());
@ -1548,7 +1650,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
(&signature, fun_id),
vec![(None, right_val.into())],
)
.map(Option::unwrap)
.map(|f| f.map(Into::into))
}
}
@ -1586,7 +1688,6 @@ 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
@ -1596,19 +1697,18 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx: &mut CodeGenContext<'ctx, '_>,
op: ast::Unaryop,
operand: (&Option<Type>, BasicValueEnum<'ctx>),
) -> Result<BasicValueEnum<'ctx>, String> {
) -> Result<Option<ValueEnum<'ctx>>, String> {
let (ty, val) = operand;
let ty = ctx.unifier.get_representative(ty.unwrap());
Ok(if ty == ctx.primitives.bool {
Ok(Some(if ty == ctx.primitives.bool {
let val = val.into_int_value();
if op == ast::Unaryop::Not {
let not = ctx
.builder
.build_int_compare(IntPredicate::EQ, val, val.get_type().const_zero(), "not")
.unwrap();
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();
generator.bool_to_int_type(ctx, not, val.get_type()).into()
not_bool.into()
} else {
let llvm_i32 = ctx.ctx.i32_type();
@ -1621,6 +1721,7 @@ 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,
@ -1666,7 +1767,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_pointer_value(val.into_pointer_value(), None);
.map_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
@ -1674,13 +1775,10 @@ 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, {}]",
"ufunc {} not supported for ndarray[bool, N]",
op.op_info().method_name,
ndims,
)
}
} else {
@ -1692,14 +1790,16 @@ 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))
gen_unaryop_expr_with_values(generator, ctx, op, (&Some(ndarray_dtype), scalar))?
.map(|val| val.to_basic_value_enum(ctx, generator, ndarray_dtype))
.unwrap()
},
)?;
mapped_ndarray.as_abi_value(ctx).into()
mapped_ndarray.as_base_value().into()
} else {
unimplemented!()
})
}))
}
/// Generates LLVM IR for a unary operator expression.
@ -1719,7 +1819,6 @@ 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
@ -1730,7 +1829,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<BasicValueEnum<'ctx>, String> {
) -> Result<Option<ValueEnum<'ctx>>, String> {
debug_assert_eq!(comparators.len(), ops.len());
if comparators.len() == 1 {
@ -1772,13 +1871,19 @@ 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(result_ndarray.as_abi_value(ctx).into());
return Ok(Some(result_ndarray.as_base_value().into()));
}
}
@ -1862,19 +1967,41 @@ 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 llvm_str = StringType::new(ctx);
let lhs = lhs.into_struct_value();
let rhs = rhs.into_struct_value();
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 llvm_i32 = ctx.ctx.i32_type();
let llvm_usize = ctx.get_size_type();
let result = call_string_eq(ctx, lhs, rhs);
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);
if *op == Cmpop::NotEq {
gen_unaryop_expr_with_values(
generator,
ctx,
Unaryop::Not,
(&Some(ctx.primitives.bool), result.into()),
)?.into_int_value()
ctx.builder.build_not(result, "").unwrap()
} else {
result
}
@ -1977,6 +2104,9 @@ 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(
@ -2025,6 +2155,8 @@ 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
@ -2112,6 +2244,11 @@ 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(
@ -2148,12 +2285,7 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
// Invert the final value if __ne__
if *op == Cmpop::NotEq {
gen_unaryop_expr_with_values(
generator,
ctx,
Unaryop::Not,
(&Some(ctx.primitives.bool), cmp_phi.into()),
)?.into_int_value()
ctx.builder.build_not(cmp_phi, "").unwrap()
} else {
cmp_phi
}
@ -2178,9 +2310,12 @@ 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(cmp_val.into())
Ok(Some(match cmp_val {
Some(v) => v.into(),
None => return Ok(None),
}))
}
/// Generates LLVM IR for a comparison operator expression.
@ -2227,7 +2362,6 @@ pub fn gen_cmpop_expr<'ctx, G: CodeGenerator>(
ops,
comparator_vals.as_slice(),
)
.map(|res| Some(res.into()))
}
/// See [`CodeGenerator::gen_expr`].
@ -2257,13 +2391,16 @@ 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()) {
TypeEnum::TObj { obj_id, .. }
if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
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 =>
{
OptionType::from_unifier_type(generator, ctx, expr.custom.unwrap())
.construct_empty(generator, ctx, None)
.as_abi_value(ctx)
ctx.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::default())
.const_null()
.into()
}
_ => codegen_unreachable!(ctx, "must be option type"),
@ -2356,7 +2493,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
);
ctx.builder.build_store(elem_ptr, *v).unwrap();
}
arr_str_ptr.as_abi_value(ctx).into()
arr_str_ptr.as_base_value().into()
}
ExprKind::Tuple { elts, .. } => {
let elements_val = elts
@ -2464,7 +2601,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.unwrap() as u64, false)],
&[zero, int32.const_int(index as u64, false)],
None,
))) as Result<_, String>
},
@ -2481,7 +2618,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
}
ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(),
&[zero, int32.const_int(index.unwrap() as u64, false)],
&[zero, int32.const_int(index as u64, false)],
None,
))
}
@ -2566,7 +2703,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)? {
@ -2748,12 +2885,8 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
};
}
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
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);
let not_null =
ctx.builder.build_is_not_null(ptr, "unwrap_not_null").unwrap();
ctx.make_assert(
generator,
not_null,
@ -2762,7 +2895,12 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
[None, None, None],
expr.location,
);
return Ok(Some(unsafe { option.load(ctx).into() }));
return Ok(Some(
ctx.builder
.build_load(ptr, "unwrap_some_load")
.map(Into::into)
.unwrap(),
));
}
ValueEnum::Dynamic(_) => {
codegen_unreachable!(ctx, "option must be static or ptr")
@ -2850,7 +2988,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
v,
(start, end, step),
);
res_array_ret.as_abi_value(ctx).into()
res_array_ret.as_base_value().into()
} else {
let len = v.load_size(ctx, Some("len"));
let raw_index = if let Some(v) = generator.gen_expr(ctx, slice)? {
@ -2905,14 +3043,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_pointer_value(ndarray.into_pointer_value(), None);
.map_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(result.into()));
return Ok(Some(ValueEnum::Dynamic(result)));
}
TypeEnum::TTuple { .. } => {
let index: u32 =
@ -3010,8 +3148,9 @@ 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().collect_vec();
let arg_values = params.iter().map(|(_, value)| value).copied().collect_vec();
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();
create_fn_and_call(
ctx,

View File

@ -1,9 +1,10 @@
use inkwell::{
attributes::{Attribute, AttributeLoc},
values::{BasicValueEnum, FloatValue},
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue},
};
use itertools::Either;
use super::{CodeGenContext, expr::infer_and_call_function};
use super::CodeGenContext;
/// Macro to generate extern function
/// Both function return type and function parameter type are `FloatValue`
@ -36,8 +37,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;
@ -45,35 +46,99 @@ macro_rules! generate_extern_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| {
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()
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()
}
};
}
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:ident`: Name of underlying extern function
/// * `$extern_fn:literal`: Name of underlying extern function
/// * (2/3/4): Number of `NDArray` that function takes as input
///
/// Note:
@ -81,51 +146,48 @@ generate_extern_fn!("unary", call_j1, "j1", "nounwind");
/// 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:ident, 2) => {
($fn_name:ident, $extern_fn:literal, 2) => {
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2);
};
($fn_name:ident, $extern_fn:ident, 3) => {
($fn_name:ident, $extern_fn:literal, 3) => {
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2, mat3);
};
($fn_name:ident, $extern_fn:ident, 4) => {
($fn_name:ident, $extern_fn:literal, 4) => {
generate_linalg_extern_fn!($fn_name, $extern_fn, mat1, mat2, mat3, mat4);
};
($fn_name:ident, $extern_fn:ident $(,$input_matrix:ident)*) => {
#[doc = concat!("Invokes the linalg `", stringify!($extern_fn), "` function." )]
($fn_name:ident, $extern_fn:literal $(,$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 = stringify!($extern_fn);
){
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);
infer_and_call_function(
ctx,
FN_NAME,
None,
&[$($input_matrix.into(),)*],
name,
Some(&|func| {
let func = ctx.module.add_function(FN_NAME, fn_type, None);
for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] {
func.add_attribute(
AttributeLoc::Function,
ctx.ctx.create_enum_attribute(
Attribute::get_named_enum_kind_id("nounwind"),
0,
),
)
}),
);
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();
}
};
}
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);

View File

@ -7,7 +7,7 @@ use inkwell::{
use nac3parser::ast::{Expr, Stmt, StrRef};
use super::{CodeGenContext, bool_to_int_type, expr::*, stmt::*, values::ArraySliceValue};
use super::{bool_to_i1, bool_to_i8, expr::*, stmt::*, values::ArraySliceValue, CodeGenContext};
use crate::{
symbol_resolver::ValueEnum,
toplevel::{DefinitionId, TopLevelDef},
@ -248,32 +248,22 @@ pub trait CodeGenerator {
gen_block(self, ctx, stmts)
}
/// Converts the value of a boolean-like value `bool_value` into an `i1`.
/// See [`bool_to_i1`].
fn bool_to_i1<'ctx>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>,
) -> IntValue<'ctx> {
self.bool_to_int_type(ctx, bool_value, ctx.ctx.bool_type())
bool_to_i1(&ctx.builder, bool_value)
}
/// Converts the value of a boolean-like value `bool_value` into an `i8`.
/// See [`bool_to_i8`].
fn bool_to_i8<'ctx>(
&self,
ctx: &CodeGenContext<'ctx, '_>,
bool_value: IntValue<'ctx>,
) -> IntValue<'ctx> {
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)
bool_to_i8(&ctx.builder, ctx.ctx, bool_value)
}
}
@ -308,6 +298,10 @@ 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()
}
}
}

View File

@ -1,131 +0,0 @@
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()
}

View File

@ -1,16 +1,15 @@
use inkwell::{
AddressSpace, IntPredicate,
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
values::{BasicValueEnum, CallSiteValue, IntValue},
AddressSpace, IntPredicate,
};
use itertools::Either;
use super::calculate_len_for_slice_range;
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
macros::codegen_unreachable,
stmt::gen_if_callback,
values::{ArrayLikeValue, ListValue},
CodeGenContext, CodeGenerator,
};
/// This function handles 'end' **inclusively**.
@ -37,6 +36,25 @@ 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);
@ -109,7 +127,7 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
);
let new_len = {
let args = [
let args = vec![
dest_idx.0.into(), // dest start idx
dest_idx.1.into(), // dest end idx
dest_idx.2.into(), // dest step
@ -132,35 +150,25 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
}
.into(),
];
infer_and_call_function(
ctx,
fun_symbol,
Some(llvm_i32.into()),
&args,
Some("slice_assign"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap()
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()
};
// update length
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();
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);
}

View File

@ -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,16 +18,18 @@ pub fn integer_power<'ctx, G: CodeGenerator + ?Sized>(
exp: IntValue<'ctx>,
signed: bool,
) -> IntValue<'ctx> {
let base_type = base.get_type();
let symbol = match (base_type.get_bit_width(), exp.get_type().get_bit_width(), signed) {
let symbol = match (base.get_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
@ -46,17 +48,85 @@ 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()
}
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 `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()
}
/// Generates a call to `gammaln` in IR. Returns an `f64` representing the result.
@ -65,16 +135,17 @@ pub fn call_gammaln<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -
assert_eq!(v.get_type(), llvm_f64);
infer_and_call_function(
ctx,
"__nac3_gammaln",
Some(llvm_f64.into()),
&[v.into()],
Some("gammaln"),
None,
)
.map(BasicValueEnum::into_float_value)
.unwrap()
let intrinsic_fn = ctx.module.get_function("__nac3_gammaln").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_gammaln", fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[v.into()], "gammaln")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}
/// Generates a call to `j0` in IR. Returns an `f64` representing the result.
@ -83,7 +154,15 @@ pub fn call_j0<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> Flo
assert_eq!(v.get_type(), llvm_f64);
infer_and_call_function(ctx, "__nac3_j0", Some(llvm_f64.into()), &[v.into()], Some("j0"), None)
.map(BasicValueEnum::into_float_value)
let intrinsic_fn = ctx.module.get_function("__nac3_j0").unwrap_or_else(|| {
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false);
ctx.module.add_function("__nac3_j0", fn_type, None)
});
ctx.builder
.build_call(intrinsic_fn, &[v.into()], "j0")
.map(CallSiteValue::try_as_basic_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value))
.map(Either::unwrap_left)
.unwrap()
}

View File

@ -1,24 +1,22 @@
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;

View File

@ -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::{ListValue, ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
values::{ndarray::NDArrayValue, ListValue, ProxyValue, TypedArrayLikeAccessor},
CodeGenContext, CodeGenerator,
};
/// 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_abi_value(ctx).into(), ndims.into(), shape.base_ptr(ctx, generator).into()],
&[list.as_base_value().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_abi_value(ctx).into(), ndarray.as_abi_value(ctx).into()],
&[list.as_base_value().into(), ndarray.as_base_value().into()],
None,
None,
);

View File

@ -1,13 +1,15 @@
use inkwell::{
AddressSpace,
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace,
};
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
expr::{create_and_call_function, infer_and_call_function},
irrt::get_usize_dependent_function_name,
values::{ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
types::ProxyType,
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_util_assert_shape_no_negative`.
@ -19,17 +21,24 @@ 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!(shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(
BasicTypeEnum::try_from(shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name =
get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_shape_no_negative");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[shape.size(ctx, generator).into(), shape.base_ptr(ctx, generator).into()],
&[
(llvm_usize.into(), shape.size(ctx, generator).into()),
(llvm_pusize.into(), shape.base_ptr(ctx, generator).into()),
],
None,
None,
);
@ -46,22 +55,29 @@ 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!(ndarray_shape.element_type(ctx, generator), llvm_usize.into());
assert_eq!(output_shape.element_type(ctx, generator), llvm_usize.into());
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()
);
let name =
get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_output_shape_same");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.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(),
(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()),
],
None,
None,
@ -77,14 +93,15 @@ 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");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[ndarray.as_abi_value(ctx).into()],
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
Some("size"),
None,
)
@ -101,14 +118,15 @@ 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");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[ndarray.as_abi_value(ctx).into()],
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
Some("nbytes"),
None,
)
@ -125,14 +143,15 @@ 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");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_usize.into()),
&[ndarray.as_abi_value(ctx).into()],
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
Some("len"),
None,
)
@ -148,14 +167,15 @@ 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");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_i1.into()),
&[ndarray.as_abi_value(ctx).into()],
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
Some("is_c_contiguous"),
None,
)
@ -174,16 +194,17 @@ 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");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_pi8.into()),
&[ndarray.as_abi_value(ctx).into(), index.into()],
&[(llvm_ndarray.into(), ndarray.as_base_value().into()), (llvm_usize.into(), index.into())],
Some("pelement"),
None,
)
@ -205,16 +226,24 @@ 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!(indices.element_type(ctx, generator), llvm_usize.into());
assert_eq!(
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_get_pelement_by_indices");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
Some(llvm_pi8.into()),
&[ndarray.as_abi_value(ctx).into(), indices.base_ptr(ctx, generator).into()],
&[
(llvm_ndarray.into(), ndarray.as_base_value().into()),
(llvm_pusize.into(), indices.base_ptr(ctx, generator).into()),
],
Some("pelement"),
None,
)
@ -229,9 +258,18 @@ 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");
infer_and_call_function(ctx, &name, None, &[ndarray.as_abi_value(ctx).into()], None, None);
create_and_call_function(
ctx,
&name,
None,
&[(llvm_ndarray.into(), ndarray.as_base_value().into())],
None,
None,
);
}
/// Generates a call to `__nac3_ndarray_copy_data`.
@ -250,7 +288,7 @@ pub fn call_nac3_ndarray_copy_data<'ctx>(
ctx,
&name,
None,
&[src_ndarray.as_abi_value(ctx).into(), dst_ndarray.as_abi_value(ctx).into()],
&[src_ndarray.as_base_value().into(), dst_ndarray.as_base_value().into()],
None,
None,
);

View File

@ -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::{ProxyType, ndarray::ShapeEntryType},
types::{ndarray::ShapeEntryType, ProxyType},
values::{
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeMutator,
ndarray::NDArrayValue,
ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor,
TypedArrayLikeMutator,
},
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_abi_value(ctx).into(), dst_ndarray.as_abi_value(ctx).into()],
&[src_ndarray.as_base_value().into(), dst_ndarray.as_base_value().into()],
None,
None,
);
@ -55,13 +55,12 @@ 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_representable(
shape_entries.base_ptr(ctx, generator).get_type(),
llvm_usize,
)
.is_ok()
);
assert!(ShapeEntryType::is_type(
generator,
ctx.ctx,
shape_entries.base_ptr(ctx, generator).get_type()
)
.is_ok());
assert_eq!(dst_ndims.get_type(), llvm_usize);
assert_eq!(dst_shape.element_type(ctx, generator), llvm_usize.into());

View File

@ -1,8 +1,8 @@
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
irrt::get_usize_dependent_function_name,
values::{ArrayLikeValue, ArraySliceValue, ProxyValue, ndarray::NDArrayValue},
values::{ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ProxyValue},
CodeGenContext, CodeGenerator,
};
/// 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_abi_value(ctx).into(),
dst_ndarray.as_abi_value(ctx).into(),
src_ndarray.as_base_value().into(),
dst_ndarray.as_base_value().into(),
],
None,
None,

View File

@ -1,13 +1,18 @@
use inkwell::values::{BasicValueEnum, IntValue};
use inkwell::{
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
AddressSpace,
};
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
expr::{create_and_call_function, infer_and_call_function},
irrt::get_usize_dependent_function_name,
types::ProxyType,
values::{
ProxyValue, TypedArrayLikeAccessor,
ndarray::{NDArrayValue, NDIterValue},
ProxyValue, TypedArrayLikeAccessor,
},
CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_nditer_initialize`.
@ -21,19 +26,23 @@ 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!(indices.element_type(ctx, generator), llvm_usize.into());
assert_eq!(
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_initialize");
infer_and_call_function(
create_and_call_function(
ctx,
&name,
None,
&[
iter.as_abi_value(ctx).into(),
ndarray.as_abi_value(ctx).into(),
indices.base_ptr(ctx, generator).into(),
(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()),
],
None,
None,
@ -54,7 +63,7 @@ pub fn call_nac3_nditer_has_element<'ctx>(
ctx,
&name,
Some(ctx.ctx.bool_type().into()),
&[iter.as_abi_value(ctx).into()],
&[iter.as_base_value().into()],
None,
None,
)
@ -68,5 +77,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_abi_value(ctx).into()], None, None);
infer_and_call_function(ctx, &name, None, &[iter.as_base_value().into()], None, None);
}

View File

@ -1,8 +1,8 @@
use inkwell::values::IntValue;
use inkwell::{types::BasicTypeEnum, values::IntValue};
use crate::codegen::{
CodeGenContext, CodeGenerator, expr::infer_and_call_function,
irrt::get_usize_dependent_function_name, values::TypedArrayLikeAccessor,
expr::infer_and_call_function, irrt::get_usize_dependent_function_name,
values::TypedArrayLikeAccessor, CodeGenContext, CodeGenerator,
};
/// Generates a call to `__nac3_ndarray_matmul_calculate_shapes`.
@ -22,12 +22,26 @@ pub fn call_nac3_ndarray_matmul_calculate_shapes<'ctx, G: CodeGenerator + ?Sized
) {
let llvm_usize = ctx.get_size_type();
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());
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()
);
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_matmul_calculate_shapes");

View File

@ -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`.

View File

@ -1,10 +1,10 @@
use inkwell::{AddressSpace, values::IntValue};
use inkwell::{values::IntValue, AddressSpace};
use crate::codegen::{
CodeGenContext, CodeGenerator,
expr::infer_and_call_function,
irrt::get_usize_dependent_function_name,
values::{ProxyValue, TypedArrayLikeAccessor, ndarray::NDArrayValue},
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
CodeGenContext, CodeGenerator,
};
/// 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_abi_value(ctx).into(),
dst_ndarray.as_abi_value(ctx).into(),
src_ndarray.as_base_value().into(),
dst_ndarray.as_base_value().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)

View File

@ -1,9 +1,10 @@
use inkwell::{
values::{BasicValueEnum, CallSiteValue, IntValue},
IntPredicate,
values::{BasicValueEnum, IntValue},
};
use itertools::Either;
use crate::codegen::{CodeGenContext, CodeGenerator, expr::infer_and_call_function};
use crate::codegen::{CodeGenContext, CodeGenerator};
/// Invokes the `__nac3_range_slice_len` in IRRT.
///
@ -22,10 +23,16 @@ 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
@ -40,14 +47,10 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
ctx.current_loc,
);
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()
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()
}

View File

@ -1,9 +1,10 @@
use inkwell::values::{BasicValueEnum, IntValue};
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue};
use itertools::Either;
use nac3parser::ast::Expr;
use crate::{
codegen::{CodeGenContext, CodeGenerator, expr::infer_and_call_function},
codegen::{CodeGenContext, CodeGenerator},
typecheck::typedef::Type,
};
@ -16,26 +17,23 @@ 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 llvm_i32 = ctx.ctx.i32_type();
assert_eq!(length.get_type(), llvm_i32);
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 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(
infer_and_call_function(
ctx,
SYMBOL,
Some(llvm_i32.into()),
&[i, length.into()],
Some("bounded_ind"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap(),
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(),
))
}

View File

@ -1,31 +1,45 @@
use inkwell::values::{BasicValueEnum, IntValue};
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue, PointerValue};
use itertools::Either;
use super::get_usize_dependent_function_name;
use crate::codegen::{CodeGenContext, expr::infer_and_call_function, values::StringValue};
use crate::codegen::CodeGenContext;
/// 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: StringValue<'ctx>,
str2: StringValue<'ctx>,
str1_ptr: PointerValue<'ctx>,
str1_len: IntValue<'ctx>,
str2_ptr: PointerValue<'ctx>,
str2_len: IntValue<'ctx>,
) -> IntValue<'ctx> {
let llvm_i1 = ctx.ctx.bool_type();
let func_name = get_usize_dependent_function_name(ctx, "nac3_str_eq");
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()
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()
}

View File

@ -1,8 +1,8 @@
use inkwell::{
AddressSpace,
intrinsics::Intrinsic,
types::AnyTypeEnum::IntType,
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue, PointerValue},
AddressSpace,
};
use itertools::Either;
@ -98,29 +98,41 @@ 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_eq!(len.get_type(), ctx.get_size_type());
debug_assert!(matches!(len.get_type().get_bit_width(), 32 | 64));
debug_assert_eq!(is_volatile.get_type().get_bit_width(), 1);
let llvm_dest_t = dest.get_type();
let llvm_src_t = src.get_type();
let llvm_len_t = len.get_type();
let 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);
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();
ctx.builder.build_memcpy(dest, dest_alignment, src, src_alignment, len).unwrap();
ctx.builder
.build_call(intrinsic_fn, &[dest.into(), src.into(), len.into(), is_volatile.into()], "")
.unwrap();
}
/// Invokes the `llvm.memcpy` intrinsic.
@ -132,6 +144,7 @@ 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());
@ -156,7 +169,7 @@ pub fn call_memcpy_generic<'ctx>(
.unwrap()
};
call_memcpy(ctx, dest, src, len);
call_memcpy(ctx, dest, src, len, is_volatile);
}
/// Invokes the `llvm.memcpy` intrinsic.
@ -170,10 +183,11 @@ 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_usize = ctx.get_size_type();
let llvm_sizeof_expr_t = llvm_i8.size_of().get_type();
let dest_elem_t = dest.get_type().get_element_type();
let src_elem_t = src.get_type().get_element_type();
@ -195,13 +209,10 @@ pub fn call_memcpy_generic_array<'ctx>(
.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();
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();
call_memcpy(ctx, dest, src, len);
call_memcpy(ctx, dest, src, len, is_volatile);
}
/// Macro to find and generate build call for llvm intrinsic (body of llvm intrinsic function)

View File

@ -2,15 +2,14 @@ use std::{
cell::OnceCell,
collections::{HashMap, HashSet},
sync::{
Arc,
atomic::{AtomicBool, Ordering},
Arc,
},
thread,
};
use crossbeam::channel::{Receiver, Sender, unbounded};
use crossbeam::channel::{unbounded, Receiver, Sender};
use inkwell::{
AddressSpace, IntPredicate, OptimizationLevel,
attributes::{Attribute, AttributeLoc},
basic_block::BasicBlock,
builder::Builder,
@ -23,6 +22,7 @@ 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::{
TopLevelContext, TopLevelDef,
helper::{PrimDef, extract_ndims},
helper::{extract_ndims, PrimDef},
numpy::unpack_ndarray_var_tys,
TopLevelContext, TopLevelDef,
},
typecheck::{
type_inferencer::{CodeLocation, PrimitiveStore},
@ -43,10 +43,7 @@ use crate::{
};
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
pub use generator::{CodeGenerator, DefaultCodeGenerator};
use types::{
ExceptionType, ListType, OptionType, ProxyType, RangeType, StringType, TupleType,
ndarray::NDArrayType,
};
use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType, TupleType};
pub mod builtin_fns;
pub mod concrete_type;
@ -541,7 +538,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() => {
let element_type = get_llvm_type(
get_llvm_type(
ctx,
module,
generator,
@ -549,9 +546,9 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
top_level,
type_cache,
*params.iter().next().unwrap().1,
);
OptionType::new_with_generator(generator, ctx, &element_type).as_abi_type().into()
)
.ptr_type(AddressSpace::default())
.into()
}
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
@ -565,7 +562,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
*params.iter().next().unwrap().1,
);
ListType::new_with_generator(generator, ctx, element_type).as_abi_type().into()
ListType::new_with_generator(generator, ctx, element_type).as_base_type().into()
}
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
@ -575,7 +572,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_abi_type().into()
NDArrayType::new_with_generator(generator, ctx, element_type, ndims).as_base_type().into()
}
_ => unreachable!(
@ -629,7 +626,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_abi_type().into()
TupleType::new_with_generator(generator, ctx, &fields).as_base_type().into()
}
TVirtual { .. } => unimplemented!(),
_ => unreachable!("{}", ty_enum.get_type_name()),
@ -789,11 +786,34 @@ pub fn gen_func_impl<
(primitives.float, context.f64_type().into()),
(primitives.bool, context.i8_type().into()),
(primitives.str, {
StringType::new_with_generator(generator, context).as_abi_type().into()
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(),
}
}),
(primitives.range, RangeType::new_with_generator(generator, context).as_abi_type().into()),
(primitives.range, RangeType::new(context).as_base_type().into()),
(primitives.exception, {
ExceptionType::new_with_generator(generator, context).as_abi_type().into()
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()
}
}),
]
.iter()
@ -832,7 +852,7 @@ pub fn gen_func_impl<
))
};
let has_sret = ret_type.is_some_and(|ty| need_sret(ty));
let has_sret = ret_type.map_or(false, |ty| need_sret(ty));
let mut params = args
.iter()
.filter(|arg| !arg.is_vararg)
@ -913,7 +933,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_int_type(&builder, param_val, context.i8_type())
bool_to_i8(&builder, context, param_val)
} else {
param_val
}
@ -1028,7 +1048,8 @@ 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})",
);
@ -1082,29 +1103,43 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
})
}
/// 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>(
/// 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>(
builder: &Builder<'ctx>,
value: IntValue<'ctx>,
ty: IntType<'ctx>,
ctx: &'ctx Context,
bool_value: IntValue<'ctx>,
) -> IntValue<'ctx> {
// 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(
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(
builder,
ctx,
builder
.build_int_compare(IntPredicate::NE, value, value.get_type().const_zero(), "tobool")
.build_int_compare(
IntPredicate::NE,
bool_value,
bool_value.get_type().const_zero(),
"",
)
.unwrap(),
ty,
),
}
}

View File

@ -1,23 +1,23 @@
use inkwell::{
IntPredicate,
values::{BasicValue, BasicValueEnum, PointerValue},
IntPredicate,
};
use nac3parser::ast::StrRef;
use super::{
CodeGenContext, CodeGenerator,
macros::codegen_unreachable,
stmt::gen_for_callback,
types::ndarray::{NDArrayType, NDIterType},
values::{ProxyValue, ndarray::shape::parse_numpy_int_sequence},
values::{ndarray::shape::parse_numpy_int_sequence, ProxyValue},
CodeGenContext, CodeGenerator,
};
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_abi_value(context))
Ok(ndarray.as_base_value())
}
/// 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_abi_value(context))
Ok(ndarray.as_base_value())
}
/// 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_abi_value(context))
Ok(ndarray.as_base_value())
}
/// Generates LLVM IR for `ndarray.full`.
@ -127,7 +127,7 @@ pub fn gen_ndarray_full<'ctx>(
fill_value_arg,
None,
);
Ok(ndarray.as_abi_value(context))
Ok(ndarray.as_base_value())
}
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_abi_value(context))
Ok(ndarray.as_base_value())
}
/// 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_abi_value(context))
Ok(ndarray.as_base_value())
}
/// 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_abi_value(context))
Ok(ndarray.as_base_value())
}
/// 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_pointer_value(this_arg.into_pointer_value(), None);
.map_value(this_arg.into_pointer_value(), None);
let ndarray = this.make_copy(generator, context);
Ok(ndarray.as_abi_value(context))
Ok(ndarray.as_base_value())
}
/// 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_pointer_value(this_arg.into_pointer_value(), None);
.map_value(this_arg.into_pointer_value(), None);
this.fill(generator, context, value_arg);
Ok(())
}
@ -316,10 +316,8 @@ 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_pointer_value(n1, None);
let b =
NDArrayType::from_unifier_type(generator, ctx, x2_ty).map_pointer_value(n2, None);
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);
// TODO: General `np.dot()` https://numpy.org/doc/stable/reference/generated/numpy.dot.html.
assert_eq!(a.get_type().ndims(), 1);

View File

@ -1,36 +0,0 @@
---
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)

View File

@ -1,42 +0,0 @@
---
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)

View File

@ -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::{Itertools, izip};
use itertools::{izip, Itertools};
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::{ExceptionType, RangeType, ndarray::NDArrayType},
types::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::{FunSignature, Type, TypeEnum, iter_type_vars},
typedef::{iter_type_vars, FunSignature, Type, TypeEnum},
},
};
@ -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.unwrap() as u64, false),
ctx.ctx.i32_type().const_int(index 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_pointer_value(target.into_pointer_value(), None);
.map_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 =
RangeType::new(ctx).map_pointer_value(iter_val.into_pointer_value(), Some("range"));
RangeValue::from_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,19 +1337,43 @@ pub fn exn_constructor<'ctx>(
pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>,
exception: Option<&ExceptionValue<'ctx>>,
exception: Option<&BasicValueEnum<'ctx>>,
loc: Location,
) {
if let Some(exception) = exception {
exception.store_location(generator, ctx, loc);
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();
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 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 raise = get_builtins(generator, ctx, "__nac3_raise");
let exception = *exception;
ctx.build_call_or_invoke(raise, &[exception.as_abi_value(ctx).into()], "raise");
ctx.build_call_or_invoke(raise, &[exception], "raise");
} else {
let resume = get_builtins(generator, ctx, "__nac3_resume");
ctx.build_call_or_invoke(resume, &[], "resume");
@ -1468,10 +1492,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);
ctx.loop_target = old_loop_target.or(ctx.loop_target).take();
let old_unwind = if finalbody.is_empty() {
old_unwind
None
} else {
let final_landingpad = ctx.ctx.append_basic_block(current_fun, "try.catch.final");
ctx.builder.position_at_end(final_landingpad);
@ -1592,7 +1616,7 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
}
ctx.unwind_target = old_unwind;
ctx.loop_target = old_loop_target.or(ctx.loop_target);
ctx.loop_target = old_loop_target.or(ctx.loop_target).take();
ctx.return_target = old_return;
ctx.builder.position_at_end(landingpad);
@ -1688,259 +1712,13 @@ pub fn gen_try<'ctx, 'a, G: CodeGenerator>(
}
/// See [`CodeGenerator::gen_with`].
pub fn gen_with<'ctx, 'a, G: CodeGenerator>(
generator: &mut G,
ctx: &mut CodeGenContext<'ctx, 'a>,
pub fn gen_with<G: CodeGenerator>(
_: &mut G,
_: &mut CodeGenContext<'_, '_>,
stmt: &Stmt<Option<Type>>,
) -> Result<(), String> {
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(())
// TODO: Implement with statement after finishing exceptions
Err(format!("With statement with custom types is not yet supported (at {})", stmt.location))
}
/// Generates IR for a `return` statement.
@ -2082,8 +1860,6 @@ 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);

View File

@ -3,30 +3,29 @@ use std::{
sync::Arc,
};
use function_name::named;
use indexmap::IndexMap;
use indoc::indoc;
use inkwell::{
OptimizationLevel,
targets::{InitializationConfig, Target},
OptimizationLevel,
};
use nac3parser::{
ast::{FileName, StrRef, fold::Fold},
ast::{fold::Fold, FileName, StrRef},
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::{
DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
composer::{ComposerConfig, TopLevelComposer},
DefinitionId, FunInstance, TopLevelContext, TopLevelDef,
},
typecheck::{
type_inferencer::{FunctionData, IdentifierInfo, Inferencer, PrimitiveStore},
@ -90,7 +89,6 @@ impl SymbolResolver for Resolver {
}
#[test]
#[named]
fn test_primitives() {
let source = indoc! { "
c = a + b
@ -183,10 +181,60 @@ fn test_primitives() {
id: 0,
};
let f = Arc::new(WithCall::new(Box::new(|module| {
insta::assert_snapshot!(
function_name!(),
module.print_to_string().to_str().map(str::trim).unwrap()
);
// 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());
})));
Target::initialize_all(&InitializationConfig::default());
@ -201,7 +249,6 @@ fn test_primitives() {
}
#[test]
#[named]
fn test_simple_call() {
let source_1 = indoc! { "
a = foo(a)
@ -336,10 +383,48 @@ fn test_simple_call() {
id: 0,
};
let f = Arc::new(WithCall::new(Box::new(|module| {
insta::assert_snapshot!(
function_name!(),
module.print_to_string().to_str().map(str::trim).unwrap()
);
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());
})));
Target::initialize_all(&InitializationConfig::default());
@ -362,18 +447,15 @@ 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_abi_type(), llvm_usize).is_ok());
assert!(ListType::is_representable(llvm_list.as_base_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_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());
let llvm_range = RangeType::new(&ctx);
assert!(RangeType::is_representable(llvm_range.as_base_type()).is_ok());
}
#[test]
@ -385,5 +467,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_abi_type(), llvm_usize).is_ok());
assert!(NDArrayType::is_representable(llvm_ndarray.as_base_type(), llvm_usize).is_ok());
}

View File

@ -1,264 +0,0 @@
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()
}
}

View File

@ -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,14 +11,13 @@ use nac3core_derive::StructFields;
use super::ProxyType;
use crate::{
codegen::{
CodeGenContext, CodeGenerator,
types::structure::{
FieldIndexCounter, StructField, StructFields, StructProxyType,
check_struct_type_matches_fields,
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
},
values::ListValue,
values::{ListValue, ProxyValue},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::{Type, TypeEnum, iter_type_vars},
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
};
/// Proxy type for a `list` type in LLVM.
@ -57,12 +56,47 @@ 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(
@ -147,16 +181,10 @@ 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_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
let ctx = ptr_ty.get_context();
@ -295,27 +323,9 @@ impl<'ctx> ListType<'ctx> {
/// Converts an existing value into a [`ListValue`].
#[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
pub fn map_value(
&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 [`ListValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
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, self.llvm_usize, name)
@ -323,64 +333,36 @@ 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_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
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)
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} 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}"));
};
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 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 alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type()
self.as_base_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> {

View File

@ -17,7 +17,8 @@
//! on the stack.
use inkwell::{
types::{BasicType, IntType},
context::Context,
types::BasicType,
values::{IntValue, PointerValue},
};
@ -25,43 +26,38 @@ 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 ABI type of which values of this type possess.
type ABI: BasicType<'ctx>;
/// The LLVM type of which values of this type possess.
/// The LLVM type of which values of this type possess. This is usually a
/// [LLVM pointer type][PointerType] for any non-primitive types.
type Base: BasicType<'ctx>;
/// The type of values represented by this type.
type Value: ProxyValue<'ctx, Type = Self>;
/// Checks whether `llvm_ty` can be represented by this [`ProxyType`].
fn is_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
llvm_ty: impl BasicType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> 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>;
/// 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>;
/// Returns the type that should be used in `alloca` IR statements.
fn alloca_type(&self) -> impl BasicType<'ctx>;
@ -126,10 +122,4 @@ 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;
}

View File

@ -1,18 +1,19 @@
use inkwell::{
AddressSpace,
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
AddressSpace,
};
use crate::{
codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
stmt::gen_if_else_expr_callback,
types::{ListType, ProxyType, ndarray::NDArrayType},
types::{ndarray::NDArrayType, ListType, ProxyType},
values::{
ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue, TypedArrayLikeAdapter,
TypedArrayLikeMutator, ndarray::NDArrayValue,
ndarray::NDArrayValue, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue,
TypedArrayLikeAdapter, TypedArrayLikeMutator,
},
CodeGenContext, CodeGenerator,
},
toplevel::helper::{arraylike_flatten_element_type, arraylike_get_ndims},
typecheck::typedef::{Type, TypeEnum},
@ -150,7 +151,7 @@ impl<'ctx> NDArrayType<'ctx> {
(list_ty, list),
name,
);
Ok(Some(ndarray.as_abi_value(ctx)))
Ok(Some(ndarray.as_base_value()))
},
|generator, ctx| {
let ndarray = self.construct_numpy_array_from_list_copy_none_impl(
@ -159,14 +160,14 @@ impl<'ctx> NDArrayType<'ctx> {
(list_ty, list),
name,
);
Ok(Some(ndarray.as_abi_value(ctx)))
Ok(Some(ndarray.as_base_value()))
},
)
.unwrap()
.map(BasicValueEnum::into_pointer_value)
.unwrap();
NDArrayType::new(ctx, dtype, ndims).map_pointer_value(ndarray, None)
NDArrayType::new(ctx, dtype, ndims).map_value(ndarray, None)
}
/// Implementation of `np_array(<ndarray>, copy=copy)`.
@ -188,18 +189,18 @@ impl<'ctx> NDArrayType<'ctx> {
|_generator, _ctx| Ok(copy),
|generator, ctx| {
let ndarray = ndarray.make_copy(generator, ctx); // Force copy
Ok(Some(ndarray.as_abi_value(ctx)))
Ok(Some(ndarray.as_base_value()))
},
|_generator, ctx| {
|_generator, _ctx| {
// No need to copy. Return `ndarray` itself.
Ok(Some(ndarray.as_abi_value(ctx)))
Ok(Some(ndarray.as_base_value()))
},
)
.unwrap()
.map(BasicValueEnum::into_pointer_value)
.unwrap();
ndarray.get_type().map_pointer_value(ndarray_val, name)
ndarray.get_type().map_value(ndarray_val, name)
}
/// Create a new ndarray like
@ -221,7 +222,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_pointer_value(object.into_pointer_value(), None);
.map_value(object.into_pointer_value(), None);
self.construct_numpy_array_list_impl(generator, ctx, (object_ty, list), copy, name)
}
@ -229,7 +230,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_pointer_value(object.into_pointer_value(), None);
.map_value(object.into_pointer_value(), None);
self.construct_numpy_array_ndarray_impl(generator, ctx, ndarray, copy, name)
}

View File

@ -1,20 +1,20 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue},
AddressSpace,
};
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::ndarray::ShapeEntryValue,
values::{ndarray::ShapeEntryValue, ProxyValue},
CodeGenContext, CodeGenerator,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@ -32,6 +32,28 @@ 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(
@ -41,6 +63,13 @@ 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> {
@ -71,16 +100,10 @@ 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_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, llvm_usize }
}
@ -116,27 +139,9 @@ impl<'ctx> ShapeEntryType<'ctx> {
/// Converts an existing value into a [`ShapeEntryValue`].
#[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
pub fn map_value(
&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 [`ShapeEntryValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
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, self.llvm_usize, name)
@ -144,58 +149,36 @@ 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_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
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)
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} 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_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 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 alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type()
self.as_base_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> {

View File

@ -1,8 +1,8 @@
use inkwell::{
AddressSpace,
context::Context,
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue},
AddressSpace,
};
use itertools::Itertools;
@ -10,15 +10,14 @@ use nac3core_derive::StructFields;
use crate::{
codegen::{
CodeGenContext, CodeGenerator,
types::{
ProxyType,
structure::{
FieldIndexCounter, StructField, StructFields, StructProxyType,
check_struct_type_matches_fields,
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
},
ProxyType,
},
values::ndarray::ContiguousNDArrayValue,
values::{ndarray::ContiguousNDArrayValue, ProxyValue},
CodeGenContext, CodeGenerator,
},
toplevel::numpy::unpack_ndarray_var_tys,
typecheck::typedef::Type,
@ -59,6 +58,36 @@ 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(
@ -68,6 +97,13 @@ 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(
@ -117,24 +153,14 @@ 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_pointer_type(
pub fn from_type(
ptr_ty: PointerType<'ctx>,
item: BasicTypeEnum<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, item, llvm_usize }
}
@ -178,28 +204,9 @@ impl<'ctx> ContiguousNDArrayType<'ctx> {
/// Converts an existing value into a [`ContiguousNDArrayValue`].
#[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
pub fn map_value(
&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.item,
self.llvm_usize,
name,
)
}
/// Converts an existing value into a [`ContiguousNDArrayValue`].
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
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(
@ -212,66 +219,36 @@ 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_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
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)
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} 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 `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 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 alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type()
self.as_base_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> {

View File

@ -1,12 +1,12 @@
use inkwell::{
IntPredicate,
values::{BasicValueEnum, IntValue},
IntPredicate,
};
use super::NDArrayType;
use crate::{
codegen::{
CodeGenContext, CodeGenerator, irrt, types::ProxyType, values::TypedArrayLikeAccessor,
irrt, types::ProxyType, values::TypedArrayLikeAccessor, CodeGenContext, CodeGenerator,
},
typecheck::typedef::Type,
};

View File

@ -1,23 +1,23 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue},
AddressSpace,
};
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,6 +35,25 @@ 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>,
@ -43,6 +62,11 @@ 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 =
@ -71,13 +95,8 @@ impl<'ctx> NDIndexType<'ctx> {
}
#[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)
}
#[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());
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, llvm_usize }
}
@ -148,26 +167,9 @@ impl<'ctx> NDIndexType<'ctx> {
}
#[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
pub fn map_value(
&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,
)
}
#[must_use]
pub fn map_pointer_value(
&self,
value: PointerValue<'ctx>,
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, self.llvm_usize, name)
@ -175,55 +177,36 @@ 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_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
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)
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} 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 `ContiguousNDArray` type, got {llvm_ty}"
));
};
let fields = NDIndexStructFields::new(ctx, llvm_usize);
check_struct_type_matches_fields(fields, llvm_ty, "NDIndex", &[])
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 alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type()
self.as_base_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> {

View File

@ -2,16 +2,16 @@ use inkwell::{types::BasicTypeEnum, values::BasicValueEnum};
use itertools::Itertools;
use crate::codegen::{
CodeGenContext, CodeGenerator,
stmt::gen_for_callback,
types::{
ProxyType,
ndarray::{NDArrayType, NDIterType},
ProxyType,
},
values::{
ArrayLikeValue, ProxyValue,
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
ArrayLikeValue, ProxyValue,
},
CodeGenContext, CodeGenerator,
};
impl<'ctx> NDArrayType<'ctx> {

View File

@ -1,20 +1,20 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{BasicValue, IntValue, PointerValue, StructValue},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{BasicValue, IntValue, PointerValue},
AddressSpace,
};
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::{TypedArrayLikeMutator, ndarray::NDArrayValue},
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeMutator},
{CodeGenContext, CodeGenerator},
},
toplevel::{helper::extract_ndims, numpy::unpack_ndarray_var_tys},
@ -62,6 +62,26 @@ 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(
@ -71,6 +91,13 @@ 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> {
@ -176,26 +203,15 @@ 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_pointer_type(
pub fn from_type(
ptr_ty: PointerType<'ctx>,
dtype: BasicTypeEnum<'ctx>,
ndims: u64,
llvm_usize: IntType<'ctx>,
) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
NDArrayType { ty: ptr_ty, dtype, ndims, llvm_usize }
}
@ -415,29 +431,9 @@ impl<'ctx> NDArrayType<'ctx> {
/// Converts an existing value into a [`NDArrayValue`].
#[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
pub fn map_value(
&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.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>,
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(
@ -451,56 +447,36 @@ 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_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
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)
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} 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_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 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 alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type()
self.as_base_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> {

View File

@ -1,8 +1,8 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::{IntValue, PointerValue},
AddressSpace,
};
use itertools::Itertools;
@ -10,14 +10,13 @@ use nac3core_derive::StructFields;
use super::ProxyType;
use crate::codegen::{
CodeGenContext, CodeGenerator, irrt,
types::structure::{
StructField, StructFields, StructProxyType, check_struct_type_matches_fields,
},
irrt,
types::structure::{check_struct_type_matches_fields, StructField, StructFields},
values::{
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter,
ndarray::{NDArrayValue, NDIterValue},
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAdapter,
},
CodeGenContext, CodeGenerator,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@ -45,12 +44,39 @@ 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> {
@ -81,16 +107,10 @@ 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_pointer_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, llvm_usize).is_ok());
pub fn from_type(ptr_ty: PointerType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::is_representable(ptr_ty, llvm_usize).is_ok());
Self { ty: ptr_ty, llvm_usize }
}
@ -159,8 +179,7 @@ impl<'ctx> NDIterType<'ctx> {
let indices =
TypedArrayLikeAdapter::from(indices, |_, _, v| v.into_int_value(), |_, _, v| v.into());
let nditer =
self.map_pointer_value(nditer, ndarray, indices.as_slice_value(ctx, generator), None);
let nditer = self.map_value(nditer, ndarray, indices.as_slice_value(ctx, generator), None);
irrt::ndarray::call_nac3_nditer_initialize(generator, ctx, nditer, ndarray, &indices);
@ -168,30 +187,9 @@ impl<'ctx> NDIterType<'ctx> {
}
#[must_use]
pub fn map_struct_value<G: CodeGenerator + ?Sized>(
pub fn map_value(
&self,
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>,
value: <<Self as ProxyType<'ctx>>::Value as ProxyValue<'ctx>>::Base,
parent: NDArrayValue<'ctx>,
indices: ArraySliceValue<'ctx>,
name: Option<&'ctx str>,
@ -207,56 +205,36 @@ 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_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
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)
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} 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_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 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 alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type()
self.as_base_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> {

View File

@ -1,188 +0,0 @@
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()
}
}

View File

@ -1,167 +1,25 @@
use inkwell::{
AddressSpace,
context::Context,
types::{AnyTypeEnum, ArrayType, BasicType, BasicTypeEnum, IntType, PointerType},
values::{ArrayValue, PointerValue},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
AddressSpace,
};
use super::ProxyType;
use crate::{
codegen::{
values::RangeValue,
{CodeGenContext, CodeGenerator},
},
typecheck::typedef::{Type, TypeEnum},
use crate::codegen::{
values::{ProxyValue, RangeValue},
{CodeGenContext, CodeGenerator},
};
/// 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> {
/// 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();
/// 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();
let AnyTypeEnum::ArrayType(llvm_range_ty) = llvm_range_ty else {
return Err(format!("Expected array type for `range` type, got {llvm_range_ty}"));
};
@ -188,17 +46,106 @@ impl<'ctx> ProxyType<'ctx> for 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_abi_type().get_element_type().into_struct_type()
self.as_base_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> {

View File

@ -1,177 +0,0 @@
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()
}
}

View File

@ -1,56 +1,14 @@
use std::marker::PhantomData;
use inkwell::{
AddressSpace,
context::AsContextRef,
types::{BasicTypeEnum, IntType, PointerType, StructType},
values::{AggregateValueEnum, BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
types::{BasicTypeEnum, IntType, StructType},
values::{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
@ -203,38 +161,17 @@ where
/// Gets the value of this field for a given `obj`.
#[must_use]
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()
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()
}
/// Sets the value of this field for a given `obj`.
#[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()
pub fn set_for_value(&self, obj: StructValue<'ctx>, value: Value) {
obj.set_field_at_index(self.index, value);
}
/// Loads the value of this field for a pointer-to-structure.
pub fn load(
/// Gets the value of this field for a pointer-to-structure.
pub fn get(
&self,
ctx: &CodeGenContext<'ctx, '_>,
pobj: PointerValue<'ctx>,
@ -250,8 +187,8 @@ where
.unwrap()
}
/// Stores the value of this field for a pointer-to-structure.
pub fn store(
/// Sets the value of this field for a pointer-to-structure.
pub fn set(
&self,
ctx: &CodeGenContext<'ctx, '_>,
pobj: PointerValue<'ctx>,

View File

@ -1,13 +1,16 @@
use inkwell::{
context::Context,
types::{BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{BasicValueEnum, PointerValue, StructValue},
types::{BasicType, BasicTypeEnum, IntType, StructType},
values::BasicValueEnum,
};
use itertools::Itertools;
use super::ProxyType;
use crate::{
codegen::{CodeGenContext, CodeGenerator, values::TupleValue},
codegen::{
values::{ProxyValue, TupleValue},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::{Type, TypeEnum},
};
@ -18,6 +21,11 @@ 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> {
@ -74,18 +82,12 @@ impl<'ctx> TupleType<'ctx> {
/// Creates an [`TupleType`] from a [`StructType`].
#[must_use]
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());
pub fn from_type(struct_ty: StructType<'ctx>, llvm_usize: IntType<'ctx>) -> Self {
debug_assert!(Self::is_representable(struct_ty).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 {
@ -110,13 +112,17 @@ 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> {
unsafe { self.ty.get_field_type_at_index_unchecked(index) }
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, name: Option<&'ctx str>) -> <Self as ProxyType<'ctx>>::Value {
self.map_struct_value(self.as_abi_type().const_zero(), name)
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)
}
/// Constructs a [`TupleValue`] from `objects`. The resulting tuple preserves the order of
@ -131,15 +137,14 @@ 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(name);
let mut value = self.construct(ctx, name);
for (i, val) in values.into_iter().enumerate() {
value.insert_element(ctx, i as u32, val);
value.store_element(ctx, i as u32, val);
}
value
@ -147,44 +152,37 @@ impl<'ctx> TupleType<'ctx> {
/// Converts an existing value into a [`ListValue`].
#[must_use]
pub fn map_struct_value(
pub fn map_value(
&self,
value: StructValue<'ctx>,
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_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_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
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)
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} else {
Err(format!("Expected struct type, got {llvm_ty:?}"))
}
}
fn has_same_repr(_: Self::Base, _: IntType<'ctx>) -> Result<(), String> {
Ok(())
fn is_representable<G: CodeGenerator + ?Sized>(
_generator: &G,
_ctx: &'ctx Context,
llvm_ty: Self::Base,
) -> Result<(), String> {
Self::is_representable(llvm_ty)
}
fn alloca_type(&self) -> impl BasicType<'ctx> {
@ -194,10 +192,6 @@ 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> {

View File

@ -1,23 +1,22 @@
use inkwell::{
AddressSpace,
context::{AsContextRef, Context, ContextRef},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType},
values::IntValue,
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use crate::codegen::{
CodeGenContext, CodeGenerator,
types::{
ProxyType,
structure::{
FieldIndexCounter, StructField, StructFields, StructProxyType,
check_struct_type_matches_fields,
check_struct_type_matches_fields, FieldIndexCounter, StructField, StructFields,
},
ProxyType,
},
values::utils::SliceValue,
values::{utils::SliceValue, ProxyValue},
CodeGenContext, CodeGenerator,
};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@ -28,7 +27,7 @@ pub struct SliceType<'ctx> {
}
#[derive(PartialEq, Eq, Clone, Copy, StructFields)]
pub struct SliceStructFields<'ctx> {
pub struct SliceFields<'ctx> {
#[value_type(bool_type())]
pub start_defined: StructField<'ctx, IntValue<'ctx>>,
#[value_type(usize)]
@ -43,14 +42,14 @@ pub struct SliceStructFields<'ctx> {
pub step: StructField<'ctx, IntValue<'ctx>>,
}
impl<'ctx> SliceStructFields<'ctx> {
/// Creates a new instance of [`SliceStructFields`] with a custom integer type for its range values.
impl<'ctx> SliceFields<'ctx> {
/// Creates a new instance of [`SliceFields`] 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();
SliceStructFields {
SliceFields {
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()),
@ -62,10 +61,60 @@ impl<'ctx> SliceStructFields<'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 = SliceStructFields::new_sized(&int_ty.get_context(), int_ty)
let field_tys = SliceFields::new_sized(&int_ty.get_context(), int_ty)
.into_iter()
.map(|field| field.1)
.collect_vec();
@ -85,16 +134,6 @@ 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 {
@ -110,24 +149,14 @@ 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_pointer_type(
pub fn from_type(
ptr_ty: PointerType<'ctx>,
int_ty: IntType<'ctx>,
llvm_usize: IntType<'ctx>,
) -> Self {
debug_assert!(Self::has_same_repr(ptr_ty, int_ty).is_ok());
debug_assert!(Self::is_representable(ptr_ty, int_ty).is_ok());
Self { ty: ptr_ty, int_ty, llvm_usize }
}
@ -172,30 +201,11 @@ 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_pointer_value(
pub fn map_value(
&self,
value: PointerValue<'ctx>,
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(
@ -208,80 +218,36 @@ 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_representable(
fn is_type<G: CodeGenerator + ?Sized>(
generator: &G,
ctx: &'ctx Context,
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)
<Self as ProxyType<'ctx>>::is_representable(generator, ctx, ty)
} 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 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 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 alloca_type(&self) -> impl BasicType<'ctx> {
self.as_abi_type().get_element_type().into_struct_type()
self.as_base_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> {

View File

@ -1,7 +1,7 @@
use inkwell::{
IntPredicate,
types::AnyTypeEnum,
values::{BasicValueEnum, IntValue, PointerValue},
IntPredicate,
};
use crate::codegen::{CodeGenContext, CodeGenerator};

View File

@ -1,188 +0,0 @@
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()
}
}

View File

@ -1,18 +1,14 @@
use inkwell::{
AddressSpace, IntPredicate,
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace, IntPredicate,
};
use super::{
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor,
UntypedArrayLikeMutator, structure::StructProxyValue,
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
};
use crate::codegen::{
types::{
ListType, ProxyType,
structure::{StructField, StructProxyType},
},
types::{structure::StructField, ListType, ProxyType},
{CodeGenContext, CodeGenerator},
};
@ -25,24 +21,13 @@ pub struct ListValue<'ctx> {
}
impl<'ctx> ListValue<'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>,
/// Checks whether `value` is an instance of `list`, returning [Err] if `value` is not an
/// instance.
pub fn is_representable(
value: PointerValue<'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)
) -> Result<(), String> {
ListType::is_representable(value.get_type(), llvm_usize)
}
/// Creates an [`ListValue`] from a [`PointerValue`].
@ -52,18 +37,24 @@ impl<'ctx> ListValue<'ctx> {
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
ListValue { value: ptr, llvm_usize, name }
}
fn items_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().items
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)
}
/// Stores the array of data elements `data` into this instance.
fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, data: PointerValue<'ctx>) {
self.items_field().store(ctx, self.value, data, self.name);
self.items_field(ctx).set(ctx, self.value, data, self.name);
}
/// Convenience method for creating a new array storing data elements with the given element
@ -101,15 +92,15 @@ impl<'ctx> ListValue<'ctx> {
ListDataProxy(self)
}
fn len_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().len
fn len_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields(&ctx.ctx).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().store(ctx, self.value, size, self.name);
self.len_field(ctx).set(ctx, self.value, size, self.name);
}
/// Returns the size of this `list` as a value.
@ -118,7 +109,7 @@ impl<'ctx> ListValue<'ctx> {
ctx: &CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> IntValue<'ctx> {
self.len_field().load(ctx, self.value, name)
self.len_field(ctx).get(ctx, self.value, name)
}
/// Returns an instance of [`ListValue`] with the `items` pointer cast to `i8*`.
@ -128,7 +119,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_abi_type(), "").unwrap(),
ctx.builder.build_pointer_cast(self.value, llvm_list_i8.as_base_type(), "").unwrap(),
self.llvm_usize,
self.name,
)
@ -136,25 +127,18 @@ 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_pointer_type(self.as_base_value().get_type(), self.llvm_usize)
ListType::from_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()
@ -179,7 +163,12 @@ impl<'ctx> ArrayLikeValue<'ctx> for ListDataProxy<'ctx, '_> {
ctx: &CodeGenContext<'ctx, '_>,
_: &G,
) -> PointerValue<'ctx> {
self.0.items_field().load(ctx, self.0.value, self.0.name)
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()
}
fn size<G: CodeGenerator + ?Sized>(

View File

@ -1,39 +1,44 @@
use inkwell::{types::IntType, values::BasicValue};
use inkwell::{context::Context, values::BasicValue};
use super::{CodeGenContext, types::ProxyType};
use super::types::ProxyType;
use crate::codegen::CodeGenerator;
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 ABI type of LLVM values represented by this instance.
type ABI: BasicValue<'ctx>;
/// The type of LLVM values represented by this instance.
/// The type of LLVM values represented by this instance. This is usually the
/// [LLVM pointer type][PointerValue].
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(value: impl BasicValue<'ctx>, llvm_usize: IntType<'ctx>) -> Result<(), String> {
Self::Type::is_representable(value.as_basic_value_enum().get_type(), llvm_usize)
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())
}
/// Returns the [type][ProxyType] of this value.
@ -41,10 +46,4 @@ 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;
}

View File

@ -1,21 +1,21 @@
use inkwell::{
types::IntType,
values::{IntValue, PointerValue, StructValue},
values::{IntValue, PointerValue},
};
use itertools::Itertools;
use crate::codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
types::{
ProxyType,
ndarray::{NDArrayType, ShapeEntryType},
structure::{StructField, StructProxyType},
structure::StructField,
ProxyType,
},
values::{
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor,
TypedArrayLikeAdapter, TypedArrayLikeMutator, ndarray::NDArrayValue,
structure::StructProxyValue,
ndarray::NDArrayValue, ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ProxyValue,
TypedArrayLikeAccessor, TypedArrayLikeAdapter, TypedArrayLikeMutator,
},
CodeGenContext, CodeGenerator,
};
#[derive(Copy, Clone)]
@ -26,24 +26,13 @@ pub struct ShapeEntryValue<'ctx> {
}
impl<'ctx> ShapeEntryValue<'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>,
/// Checks whether `value` is an instance of `ShapeEntry`, returning [Err] if `value` is
/// not an instance.
pub fn is_representable(
value: PointerValue<'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)
) -> Result<(), String> {
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
}
/// Creates an [`ShapeEntryValue`] from a [`PointerValue`].
@ -53,50 +42,43 @@ impl<'ctx> ShapeEntryValue<'ctx> {
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, llvm_usize, name }
}
fn ndims_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().ndims
self.get_type().get_fields(self.value.get_type().get_context()).ndims
}
/// Stores the number of dimensions into this value.
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
self.ndims_field().store(ctx, self.value, value, self.name);
self.ndims_field().set(ctx, self.value, value, self.name);
}
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().shape
self.get_type().get_fields(self.value.get_type().get_context()).shape
}
/// Stores the shape into this value.
pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
self.shape_field().store(ctx, self.value, value, self.name);
self.shape_field().set(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_pointer_type(self.value.get_type(), self.llvm_usize)
Self::Type::from_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()
@ -167,11 +149,9 @@ 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`.
@ -187,7 +167,7 @@ fn broadcast_shapes<'ctx, G, Shape>(
None,
)
};
let shape_entry = llvm_shape_ty.map_pointer_value(pshape_entry, None);
let shape_entry = llvm_shape_ty.map_value(pshape_entry, None);
let in_ndims = llvm_usize.const_int(*in_ndims, false);
shape_entry.store_ndims(ctx, in_ndims);

View File

@ -1,18 +1,17 @@
use inkwell::{
AddressSpace,
types::{BasicType, BasicTypeEnum, IntType},
values::{IntValue, PointerValue, StructValue},
values::{IntValue, PointerValue},
AddressSpace,
};
use super::NDArrayValue;
use super::{ArrayLikeValue, NDArrayValue, ProxyValue};
use crate::codegen::{
CodeGenContext, CodeGenerator,
stmt::gen_if_callback,
types::{
ndarray::{ContiguousNDArrayType, NDArrayType},
structure::{StructField, StructProxyType},
structure::StructField,
},
values::{ArrayLikeValue, ProxyValue, structure::StructProxyValue},
CodeGenContext, CodeGenerator,
};
#[derive(Copy, Clone)]
@ -24,25 +23,13 @@ pub struct ContiguousNDArrayValue<'ctx> {
}
impl<'ctx> ContiguousNDArrayValue<'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>,
/// Checks whether `value` is an instance of `ContiguousNDArray`, returning [Err] if `value` is
/// not an instance.
pub fn is_representable(
value: PointerValue<'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, dtype, llvm_usize, name)
) -> Result<(), String> {
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
}
/// Creates an [`ContiguousNDArrayValue`] from a [`PointerValue`].
@ -53,7 +40,7 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, item: dtype, llvm_usize, name }
}
@ -63,7 +50,7 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
}
pub fn store_ndims(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
self.ndims_field().store(ctx, self.as_abi_value(ctx), value, self.name);
self.ndims_field().set(ctx, self.as_base_value(), value, self.name);
}
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
@ -71,11 +58,11 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
}
pub fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
self.shape_field().store(ctx, self.as_abi_value(ctx), value, self.name);
self.shape_field().set(ctx, self.as_base_value(), value, self.name);
}
pub fn load_shape(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.shape_field().load(ctx, self.value, self.name)
self.shape_field().get(ctx, self.value, self.name)
}
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
@ -83,21 +70,20 @@ impl<'ctx> ContiguousNDArrayValue<'ctx> {
}
pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
self.data_field().store(ctx, self.as_abi_value(ctx), value, self.name);
self.data_field().set(ctx, self.as_base_value(), value, self.name);
}
pub fn load_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.data_field().load(ctx, self.value, self.name)
self.data_field().get(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_pointer_type(
<Self as ProxyValue<'ctx>>::Type::from_type(
self.as_base_value().get_type(),
self.item,
self.llvm_usize,
@ -107,14 +93,8 @@ 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()
@ -153,7 +133,7 @@ impl<'ctx> NDArrayValue<'ctx> {
|_, ctx| Ok(self.is_c_contiguous(ctx)),
|_, ctx| {
// This ndarray is contiguous.
let data = self.data_field().load(ctx, self.as_abi_value(ctx), self.name);
let data = self.data_field(ctx).get(ctx, self.as_base_value(), self.name);
let data = ctx
.builder
.build_pointer_cast(data, result.item.ptr_type(AddressSpace::default()), "")

View File

@ -2,9 +2,9 @@ use inkwell::values::{BasicValue, BasicValueEnum};
use super::{NDArrayValue, NDIterValue, ScalarOrNDArray};
use crate::codegen::{
CodeGenContext, CodeGenerator,
stmt::{BreakContinueHooks, gen_for_callback},
stmt::{gen_for_callback, BreakContinueHooks},
types::ndarray::NDIterType,
CodeGenContext, CodeGenerator,
};
impl<'ctx> NDArrayValue<'ctx> {

View File

@ -1,7 +1,7 @@
use inkwell::{
AddressSpace,
types::IntType,
values::{IntValue, PointerValue, StructValue},
values::{IntValue, PointerValue},
AddressSpace,
};
use itertools::Itertools;
@ -9,15 +9,14 @@ use nac3parser::ast::{Expr, ExprKind};
use crate::{
codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
types::{
ndarray::{NDArrayType, NDIndexType},
structure::{StructField, StructProxyType},
structure::StructField,
utils::SliceType,
},
values::{
ProxyValue, ndarray::NDArrayValue, structure::StructProxyValue, utils::RustSlice,
},
values::{ndarray::NDArrayValue, utils::RustSlice, ProxyValue},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::Type,
};
@ -31,24 +30,13 @@ pub struct NDIndexValue<'ctx> {
}
impl<'ctx> NDIndexValue<'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>,
/// Checks whether `value` is an instance of `ndindex`, returning [Err] if `value` is not an
/// instance.
pub fn is_representable(
value: PointerValue<'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)
) -> Result<(), String> {
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
}
/// Creates an [`NDIndexValue`] from a [`PointerValue`].
@ -58,7 +46,7 @@ impl<'ctx> NDIndexValue<'ctx> {
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, llvm_usize, name }
}
@ -68,11 +56,11 @@ impl<'ctx> NDIndexValue<'ctx> {
}
pub fn load_type(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.type_field().load(ctx, self.value, self.name)
self.type_field().get(ctx, self.value, self.name)
}
pub fn store_type(&self, ctx: &CodeGenContext<'ctx, '_>, value: IntValue<'ctx>) {
self.type_field().store(ctx, self.value, value, self.name);
self.type_field().set(ctx, self.value, value, self.name);
}
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
@ -80,34 +68,27 @@ impl<'ctx> NDIndexValue<'ctx> {
}
pub fn load_data(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
self.data_field().load(ctx, self.value, self.name)
self.data_field().get(ctx, self.value, self.name)
}
pub fn store_data(&self, ctx: &CodeGenContext<'ctx, '_>, value: PointerValue<'ctx>) {
self.data_field().store(ctx, self.value, value, self.name);
self.data_field().set(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_pointer_type(self.value.get_type(), self.llvm_usize)
Self::Type::from_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()

View File

@ -1,11 +1,11 @@
use inkwell::{types::BasicTypeEnum, values::BasicValueEnum};
use crate::codegen::{
CodeGenContext, CodeGenerator,
values::{
ProxyValue,
ndarray::{NDArrayOut, NDArrayValue, ScalarOrNDArray},
ProxyValue,
},
CodeGenContext, CodeGenerator,
};
impl<'ctx> NDArrayValue<'ctx> {

View File

@ -5,7 +5,6 @@ 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,
@ -14,6 +13,7 @@ use crate::{
ArrayLikeValue, ArraySliceValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
UntypedArrayLikeAccessor, UntypedArrayLikeMutator,
},
CodeGenContext, CodeGenerator,
},
toplevel::helper::arraylike_flatten_element_type,
typecheck::{magic_methods::Binop, typedef::Type},
@ -213,7 +213,9 @@ 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();
@ -224,7 +226,9 @@ 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(())

View File

@ -1,28 +1,25 @@
use std::iter::repeat_n;
use inkwell::{
AddressSpace, IntPredicate,
types::{AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, IntType},
values::{BasicValue, BasicValueEnum, IntValue, PointerValue, StructValue},
values::{BasicValue, BasicValueEnum, IntValue, PointerValue},
AddressSpace, IntPredicate,
};
use itertools::Itertools;
use super::{
ArrayLikeIndexer, ArrayLikeValue, ProxyValue, TupleValue, TypedArrayLikeAccessor,
TypedArrayLikeAdapter, TypedArrayLikeMutator, UntypedArrayLikeAccessor,
UntypedArrayLikeMutator, structure::StructProxyValue,
UntypedArrayLikeMutator,
};
use crate::{
codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
llvm_intrinsics::{call_int_umin, call_memcpy_generic_array},
stmt::gen_for_callback_incrementing,
type_aligned_alloca,
types::{
TupleType,
ndarray::NDArrayType,
structure::{StructField, StructProxyType},
},
types::{ndarray::NDArrayType, structure::StructField, TupleType},
CodeGenContext, CodeGenerator,
},
typecheck::typedef::{Type, TypeEnum},
};
@ -52,26 +49,13 @@ pub struct NDArrayValue<'ctx> {
}
impl<'ctx> NDArrayValue<'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,
/// Checks whether `value` is an instance of `NDArray`, returning [Err] if `value` is not an
/// instance.
pub fn is_representable(
value: PointerValue<'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, dtype, ndims, llvm_usize, name)
) -> Result<(), String> {
NDArrayType::is_representable(value.get_type(), llvm_usize)
}
/// Creates an [`NDArrayValue`] from a [`PointerValue`].
@ -83,50 +67,57 @@ impl<'ctx> NDArrayValue<'ctx> {
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
NDArrayValue { value: ptr, dtype, ndims, llvm_usize, name }
}
fn ndims_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().ndims
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)
}
/// 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());
self.ndims_field().store(ctx, self.value, ndims, self.name);
let pndims = self.ptr_to_ndims(ctx);
ctx.builder.build_store(pndims, ndims).unwrap();
}
/// Returns the number of dimensions of this `NDArray` as a value.
pub fn load_ndims(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.ndims_field().load(ctx, self.value, self.name)
let pndims = self.ptr_to_ndims(ctx);
ctx.builder.build_load(pndims, "").map(BasicValueEnum::into_int_value).unwrap()
}
fn itemsize_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().itemsize
fn itemsize_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields(ctx.ctx).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().store(ctx, self.value, itemsize, self.name);
self.itemsize_field(ctx).set(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().load(ctx, self.value, self.name)
self.itemsize_field(ctx).get(ctx, self.value, self.name)
}
fn shape_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().shape
fn shape_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields(ctx.ctx).shape
}
/// Stores the array of dimension sizes `dims` into this instance.
fn store_shape(&self, ctx: &CodeGenContext<'ctx, '_>, dims: PointerValue<'ctx>) {
self.shape_field().store(ctx, self.value, dims, self.name);
self.shape_field(ctx).set(ctx, self.as_base_value(), dims, self.name);
}
/// Convenience method for creating a new array storing dimension sizes with the given `size`.
@ -145,13 +136,16 @@ impl<'ctx> NDArrayValue<'ctx> {
NDArrayShapeProxy(self)
}
fn strides_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().strides
fn strides_field(
&self,
ctx: &CodeGenContext<'ctx, '_>,
) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields(ctx.ctx).strides
}
/// Stores the array of stride sizes `strides` into this instance.
fn store_strides(&self, ctx: &CodeGenContext<'ctx, '_>, strides: PointerValue<'ctx>) {
self.strides_field().store(ctx, self.value, strides, self.name);
self.strides_field(ctx).set(ctx, self.as_base_value(), strides, self.name);
}
/// Convenience method for creating a new array storing the stride with the given `size`.
@ -170,14 +164,14 @@ impl<'ctx> NDArrayValue<'ctx> {
NDArrayStridesProxy(self)
}
fn data_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().data
fn data_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields(ctx.ctx).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().ptr_by_gep(ctx, self.value, self.name)
self.data_field(ctx).ptr_by_gep(ctx, self.value, self.name)
}
/// Stores the array of data elements `data` into this instance.
@ -186,7 +180,7 @@ impl<'ctx> NDArrayValue<'ctx> {
.builder
.build_bit_cast(data, ctx.ctx.i8_type().ptr_type(AddressSpace::default()), "")
.unwrap();
self.data_field().store(ctx, self.value, data.into_pointer_value(), self.name);
self.data_field(ctx).set(ctx, self.as_base_value(), data.into_pointer_value(), self.name);
}
/// Convenience method for creating a new array storing data elements with the given element
@ -225,7 +219,13 @@ 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);
call_memcpy_generic_array(
ctx,
self.shape().base_ptr(ctx, generator),
shape,
num_items,
ctx.ctx.bool_type().const_zero(),
);
}
/// Copy shape dimensions from an ndarray.
@ -251,7 +251,13 @@ 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);
call_memcpy_generic_array(
ctx,
self.strides().base_ptr(ctx, generator),
strides,
num_items,
ctx.ctx.bool_type().const_zero(),
);
}
/// Copy strides dimensions from an ndarray.
@ -465,12 +471,11 @@ 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_pointer_type(
NDArrayType::from_type(
self.as_base_value().get_type(),
self.dtype,
self.ndims,
@ -481,14 +486,8 @@ 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()
@ -513,7 +512,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayShapeProxy<'ctx, '_> {
ctx: &CodeGenContext<'ctx, '_>,
_: &G,
) -> PointerValue<'ctx> {
self.0.shape_field().load(ctx, self.0.value, self.0.name)
self.0.shape_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
}
fn size<G: CodeGenerator + ?Sized>(
@ -611,7 +610,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayStridesProxy<'ctx, '_> {
ctx: &CodeGenContext<'ctx, '_>,
_: &G,
) -> PointerValue<'ctx> {
self.0.strides_field().load(ctx, self.0.value, self.0.name)
self.0.strides_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
}
fn size<G: CodeGenerator + ?Sized>(
@ -709,7 +708,7 @@ impl<'ctx> ArrayLikeValue<'ctx> for NDArrayDataProxy<'ctx, '_> {
ctx: &CodeGenContext<'ctx, '_>,
_: &G,
) -> PointerValue<'ctx> {
self.0.data_field().load(ctx, self.0.value, self.0.name)
self.0.data_field(ctx).get(ctx, self.0.as_base_value(), self.0.name)
}
fn size<G: CodeGenerator + ?Sized>(
@ -963,7 +962,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_pointer_value(object.into_pointer_value(), None);
.map_value(object.into_pointer_value(), None);
ScalarOrNDArray::NDArray(ndarray)
}
@ -976,7 +975,7 @@ impl<'ctx> ScalarOrNDArray<'ctx> {
pub fn to_basic_value_enum(self) -> BasicValueEnum<'ctx> {
match self {
ScalarOrNDArray::Scalar(scalar) => scalar,
ScalarOrNDArray::NDArray(ndarray) => ndarray.value.into(),
ScalarOrNDArray::NDArray(ndarray) => ndarray.as_base_value().into(),
}
}

View File

@ -1,18 +1,16 @@
use inkwell::{
AddressSpace,
types::{BasicType, IntType},
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace,
};
use super::NDArrayValue;
use super::{NDArrayValue, ProxyValue};
use crate::codegen::{
CodeGenContext, CodeGenerator, irrt,
stmt::{BreakContinueHooks, gen_for_callback},
types::{
ndarray::NDIterType,
structure::{StructField, StructProxyType},
},
values::{ArraySliceValue, ProxyValue, TypedArrayLikeAdapter, structure::StructProxyValue},
irrt,
stmt::{gen_for_callback, BreakContinueHooks},
types::{ndarray::NDIterType, structure::StructField},
values::{ArraySliceValue, TypedArrayLikeAdapter},
CodeGenContext, CodeGenerator,
};
#[derive(Copy, Clone)]
@ -25,26 +23,13 @@ pub struct NDIterValue<'ctx> {
}
impl<'ctx> NDIterValue<'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>,
/// Checks whether `value` is an instance of `NDArray`, returning [Err] if `value` is not an
/// instance.
pub fn is_representable(
value: PointerValue<'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, parent, indices, llvm_usize, name)
) -> Result<(), String> {
<Self as ProxyValue>::Type::is_representable(value.get_type(), llvm_usize)
}
/// Creates an [`NDArrayValue`] from a [`PointerValue`].
@ -56,7 +41,7 @@ impl<'ctx> NDIterValue<'ctx> {
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, parent, indices, llvm_usize, name }
}
@ -80,8 +65,11 @@ impl<'ctx> NDIterValue<'ctx> {
irrt::ndarray::call_nac3_nditer_next(ctx, *self);
}
fn element_field(&self) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields().element
fn element_field(
&self,
ctx: &CodeGenContext<'ctx, '_>,
) -> StructField<'ctx, PointerValue<'ctx>> {
self.get_type().get_fields(ctx.ctx).element
}
/// Get pointer to the current element.
@ -89,7 +77,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().load(ctx, self.as_abi_value(ctx), self.name);
let p = self.element_field(ctx).get(ctx, self.as_base_value(), self.name);
ctx.builder
.build_pointer_cast(p, elem_ty.ptr_type(AddressSpace::default()), "element")
.unwrap()
@ -102,14 +90,14 @@ impl<'ctx> NDIterValue<'ctx> {
ctx.builder.build_load(p, "value").unwrap()
}
fn nth_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields().nth
fn nth_field(&self, ctx: &CodeGenContext<'ctx, '_>) -> StructField<'ctx, IntValue<'ctx>> {
self.get_type().get_fields(ctx.ctx).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().load(ctx, self.as_abi_value(ctx), self.name)
self.nth_field(ctx).get(ctx, self.as_base_value(), self.name)
}
/// Get the indices of the current element.
@ -126,25 +114,18 @@ 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_pointer_type(self.as_base_value().get_type(), self.llvm_usize)
NDIterType::from_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()

View File

@ -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>> + use<'ctx, G> {
) -> impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>> {
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_pointer_value(input_seq.into_pointer_value(), None);
.map_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_struct_value(input_seq.into_struct_value(), None);
.map_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.extract_element(ctx, i).into_int_value();
let int = input_seq.load_element(ctx, i).into_int_value();
let int = ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
unsafe {

View File

@ -4,13 +4,14 @@ use inkwell::values::{IntValue, PointerValue};
use itertools::Itertools;
use crate::codegen::{
CodeGenContext, CodeGenerator, irrt,
irrt,
stmt::gen_if_callback,
types::ndarray::NDArrayType,
values::{
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
ndarray::{NDArrayValue, RustNDIndex},
ArrayLikeValue, ArraySliceValue, ProxyValue, TypedArrayLikeAccessor, TypedArrayLikeAdapter,
},
CodeGenContext, CodeGenerator,
};
impl<'ctx> NDArrayValue<'ctx> {

View File

@ -1,75 +0,0 @@
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()
}
}

View File

@ -1,50 +1,27 @@
use inkwell::{
types::IntType,
values::{ArrayValue, BasicValueEnum, IntValue, PointerValue},
};
use inkwell::values::{BasicValueEnum, IntValue, PointerValue};
use super::ProxyValue;
use crate::codegen::{CodeGenContext, CodeGenerator, types::RangeType};
use crate::codegen::{types::RangeType, CodeGenContext};
/// 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> {
/// 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)
/// 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_pointer_value(
ptr: PointerValue<'ctx>,
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
pub fn from_pointer_value(ptr: PointerValue<'ctx>, name: Option<&'ctx str>) -> Self {
debug_assert!(Self::is_representable(ptr).is_ok());
RangeValue { value: ptr, llvm_usize, name }
RangeValue { value: ptr, name }
}
fn ptr_to_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> PointerValue<'ctx> {
@ -54,7 +31,7 @@ impl<'ctx> RangeValue<'ctx> {
unsafe {
ctx.builder
.build_in_bounds_gep(
self.as_abi_value(ctx),
self.as_base_value(),
&[llvm_i32.const_zero(), llvm_i32.const_int(0, false)],
var_name.as_str(),
)
@ -69,7 +46,7 @@ impl<'ctx> RangeValue<'ctx> {
unsafe {
ctx.builder
.build_in_bounds_gep(
self.as_abi_value(ctx),
self.as_base_value(),
&[llvm_i32.const_zero(), llvm_i32.const_int(1, false)],
var_name.as_str(),
)
@ -84,7 +61,7 @@ impl<'ctx> RangeValue<'ctx> {
unsafe {
ctx.builder
.build_in_bounds_gep(
self.as_abi_value(ctx),
self.as_base_value(),
&[llvm_i32.const_zero(), llvm_i32.const_int(2, false)],
var_name.as_str(),
)
@ -157,21 +134,16 @@ 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_pointer_type(self.value.get_type(), self.llvm_usize)
RangeType::from_type(self.value.get_type())
}
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> {

View File

@ -1,87 +0,0 @@
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()
}
}

View File

@ -1,24 +0,0 @@
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()
}
}

View File

@ -1,10 +1,10 @@
use inkwell::{
types::IntType,
values::{BasicValue, BasicValueEnum, PointerValue, StructValue},
values::{BasicValue, BasicValueEnum, StructValue},
};
use super::ProxyValue;
use crate::codegen::{CodeGenContext, types::TupleType};
use crate::codegen::{types::TupleType, CodeGenContext};
#[derive(Copy, Clone)]
pub struct TupleValue<'ctx> {
@ -14,6 +14,15 @@ 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(
@ -21,31 +30,13 @@ impl<'ctx> TupleValue<'ctx> {
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(value, llvm_usize).is_ok());
debug_assert!(Self::is_representable(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 insert_element(
pub fn store_element(
&mut self,
ctx: &CodeGenContext<'ctx, '_>,
index: u32,
@ -63,11 +54,7 @@ impl<'ctx> TupleValue<'ctx> {
}
/// Loads a value from the tuple element at the given `index`.
pub fn extract_element(
&self,
ctx: &CodeGenContext<'ctx, '_>,
index: u32,
) -> BasicValueEnum<'ctx> {
pub fn load_element(&self, ctx: &CodeGenContext<'ctx, '_>, index: u32) -> BasicValueEnum<'ctx> {
ctx.builder
.build_extract_value(
self.value,
@ -79,21 +66,16 @@ 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_struct_type(self.as_base_value().get_type(), self.llvm_usize)
TupleType::from_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> {

View File

@ -1,18 +1,15 @@
use inkwell::{
types::IntType,
values::{IntValue, PointerValue, StructValue},
values::{IntValue, PointerValue},
};
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,
};
@ -27,25 +24,13 @@ pub struct SliceValue<'ctx> {
}
impl<'ctx> SliceValue<'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>,
/// Checks whether `value` is an instance of `ContiguousNDArray`, returning [Err] if `value` is
/// not an instance.
pub fn is_representable(
value: PointerValue<'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, int_ty, llvm_usize, name)
) -> Result<(), String> {
<Self as ProxyValue<'ctx>>::Type::is_representable(value.get_type(), llvm_usize)
}
/// Creates an [`SliceValue`] from a [`PointerValue`].
@ -56,7 +41,7 @@ impl<'ctx> SliceValue<'ctx> {
llvm_usize: IntType<'ctx>,
name: Option<&'ctx str>,
) -> Self {
debug_assert!(Self::is_instance(ptr, llvm_usize).is_ok());
debug_assert!(Self::is_representable(ptr, llvm_usize).is_ok());
Self { value: ptr, int_ty, llvm_usize, name }
}
@ -66,7 +51,7 @@ impl<'ctx> SliceValue<'ctx> {
}
pub fn load_start_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.start_defined_field().load(ctx, self.value, self.name)
self.start_defined_field().get(ctx, self.value, self.name)
}
fn start_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
@ -74,22 +59,22 @@ impl<'ctx> SliceValue<'ctx> {
}
pub fn load_start(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.start_field().load(ctx, self.value, self.name)
self.start_field().get(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().store(
self.start_defined_field().set(
ctx,
self.value,
ctx.ctx.bool_type().const_all_ones(),
self.name,
);
self.start_field().store(ctx, self.value, start, self.name);
self.start_field().set(ctx, self.value, start, self.name);
}
None => self.start_defined_field().store(
None => self.start_defined_field().set(
ctx,
self.value,
ctx.ctx.bool_type().const_zero(),
@ -103,7 +88,7 @@ impl<'ctx> SliceValue<'ctx> {
}
pub fn load_stop_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.stop_defined_field().load(ctx, self.value, self.name)
self.stop_defined_field().get(ctx, self.value, self.name)
}
fn stop_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
@ -111,22 +96,22 @@ impl<'ctx> SliceValue<'ctx> {
}
pub fn load_stop(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.stop_field().load(ctx, self.value, self.name)
self.stop_field().get(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().store(
self.stop_defined_field().set(
ctx,
self.value,
ctx.ctx.bool_type().const_all_ones(),
self.name,
);
self.stop_field().store(ctx, self.value, stop, self.name);
self.stop_field().set(ctx, self.value, stop, self.name);
}
None => self.stop_defined_field().store(
None => self.stop_defined_field().set(
ctx,
self.value,
ctx.ctx.bool_type().const_zero(),
@ -140,7 +125,7 @@ impl<'ctx> SliceValue<'ctx> {
}
pub fn load_step_defined(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.step_defined_field().load(ctx, self.value, self.name)
self.step_defined_field().get(ctx, self.value, self.name)
}
fn step_field(&self) -> StructField<'ctx, IntValue<'ctx>> {
@ -148,22 +133,22 @@ impl<'ctx> SliceValue<'ctx> {
}
pub fn load_step(&self, ctx: &CodeGenContext<'ctx, '_>) -> IntValue<'ctx> {
self.step_field().load(ctx, self.value, self.name)
self.step_field().get(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().store(
self.step_defined_field().set(
ctx,
self.value,
ctx.ctx.bool_type().const_all_ones(),
self.name,
);
self.step_field().store(ctx, self.value, step, self.name);
self.step_field().set(ctx, self.value, step, self.name);
}
None => self.step_defined_field().store(
None => self.step_defined_field().set(
ctx,
self.value,
ctx.ctx.bool_type().const_zero(),
@ -174,25 +159,18 @@ 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_pointer_type(self.value.get_type(), self.int_ty, self.llvm_usize)
Self::Type::from_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()

View File

@ -6,14 +6,14 @@ use std::{
};
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
use itertools::{Itertools, izip};
use itertools::{izip, Itertools};
use parking_lot::RwLock;
use nac3parser::ast::{Constant, Expr, Location, StrRef};
use crate::{
codegen::{CodeGenContext, CodeGenerator},
toplevel::{DefinitionId, TopLevelDef, type_annotation::TypeAnnotation},
toplevel::{type_annotation::TypeAnnotation, DefinitionId, TopLevelDef},
typecheck::{
type_inferencer::PrimitiveStore,
typedef::{Type, TypeEnum, Unifier, VarMap},

View File

@ -1,13 +1,13 @@
use std::iter::once;
use indexmap::IndexMap;
use inkwell::{IntPredicate, values::BasicValue};
use inkwell::{values::BasicValue, IntPredicate};
use strum::IntoEnumIterator;
use super::{
helper::{
PrimDef, PrimDefDetails, arraylike_flatten_element_type, debug_assert_prim_is_allowed,
extract_ndims, make_exception_fields,
arraylike_flatten_element_type, debug_assert_prim_is_allowed, extract_ndims,
make_exception_fields, PrimDef, PrimDefDetails,
},
numpy::{make_ndarray_ty, unpack_ndarray_var_tys},
*,
@ -17,14 +17,14 @@ use crate::{
builtin_fns,
numpy::*,
stmt::{exn_constructor, gen_if_callback},
types::{RangeType, ndarray::NDArrayType},
types::ndarray::NDArrayType,
values::{
ProxyValue,
ndarray::{ScalarOrNDArray, shape::parse_numpy_int_sequence},
ndarray::{shape::parse_numpy_int_sequence, ScalarOrNDArray},
ProxyValue, RangeValue,
},
},
symbol_resolver::SymbolValue,
typecheck::typedef::{TypeVar, VarMap, into_var_map, iter_type_vars},
typecheck::typedef::{into_var_map, iter_type_vars, TypeVar, VarMap},
};
type BuiltinInfo = Vec<(Arc<RwLock<TopLevelDef>>, Option<Stmt>)>;
@ -36,7 +36,9 @@ pub fn get_exn_constructor(
unifier: &mut Unifier,
primitives: &PrimitiveStore,
) -> (TopLevelDef, TopLevelDef, Type, Type) {
let PrimitiveStore { int32, int64, str: string, .. } = *primitives;
let int32 = primitives.int32;
let int64 = primitives.int64;
let string = primitives.str;
let exception_fields = make_exception_fields(int32, int64, string);
let exn_cons_args = vec![
FuncArg {
@ -479,9 +481,7 @@ 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 = RangeType::new(ctx).map_pointer_value(zelf, Some("range"));
let zelf = RangeValue::from_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_abi_value(ctx).into()))
Ok(Some(zelf.as_base_value().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_pointer_value(ndarray.into_pointer_value(), None);
.map_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_pointer_value(ndarray.into_pointer_value(), None);
.map_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_abi_value(ctx).into()))
Ok(Some(result_tuple.as_base_value().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_pointer_value(arg_val.into_pointer_value(), None);
.map_value(arg_val.into_pointer_value(), None);
let ndarray = ndarray.transpose(generator, ctx, None); // TODO: Add axes argument
Ok(Some(ndarray.as_abi_value(ctx).into()))
Ok(Some(ndarray.as_base_value().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_pointer_value(ndarray_val.into_pointer_value(), None);
.map_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_abi_value(ctx).as_basic_value_enum()))
Ok(Some(new_ndarray.as_base_value().as_basic_value_enum()))
}),
)
}

View File

@ -1,7 +1,7 @@
use std::rc::Rc;
use indexmap::IndexMap;
use nac3parser::ast::{ExprKind, Ident, fold::Fold};
use nac3parser::ast::{fold::Fold, ExprKind, Ident};
use super::*;
use crate::{
@ -265,7 +265,8 @@ 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() {
@ -276,7 +277,8 @@ 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
));
}
@ -292,7 +294,7 @@ impl TopLevelComposer {
resolver.clone(),
fully_qualified_class_name,
Some(constructor_ty),
Some(ast.location),
Some(ast.location)
))),
None,
);
@ -319,7 +321,8 @@ 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(
@ -329,7 +332,8 @@ 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() + {
@ -376,11 +380,7 @@ 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,7 +393,8 @@ 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
));
}
@ -407,7 +408,7 @@ impl TopLevelComposer {
// dummy here, unify with correct type later
ty_to_be_unified,
resolver,
Some(ast.location),
Some(ast.location)
))
.into(),
Some(ast),
@ -431,10 +432,7 @@ 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, .. } => {
@ -463,9 +461,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,
@ -1236,7 +1234,7 @@ impl TopLevelComposer {
ExprKind::Subscript { value, slice, .. }
if matches!(
&value.node,
ast::ExprKind::Name { id, .. } if core_config.kernel_ann.is_some_and(|c| id == &c.into())
ast::ExprKind::Name { id, .. } if core_config.kernel_ann.map_or(false, |c| id == &c.into())
) =>
{
(slice, true)
@ -1407,14 +1405,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)> =
@ -1443,10 +1441,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(())
}
@ -1523,7 +1521,8 @@ impl TopLevelComposer {
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
{
// create constructor for these classes
let PrimitiveStore { str: string, int64, .. } = *primitives_ty;
let string = primitives_ty.str;
let int64 = primitives_ty.int64;
let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![
FuncArg {
@ -1623,10 +1622,14 @@ 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,
),
]));
}
}
}
@ -1898,8 +1901,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();
@ -1913,8 +1916,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})"),
]));
}
}
}
@ -1999,15 +2002,13 @@ impl TopLevelComposer {
ExprKind::Subscript { value, slice, .. }
if matches!(
&value.node,
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()
ast::ExprKind::Name { id, .. } if self.core_config.kernel_ann.map_or(false, |c| id == &c.into())
) =>
{
slice
}
_ if self.core_config.kernel_ann.is_none() => ty_decl,
_ => unreachable!(
"Global variables should be annotated with Kernel[] or KernelInvariant[]"
), // ignore fields annotated otherwise
_ => unreachable!("Global variables should be annotated with Kernel[]"), // ignore fields annotated otherwise
};
let ty_annotation = parse_ast_to_type_annotation_kinds(

View File

@ -8,7 +8,7 @@ use nac3parser::ast::{Constant, ExprKind, Location};
use super::{numpy::unpack_ndarray_var_tys, *};
use crate::{
symbol_resolver::SymbolValue,
typecheck::typedef::{Mapping, TypeVarId, VarMap, into_var_map, iter_type_vars},
typecheck::typedef::{into_var_map, iter_type_vars, Mapping, TypeVarId, VarMap},
};
/// 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,10 +1038,7 @@ 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
@ -1236,9 +1233,7 @@ 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