From aff819dfefd124caf6310d191a087826c8c3a95c Mon Sep 17 00:00:00 2001 From: Edgar Date: Tue, 7 May 2024 15:33:43 +0200 Subject: [PATCH] Add pointer addition (arithmetic) and intrinsic parsing --- crates/concrete_ast/src/common.rs | 7 ++++ crates/concrete_ast/src/functions.rs | 3 +- crates/concrete_codegen_mlir/src/codegen.rs | 39 ++++++++++++++++++++- crates/concrete_codegen_mlir/src/errors.rs | 2 ++ crates/concrete_driver/tests/common.rs | 18 +++++++++- crates/concrete_driver/tests/examples.rs | 22 +++++++++++- crates/concrete_ir/src/lib.rs | 16 ++++++++- crates/concrete_ir/src/lowering.rs | 35 ++++++++++++++---- crates/concrete_parser/src/grammar.lalrpop | 20 ++++++++++- crates/concrete_parser/src/lib.rs | 11 ++++++ crates/concrete_parser/src/tokens.rs | 2 ++ examples/hello_world_hacky.con | 36 +++++++++++++++++++ 12 files changed, 198 insertions(+), 13 deletions(-) create mode 100644 examples/hello_world_hacky.con diff --git a/crates/concrete_ast/src/common.rs b/crates/concrete_ast/src/common.rs index 3de3ee3..5b8d48f 100644 --- a/crates/concrete_ast/src/common.rs +++ b/crates/concrete_ast/src/common.rs @@ -37,3 +37,10 @@ pub struct GenericParam { pub params: Vec, pub span: Span, } + +#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct Attribute { + pub name: String, + pub value: Option, + pub span: Span, +} diff --git a/crates/concrete_ast/src/functions.rs b/crates/concrete_ast/src/functions.rs index 7ef8746..42c2e0d 100644 --- a/crates/concrete_ast/src/functions.rs +++ b/crates/concrete_ast/src/functions.rs @@ -1,5 +1,5 @@ use crate::{ - common::{DocString, GenericParam, Ident, Span}, + common::{Attribute, DocString, GenericParam, Ident, Span}, statements::Statement, types::TypeSpec, }; @@ -13,6 +13,7 @@ pub struct FunctionDecl { pub ret_type: Option, pub is_extern: bool, pub is_pub: bool, + pub attributes: Vec, pub span: Span, } diff --git a/crates/concrete_codegen_mlir/src/codegen.rs b/crates/concrete_codegen_mlir/src/codegen.rs index 1aa5e5d..2dd5fb8 100644 --- a/crates/concrete_codegen_mlir/src/codegen.rs +++ b/crates/concrete_codegen_mlir/src/codegen.rs @@ -613,10 +613,32 @@ fn compile_binop<'c: 'b, 'b>( let is_float = matches!(lhs_ty.kind, TyKind::Float(_)); let is_signed = matches!(lhs_ty.kind, TyKind::Int(_)); + let is_ptr = if let TyKind::Ptr(inner, _) = &lhs_ty.kind { + Some((*inner).clone()) + } else { + None + }; Ok(match op { BinOp::Add => { - let value = if is_float { + let value = if let Some(inner) = is_ptr { + let inner_ty = compile_type(ctx.module_ctx, &inner); + block + .append_operation( + ods::llvm::getelementptr( + ctx.context(), + pointer(ctx.context(), 0), + lhs, + &[rhs], + DenseI32ArrayAttribute::new(ctx.context(), &[i32::MIN]), + TypeAttribute::new(inner_ty), + location, + ) + .into(), + ) + .result(0)? + .into() + } else if is_float { block .append_operation(arith::addf(lhs, rhs, location)) .result(0)? @@ -630,6 +652,11 @@ fn compile_binop<'c: 'b, 'b>( (value, lhs_ty) } BinOp::Sub => { + if is_ptr.is_some() { + return Err(CodegenError::NotImplemented( + "substracting from a pointer is not yet implemented".to_string(), + )); + } let value = if is_float { block .append_operation(arith::subf(lhs, rhs, location)) @@ -644,6 +671,11 @@ fn compile_binop<'c: 'b, 'b>( (value, lhs_ty) } BinOp::Mul => { + if is_ptr.is_some() { + return Err(CodegenError::NotImplemented( + "multiplying a pointer is not yet implemented".to_string(), + )); + } let value = if is_float { block .append_operation(arith::mulf(lhs, rhs, location)) @@ -658,6 +690,11 @@ fn compile_binop<'c: 'b, 'b>( (value, lhs_ty) } BinOp::Div => { + if is_ptr.is_some() { + return Err(CodegenError::NotImplemented( + "dividing a pointer is not yet implemented".to_string(), + )); + } let value = if is_float { block .append_operation(arith::divf(lhs, rhs, location)) diff --git a/crates/concrete_codegen_mlir/src/errors.rs b/crates/concrete_codegen_mlir/src/errors.rs index 55d5092..1d90b88 100644 --- a/crates/concrete_codegen_mlir/src/errors.rs +++ b/crates/concrete_codegen_mlir/src/errors.rs @@ -8,4 +8,6 @@ pub enum CodegenError { LLVMCompileError(String), #[error("melior error: {0}")] MeliorError(#[from] melior::Error), + #[error("not yet implemented: {0}")] + NotImplemented(String), } diff --git a/crates/concrete_driver/tests/common.rs b/crates/concrete_driver/tests/common.rs index ba1e261..bdf2e5d 100644 --- a/crates/concrete_driver/tests/common.rs +++ b/crates/concrete_driver/tests/common.rs @@ -2,7 +2,7 @@ use std::{ borrow::Cow, fmt, path::{Path, PathBuf}, - process::Output, + process::{Output, Stdio}, }; use ariadne::Source; @@ -110,6 +110,7 @@ pub fn compile_program( pub fn run_program(program: &Path) -> Result { std::process::Command::new(program) + .stdout(Stdio::piped()) .spawn()? .wait_with_output() } @@ -122,3 +123,18 @@ pub fn compile_and_run(source: &str, name: &str, library: bool, optlevel: OptLev output.status.code().unwrap() } + +#[allow(unused)] // false positive +#[track_caller] +pub fn compile_and_run_output( + source: &str, + name: &str, + library: bool, + optlevel: OptLevel, +) -> String { + let result = compile_program(source, name, library, optlevel).expect("failed to compile"); + + let output = run_program(&result.binary_file).expect("failed to run"); + + std::str::from_utf8(&output.stdout).unwrap().to_string() +} diff --git a/crates/concrete_driver/tests/examples.rs b/crates/concrete_driver/tests/examples.rs index 455f58e..1377b7f 100644 --- a/crates/concrete_driver/tests/examples.rs +++ b/crates/concrete_driver/tests/examples.rs @@ -1,4 +1,4 @@ -use crate::common::compile_and_run; +use crate::common::{compile_and_run, compile_and_run_output}; use concrete_session::config::OptLevel; use test_case::test_case; @@ -39,3 +39,23 @@ fn example_tests(source: &str, name: &str, is_library: bool, status_code: i32) { compile_and_run(source, name, is_library, OptLevel::Aggressive) ); } + +#[test_case(include_str!("../../../examples/hello_world_hacky.con"), "hello_world_hacky", false, "Hello World\n" ; "hello_world_hacky.con")] +fn example_tests_with_output(source: &str, name: &str, is_library: bool, result: &str) { + assert_eq!( + result, + compile_and_run_output(source, name, is_library, OptLevel::None) + ); + assert_eq!( + result, + compile_and_run_output(source, name, is_library, OptLevel::Less) + ); + assert_eq!( + result, + compile_and_run_output(source, name, is_library, OptLevel::Default) + ); + assert_eq!( + result, + compile_and_run_output(source, name, is_library, OptLevel::Aggressive) + ); +} diff --git a/crates/concrete_ir/src/lib.rs b/crates/concrete_ir/src/lib.rs index 7be040f..464a532 100644 --- a/crates/concrete_ir/src/lib.rs +++ b/crates/concrete_ir/src/lib.rs @@ -68,6 +68,7 @@ pub struct FnBody { pub id: DefId, pub name: String, pub is_extern: bool, + pub is_intrinsic: Option, pub basic_blocks: Vec, pub locals: Vec, } @@ -397,7 +398,15 @@ impl fmt::Display for TyKind { FloatTy::F64 => write!(f, "f32"), }, TyKind::String => write!(f, "string"), - TyKind::Array(_, _) => todo!(), + TyKind::Array(inner, size) => { + let value = + if let ConstKind::Value(ValueTree::Leaf(ConstValue::U64(x))) = &size.data { + *x + } else { + unreachable!("const data for array sizes should always be u64") + }; + write!(f, "[{}; {:?}]", inner.kind, value) + } TyKind::Ref(inner, is_mut) => { let word = if let Mutability::Mut = is_mut { "mut" @@ -571,3 +580,8 @@ pub enum ConstValue { F32(f32), F64(f64), } + +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum ConcreteIntrinsic { + // Todo: Add intrinsics here +} diff --git a/crates/concrete_ir/src/lowering.rs b/crates/concrete_ir/src/lowering.rs index 6cf620e..dd2104c 100644 --- a/crates/concrete_ir/src/lowering.rs +++ b/crates/concrete_ir/src/lowering.rs @@ -16,10 +16,10 @@ use concrete_ast::{ }; use crate::{ - AdtBody, BasicBlock, BinOp, ConstData, ConstKind, ConstValue, DefId, FloatTy, FnBody, IntTy, - Local, LocalKind, LogOp, Mutability, Operand, Place, PlaceElem, ProgramBody, Rvalue, Statement, - StatementKind, SwitchTargets, Terminator, TerminatorKind, Ty, TyKind, UintTy, ValueTree, - VariantDef, + AdtBody, BasicBlock, BinOp, ConcreteIntrinsic, ConstData, ConstKind, ConstValue, DefId, + FloatTy, FnBody, IntTy, Local, LocalKind, LogOp, Mutability, Operand, Place, PlaceElem, + ProgramBody, Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, Ty, + TyKind, UintTy, ValueTree, VariantDef, }; use self::errors::LoweringError; @@ -217,11 +217,16 @@ fn lower_func( func: &FunctionDef, module_id: DefId, ) -> Result { + let is_intrinsic: Option = None; + + // TODO: parse insintrics here. + let mut builder = FnBodyBuilder { body: FnBody { basic_blocks: Vec::new(), locals: Vec::new(), is_extern: func.decl.is_extern, + is_intrinsic, name: func.decl.name.name.clone(), id: { let body = ctx.body.modules.get(&module_id).unwrap(); @@ -350,11 +355,16 @@ fn lower_func_decl( func: &FunctionDecl, module_id: DefId, ) -> Result { + let is_intrinsic: Option = None; + + // TODO: parse insintrics here. + let builder = FnBodyBuilder { body: FnBody { basic_blocks: Vec::new(), locals: Vec::new(), is_extern: func.is_extern, + is_intrinsic, name: func.name.name.clone(), id: { let body = ctx.body.modules.get(&module_id).unwrap(); @@ -1236,14 +1246,22 @@ fn lower_binary_op( } else { lower_expression(builder, lhs, type_hint.clone())? }; + + // We must handle the special case where you can do ptr + offset. + let is_lhs_ptr = matches!(lhs_ty.kind, TyKind::Ptr(_, _)); + let (rhs, rhs_ty, rhs_span) = if type_hint.is_none() { let ty = find_expression_type(builder, rhs).unwrap_or(lhs_ty.clone()); - lower_expression(builder, rhs, Some(ty))? + lower_expression(builder, rhs, if is_lhs_ptr { None } else { Some(ty) })? } else { - lower_expression(builder, rhs, type_hint.clone())? + lower_expression( + builder, + rhs, + if is_lhs_ptr { None } else { type_hint.clone() }, + )? }; - if lhs_ty != rhs_ty { + if !is_lhs_ptr && lhs_ty != rhs_ty { return Err(LoweringError::UnexpectedType { span: rhs_span, found: rhs_ty, @@ -1409,6 +1427,9 @@ fn lower_value_expr( UintTy::U128 => ConstValue::U128(*value), }, TyKind::Bool => ConstValue::Bool(*value != 0), + TyKind::Ptr(ref _inner, _mutable) => { + ConstValue::I64((*value).try_into().expect("value out of range")) + } x => unreachable!("{:?}", x), })), }, diff --git a/crates/concrete_parser/src/grammar.lalrpop b/crates/concrete_parser/src/grammar.lalrpop index 3b40d96..7990ba8 100644 --- a/crates/concrete_parser/src/grammar.lalrpop +++ b/crates/concrete_parser/src/grammar.lalrpop @@ -53,6 +53,7 @@ extern { ":" => Token::Colon, "->" => Token::Arrow, "," => Token::Coma, + "#" => Token::Hashtag, "<" => Token::LessThanSign, ">" => Token::MoreThanSign, ">=" => Token::MoreThanEqSign, @@ -119,6 +120,14 @@ PlusSeparated: Vec = { } }; +List: Vec = { + => vec![<>], + > => { + s.push(n); + s + }, +} + // Requires the semicolon at end SemiColonSeparated: Vec = { ";" => vec![<>], @@ -291,13 +300,22 @@ pub(crate) Param: ast::functions::Param = { } } +pub(crate) Attribute: ast::common::Attribute = { + "#" "[" )?> "]" => ast::common::Attribute { + name, + value, + span: ast::common::Span::new(lo, hi), + } +} + pub(crate) FunctionDecl: ast::functions::FunctionDecl = { - + ?> "fn" "(" > ")" => ast::functions::FunctionDecl { doc_string: None, generic_params: generic_params.unwrap_or(vec![]), + attributes: attributes.unwrap_or(vec![]), name, params, ret_type, diff --git a/crates/concrete_parser/src/lib.rs b/crates/concrete_parser/src/lib.rs index 3108b09..5acfc43 100644 --- a/crates/concrete_parser/src/lib.rs +++ b/crates/concrete_parser/src/lib.rs @@ -285,6 +285,17 @@ mod ModuleName { return arr[1][0]; } +}"##; + let lexer = Lexer::new(source); + let parser = grammar::ProgramParser::new(); + parser.parse(lexer).unwrap(); + } + + #[test] + fn parse_intrinsic() { + let source = r##"mod MyMod { + #[intrinsic = "simdsomething"] + pub extern fn myintrinsic(); }"##; let lexer = Lexer::new(source); let parser = grammar::ProgramParser::new(); diff --git a/crates/concrete_parser/src/tokens.rs b/crates/concrete_parser/src/tokens.rs index 4b1326c..c9a884a 100644 --- a/crates/concrete_parser/src/tokens.rs +++ b/crates/concrete_parser/src/tokens.rs @@ -109,6 +109,8 @@ pub enum Token { Coma, #[token(".")] Dot, + #[token("#")] + Hashtag, #[token("<")] LessThanSign, #[token(">")] diff --git a/examples/hello_world_hacky.con b/examples/hello_world_hacky.con new file mode 100644 index 0000000..ed27057 --- /dev/null +++ b/examples/hello_world_hacky.con @@ -0,0 +1,36 @@ +mod HelloWorld { + pub extern fn malloc(size: u64) -> *mut u8; + pub extern fn puts(data: *mut u8) -> i32; + + fn main() -> i32 { + let origin: *mut u8 = malloc(12); + let mut p: *mut u8 = origin; + + *p = 'H'; + p = p + 1; + *p = 'e'; + p = p + 1; + *p = 'l'; + p = p + 1; + *p = 'l'; + p = p + 1; + *p = 'o'; + p = p + 1; + *p = ' '; + p = p + 1; + *p = 'W'; + p = p + 1; + *p = 'o'; + p = p + 1; + *p = 'r'; + p = p + 1; + *p = 'l'; + p = p + 1; + *p = 'd'; + p = p + 1; + *p = '\0'; + puts(origin); + + return 0; + } +}