-
Notifications
You must be signed in to change notification settings - Fork 243
Added Kaleidoscope example #19
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
Conversation
Oh, by the way, this should lead to the update of #2. |
Codecov Report
@@ Coverage Diff @@
## master #19 +/- ##
=======================================
Coverage 46.06% 46.06%
=======================================
Files 35 35
Lines 3645 3645
=======================================
Hits 1679 1679
Misses 1966 1966
Continue to review full report at Codecov.
|
Want me to add tests to Kaleidoscope? In any case, I'll add some improvements, and after this I'm planning to work on the documentation. I also have some remarks on some structures and enums |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lot here, so I'm going to do another pass or two in the next couple days. But I did my initial review and it looks great overall.
I'd prefer to avoid transmute like the plague, so please help me understand why they're really needed so we can think about how to replace the ones that aren't.
Also, kaleidoscope.rs is huge! Thank you for all of the time that you've put into this. I really appreciate it. I think we should split it into at least three or four files:
examples/kaleidoscope/lexer.rs
examples/kaleidoscope/parser.rs
examples/kaleidoscope/llvm.rs
and maybe a main/drive type of file, where we can add commandline argument parsing stuff, if needed?
examples/kaleidoscope.rs
Outdated
}; | ||
} | ||
|
||
pub extern fn putchard(x: f64) -> f64 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are these extern functions for? If you need putchar, you should be able to call the llvm putchar function. It's also unclear why you truncate a 64bit float down to an 8 bit int and then back up to a char?
examples/kaleidoscope.rs
Outdated
fpm: pass_manager, | ||
module: module, | ||
function: function, | ||
fn_value: unsafe { std::mem::transmute(0usize) }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Transmute should be avoided unless absolutely necessary. It looks to me like this could be replaced with a None
of type Option<FunctionValue>
?
examples/kaleidoscope.rs
Outdated
if is_anonymous { | ||
let ee = module.create_jit_execution_engine(0).unwrap(); | ||
|
||
// 2017-02-10 <6A> I still can't add my own functions with either add_global_mapping or add_symbol. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you explain why add_global_mapping
and add_symbol
are needed? Also, I don't believe LLVMAddSymbol was introduced until 3.7, and I would like this to eventually work in 3.6 as well.
examples/kaleidoscope.rs
Outdated
|
||
/// Represents a primitive syntax token. | ||
#[derive(Debug, Clone)] | ||
pub enum Token { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a personal preference, but would you mind keeping one enum variant per line and alphabetizing them? I find it's easier to visually find variants when looking at it that way.
examples/kaleidoscope.rs
Outdated
|
||
// Skip whitespaces | ||
loop { | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's hard to tell from just looking at the code, do the two scopes need to be separate? If so, maybe add a comment explaining why.
examples/kaleidoscope.rs
Outdated
let var_val = self.compile_expr(&right)?; | ||
let var = self.variables.get(var_name.as_str()).ok_or("")?; | ||
|
||
Ok(unsafe { std::mem::transmute(self.builder.build_store(&var, &var_val)) }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this transmuting to? IIRC store doesn't return useful data but the instruction. Maybe you want to return the actual value that was stored?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method returns a FloatValue
, but build_store
returns a PointerValue
. I'm casting the latter to the former here, since the structure is actually a pointer to a float.
examples/kaleidoscope.rs
Outdated
(&else_val, &else_bb) | ||
]); | ||
|
||
Ok(unsafe { std::mem::transmute(phi) }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to do phi.as_basic_value().into_float_value()
or similar in the latest version of Inkwell
examples/kaleidoscope.rs
Outdated
let curr_var = self.builder.build_load(&start_alloca, var_name); | ||
let next_var = self.builder.build_float_add(&curr_var.as_float_value(), &step, "nextvar"); | ||
|
||
self.builder.build_store(&unsafe { std::mem::transmute(next_var) }, &start_alloca); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is a float being used as a pointer? Did you mean build_store(&start_alloca, &next_var)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thanks!
Yeah, in theory |
My last commit should address all your concerns. The only |
I got to try it out and I like it a lot! Some thoughts:
|
src/execution_engine.rs
Outdated
@@ -50,6 +50,12 @@ impl ExecutionEngine { | |||
} | |||
} | |||
|
|||
pub fn add_global_mapping(&mut self, global: &AnyValue, addr: *const ()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might make sense to remove this for now since it's not used and is likely to change anyway. Long term it may end up looking something like this:
struct MemoryAddress(usize);
pub fn add_global_mapping(&(mut?) self, global: &GlobalValue, addr: &MemoryAddress) {}
For example, the execution engine outputs an address which can easily be moved or copied out of the EE's scope, so I think that will end up returning a non-Copy &MemoryAddress
so that the underlying value is kept in scope but is still transmutable to a function. Maybe the user will be able to create their own (btw, you can safely cast any pointer to a usize like so: let ptr_val = &a as *const _ as usize;
or similar)
What do you think?
// REVIEW: as_raw_fd docs suggest it only works in *nix | ||
// Also, should_close should maybe be hardcoded to true? | ||
unsafe { | ||
LLVMWriteBitcodeToFD(self.module, file.as_raw_fd(), should_close as i32, unbuffered as i32) == 0 | ||
} | ||
} | ||
|
||
#[cfg(windows)] | ||
#[allow(unused_variables)] | ||
pub fn write_bitcode_to_file(&self, file: &File, should_close: bool, unbuffered: bool) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sweet! Thanks! I hope we'll be able to get this working on windows eventually, and if not we'll probably just remove the method completely from windows.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting the file's raw handle might work, but I haven't tried it out.
examples/kaleidoscope.rs
Outdated
loop { | ||
let ch = match chars.peek() { | ||
Some(ch) => *ch, | ||
None => { return Ok(Token::EOF); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, I believe you can just do None => return Ok(Token::EOF),
examples/kaleidoscope.rs
Outdated
loop { | ||
let ch = match chars.peek() { | ||
Some(ch) => *ch, | ||
None => { return Ok(Token::EOF); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
|
||
let op = match self.curr() { | ||
Op(ch) => ch, | ||
_ => { return Err("Expected operator in custom operator declaration."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
|
||
let op = match self.curr() { | ||
Op(ch) => ch, | ||
_ => { return Err("Expected operator in custom operator declaration."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
(name, true, 0) | ||
}, | ||
|
||
_ => { return Err("Expected identifier in prototype declaration.") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets
examples/kaleidoscope.rs
Outdated
|
||
match self.curr() { | ||
LParen => (), | ||
_ => { return Err("Expected '(' character in prototype declaration.") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets
examples/kaleidoscope.rs
Outdated
loop { | ||
match self.curr() { | ||
Ident(name) => args.push(name), | ||
_ => { return Err("Expected identifier in parameter declaration.") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets
examples/kaleidoscope.rs
Outdated
Comma => { | ||
self.advance(); | ||
}, | ||
_ => { return Err("Expected ',' or ')' character in prototype declaration.") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets
examples/kaleidoscope.rs
Outdated
fn parse_paren_expr(&mut self) -> Result<Expr, &'static str> { | ||
match self.current()? { | ||
LParen => (), | ||
_ => { return Err("Expected '(' character at start of parenthesized expression.") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets
examples/kaleidoscope.rs
Outdated
|
||
match self.current()? { | ||
RParen => (), | ||
_ => { return Err("Expected ')' character at end of parenthesized expression.") } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets
examples/kaleidoscope.rs
Outdated
fn parse_id_expr(&mut self) -> Result<Expr, &'static str> { | ||
let id = match self.curr() { | ||
Ident(id) => id, | ||
_ => { return Err("Expected identifier."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
match self.current()? { | ||
Comma => (), | ||
RParen => break, | ||
_ => { return Err("Expected ',' character in function call."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
self.advance()?; | ||
ch | ||
}, | ||
_ => { return self.parse_primary(); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
|
||
let op = match self.curr() { | ||
Op(op) => op, | ||
_ => { return Err("Invalid operator."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
// eat 'then' token | ||
match self.current() { | ||
Ok(Then) => { self.advance()? }, | ||
_ => { return Err("Expected 'then' keyword."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
// eat 'else' token | ||
match self.current() { | ||
Ok(Else) => { self.advance()? }, | ||
_ => { return Err("Expected 'else' keyword."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
|
||
let name = match self.curr() { | ||
Ident(n) => n, | ||
_ => { return Err("Expected identifier in for loop."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
// eat '=' token | ||
match self.curr() { | ||
Op('=') => { self.advance()?; }, | ||
_ => { return Err("Expected '=' character in for loop."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
// eat ',' token | ||
match self.current()? { | ||
Comma => { self.advance()?; }, | ||
_ => { return Err("Expected ',' character in for loop."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
examples/kaleidoscope.rs
Outdated
// eat 'in' token | ||
match self.current()? { | ||
In => { self.advance()?; }, | ||
_ => { return Err("Expected 'in' keyword in for loop."); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for brackets or semicolon
Some stylistic comments, but this PR is starting to look pretty good. Did you try running clippy on it by any chance? |
Nope, I'll get to it right away. Also thanks for your stylistic comments, but it wasn't worth the trouble to comment every single mistake... And I just realized I let up something I had used for testing purposes... Oops. Oh, and the space on the left of the |
No trouble at all. I'll leave it up to you as to whether you want the space or not. Maybe consider something like Python's approach: > 1 + 1
2 |
I instead went with a different approach and prefixed all input with |
Cool! |
Added the Kaleidoscope example to the project. Additionally,
write_bitcode_to_file
now compiles on Windows (although it will always fail and returnfalse
).Note: Yes, the first three commits have the wrong author, but after an hour of trying to fix this, I just give up on the fame.