Compare commits

...

7 Commits

Author SHA1 Message Date
David Mak bf3aadff87 core: Move bitcode verification error message into panic message 2023-09-21 12:06:56 +08:00
David Mak bc7a4b34dd core: Minor refactor allocate_list 2023-09-21 12:06:56 +08:00
David Mak 1368c648c5 core: Add name to build_gep_and_load 2023-09-21 12:06:56 +08:00
David Mak 20aa094b1f core: Remove emit_llvm from CodeGenLLVMOptions
We instead output an LLVM bitcode file when the option is specified on
the command-line.
2023-09-21 12:06:56 +08:00
David Mak 5ab8d751ed standalone: Add ability to execute via lli 2023-09-21 12:06:56 +08:00
David Mak c7d5d75014 core: Replace deprecated _ExtInt with _BitInt 2023-09-21 12:06:56 +08:00
David Mak 72570fbb16 core: Fix missing changes for codegen tests
Apparently the changes were dropped after rebasing.
2023-09-21 12:06:56 +08:00
13 changed files with 268 additions and 96 deletions

View File

@ -529,7 +529,7 @@ pub fn attributes_writeback<'ctx, 'a>(
let index = ctx.get_attr_index(ty, *name); let index = ctx.get_attr_index(ty, *name);
values.push((*field_ty, ctx.build_gep_and_load( values.push((*field_ty, ctx.build_gep_and_load(
obj.into_pointer_value(), obj.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)]))); &[zero, int32.const_int(index as u64, false)], None)));
} }
} }
if !attributes.is_empty() { if !attributes.is_empty() {

View File

@ -916,7 +916,6 @@ impl Nac3 {
llvm_options: CodeGenLLVMOptions { llvm_options: CodeGenLLVMOptions {
opt_level: OptimizationLevel::Default, opt_level: OptimizationLevel::Default,
target: Nac3::get_llvm_target_options(isa), target: Nac3::get_llvm_target_options(isa),
emit_llvm: false,
} }
}) })
} }

View File

