Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rust 1.81 + 1.82 + Lalrpop 0.21 + FromStr impl + Lalrpop 0.22 #336

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
6 changes: 3 additions & 3 deletions bril-rs/bril2json/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ keywords = ["compiler", "bril", "parser", "data-structures", "language"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.4", features = ["derive"] }
lalrpop-util = { version = "0.20", features = ["lexer"] }
clap = { version = "4.4", features = ["derive"] }
lalrpop-util = { version = "0.22", features = ["lexer"] }
regex = "1.10"

# Add a build-time dependency on the lalrpop library:
[build-dependencies]
lalrpop = "0.20"
lalrpop = "0.22"

[dependencies.bril-rs]
version = "0.1.0"
Expand Down
1 change: 1 addition & 0 deletions bril-rs/bril2json/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
let mut config = lalrpop::Configuration::new();
config.emit_rerun_directives(true);
config.generate_in_source_tree();
config.process_current_dir()
}
36 changes: 16 additions & 20 deletions bril-rs/bril2json/src/bril_grammar.lalrpop
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
#![allow(clippy::needless_lifetimes)]
#![allow(clippy::just_underscores_and_digits)]
#![allow(clippy::deprecated_cfg_attr)]
#![allow(clippy::no_effect_underscore_binding)]
#![allow(clippy::unnested_or_patterns)]
#![allow(clippy::trivially_copy_pass_by_ref)]
#![allow(clippy::missing_const_for_fn)]
#![allow(clippy::unnecessary_wraps)]
#![allow(clippy::redundant_pub_crate)]
#![allow(clippy::cloned_instead_of_copied)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::use_self)]
#![allow(clippy::needless_pass_by_value)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::uninlined_format_args)]
#![allow(clippy::match_same_arms)]
#![allow(clippy::option_if_let_else)]
#![allow(clippy::extra_unused_lifetimes)]
#![allow(clippy::needless_raw_string_hashes)]
#![expect(clippy::no_effect_underscore_binding)]
#![expect(clippy::unnested_or_patterns)]
#![expect(clippy::trivially_copy_pass_by_ref)]
#![expect(clippy::missing_const_for_fn)]
#![expect(clippy::unnecessary_wraps)]
#![expect(clippy::redundant_pub_crate)]
#![expect(clippy::cloned_instead_of_copied)]
#![expect(clippy::too_many_lines)]
#![expect(clippy::use_self)]
#![expect(clippy::needless_pass_by_value)]
#![expect(clippy::cast_sign_loss)]
#![expect(clippy::must_use_candidate)]
#![expect(clippy::uninlined_format_args)]
#![expect(clippy::match_same_arms)]
#![expect(clippy::option_if_let_else)]
#![expect(clippy::extra_unused_lifetimes)]

use std::str::FromStr;
use std::path::PathBuf;
Expand Down
4 changes: 4 additions & 0 deletions bril-rs/brild/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use thiserror::Error;

