1
0
forked from M-Labs/nac3

Compare commits

...

15 Commits

Author SHA1 Message Date
82a580c5c6 flake: update ARTIQ source used for PGO 2025-02-10 16:53:35 +08:00
715dc71396 nac3artiq: acquire special python identifiers 2025-02-10 16:42:49 +08:00
064aa0411f [core] codegen: Add Exception{Type,Value} 2025-02-10 11:29:58 +08:00
57552fb2f6 [core] codegen: Add Option{Type,Value} 2025-02-10 11:29:58 +08:00
35e9c5b38e [core] codegen: Add String{Type,Value} 2025-02-10 11:29:58 +08:00
0a761cb263 [core] Use more TupleType constructors 2025-02-10 11:29:58 +08:00
67f42185de [core] codegen/expr: Add concrete ndims value to error message 2025-02-10 11:29:58 +08:00
69542c38a2 [core] codegen: Rename TupleValue::{store,load} -> {insert,extract}
Better matches the underlying operation.
2025-02-10 11:29:55 +08:00
2df22e29f7 [core] codegen: Simplify TupleType::construct 2025-02-10 11:26:45 +08:00
a078481cd2 [meta] Minor simplification for PrimStore extraction 2025-02-10 11:26:45 +08:00
c37c7e8975 [core] codegen/expr: Simplify gen_*_expr_with_values return value
These functions always return `BasicValueEnum` because they operate on
`BasicValueEnum`s, and they also always return a value.
2025-02-10 11:26:45 +08:00
0d8cb909dd [core] codegen/expr: Fix and use gen_unaryop_expr for boolean not ops
While refactoring, I ran into the issue where `!true == true`, which was
caused by the same upper 7-bit of booleans being undefined issue that
was encountered before. It turns out the implementation in
`gen_unaryop_expr` is also inadequate, as `(~v & (i1) 0x1)`` will still
leave upper 7 bits undefined (for whatever reason).

This commit fixes this issue once and for all by using a combination of
`icmp` + `zext` to ensure that the resulting value must be `0 | 1`, and
refactor to use that whenever we need to invert boolean values.
2025-02-10 11:26:45 +08:00
529fa67855 [core] codegen: Add bool_to_int_type to replace bool_to_{i1,i8}
Unifies the implementation for both functions.
2025-02-10 11:26:45 +08:00
f52ba9f151 [core] codegen/irrt: Refactor IRRT to use more create/infer fns 2025-02-10 10:56:24 +08:00
6bcdc3ce00 [core] codegen/extern_fns: Change expansion pattern
Makes more sense to attach the parameter delimiter to the end of each
parameter.
2025-02-10 10:56:22 +08:00
32 changed files with 1611 additions and 854 deletions

View File

@ -113,8 +113,8 @@
(pkgs.fetchFromGitHub { (pkgs.fetchFromGitHub {
owner = "m-labs"; owner = "m-labs";
repo = "artiq"; repo = "artiq";
rev = "28c9de3e251daa89a8c9fd79d5ab64a3ec03bac6"; rev = "554b0749ca5985bf4d006c4f29a05e83de0a226d";
sha256 = "sha256-vAvpbHc5B+1wtG8zqN7j9dQE1ON+i22v+uqA+tw6Gak="; sha256 = "sha256-3eSNHTSlmdzLMcEMIspxqjmjrcQe4aIGqIfRgquUg18=";
}) })
]; ];
buildInputs = [ buildInputs = [

View File

@ -16,7 +16,7 @@ __all__ = [
"rpc", "ms", "us", "ns", "rpc", "ms", "us", "ns",
"print_int32", "print_int64", "print_int32", "print_int64",
"Core", "TTLOut", "Core", "TTLOut",
"parallel", "sequential" "parallel", "legacy_parallel", "sequential"
] ]
@ -245,7 +245,7 @@ class Core:
embedding = EmbeddingMap() embedding = EmbeddingMap()
if allow_registration: if allow_registration:
compiler.analyze(registered_functions, registered_classes, set()) compiler.analyze(registered_functions, registered_classes, special_ids, set())
allow_registration = False allow_registration = False
if hasattr(method, "__self__"): if hasattr(method, "__self__"):
@ -336,4 +336,11 @@ class UnwrapNoneError(Exception):
artiq_builtin = True artiq_builtin = True
parallel = KernelContextManager() parallel = KernelContextManager()
legacy_parallel = KernelContextManager()
sequential = KernelContextManager() sequential = KernelContextManager()
special_ids = {
"parallel": id(parallel),
"legacy_parallel": id(legacy_parallel),
"sequential": id(sequential),
}

View File

@ -12,10 +12,10 @@ use pyo3::{
PyObject, PyResult, Python, PyObject, PyResult, Python,
}; };
use super::{symbol_resolver::InnerResolver, timeline::TimeFns}; use super::{symbol_resolver::InnerResolver, timeline::TimeFns, SpecialPythonId};
use nac3core::{ use nac3core::{
codegen::{ codegen::{
expr::{destructure_range, gen_call}, expr::{create_fn_and_call, destructure_range, gen_call, infer_and_call_function},
llvm_intrinsics::{call_int_smax, call_memcpy, call_stackrestore, call_stacksave}, llvm_intrinsics::{call_int_smax, call_memcpy, call_stackrestore, call_stacksave},
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with}, stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
type_aligned_alloca, type_aligned_alloca,
@ -41,7 +41,10 @@ use nac3core::{
numpy::unpack_ndarray_var_tys, numpy::unpack_ndarray_var_tys,
DefinitionId, GenCall, DefinitionId, GenCall,
}, },
typecheck::typedef::{iter_type_vars, FunSignature, FuncArg, Type, TypeEnum, VarMap}, typecheck::{
type_inferencer::PrimitiveStore,
typedef::{iter_type_vars, FunSignature, FuncArg, Type, TypeEnum, VarMap},
},
}; };
/// The parallelism mode within a block. /// The parallelism mode within a block.
@ -83,6 +86,9 @@ pub struct ArtiqCodeGenerator<'a> {
/// The current parallel context refers to the nearest `with parallel` or `with legacy_parallel` /// 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. /// statement, which is used to determine when and how the timeline should be updated.
parallel_mode: ParallelMode, parallel_mode: ParallelMode,
/// Specially treated python IDs to identify `with parallel` and `with sequential` blocks.
special_ids: SpecialPythonId,
} }
impl<'a> ArtiqCodeGenerator<'a> { impl<'a> ArtiqCodeGenerator<'a> {
@ -90,6 +96,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
name: String, name: String,
size_t: IntType<'_>, size_t: IntType<'_>,
timeline: &'a (dyn TimeFns + Sync), timeline: &'a (dyn TimeFns + Sync),
special_ids: SpecialPythonId,
) -> ArtiqCodeGenerator<'a> { ) -> ArtiqCodeGenerator<'a> {
assert!(matches!(size_t.get_bit_width(), 32 | 64)); assert!(matches!(size_t.get_bit_width(), 32 | 64));
ArtiqCodeGenerator { ArtiqCodeGenerator {
@ -100,6 +107,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
end: None, end: None,
timeline, timeline,
parallel_mode: ParallelMode::None, parallel_mode: ParallelMode::None,
special_ids,
} }
} }
@ -109,9 +117,10 @@ impl<'a> ArtiqCodeGenerator<'a> {
ctx: &Context, ctx: &Context,
target_machine: &TargetMachine, target_machine: &TargetMachine,
timeline: &'a (dyn TimeFns + Sync), timeline: &'a (dyn TimeFns + Sync),
special_ids: SpecialPythonId,
) -> ArtiqCodeGenerator<'a> { ) -> ArtiqCodeGenerator<'a> {
let llvm_usize = ctx.ptr_sized_int_type(&target_machine.get_target_data(), None); let llvm_usize = ctx.ptr_sized_int_type(&target_machine.get_target_data(), None);
Self::new(name, llvm_usize, timeline) Self::new(name, llvm_usize, timeline, special_ids)
} }
/// If the generator is currently in a direct-`parallel` block context, emits IR that resets the /// If the generator is currently in a direct-`parallel` block context, emits IR that resets the
@ -257,122 +266,140 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
// - If there is a end variable, it indicates that we are (indirectly) inside a // - If there is a end variable, it indicates that we are (indirectly) inside a
// parallel block, and we should update the max end value. // parallel block, and we should update the max end value.
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node { if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
if id == &"parallel".into() || id == &"legacy_parallel".into() { let resolver = ctx.resolver.clone();
let old_start = self.start.take(); if let Some(static_value) =
let old_end = self.end.take(); if let Some((_ptr, static_value, _counter)) = ctx.var_assignment.get(id) {
let old_parallel_mode = self.parallel_mode; static_value.clone()
} else if let Some(ValueEnum::Static(val)) =
resolver.get_symbol_value(*id, ctx, self)
{
Some(val)
} else {
None
}
{
let python_id = static_value.get_unique_identifier();
if python_id == self.special_ids.parallel
|| python_id == self.special_ids.legacy_parallel
{
let old_start = self.start.take();
let old_end = self.end.take();
let old_parallel_mode = self.parallel_mode;
let now = if let Some(old_start) = &old_start { let now = if let Some(old_start) = &old_start {
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum( self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(
ctx,
self,
old_start.custom.unwrap(),
)?
} else {
self.timeline.emit_now_mu(ctx)
};
// Emulate variable allocation, as we need to use the CodeGenContext
// HashMap to store our variable due to lifetime limitation
// Note: we should be able to store variables directly if generic
// associative type is used by limiting the lifetime of CodeGenerator to
// the LLVM Context.
// The name is guaranteed to be unique as users cannot use this as variable
// name.
self.start = old_start.clone().map_or_else(
|| {
let start = format!("with-{}-start", self.name_counter).into();
let start_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: start, ctx: *name_ctx },
custom: Some(ctx.primitives.int64),
};
let start = self
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
.unwrap();
ctx.builder.build_store(start, now).unwrap();
Ok(Some(start_expr)) as Result<_, String>
},
|v| Ok(Some(v)),
)?;
let end = format!("with-{}-end", self.name_counter).into();
let end_expr = Located {
// location does not matter at this point
location: stmt.location,
node: ExprKind::Name { id: end, ctx: *name_ctx },
custom: Some(ctx.primitives.int64),
};
let end = self.gen_store_target(ctx, &end_expr, Some("end.addr"))?.unwrap();
ctx.builder.build_store(end, now).unwrap();
self.end = Some(end_expr);
self.name_counter += 1;
self.parallel_mode = if python_id == self.special_ids.parallel {
ParallelMode::Deep
} else if python_id == self.special_ids.legacy_parallel {
ParallelMode::Legacy
} else {
unreachable!()
};
self.gen_block(ctx, body.iter())?;
let current = ctx.builder.get_insert_block().unwrap();
// if the current block is terminated, move before the terminator
// we want to set the timeline before reaching the terminator
// TODO: This may be unsound if there are multiple exit paths in the
// block... e.g.
// if ...:
// return
// Perhaps we can fix this by using actual with block?
let reset_position = if let Some(terminator) = current.get_terminator() {
ctx.builder.position_before(&terminator);
true
} else {
false
};
// set duration
let end_expr = self.end.take().unwrap();
let end_val = self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(
ctx, ctx,
self, self,
old_start.custom.unwrap(), end_expr.custom.unwrap(),
)? )?;
} else {
self.timeline.emit_now_mu(ctx)
};
// Emulate variable allocation, as we need to use the CodeGenContext // inside a sequential block
// HashMap to store our variable due to lifetime limitation if old_start.is_none() {
// Note: we should be able to store variables directly if generic self.timeline.emit_at_mu(ctx, end_val);
// 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!(),
};
self.gen_block(ctx, body.iter())?; // inside a parallel block, should update the outer max now_mu
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
let current = ctx.builder.get_insert_block().unwrap(); self.parallel_mode = old_parallel_mode;
self.end = old_end;
self.start = old_start;
// if the current block is terminated, move before the terminator if reset_position {
// we want to set the timeline before reaching the terminator ctx.builder.position_at_end(current);
// 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 return Ok(());
let end_expr = self.end.take().unwrap(); } else if python_id == self.special_ids.sequential {
let end_val = self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum( // For deep parallel, temporarily take away start to avoid function calls in
ctx, // the block from resetting the timeline.
self, // This does not affect legacy parallel, as the timeline will be reset after
end_expr.custom.unwrap(), // this block finishes execution.
)?; let start = self.start.take();
self.gen_block(ctx, body.iter())?;
self.start = start;
// inside a sequential block // Reset the timeline when we are exiting the sequential block
if old_start.is_none() { // Legacy parallel does not need this, since it will be reset after codegen
self.timeline.emit_at_mu(ctx, end_val); // for this statement is completed
if self.parallel_mode == ParallelMode::Deep {
self.timeline_reset_start(ctx)?;
}
return Ok(());
} }
// inside a parallel block, should update the outer max now_mu
self.timeline_update_end_max(ctx, old_end.clone(), Some("outer.end"))?;
self.parallel_mode = old_parallel_mode;
self.end = old_end;
self.start = old_start;
if reset_position {
ctx.builder.position_at_end(current);
}
return Ok(());
} else if id == &"sequential".into() {
// For deep parallel, temporarily take away start to avoid function calls in
// the block from resetting the timeline.
// This does not affect legacy parallel, as the timeline will be reset after
// this block finishes execution.
let start = self.start.take();
self.gen_block(ctx, body.iter())?;
self.start = start;
// Reset the timeline when we are exiting the sequential block
// Legacy parallel does not need this, since it will be reset after codegen
// for this statement is completed
if self.parallel_mode == ParallelMode::Deep {
self.timeline_reset_start(ctx)?;
}
return Ok(());
} }
} }
} }
@ -389,12 +416,7 @@ fn gen_rpc_tag(
) -> Result<(), String> { ) -> Result<(), String> {
use nac3core::typecheck::typedef::TypeEnum::*; use nac3core::typecheck::typedef::TypeEnum::*;
let int32 = ctx.primitives.int32; let PrimitiveStore { int32, int64, float, bool, str, none, .. } = ctx.primitives;
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) { if ctx.unifier.unioned(ty, int32) {
buffer.push(b'i'); buffer.push(b'i');
@ -914,47 +936,14 @@ fn rpc_codegen_callback_fn<'ctx>(
} }
// call // call
if is_async { infer_and_call_function(
let rpc_send_async = ctx.module.get_function("rpc_send_async").unwrap_or_else(|| { ctx,
ctx.module.add_function( if is_async { "rpc_send_async" } else { "rpc_send" },
"rpc_send_async", None,
ctx.ctx.void_type().fn_type( &[service_id.into(), tag_ptr.into(), args_ptr.into()],
&[ Some("rpc.send"),
int32.into(), None,
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 // reclaim stack space used by arguments
call_stackrestore(ctx, stackptr); call_stackrestore(ctx, stackptr);
@ -1168,29 +1157,22 @@ fn polymorphic_print<'ctx>(
debug_assert!(!fmt.is_empty()); debug_assert!(!fmt.is_empty());
debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8); debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8);
let fn_name = if as_rtio { "rtio_log" } else { "core_log" }; let llvm_i32 = ctx.ctx.i32_type();
let print_fn = ctx.module.get_function(fn_name).unwrap_or_else(|| { let llvm_pi8 = ctx.ctx.i8_type().ptr_type(AddressSpace::default());
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 = ctx.gen_string(generator, fmt);
let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value(); let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value();
ctx.builder create_fn_and_call(
.build_call( ctx,
print_fn, if as_rtio { "rtio_log" } else { "core_log" },
&once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(), if as_rtio { None } else { Some(llvm_i32.into()) },
"", &[llvm_pi8.into()],
) &once(fmt.into()).chain(args).map(BasicValueEnum::into).collect_vec(),
.unwrap(); true,
None,
None,
);
}; };
let llvm_i32 = ctx.ctx.i32_type(); let llvm_i32 = ctx.ctx.i32_type();

View File

@ -162,6 +162,13 @@ pub struct PrimitivePythonId {
module: u64, module: u64,
} }
#[derive(Clone, Default)]
pub struct SpecialPythonId {
parallel: u64,
legacy_parallel: u64,
sequential: u64,
}
type TopLevelComponent = (Stmt, String, PyObject); type TopLevelComponent = (Stmt, String, PyObject);
// TopLevelComposer is unsendable as it holds the unification table, which is // TopLevelComposer is unsendable as it holds the unification table, which is
@ -179,6 +186,7 @@ struct Nac3 {
string_store: Arc<RwLock<HashMap<String, i32>>>, string_store: Arc<RwLock<HashMap<String, i32>>>,
exception_ids: Arc<RwLock<HashMap<usize, usize>>>, exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
deferred_eval_store: DeferredEvaluationStore, deferred_eval_store: DeferredEvaluationStore,
special_ids: SpecialPythonId,
/// LLVM-related options for code generation. /// LLVM-related options for code generation.
llvm_options: CodeGenLLVMOptions, llvm_options: CodeGenLLVMOptions,
} }
@ -797,6 +805,7 @@ impl Nac3 {
&context, &context,
&self.get_llvm_target_machine(), &self.get_llvm_target_machine(),
self.time_fns, self.time_fns,
self.special_ids.clone(),
)) ))
}) })
.collect(); .collect();
@ -813,6 +822,7 @@ impl Nac3 {
&context, &context,
&self.get_llvm_target_machine(), &self.get_llvm_target_machine(),
self.time_fns, self.time_fns,
self.special_ids.clone(),
); );
let module = context.create_module("main"); let module = context.create_module("main");
let target_machine = self.llvm_options.create_target_machine().unwrap(); let target_machine = self.llvm_options.create_target_machine().unwrap();
@ -1192,6 +1202,7 @@ impl Nac3 {
string_store: Arc::new(string_store.into()), string_store: Arc::new(string_store.into()),
exception_ids: Arc::default(), exception_ids: Arc::default(),
deferred_eval_store: DeferredEvaluationStore::new(), deferred_eval_store: DeferredEvaluationStore::new(),
special_ids: Default::default(),
llvm_options: CodeGenLLVMOptions { llvm_options: CodeGenLLVMOptions {
opt_level: OptimizationLevel::Default, opt_level: OptimizationLevel::Default,
target: isa.get_llvm_target_options(), target: isa.get_llvm_target_options(),
@ -1203,6 +1214,7 @@ impl Nac3 {
&mut self, &mut self,
functions: &PySet, functions: &PySet,
classes: &PySet, classes: &PySet,
special_ids: &PyDict,
content_modules: &PySet, content_modules: &PySet,
) -> PyResult<()> { ) -> PyResult<()> {
let (modules, class_ids) = let (modules, class_ids) =
@ -1236,6 +1248,25 @@ impl Nac3 {
for module in modules.into_values() { for module in modules.into_values() {
self.register_module(&module, &class_ids)?; 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(()) Ok(())
} }

View File

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

@ -32,7 +32,9 @@ use super::{
gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise, gen_for_callback_incrementing, gen_if_callback, gen_if_else_expr_callback, gen_raise,
gen_var, gen_var,
}, },
types::{ndarray::NDArrayType, ListType, RangeType}, types::{
ndarray::NDArrayType, ExceptionType, ListType, OptionType, RangeType, StringType, TupleType,
},
values::{ values::{
ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray}, ndarray::{NDArrayOut, RustNDIndex, ScalarOrNDArray},
ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue, ArrayLikeIndexer, ArrayLikeValue, ListValue, ProxyValue, RangeValue,
@ -43,7 +45,7 @@ use super::{
use crate::{ use crate::{
symbol_resolver::{SymbolValue, ValueEnum}, symbol_resolver::{SymbolValue, ValueEnum},
toplevel::{ toplevel::{
helper::{arraylike_flatten_element_type, PrimDef}, helper::{arraylike_flatten_element_type, extract_ndims, PrimDef},
numpy::unpack_ndarray_var_tys, numpy::unpack_ndarray_var_tys,
DefinitionId, TopLevelDef, DefinitionId, TopLevelDef,
}, },
@ -168,65 +170,27 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
SymbolValue::Bool(v) => self.ctx.i8_type().const_int(u64::from(*v), true).into(), 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::Double(v) => self.ctx.f64_type().const_float(*v).into(),
SymbolValue::Str(v) => { SymbolValue::Str(v) => {
let str_ptr = self StringType::new(self).construct_constant(self, v, None).as_abi_value(self).into()
.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) => { SymbolValue::Tuple(ls) => {
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec(); let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec();
let fields = vals.iter().map(BasicValueEnum::get_type).collect_vec(); let fields = vals.iter().map(BasicValueEnum::get_type).collect_vec();
let ty = self.ctx.struct_type(&fields, false); TupleType::new(self, &fields)
let ptr = gen_var(self, ty.into(), Some("tuple")).unwrap(); .construct_from_objects(self, vals, Some("tup_val"))
let zero = self.ctx.i32_type().const_zero(); .as_abi_value(self)
unsafe { .into()
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) => { SymbolValue::OptionSome(v) => {
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == self.primitives.option.obj_id(&self.unifier).unwrap() =>
{
*params.iter().next().unwrap().1
}
_ => codegen_unreachable!(self, "must be option type"),
};
let val = self.gen_symbol_val(generator, v, ty); let val = self.gen_symbol_val(generator, v, ty);
let ptr = generator OptionType::from_unifier_type(generator, self, ty)
.gen_var_alloc(self, val.get_type(), Some("default_opt_some")) .construct_some_value(generator, self, &val, None)
.unwrap(); .as_abi_value(self)
self.builder.build_store(ptr, val).unwrap(); .into()
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(),
} }
} }
@ -321,15 +285,10 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
if let Some(v) = self.const_strings.get(v) { if let Some(v) = self.const_strings.get(v) {
Some(*v) Some(*v)
} else { } else {
let str_ptr = self let val = StringType::new(self)
.builder .construct_constant(self, v, None)
.build_global_string_ptr(v, "const") .as_abi_value(self)
.map(|v| v.as_pointer_value().into()) .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); self.const_strings.insert(v.to_string(), val);
Some(val) Some(val)
} }
@ -619,42 +578,35 @@ impl<'ctx> CodeGenContext<'ctx, '_> {
params: [Option<IntValue<'ctx>>; 3], params: [Option<IntValue<'ctx>>; 3],
loc: Location, 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 { let zelf = if let Some(exception_val) = self.exception_val {
exception_val llvm_exn.map_pointer_value(exception_val, Some("exn"))
} else { } else {
let ty = self.get_llvm_type(generator, self.primitives.exception).into_pointer_type(); let zelf = llvm_exn.alloca_var(generator, self, Some("exn"));
let zelf_ty: BasicTypeEnum = ty.get_element_type().into_struct_type().into(); self.exception_val = Some(zelf.as_abi_value(self));
let zelf = generator.gen_var_alloc(self, zelf_ty, Some("exn")).unwrap(); zelf
*self.exception_val.insert(zelf)
}; };
let int32 = self.ctx.i32_type();
let zero = int32.const_zero(); let id = self.resolver.get_string_id(name);
unsafe { zelf.store_name(self, llvm_i32.const_int(id as u64, false));
let id_ptr = self.builder.build_in_bounds_gep(zelf, &[zero, zero], "exn.id").unwrap(); zelf.store_message(self, msg.into_struct_value());
let id = self.resolver.get_string_id(name); zelf.store_params(
self.builder.build_store(id_ptr, int32.const_int(id as u64, false)).unwrap(); self,
let ptr = self params
.builder .iter()
.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg") .map(|p| {
.unwrap(); p.map_or(llvm_i64.const_zero(), |v| {
self.builder.build_store(ptr, msg).unwrap(); self.builder.build_int_s_extend(v, self.ctx.i64_type(), "sext").unwrap()
let i64_zero = self.ctx.i64_type().const_zero(); })
for (i, attr_ind) in [6, 7, 8].iter().enumerate() { })
let ptr = self .collect_array()
.builder .as_ref()
.build_in_bounds_gep( .unwrap(),
zelf, );
&[zero, int32.const_int(*attr_ind, false)], gen_raise(generator, self, Some(&zelf), loc);
"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>( pub fn make_assert<G: CodeGenerator + ?Sized>(
@ -1319,7 +1271,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
op: Binop, op: Binop,
right: (&Option<Type>, BasicValueEnum<'ctx>), right: (&Option<Type>, BasicValueEnum<'ctx>),
loc: Location, loc: Location,
) -> Result<Option<ValueEnum<'ctx>>, String> { ) -> Result<BasicValueEnum<'ctx>, String> {
let (left_ty, left_val) = left; let (left_ty, left_val) = left;
let (right_ty, right_val) = right; let (right_ty, right_val) = right;
@ -1330,14 +1282,14 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
// which would be unchanged until further unification, which we would never do // which would be unchanged until further unification, which we would never do
// when doing code generation for function instances // when doing code generation for function instances
if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) { if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, true).into())) Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, true))
} else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) { } else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) {
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, false).into())) Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, false))
} else if [Operator::LShift, Operator::RShift].contains(&op.base) { } else if [Operator::LShift, Operator::RShift].contains(&op.base) {
let signed = [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1); let signed = [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1);
Ok(Some(ctx.gen_int_ops(generator, op.base, left_val, right_val, signed).into())) Ok(ctx.gen_int_ops(generator, op.base, left_val, right_val, signed))
} else if ty1 == ty2 && ctx.primitives.float == ty1 { } else if ty1 == ty2 && ctx.primitives.float == ty1 {
Ok(Some(ctx.gen_float_ops(op.base, left_val, right_val).into())) Ok(ctx.gen_float_ops(op.base, left_val, right_val))
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 { } else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
// Pow is the only operator that would pass typecheck between float and int // Pow is the only operator that would pass typecheck between float and int
assert_eq!(op.base, Operator::Pow); assert_eq!(op.base, Operator::Pow);
@ -1347,7 +1299,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
right_val.into_int_value(), right_val.into_int_value(),
Some("f_pow_i"), Some("f_pow_i"),
); );
Ok(Some(res.into())) Ok(res.into())
} else if ty1.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id()) } 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()) || ty2.obj_id(&ctx.unifier).is_some_and(|id| id == PrimDef::List.id())
{ {
@ -1437,7 +1389,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
ctx.ctx.bool_type().const_zero(), ctx.ctx.bool_type().const_zero(),
); );
Ok(Some(new_list.as_abi_value(ctx).into())) Ok(new_list.as_abi_value(ctx).into())
} }
Operator::Mult => { Operator::Mult => {
@ -1524,7 +1476,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
llvm_usize.const_int(1, false), llvm_usize.const_int(1, false),
)?; )?;
Ok(Some(new_list.as_abi_value(ctx).into())) Ok(new_list.as_abi_value(ctx).into())
} }
_ => todo!("Operator not supported"), _ => todo!("Operator not supported"),
@ -1563,7 +1515,7 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
let result = left let result = left
.matmul(generator, ctx, ty1, (ty2, right), (common_dtype, out)) .matmul(generator, ctx, ty1, (ty2, right), (common_dtype, out))
.split_unsized(generator, ctx); .split_unsized(generator, ctx);
Ok(Some(result.to_basic_value_enum().into())) Ok(result.to_basic_value_enum())
} else { } else {
// For other operations, they are all elementwise operations. // For other operations, they are all elementwise operations.
@ -1594,14 +1546,12 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
op, op,
(&Some(ty2_dtype), right_value), (&Some(ty2_dtype), right_value),
ctx.current_loc, ctx.current_loc,
)? )?;
.unwrap()
.to_basic_value_enum(ctx, generator, common_dtype)?;
Ok(result) Ok(result)
}) })
.unwrap(); .unwrap();
Ok(Some(result.as_abi_value(ctx).into())) Ok(result.as_abi_value(ctx).into())
} }
} else { } else {
let left_ty_enum = ctx.unifier.get_ty_immutable(left_ty.unwrap()); let left_ty_enum = ctx.unifier.get_ty_immutable(left_ty.unwrap());
@ -1650,7 +1600,8 @@ pub fn gen_binop_expr_with_values<'ctx, G: CodeGenerator>(
(&signature, fun_id), (&signature, fun_id),
vec![(None, right_val.into())], vec![(None, right_val.into())],
) )
.map(|f| f.map(Into::into)) .map(Option::unwrap)
.map(BasicValueEnum::into)
} }
} }
@ -1688,6 +1639,7 @@ pub fn gen_binop_expr<'ctx, G: CodeGenerator>(
(&right.custom, right_val), (&right.custom, right_val),
loc, loc,
) )
.map(|res| Some(res.into()))
} }
/// Generates LLVM IR for a unary operator expression using the [`Type`] and /// Generates LLVM IR for a unary operator expression using the [`Type`] and
@ -1697,18 +1649,19 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,
op: ast::Unaryop, op: ast::Unaryop,
operand: (&Option<Type>, BasicValueEnum<'ctx>), operand: (&Option<Type>, BasicValueEnum<'ctx>),
) -> Result<Option<ValueEnum<'ctx>>, String> { ) -> Result<BasicValueEnum<'ctx>, String> {
let (ty, val) = operand; let (ty, val) = operand;
let ty = ctx.unifier.get_representative(ty.unwrap()); let ty = ctx.unifier.get_representative(ty.unwrap());
Ok(Some(if ty == ctx.primitives.bool { Ok(if ty == ctx.primitives.bool {
let val = val.into_int_value(); let val = val.into_int_value();
if op == ast::Unaryop::Not { if op == ast::Unaryop::Not {
let not = ctx.builder.build_not(val, "not").unwrap(); let not = ctx
let not_bool = .builder
ctx.builder.build_and(not, not.get_type().const_int(1, false), "").unwrap(); .build_int_compare(IntPredicate::EQ, val, val.get_type().const_zero(), "not")
.unwrap();
not_bool.into() generator.bool_to_int_type(ctx, not, val.get_type()).into()
} else { } else {
let llvm_i32 = ctx.ctx.i32_type(); let llvm_i32 = ctx.ctx.i32_type();
@ -1721,7 +1674,6 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx.builder.build_int_z_extend(val, llvm_i32, "").map(Into::into).unwrap(), ctx.builder.build_int_z_extend(val, llvm_i32, "").map(Into::into).unwrap(),
), ),
)? )?
.unwrap()
} }
} else if [ } else if [
ctx.primitives.int32, ctx.primitives.int32,
@ -1775,10 +1727,13 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
if op == ast::Unaryop::Invert { if op == ast::Unaryop::Invert {
ast::Unaryop::Not ast::Unaryop::Not
} else { } else {
let ndims = extract_ndims(&ctx.unifier, ty);
codegen_unreachable!( codegen_unreachable!(
ctx, ctx,
"ufunc {} not supported for ndarray[bool, N]", "ufunc {} not supported for ndarray[bool, {}]",
op.op_info().method_name, op.op_info().method_name,
ndims,
) )
} }
} else { } else {
@ -1790,16 +1745,14 @@ pub fn gen_unaryop_expr_with_values<'ctx, G: CodeGenerator>(
ctx, ctx,
NDArrayOut::NewNDArray { dtype: ndarray.get_type().element_type() }, NDArrayOut::NewNDArray { dtype: ndarray.get_type().element_type() },
|generator, ctx, scalar| { |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_abi_value(ctx).into()
} else { } else {
unimplemented!() unimplemented!()
})) })
} }
/// Generates LLVM IR for a unary operator expression. /// Generates LLVM IR for a unary operator expression.
@ -1819,6 +1772,7 @@ pub fn gen_unaryop_expr<'ctx, G: CodeGenerator>(
}; };
gen_unaryop_expr_with_values(generator, ctx, op, (&operand.custom, val)) 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 /// Generates LLVM IR for a comparison operator expression using the [`Type`] and
@ -1829,7 +1783,7 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
left: (Option<Type>, BasicValueEnum<'ctx>), left: (Option<Type>, BasicValueEnum<'ctx>),
ops: &[ast::Cmpop], ops: &[ast::Cmpop],
comparators: &[(Option<Type>, BasicValueEnum<'ctx>)], comparators: &[(Option<Type>, BasicValueEnum<'ctx>)],
) -> Result<Option<ValueEnum<'ctx>>, String> { ) -> Result<BasicValueEnum<'ctx>, String> {
debug_assert_eq!(comparators.len(), ops.len()); debug_assert_eq!(comparators.len(), ops.len());
if comparators.len() == 1 { if comparators.len() == 1 {
@ -1871,19 +1825,13 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
(Some(left_ty_dtype), left_scalar), (Some(left_ty_dtype), left_scalar),
&[op], &[op],
&[(Some(right_ty_dtype), right_scalar)], &[(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()) Ok(generator.bool_to_i8(ctx, val.into_int_value()).into())
}, },
)?; )?;
return Ok(Some(result_ndarray.as_abi_value(ctx).into())); return Ok(result_ndarray.as_abi_value(ctx).into());
} }
} }
@ -1967,41 +1915,19 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
} else if left_ty == ctx.primitives.str { } else if left_ty == ctx.primitives.str {
assert!(ctx.unifier.unioned(left_ty, right_ty)); assert!(ctx.unifier.unioned(left_ty, right_ty));
let lhs = lhs.into_struct_value(); let llvm_str = StringType::new(ctx);
let rhs = rhs.into_struct_value();
let llvm_i32 = ctx.ctx.i32_type(); let lhs = llvm_str.map_struct_value(lhs.into_struct_value(), None);
let llvm_usize = ctx.get_size_type(); let rhs = llvm_str.map_struct_value(rhs.into_struct_value(), None);
let plhs = generator.gen_var_alloc(ctx, lhs.get_type().into(), None).unwrap(); let result = call_string_eq(ctx, lhs, rhs);
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 { if *op == Cmpop::NotEq {
ctx.builder.build_not(result, "").unwrap() gen_unaryop_expr_with_values(
generator,
ctx,
Unaryop::Not,
(&Some(ctx.primitives.bool), result.into()),
)?.into_int_value()
} else { } else {
result result
} }
@ -2104,9 +2030,6 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
&[Cmpop::Eq], &[Cmpop::Eq],
&[(Some(right_elem_ty), right)], &[(Some(right_elem_ty), right)],
)? )?
.unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)
.unwrap()
.into_int_value(); .into_int_value();
gen_if_callback( gen_if_callback(
@ -2155,8 +2078,6 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
Unaryop::Not, Unaryop::Not,
(&Some(ctx.primitives.bool), acc.into()), (&Some(ctx.primitives.bool), acc.into()),
)? )?
.unwrap()
.to_basic_value_enum(ctx, generator, ctx.primitives.bool)?
.into_int_value() .into_int_value()
} else { } else {
acc acc
@ -2244,11 +2165,6 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
&[op], &[op],
&[(Some(right_ty), right_elem)], &[(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)?; .map(BasicValueEnum::into_int_value)?;
Ok(ctx.builder.build_not( Ok(ctx.builder.build_not(
@ -2285,7 +2201,12 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
// Invert the final value if __ne__ // Invert the final value if __ne__
if *op == Cmpop::NotEq { if *op == Cmpop::NotEq {
ctx.builder.build_not(cmp_phi, "").unwrap() gen_unaryop_expr_with_values(
generator,
ctx,
Unaryop::Not,
(&Some(ctx.primitives.bool), cmp_phi.into()),
)?.into_int_value()
} else { } else {
cmp_phi cmp_phi
} }
@ -2310,12 +2231,9 @@ pub fn gen_cmpop_expr_with_values<'ctx, G: CodeGenerator>(
}; };
Ok(prev?.map(|v| ctx.builder.build_and(v, current, "cmp").unwrap()).or(Some(current))) Ok(prev?.map(|v| ctx.builder.build_and(v, current, "cmp").unwrap()).or(Some(current)))
})?; })?.unwrap();
Ok(Some(match cmp_val { Ok(cmp_val.into())
Some(v) => v.into(),
None => return Ok(None),
}))
} }
/// Generates LLVM IR for a comparison operator expression. /// Generates LLVM IR for a comparison operator expression.
@ -2362,6 +2280,7 @@ pub fn gen_cmpop_expr<'ctx, G: CodeGenerator>(
ops, ops,
comparator_vals.as_slice(), comparator_vals.as_slice(),
) )
.map(|res| Some(res.into()))
} }
/// See [`CodeGenerator::gen_expr`]. /// See [`CodeGenerator::gen_expr`].
@ -2391,16 +2310,13 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
const_val.into() const_val.into()
} }
ExprKind::Name { id, .. } if id == &"none".into() => { ExprKind::Name { id, .. } if id == &"none".into() => {
match ( match &*ctx.unifier.get_ty(expr.custom.unwrap()) {
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(), TypeEnum::TObj { obj_id, .. }
ctx.unifier.get_ty(ctx.primitives.option).as_ref(), if *obj_id == ctx.primitives.option.obj_id(&ctx.unifier).unwrap() =>
) {
(TypeEnum::TObj { obj_id, params, .. }, TypeEnum::TObj { obj_id: opt_id, .. })
if *obj_id == *opt_id =>
{ {
ctx.get_llvm_type(generator, *params.iter().next().unwrap().1) OptionType::from_unifier_type(generator, ctx, expr.custom.unwrap())
.ptr_type(AddressSpace::default()) .construct_empty(generator, ctx, None)
.const_null() .as_abi_value(ctx)
.into() .into()
} }
_ => codegen_unreachable!(ctx, "must be option type"), _ => codegen_unreachable!(ctx, "must be option type"),
@ -2885,8 +2801,12 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
}; };
} }
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => { ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
let not_null = let option = OptionType::from_pointer_type(
ctx.builder.build_is_not_null(ptr, "unwrap_not_null").unwrap(); ptr.get_type(),
ctx.get_size_type(),
)
.map_pointer_value(ptr, None);
let not_null = option.is_some(ctx);
ctx.make_assert( ctx.make_assert(
generator, generator,
not_null, not_null,
@ -2895,12 +2815,7 @@ pub fn gen_expr<'ctx, G: CodeGenerator>(
[None, None, None], [None, None, None],
expr.location, expr.location,
); );
return Ok(Some( return Ok(Some(unsafe { option.load(ctx).into() }));
ctx.builder
.build_load(ptr, "unwrap_some_load")
.map(Into::into)
.unwrap(),
));
} }
ValueEnum::Dynamic(_) => { ValueEnum::Dynamic(_) => {
codegen_unreachable!(ctx, "option must be static or ptr") codegen_unreachable!(ctx, "option must be static or ptr")

View File

@ -1,10 +1,9 @@
use inkwell::{ use inkwell::{
attributes::{Attribute, AttributeLoc}, attributes::{Attribute, AttributeLoc},
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue}, values::{BasicValueEnum, FloatValue, IntValue},
}; };
use itertools::Either;
use super::CodeGenContext; use super::{expr::infer_and_call_function, CodeGenContext};
/// Macro to generate extern function /// Macro to generate extern function
/// Both function return type and function parameter type are `FloatValue` /// Both function return type and function parameter type are `FloatValue`
@ -37,8 +36,8 @@ macro_rules! generate_extern_fn {
($fn_name:ident, $extern_fn:literal $(,$args:ident)* $(,$attributes:literal)*) => { ($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." )] #[doc = concat!("Invokes the [`", stringify!($extern_fn), "`](https://en.cppreference.com/w/c/numeric/math/", stringify!($llvm_name), ") function." )]
pub fn $fn_name<'ctx>( pub fn $fn_name<'ctx>(
ctx: &CodeGenContext<'ctx, '_> ctx: &CodeGenContext<'ctx, '_>,
$(,$args: FloatValue<'ctx>)*, $($args: FloatValue<'ctx>,)*
name: Option<&str>, name: Option<&str>,
) -> FloatValue<'ctx> { ) -> FloatValue<'ctx> {
const FN_NAME: &str = $extern_fn; const FN_NAME: &str = $extern_fn;
@ -46,24 +45,23 @@ macro_rules! generate_extern_fn {
let llvm_f64 = ctx.ctx.f64_type(); let llvm_f64 = ctx.ctx.f64_type();
$(debug_assert_eq!($args.get_type(), llvm_f64);)* $(debug_assert_eq!($args.get_type(), llvm_f64);)*
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { infer_and_call_function(
let fn_type = llvm_f64.fn_type(&[$($args.get_type().into()),*], false); ctx,
let func = ctx.module.add_function(FN_NAME, fn_type, None); FN_NAME,
for attr in [$($attributes),*] { Some(llvm_f64.into()),
func.add_attribute( &[$($args.into()),*],
AttributeLoc::Function, name,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), Some(&|func| {
); for attr in [$($attributes),*] {
} func.add_attribute(
func AttributeLoc::Function,
}); ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
);
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(BasicValueEnum::into_float_value)
.map(Either::unwrap_left) .unwrap()
.unwrap()
} }
}; };
} }
@ -112,25 +110,23 @@ pub fn call_ldexp<'ctx>(
debug_assert_eq!(arg.get_type(), llvm_f64); debug_assert_eq!(arg.get_type(), llvm_f64);
debug_assert_eq!(exp.get_type(), llvm_i32); debug_assert_eq!(exp.get_type(), llvm_i32);
let extern_fn = ctx.module.get_function(FN_NAME).unwrap_or_else(|| { infer_and_call_function(
let fn_type = llvm_f64.fn_type(&[llvm_f64.into(), llvm_i32.into()], false); ctx,
let func = ctx.module.add_function(FN_NAME, fn_type, None); FN_NAME,
for attr in ["mustprogress", "nofree", "nounwind", "willreturn"] { Some(llvm_f64.into()),
func.add_attribute( &[arg.into(), exp.into()],
AttributeLoc::Function, name,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), Some(&|func| {
); for attr in ["mustprogress", "nofree", "nounwind", "willreturn"] {
} func.add_attribute(
AttributeLoc::Function,
func ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
}); );
}
ctx.builder }),
.build_call(extern_fn, &[arg.into(), exp.into()], name.unwrap_or_default()) )
.map(CallSiteValue::try_as_basic_value) .map(BasicValueEnum::into_float_value)
.map(|v| v.map_left(BasicValueEnum::into_float_value)) .unwrap()
.map(Either::unwrap_left)
.unwrap()
} }
/// Macro to generate `np_linalg` and `sp_linalg` functions /// Macro to generate `np_linalg` and `sp_linalg` functions
@ -158,25 +154,27 @@ macro_rules! generate_linalg_extern_fn {
($fn_name:ident, $extern_fn:literal $(,$input_matrix:ident)*) => { ($fn_name:ident, $extern_fn:literal $(,$input_matrix:ident)*) => {
#[doc = concat!("Invokes the linalg `", stringify!($extern_fn), " function." )] #[doc = concat!("Invokes the linalg `", stringify!($extern_fn), " function." )]
pub fn $fn_name<'ctx>( pub fn $fn_name<'ctx>(
ctx: &mut CodeGenContext<'ctx, '_> ctx: &mut CodeGenContext<'ctx, '_>,
$(,$input_matrix: BasicValueEnum<'ctx>)*, $($input_matrix: BasicValueEnum<'ctx>,)*
name: Option<&str>, name: Option<&str>,
){ ){
const FN_NAME: &str = $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);
let func = ctx.module.add_function(FN_NAME, fn_type, None); infer_and_call_function(
for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] { ctx,
func.add_attribute( FN_NAME,
AttributeLoc::Function, None,
ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0), &[$($input_matrix.into(),)*],
); name,
} Some(&|func| {
func for attr in ["mustprogress", "nofree", "nounwind", "willreturn", "writeonly"] {
}); func.add_attribute(
AttributeLoc::Function,
ctx.builder.build_call(extern_fn, &[$($input_matrix.into(),)*], name.unwrap_or_default()).unwrap(); ctx.ctx.create_enum_attribute(Attribute::get_named_enum_kind_id(attr), 0),
);
}
}),
);
} }
}; };
} }

View File

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

View File

@ -1,13 +1,14 @@
use inkwell::{ use inkwell::{
types::BasicTypeEnum, types::BasicTypeEnum,
values::{BasicValueEnum, CallSiteValue, IntValue}, values::{BasicValueEnum, IntValue},
AddressSpace, IntPredicate, AddressSpace, IntPredicate,
}; };
use itertools::Either;
use super::calculate_len_for_slice_range; use super::calculate_len_for_slice_range;
use crate::codegen::{ use crate::codegen::{
expr::infer_and_call_function,
macros::codegen_unreachable, macros::codegen_unreachable,
stmt::gen_if_callback,
values::{ArrayLikeValue, ListValue}, values::{ArrayLikeValue, ListValue},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -36,25 +37,6 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
assert_eq!(src_idx.2.get_type(), llvm_i32); assert_eq!(src_idx.2.get_type(), llvm_i32);
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", llvm_pi8); 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 zero = llvm_i32.const_zero();
let one = llvm_i32.const_int(1, false); let one = llvm_i32.const_int(1, false);
@ -127,7 +109,7 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
); );
let new_len = { let new_len = {
let args = vec![ let args = [
dest_idx.0.into(), // dest start idx dest_idx.0.into(), // dest start idx
dest_idx.1.into(), // dest end idx dest_idx.1.into(), // dest end idx
dest_idx.2.into(), // dest step dest_idx.2.into(), // dest step
@ -150,25 +132,35 @@ pub fn list_slice_assignment<'ctx, G: CodeGenerator + ?Sized>(
} }
.into(), .into(),
]; ];
ctx.builder infer_and_call_function(
.build_call(slice_assign_fun, args.as_slice(), "slice_assign") ctx,
.map(CallSiteValue::try_as_basic_value) fun_symbol,
.map(|v| v.map_left(BasicValueEnum::into_int_value)) Some(llvm_i32.into()),
.map(Either::unwrap_left) &args,
.unwrap() Some("slice_assign"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap()
}; };
// update length // update length
let need_update = gen_if_callback(
ctx.builder.build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update").unwrap(); generator,
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap(); ctx,
let update_bb = ctx.ctx.append_basic_block(current, "update"); |_, ctx| {
let cont_bb = ctx.ctx.append_basic_block(current, "cont"); Ok(ctx
ctx.builder.build_conditional_branch(need_update, update_bb, cont_bb).unwrap(); .builder
ctx.builder.position_at_end(update_bb); .build_int_compare(IntPredicate::NE, new_len, dest_len, "need_update")
let new_len = .unwrap())
ctx.builder.build_int_z_extend_or_bit_cast(new_len, llvm_usize, "new_len").unwrap(); },
dest_arr.store_size(ctx, new_len); |_, ctx| {
ctx.builder.build_unconditional_branch(cont_bb).unwrap(); let new_len =
ctx.builder.position_at_end(cont_bb); 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();
} }

View File

@ -1,10 +1,10 @@
use inkwell::{ use inkwell::{
values::{BasicValueEnum, CallSiteValue, FloatValue, IntValue}, values::{BasicValueEnum, FloatValue, IntValue},
IntPredicate, IntPredicate,
}; };
use itertools::Either;
use crate::codegen::{ use crate::codegen::{
expr::infer_and_call_function,
macros::codegen_unreachable, macros::codegen_unreachable,
{CodeGenContext, CodeGenerator}, {CodeGenContext, CodeGenerator},
}; };
@ -18,18 +18,16 @@ pub fn integer_power<'ctx, G: CodeGenerator + ?Sized>(
exp: IntValue<'ctx>, exp: IntValue<'ctx>,
signed: bool, signed: bool,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let symbol = match (base.get_type().get_bit_width(), exp.get_type().get_bit_width(), signed) { let base_type = base.get_type();
let symbol = match (base_type.get_bit_width(), exp.get_type().get_bit_width(), signed) {
(32, 32, true) => "__nac3_int_exp_int32_t", (32, 32, true) => "__nac3_int_exp_int32_t",
(64, 64, true) => "__nac3_int_exp_int64_t", (64, 64, true) => "__nac3_int_exp_int64_t",
(32, 32, false) => "__nac3_int_exp_uint32_t", (32, 32, false) => "__nac3_int_exp_uint32_t",
(64, 64, false) => "__nac3_int_exp_uint64_t", (64, 64, false) => "__nac3_int_exp_uint64_t",
_ => codegen_unreachable!(ctx), _ => 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 // throw exception when exp < 0
let ge_zero = ctx let ge_zero = ctx
.builder .builder
@ -48,12 +46,17 @@ pub fn integer_power<'ctx, G: CodeGenerator + ?Sized>(
[None, None, None], [None, None, None],
ctx.current_loc, ctx.current_loc,
); );
ctx.builder
.build_call(pow_fun, &[base.into(), exp.into()], "call_int_pow") infer_and_call_function(
.map(CallSiteValue::try_as_basic_value) ctx,
.map(|v| v.map_left(BasicValueEnum::into_int_value)) symbol,
.map(Either::unwrap_left) Some(base_type.into()),
.unwrap() &[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. /// Generates a call to `isinf` in IR. Returns an `i1` representing the result.
@ -67,20 +70,17 @@ pub fn call_isinf<'ctx, G: CodeGenerator + ?Sized>(
assert_eq!(v.get_type(), llvm_f64); assert_eq!(v.get_type(), llvm_f64);
let intrinsic_fn = ctx.module.get_function("__nac3_isinf").unwrap_or_else(|| { infer_and_call_function(
let fn_type = llvm_i32.fn_type(&[llvm_f64.into()], false); ctx,
ctx.module.add_function("__nac3_isinf", fn_type, None) "__nac3_isinf",
}); Some(llvm_i32.into()),
&[v.into()],
let ret = ctx Some("isinf"),
.builder None,
.build_call(intrinsic_fn, &[v.into()], "isinf") )
.map(CallSiteValue::try_as_basic_value) .map(BasicValueEnum::into_int_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value)) .map(|ret| generator.bool_to_i1(ctx, ret))
.map(Either::unwrap_left) .unwrap()
.unwrap();
generator.bool_to_i1(ctx, ret)
} }
/// Generates a call to `isnan` in IR. Returns an `i1` representing the result. /// Generates a call to `isnan` in IR. Returns an `i1` representing the result.
@ -94,20 +94,17 @@ pub fn call_isnan<'ctx, G: CodeGenerator + ?Sized>(
assert_eq!(v.get_type(), llvm_f64); assert_eq!(v.get_type(), llvm_f64);
let intrinsic_fn = ctx.module.get_function("__nac3_isnan").unwrap_or_else(|| { infer_and_call_function(
let fn_type = llvm_i32.fn_type(&[llvm_f64.into()], false); ctx,
ctx.module.add_function("__nac3_isnan", fn_type, None) "__nac3_isnan",
}); Some(llvm_i32.into()),
&[v.into()],
let ret = ctx Some("isnan"),
.builder None,
.build_call(intrinsic_fn, &[v.into()], "isnan") )
.map(CallSiteValue::try_as_basic_value) .map(BasicValueEnum::into_int_value)
.map(|v| v.map_left(BasicValueEnum::into_int_value)) .map(|ret| generator.bool_to_i1(ctx, ret))
.map(Either::unwrap_left) .unwrap()
.unwrap();
generator.bool_to_i1(ctx, ret)
} }
/// Generates a call to `gamma` in IR. Returns an `f64` representing the result. /// Generates a call to `gamma` in IR. Returns an `f64` representing the result.
@ -116,17 +113,16 @@ pub fn call_gamma<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) ->
assert_eq!(v.get_type(), llvm_f64); assert_eq!(v.get_type(), llvm_f64);
let intrinsic_fn = ctx.module.get_function("__nac3_gamma").unwrap_or_else(|| { infer_and_call_function(
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); ctx,
ctx.module.add_function("__nac3_gamma", fn_type, None) "__nac3_gamma",
}); Some(llvm_f64.into()),
&[v.into()],
ctx.builder Some("gamma"),
.build_call(intrinsic_fn, &[v.into()], "gamma") None,
.map(CallSiteValue::try_as_basic_value) )
.map(|v| v.map_left(BasicValueEnum::into_float_value)) .map(BasicValueEnum::into_float_value)
.map(Either::unwrap_left) .unwrap()
.unwrap()
} }
/// Generates a call to `gammaln` in IR. Returns an `f64` representing the result. /// Generates a call to `gammaln` in IR. Returns an `f64` representing the result.
@ -135,17 +131,16 @@ pub fn call_gammaln<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -
assert_eq!(v.get_type(), llvm_f64); assert_eq!(v.get_type(), llvm_f64);
let intrinsic_fn = ctx.module.get_function("__nac3_gammaln").unwrap_or_else(|| { infer_and_call_function(
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); ctx,
ctx.module.add_function("__nac3_gammaln", fn_type, None) "__nac3_gammaln",
}); Some(llvm_f64.into()),
&[v.into()],
ctx.builder Some("gammaln"),
.build_call(intrinsic_fn, &[v.into()], "gammaln") None,
.map(CallSiteValue::try_as_basic_value) )
.map(|v| v.map_left(BasicValueEnum::into_float_value)) .map(BasicValueEnum::into_float_value)
.map(Either::unwrap_left) .unwrap()
.unwrap()
} }
/// Generates a call to `j0` in IR. Returns an `f64` representing the result. /// Generates a call to `j0` in IR. Returns an `f64` representing the result.
@ -154,15 +149,7 @@ pub fn call_j0<'ctx>(ctx: &CodeGenContext<'ctx, '_>, v: FloatValue<'ctx>) -> Flo
assert_eq!(v.get_type(), llvm_f64); assert_eq!(v.get_type(), llvm_f64);
let intrinsic_fn = ctx.module.get_function("__nac3_j0").unwrap_or_else(|| { infer_and_call_function(ctx, "__nac3_j0", Some(llvm_f64.into()), &[v.into()], Some("j0"), None)
let fn_type = llvm_f64.fn_type(&[llvm_f64.into()], false); .map(BasicValueEnum::into_float_value)
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() .unwrap()
} }

View File

@ -1,13 +1,11 @@
use inkwell::{ use inkwell::{
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue, PointerValue}, values::{BasicValueEnum, IntValue, PointerValue},
AddressSpace, AddressSpace,
}; };
use crate::codegen::{ use crate::codegen::{
expr::{create_and_call_function, infer_and_call_function}, expr::infer_and_call_function,
irrt::get_usize_dependent_function_name, irrt::get_usize_dependent_function_name,
types::ProxyType,
values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor}, values::{ndarray::NDArrayValue, ProxyValue, TypedArrayLikeAccessor},
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -21,24 +19,17 @@ pub fn call_nac3_ndarray_util_assert_shape_no_negative<'ctx, G: CodeGenerator +
shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>, shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
) { ) {
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
assert_eq!( assert_eq!(shape.element_type(ctx, generator), llvm_usize.into());
BasicTypeEnum::try_from(shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name = let name =
get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_shape_no_negative"); get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_shape_no_negative");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
Some(llvm_usize.into()), 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,
None, None,
); );
@ -55,29 +46,22 @@ pub fn call_nac3_ndarray_util_assert_output_shape_same<'ctx, G: CodeGenerator +
output_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>, output_shape: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
) { ) {
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
assert_eq!( assert_eq!(ndarray_shape.element_type(ctx, generator), llvm_usize.into());
BasicTypeEnum::try_from(ndarray_shape.element_type(ctx, generator)).unwrap(), assert_eq!(output_shape.element_type(ctx, generator), llvm_usize.into());
llvm_usize.into()
);
assert_eq!(
BasicTypeEnum::try_from(output_shape.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name = let name =
get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_output_shape_same"); get_usize_dependent_function_name(ctx, "__nac3_ndarray_util_assert_output_shape_same");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
Some(llvm_usize.into()), Some(llvm_usize.into()),
&[ &[
(llvm_usize.into(), ndarray_shape.size(ctx, generator).into()), ndarray_shape.size(ctx, generator).into(),
(llvm_pusize.into(), ndarray_shape.base_ptr(ctx, generator).into()), ndarray_shape.base_ptr(ctx, generator).into(),
(llvm_usize.into(), output_shape.size(ctx, generator).into()), output_shape.size(ctx, generator).into(),
(llvm_pusize.into(), output_shape.base_ptr(ctx, generator).into()), output_shape.base_ptr(ctx, generator).into(),
], ],
None, None,
None, None,
@ -93,15 +77,14 @@ pub fn call_nac3_ndarray_size<'ctx>(
ndarray: NDArrayValue<'ctx>, ndarray: NDArrayValue<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_size"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_size");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
Some(llvm_usize.into()), Some(llvm_usize.into()),
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())], &[ndarray.as_abi_value(ctx).into()],
Some("size"), Some("size"),
None, None,
) )
@ -118,15 +101,14 @@ pub fn call_nac3_ndarray_nbytes<'ctx>(
ndarray: NDArrayValue<'ctx>, ndarray: NDArrayValue<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_nbytes"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_nbytes");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
Some(llvm_usize.into()), Some(llvm_usize.into()),
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())], &[ndarray.as_abi_value(ctx).into()],
Some("nbytes"), Some("nbytes"),
None, None,
) )
@ -143,15 +125,14 @@ pub fn call_nac3_ndarray_len<'ctx>(
ndarray: NDArrayValue<'ctx>, ndarray: NDArrayValue<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_len"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_len");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
Some(llvm_usize.into()), Some(llvm_usize.into()),
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())], &[ndarray.as_abi_value(ctx).into()],
Some("len"), Some("len"),
None, None,
) )
@ -167,15 +148,14 @@ pub fn call_nac3_ndarray_is_c_contiguous<'ctx>(
ndarray: NDArrayValue<'ctx>, ndarray: NDArrayValue<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let llvm_i1 = ctx.ctx.bool_type(); let llvm_i1 = ctx.ctx.bool_type();
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_is_c_contiguous"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_is_c_contiguous");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
Some(llvm_i1.into()), Some(llvm_i1.into()),
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())], &[ndarray.as_abi_value(ctx).into()],
Some("is_c_contiguous"), Some("is_c_contiguous"),
None, None,
) )
@ -194,20 +174,16 @@ pub fn call_nac3_ndarray_get_nth_pelement<'ctx>(
let llvm_i8 = ctx.ctx.i8_type(); let llvm_i8 = ctx.ctx.i8_type();
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default()); let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
let llvm_ndarray = ndarray.get_type();
assert_eq!(index.get_type(), llvm_usize); assert_eq!(index.get_type(), llvm_usize);
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_get_nth_pelement"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_get_nth_pelement");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
Some(llvm_pi8.into()), Some(llvm_pi8.into()),
&[ &[ndarray.as_abi_value(ctx).into(), index.into()],
(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into()),
(llvm_usize.into(), index.into()),
],
Some("pelement"), Some("pelement"),
None, None,
) )
@ -229,24 +205,16 @@ pub fn call_nac3_ndarray_get_pelement_by_indices<'ctx, G: CodeGenerator + ?Sized
let llvm_i8 = ctx.ctx.i8_type(); let llvm_i8 = ctx.ctx.i8_type();
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default()); let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
let llvm_ndarray = ndarray.get_type();
assert_eq!( assert_eq!(indices.element_type(ctx, generator), llvm_usize.into());
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"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_get_pelement_by_indices");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
Some(llvm_pi8.into()), Some(llvm_pi8.into()),
&[ &[ndarray.as_abi_value(ctx).into(), indices.base_ptr(ctx, generator).into()],
(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into()),
(llvm_pusize.into(), indices.base_ptr(ctx, generator).into()),
],
Some("pelement"), Some("pelement"),
None, None,
) )
@ -261,18 +229,9 @@ pub fn call_nac3_ndarray_set_strides_by_shape<'ctx>(
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
ndarray: NDArrayValue<'ctx>, ndarray: NDArrayValue<'ctx>,
) { ) {
let llvm_ndarray = ndarray.get_type();
let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_set_strides_by_shape"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_set_strides_by_shape");
create_and_call_function( infer_and_call_function(ctx, &name, None, &[ndarray.as_abi_value(ctx).into()], None, None);
ctx,
&name,
None,
&[(llvm_ndarray.as_abi_type().into(), ndarray.as_abi_value(ctx).into())],
None,
None,
);
} }
/// Generates a call to `__nac3_ndarray_copy_data`. /// Generates a call to `__nac3_ndarray_copy_data`.

View File

@ -1,13 +1,8 @@
use inkwell::{ use inkwell::values::{BasicValueEnum, IntValue};
types::BasicTypeEnum,
values::{BasicValueEnum, IntValue},
AddressSpace,
};
use crate::codegen::{ use crate::codegen::{
expr::{create_and_call_function, infer_and_call_function}, expr::infer_and_call_function,
irrt::get_usize_dependent_function_name, irrt::get_usize_dependent_function_name,
types::ProxyType,
values::{ values::{
ndarray::{NDArrayValue, NDIterValue}, ndarray::{NDArrayValue, NDIterValue},
ProxyValue, TypedArrayLikeAccessor, ProxyValue, TypedArrayLikeAccessor,
@ -26,23 +21,19 @@ pub fn call_nac3_nditer_initialize<'ctx, G: CodeGenerator + ?Sized>(
indices: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>, indices: &impl TypedArrayLikeAccessor<'ctx, G, IntValue<'ctx>>,
) { ) {
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
let llvm_pusize = llvm_usize.ptr_type(AddressSpace::default());
assert_eq!( assert_eq!(indices.element_type(ctx, generator), llvm_usize.into());
BasicTypeEnum::try_from(indices.element_type(ctx, generator)).unwrap(),
llvm_usize.into()
);
let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_initialize"); let name = get_usize_dependent_function_name(ctx, "__nac3_nditer_initialize");
create_and_call_function( infer_and_call_function(
ctx, ctx,
&name, &name,
None, None,
&[ &[
(iter.get_type().as_abi_type().into(), iter.as_abi_value(ctx).into()), iter.as_abi_value(ctx).into(),
(ndarray.get_type().as_abi_type().into(), ndarray.as_abi_value(ctx).into()), ndarray.as_abi_value(ctx).into(),
(llvm_pusize.into(), indices.base_ptr(ctx, generator).into()), indices.base_ptr(ctx, generator).into(),
], ],
None, None,
None, None,

View File

@ -1,4 +1,4 @@
use inkwell::{types::BasicTypeEnum, values::IntValue}; use inkwell::values::IntValue;
use crate::codegen::{ use crate::codegen::{
expr::infer_and_call_function, irrt::get_usize_dependent_function_name, expr::infer_and_call_function, irrt::get_usize_dependent_function_name,
@ -22,26 +22,12 @@ pub fn call_nac3_ndarray_matmul_calculate_shapes<'ctx, G: CodeGenerator + ?Sized
) { ) {
let llvm_usize = ctx.get_size_type(); let llvm_usize = ctx.get_size_type();
assert_eq!( assert_eq!(a_shape.element_type(ctx, generator), llvm_usize.into());
BasicTypeEnum::try_from(a_shape.element_type(ctx, generator)).unwrap(), assert_eq!(b_shape.element_type(ctx, generator), llvm_usize.into());
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!( assert_eq!(new_b_shape.element_type(ctx, generator), llvm_usize.into());
BasicTypeEnum::try_from(b_shape.element_type(ctx, generator)).unwrap(), assert_eq!(dst_shape.element_type(ctx, generator), llvm_usize.into());
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"); let name = get_usize_dependent_function_name(ctx, "__nac3_ndarray_matmul_calculate_shapes");

View File

@ -1,10 +1,9 @@
use inkwell::{ use inkwell::{
values::{BasicValueEnum, CallSiteValue, IntValue}, values::{BasicValueEnum, IntValue},
IntPredicate, IntPredicate,
}; };
use itertools::Either;
use crate::codegen::{CodeGenContext, CodeGenerator}; use crate::codegen::{expr::infer_and_call_function, CodeGenContext, CodeGenerator};
/// Invokes the `__nac3_range_slice_len` in IRRT. /// Invokes the `__nac3_range_slice_len` in IRRT.
/// ///
@ -23,16 +22,10 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
const SYMBOL: &str = "__nac3_range_slice_len"; const SYMBOL: &str = "__nac3_range_slice_len";
let llvm_i32 = ctx.ctx.i32_type(); let llvm_i32 = ctx.ctx.i32_type();
assert_eq!(start.get_type(), llvm_i32); assert_eq!(start.get_type(), llvm_i32);
assert_eq!(end.get_type(), llvm_i32); assert_eq!(end.get_type(), llvm_i32);
assert_eq!(step.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 // assert step != 0, throw exception if not
let not_zero = ctx let not_zero = ctx
.builder .builder
@ -47,10 +40,14 @@ pub fn calculate_len_for_slice_range<'ctx, G: CodeGenerator + ?Sized>(
ctx.current_loc, ctx.current_loc,
); );
ctx.builder infer_and_call_function(
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len") ctx,
.map(CallSiteValue::try_as_basic_value) SYMBOL,
.map(|v| v.map_left(BasicValueEnum::into_int_value)) Some(llvm_i32.into()),
.map(Either::unwrap_left) &[start.into(), end.into(), step.into()],
.unwrap() Some("calc_len"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap()
} }

View File

@ -1,10 +1,9 @@
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue}; use inkwell::values::{BasicValueEnum, IntValue};
use itertools::Either;
use nac3parser::ast::Expr; use nac3parser::ast::Expr;
use crate::{ use crate::{
codegen::{CodeGenContext, CodeGenerator}, codegen::{expr::infer_and_call_function, CodeGenContext, CodeGenerator},
typecheck::typedef::Type, typecheck::typedef::Type,
}; };
@ -17,23 +16,26 @@ pub fn handle_slice_index_bound<'ctx, G: CodeGenerator>(
length: IntValue<'ctx>, length: IntValue<'ctx>,
) -> Result<Option<IntValue<'ctx>>, String> { ) -> Result<Option<IntValue<'ctx>>, String> {
const SYMBOL: &str = "__nac3_slice_index_bound"; const SYMBOL: &str = "__nac3_slice_index_bound";
let func = ctx.module.get_function(SYMBOL).unwrap_or_else(|| {
let i32_t = ctx.ctx.i32_type(); let llvm_i32 = ctx.ctx.i32_type();
let fn_t = i32_t.fn_type(&[i32_t.into(), i32_t.into()], false); assert_eq!(length.get_type(), llvm_i32);
ctx.module.add_function(SYMBOL, fn_t, None)
});
let i = if let Some(v) = generator.gen_expr(ctx, i)? { let i = if let Some(v) = generator.gen_expr(ctx, i)? {
v.to_basic_value_enum(ctx, generator, i.custom.unwrap())? v.to_basic_value_enum(ctx, generator, i.custom.unwrap())?
} else { } else {
return Ok(None); return Ok(None);
}; };
Ok(Some( Ok(Some(
ctx.builder infer_and_call_function(
.build_call(func, &[i.into(), length.into()], "bounded_ind") ctx,
.map(CallSiteValue::try_as_basic_value) SYMBOL,
.map(|v| v.map_left(BasicValueEnum::into_int_value)) Some(llvm_i32.into()),
.map(Either::unwrap_left) &[i, length.into()],
.unwrap(), Some("bounded_ind"),
None,
)
.map(BasicValueEnum::into_int_value)
.unwrap(),
)) ))
} }

View File

@ -1,45 +1,31 @@
use inkwell::values::{BasicValueEnum, CallSiteValue, IntValue, PointerValue}; use inkwell::values::{BasicValueEnum, IntValue};
use itertools::Either;
use super::get_usize_dependent_function_name; use super::get_usize_dependent_function_name;
use crate::codegen::CodeGenContext; use crate::codegen::{expr::infer_and_call_function, values::StringValue, CodeGenContext};
/// Generates a call to string equality comparison. Returns an `i1` representing whether the strings are equal. /// Generates a call to string equality comparison. Returns an `i1` representing whether the strings are equal.
pub fn call_string_eq<'ctx>( pub fn call_string_eq<'ctx>(
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
str1_ptr: PointerValue<'ctx>, str1: StringValue<'ctx>,
str1_len: IntValue<'ctx>, str2: StringValue<'ctx>,
str2_ptr: PointerValue<'ctx>,
str2_len: IntValue<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let llvm_i1 = ctx.ctx.bool_type(); let llvm_i1 = ctx.ctx.bool_type();
let func_name = get_usize_dependent_function_name(ctx, "nac3_str_eq"); let func_name = get_usize_dependent_function_name(ctx, "nac3_str_eq");
let func = ctx.module.get_function(&func_name).unwrap_or_else(|| { infer_and_call_function(
ctx.module.add_function( ctx,
&func_name, &func_name,
llvm_i1.fn_type( Some(llvm_i1.into()),
&[ &[
str1_ptr.get_type().into(), str1.extract_ptr(ctx).into(),
str1_len.get_type().into(), str1.extract_len(ctx).into(),
str2_ptr.get_type().into(), str2.extract_ptr(ctx).into(),
str2_len.get_type().into(), str2.extract_len(ctx).into(),
], ],
false, Some("str_eq_call"),
), None,
None, )
) .map(BasicValueEnum::into_int_value)
}); .unwrap()
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

@ -43,7 +43,9 @@ use crate::{
}; };
use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore}; use concrete_type::{ConcreteType, ConcreteTypeEnum, ConcreteTypeStore};
pub use generator::{CodeGenerator, DefaultCodeGenerator}; pub use generator::{CodeGenerator, DefaultCodeGenerator};
use types::{ndarray::NDArrayType, ListType, ProxyType, RangeType, TupleType}; use types::{
ndarray::NDArrayType, ListType, OptionType, ProxyType, RangeType, StringType, TupleType,
};
pub mod builtin_fns; pub mod builtin_fns;
pub mod concrete_type; pub mod concrete_type;
@ -538,7 +540,7 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
if PrimDef::contains_id(*obj_id) { if PrimDef::contains_id(*obj_id) {
return match &*unifier.get_ty_immutable(ty) { return match &*unifier.get_ty_immutable(ty) {
TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => { TObj { obj_id, params, .. } if *obj_id == PrimDef::Option.id() => {
get_llvm_type( let element_type = get_llvm_type(
ctx, ctx,
module, module,
generator, generator,
@ -546,9 +548,9 @@ fn get_llvm_type<'ctx, G: CodeGenerator + ?Sized>(
top_level, top_level,
type_cache, type_cache,
*params.iter().next().unwrap().1, *params.iter().next().unwrap().1,
) );
.ptr_type(AddressSpace::default())
.into() OptionType::new_with_generator(generator, ctx, &element_type).as_abi_type().into()
} }
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => { TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
@ -786,19 +788,7 @@ pub fn gen_func_impl<
(primitives.float, context.f64_type().into()), (primitives.float, context.f64_type().into()),
(primitives.bool, context.i8_type().into()), (primitives.bool, context.i8_type().into()),
(primitives.str, { (primitives.str, {
let name = "str"; StringType::new_with_generator(generator, context).as_abi_type().into()
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_with_generator(generator, context).as_abi_type().into()),
(primitives.exception, { (primitives.exception, {
@ -933,7 +923,7 @@ pub fn gen_func_impl<
let param_val = param.into_int_value(); let param_val = param.into_int_value();
if expected_ty.get_bit_width() == 8 && param_val.get_type().get_bit_width() == 1 { if expected_ty.get_bit_width() == 8 && param_val.get_type().get_bit_width() == 1 {
bool_to_i8(&builder, context, param_val) bool_to_int_type(&builder, param_val, context.i8_type())
} else { } else {
param_val param_val
} }
@ -1103,43 +1093,29 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
}) })
} }
/// Converts the value of a boolean-like value `bool_value` into an `i1`. /// Converts the value of a boolean-like value `value` into an arbitrary [`IntType`].
fn bool_to_i1<'ctx>(builder: &Builder<'ctx>, bool_value: IntValue<'ctx>) -> IntValue<'ctx> { ///
if bool_value.get_type().get_bit_width() == 1 { /// This has the same semantics as `(ty)(value != 0)` in C.
bool_value ///
} else { /// The returned value is guaranteed to either be `0` or `1`, except for `ty == i1` where only the
builder /// least-significant bit would be guaranteed to be `0` or `1`.
.build_int_compare( fn bool_to_int_type<'ctx>(
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>, builder: &Builder<'ctx>,
ctx: &'ctx Context, value: IntValue<'ctx>,
bool_value: IntValue<'ctx>, ty: IntType<'ctx>,
) -> IntValue<'ctx> { ) -> IntValue<'ctx> {
let value_bits = bool_value.get_type().get_bit_width(); // i1 -> i1 : %value ; no-op
match value_bits { // i1 -> i<N> : zext i1 %value to i<N> ; guaranteed to be 0 or 1 - see docs
8 => bool_value, // i<M> -> i<N>: zext i1 (icmp eq i<M> %value, 0) to i<N> ; same as i<M> -> i1 -> i<N>
1 => builder.build_int_z_extend(bool_value, ctx.i8_type(), "frombool").unwrap(), match (value.get_type().get_bit_width(), ty.get_bit_width()) {
_ => bool_to_i8( (1, 1) => value,
(1, _) => builder.build_int_z_extend(value, ty, "frombool").unwrap(),
_ => bool_to_int_type(
builder, builder,
ctx,
builder builder
.build_int_compare( .build_int_compare(IntPredicate::NE, value, value.get_type().const_zero(), "tobool")
IntPredicate::NE,
bool_value,
bool_value.get_type().const_zero(),
"",
)
.unwrap(), .unwrap(),
ty,
), ),
} }
} }

View File

@ -17,10 +17,10 @@ use super::{
gen_in_range_check, gen_in_range_check,
irrt::{handle_slice_indices, list_slice_assignment}, irrt::{handle_slice_indices, list_slice_assignment},
macros::codegen_unreachable, macros::codegen_unreachable,
types::{ndarray::NDArrayType, RangeType}, types::{ndarray::NDArrayType, ExceptionType, RangeType},
values::{ values::{
ndarray::{RustNDIndex, ScalarOrNDArray}, ndarray::{RustNDIndex, ScalarOrNDArray},
ArrayLikeIndexer, ArraySliceValue, ListValue, ProxyValue, ArrayLikeIndexer, ArraySliceValue, ExceptionValue, ListValue, ProxyValue,
}, },
CodeGenContext, CodeGenerator, CodeGenContext, CodeGenerator,
}; };
@ -1337,43 +1337,19 @@ pub fn exn_constructor<'ctx>(
pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>( pub fn gen_raise<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G, generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,
exception: Option<&BasicValueEnum<'ctx>>, exception: Option<&ExceptionValue<'ctx>>,
loc: Location, loc: Location,
) { ) {
if let Some(exception) = exception { if let Some(exception) = exception {
unsafe { exception.store_location(generator, ctx, loc);
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().unwrap().get_parent().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()); let fun_name = ctx.gen_string(generator, current_fun.get_name().to_str().unwrap());
let name_ptr = ctx exception.store_func(ctx, fun_name);
.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 raise = get_builtins(generator, ctx, "__nac3_raise");
let exception = *exception; let exception = *exception;
ctx.build_call_or_invoke(raise, &[exception], "raise"); ctx.build_call_or_invoke(raise, &[exception.as_abi_value(ctx).into()], "raise");
} else { } else {
let resume = get_builtins(generator, ctx, "__nac3_resume"); let resume = get_builtins(generator, ctx, "__nac3_resume");
ctx.build_call_or_invoke(resume, &[], "resume"); ctx.build_call_or_invoke(resume, &[], "resume");
@ -1860,6 +1836,8 @@ pub fn gen_stmt<G: CodeGenerator>(
} else { } else {
return Ok(()); 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); gen_raise(generator, ctx, Some(&exc), stmt.location);
} else { } else {
gen_raise(generator, ctx, None, stmt.location); gen_raise(generator, ctx, None, stmt.location);

View File

@ -0,0 +1,257 @@
use inkwell::{
context::{AsContextRef, Context},
types::{AnyTypeEnum, BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use super::{
structure::{check_struct_type_matches_fields, StructField, StructFields, StructProxyType},
ProxyType,
};
use crate::{
codegen::{values::ExceptionValue, CodeGenContext, CodeGenerator},
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> {
assert!(ctx.get_struct_type("str").is_some());
let field_tys =
Self::fields(ctx, llvm_usize).into_iter().map(|field| field.1).collect_vec();
ctx.struct_type(&field_tys, false).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

@ -25,13 +25,19 @@ use super::{
values::{ArraySliceValue, ProxyValue}, values::{ArraySliceValue, ProxyValue},
{CodeGenContext, CodeGenerator}, {CodeGenContext, CodeGenerator},
}; };
pub use exception::*;
pub use list::*; pub use list::*;
pub use option::*;
pub use range::*; pub use range::*;
pub use string::*;
pub use tuple::*; pub use tuple::*;
mod exception;
mod list; mod list;
pub mod ndarray; pub mod ndarray;
mod option;
mod range; mod range;
mod string;
pub mod structure; pub mod structure;
mod tuple; mod tuple;
pub mod utils; pub mod utils;

View File

@ -0,0 +1,188 @@
use inkwell::{
context::Context,
types::{BasicType, BasicTypeEnum, IntType, PointerType},
values::{BasicValue, BasicValueEnum, PointerValue},
AddressSpace,
};
use super::ProxyType;
use crate::{
codegen::{values::OptionValue, CodeGenContext, CodeGenerator},
typecheck::typedef::{iter_type_vars, Type, TypeEnum},
};
/// 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

@ -0,0 +1,177 @@
use inkwell::{
context::Context,
types::{BasicType, BasicTypeEnum, IntType, PointerType, StructType},
values::{GlobalValue, IntValue, PointerValue, StructValue},
AddressSpace,
};
use itertools::Itertools;
use nac3core_derive::StructFields;
use super::{
structure::{check_struct_type_matches_fields, StructField, StructFields},
ProxyType,
};
use crate::codegen::{values::StringValue, CodeGenContext, CodeGenerator};
/// 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

@ -115,15 +115,8 @@ impl<'ctx> TupleType<'ctx> {
/// Constructs a [`TupleValue`] from this type by zero-initializing the tuple value. /// Constructs a [`TupleValue`] from this type by zero-initializing the tuple value.
#[must_use] #[must_use]
pub fn construct( pub fn construct(&self, name: Option<&'ctx str>) -> <Self as ProxyType<'ctx>>::Value {
&self, self.map_struct_value(self.as_abi_type().const_zero(), name)
ctx: &CodeGenContext<'ctx, '_>,
name: Option<&'ctx str>,
) -> <Self as ProxyType<'ctx>>::Value {
self.map_struct_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 /// Constructs a [`TupleValue`] from `objects`. The resulting tuple preserves the order of
@ -143,9 +136,9 @@ impl<'ctx> TupleType<'ctx> {
.enumerate() .enumerate()
.all(|(i, v)| { v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) } })); .all(|(i, v)| { v.get_type() == unsafe { self.type_at_index_unchecked(i as u32) } }));
let mut value = self.construct(ctx, name); let mut value = self.construct(name);
for (i, val) in values.into_iter().enumerate() { for (i, val) in values.into_iter().enumerate() {
value.store_element(ctx, i as u32, val); value.insert_element(ctx, i as u32, val);
} }
value value

View File

@ -0,0 +1,188 @@
use inkwell::{
types::IntType,
values::{IntValue, PointerValue, StructValue},
};
use itertools::Itertools;
use nac3parser::ast::Location;
use super::{structure::StructProxyValue, ProxyValue, StringValue};
use crate::codegen::{
types::{
structure::{StructField, StructProxyType},
ExceptionType,
},
CodeGenContext, CodeGenerator,
};
/// 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

@ -2,14 +2,20 @@ use inkwell::{types::IntType, values::BasicValue};
use super::{types::ProxyType, CodeGenContext}; use super::{types::ProxyType, CodeGenContext};
pub use array::*; pub use array::*;
pub use exception::*;
pub use list::*; pub use list::*;
pub use option::*;
pub use range::*; pub use range::*;
pub use string::*;
pub use tuple::*; pub use tuple::*;
mod array; mod array;
mod exception;
mod list; mod list;
pub mod ndarray; pub mod ndarray;
mod option;
mod range; mod range;
mod string;
pub mod structure; pub mod structure;
mod tuple; mod tuple;
pub mod utils; pub mod utils;

View File

@ -213,9 +213,7 @@ fn matmul_at_least_2d<'ctx, G: CodeGenerator>(
Binop::normal(Operator::Mult), Binop::normal(Operator::Mult),
(&Some(rhs_dtype), b_kj), (&Some(rhs_dtype), b_kj),
ctx.current_loc, ctx.current_loc,
)? )?;
.unwrap()
.to_basic_value_enum(ctx, generator, dst_dtype)?;
// dst_[...]ij += x // dst_[...]ij += x
let dst_ij = ctx.builder.build_load(pdst_ij, "").unwrap(); let dst_ij = ctx.builder.build_load(pdst_ij, "").unwrap();
@ -226,9 +224,7 @@ fn matmul_at_least_2d<'ctx, G: CodeGenerator>(
Binop::normal(Operator::Add), Binop::normal(Operator::Add),
(&Some(dst_dtype), x), (&Some(dst_dtype), x),
ctx.current_loc, ctx.current_loc,
)? )?;
.unwrap()
.to_basic_value_enum(ctx, generator, dst_dtype)?;
ctx.builder.build_store(pdst_ij, dst_ij).unwrap(); ctx.builder.build_store(pdst_ij, dst_ij).unwrap();
Ok(()) Ok(())

View File

@ -106,7 +106,7 @@ pub fn parse_numpy_int_sequence<'ctx, G: CodeGenerator + ?Sized>(
for i in 0..input_seq.get_type().num_elements() { for i in 0..input_seq.get_type().num_elements() {
// Get the i-th element off of the tuple and load it into `result`. // Get the i-th element off of the tuple and load it into `result`.
let int = input_seq.load_element(ctx, i).into_int_value(); let int = input_seq.extract_element(ctx, i).into_int_value();
let int = ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap(); let int = ctx.builder.build_int_s_extend_or_bit_cast(int, llvm_usize, "").unwrap();
unsafe { unsafe {

View File

@ -0,0 +1,75 @@
use inkwell::{
types::IntType,
values::{BasicValueEnum, IntValue, PointerValue},
};
use super::ProxyValue;
use crate::codegen::{types::OptionType, CodeGenContext};
/// 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

@ -0,0 +1,87 @@
use inkwell::{
types::IntType,
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
};
use crate::codegen::{
types::{structure::StructField, StringType},
values::ProxyValue,
CodeGenContext,
};
/// 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

@ -45,7 +45,7 @@ impl<'ctx> TupleValue<'ctx> {
} }
/// Stores a value into the tuple element at the given `index`. /// Stores a value into the tuple element at the given `index`.
pub fn store_element( pub fn insert_element(
&mut self, &mut self,
ctx: &CodeGenContext<'ctx, '_>, ctx: &CodeGenContext<'ctx, '_>,
index: u32, index: u32,
@ -63,7 +63,11 @@ impl<'ctx> TupleValue<'ctx> {
} }
/// Loads a value from the tuple element at the given `index`. /// Loads a value from the tuple element at the given `index`.
pub fn load_element(&self, ctx: &CodeGenContext<'ctx, '_>, index: u32) -> BasicValueEnum<'ctx> { pub fn extract_element(
&self,
ctx: &CodeGenContext<'ctx, '_>,
index: u32,
) -> BasicValueEnum<'ctx> {
ctx.builder ctx.builder
.build_extract_value( .build_extract_value(
self.value, self.value,

View File

@ -36,9 +36,7 @@ pub fn get_exn_constructor(
unifier: &mut Unifier, unifier: &mut Unifier,
primitives: &PrimitiveStore, primitives: &PrimitiveStore,
) -> (TopLevelDef, TopLevelDef, Type, Type) { ) -> (TopLevelDef, TopLevelDef, Type, Type) {
let int32 = primitives.int32; let PrimitiveStore { int32, int64, str: string, .. } = *primitives;
let int64 = primitives.int64;
let string = primitives.str;
let exception_fields = make_exception_fields(int32, int64, string); let exception_fields = make_exception_fields(int32, int64, string);
let exn_cons_args = vec![ let exn_cons_args = vec![
FuncArg { FuncArg {

View File

@ -1521,8 +1521,7 @@ impl TopLevelComposer {
.any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7)) .any(|ann| matches!(ann, TypeAnnotation::CustomClass { id, .. } if id.0 == 7))
{ {
// create constructor for these classes // create constructor for these classes
let string = primitives_ty.str; let PrimitiveStore { str: string, int64, .. } = *primitives_ty;
let int64 = primitives_ty.int64;
let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature { let signature = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![ args: vec![
FuncArg { FuncArg {