@ -62,8 +62,9 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
&mut self, &mut self,
ptr: PointerValue<'ctx>, ptr: PointerValue<'ctx>,
index: &[IntValue<'ctx>], index: &[IntValue<'ctx>],
name: Option<&str>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
unsafe { self.builder.build_load(self.builder.build_gep(ptr, index, "gep"), "load") } unsafe { self.builder.build_load(self.builder.build_gep(ptr, index, ""), name.unwrap_or_default()) }
} }
fn get_subst_key( fn get_subst_key(
@ -753,43 +754,66 @@ pub fn destructure_range<'ctx, 'a>(
) -> (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>) { ) -> (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>) {
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let start = ctx let start = ctx
.build_gep_and_load(range, &[int32.const_zero(), int32.const_int(0, false)]) .build_gep_and_load(range, &[int32.const_zero(), int32.const_int(0, false)], Some("range.start"))
.into_int_value(); .into_int_value();
let end = ctx let end = ctx
.build_gep_and_load(range, &[int32.const_zero(), int32.const_int(1, false)]) .build_gep_and_load(range, &[int32.const_zero(), int32.const_int(1, false)], Some("range.stop"))
.into_int_value(); .into_int_value();
let step = ctx let step = ctx
.build_gep_and_load(range, &[int32.const_zero(), int32.const_int(2, false)]) .build_gep_and_load(range, &[int32.const_zero(), int32.const_int(2, false)], Some("range.step"))
.into_int_value(); .into_int_value();
(start, end, step) (start, end, step)
} }
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting
/// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
///
/// Returns an instance of [PointerValue] pointing to the List structure. The List structure is
/// defined as `type { ty*, size_t }` in LLVM, where the first element stores the pointer to the
/// data, and the second element stores the size of the List.
pub fn allocate_list<'ctx, 'a, G: CodeGenerator>( pub fn allocate_list<'ctx, 'a, G: CodeGenerator>(
generator: &mut G, generator: &mut G,
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
ty: BasicTypeEnum<'ctx>, ty: BasicTypeEnum<'ctx>,
length: IntValue<'ctx>, length: IntValue<'ctx>,
name: Option<&str>,
) -> PointerValue<'ctx> { ) -> PointerValue<'ctx> {
let arr_ptr = ctx.builder.build_array_alloca(ty, length, "tmparr");
let size_t = generator.get_size_type(ctx.ctx); let size_t = generator.get_size_type(ctx.ctx);
let i32_t = ctx.ctx.i32_type(); let i32_t = ctx.ctx.i32_type();
let arr_ty = // List structure; type { ty*, size_t }
ctx.ctx.struct_type(&[ty.ptr_type(AddressSpace::default()).into(), size_t.into()], false); let arr_ty = ctx.ctx
.struct_type(&[ty.ptr_type(AddressSpace::default()).into(), size_t.into()], false);
let zero = ctx.ctx.i32_type().const_zero(); let zero = ctx.ctx.i32_type().const_zero();
let arr_str_ptr = ctx.builder.build_alloca(arr_ty, "tmparrstr");
let arr_str_ptr = ctx.builder.build_alloca(
arr_ty, format!("{}.addr", name.unwrap_or("list")).as_str()
);
unsafe { unsafe {
// Pointer to the `length` element of the list structure
let len_ptr = ctx.builder.build_in_bounds_gep( let len_ptr = ctx.builder.build_in_bounds_gep(
arr_str_ptr, arr_str_ptr,
&[zero, i32_t.const_int(1, false)], &[zero, i32_t.const_int(1, false)],
"len_ptr", ""
);
let length = ctx.builder.build_int_z_extend(
length,
size_t,
""
); );
let length = ctx.builder.build_int_z_extend(length, size_t, "zext");
ctx.builder.build_store(len_ptr, length); ctx.builder.build_store(len_ptr, length);
let ptr_to_arr =
ctx.builder.build_in_bounds_gep(arr_str_ptr, &[zero, i32_t.const_zero()], "ptr_to_arr"); // Pointer to the `data` element of the list structure
let arr_ptr = ctx.builder.build_array_alloca(ty, length, "");
let ptr_to_arr = ctx.builder.build_in_bounds_gep(
arr_str_ptr,
&[zero, i32_t.const_zero()],
""
);
ctx.builder.build_store(ptr_to_arr, arr_ptr); ctx.builder.build_store(ptr_to_arr, arr_ptr);
arr_str_ptr
} }
arr_str_ptr
} }
pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>( pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
@ -799,9 +823,15 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
) -> Result<BasicValueEnum<'ctx>, String> { ) -> Result<BasicValueEnum<'ctx>, String> {
if let ExprKind::ListComp { elt, generators } = &expr.node { if let ExprKind::ListComp { elt, generators } = &expr.node {
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap(); let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let test_bb = ctx.ctx.append_basic_block(current, "test");
let body_bb = ctx.ctx.append_basic_block(current, "body"); let init_bb = ctx.ctx.append_basic_block(current, "listcomp.init");
let cont_bb = ctx.ctx.append_basic_block(current, "cont"); let test_bb = ctx.ctx.append_basic_block(current, "listcomp.test");
let body_bb = ctx.ctx.append_basic_block(current, "listcomp.body");
let cont_bb = ctx.ctx.append_basic_block(current, "listcomp.cont");
ctx.builder.build_unconditional_branch(init_bb);
ctx.builder.position_at_end(init_bb);
let Comprehension { target, iter, ifs, .. } = &generators[0]; let Comprehension { target, iter, ifs, .. } = &generators[0];
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator, iter.custom.unwrap())?; let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator, iter.custom.unwrap())?;
@ -830,10 +860,11 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
// in case length is non-positive // in case length is non-positive
let is_valid = let is_valid =
ctx.builder.build_int_compare(inkwell::IntPredicate::SGT, length, zero_32, "check"); ctx.builder.build_int_compare(inkwell::IntPredicate::SGT, length, zero_32, "check");
let normal = ctx.ctx.append_basic_block(current, "normal_list"); let normal = ctx.ctx.append_basic_block(current, "listcomp.normal_list");
let empty = ctx.ctx.append_basic_block(current, "empty_list"); let empty = ctx.ctx.append_basic_block(current, "listcomp.empty_list");
let list_init = ctx.ctx.append_basic_block(current, "list_init"); let list_init = ctx.ctx.append_basic_block(current, "listcomp.list_init");
ctx.builder.build_conditional_branch(is_valid, normal, empty); ctx.builder.build_conditional_branch(is_valid, normal, empty);
// normal: allocate a list // normal: allocate a list
ctx.builder.position_at_end(normal); ctx.builder.position_at_end(normal);
let list_a = allocate_list( let list_a = allocate_list(
@ -841,21 +872,31 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
ctx, ctx,
elem_ty, elem_ty,
ctx.builder.build_int_z_extend_or_bit_cast(length, size_t, "z_ext_len"), ctx.builder.build_int_z_extend_or_bit_cast(length, size_t, "z_ext_len"),
Some("listcomp"),
); );
ctx.builder.build_unconditional_branch(list_init); ctx.builder.build_unconditional_branch(list_init);
ctx.builder.position_at_end(empty); ctx.builder.position_at_end(empty);
let list_b = allocate_list(generator, ctx, elem_ty, zero_size_t); let list_b = allocate_list(
generator,
ctx,
elem_ty,
zero_size_t,
Some("list_b")
);
ctx.builder.build_unconditional_branch(list_init); ctx.builder.build_unconditional_branch(list_init);
ctx.builder.position_at_end(list_init); ctx.builder.position_at_end(list_init);
let phi = ctx.builder.build_phi(list_a.get_type(), "phi"); let phi = ctx.builder.build_phi(list_a.get_type(), "phi");
phi.add_incoming(&[(&list_a, normal), (&list_b, empty)]); phi.add_incoming(&[(&list_a, normal), (&list_b, empty)]);
list = phi.as_basic_value().into_pointer_value(); list = phi.as_basic_value().into_pointer_value();
list_content = list_content = ctx.build_gep_and_load(list, &[zero_size_t, zero_32], Some("list_content"))
ctx.build_gep_and_load(list, &[zero_size_t, zero_32]).into_pointer_value(); .into_pointer_value();
let i = generator.gen_store_target(ctx, target, Some("i.addr"))?; let i = generator.gen_store_target(ctx, target, Some("i.addr"))?;
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init")); ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
ctx.builder.build_unconditional_branch(test_bb); ctx.builder.build_unconditional_branch(test_bb);
ctx.builder.position_at_end(test_bb); ctx.builder.position_at_end(test_bb);
let sign = let sign =
ctx.builder.build_int_compare(inkwell::IntPredicate::SGT, step, zero_32, "sign"); ctx.builder.build_int_compare(inkwell::IntPredicate::SGT, step, zero_32, "sign");
@ -877,32 +918,36 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
body_bb, body_bb,
cont_bb, cont_bb,
); );
ctx.builder.position_at_end(body_bb); ctx.builder.position_at_end(body_bb);
} else { } else {
let length = ctx let length = ctx
.build_gep_and_load( .build_gep_and_load(
iter_val.into_pointer_value(), iter_val.into_pointer_value(),
&[zero_size_t, int32.const_int(1, false)], &[zero_size_t, int32.const_int(1, false)],
Some("length"),
) )
.into_int_value(); .into_int_value();
list = allocate_list(generator, ctx, elem_ty, length); list = allocate_list(generator, ctx, elem_ty, length, Some("listcomp"));
list_content = list_content =
ctx.build_gep_and_load(list, &[zero_size_t, zero_32]).into_pointer_value(); ctx.build_gep_and_load(list, &[zero_size_t, zero_32], Some("list_content")).into_pointer_value();
let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?; let counter = generator.gen_var_alloc(ctx, size_t.into(), Some("counter.addr"))?;
// counter = -1 // counter = -1
ctx.builder.build_store(counter, size_t.const_int(u64::max_value(), true)); ctx.builder.build_store(counter, size_t.const_int(u64::max_value(), true));
ctx.builder.build_unconditional_branch(test_bb); ctx.builder.build_unconditional_branch(test_bb);
ctx.builder.position_at_end(test_bb); ctx.builder.position_at_end(test_bb);
let tmp = ctx.builder.build_load(counter, "i").into_int_value(); let tmp = ctx.builder.build_load(counter, "i").into_int_value();
let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc"); let tmp = ctx.builder.build_int_add(tmp, size_t.const_int(1, false), "inc");
ctx.builder.build_store(counter, tmp); ctx.builder.build_store(counter, tmp);
let cmp = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, tmp, length, "cmp"); let cmp = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, tmp, length, "cmp");
ctx.builder.build_conditional_branch(cmp, body_bb, cont_bb); ctx.builder.build_conditional_branch(cmp, body_bb, cont_bb);
ctx.builder.position_at_end(body_bb); ctx.builder.position_at_end(body_bb);
let arr_ptr = ctx let arr_ptr = ctx
.build_gep_and_load(iter_val.into_pointer_value(), &[zero_size_t, zero_32]) .build_gep_and_load(iter_val.into_pointer_value(), &[zero_size_t, zero_32], Some("arr.addr"))
.into_pointer_value(); .into_pointer_value();
let val = ctx.build_gep_and_load(arr_ptr, &[tmp]); let val = ctx.build_gep_and_load(arr_ptr, &[tmp], Some("val"));
generator.gen_assign(ctx, target, val.into())?; generator.gen_assign(ctx, target, val.into())?;
} }
for cond in ifs.iter() { for cond in ifs.iter() {
@ -913,8 +958,10 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
.into_int_value(); .into_int_value();
let succ = ctx.ctx.append_basic_block(current, "then"); let succ = ctx.ctx.append_basic_block(current, "then");
ctx.builder.build_conditional_branch(result, succ, test_bb); ctx.builder.build_conditional_branch(result, succ, test_bb);
ctx.builder.position_at_end(succ); ctx.builder.position_at_end(succ);
} }
let elem = generator.gen_expr(ctx, elt)?.unwrap(); let elem = generator.gen_expr(ctx, elt)?.unwrap();
let i = ctx.builder.build_load(index, "i").into_int_value(); let i = ctx.builder.build_load(index, "i").into_int_value();
let elem_ptr = unsafe { ctx.builder.build_gep(list_content, &[i], "elem_ptr") }; let elem_ptr = unsafe { ctx.builder.build_gep(list_content, &[i], "elem_ptr") };
@ -923,11 +970,13 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
ctx.builder ctx.builder
.build_store(index, ctx.builder.build_int_add(i, size_t.const_int(1, false), "inc")); .build_store(index, ctx.builder.build_int_add(i, size_t.const_int(1, false), "inc"));
ctx.builder.build_unconditional_branch(test_bb); ctx.builder.build_unconditional_branch(test_bb);
ctx.builder.position_at_end(cont_bb); ctx.builder.position_at_end(cont_bb);
let len_ptr = unsafe { let len_ptr = unsafe {
ctx.builder.build_gep(list, &[zero_size_t, int32.const_int(1, false)], "length") ctx.builder.build_gep(list, &[zero_size_t, int32.const_int(1, false)], "length")
}; };
ctx.builder.build_store(len_ptr, ctx.builder.build_load(index, "index")); ctx.builder.build_store(len_ptr, ctx.builder.build_load(index, "index"));
Ok(list.into()) Ok(list.into())
} else { } else {
unreachable!() unreachable!()
@ -1070,7 +1119,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
} }
} }
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) { ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(), Some((ptr, None, _)) => ctx.builder.build_load(*ptr, id.to_string().as_str()).into(),
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()), Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),
None => { None => {
let resolver = ctx.resolver.clone(); let resolver = ctx.resolver.clone();
@ -1101,8 +1150,9 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
elements[0].get_type() elements[0].get_type()
}; };
let length = generator.get_size_type(ctx.ctx).const_int(elements.len() as u64, false); let length = generator.get_size_type(ctx.ctx).const_int(elements.len() as u64, false);
let arr_str_ptr = allocate_list(generator, ctx, ty, length); let arr_str_ptr = allocate_list(generator, ctx, ty, length, Some("list"));
let arr_ptr = ctx.build_gep_and_load(arr_str_ptr, &[zero, zero]).into_pointer_value(); let arr_ptr = ctx.build_gep_and_load(arr_str_ptr, &[zero, zero], Some("list.ptr.addr"))
.into_pointer_value();
unsafe { unsafe {
for (i, v) in elements.iter().enumerate() { for (i, v) in elements.iter().enumerate() {
let elem_ptr = ctx.builder.build_gep( let elem_ptr = ctx.builder.build_gep(
@ -1148,6 +1198,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
Ok(ValueEnum::Dynamic(ctx.build_gep_and_load( Ok(ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(), v.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)], &[zero, int32.const_int(index as u64, false)],
None,
))) as Result<_, String> ))) as Result<_, String>
}, Ok)?, }, Ok)?,
ValueEnum::Dynamic(v) => { ValueEnum::Dynamic(v) => {
@ -1155,6 +1206,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
ValueEnum::Dynamic(ctx.build_gep_and_load( ValueEnum::Dynamic(ctx.build_gep_and_load(
v.into_pointer_value(), v.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)], &[zero, int32.const_int(index as u64, false)],
None,
)) ))
} }
} }
@ -1511,7 +1563,8 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.to_basic_value_enum(ctx, generator, value.custom.unwrap())? .to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_pointer_value(); .into_pointer_value();
let ty = ctx.get_llvm_type(generator, *ty); let ty = ctx.get_llvm_type(generator, *ty);
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero]).into_pointer_value(); let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero], Some("arr.addr"))
.into_pointer_value();
if let ExprKind::Slice { lower, upper, step } = &slice.node { if let ExprKind::Slice { lower, upper, step } = &slice.node {
let one = int32.const_int(1, false); let one = int32.const_int(1, false);
let (start, end, step) = let (start, end, step) =
@ -1535,7 +1588,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.into_int_value(), .into_int_value(),
step, step,
); );
let res_array_ret = allocate_list(generator, ctx, ty, length); let res_array_ret = allocate_list(generator, ctx, ty, length, Some("ret"));
let res_ind = let res_ind =
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)?; handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)?;
list_slice_assignment( list_slice_assignment(
@ -1550,7 +1603,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
res_array_ret.into() res_array_ret.into()
} else { } else {
let len = ctx let len = ctx
.build_gep_and_load(v, &[zero, int32.const_int(1, false)]) .build_gep_and_load(v, &[zero, int32.const_int(1, false)], Some("len"))
.into_int_value(); .into_int_value();
let raw_index = generator let raw_index = generator
.gen_expr(ctx, slice)? .gen_expr(ctx, slice)?
@ -1590,7 +1643,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
[Some(raw_index), Some(len), None], [Some(raw_index), Some(len), None],
expr.location, expr.location,
); );
ctx.build_gep_and_load(arr_ptr, &[index]).into() ctx.build_gep_and_load(arr_ptr, &[index], None).into()
} }
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) { } else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
let index: u32 = let index: u32 =

