Kernel only attribute annotation (#76) #127

Merged
sb10q merged 8 commits from kernel_only_annotation into master 2021-12-19 11:04:53 +08:00
4 changed files with 125 additions and 69 deletions
Showing only changes of commit 6ce2f1b7ec - Show all commits

View File

@ -1,25 +1,30 @@
# NAC3 compiler # NAC3
NAC3 is a major, backward-incompatible rewrite of the compiler for the [ARTIQ](https://m-labs.hk/artiq) physics experiment control and data acquisition system. It features greatly improved compilation speeds, a much better type system, and more predictable and transparent operation.
NAC3 has a modular design and its applicability reaches beyond ARTIQ. The ``nac3core`` module does not contain anything specific to ARTIQ, and can be used in any project that requires compiling Python to machine code.
**WARNING: NAC3 is currently experimental software and several important features are not implemented yet.**
## Packaging
NAC3 is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2.4+ and enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
## Try NAC3
After setting up Nix as above, use ``nix shell github:m-labs/artiq/nac3`` to get a shell with the NAC3 version of ARTIQ. See the ``examples`` directory in ARTIQ (``nac3`` Git branch) for some samples of NAC3 kernel code.
## For developers
This repository contains: This repository contains:
- nac3ast: Python abstract syntax tree definition (based on RustPython). - ``nac3ast``: Python abstract syntax tree definition (based on RustPython).
- nac3parser: Python parser (based on RustPython). - ``nac3parser``: Python parser (based on RustPython).
- nac3core: Core compiler library, containing type-checking and code - ``nac3core``: Core compiler library, containing type-checking and code generation.
generation. - ``nac3standalone``: Standalone compiler tool (core language only).
- nac3standalone: Standalone compiler tool (core language only). - ``nac3artiq``: Integration with ARTIQ and implementation of ARTIQ-specific extensions to the core language.
- nac3artiq: Integration with ARTIQ and implementation of ARTIQ-specific - ``runkernel``: Simple program that runs compiled ARTIQ kernels on the host and displays RTIO operations. Useful for testing without hardware.
extensions to the core language.
- runkernel: Simple program that runs compiled ARTIQ kernels on the host
and displays RTIO operations. Useful for testing without hardware.
Use ``nix develop`` in this repository to enter a development shell.
If you are using a different shell than bash you can use e.g. ``nix develop --command fish``.
The core compiler knows nothing about symbol resolution, host variables Build NAC3 with ``cargo build --release``. See the demonstrations in ``nac3artiq`` and ``nac3standalone``.
etc. nac3artiq and nac3standalone provide (implement) the
symbol resolver to the core compiler for resolving the type and value for
unknown symbols. The core compiler only type checks classes and functions
requested by nac3artiq/nac3standalone (the API should allow the
caller to specify which methods should be compiled). After type checking, the
compiler analyses the set of functions/classes that are used and performs
code generation.
value could be integer values, boolean values, bytes (for memcpy), function ID
(full name + concrete type)

View File

@ -1,5 +1,5 @@
use std::cell::RefCell; use std::cell::RefCell;
use inkwell::{IntPredicate, FloatPredicate, values::BasicValueEnum}; use inkwell::{IntPredicate::{self, *}, FloatPredicate, values::IntValue};
use crate::{symbol_resolver::SymbolValue, codegen::expr::destructure_range}; use crate::{symbol_resolver::SymbolValue, codegen::expr::destructure_range};
use super::*; use super::*;
@ -69,10 +69,10 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
if ctx.unifier.unioned(arg_ty, boolean) { if ctx.unifier.unioned(arg_ty, boolean) {
Some( Some(
ctx.builder ctx.builder
.build_int_s_extend( .build_int_z_extend(
arg.into_int_value(), arg.into_int_value(),
ctx.ctx.i32_type(), ctx.ctx.i32_type(),
"sext", "zext",
) )
.into(), .into(),
) )
@ -129,10 +129,10 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
{ {
Some( Some(
ctx.builder ctx.builder
.build_int_s_extend( .build_int_z_extend(
arg.into_int_value(), arg.into_int_value(),
ctx.ctx.i64_type(), ctx.ctx.i64_type(),
"sext", "zext",
) )
.into(), .into(),
) )
@ -570,10 +570,10 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
ty: arg_ty.0, ty: arg_ty.0,
default_value: None default_value: None
}], }],
ret: int32, ret: int64,
vars: vec![(list_var.1, list_var.0), (arg_ty.1, arg_ty.0)].into_iter().collect(), vars: vec![(list_var.1, list_var.0), (arg_ty.1, arg_ty.0)].into_iter().collect(),
}))), }))),
var_id: Default::default(), var_id: vec![arg_ty.1],
instance_to_symbol: Default::default(), instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(), instance_to_stmt: Default::default(),
resolver: None, resolver: None,
@ -582,45 +582,13 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
let range_ty = ctx.primitives.range; let range_ty = ctx.primitives.range;
let arg_ty = fun.0.args[0].ty; let arg_ty = fun.0.args[0].ty;
let arg = args[0].1; let arg = args[0].1;
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
if ctx.unifier.unioned(arg_ty, range_ty) { if ctx.unifier.unioned(arg_ty, range_ty) {
let int1 = ctx.ctx.bool_type();
let one = int32.const_int(1, false);
let falze = int1.const_int(0, false);
let abs_intrinsic =
ctx.module.get_function("llvm.abs.i32").unwrap_or_else(|| {
let fn_type = int32.fn_type(&[int32.into(), int1.into()], false);
ctx.module.add_function("llvm.abs.i32", fn_type, None)
});
let arg = arg.into_pointer_value(); let arg = arg.into_pointer_value();
let (start, end, step) = destructure_range(ctx, arg); let (start, end, step) = destructure_range(ctx, arg);
let diff = ctx.builder.build_int_sub(end, start, "diff"); Some(calculate_len_for_slice_range(ctx, start, end, step).into())
let diff = if let BasicValueEnum::IntValue(val) = ctx
.builder
.build_call(abs_intrinsic, &[diff.into(), falze.into()], "absdiff")
.try_as_basic_value()
.left()
.unwrap() {
val
} else {
unreachable!();
};
let diff = ctx.builder.build_int_sub(diff, one, "diff");
let step = if let BasicValueEnum::IntValue(val) = ctx
.builder
.build_call(abs_intrinsic, &[step.into(), falze.into()], "absstep")
.try_as_basic_value()
.left()
.unwrap() {
val
} else {
unreachable!();
};
let length = ctx.builder.build_int_signed_div(diff, step, "div");
let length = ctx.builder.build_int_add(length, int32.const_int(1, false), "add1");
Some(length.into())
} else { } else {
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
Some(ctx.build_gep_and_load(arg.into_pointer_value(), &[zero, zero])) Some(ctx.build_gep_and_load(arg.into_pointer_value(), &[zero, zero]))
} }
}, },
@ -649,3 +617,78 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
] ]
) )
} }
// equivalent code:
// def length(start, end, step != 0):
// diff = end - start
// if diff > 0 and step > 0:
// return ((diff - 1) // step) + 1
// elif diff < 0 and step < 0:
// return ((diff + 1) // step) + 1
// else:
// return 0
pub fn calculate_len_for_slice_range<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
start: IntValue<'ctx>,
end: IntValue<'ctx>,
step: IntValue<'ctx>,
) -> IntValue<'ctx> {
let int64 = ctx.ctx.i64_type();
let start = ctx.builder.build_int_s_extend(start, int64, "start");
let end = ctx.builder.build_int_s_extend(end, int64, "end");
let step = ctx.builder.build_int_s_extend(step, int64, "step");
let diff = ctx.builder.build_int_sub(end, start, "diff");
let diff_pos = ctx.builder.build_int_compare(SGT, diff, int64.const_zero(), "diffpos");
let step_pos = ctx.builder.build_int_compare(SGT, step, int64.const_zero(), "steppos");
let test_1 = ctx.builder.build_and(diff_pos, step_pos, "bothpos");
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let then_bb = ctx.ctx.append_basic_block(current, "then");
let else_bb = ctx.ctx.append_basic_block(current, "else");
let then_bb_2 = ctx.ctx.append_basic_block(current, "then_2");
let else_bb_2 = ctx.ctx.append_basic_block(current, "else_2");
let cont_bb_2 = ctx.ctx.append_basic_block(current, "cont_2");
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
ctx.builder.build_conditional_branch(test_1, then_bb, else_bb);
ctx.builder.position_at_end(then_bb);
let length_pos = {
let diff_pos_min_1 = ctx.builder.build_int_sub(diff, int64.const_int(1, false), "diffminone");
let length_pos = ctx.builder.build_int_signed_div(diff_pos_min_1, step, "div");
ctx.builder.build_int_add(length_pos, int64.const_int(1, false), "add1")
};
ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(else_bb);
let phi_1 = {
let diff_neg = ctx.builder.build_int_compare(SLT, diff, int64.const_zero(), "diffneg");
let step_neg = ctx.builder.build_int_compare(SLT, step, int64.const_zero(), "stepneg");
let test_2 = ctx.builder.build_and(diff_neg, step_neg, "bothneg");
ctx.builder.build_conditional_branch(test_2, then_bb_2, else_bb_2);
ctx.builder.position_at_end(then_bb_2);
let length_neg = {
let diff_neg_add_1 = ctx.builder.build_int_add(diff, int64.const_int(1, false), "diffminone");
let length_neg = ctx.builder.build_int_signed_div(diff_neg_add_1, step, "div");
ctx.builder.build_int_add(length_neg, int64.const_int(1, false), "add1")
};
ctx.builder.build_unconditional_branch(cont_bb_2);
ctx.builder.position_at_end(else_bb_2);
let length_zero = int64.const_zero();
ctx.builder.build_unconditional_branch(cont_bb_2);
ctx.builder.position_at_end(cont_bb_2);
let phi_1 = ctx.builder.build_phi(int64, "lenphi1");
phi_1.add_incoming(&[(&length_neg, then_bb_2), (&length_zero, else_bb_2)]);
phi_1.as_basic_value().into_int_value()
};
ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(cont_bb);
let phi = ctx.builder.build_phi(int64, "lenphi");
phi.add_incoming(&[(&length_pos, then_bb), (&phi_1, cont_bb_2)]);
phi.as_basic_value().into_int_value()
}

View File

@ -1,5 +1,9 @@
@extern @extern
def output_int(x: int32): def output_int32(x: int32):
...
@extern
def output_int64(x: int64):
... ...
@ -27,10 +31,10 @@ class B:
def run() -> int32: def run() -> int32:
a = A(10) a = A(10)
output_int(a.a) output_int32(a.a)
a = A(20) a = A(20)
output_int(a.a) output_int32(a.a)
output_int(a.get_a()) output_int32(a.get_a())
output_int(a.get_b().b) output_int32(a.get_b().b)
return 0 return 0

View File

@ -1,10 +1,14 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
void output_int(int x) { void output_int32(int x) {
printf("%d\n", x); printf("%d\n", x);
} }
void output_int64(long x) {
printf("%ld\n", x);
}
void output_asciiart(int x) { void output_asciiart(int x) {
static char chars[] = " .,-:;i+hHM$*#@ "; static char chars[] = " .,-:;i+hHM$*#@ ";
if(x < 0) { if(x < 0) {