#[expect(
clippy::module_name_repetitions,
reason = "I allow for the Error suffix on enums"
)]
#[derive(Error, Debug)]
pub enum BrildError {
#[error("Could not find a complete path for `{0}` from the list of provided libraries")]
Expand Down
4 changes: 2 additions & 2 deletions bril-rs/brild/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![warn(missing_docs)]
#![warn(clippy::allow_attributes)]
#![doc = include_str!("../README.md")]
#![allow(clippy::module_name_repetitions)]

#[doc(hidden)]
pub mod cli;
Expand All @@ -22,7 +22,7 @@ use bril_rs::{

use crate::error::BrildError;

fn mangle_name(path: &Path, func_name: &String) -> String {
fn mangle_name(path: &Path, func_name: &str) -> String {
let mut parts = path.components();
parts.next();

Expand Down
9 changes: 8 additions & 1 deletion bril-rs/brillvm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ be updated as Inkwell gets new support, within the constraints of the rust compi

## Runtime

You must have a linkable runtime library available in the LLVM bc format. You can get this by calling `make rt`
You must have a linkable runtime library available in the LLVM bc format.
You can get this by calling `make rt`.

## Usage

Expand Down Expand Up @@ -38,6 +39,12 @@ the LLVM `mem2reg` pass will be sufficient if that is needed) but if you choose
to supply Bril code with `phi` operations, Brillvm will assume that they follow
LLVM's additional constraints.

### Limitations

- Rust and Bril both allow variables to be re-declared with different types. Brillvm
does not yet support this and assumes that a given variable will only have one
type within a function.

## TroubleShooting

### Floating Point values
Expand Down
1 change: 1 addition & 0 deletions bril-rs/brillvm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![warn(clippy::allow_attributes)]
#![allow(clippy::doc_markdown)] // Not uniform? Just lints on one line
#![allow(clippy::too_many_lines)]
#![allow(clippy::needless_for_each)]
Expand Down
65 changes: 45 additions & 20 deletions bril-rs/brillvm/src/llvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ fn unwrap_bril_ptrtype(ty: &Type) -> &Type {
fn build_functiontype<'a>(
context: &'a Context,
args: &[&Type],
return_ty: &Option<Type>,
return_ty: Option<&Type>,
) -> FunctionType<'a> {
let param_types: Vec<BasicMetadataTypeEnum> = args
.iter()
.map(|t| llvm_type_map(context, t, Into::into))
.collect();
#[allow(clippy::option_if_let_else)] // I think this is more readable
#[expect(clippy::option_if_let_else)] // I think this is more readable
match return_ty {
None => context.void_type().fn_type(&param_types, false),
Some(t) => llvm_type_map(context, t, |t| t.fn_type(&param_types, false)),
Expand Down Expand Up @@ -96,10 +96,19 @@ impl<'a, 'b> Heap<'a, 'b> {
name: &'b String,
ty: &Type,
) -> WrappedPointer<'a> {
self.map
let result = self
.map
.entry(name)
.or_insert_with(|| WrappedPointer::new(builder, context, name, ty))
.clone()
.clone();
if result.ty != *ty {
println!(
"`{}` had type `{}` but is now being assigned type `{}`",
name, result.ty, ty
);
unimplemented!("brillvm does not currently support variables within a function having different types. Implementing this might require a control flow analysis? Feel free to try and implement this.")
}
result
}

fn get(&self, name: &String) -> WrappedPointer<'a> {
Expand Down Expand Up @@ -179,7 +188,7 @@ fn block_map_get<'a>(
}

// The workhorse of converting a Bril Instruction to an LLVM Instruction
#[allow(clippy::too_many_arguments)]
#[expect(clippy::too_many_arguments)]
fn build_instruction<'a, 'b>(
i: &'b Instruction,
context: &'a Context,
Expand All @@ -198,7 +207,7 @@ fn build_instruction<'a, 'b>(
const_type: Type::Float,
value: Literal::Int(i),
} => {
#[allow(clippy::cast_precision_loss)]
#[expect(clippy::cast_precision_loss)]
builder
.build_store(
heap.get(dest).ptr,
Expand All @@ -212,7 +221,7 @@ fn build_instruction<'a, 'b>(
const_type: _,
value: Literal::Int(i),
} => {
#[allow(clippy::cast_sign_loss)]
#[expect(clippy::cast_sign_loss, reason = "u64 because of the C++/C API")]
builder
.build_store(
heap.get(dest).ptr,
Expand Down Expand Up @@ -1209,7 +1218,7 @@ fn build_instruction<'a, 'b>(
}

// Check for instructions that end a block
const fn is_terminating_instr(i: &Option<Instruction>) -> bool {
const fn is_terminating_instr(i: Option<&Instruction>) -> bool {
matches!(
i,
Some(Instruction::Effect {
Expand Down Expand Up @@ -1237,7 +1246,10 @@ pub fn create_module_from_program<'a>(
let mut fresh = Fresh::new();

// Add all functions to the module, initialize all variables in the heap, and setup for the second phase
#[allow(clippy::needless_collect)]
#[expect(
clippy::needless_collect,
reason = "Important to collect, can't be done lazily because we need all functions to be loaded in before a call instruction of a function is processed."
)]
let funcs: Vec<_> = functions
.iter()
.map(
Expand All @@ -1254,7 +1266,7 @@ pub fn create_module_from_program<'a>(
.iter()
.map(|Argument { arg_type, .. }| arg_type)
.collect::<Vec<_>>(),
return_type,
return_type.as_ref(),
);

let func_name = if name == "main" { "_main" } else { name };
Expand Down Expand Up @@ -1294,29 +1306,30 @@ pub fn create_module_from_program<'a>(
}
});

(llvm_func, instrs, block, heap)
(llvm_func, instrs, block, heap, return_type)
},
)
.collect(); // Important to collect, can't be done lazily because we need all functions to be loaded in before a call instruction of a function is processed.
.collect();

// Now actually build each function
funcs
.into_iter()
.for_each(|(llvm_func, instrs, mut block, heap)| {
.for_each(|(llvm_func, instrs, mut block, heap, return_type)| {
let mut last_instr = None;

// Maps labels to llvm blocks for jumps
let mut block_map = HashMap::new();

// If their are actually instructions, proceed
if !instrs.is_empty() {
builder.position_at_end(block);

// Maps labels to llvm blocks for jumps
let mut block_map = HashMap::new();
instrs.iter().for_each(|i| match i {
bril_rs::Code::Label { label, .. } => {
let new_block = block_map_get(context, llvm_func, &mut block_map, label);

// Check if wee need to insert a jump since all llvm blocks must be terminated
if !is_terminating_instr(&last_instr) {
// Check if we need to insert a jump since all llvm blocks must be terminated
if !is_terminating_instr(last_instr.as_ref()) {
builder
.build_unconditional_branch(block_map_get(
context,
Expand All @@ -1335,7 +1348,7 @@ pub fn create_module_from_program<'a>(
bril_rs::Code::Instruction(i) => {
// Check if we are in a basic block that has already been terminated
// If so, we just keep skipping unreachable instructions until we hit a new block or run out of instructions
if !is_terminating_instr(&last_instr) {
if !is_terminating_instr(last_instr.as_ref()) {
build_instruction(
i,
context,
Expand All @@ -1353,8 +1366,20 @@ pub fn create_module_from_program<'a>(
}

// Make sure every function is terminated with a return if not already
if !is_terminating_instr(&last_instr) {
builder.build_return(None).unwrap();
if !is_terminating_instr(last_instr.as_ref()) {
if return_type.is_none() {
builder.build_return(None).unwrap();
} else {
// This block did not have a terminating instruction
// Returning void is ill-typed for this function
// This code should be unreachable in well-formed Bril
// Let's just arbitrarily jump to avoid needing to
// instantiate a valid return value.
assert!(!block_map.is_empty());
builder
.build_unconditional_branch(*block_map.values().next().unwrap())
.unwrap();
}
}
});

Expand Down
1 change: 1 addition & 0 deletions bril-rs/rs2bril/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![warn(missing_docs)]
#![warn(clippy::allow_attributes)]
#![doc = include_str!("../README.md")]
#![allow(clippy::too_many_lines)]
#![allow(clippy::cognitive_complexity)]
Expand Down
Loading