View File

@ -1,9 +1,9 @@
typedef _ExtInt(8) int8_t; typedef _BitInt(8) int8_t;
typedef unsigned _ExtInt(8) uint8_t; typedef unsigned _BitInt(8) uint8_t;
typedef _ExtInt(32) int32_t; typedef _BitInt(32) int32_t;
typedef unsigned _ExtInt(32) uint32_t; typedef unsigned _BitInt(32) uint32_t;
typedef _ExtInt(64) int64_t; typedef _BitInt(64) int64_t;
typedef unsigned _ExtInt(64) uint64_t; typedef unsigned _BitInt(64) uint64_t;
# define MAX(a, b) (a > b ? a : b) # define MAX(a, b) (a > b ? a : b)
# define MIN(a, b) (a > b ? b : a) # define MIN(a, b) (a > b ? b : a)

View File

@ -162,7 +162,7 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero(); let zero = int32.const_zero();
let one = int32.const_int(1, false); let one = int32.const_int(1, false);
let length = ctx.build_gep_and_load(list, &[zero, one]).into_int_value(); let length = ctx.build_gep_and_load(list, &[zero, one], Some("length")).into_int_value();
let length = ctx.builder.build_int_truncate_or_bit_cast(length, int32, "leni32"); let length = ctx.builder.build_int_truncate_or_bit_cast(length, int32, "leni32");
Ok(match (start, end, step) { Ok(match (start, end, step) {
(s, e, None) => ( (s, e, None) => (
@ -309,21 +309,21 @@ pub fn list_slice_assignment<'ctx, 'a>(
let zero = int32.const_zero(); let zero = int32.const_zero();
let one = int32.const_int(1, false); let one = int32.const_int(1, false);
let dest_arr_ptr = ctx.build_gep_and_load(dest_arr, &[zero, zero]); let dest_arr_ptr = ctx.build_gep_and_load(dest_arr, &[zero, zero], Some("dest.addr"));
let dest_arr_ptr = ctx.builder.build_pointer_cast( let dest_arr_ptr = ctx.builder.build_pointer_cast(
dest_arr_ptr.into_pointer_value(), dest_arr_ptr.into_pointer_value(),
elem_ptr_type, elem_ptr_type,
"dest_arr_ptr_cast", "dest_arr_ptr_cast",
); );
let dest_len = ctx.build_gep_and_load(dest_arr, &[zero, one]).into_int_value(); let dest_len = ctx.build_gep_and_load(dest_arr, &[zero, one], Some("dest.len")).into_int_value();
let dest_len = ctx.builder.build_int_truncate_or_bit_cast(dest_len, int32, "srclen32"); let dest_len = ctx.builder.build_int_truncate_or_bit_cast(dest_len, int32, "srclen32");
let src_arr_ptr = ctx.build_gep_and_load(src_arr, &[zero, zero]); let src_arr_ptr = ctx.build_gep_and_load(src_arr, &[zero, zero], Some("src.addr"));
let src_arr_ptr = ctx.builder.build_pointer_cast( let src_arr_ptr = ctx.builder.build_pointer_cast(
src_arr_ptr.into_pointer_value(), src_arr_ptr.into_pointer_value(),
elem_ptr_type, elem_ptr_type,
"src_arr_ptr_cast", "src_arr_ptr_cast",
); );
let src_len = ctx.build_gep_and_load(src_arr, &[zero, one]).into_int_value(); let src_len = ctx.build_gep_and_load(src_arr, &[zero, one], Some("src.len")).into_int_value();
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32"); let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32");
// index in bound and positive should be done // index in bound and positive should be done

View File

@ -61,9 +61,6 @@ pub struct CodeGenLLVMOptions {
/// Options related to the target machine. /// Options related to the target machine.
pub target: CodeGenTargetMachineOptions, pub target: CodeGenTargetMachineOptions,
/// Whether to output the LLVM IR after generation is complete.
pub emit_llvm: bool,
} }
/// Additional options for code generation for the target machine. /// Additional options for code generation for the target machine.
@ -322,8 +319,7 @@ impl WorkerRegistry {
let result = module.verify(); let result = module.verify();
if let Err(err) = result { if let Err(err) = result {
println!("{}", module.print_to_string().to_str().unwrap()); println!("{}", module.print_to_string().to_str().unwrap());
println!("{}", err.to_string()); panic!("{}", err.to_string())
panic!()
} }
let pass_options = PassBuilderOptions::create(); let pass_options = PassBuilderOptions::create();
@ -339,11 +335,6 @@ impl WorkerRegistry {
err.to_string()); err.to_string());
} }
if self.llvm_options.emit_llvm {
println!("LLVM IR for {}\n{}", module.get_name().to_str().unwrap(), module.to_string());
println!();
}
f.run(&module); f.run(&module);
let mut lock = self.task_count.lock(); let mut lock = self.task_count.lock();
*lock += 1; *lock += 1;

View File

@ -93,7 +93,7 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
.to_basic_value_enum(ctx, generator, value.custom.unwrap())? .to_basic_value_enum(ctx, generator, value.custom.unwrap())?
.into_pointer_value(); .into_pointer_value();
let len = ctx let len = ctx
.build_gep_and_load(v, &[zero, i32_type.const_int(1, false)]) .build_gep_and_load(v, &[zero, i32_type.const_int(1, false)], Some("len"))
.into_int_value(); .into_int_value();
let raw_index = generator let raw_index = generator
.gen_expr(ctx, slice)? .gen_expr(ctx, slice)?
@ -135,7 +135,7 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
); );
unsafe { unsafe {
let arr_ptr = ctx let arr_ptr = ctx
.build_gep_and_load(v, &[i32_type.const_zero(), i32_type.const_zero()]) .build_gep_and_load(v, &[i32_type.const_zero(), i32_type.const_zero()], Some("arr.addr"))
.into_pointer_value(); .into_pointer_value();
ctx.builder.build_gep(arr_ptr, &[index], name.unwrap_or("")) ctx.builder.build_gep(arr_ptr, &[index], name.unwrap_or(""))
} }
@ -327,6 +327,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
.build_gep_and_load( .build_gep_and_load(
iter_val.into_pointer_value(), iter_val.into_pointer_value(),
&[zero, int32.const_int(1, false)], &[zero, int32.const_int(1, false)],
Some("len")
) )
.into_int_value(); .into_int_value();
ctx.builder.build_unconditional_branch(test_bb); ctx.builder.build_unconditional_branch(test_bb);
@ -338,9 +339,9 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
ctx.builder.position_at_end(body_bb); ctx.builder.position_at_end(body_bb);
let arr_ptr = ctx let arr_ptr = ctx
.build_gep_and_load(iter_val.into_pointer_value(), &[zero, zero]) .build_gep_and_load(iter_val.into_pointer_value(), &[zero, zero], Some("arr.addr"))
.into_pointer_value(); .into_pointer_value();
let val = ctx.build_gep_and_load(arr_ptr, &[index]); let val = ctx.build_gep_and_load(arr_ptr, &[index], Some("val"));
generator.gen_assign(ctx, target, val.into())?; generator.gen_assign(ctx, target, val.into())?;
gen_block(generator, ctx, body.iter())?; gen_block(generator, ctx, body.iter())?;

View File

@ -21,6 +21,7 @@ use nac3parser::{
use parking_lot::RwLock; use parking_lot::RwLock;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::sync::Arc; use std::sync::Arc;
use inkwell::targets::{InitializationConfig, Target};
struct Resolver { struct Resolver {
id_to_type: HashMap<StrRef, Type>, id_to_type: HashMap<StrRef, Type>,
@ -181,24 +182,18 @@ fn test_primitives() {
let expected = indoc! {" let expected = indoc! {"
; ModuleID = 'test' ; ModuleID = 'test'
source_filename = \"test\" source_filename = \"test\"
define i32 @testing(i32 %0, i32 %1) !dbg !4 { ; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @testing(i32 %0, i32 %1) local_unnamed_addr #0 !dbg !4 {
init: init:
%add = add i32 %0, %1, !dbg !9 %add = add i32 %1, %0, !dbg !9
%cmp = icmp eq i32 %add, 1, !dbg !10 %cmp = icmp eq i32 %add, 1, !dbg !10
br i1 %cmp, label %then, label %else, !dbg !10 %. = select i1 %cmp, i32 %0, i32 0, !dbg !11
ret i32 %., !dbg !12
then: ; preds = %init
br label %cont, !dbg !11
else: ; preds = %init
br label %cont, !dbg !12
cont: ; preds = %else, %then
%if_exp_result.0 = phi i32 [ %0, %then ], [ 0, %else ], !dbg !13
ret i32 %if_exp_result.0, !dbg !14
} }
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
!llvm.module.flags = !{!0, !1} !llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2} !llvm.dbg.cu = !{!2}
@ -213,19 +208,18 @@ fn test_primitives() {
!8 = !{} !8 = !{}
!9 = !DILocation(line: 1, column: 9, scope: !4) !9 = !DILocation(line: 1, column: 9, scope: !4)
!10 = !DILocation(line: 2, column: 15, scope: !4) !10 = !DILocation(line: 2, column: 15, scope: !4)
!11 = !DILocation(line: 2, column: 5, scope: !4) !11 = !DILocation(line: 0, scope: !4)
!12 = !DILocation(line: 2, column: 22, scope: !4) !12 = !DILocation(line: 3, column: 8, scope: !4)
!13 = !DILocation(line: 0, scope: !4)
!14 = !DILocation(line: 3, column: 8, scope: !4)
"} "}
.trim(); .trim();
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim()); assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
}))); })));
Target::initialize_all(&InitializationConfig::default());
let llvm_options = CodeGenLLVMOptions { let llvm_options = CodeGenLLVMOptions {
opt_level: OptimizationLevel::Default, opt_level: OptimizationLevel::Default,
target: CodeGenTargetMachineOptions::from_host_triple(), target: CodeGenTargetMachineOptions::from_host_triple(),
emit_llvm: false,
}; };
let (registry, handles) = WorkerRegistry::create_workers( let (registry, handles) = WorkerRegistry::create_workers(
threads, threads,
@ -373,22 +367,26 @@ fn test_simple_call() {
; ModuleID = 'test' ; ModuleID = 'test'
source_filename = \"test\" source_filename = \"test\"
define i32 @testing(i32 %0) !dbg !5 { ; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @testing(i32 %0) local_unnamed_addr #0 !dbg !5 {
init: init:
%call = call i32 @foo.0(i32 %0), !dbg !10 %add.i = shl i32 %0, 1, !dbg !10
%mul = mul i32 %call, 2, !dbg !11 %mul = add i32 %add.i, 2, !dbg !10
ret i32 %mul, !dbg !11 ret i32 %mul, !dbg !10
} }
define i32 @foo.0(i32 %0) !dbg !12 { ; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn
define i32 @foo.0(i32 %0) local_unnamed_addr #0 !dbg !11 {
init: init:
%add = add i32 %0, 1, !dbg !13 %add = add i32 %0, 1, !dbg !12
ret i32 %add, !dbg !13 ret i32 %add, !dbg !12
} }
attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn }
!llvm.module.flags = !{!0, !1} !llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2, !4} !llvm.dbg.cu = !{!2, !4}
!0 = !{i32 2, !\"Debug Info Version\", i32 3} !0 = !{i32 2, !\"Debug Info Version\", i32 3}
!1 = !{i32 2, !\"Dwarf Version\", i32 4} !1 = !{i32 2, !\"Dwarf Version\", i32 4}
!2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) !2 = distinct !DICompileUnit(language: DW_LANG_Python, file: !3, producer: \"NAC3\", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
@ -399,19 +397,19 @@ fn test_simple_call() {
!7 = !{!8} !7 = !{!8}
!8 = !DIBasicType(name: \"_\", flags: DIFlagPublic) !8 = !DIBasicType(name: \"_\", flags: DIFlagPublic)
!9 = !{} !9 = !{}
!10 = !DILocation(line: 1, column: 9, scope: !5) !10 = !DILocation(line: 2, column: 12, scope: !5)
!11 = !DILocation(line: 2, column: 12, scope: !5) !11 = distinct !DISubprogram(name: \"foo.0\", linkageName: \"foo.0\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9)
!12 = distinct !DISubprogram(name: \"foo.0\", linkageName: \"foo.0\", scope: null, file: !3, line: 1, type: !6, scopeLine: 1, flags: DIFlagPublic, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !4, retainedNodes: !9) !12 = !DILocation(line: 1, column: 12, scope: !11)
!13 = !DILocation(line: 1, column: 12, scope: !12)
"} "}
.trim(); .trim();
assert_eq!(expected, module.print_to_string().to_str().unwrap().trim()); assert_eq!(expected, module.print_to_string().to_str().unwrap().trim());
}))); })));
Target::initialize_all(&InitializationConfig::default());
let llvm_options = CodeGenLLVMOptions { let llvm_options = CodeGenLLVMOptions {
opt_level: OptimizationLevel::Default, opt_level: OptimizationLevel::Default,
target: CodeGenTargetMachineOptions::from_host_triple(), target: CodeGenTargetMachineOptions::from_host_triple(),
emit_llvm: false,
}; };
let (registry, handles) = WorkerRegistry::create_workers( let (registry, handles) = WorkerRegistry::create_workers(
threads, threads,

View File

@ -1013,6 +1013,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
.build_gep_and_load( .build_gep_and_load(
arg.into_pointer_value(), arg.into_pointer_value(),
&[zero, int32.const_int(1, false)], &[zero, int32.const_int(1, false)],
None,
) )
.into_int_value(); .into_int_value();
if len.get_type().get_bit_width() != 32 { if len.get_type().get_bit_width() != 32 {

View File

@ -0,0 +1,84 @@
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define min(a, b) \
({ \
__typeof__(a) _a = (a); \
__typeof__(b) _b = (b); \
_a < _b ? _a : _b; \
})
#if __SIZEOF_POINTER__ == 8
#define usize uint64_t
#elif __SIZEOF_POINTER__ == 4
#define usize uint32_t
#elif __SIZEOF_POINTER__ == 2
#define usize uint16_t
#endif
void output_int32(const int32_t x) {
printf("%d\n", x);
}
void output_int64(const int64_t x) {
printf("%ld\n", x);
}
void output_uint32(const uint32_t x) {
printf("%d\n", x);
}
void output_uint64(const uint64_t x) {
printf("%ld\n", x);
}
void output_asciiart(const int32_t x) {
const char* chars = " .,-:;i+hHM$*#@ ";
if (x < 0) {
fputc('\n', stdout);
} else {
fputc(chars[x], stdout);
}
}
struct cslice_int32 {
const int32_t* data;
usize len;
};
void output_int32_list(struct cslice_int32* slice) {
fputc('[', stdout);
for (usize i = 0; i < slice->len; ++i) {
if (i == slice->len - 1) {
printf("%d", slice->data[i]);
} else {
printf("%d, ", slice->data[i]);
}
}
puts("]");
}
uint32_t __nac3_personality(
__attribute__((unused)) uint32_t state,
__attribute__((unused)) uint32_t exception_object,
__attribute__((unused)) uint32_t context) {
assert(false && "__nac3_personality not implemented");
exit(101);
__builtin_unreachable();
}
uint32_t __nac3_raise(uint32_t state, uint32_t exception_object, uint32_t context) {
printf("__nac3_raise(state: %x, exception_object: %x, context: %x\n", state, exception_object, context);
exit(101);
__builtin_unreachable();
}
extern int32_t run();
int main() {
run();
}

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -e
if [ -z "$1" ]; then
echo "No argument supplied"
exit 1
fi
if [ -e ../../target/release/nac3standalone ]; then
nac3standalone=../../target/release/nac3standalone
else
# used by Nix builds
nac3standalone=../../target/x86_64-unknown-linux-gnu/release/nac3standalone
fi
rm -f *.o *.bc
$nac3standalone --emit-llvm "$@"
gcc -c -std=c11 -Wall -Wextra -pedantic-errors -Werror=pedantic -O3 -o demo.o demo.c
clang -S -Wall -Wextra -O3 -emit-llvm -o irrt.bc ../../nac3core/src/codegen/irrt/irrt.c
lli --extra-object demo.o --extra-module irrt.bc main.bc

View File

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

View File

@ -305,7 +305,6 @@ fn main() {
features: target_features, features: target_features,
..host_target_machine ..host_target_machine
}, },
emit_llvm,
}; };
let task = CodeGenTask { let task = CodeGenTask {
@ -340,11 +339,19 @@ fn main() {
let main = context let main = context
.create_module_from_ir(MemoryBuffer::create_from_memory_range(&buffers[0], "main")) .create_module_from_ir(MemoryBuffer::create_from_memory_range(&buffers[0], "main"))
.unwrap(); .unwrap();
for buffer in buffers.iter().skip(1) { if emit_llvm {
main.write_bitcode_to_path(Path::new("main.bc"));
}
for (idx, buffer) in buffers.iter().skip(1).enumerate() {
let other = context let other = context
.create_module_from_ir(MemoryBuffer::create_from_memory_range(buffer, "main")) .create_module_from_ir(MemoryBuffer::create_from_memory_range(buffer, "main"))
.unwrap(); .unwrap();
if emit_llvm {
other.write_bitcode_to_path(Path::new(&format!("module{}.bc", idx)));
}
main.link_in_module(other).unwrap(); main.link_in_module(other).unwrap();
} }
main.link_in_module(load_irrt(&context)).unwrap(); main.link_in_module(load_irrt(&context)).unwrap();