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

Global variables are now loaded from _ENV. #11

Merged
merged 2 commits into from
Jan 16, 2019
Merged

Global variables are now loaded from _ENV. #11

merged 2 commits into from
Jan 16, 2019

Conversation

rbartlensky
Copy link
Owner

In the Lua specification, the global variables are loaded from a global
table called _ENV. Programs can overwrite this variable as well, and
can read and write from/to it.
There are a few changes:

  • The compiler:
    • assumes that _ENV is always stored in register 0
    • replaces all global variable writing, and reading with GetAttr, and
      SetAttr instructions
  • The vm:
    • always creates a LuaTable and stores it in register 0, so that globals
      can be easily accessed.
    • can now set and get attributes of LuaTables

@ltratt
Copy link
Collaborator

ltratt commented Dec 13, 2018

Can you benchmark a couple of programs that do simple global read/writes before/after this PR?

@rbartlensky
Copy link
Owner Author

Can you benchmark a couple of programs that do simple global read/writes before/after this PR?

I have a hunch that programs before this PR are slightly faster, but only because I implemented globals without considering _ENV. But yes, I can try to create a few programs, and benchmark the interpreter on them!

@rbartlensky rbartlensky force-pushed the env-globals branch 2 times, most recently from aafb8f0 to 8261df0 Compare December 14, 2018 15:57
@rbartlensky
Copy link
Owner Author

I would like to merge this PR before writing any benchmarks. I am going to implement functions and loops first, and then come back to optimizing the _ENV lookups.

@rbartlensky
Copy link
Owner Author

As an extra example, _ENV is handled in the following way:

Example program:

x = 2 -- equivalent to _ENV["x"] = 2

The compiler generates the following bytecode:

LDI     1 0   # load into register 1 integer 0
LDS     2 0   # load into register 2 string 0
SetAttr 0 2 1 # _ENV is always in register 0, _ENV["x"] = 2

and the tables:

Strings: { 0 -> "x" }
Integers: { 0 -> 2 }
Floats: {}

Every LuaString has an extra field which is the index of the string in the constant table. This field will be None if the user generates strings through concatenation.

The VM only executes set/get_attr if the loaded LuaString doesn't have an index. If it does, then the attribute is looked up via the env_attrs vector.

@ltratt
Copy link
Collaborator

ltratt commented Jan 15, 2019

I guess I was expecting something more like "GLOBAL_ASSIGN 0 2" where "0" == "print" and "1" is register 1 (which, presumably, holds the value 1). However, this is something we can optimise / change later.

@rbartlensky
Copy link
Owner Author

I guess I was expecting something more like "GLOBAL_ASSIGN 0 2" where "0" == "print" and "1" is register 1 (which, presumably, holds the value 1). However, this is something we can optimise / change later.

I see what you mean. That might speed things up, and the changes should be easy to make even later on.

@@ -1,5 +1,8 @@
use std::{collections::HashMap, vec::Vec};

/// The register in which `_ENV` lives.
pub const ENV_REG: usize = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth using up a register for ENV? We could stick it in a special variable inside the VM?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say yes, because most of the time you use functions from the standard library. I could introduce new instructions such as GET/SET_ENV, or have a special instruction which loads the _ENV into a variable, and then use GET/SET_ATTR on the register which has _ENV.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes to which question?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh sorry, yes to the first question. I think I am starting to see your point. But if I special case _ENV, that means that I will need special instructions as well, which will have _ENV as an implicit argument.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Let's leave that to a later PR.

let from = &vm.registers[arg2];
let attr = &vm.registers[third_arg(instr) as usize];
match attr.get_constant_index() {
Some(i) if arg2 == ENV_REG => vm.env_attrs[i].clone(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a bit weird that nothing in the if arm references the left hand-side. The logic feels sort of inside-out to me.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason why it should. The left hand-side is the register in which the value is copied. I am only interested to see if the value comes from _ENV or not. Either way, the value is copied here in the left hand-side. I hope I am not misunderstanding your comment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is that the logic here feels like it should be something like:

if arg2 == ENV_REG {
    if let Some(i) = attr.get_constant_index() {
        ...
    }
}

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, but then I would only be able to write this:

        if arg2 == ENV_REG {
            if let Some(i) = attr.get_constant_index() {
                vm.env_attrs[i].clone()
            } else {
                from.get_attr(attr)?
            }
        } else {
            from.get_attr(attr)?
        }

I prefer my initial solution, but this might be slightly more efficient and more readable?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw this merged, but I don't think it is part of stable. At least it doesn't work on rust playground (I made sure to check rust 2018).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

match attr.get_constant_index() {
   Some(i) => if arg2 == ENV_REG { vm.env_attrs[i].clone() } else { from.get_attr(attr)? }
   None => ...
}

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will duplicate from.get_attr(attr)? this way. Is that fine?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your call. It feels clearer to me, but I don't feel strongly about it.

let val = vm.registers[third_arg(instr) as usize].clone();
let arg1 = first_arg(instr) as usize;
match attr.get_constant_index() {
Some(i) if arg1 == ENV_REG => vm.env_attrs[i] = val,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

@rbartlensky
Copy link
Owner Author

Ready for another review!

In the Lua specification, the global variables are loaded from a global
table called `_ENV`. Programs can overwrite this variable as well, and
can read and write from/to it.
There are a few changes:
* The compiler:
  * assumes that `_ENV` is always stored in register 0
  * replaces all global variable writing, and reading with `GetAttr`, and
  `SetAttr` instructions
* The vm:
  * always creates a `LuaTable` and stores it in register 0, so that globals
can be easily accessed.
  * can now set and get attributes of `LuaTable`s
@ltratt
Copy link
Collaborator

ltratt commented Jan 15, 2019

Please squash.

@rbartlensky
Copy link
Owner Author

Squashed!

@ltratt ltratt merged commit b24c797 into master Jan 16, 2019
@ltratt ltratt deleted the env-globals branch January 16, 2019 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants