-
Notifications
You must be signed in to change notification settings - Fork 5
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
BUG-2199: Fix for the FSRT Multiple Definitions for Same Variable Bug #47
Conversation
Thank you for your submission! Like many open source projects, we ask that you sign our CLA (Contributor License Agreement) before we can accept your contribution. Already signed the CLA? To re-check, try refreshing the page. |
@@ -1464,7 +1467,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
let id = ident.to_id(); | |||
let new_def = | |||
self.res.get_or_insert_sym(id.clone(), self.module); | |||
let var_id = self.body.get_or_insert_global(new_def); | |||
let var_id = self.body.insert_global(new_def); |
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 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.
Yeah, we shouldn't be creating new global variables/var ids, since this will create two separate variables:
const b = 3;
const a = { b };
%b = 3
%a.b = %newb
Very clean code 🙂 |
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 main problem with doing this during lowering is that you need to keep track of which uses of a symbol correspond to the new definition that you created. Right now it's implicitly the last one, which doesn't work for control flow across basic blocks.
Also, new uses of an identifier shouldn't create another VarId, rather it should refer to the last VarId that was assigned.
Regardless, nice work so far! Let me know if you have any questions.
@@ -1464,7 +1467,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
let id = ident.to_id(); | |||
let new_def = | |||
self.res.get_or_insert_sym(id.clone(), self.module); | |||
let var_id = self.body.get_or_insert_global(new_def); | |||
let var_id = self.body.insert_global(new_def); |
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.
Yeah, we shouldn't be creating new global variables/var ids, since this will create two separate variables:
const b = 3;
const a = { b };
%b = 3
%a.b = %newb
@jwong101, I thought this bug was for within basic blocks though? Shouldn't we wait until the dominator tree is merged in and we can track between basic blocks then? |
Thank you for the feedback @jwong101, taking a look at this right now. I believe the new uses of an identifier case should be fairly simple to resolve - I'm investigating it now. Regarding the issue with lowering and needing to track which uses of a symbol correspond to the new definition created, let me ensure that it's explicitly defined and not implicitly. Ideally, if it's explicitly defined, I think that should translate fairly smoothly to incorporating the dominator tree work as well. I'll touch base with you and @gersbach regarding this soon when I've done more work to research it. Thank you! |
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.
Added comments where extraneous var ids are created.
While you can go with this approach, it will make the renaming trickier in later stages. Mainly because you now have to do another lookup to check the "true" alias of a var ID on every use to do the correction. I.e.:
// mapping
// %0 -> a
// %1 -> b
// %2 -> a
// %3 -> c
// %4 -> d
let a = 1; // %0 = 1
if (true) {
let b = a // %1 = %0
a = 3; // %2 = 3
let c = a; // %3 = %2
return;
}
let d = a; // %4 = %2 ((assuming we visit the if statement bb first)
While you could solve this by checking the mapping, it's a lot easier to just treat the raw var IDs as the potential aliases.
Though one thing you could do in the lowerering code is treating global assignments differently(basically variables that aren't defined in the function body). Those can't be in SSA form, since the assignment semantics are different. (they should have a separate IR statement tbh)
@@ -1394,7 +1397,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
warn!("unknown symbol: {}", id.0); | |||
return Literal::Undef.into(); | |||
}; | |||
let var = self.body.get_or_insert_global(def); | |||
let var = self.body.insert_global(def); |
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.
This will create duplicate var IDs on each use of ident
, which will make coalescing them trickier when handling the multi basic block case.
@@ -1192,7 +1195,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
.iter() | |||
.find(|(name, defid)| name == "constructor") | |||
{ | |||
let var = self.body.get_or_insert_global(*def_constructor); | |||
let var = self.body.insert_global(*def_constructor); |
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.
Same here, uses shouldn't create new defs.
@@ -1206,7 +1209,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
.iter() | |||
.find(|(name, defid)| name == &ident.sym) | |||
{ | |||
let var = self.body.get_or_insert_global(*def_constructor); | |||
let var = self.body.insert_global(*def_constructor); |
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.
Same here, this is a use.
@@ -1351,7 +1354,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
} | |||
Expr::Ident(ident) => { | |||
let defid = self.res.sym_to_id(ident.to_id(), self.module); | |||
let varid = self.body.get_or_insert_global(defid.unwrap()); | |||
let varid = self.body.insert_global(defid.unwrap()); |
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.
Use not a def
@@ -1414,7 +1417,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
warn!("unknown symbol: {}", id.0); | |||
return Literal::Undef.into(); | |||
}; | |||
let var = self.body.get_or_insert_global(def); | |||
let var = self.body.insert_global(def); |
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.
use not a def
@@ -1424,7 +1427,7 @@ impl<'cx> FunctionAnalyzer<'cx> { | |||
warn!("unknown symbol: {}", ns.0); | |||
return Literal::Undef.into(); | |||
}; | |||
let var = self.body.get_or_insert_global(def); | |||
let var = self.body.insert_global(def); |
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.
use not a def
FSRT should be in “static single assignment form”, i.e. there should be exactly one definition of each variable, as this simplifies later analysis. This PR fixes this bug, and ensures that every variable has one definition.
More on the bug: Take this code example below:
The above function currently gives this IR:
Note that in the current output of our IR that every “use” of the variable %7 only needs to consider the definition of “%7 = 10”. Thus, we can replace the assignment “%7 = 10” with “%11 = 10” and all following uses of “%7” with the new variable “%11”.